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.system.OsConstants.IPPROTO_ICMPV6; 20 21 import static com.android.net.module.util.IpUtils.icmpv6Checksum; 22 import static com.android.net.module.util.NetworkStackConstants.ETHER_SRC_ADDR_OFFSET; 23 import static com.android.networkstack.tethering.util.TetheringUtils.getTetheringJniLibraryName; 24 25 import static org.junit.Assert.assertEquals; 26 import static org.junit.Assert.assertNotNull; 27 28 import android.app.Instrumentation; 29 import android.content.Context; 30 import android.net.INetd; 31 import android.net.InetAddresses; 32 import android.net.MacAddress; 33 import android.os.Build; 34 import android.os.Handler; 35 import android.os.HandlerThread; 36 import android.os.IBinder; 37 import android.os.Looper; 38 39 import androidx.test.InstrumentationRegistry; 40 import androidx.test.filters.SmallTest; 41 42 import com.android.net.module.util.InterfaceParams; 43 import com.android.networkstack.tethering.util.TetheringUtils; 44 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; 45 import com.android.testutils.DevSdkIgnoreRunner; 46 import com.android.testutils.TapPacketReader; 47 import com.android.testutils.TapPacketReaderRule; 48 49 import org.junit.After; 50 import org.junit.Before; 51 import org.junit.BeforeClass; 52 import org.junit.Rule; 53 import org.junit.Test; 54 import org.junit.runner.RunWith; 55 import org.mockito.MockitoAnnotations; 56 57 import java.io.IOException; 58 import java.nio.ByteBuffer; 59 60 @RunWith(DevSdkIgnoreRunner.class) 61 @IgnoreUpTo(Build.VERSION_CODES.R) 62 @SmallTest 63 public class DadProxyTest { 64 private static final int DATA_BUFFER_LEN = 4096; 65 private static final int PACKET_TIMEOUT_MS = 2_000; // Long enough for DAD to succeed. 66 67 // Start the readers manually on a common handler shared with DadProxy, for simplicity 68 @Rule 69 public final TapPacketReaderRule mUpstreamReader = new TapPacketReaderRule( 70 DATA_BUFFER_LEN, false /* autoStart */); 71 @Rule 72 public final TapPacketReaderRule mTetheredReader = new TapPacketReaderRule( 73 DATA_BUFFER_LEN, false /* autoStart */); 74 75 private InterfaceParams mUpstreamParams, mTetheredParams; 76 private HandlerThread mHandlerThread; 77 private Handler mHandler; 78 private TapPacketReader mUpstreamPacketReader, mTetheredPacketReader; 79 80 private static INetd sNetd; 81 82 @BeforeClass setupOnce()83 public static void setupOnce() { 84 System.loadLibrary(getTetheringJniLibraryName()); 85 86 final Instrumentation inst = InstrumentationRegistry.getInstrumentation(); 87 final IBinder netdIBinder = 88 (IBinder) inst.getContext().getSystemService(Context.NETD_SERVICE); 89 sNetd = INetd.Stub.asInterface(netdIBinder); 90 } 91 92 @Before setUp()93 public void setUp() throws Exception { 94 MockitoAnnotations.initMocks(this); 95 96 mHandlerThread = new HandlerThread(getClass().getSimpleName()); 97 mHandlerThread.start(); 98 mHandler = new Handler(mHandlerThread.getLooper()); 99 100 setupTapInterfaces(); 101 102 // Looper must be prepared here since AndroidJUnitRunner runs tests on separate threads. 103 if (Looper.myLooper() == null) Looper.prepare(); 104 105 DadProxy mProxy = setupProxy(); 106 } 107 108 @After tearDown()109 public void tearDown() throws Exception { 110 mUpstreamReader.stop(); 111 mTetheredReader.stop(); 112 113 if (mHandlerThread != null) { 114 mHandlerThread.quitSafely(); 115 mHandlerThread.join(PACKET_TIMEOUT_MS); 116 } 117 118 if (mTetheredParams != null) { 119 sNetd.networkRemoveInterface(INetd.LOCAL_NET_ID, mTetheredParams.name); 120 } 121 if (mUpstreamParams != null) { 122 sNetd.networkRemoveInterface(INetd.LOCAL_NET_ID, mUpstreamParams.name); 123 } 124 } 125 setupTapInterfaces()126 private void setupTapInterfaces() throws Exception { 127 // Create upstream test iface. 128 mUpstreamReader.start(mHandler); 129 final String upstreamIface = mUpstreamReader.iface.getInterfaceName(); 130 mUpstreamParams = InterfaceParams.getByName(upstreamIface); 131 assertNotNull(mUpstreamParams); 132 mUpstreamPacketReader = mUpstreamReader.getReader(); 133 134 // Create tethered test iface. 135 mTetheredReader.start(mHandler); 136 final String tetheredIface = mTetheredReader.getIface().getInterfaceName(); 137 mTetheredParams = InterfaceParams.getByName(tetheredIface); 138 assertNotNull(mTetheredParams); 139 mTetheredPacketReader = mTetheredReader.getReader(); 140 } 141 142 private static final int IPV6_HEADER_LEN = 40; 143 private static final int ETH_HEADER_LEN = 14; 144 private static final int ICMPV6_NA_NS_LEN = 24; 145 private static final int LL_TARGET_OPTION_LEN = 8; 146 private static final int ICMPV6_CHECKSUM_OFFSET = 2; 147 private static final int ETHER_TYPE_IPV6 = 0x86dd; 148 createDadPacket(int type)149 private static ByteBuffer createDadPacket(int type) { 150 // Refer to buildArpPacket() 151 int icmpLen = ICMPV6_NA_NS_LEN 152 + (type == NeighborPacketForwarder.ICMPV6_NEIGHBOR_ADVERTISEMENT 153 ? LL_TARGET_OPTION_LEN : 0); 154 final ByteBuffer buf = ByteBuffer.allocate(icmpLen + IPV6_HEADER_LEN + ETH_HEADER_LEN); 155 156 // Ethernet header. 157 final MacAddress srcMac = MacAddress.fromString("33:33:ff:66:77:88"); 158 buf.put(srcMac.toByteArray()); 159 final MacAddress dstMac = MacAddress.fromString("01:02:03:04:05:06"); 160 buf.put(dstMac.toByteArray()); 161 buf.putShort((short) ETHER_TYPE_IPV6); 162 163 // IPv6 header 164 byte[] version = {(byte) 0x60, 0x00, 0x00, 0x00}; 165 buf.put(version); // Version 166 buf.putShort((byte) icmpLen); // Length 167 buf.put((byte) IPPROTO_ICMPV6); // Next header 168 buf.put((byte) 0xff); // Hop limit 169 170 final byte[] target = 171 InetAddresses.parseNumericAddress("fe80::1122:3344:5566:7788").getAddress(); 172 final byte[] src; 173 final byte[] dst; 174 if (type == NeighborPacketForwarder.ICMPV6_NEIGHBOR_SOLICITATION) { 175 src = InetAddresses.parseNumericAddress("::").getAddress(); 176 dst = InetAddresses.parseNumericAddress("ff02::1:ff66:7788").getAddress(); 177 } else { 178 src = target; 179 dst = TetheringUtils.ALL_NODES; 180 } 181 buf.put(src); 182 buf.put(dst); 183 184 // ICMPv6 Header 185 buf.put((byte) type); // Type 186 buf.put((byte) 0x00); // Code 187 buf.putShort((short) 0); // Checksum 188 buf.putInt(0); // Reserved 189 buf.put(target); 190 191 if (type == NeighborPacketForwarder.ICMPV6_NEIGHBOR_ADVERTISEMENT) { 192 //NA packet has LL target address 193 //ICMPv6 Option 194 buf.put((byte) 0x02); // Type 195 buf.put((byte) 0x01); // Length 196 byte[] ll_target = MacAddress.fromString("01:02:03:04:05:06").toByteArray(); 197 buf.put(ll_target); 198 } 199 200 // Populate checksum field 201 final int transportOffset = ETH_HEADER_LEN + IPV6_HEADER_LEN; 202 final short checksum = icmpv6Checksum(buf, ETH_HEADER_LEN, transportOffset, icmpLen); 203 buf.putShort(transportOffset + ICMPV6_CHECKSUM_OFFSET, checksum); 204 205 buf.flip(); 206 return buf; 207 } 208 setupProxy()209 private DadProxy setupProxy() throws Exception { 210 DadProxy proxy = new DadProxy(mHandler, mTetheredParams); 211 mHandler.post(() -> proxy.setUpstreamIface(mUpstreamParams)); 212 213 // Upstream iface is added to local network to simplify test case. 214 // Otherwise the test needs to create and destroy a network for the upstream iface. 215 sNetd.networkAddInterface(INetd.LOCAL_NET_ID, mUpstreamParams.name); 216 sNetd.networkAddInterface(INetd.LOCAL_NET_ID, mTetheredParams.name); 217 218 return proxy; 219 } 220 221 // TODO: change to assert. waitForPacket(ByteBuffer packet, TapPacketReader reader)222 private boolean waitForPacket(ByteBuffer packet, TapPacketReader reader) { 223 byte[] p; 224 225 while ((p = reader.popPacket(PACKET_TIMEOUT_MS)) != null) { 226 final ByteBuffer buffer = ByteBuffer.wrap(p); 227 228 if (buffer.compareTo(packet) == 0) return true; 229 } 230 return false; 231 } 232 copy(ByteBuffer buf)233 private ByteBuffer copy(ByteBuffer buf) { 234 // There does not seem to be a way to copy ByteBuffers. ByteBuffer does not implement 235 // clone() and duplicate() copies the metadata but shares the contents. 236 return ByteBuffer.wrap(buf.array().clone()); 237 } 238 updateDstMac(ByteBuffer buf, MacAddress mac)239 private void updateDstMac(ByteBuffer buf, MacAddress mac) { 240 buf.put(mac.toByteArray()); 241 buf.rewind(); 242 } updateSrcMac(ByteBuffer buf, InterfaceParams ifaceParams)243 private void updateSrcMac(ByteBuffer buf, InterfaceParams ifaceParams) { 244 buf.position(ETHER_SRC_ADDR_OFFSET); 245 buf.put(ifaceParams.macAddr.toByteArray()); 246 buf.rewind(); 247 } 248 receivePacketAndMaybeExpectForwarded(boolean expectForwarded, ByteBuffer in, TapPacketReader inReader, ByteBuffer out, TapPacketReader outReader)249 private void receivePacketAndMaybeExpectForwarded(boolean expectForwarded, 250 ByteBuffer in, TapPacketReader inReader, ByteBuffer out, TapPacketReader outReader) 251 throws IOException { 252 253 inReader.sendResponse(in); 254 if (waitForPacket(out, outReader)) return; 255 256 // When the test runs, DAD may be in progress, because the interface has just been created. 257 // If so, the DAD proxy will get EADDRNOTAVAIL when trying to send packets. It is not 258 // possible to work around this using IPV6_FREEBIND or IPV6_TRANSPARENT options because the 259 // kernel rawv6 code doesn't consider those options either when binding or when sending, and 260 // doesn't get the source address from the packet even in IPPROTO_RAW/HDRINCL mode (it only 261 // gets it from the socket or from cmsg). 262 // 263 // If DAD was in progress when the above was attempted, try again and expect the packet to 264 // be forwarded. Don't disable DAD in the test because if we did, the test would not notice 265 // if, for example, the DAD proxy code just crashed if it received EADDRNOTAVAIL. 266 final String msg = expectForwarded 267 ? "Did not receive expected packet even after waiting for DAD:" 268 : "Unexpectedly received packet:"; 269 270 inReader.sendResponse(in); 271 assertEquals(msg, expectForwarded, waitForPacket(out, outReader)); 272 } 273 receivePacketAndExpectForwarded(ByteBuffer in, TapPacketReader inReader, ByteBuffer out, TapPacketReader outReader)274 private void receivePacketAndExpectForwarded(ByteBuffer in, TapPacketReader inReader, 275 ByteBuffer out, TapPacketReader outReader) throws IOException { 276 receivePacketAndMaybeExpectForwarded(true, in, inReader, out, outReader); 277 } 278 receivePacketAndExpectNotForwarded(ByteBuffer in, TapPacketReader inReader, ByteBuffer out, TapPacketReader outReader)279 private void receivePacketAndExpectNotForwarded(ByteBuffer in, TapPacketReader inReader, 280 ByteBuffer out, TapPacketReader outReader) throws IOException { 281 receivePacketAndMaybeExpectForwarded(false, in, inReader, out, outReader); 282 } 283 284 @Test testNaForwardingFromUpstreamToTether()285 public void testNaForwardingFromUpstreamToTether() throws Exception { 286 ByteBuffer na = createDadPacket(NeighborPacketForwarder.ICMPV6_NEIGHBOR_ADVERTISEMENT); 287 288 ByteBuffer out = copy(na); 289 updateDstMac(out, MacAddress.fromString("33:33:00:00:00:01")); 290 updateSrcMac(out, mTetheredParams); 291 292 receivePacketAndExpectForwarded(na, mUpstreamPacketReader, out, mTetheredPacketReader); 293 } 294 295 @Test 296 // TODO: remove test once DAD works in both directions. testNaForwardingFromTetherToUpstream()297 public void testNaForwardingFromTetherToUpstream() throws Exception { 298 ByteBuffer na = createDadPacket(NeighborPacketForwarder.ICMPV6_NEIGHBOR_ADVERTISEMENT); 299 300 ByteBuffer out = copy(na); 301 updateDstMac(out, MacAddress.fromString("33:33:00:00:00:01")); 302 updateSrcMac(out, mTetheredParams); 303 304 receivePacketAndExpectNotForwarded(na, mTetheredPacketReader, out, mUpstreamPacketReader); 305 } 306 307 @Test testNsForwardingFromTetherToUpstream()308 public void testNsForwardingFromTetherToUpstream() throws Exception { 309 ByteBuffer ns = createDadPacket(NeighborPacketForwarder.ICMPV6_NEIGHBOR_SOLICITATION); 310 311 ByteBuffer out = copy(ns); 312 updateSrcMac(out, mUpstreamParams); 313 314 receivePacketAndExpectForwarded(ns, mTetheredPacketReader, out, mUpstreamPacketReader); 315 } 316 317 @Test 318 // TODO: remove test once DAD works in both directions. testNsForwardingFromUpstreamToTether()319 public void testNsForwardingFromUpstreamToTether() throws Exception { 320 ByteBuffer ns = createDadPacket(NeighborPacketForwarder.ICMPV6_NEIGHBOR_SOLICITATION); 321 322 ByteBuffer out = copy(ns); 323 updateSrcMac(ns, mUpstreamParams); 324 325 receivePacketAndExpectNotForwarded(ns, mUpstreamPacketReader, out, mTetheredPacketReader); 326 } 327 } 328