• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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