1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.net.ip; 18 19 import static android.net.RouteInfo.RTN_UNICAST; 20 21 import static com.android.net.module.util.NetworkStackConstants.ETHER_HEADER_LEN; 22 import static com.android.net.module.util.NetworkStackConstants.ETHER_TYPE_IPV6; 23 import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ND_OPTION_MTU; 24 import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ND_OPTION_PIO; 25 import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ND_OPTION_RDNSS; 26 import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ND_OPTION_SLLA; 27 import static com.android.net.module.util.NetworkStackConstants.ICMPV6_RA_HEADER_LEN; 28 import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ROUTER_ADVERTISEMENT; 29 import static com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_ALL_NODES_MULTICAST; 30 import static com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_LEN; 31 import static com.android.net.module.util.NetworkStackConstants.IPV6_HEADER_LEN; 32 import static com.android.net.module.util.NetworkStackConstants.PIO_FLAG_AUTONOMOUS; 33 import static com.android.net.module.util.NetworkStackConstants.PIO_FLAG_ON_LINK; 34 35 import static org.junit.Assert.assertEquals; 36 import static org.junit.Assert.assertNotNull; 37 import static org.junit.Assert.assertTrue; 38 import static org.junit.Assert.fail; 39 40 import android.app.Instrumentation; 41 import android.content.Context; 42 import android.net.INetd; 43 import android.net.IpPrefix; 44 import android.net.MacAddress; 45 import android.net.RouteInfo; 46 import android.net.ip.RouterAdvertisementDaemon.RaParams; 47 import android.os.Handler; 48 import android.os.HandlerThread; 49 import android.os.IBinder; 50 import android.os.Looper; 51 52 import androidx.test.InstrumentationRegistry; 53 import androidx.test.filters.SmallTest; 54 import androidx.test.runner.AndroidJUnit4; 55 56 import com.android.net.module.util.InterfaceParams; 57 import com.android.net.module.util.Ipv6Utils; 58 import com.android.net.module.util.NetdUtils; 59 import com.android.net.module.util.Struct; 60 import com.android.net.module.util.structs.EthernetHeader; 61 import com.android.net.module.util.structs.Icmpv6Header; 62 import com.android.net.module.util.structs.Ipv6Header; 63 import com.android.net.module.util.structs.LlaOption; 64 import com.android.net.module.util.structs.MtuOption; 65 import com.android.net.module.util.structs.PrefixInformationOption; 66 import com.android.net.module.util.structs.RaHeader; 67 import com.android.net.module.util.structs.RdnssOption; 68 import com.android.testutils.TapPacketReader; 69 import com.android.testutils.TapPacketReaderRule; 70 71 import org.junit.After; 72 import org.junit.Before; 73 import org.junit.BeforeClass; 74 import org.junit.Rule; 75 import org.junit.Test; 76 import org.junit.runner.RunWith; 77 import org.mockito.MockitoAnnotations; 78 79 import java.net.Inet6Address; 80 import java.net.InetAddress; 81 import java.nio.ByteBuffer; 82 import java.util.HashSet; 83 import java.util.List; 84 85 @RunWith(AndroidJUnit4.class) 86 @SmallTest 87 public final class RouterAdvertisementDaemonTest { 88 private static final String TAG = RouterAdvertisementDaemonTest.class.getSimpleName(); 89 private static final int DATA_BUFFER_LEN = 4096; 90 private static final int PACKET_TIMEOUT_MS = 5_000; 91 92 @Rule 93 public final TapPacketReaderRule mTetheredReader = new TapPacketReaderRule( 94 DATA_BUFFER_LEN, false /* autoStart */); 95 96 private InterfaceParams mTetheredParams; 97 private HandlerThread mHandlerThread; 98 private Handler mHandler; 99 private TapPacketReader mTetheredPacketReader; 100 private RouterAdvertisementDaemon mRaDaemon; 101 102 private static INetd sNetd; 103 104 @BeforeClass setupOnce()105 public static void setupOnce() { 106 final Instrumentation inst = InstrumentationRegistry.getInstrumentation(); 107 final IBinder netdIBinder = 108 (IBinder) inst.getContext().getSystemService(Context.NETD_SERVICE); 109 sNetd = INetd.Stub.asInterface(netdIBinder); 110 } 111 112 @Before setUp()113 public void setUp() throws Exception { 114 MockitoAnnotations.initMocks(this); 115 116 mHandlerThread = new HandlerThread(getClass().getSimpleName()); 117 mHandlerThread.start(); 118 mHandler = new Handler(mHandlerThread.getLooper()); 119 120 setupTapInterfaces(); 121 122 // Looper must be prepared here since AndroidJUnitRunner runs tests on separate threads. 123 if (Looper.myLooper() == null) Looper.prepare(); 124 125 mRaDaemon = new RouterAdvertisementDaemon(mTetheredParams); 126 sNetd.networkAddInterface(INetd.LOCAL_NET_ID, mTetheredParams.name); 127 } 128 129 @After tearDown()130 public void tearDown() throws Exception { 131 mTetheredReader.stop(); 132 if (mHandlerThread != null) { 133 mHandlerThread.quitSafely(); 134 mHandlerThread.join(PACKET_TIMEOUT_MS); 135 } 136 137 if (mTetheredParams != null) { 138 sNetd.networkRemoveInterface(INetd.LOCAL_NET_ID, mTetheredParams.name); 139 } 140 } 141 setupTapInterfaces()142 private void setupTapInterfaces() { 143 // Create tethered test iface. 144 mTetheredReader.start(mHandler); 145 mTetheredParams = InterfaceParams.getByName(mTetheredReader.iface.getInterfaceName()); 146 assertNotNull(mTetheredParams); 147 mTetheredPacketReader = mTetheredReader.getReader(); 148 mHandler.post(mTetheredPacketReader::start); 149 } 150 151 private class TestRaPacket { 152 final RaParams mNewParams, mOldParams; 153 TestRaPacket(final RaParams oldParams, final RaParams newParams)154 TestRaPacket(final RaParams oldParams, final RaParams newParams) { 155 mOldParams = oldParams; 156 mNewParams = newParams; 157 } 158 isPacketMatched(final byte[] pkt, boolean multicast)159 public boolean isPacketMatched(final byte[] pkt, boolean multicast) throws Exception { 160 if (pkt.length < (ETHER_HEADER_LEN + IPV6_HEADER_LEN + ICMPV6_RA_HEADER_LEN)) { 161 return false; 162 } 163 final ByteBuffer buf = ByteBuffer.wrap(pkt); 164 165 // Parse Ethernet header 166 final EthernetHeader ethHdr = Struct.parse(EthernetHeader.class, buf); 167 if (ethHdr.etherType != ETHER_TYPE_IPV6) return false; 168 169 // Parse IPv6 header 170 final Ipv6Header ipv6Hdr = Struct.parse(Ipv6Header.class, buf); 171 assertEquals((ipv6Hdr.vtf >> 28), 6 /* ip version*/); 172 173 final int payLoadLength = pkt.length - ETHER_HEADER_LEN - IPV6_HEADER_LEN; 174 assertEquals(payLoadLength, ipv6Hdr.payloadLength); 175 176 // Parse ICMPv6 header 177 final Icmpv6Header icmpv6Hdr = Struct.parse(Icmpv6Header.class, buf); 178 if (icmpv6Hdr.type != (short) ICMPV6_ROUTER_ADVERTISEMENT) return false; 179 180 // Check whether IPv6 destination address is multicast or unicast 181 if (multicast) { 182 assertEquals(ipv6Hdr.dstIp, IPV6_ADDR_ALL_NODES_MULTICAST); 183 } else { 184 // The unicast IPv6 destination address in RA can be either link-local or global 185 // IPv6 address. This test only expects link-local address. 186 assertTrue(ipv6Hdr.dstIp.isLinkLocalAddress()); 187 } 188 189 // Parse RA header 190 final RaHeader raHdr = Struct.parse(RaHeader.class, buf); 191 assertEquals(mNewParams.hopLimit, raHdr.hopLimit); 192 193 while (buf.position() < pkt.length) { 194 final int currentPos = buf.position(); 195 final int type = Byte.toUnsignedInt(buf.get()); 196 final int length = Byte.toUnsignedInt(buf.get()); 197 switch (type) { 198 case ICMPV6_ND_OPTION_PIO: 199 // length is 4 because this test only expects one PIO included in the 200 // router advertisement packet. 201 assertEquals(4, length); 202 203 final ByteBuffer pioBuf = ByteBuffer.wrap(buf.array(), currentPos, 204 Struct.getSize(PrefixInformationOption.class)); 205 final PrefixInformationOption pio = 206 Struct.parse(PrefixInformationOption.class, pioBuf); 207 assertEquals((byte) (PIO_FLAG_ON_LINK | PIO_FLAG_AUTONOMOUS), pio.flags); 208 209 final InetAddress address = InetAddress.getByAddress(pio.prefix); 210 final IpPrefix prefix = new IpPrefix(address, pio.prefixLen); 211 if (mNewParams.prefixes.contains(prefix)) { 212 assertTrue(pio.validLifetime > 0); 213 assertTrue(pio.preferredLifetime > 0); 214 } else if (mOldParams != null && mOldParams.prefixes.contains(prefix)) { 215 assertEquals(0, pio.validLifetime); 216 assertEquals(0, pio.preferredLifetime); 217 } else { 218 fail("Unexpected prefix: " + prefix); 219 } 220 221 // Move ByteBuffer position to the next option. 222 buf.position(currentPos + Struct.getSize(PrefixInformationOption.class)); 223 break; 224 case ICMPV6_ND_OPTION_MTU: 225 assertEquals(1, length); 226 227 final ByteBuffer mtuBuf = ByteBuffer.wrap(buf.array(), currentPos, 228 Struct.getSize(MtuOption.class)); 229 final MtuOption mtu = Struct.parse(MtuOption.class, mtuBuf); 230 assertEquals(mNewParams.mtu, mtu.mtu); 231 232 // Move ByteBuffer position to the next option. 233 buf.position(currentPos + Struct.getSize(MtuOption.class)); 234 break; 235 case ICMPV6_ND_OPTION_RDNSS: 236 final int rdnssHeaderLen = Struct.getSize(RdnssOption.class); 237 final ByteBuffer RdnssBuf = ByteBuffer.wrap(buf.array(), currentPos, 238 rdnssHeaderLen); 239 final RdnssOption rdnss = Struct.parse(RdnssOption.class, RdnssBuf); 240 final String msg = 241 rdnss.lifetime > 0 ? "Unknown dns" : "Unknown deprecated dns"; 242 final HashSet<Inet6Address> dnses = 243 rdnss.lifetime > 0 ? mNewParams.dnses : mOldParams.dnses; 244 assertNotNull(msg, dnses); 245 246 // Check DNS servers included in this option. 247 buf.position(currentPos + rdnssHeaderLen); // skip the rdnss option header 248 final int numOfDnses = (length - 1) / 2; 249 for (int i = 0; i < numOfDnses; i++) { 250 byte[] rawAddress = new byte[IPV6_ADDR_LEN]; 251 buf.get(rawAddress); 252 final Inet6Address dns = 253 (Inet6Address) InetAddress.getByAddress(rawAddress); 254 if (!dnses.contains(dns)) fail("Unexpected dns: " + dns); 255 } 256 // Unnecessary to move ByteBuffer position here, since the position has been 257 // moved forward correctly after reading DNS servers from ByteBuffer. 258 break; 259 case ICMPV6_ND_OPTION_SLLA: 260 // Do nothing, just move ByteBuffer position to the next option. 261 buf.position(currentPos + Struct.getSize(LlaOption.class)); 262 break; 263 default: 264 fail("Unknown RA option type " + type); 265 } 266 } 267 return true; 268 } 269 } 270 createRaParams(final String ipv6Address)271 private RaParams createRaParams(final String ipv6Address) throws Exception { 272 final RaParams params = new RaParams(); 273 final Inet6Address address = (Inet6Address) InetAddress.getByName(ipv6Address); 274 params.dnses.add(address); 275 params.prefixes.add(new IpPrefix(address, 64)); 276 277 return params; 278 } 279 isRaPacket(final TestRaPacket testRa, boolean multicast)280 private boolean isRaPacket(final TestRaPacket testRa, boolean multicast) throws Exception { 281 byte[] packet; 282 while ((packet = mTetheredPacketReader.poll(PACKET_TIMEOUT_MS)) != null) { 283 if (testRa.isPacketMatched(packet, multicast)) { 284 return true; 285 } 286 } 287 return false; 288 } 289 assertUnicastRaPacket(final TestRaPacket testRa)290 private void assertUnicastRaPacket(final TestRaPacket testRa) throws Exception { 291 assertTrue(isRaPacket(testRa, false /* multicast */)); 292 } 293 assertMulticastRaPacket(final TestRaPacket testRa)294 private void assertMulticastRaPacket(final TestRaPacket testRa) throws Exception { 295 assertTrue(isRaPacket(testRa, true /* multicast */)); 296 } 297 createRsPacket(final String srcIp)298 private ByteBuffer createRsPacket(final String srcIp) throws Exception { 299 final MacAddress dstMac = MacAddress.fromString("33:33:03:04:05:06"); 300 final MacAddress srcMac = mTetheredParams.macAddr; 301 final ByteBuffer slla = LlaOption.build((byte) ICMPV6_ND_OPTION_SLLA, srcMac); 302 303 return Ipv6Utils.buildRsPacket(srcMac, dstMac, (Inet6Address) InetAddress.getByName(srcIp), 304 IPV6_ADDR_ALL_NODES_MULTICAST, slla); 305 } 306 307 @Test testUnSolicitRouterAdvertisement()308 public void testUnSolicitRouterAdvertisement() throws Exception { 309 assertTrue(mRaDaemon.start()); 310 final RaParams params1 = createRaParams("2001:1122:3344::5566"); 311 mRaDaemon.buildNewRa(null, params1); 312 assertMulticastRaPacket(new TestRaPacket(null, params1)); 313 314 final RaParams params2 = createRaParams("2006:3344:5566::7788"); 315 mRaDaemon.buildNewRa(params1, params2); 316 assertMulticastRaPacket(new TestRaPacket(params1, params2)); 317 } 318 319 @Test testSolicitRouterAdvertisement()320 public void testSolicitRouterAdvertisement() throws Exception { 321 // Enable IPv6 forwarding is necessary, which makes kernel process RS correctly and 322 // create the neighbor entry for peer's link-layer address and IPv6 address. Otherwise, 323 // when device receives RS with IPv6 link-local address as source address, it has to 324 // initiate the address resolution first before responding the unicast RA. 325 sNetd.setProcSysNet(INetd.IPV6, INetd.CONF, mTetheredParams.name, "forwarding", "1"); 326 327 assertTrue(mRaDaemon.start()); 328 final RaParams params1 = createRaParams("2001:1122:3344::5566"); 329 mRaDaemon.buildNewRa(null, params1); 330 assertMulticastRaPacket(new TestRaPacket(null, params1)); 331 332 // Add a default route "fe80::/64 -> ::" to local network, otherwise, device will fail to 333 // send the unicast RA out due to the ENETUNREACH error(No route to the peer's link-local 334 // address is present). 335 final String iface = mTetheredParams.name; 336 final RouteInfo linkLocalRoute = 337 new RouteInfo(new IpPrefix("fe80::/64"), null, iface, RTN_UNICAST); 338 NetdUtils.addRoutesToLocalNetwork(sNetd, iface, List.of(linkLocalRoute)); 339 340 final ByteBuffer rs = createRsPacket("fe80::1122:3344:5566:7788"); 341 mTetheredPacketReader.sendResponse(rs); 342 assertUnicastRaPacket(new TestRaPacket(null, params1)); 343 } 344 } 345