diff options
author | Yuyang Huang <yuyanghuang@google.com> | 2023-08-24 07:37:46 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2023-08-24 07:37:46 +0000 |
commit | fcc4a3a15ad6ae81fa5200a541d9c5a74b8a008c (patch) | |
tree | 6d22f68751c97fe222ba5d3a976edde1a2f1df2e | |
parent | d4aa590f7e1ee19044502a0116cbb574e210f4f1 (diff) | |
parent | 027be158fc418d7838d47ff4f8541918822da6f6 (diff) | |
download | net-fcc4a3a15ad6ae81fa5200a541d9c5a74b8a008c.tar.gz |
Merge "Move ArpPacket.java to frameworks/libs/net" into main am: 027be158fc
Original change: https://android-review.googlesource.com/c/platform/frameworks/libs/net/+/2720913
Change-Id: Ibc1f622dd7b93d1e565f792a0580d1a8d3045798
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r-- | common/Android.bp | 1 | ||||
-rw-r--r-- | common/device/com/android/net/module/util/arp/ArpPacket.java | 171 | ||||
-rw-r--r-- | common/tests/unit/src/com/android/net/module/util/ArpPacketTest.java | 201 |
3 files changed, 373 insertions, 0 deletions
diff --git a/common/Android.bp b/common/Android.bp index d3e68b75..0b42219c 100644 --- a/common/Android.bp +++ b/common/Android.bp @@ -34,6 +34,7 @@ package { java_library { name: "net-utils-device-common", srcs: [ + "device/com/android/net/module/util/arp/ArpPacket.java", "device/com/android/net/module/util/DeviceConfigUtils.java", "device/com/android/net/module/util/DomainUtils.java", "device/com/android/net/module/util/FdEventsReader.java", diff --git a/common/device/com/android/net/module/util/arp/ArpPacket.java b/common/device/com/android/net/module/util/arp/ArpPacket.java new file mode 100644 index 00000000..dab9694b --- /dev/null +++ b/common/device/com/android/net/module/util/arp/ArpPacket.java @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.net.module.util.arp; + +import static android.system.OsConstants.ETH_P_ARP; +import static android.system.OsConstants.ETH_P_IP; + +import static com.android.net.module.util.NetworkStackConstants.ARP_ETHER_IPV4_LEN; +import static com.android.net.module.util.NetworkStackConstants.ARP_HWTYPE_ETHER; +import static com.android.net.module.util.NetworkStackConstants.ARP_REPLY; +import static com.android.net.module.util.NetworkStackConstants.ARP_REQUEST; +import static com.android.net.module.util.NetworkStackConstants.ETHER_ADDR_LEN; +import static com.android.net.module.util.NetworkStackConstants.IPV4_ADDR_LEN; + +import android.net.MacAddress; + +import com.android.internal.annotations.VisibleForTesting; + +import java.net.Inet4Address; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; + +/** + * Defines basic data and operations needed to build and parse packets for the + * ARP protocol. + * + * @hide + */ +public class ArpPacket { + private static final String TAG = "ArpPacket"; + + public final short opCode; + public final Inet4Address senderIp; + public final Inet4Address targetIp; + public final MacAddress senderHwAddress; + public final MacAddress targetHwAddress; + + ArpPacket(short opCode, MacAddress senderHwAddress, Inet4Address senderIp, + MacAddress targetHwAddress, Inet4Address targetIp) { + this.opCode = opCode; + this.senderHwAddress = senderHwAddress; + this.senderIp = senderIp; + this.targetHwAddress = targetHwAddress; + this.targetIp = targetIp; + } + + /** + * Build an ARP packet from the required specified parameters. + */ + @VisibleForTesting + public static ByteBuffer buildArpPacket(final byte[] dstMac, final byte[] srcMac, + final byte[] targetIp, final byte[] targetHwAddress, byte[] senderIp, + final short opCode) { + final ByteBuffer buf = ByteBuffer.allocate(ARP_ETHER_IPV4_LEN); + + // Ether header + buf.put(dstMac); + buf.put(srcMac); + buf.putShort((short) ETH_P_ARP); + + // ARP header + buf.putShort((short) ARP_HWTYPE_ETHER); // hrd + buf.putShort((short) ETH_P_IP); // pro + buf.put((byte) ETHER_ADDR_LEN); // hln + buf.put((byte) IPV4_ADDR_LEN); // pln + buf.putShort(opCode); // op + buf.put(srcMac); // sha + buf.put(senderIp); // spa + buf.put(targetHwAddress); // tha + buf.put(targetIp); // tpa + buf.flip(); + return buf; + } + + /** + * Parse an ARP packet from a ByteBuffer object. + */ + @VisibleForTesting + public static ArpPacket parseArpPacket(final byte[] recvbuf, final int length) + throws ParseException { + try { + if (length < ARP_ETHER_IPV4_LEN || recvbuf.length < length) { + throw new ParseException("Invalid packet length: " + length); + } + + final ByteBuffer buffer = ByteBuffer.wrap(recvbuf, 0, length); + byte[] l2dst = new byte[ETHER_ADDR_LEN]; + byte[] l2src = new byte[ETHER_ADDR_LEN]; + buffer.get(l2dst); + buffer.get(l2src); + + final short etherType = buffer.getShort(); + if (etherType != ETH_P_ARP) { + throw new ParseException("Incorrect Ether Type: " + etherType); + } + + final short hwType = buffer.getShort(); + if (hwType != ARP_HWTYPE_ETHER) { + throw new ParseException("Incorrect HW Type: " + hwType); + } + + final short protoType = buffer.getShort(); + if (protoType != ETH_P_IP) { + throw new ParseException("Incorrect Protocol Type: " + protoType); + } + + final byte hwAddrLength = buffer.get(); + if (hwAddrLength != ETHER_ADDR_LEN) { + throw new ParseException("Incorrect HW address length: " + hwAddrLength); + } + + final byte ipAddrLength = buffer.get(); + if (ipAddrLength != IPV4_ADDR_LEN) { + throw new ParseException("Incorrect Protocol address length: " + ipAddrLength); + } + + final short opCode = buffer.getShort(); + if (opCode != ARP_REQUEST && opCode != ARP_REPLY) { + throw new ParseException("Incorrect opCode: " + opCode); + } + + byte[] senderHwAddress = new byte[ETHER_ADDR_LEN]; + byte[] senderIp = new byte[IPV4_ADDR_LEN]; + buffer.get(senderHwAddress); + buffer.get(senderIp); + + byte[] targetHwAddress = new byte[ETHER_ADDR_LEN]; + byte[] targetIp = new byte[IPV4_ADDR_LEN]; + buffer.get(targetHwAddress); + buffer.get(targetIp); + + return new ArpPacket(opCode, MacAddress.fromBytes(senderHwAddress), + (Inet4Address) InetAddress.getByAddress(senderIp), + MacAddress.fromBytes(targetHwAddress), + (Inet4Address) InetAddress.getByAddress(targetIp)); + } catch (IndexOutOfBoundsException e) { + throw new ParseException("Invalid index when wrapping a byte array into a buffer"); + } catch (BufferUnderflowException e) { + throw new ParseException("Invalid buffer position"); + } catch (IllegalArgumentException e) { + throw new ParseException("Invalid MAC address representation"); + } catch (UnknownHostException e) { + throw new ParseException("Invalid IP address of Host"); + } + } + + /** + * Thrown when parsing ARP packet failed. + */ + public static class ParseException extends Exception { + ParseException(String message) { + super(message); + } + } +} diff --git a/common/tests/unit/src/com/android/net/module/util/ArpPacketTest.java b/common/tests/unit/src/com/android/net/module/util/ArpPacketTest.java new file mode 100644 index 00000000..e25d5548 --- /dev/null +++ b/common/tests/unit/src/com/android/net/module/util/ArpPacketTest.java @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.net.module.util; + +import static com.android.net.module.util.NetworkStackConstants.ARP_REQUEST; +import static com.android.net.module.util.NetworkStackConstants.ETHER_ADDR_LEN; +import static com.android.net.module.util.NetworkStackConstants.ETHER_BROADCAST; +import static com.android.testutils.MiscAsserts.assertThrows; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +import android.net.InetAddresses; +import android.net.MacAddress; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.net.module.util.arp.ArpPacket; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.net.Inet4Address; +import java.nio.ByteBuffer; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public final class ArpPacketTest { + + private static final Inet4Address TEST_IPV4_ADDR = + (Inet4Address) InetAddresses.parseNumericAddress("192.168.1.2"); + private static final Inet4Address INADDR_ANY = + (Inet4Address) InetAddresses.parseNumericAddress("0.0.0.0"); + private static final byte[] TEST_SENDER_MAC_ADDR = new byte[] { + 0x00, 0x1a, 0x11, 0x22, 0x33, 0x33 }; + private static final byte[] TEST_TARGET_MAC_ADDR = new byte[] { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + private static final byte[] TEST_ARP_PROBE = new byte[] { + // dst mac address + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + // src mac address + (byte) 0x00, (byte) 0x1a, (byte) 0x11, (byte) 0x22, (byte) 0x33, (byte) 0x33, + // ether type + (byte) 0x08, (byte) 0x06, + // hardware type + (byte) 0x00, (byte) 0x01, + // protocol type + (byte) 0x08, (byte) 0x00, + // hardware address size + (byte) 0x06, + // protocol address size + (byte) 0x04, + // opcode + (byte) 0x00, (byte) 0x01, + // sender mac address + (byte) 0x00, (byte) 0x1a, (byte) 0x11, (byte) 0x22, (byte) 0x33, (byte) 0x33, + // sender IP address + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // target mac address + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // target IP address + (byte) 0xc0, (byte) 0xa8, (byte) 0x01, (byte) 0x02, + }; + + private static final byte[] TEST_ARP_ANNOUNCE = new byte[] { + // dst mac address + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + // src mac address + (byte) 0x00, (byte) 0x1a, (byte) 0x11, (byte) 0x22, (byte) 0x33, (byte) 0x33, + // ether type + (byte) 0x08, (byte) 0x06, + // hardware type + (byte) 0x00, (byte) 0x01, + // protocol type + (byte) 0x08, (byte) 0x00, + // hardware address size + (byte) 0x06, + // protocol address size + (byte) 0x04, + // opcode + (byte) 0x00, (byte) 0x01, + // sender mac address + (byte) 0x00, (byte) 0x1a, (byte) 0x11, (byte) 0x22, (byte) 0x33, (byte) 0x33, + // sender IP address + (byte) 0xc0, (byte) 0xa8, (byte) 0x01, (byte) 0x02, + // target mac address + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // target IP address + (byte) 0xc0, (byte) 0xa8, (byte) 0x01, (byte) 0x02, + }; + + private static final byte[] TEST_ARP_PROBE_TRUNCATED = new byte[] { + // dst mac address + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + // src mac address + (byte) 0x00, (byte) 0x1a, (byte) 0x11, (byte) 0x22, (byte) 0x33, (byte) 0x33, + // ether type + (byte) 0x08, (byte) 0x06, + // hardware type + (byte) 0x00, (byte) 0x01, + // protocol type + (byte) 0x08, (byte) 0x00, + // hardware address size + (byte) 0x06, + // protocol address size + (byte) 0x04, + // opcode + (byte) 0x00, + }; + + private static final byte[] TEST_ARP_PROBE_TRUNCATED_MAC = new byte[] { + // dst mac address + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + // src mac address + (byte) 0x00, (byte) 0x1a, (byte) 0x11, (byte) 0x22, (byte) 0x33, (byte) 0x33, + // ether type + (byte) 0x08, (byte) 0x06, + // hardware type + (byte) 0x00, (byte) 0x01, + // protocol type + (byte) 0x08, (byte) 0x00, + // hardware address size + (byte) 0x06, + // protocol address size + (byte) 0x04, + // opcode + (byte) 0x00, (byte) 0x01, + // sender mac address + (byte) 0x00, (byte) 0x1a, (byte) 0x11, (byte) 0x22, (byte) 0x33, + }; + + @Test + public void testBuildArpProbePacket() throws Exception { + final ByteBuffer arpProbe = ArpPacket.buildArpPacket(ETHER_BROADCAST, + TEST_SENDER_MAC_ADDR, TEST_IPV4_ADDR.getAddress(), new byte[ETHER_ADDR_LEN], + INADDR_ANY.getAddress(), (short) ARP_REQUEST); + assertArrayEquals(arpProbe.array(), TEST_ARP_PROBE); + } + + @Test + public void testBuildArpAnnouncePacket() throws Exception { + final ByteBuffer arpAnnounce = ArpPacket.buildArpPacket(ETHER_BROADCAST, + TEST_SENDER_MAC_ADDR, TEST_IPV4_ADDR.getAddress(), new byte[ETHER_ADDR_LEN], + TEST_IPV4_ADDR.getAddress(), (short) ARP_REQUEST); + assertArrayEquals(arpAnnounce.array(), TEST_ARP_ANNOUNCE); + } + + @Test + public void testParseArpProbePacket() throws Exception { + final ArpPacket packet = ArpPacket.parseArpPacket(TEST_ARP_PROBE, TEST_ARP_PROBE.length); + assertEquals(packet.opCode, ARP_REQUEST); + assertEquals(packet.senderHwAddress, MacAddress.fromBytes(TEST_SENDER_MAC_ADDR)); + assertEquals(packet.targetHwAddress, MacAddress.fromBytes(TEST_TARGET_MAC_ADDR)); + assertEquals(packet.senderIp, INADDR_ANY); + assertEquals(packet.targetIp, TEST_IPV4_ADDR); + } + + @Test + public void testParseArpAnnouncePacket() throws Exception { + final ArpPacket packet = ArpPacket.parseArpPacket(TEST_ARP_ANNOUNCE, + TEST_ARP_ANNOUNCE.length); + assertEquals(packet.opCode, ARP_REQUEST); + assertEquals(packet.senderHwAddress, MacAddress.fromBytes(TEST_SENDER_MAC_ADDR)); + assertEquals(packet.targetHwAddress, MacAddress.fromBytes(TEST_TARGET_MAC_ADDR)); + assertEquals(packet.senderIp, TEST_IPV4_ADDR); + assertEquals(packet.targetIp, TEST_IPV4_ADDR); + } + + @Test + public void testParseArpPacket_invalidByteBufferParameters() throws Exception { + assertThrows(ArpPacket.ParseException.class, () -> ArpPacket.parseArpPacket( + TEST_ARP_PROBE, 0)); + } + + @Test + public void testParseArpPacket_truncatedPacket() throws Exception { + assertThrows(ArpPacket.ParseException.class, () -> ArpPacket.parseArpPacket( + TEST_ARP_PROBE_TRUNCATED, TEST_ARP_PROBE_TRUNCATED.length)); + } + + @Test + public void testParseArpPacket_truncatedMacAddress() throws Exception { + assertThrows(ArpPacket.ParseException.class, () -> ArpPacket.parseArpPacket( + TEST_ARP_PROBE_TRUNCATED_MAC, TEST_ARP_PROBE_TRUNCATED.length)); + } +} |