Improve RouterAdvertisementDaemonTest.

Add separate testing for unicast and multicast RAs.
Make the test more realistic by:
- Enabling forwarding. This ensures that the daemon actually
  receives the RS.
- Adding a link-local route. This ensures that the daemon is
  actually able to send a unicast response.

Bug: 154669942
Test: atest TetheringPrivilegedTests
Change-Id: Ibb1f51b5b1871657d7feff39335d3c71586cf64f
diff --git a/Tethering/tests/privileged/src/android/net/ip/RouterAdvertisementDaemonTest.java b/Tethering/tests/privileged/src/android/net/ip/RouterAdvertisementDaemonTest.java
index 14dae5c..1d94214 100644
--- a/Tethering/tests/privileged/src/android/net/ip/RouterAdvertisementDaemonTest.java
+++ b/Tethering/tests/privileged/src/android/net/ip/RouterAdvertisementDaemonTest.java
@@ -16,6 +16,8 @@
 
 package android.net.ip;
 
+import static android.net.RouteInfo.RTN_UNICAST;
+
 import static com.android.net.module.util.NetworkStackConstants.ETHER_HEADER_LEN;
 import static com.android.net.module.util.NetworkStackConstants.ETHER_TYPE_IPV6;
 import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ND_OPTION_MTU;
@@ -27,6 +29,8 @@
 import static com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_ALL_NODES_MULTICAST;
 import static com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_LEN;
 import static com.android.net.module.util.NetworkStackConstants.IPV6_HEADER_LEN;
+import static com.android.net.module.util.NetworkStackConstants.PIO_FLAG_AUTONOMOUS;
+import static com.android.net.module.util.NetworkStackConstants.PIO_FLAG_ON_LINK;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
@@ -38,7 +42,9 @@
 import android.net.INetd;
 import android.net.IpPrefix;
 import android.net.MacAddress;
+import android.net.RouteInfo;
 import android.net.ip.RouterAdvertisementDaemon.RaParams;
+import android.net.shared.RouteUtils;
 import android.net.util.InterfaceParams;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -74,6 +80,7 @@
 import java.net.InetAddress;
 import java.nio.ByteBuffer;
 import java.util.HashSet;
+import java.util.List;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
@@ -96,8 +103,6 @@
 
     @BeforeClass
     public static void setupOnce() {
-        System.loadLibrary("tetherutilsjni");
-
         final Instrumentation inst = InstrumentationRegistry.getInstrumentation();
         final IBinder netdIBinder =
                 (IBinder) inst.getContext().getSystemService(Context.NETD_SERVICE);
@@ -151,7 +156,7 @@
             mNewParams = newParams;
         }
 
