Merge "Support Neighbor Advertisement packet w/o TLLA option parsing." am: c6eb439fcb am: a73d636cc5

Original change: https://android-review.googlesource.com/c/platform/packages/modules/NetworkStack/+/1736503

Change-Id: If32c3b7fa2f5f49039d7449c4856c0c6b9422250
diff --git a/src/com/android/networkstack/packets/NeighborAdvertisement.java b/src/com/android/networkstack/packets/NeighborAdvertisement.java
index e6cdfc8..ef38314 100644
--- a/src/com/android/networkstack/packets/NeighborAdvertisement.java
+++ b/src/com/android/networkstack/packets/NeighborAdvertisement.java
@@ -17,13 +17,14 @@
 package com.android.networkstack.packets;
 
 import static com.android.net.module.util.NetworkStackConstants.ETHER_HEADER_LEN;
-import static com.android.net.module.util.NetworkStackConstants.ICMPV6_HEADER_MIN_LEN;
+import static com.android.net.module.util.NetworkStackConstants.ICMPV6_NA_HEADER_LEN;
 import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ND_OPTION_TLLA;
 import static com.android.net.module.util.NetworkStackConstants.IPV6_HEADER_LEN;
 
 import android.net.MacAddress;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 import com.android.net.module.util.Ipv6Utils;
 import com.android.net.module.util.Struct;
@@ -50,12 +51,12 @@
     public final Icmpv6Header icmpv6Hdr;
     @NonNull
     public final NaHeader naHdr;
