diff --git a/Tethering/tests/privileged/src/android/net/ip/RouterAdvertisementDaemonTest.java b/Tethering/tests/privileged/src/android/net/ip/RouterAdvertisementDaemonTest.java index 14dae5c562..1d942146e1 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.ICMPV6_ROUTER_AD 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.content.Context; 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.Inet6Address; import java.net.InetAddress; import java.nio.ByteBuffer; import java.util.HashSet; +import java.util.List; @RunWith(AndroidJUnit4.class) @SmallTest @@ -96,8 +103,6 @@ public final class RouterAdvertisementDaemonTest { @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 @@ public final class RouterAdvertisementDaemonTest { 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 @@ public final class RouterAdvertisementDaemonTest { 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 @@ public final class RouterAdvertisementDaemonTest { 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 @@ public final class RouterAdvertisementDaemonTest { 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 @@ public final class RouterAdvertisementDaemonTest { 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 @@ public final class RouterAdvertisementDaemonTest { 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)); } }