-        public boolean isPacketMatched(final byte[] pkt) throws Exception {
+        public boolean isPacketMatched(final byte[] pkt, boolean multicast) throws Exception {
             if (pkt.length < (ETHER_HEADER_LEN + IPV6_HEADER_LEN + ICMPV6_RA_HEADER_LEN)) {
                 return false;
             }
@@ -172,6 +177,15 @@
             final Icmpv6Header icmpv6Hdr = Struct.parse(Icmpv6Header.class, buf);
             if (icmpv6Hdr.type != (short) ICMPV6_ROUTER_ADVERTISEMENT) return false;
 
+            // Check whether IPv6 destination address is multicast or unicast
+            if (multicast) {
+                assertEquals(ipv6Hdr.dstIp, IPV6_ADDR_ALL_NODES_MULTICAST);
+            } else {
+                // The unicast IPv6 destination address in RA can be either link-local or global
+                // IPv6 address. This test only expects link-local address.
+                assertTrue(ipv6Hdr.dstIp.isLinkLocalAddress());
+            }
+
             // Parse RA header
             final RaHeader raHdr = Struct.parse(RaHeader.class, buf);
             assertEquals(mNewParams.hopLimit, raHdr.hopLimit);
@@ -182,13 +196,15 @@
                 final int length = Byte.toUnsignedInt(buf.get());
                 switch (type) {
                     case ICMPV6_ND_OPTION_PIO:
+                        // length is 4 because this test only expects one PIO included in the
+                        // router advertisement packet.
                         assertEquals(4, length);
 
                         final ByteBuffer pioBuf = ByteBuffer.wrap(buf.array(), currentPos,
                                 Struct.getSize(PrefixInformationOption.class));
                         final PrefixInformationOption pio =
                                 Struct.parse(PrefixInformationOption.class, pioBuf);
-                        assertEquals((byte) 0xc0, pio.flags); // L & A set
+                        assertEquals((byte) (PIO_FLAG_ON_LINK | PIO_FLAG_AUTONOMOUS), pio.flags);
 
                         final InetAddress address = InetAddress.getByAddress(pio.prefix);
                         final IpPrefix prefix = new IpPrefix(address, pio.prefixLen);
@@ -199,7 +215,7 @@
                             assertEquals(0, pio.validLifetime);
                             assertEquals(0, pio.preferredLifetime);
                         } else {
-                            fail("Unepxected prefix: " + prefix);
+                            fail("Unexpected prefix: " + prefix);
                         }
 
                         // Move ByteBuffer position to the next option.
@@ -261,15 +277,24 @@
         return params;
     }
 
-    private boolean assertRaPacket(final TestRaPacket testRa)
-            throws Exception {
+    private boolean isRaPacket(final TestRaPacket testRa, boolean multicast) throws Exception {
         byte[] packet;
         while ((packet = mTetheredPacketReader.poll(PACKET_TIMEOUT_MS)) != null) {
-            if (testRa.isPacketMatched(packet)) return true;
+            if (testRa.isPacketMatched(packet, multicast)) {
+                return true;
+            }
         }
         return false;
     }
 
+    private void assertUnicastRaPacket(final TestRaPacket testRa) throws Exception {
+        assertTrue(isRaPacket(testRa, false /* multicast */));
+    }
+
+    private void assertMulticastRaPacket(final TestRaPacket testRa) throws Exception {
+        assertTrue(isRaPacket(testRa, true /* multicast */));
+    }
+
     private ByteBuffer createRsPacket(final String srcIp) throws Exception {
         final MacAddress dstMac = MacAddress.fromString("33:33:03:04:05:06");
         final MacAddress srcMac = mTetheredParams.macAddr;
@@ -284,22 +309,36 @@
         assertTrue(mRaDaemon.start());
         final RaParams params1 = createRaParams("2001:1122:3344::5566");
         mRaDaemon.buildNewRa(null, params1);
-        assertRaPacket(new TestRaPacket(null, params1));
+        assertMulticastRaPacket(new TestRaPacket(null, params1));
 
         final RaParams params2 = createRaParams("2006:3344:5566::7788");
         mRaDaemon.buildNewRa(params1, params2);
-        assertRaPacket(new TestRaPacket(params1, params2));
+        assertMulticastRaPacket(new TestRaPacket(params1, params2));
     }
 
     @Test
     public void testSolicitRouterAdvertisement() throws Exception {
+        // Enable IPv6 forwarding is necessary, which makes kernel process RS correctly and
+        // create the neighbor entry for peer's link-layer address and IPv6 address. Otherwise,
+        // when device receives RS with IPv6 link-local address as source address, it has to
+        // initiate the address resolution first before responding the unicast RA.
+        sNetd.setProcSysNet(INetd.IPV6, INetd.CONF, mTetheredParams.name, "forwarding", "1");
+
         assertTrue(mRaDaemon.start());
         final RaParams params1 = createRaParams("2001:1122:3344::5566");
         mRaDaemon.buildNewRa(null, params1);
-        assertRaPacket(new TestRaPacket(null, params1));
+        assertMulticastRaPacket(new TestRaPacket(null, params1));
+
+        // Add a default route "fe80::/64 -> ::" to local network, otherwise, device will fail to
+        // send the unicast RA out due to the ENETUNREACH error(No route to the peer's link-local
+        // address is present).
+        final String iface = mTetheredParams.name;
+        final RouteInfo linkLocalRoute =
+                new RouteInfo(new IpPrefix("fe80::/64"), null, iface, RTN_UNICAST);
+        RouteUtils.addRoutesToLocalNetwork(sNetd, iface, List.of(linkLocalRoute));
 
         final ByteBuffer rs = createRsPacket("fe80::1122:3344:5566:7788");
         mTetheredPacketReader.sendResponse(rs);
-        assertRaPacket(new TestRaPacket(null, params1));
+        assertUnicastRaPacket(new TestRaPacket(null, params1));
     }
 }