summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYuyang Huang <yuyanghuang@google.com>2023-08-24 07:37:46 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2023-08-24 07:37:46 +0000
commitfcc4a3a15ad6ae81fa5200a541d9c5a74b8a008c (patch)
tree6d22f68751c97fe222ba5d3a976edde1a2f1df2e
parentd4aa590f7e1ee19044502a0116cbb574e210f4f1 (diff)
parent027be158fc418d7838d47ff4f8541918822da6f6 (diff)
downloadnet-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.bp1
-rw-r--r--common/device/com/android/net/module/util/arp/ArpPacket.java171
-rw-r--r--common/tests/unit/src/com/android/net/module/util/ArpPacketTest.java201
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));
+ }
+}