-    @NonNull
+    @Nullable
     public final LlaOption tlla;
 
     public NeighborAdvertisement(@NonNull final EthernetHeader ethHdr,
             @NonNull final Ipv6Header ipv6Hdr, @NonNull final Icmpv6Header icmpv6Hdr,
-            @NonNull final NaHeader naHdr, @NonNull final LlaOption tlla) {
+            @NonNull final NaHeader naHdr, @Nullable final LlaOption tlla) {
         this.ethHdr = ethHdr;
         this.ipv6Hdr = ipv6Hdr;
         this.icmpv6Hdr = icmpv6Hdr;
@@ -71,7 +72,7 @@
         final int ipv6HeaderLen = Struct.getSize(Ipv6Header.class);
         final int icmpv6HeaderLen = Struct.getSize(Icmpv6Header.class);
         final int naHeaderLen = Struct.getSize(NaHeader.class);
-        final int tllaOptionLen = Struct.getSize(LlaOption.class);
+        final int tllaOptionLen = (tlla == null) ? 0 : Struct.getSize(LlaOption.class);
         final ByteBuffer packet = ByteBuffer.allocate(etherHeaderLen + ipv6HeaderLen
                 + icmpv6HeaderLen + naHeaderLen + tllaOptionLen);
 
@@ -79,7 +80,9 @@
         ipv6Hdr.writeToByteBuffer(packet);
         icmpv6Hdr.writeToByteBuffer(packet);
         naHdr.writeToByteBuffer(packet);
-        tlla.writeToByteBuffer(packet);
+        if (tlla != null) {
+            tlla.writeToByteBuffer(packet);
+        }
         packet.flip();
 
         return packet;
@@ -100,7 +103,7 @@
      */
     public static NeighborAdvertisement parse(@NonNull final byte[] recvbuf, final int length)
             throws ParseException {
-        if (length < ETHER_HEADER_LEN + IPV6_HEADER_LEN + ICMPV6_HEADER_MIN_LEN
+        if (length < ETHER_HEADER_LEN + IPV6_HEADER_LEN + ICMPV6_NA_HEADER_LEN
                 || recvbuf.length < length) {
             throw new ParseException("Invalid packet length: " + length);
         }
@@ -111,7 +114,9 @@
         final Ipv6Header ipv6Hdr = Struct.parse(Ipv6Header.class, packet);
         final Icmpv6Header icmpv6Hdr = Struct.parse(Icmpv6Header.class, packet);
         final NaHeader naHdr = Struct.parse(NaHeader.class, packet);
-        final LlaOption tlla = Struct.parse(LlaOption.class, packet);
+        final LlaOption tlla = (packet.remaining() == 0)
+                ? null
+                : Struct.parse(LlaOption.class, packet);
 
         return new NeighborAdvertisement(ethHdr, ipv6Hdr, icmpv6Hdr, naHdr, tlla);
     }
diff --git a/src/com/android/networkstack/packets/NeighborSolicitation.java b/src/com/android/networkstack/packets/NeighborSolicitation.java
index e743209..5c3e40a 100644
--- a/src/com/android/networkstack/packets/NeighborSolicitation.java
+++ b/src/com/android/networkstack/packets/NeighborSolicitation.java
@@ -17,8 +17,8 @@
 package com.android.networkstack.packets;
 
 import static com.android.net.module.util.NetworkStackConstants.ETHER_HEADER_LEN;
-import static com.android.net.module.util.NetworkStackConstants.ICMPV6_HEADER_MIN_LEN;
 import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ND_OPTION_SLLA;
+import static com.android.net.module.util.NetworkStackConstants.ICMPV6_NS_HEADER_LEN;
 import static com.android.net.module.util.NetworkStackConstants.IPV6_HEADER_LEN;
 
 import android.net.MacAddress;
@@ -43,8 +43,6 @@
  * @hide
  */
 public class NeighborSolicitation {
-    private static final int NS_HEADER_LEN = Struct.getSize(NsHeader.class);
-
     @NonNull
     public final EthernetHeader ethHdr;
     @NonNull
@@ -105,7 +103,7 @@
      */
     public static NeighborSolicitation parse(@NonNull final byte[] recvbuf, final int length)
             throws ParseException {
-        if (length < ETHER_HEADER_LEN + IPV6_HEADER_LEN + ICMPV6_HEADER_MIN_LEN + NS_HEADER_LEN
+        if (length < ETHER_HEADER_LEN + IPV6_HEADER_LEN + ICMPV6_NS_HEADER_LEN
                 || recvbuf.length < length) {
             throw new ParseException("Invalid packet length: " + length);
         }
diff --git a/tests/unit/src/com/android/networkstack/packets/NeighborAdvertisementTest.java b/tests/unit/src/com/android/networkstack/packets/NeighborAdvertisementTest.java
index 3317b2b..c689d7b 100644
--- a/tests/unit/src/com/android/networkstack/packets/NeighborAdvertisementTest.java
+++ b/tests/unit/src/com/android/networkstack/packets/NeighborAdvertisementTest.java
@@ -19,6 +19,7 @@
 import static android.system.OsConstants.ETH_P_IPV6;
 import static android.system.OsConstants.IPPROTO_ICMPV6;
 
+import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ND_OPTION_TLLA;
 import static com.android.net.module.util.NetworkStackConstants.ICMPV6_NEIGHBOR_ADVERTISEMENT;
 import static com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_ALL_ROUTERS_MULTICAST;
 import static com.android.testutils.MiscAsserts.assertThrows;
@@ -90,6 +91,41 @@
         // Link-Layer address
         (byte) 0xea, (byte) 0xbe, (byte) 0x11, (byte) 0x25, (byte) 0xc1, (byte) 0x25,
     };
+    private static final byte[] TEST_GRATUITOUS_NA_WITHOUT_TLLA = new byte[] {
+        // dst mac address
+        (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+        // src mac address
+        (byte) 0xea, (byte) 0xbe, (byte) 0x11, (byte) 0x25, (byte) 0xc1, (byte) 0x25,
+        // ether type
+        (byte) 0x86, (byte) 0xdd,
+        // version, priority and flow label
+        (byte) 0x60, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        // length
+        (byte) 0x00, (byte) 0x20,
+        // next header
+        (byte) 0x3a,
+        // hop limit
+        (byte) 0xff,
+        // source address
+        (byte) 0xfe, (byte) 0x80, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0xdf, (byte) 0xd9, (byte) 0x50, (byte) 0xa0,
+        (byte) 0xcc, (byte) 0x7b, (byte) 0x7d, (byte) 0x6d,
+        // destination address
+        (byte) 0xff, (byte) 0x02, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x02,
+        // ICMP type, code, checksum
+        (byte) 0x88, (byte) 0x00, (byte) 0x3a, (byte) 0x3c,
+        // flags
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        // target address
+        (byte) 0x20, (byte) 0x01, (byte) 0x0d, (byte) 0xb8,
+        (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00,
+        (byte) 0xc9, (byte) 0x28, (byte) 0x25, (byte) 0x0d,
+        (byte) 0xb9, (byte) 0x0c, (byte) 0x31, (byte) 0x78,
+    };
     private static final byte[] TEST_GRATUITOUS_NA_LESS_LENGTH = new byte[] {
         // dst mac address
         (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
@@ -115,6 +151,10 @@
         (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
         (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
         (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x02,
+        // ICMP type, code, checksum
+        (byte) 0x88, (byte) 0x00, (byte) 0x3a, (byte) 0x3c,
+        // flags
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
     };
     private static final byte[] TEST_GRATUITOUS_NA_TRUNCATED = new byte[] {
         // dst mac address
@@ -152,7 +192,7 @@
         (byte) 0xb9, (byte) 0x0c, (byte) 0x31, (byte) 0x78,
         // TLLA option
         (byte) 0x02, (byte) 0x01,
-        // Link-Layer address
+        // truncatd Link-Layer address: 4bytes
         (byte) 0xea, (byte) 0xbe, (byte) 0x11, (byte) 0x25,
     };
 
@@ -165,11 +205,8 @@
         assertArrayEquals(na.array(), TEST_GRATUITOUS_NA);
     }
 
-    @Test
-    public void testGratuitousNa_parse() throws Exception {
-        final NeighborAdvertisement na = NeighborAdvertisement.parse(TEST_GRATUITOUS_NA,
-                TEST_GRATUITOUS_NA.length);
-
+    private void assertNeighborAdvertisement(final NeighborAdvertisement na,
+            boolean hasTllaOption) {
         assertArrayEquals(TEST_SOURCE_MAC_ADDR, na.ethHdr.srcMac.toByteArray());
         assertArrayEquals(TEST_DST_MAC_ADDR, na.ethHdr.dstMac.toByteArray());
         assertEquals(ETH_P_IPV6, na.ethHdr.etherType);
@@ -181,20 +218,46 @@
         assertEquals(0, na.icmpv6Hdr.code);
         assertEquals(0, na.naHdr.flags);
         assertEquals(TEST_TARGET_ADDR, na.naHdr.target);
-        assertEquals(2, na.tlla.type);
-        assertEquals(1, na.tlla.length);
-        assertArrayEquals(TEST_SOURCE_MAC_ADDR, na.tlla.linkLayerAddress.toByteArray());
+        if (hasTllaOption) {
+            assertEquals(ICMPV6_ND_OPTION_TLLA, na.tlla.type);
+            assertEquals(1, na.tlla.length);
+            assertArrayEquals(TEST_SOURCE_MAC_ADDR, na.tlla.linkLayerAddress.toByteArray());
+        }
+    }
 
+    @Test
+    public void testGratuitousNa_parse() throws Exception {
+        final NeighborAdvertisement na = NeighborAdvertisement.parse(TEST_GRATUITOUS_NA,
+                TEST_GRATUITOUS_NA.length);
+
+        assertNeighborAdvertisement(na, true /* hasTllaOption */);
         assertArrayEquals(TEST_GRATUITOUS_NA, na.toByteBuffer().array());
     }
 
     @Test
-    public void testGratuitousNa_invalidByteBufferParameters() throws Exception {
+    public void testGratuitousNa_parseWithoutTllaOption() throws Exception {
+        final NeighborAdvertisement na =
+                NeighborAdvertisement.parse(TEST_GRATUITOUS_NA_WITHOUT_TLLA,
+                        TEST_GRATUITOUS_NA_WITHOUT_TLLA.length);
+
+        assertNeighborAdvertisement(na, false /* hasTllaOption */);
+        assertArrayEquals(TEST_GRATUITOUS_NA_WITHOUT_TLLA, na.toByteBuffer().array());
+    }
+
+    @Test
+    public void testGratuitousNa_zeroPacketLength() throws Exception {
         assertThrows(NeighborAdvertisement.ParseException.class,
                 () -> NeighborAdvertisement.parse(TEST_GRATUITOUS_NA, 0));
     }
 
     @Test
+    public void testGratuitousNa_invalidByteBufferLength() throws Exception {
+        assertThrows(NeighborAdvertisement.ParseException.class,
+                () -> NeighborAdvertisement.parse(TEST_GRATUITOUS_NA_TRUNCATED,
+                                                  TEST_GRATUITOUS_NA.length));
+    }
+
+    @Test
     public void testGratuitousNa_lessPacketLength() throws Exception {
         assertThrows(NeighborAdvertisement.ParseException.class,
                 () -> NeighborAdvertisement.parse(TEST_GRATUITOUS_NA_LESS_LENGTH,
diff --git a/tests/unit/src/com/android/networkstack/packets/NeighborSolicitationTest.java b/tests/unit/src/com/android/networkstack/packets/NeighborSolicitationTest.java
index c3ff239..18a3ef3 100644
--- a/tests/unit/src/com/android/networkstack/packets/NeighborSolicitationTest.java
+++ b/tests/unit/src/com/android/networkstack/packets/NeighborSolicitationTest.java
@@ -19,6 +19,7 @@
 import static android.system.OsConstants.ETH_P_IPV6;
 import static android.system.OsConstants.IPPROTO_ICMPV6;
 
+import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ND_OPTION_SLLA;
 import static com.android.net.module.util.NetworkStackConstants.ICMPV6_NEIGHBOR_SOLICITATION;
 import static com.android.testutils.MiscAsserts.assertThrows;
 
@@ -193,7 +194,7 @@
         (byte) 0x11, (byte) 0x22, (byte) 0x33, (byte) 0x44,
         // slla option
         (byte) 0x01, (byte) 0x01,
-        // link-layer address
+        // truncatd link-layer address: 4bytes
         (byte) 0x06, (byte) 0x5a, (byte) 0xac, (byte) 0x02,
     };
 
@@ -218,6 +219,8 @@
         assertEquals(0, ns.icmpv6Hdr.code);
         assertEquals(TEST_TARGET_ADDR, ns.nsHdr.target);
         if (hasSllaOption) {
+            assertEquals(ICMPV6_ND_OPTION_SLLA, ns.slla.type);
+            assertEquals(1, ns.slla.length);
             assertEquals(MacAddress.fromBytes(TEST_SOURCE_MAC_ADDR), ns.slla.linkLayerAddress);
         }
     }