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