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