1 /* 2 * Copyright (C) 2019 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.dhcp.DhcpClient.EXPIRED_LEASE; 20 import static android.net.dhcp.DhcpPacket.DHCP_BOOTREQUEST; 21 import static android.net.dhcp.DhcpPacket.DHCP_CLIENT; 22 import static android.net.dhcp.DhcpPacket.DHCP_MAGIC_COOKIE; 23 import static android.net.dhcp.DhcpPacket.DHCP_SERVER; 24 import static android.net.dhcp.DhcpPacket.ENCAP_L2; 25 import static android.net.dhcp.DhcpPacket.INADDR_BROADCAST; 26 import static android.net.dhcp.DhcpPacket.INFINITE_LEASE; 27 import static android.net.dhcp.DhcpResultsParcelableUtil.fromStableParcelable; 28 import static android.net.ipmemorystore.Status.SUCCESS; 29 import static android.system.OsConstants.ETH_P_IPV6; 30 import static android.system.OsConstants.IFA_F_TEMPORARY; 31 import static android.system.OsConstants.IPPROTO_ICMPV6; 32 import static android.system.OsConstants.IPPROTO_TCP; 33 34 import static com.android.net.module.util.Inet4AddressUtils.getBroadcastAddress; 35 import static com.android.net.module.util.Inet4AddressUtils.getPrefixMaskAsInet4Address; 36 import static com.android.server.util.NetworkStackConstants.ARP_REPLY; 37 import static com.android.server.util.NetworkStackConstants.ARP_REQUEST; 38 import static com.android.server.util.NetworkStackConstants.ETHER_ADDR_LEN; 39 import static com.android.server.util.NetworkStackConstants.ETHER_HEADER_LEN; 40 import static com.android.server.util.NetworkStackConstants.ETHER_TYPE_IPV6; 41 import static com.android.server.util.NetworkStackConstants.ETHER_TYPE_OFFSET; 42 import static com.android.server.util.NetworkStackConstants.ICMPV6_CHECKSUM_OFFSET; 43 import static com.android.server.util.NetworkStackConstants.ICMPV6_ND_OPTION_LENGTH_SCALING_FACTOR; 44 import static com.android.server.util.NetworkStackConstants.ICMPV6_ND_OPTION_PIO; 45 import static com.android.server.util.NetworkStackConstants.ICMPV6_ND_OPTION_RDNSS; 46 import static com.android.server.util.NetworkStackConstants.ICMPV6_RA_HEADER_LEN; 47 import static com.android.server.util.NetworkStackConstants.ICMPV6_ROUTER_ADVERTISEMENT; 48 import static com.android.server.util.NetworkStackConstants.ICMPV6_ROUTER_SOLICITATION; 49 import static com.android.server.util.NetworkStackConstants.IPV6_HEADER_LEN; 50 import static com.android.server.util.NetworkStackConstants.IPV6_LEN_OFFSET; 51 import static com.android.server.util.NetworkStackConstants.IPV6_PROTOCOL_OFFSET; 52 53 import static junit.framework.Assert.fail; 54 55 import static org.junit.Assert.assertArrayEquals; 56 import static org.junit.Assert.assertEquals; 57 import static org.junit.Assert.assertFalse; 58 import static org.junit.Assert.assertNotEquals; 59 import static org.junit.Assert.assertNotNull; 60 import static org.junit.Assert.assertNull; 61 import static org.junit.Assert.assertTrue; 62 import static org.junit.Assume.assumeFalse; 63 import static org.junit.Assume.assumeTrue; 64 import static org.mockito.ArgumentMatchers.anyInt; 65 import static org.mockito.ArgumentMatchers.contains; 66 import static org.mockito.ArgumentMatchers.longThat; 67 import static org.mockito.Mockito.any; 68 import static org.mockito.Mockito.argThat; 69 import static org.mockito.Mockito.doAnswer; 70 import static org.mockito.Mockito.doThrow; 71 import static org.mockito.Mockito.eq; 72 import static org.mockito.Mockito.inOrder; 73 import static org.mockito.Mockito.never; 74 import static org.mockito.Mockito.reset; 75 import static org.mockito.Mockito.spy; 76 import static org.mockito.Mockito.timeout; 77 import static org.mockito.Mockito.times; 78 import static org.mockito.Mockito.verify; 79 import static org.mockito.Mockito.when; 80 81 import android.app.AlarmManager; 82 import android.app.AlarmManager.OnAlarmListener; 83 import android.app.Instrumentation; 84 import android.content.ContentResolver; 85 import android.content.Context; 86 import android.content.res.Resources; 87 import android.net.ConnectivityManager; 88 import android.net.DhcpResults; 89 import android.net.DhcpResultsParcelable; 90 import android.net.INetd; 91 import android.net.InetAddresses; 92 import android.net.InterfaceConfigurationParcel; 93 import android.net.IpPrefix; 94 import android.net.Layer2InformationParcelable; 95 import android.net.Layer2PacketParcelable; 96 import android.net.LinkAddress; 97 import android.net.LinkProperties; 98 import android.net.MacAddress; 99 import android.net.NetworkStackIpMemoryStore; 100 import android.net.TestNetworkInterface; 101 import android.net.TestNetworkManager; 102 import android.net.Uri; 103 import android.net.dhcp.DhcpClient; 104 import android.net.dhcp.DhcpDeclinePacket; 105 import android.net.dhcp.DhcpDiscoverPacket; 106 import android.net.dhcp.DhcpPacket; 107 import android.net.dhcp.DhcpPacket.ParseException; 108 import android.net.dhcp.DhcpRequestPacket; 109 import android.net.ipmemorystore.NetworkAttributes; 110 import android.net.ipmemorystore.OnNetworkAttributesRetrievedListener; 111 import android.net.ipmemorystore.Status; 112 import android.net.netlink.StructNdOptPref64; 113 import android.net.shared.Layer2Information; 114 import android.net.shared.ProvisioningConfiguration; 115 import android.net.shared.ProvisioningConfiguration.ScanResultInfo; 116 import android.net.util.InterfaceParams; 117 import android.net.util.IpUtils; 118 import android.net.util.NetworkStackUtils; 119 import android.os.Build; 120 import android.os.Handler; 121 import android.os.HandlerThread; 122 import android.os.IBinder; 123 import android.os.PowerManager; 124 import android.os.RemoteException; 125 import android.os.SystemClock; 126 import android.os.SystemProperties; 127 import android.system.ErrnoException; 128 import android.system.Os; 129 130 import androidx.test.InstrumentationRegistry; 131 import androidx.test.filters.SmallTest; 132 import androidx.test.runner.AndroidJUnit4; 133 134 import com.android.internal.util.StateMachine; 135 import com.android.networkstack.apishim.CaptivePortalDataShimImpl; 136 import com.android.networkstack.apishim.ConstantsShim; 137 import com.android.networkstack.apishim.common.ShimUtils; 138 import com.android.networkstack.arp.ArpPacket; 139 import com.android.networkstack.metrics.IpProvisioningMetrics; 140 import com.android.server.NetworkObserver; 141 import com.android.server.NetworkObserverRegistry; 142 import com.android.server.NetworkStackService.NetworkStackServiceManager; 143 import com.android.server.connectivity.ipmemorystore.IpMemoryStoreService; 144 import com.android.testutils.DevSdkIgnoreRule; 145 import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter; 146 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; 147 import com.android.testutils.HandlerUtilsKt; 148 import com.android.testutils.TapPacketReader; 149 150 import org.junit.After; 151 import org.junit.Before; 152 import org.junit.Rule; 153 import org.junit.Test; 154 import org.junit.runner.RunWith; 155 import org.mockito.ArgumentCaptor; 156 import org.mockito.InOrder; 157 import org.mockito.Mock; 158 import org.mockito.MockitoAnnotations; 159 import org.mockito.Spy; 160 161 import java.io.FileDescriptor; 162 import java.io.IOException; 163 import java.net.Inet4Address; 164 import java.net.InetAddress; 165 import java.net.NetworkInterface; 166 import java.nio.ByteBuffer; 167 import java.util.ArrayList; 168 import java.util.Arrays; 169 import java.util.Collection; 170 import java.util.Collections; 171 import java.util.HashMap; 172 import java.util.List; 173 import java.util.Objects; 174 import java.util.Random; 175 import java.util.concurrent.CompletableFuture; 176 import java.util.concurrent.CountDownLatch; 177 import java.util.concurrent.TimeUnit; 178 import java.util.concurrent.atomic.AtomicReference; 179 import java.util.function.Predicate; 180 181 /** 182 * Tests for IpClient. 183 */ 184 @RunWith(AndroidJUnit4.class) 185 @SmallTest 186 public class IpClientIntegrationTest { 187 private static final int DATA_BUFFER_LEN = 4096; 188 private static final int PACKET_TIMEOUT_MS = 5_000; 189 private static final int TEST_TIMEOUT_MS = 400; 190 private static final String TEST_L2KEY = "some l2key"; 191 private static final String TEST_CLUSTER = "some cluster"; 192 private static final int TEST_LEASE_DURATION_S = 3_600; // 1 hour 193 194 // TODO: move to NetlinkConstants, NetworkStackConstants, or OsConstants. 195 private static final int IFA_F_STABLE_PRIVACY = 0x800; 196 197 @Rule 198 public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule(); 199 200 @Mock private Context mContext; 201 @Mock private ConnectivityManager mCm; 202 @Mock private Resources mResources; 203 @Mock private IIpClientCallbacks mCb; 204 @Mock private AlarmManager mAlarm; 205 @Mock private ContentResolver mContentResolver; 206 @Mock private NetworkStackServiceManager mNetworkStackServiceManager; 207 @Mock private NetworkStackIpMemoryStore mIpMemoryStore; 208 @Mock private IpMemoryStoreService mIpMemoryStoreService; 209 @Mock private PowerManager.WakeLock mTimeoutWakeLock; 210 211 @Spy private INetd mNetd; 212 213 private String mIfaceName; 214 private NetworkObserverRegistry mNetworkObserverRegistry; 215 private HandlerThread mPacketReaderThread; 216 private Handler mHandler; 217 private TapPacketReader mPacketReader; 218 private FileDescriptor mTapFd; 219 private IpClient mIpc; 220 private Dependencies mDependencies; 221 private byte[] mClientMac; 222 223 // Ethernet header 224 private static final int ETH_HEADER_LEN = 14; 225 226 // IP header 227 private static final int IPV4_HEADER_LEN = 20; 228 private static final int IPV4_SRC_ADDR_OFFSET = ETH_HEADER_LEN + 12; 229 private static final int IPV4_DST_ADDR_OFFSET = IPV4_SRC_ADDR_OFFSET + 4; 230 231 // UDP header 232 private static final int UDP_HEADER_LEN = 8; 233 private static final int UDP_HEADER_OFFSET = ETH_HEADER_LEN + IPV4_HEADER_LEN; 234 private static final int UDP_SRC_PORT_OFFSET = UDP_HEADER_OFFSET + 0; 235 236 // DHCP header 237 private static final int DHCP_HEADER_OFFSET = ETH_HEADER_LEN + IPV4_HEADER_LEN 238 + UDP_HEADER_LEN; 239 private static final int DHCP_MESSAGE_OP_CODE_OFFSET = DHCP_HEADER_OFFSET + 0; 240 private static final int DHCP_TRANSACTION_ID_OFFSET = DHCP_HEADER_OFFSET + 4; 241 private static final int DHCP_OPTION_MAGIC_COOKIE_OFFSET = DHCP_HEADER_OFFSET + 236; 242 243 private static final Inet4Address SERVER_ADDR = 244 (Inet4Address) InetAddresses.parseNumericAddress("192.168.1.100"); 245 private static final Inet4Address CLIENT_ADDR = 246 (Inet4Address) InetAddresses.parseNumericAddress("192.168.1.2"); 247 private static final Inet4Address CLIENT_ADDR_NEW = 248 (Inet4Address) InetAddresses.parseNumericAddress("192.168.1.3"); 249 private static final Inet4Address INADDR_ANY = 250 (Inet4Address) InetAddresses.parseNumericAddress("0.0.0.0"); 251 private static final int PREFIX_LENGTH = 24; 252 private static final Inet4Address NETMASK = getPrefixMaskAsInet4Address(PREFIX_LENGTH); 253 private static final Inet4Address BROADCAST_ADDR = getBroadcastAddress( 254 SERVER_ADDR, PREFIX_LENGTH); 255 private static final String HOSTNAME = "testhostname"; 256 private static final int TEST_DEFAULT_MTU = 1500; 257 private static final int TEST_MIN_MTU = 1280; 258 private static final byte[] SERVER_MAC = new byte[] { 0x00, 0x1A, 0x11, 0x22, 0x33, 0x44 }; 259 private static final String TEST_HOST_NAME = "AOSP on Crosshatch"; 260 private static final String TEST_HOST_NAME_TRANSLITERATION = "AOSP-on-Crosshatch"; 261 private static final String TEST_CAPTIVE_PORTAL_URL = "https://example.com/capportapi"; 262 private static final byte[] TEST_HOTSPOT_OUI = new byte[] { 263 (byte) 0x00, (byte) 0x17, (byte) 0xF2 264 }; 265 private static final byte TEST_VENDOR_SPECIFIC_TYPE = 0x06; 266 267 private static final String TEST_DEFAULT_SSID = "test_ssid"; 268 private static final String TEST_DEFAULT_BSSID = "00:11:22:33:44:55"; 269 private static final String TEST_DHCP_ROAM_SSID = "0001docomo"; 270 private static final String TEST_DHCP_ROAM_BSSID = "00:4e:35:17:98:55"; 271 private static final String TEST_DHCP_ROAM_L2KEY = "roaming_l2key"; 272 private static final String TEST_DHCP_ROAM_CLUSTER = "roaming_cluster"; 273 private static final byte[] TEST_AP_OUI = new byte[] { 0x00, 0x1A, 0x11 }; 274 275 private class Dependencies extends IpClient.Dependencies { 276 private boolean mIsDhcpLeaseCacheEnabled; 277 private boolean mIsDhcpRapidCommitEnabled; 278 private boolean mIsDhcpIpConflictDetectEnabled; 279 // Can't use SparseIntArray, it doesn't have an easy way to know if a key is not present. 280 private HashMap<String, Integer> mIntConfigProperties = new HashMap<>(); 281 private DhcpClient mDhcpClient; 282 private boolean mIsHostnameConfigurationEnabled; 283 private String mHostname; 284 setDhcpLeaseCacheEnabled(final boolean enable)285 public void setDhcpLeaseCacheEnabled(final boolean enable) { 286 mIsDhcpLeaseCacheEnabled = enable; 287 } 288 setDhcpRapidCommitEnabled(final boolean enable)289 public void setDhcpRapidCommitEnabled(final boolean enable) { 290 mIsDhcpRapidCommitEnabled = enable; 291 } 292 setDhcpIpConflictDetectEnabled(final boolean enable)293 public void setDhcpIpConflictDetectEnabled(final boolean enable) { 294 mIsDhcpIpConflictDetectEnabled = enable; 295 } 296 setHostnameConfiguration(final boolean enable, final String hostname)297 public void setHostnameConfiguration(final boolean enable, final String hostname) { 298 mIsHostnameConfigurationEnabled = enable; 299 mHostname = hostname; 300 } 301 302 @Override getNetd(Context context)303 public INetd getNetd(Context context) { 304 return mNetd; 305 } 306 307 @Override getIpMemoryStore(Context context, NetworkStackServiceManager nssManager)308 public NetworkStackIpMemoryStore getIpMemoryStore(Context context, 309 NetworkStackServiceManager nssManager) { 310 return mIpMemoryStore; 311 } 312 313 @Override makeDhcpClient(Context context, StateMachine controller, InterfaceParams ifParams, DhcpClient.Dependencies deps)314 public DhcpClient makeDhcpClient(Context context, StateMachine controller, 315 InterfaceParams ifParams, DhcpClient.Dependencies deps) { 316 mDhcpClient = DhcpClient.makeDhcpClient(context, controller, ifParams, deps); 317 return mDhcpClient; 318 } 319 320 @Override getDhcpClientDependencies( NetworkStackIpMemoryStore ipMemoryStore, IpProvisioningMetrics metrics)321 public DhcpClient.Dependencies getDhcpClientDependencies( 322 NetworkStackIpMemoryStore ipMemoryStore, IpProvisioningMetrics metrics) { 323 return new DhcpClient.Dependencies(ipMemoryStore, metrics) { 324 @Override 325 public boolean isFeatureEnabled(final Context context, final String name, 326 final boolean defaultEnabled) { 327 switch (name) { 328 case NetworkStackUtils.DHCP_RAPID_COMMIT_VERSION: 329 return mIsDhcpRapidCommitEnabled; 330 case NetworkStackUtils.DHCP_INIT_REBOOT_VERSION: 331 return mIsDhcpLeaseCacheEnabled; 332 case NetworkStackUtils.DHCP_IP_CONFLICT_DETECT_VERSION: 333 return mIsDhcpIpConflictDetectEnabled; 334 default: 335 fail("Invalid experiment flag: " + name); 336 return false; 337 } 338 } 339 340 @Override 341 public int getIntDeviceConfig(final String name, int minimumValue, 342 int maximumValue, int defaultValue) { 343 return getDeviceConfigPropertyInt(name, 0 /* default value */); 344 } 345 346 @Override 347 public PowerManager.WakeLock getWakeLock(final PowerManager powerManager) { 348 return mTimeoutWakeLock; 349 } 350 351 @Override 352 public boolean getSendHostnameOption(final Context context) { 353 return mIsHostnameConfigurationEnabled; 354 } 355 356 @Override 357 public String getDeviceName(final Context context) { 358 return mIsHostnameConfigurationEnabled ? mHostname : null; 359 } 360 }; 361 } 362 363 @Override 364 public int getDeviceConfigPropertyInt(String name, int defaultValue) { 365 Integer value = mIntConfigProperties.get(name); 366 if (value == null) { 367 throw new IllegalStateException("Non-mocked device config property " + name); 368 } 369 return value; 370 } 371 372 public void setDeviceConfigProperty(String name, int value) { 373 mIntConfigProperties.put(name, value); 374 } 375 } 376 377 @Before 378 public void setUp() throws Exception { 379 MockitoAnnotations.initMocks(this); 380 381 mDependencies = new Dependencies(); 382 when(mContext.getSystemService(eq(Context.ALARM_SERVICE))).thenReturn(mAlarm); 383 when(mContext.getSystemService(eq(ConnectivityManager.class))).thenReturn(mCm); 384 when(mContext.getResources()).thenReturn(mResources); 385 when(mContext.getContentResolver()).thenReturn(mContentResolver); 386 when(mNetworkStackServiceManager.getIpMemoryStoreService()) 387 .thenReturn(mIpMemoryStoreService); 388 389 mDependencies.setDeviceConfigProperty(IpClient.CONFIG_MIN_RDNSS_LIFETIME, 67); 390 mDependencies.setDeviceConfigProperty(DhcpClient.DHCP_RESTART_CONFIG_DELAY, 10); 391 mDependencies.setDeviceConfigProperty(DhcpClient.ARP_FIRST_PROBE_DELAY_MS, 10); 392 mDependencies.setDeviceConfigProperty(DhcpClient.ARP_PROBE_MIN_MS, 10); 393 mDependencies.setDeviceConfigProperty(DhcpClient.ARP_PROBE_MAX_MS, 20); 394 mDependencies.setDeviceConfigProperty(DhcpClient.ARP_FIRST_ANNOUNCE_DELAY_MS, 10); 395 mDependencies.setDeviceConfigProperty(DhcpClient.ARP_ANNOUNCE_INTERVAL_MS, 10); 396 397 setUpTapInterface(); 398 setUpIpClient(); 399 } 400 401 private void awaitIpClientShutdown() throws Exception { 402 verify(mCb, timeout(TEST_TIMEOUT_MS)).onQuit(); 403 } 404 405 @After 406 public void tearDown() throws Exception { 407 if (mPacketReader != null) { 408 mHandler.post(() -> mPacketReader.stop()); // Also closes the socket 409 mTapFd = null; 410 } 411 if (mPacketReaderThread != null) { 412 mPacketReaderThread.quitSafely(); 413 } 414 mIpc.shutdown(); 415 awaitIpClientShutdown(); 416 } 417 418 private void setUpTapInterface() { 419 final Instrumentation inst = InstrumentationRegistry.getInstrumentation(); 420 // Adopt the shell permission identity to create a test TAP interface. 421 inst.getUiAutomation().adoptShellPermissionIdentity(); 422 423 final TestNetworkInterface iface; 424 try { 425 final TestNetworkManager tnm = (TestNetworkManager) 426 inst.getContext().getSystemService(Context.TEST_NETWORK_SERVICE); 427 iface = tnm.createTapInterface(); 428 } finally { 429 // Drop the identity in order to regain the network stack permissions, which the shell 430 // does not have. 431 inst.getUiAutomation().dropShellPermissionIdentity(); 432 } 433 mIfaceName = iface.getInterfaceName(); 434 mClientMac = InterfaceParams.getByName(mIfaceName).macAddr.toByteArray(); 435 mPacketReaderThread = new HandlerThread(IpClientIntegrationTest.class.getSimpleName()); 436 mPacketReaderThread.start(); 437 mHandler = mPacketReaderThread.getThreadHandler(); 438 439 // Detach the FileDescriptor from the ParcelFileDescriptor. 440 // Otherwise, the garbage collector might call the ParcelFileDescriptor's finalizer, which 441 // closes the FileDescriptor and destroys our tap interface. An alternative would be to 442 // make the ParcelFileDescriptor or the TestNetworkInterface a class member so they never 443 // go out of scope. 444 mTapFd = new FileDescriptor(); 445 mTapFd.setInt$(iface.getFileDescriptor().detachFd()); 446 mPacketReader = new TapPacketReader(mHandler, mTapFd, DATA_BUFFER_LEN); 447 mHandler.post(() -> mPacketReader.start()); 448 } 449 450 private void setUpIpClient() throws Exception { 451 final Instrumentation inst = InstrumentationRegistry.getInstrumentation(); 452 final IBinder netdIBinder = 453 (IBinder) inst.getContext().getSystemService(Context.NETD_SERVICE); 454 mNetd = spy(INetd.Stub.asInterface(netdIBinder)); 455 when(mContext.getSystemService(eq(Context.NETD_SERVICE))).thenReturn(netdIBinder); 456 assertNotNull(mNetd); 457 458 mNetworkObserverRegistry = new NetworkObserverRegistry(); 459 mNetworkObserverRegistry.register(mNetd); 460 mIpc = new IpClient(mContext, mIfaceName, mCb, mNetworkObserverRegistry, 461 mNetworkStackServiceManager, mDependencies); 462 // Wait for IpClient to enter its initial state. Otherwise, additional setup steps or tests 463 // that mock IpClient's dependencies might interact with those mocks while IpClient is 464 // starting. This would cause UnfinishedStubbingExceptions as mocks cannot be interacted 465 // with while they are being stubbed. 466 HandlerUtilsKt.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS); 467 468 // Tell the IpMemoryStore immediately to answer any question about network attributes with a 469 // null response. Otherwise, the DHCP client will wait for two seconds before starting, 470 // while its query to the IpMemoryStore times out. 471 // This does not affect any test that makes the mock memory store return results, because 472 // unlike when(), it is documented that doAnswer() can be called more than once, to change 473 // the behaviour of a mock in the middle of a test. 474 doAnswer(invocation -> { 475 final String l2Key = invocation.getArgument(0); 476 ((OnNetworkAttributesRetrievedListener) invocation.getArgument(1)) 477 .onNetworkAttributesRetrieved(new Status(SUCCESS), l2Key, null); 478 return null; 479 }).when(mIpMemoryStore).retrieveNetworkAttributes(any(), any()); 480 481 disableIpv6ProvisioningDelays(); 482 } 483 484 private <T> T verifyWithTimeout(InOrder inOrder, T t) { 485 if (inOrder != null) { 486 return inOrder.verify(t, timeout(TEST_TIMEOUT_MS)); 487 } else { 488 return verify(t, timeout(TEST_TIMEOUT_MS)); 489 } 490 } 491 492 private void expectAlarmCancelled(InOrder inOrder, OnAlarmListener listener) { 493 inOrder.verify(mAlarm, timeout(TEST_TIMEOUT_MS)).cancel(eq(listener)); 494 } 495 496 private OnAlarmListener expectAlarmSet(InOrder inOrder, String tagMatch, int afterSeconds) { 497 // Allow +/- 3 seconds to prevent flaky tests. 498 final long when = SystemClock.elapsedRealtime() + afterSeconds * 1000; 499 final long min = when - 3 * 1000; 500 final long max = when + 3 * 1000; 501 ArgumentCaptor<OnAlarmListener> captor = ArgumentCaptor.forClass(OnAlarmListener.class); 502 verifyWithTimeout(inOrder, mAlarm).setExact( 503 anyInt(), longThat(x -> x >= min && x <= max), 504 contains(tagMatch), captor.capture(), eq(mIpc.getHandler())); 505 return captor.getValue(); 506 } 507 508 private boolean packetContainsExpectedField(final byte[] packet, final int offset, 509 final byte[] expected) { 510 if (packet.length < offset + expected.length) return false; 511 for (int i = 0; i < expected.length; ++i) { 512 if (packet[offset + i] != expected[i]) return false; 513 } 514 return true; 515 } 516 517 private boolean isDhcpPacket(final byte[] packet) { 518 final ByteBuffer buffer = ByteBuffer.wrap(packet); 519 520 // check the packet length 521 if (packet.length < DHCP_HEADER_OFFSET) return false; 522 523 // check the source port and dest port in UDP header 524 buffer.position(UDP_SRC_PORT_OFFSET); 525 final short udpSrcPort = buffer.getShort(); 526 final short udpDstPort = buffer.getShort(); 527 if (udpSrcPort != DHCP_CLIENT || udpDstPort != DHCP_SERVER) return false; 528 529 // check DHCP message type 530 buffer.position(DHCP_MESSAGE_OP_CODE_OFFSET); 531 final byte dhcpOpCode = buffer.get(); 532 if (dhcpOpCode != DHCP_BOOTREQUEST) return false; 533 534 // check DHCP magic cookie 535 buffer.position(DHCP_OPTION_MAGIC_COOKIE_OFFSET); 536 final int dhcpMagicCookie = buffer.getInt(); 537 if (dhcpMagicCookie != DHCP_MAGIC_COOKIE) return false; 538 539 return true; 540 } 541 542 private ArpPacket parseArpPacketOrNull(final byte[] packet) { 543 try { 544 return ArpPacket.parseArpPacket(packet, packet.length); 545 } catch (ArpPacket.ParseException e) { 546 return null; 547 } 548 } 549 550 private static ByteBuffer buildDhcpOfferPacket(final DhcpPacket packet, 551 final Integer leaseTimeSec, final short mtu, final String captivePortalUrl) { 552 return DhcpPacket.buildOfferPacket(DhcpPacket.ENCAP_L2, packet.getTransactionId(), 553 false /* broadcast */, SERVER_ADDR, INADDR_ANY /* relayIp */, 554 CLIENT_ADDR /* yourIp */, packet.getClientMac(), leaseTimeSec, 555 NETMASK /* netMask */, BROADCAST_ADDR /* bcAddr */, 556 Collections.singletonList(SERVER_ADDR) /* gateways */, 557 Collections.singletonList(SERVER_ADDR) /* dnsServers */, 558 SERVER_ADDR /* dhcpServerIdentifier */, null /* domainName */, HOSTNAME, 559 false /* metered */, mtu, captivePortalUrl); 560 } 561 562 private static ByteBuffer buildDhcpAckPacket(final DhcpPacket packet, 563 final Inet4Address clientAddress, final Integer leaseTimeSec, final short mtu, 564 final boolean rapidCommit, final String captivePortalApiUrl) { 565 return DhcpPacket.buildAckPacket(DhcpPacket.ENCAP_L2, packet.getTransactionId(), 566 false /* broadcast */, SERVER_ADDR, INADDR_ANY /* relayIp */, 567 clientAddress /* yourIp */, CLIENT_ADDR /* requestIp */, packet.getClientMac(), 568 leaseTimeSec, NETMASK /* netMask */, BROADCAST_ADDR /* bcAddr */, 569 Collections.singletonList(SERVER_ADDR) /* gateways */, 570 Collections.singletonList(SERVER_ADDR) /* dnsServers */, 571 SERVER_ADDR /* dhcpServerIdentifier */, null /* domainName */, HOSTNAME, 572 false /* metered */, mtu, rapidCommit, captivePortalApiUrl); 573 } 574 575 private static ByteBuffer buildDhcpNakPacket(final DhcpPacket packet) { 576 return DhcpPacket.buildNakPacket(DhcpPacket.ENCAP_L2, packet.getTransactionId(), 577 SERVER_ADDR /* serverIp */, INADDR_ANY /* relayIp */, packet.getClientMac(), 578 false /* broadcast */, "duplicated request IP address"); 579 } 580 581 private void sendArpReply(final byte[] clientMac) throws IOException { 582 final ByteBuffer packet = ArpPacket.buildArpPacket(clientMac /* dst */, 583 SERVER_MAC /* src */, INADDR_ANY.getAddress() /* target IP */, 584 clientMac /* target HW address */, CLIENT_ADDR.getAddress() /* sender IP */, 585 (short) ARP_REPLY); 586 mPacketReader.sendResponse(packet); 587 } 588 589 private void sendArpProbe() throws IOException { 590 final ByteBuffer packet = ArpPacket.buildArpPacket(DhcpPacket.ETHER_BROADCAST /* dst */, 591 SERVER_MAC /* src */, CLIENT_ADDR.getAddress() /* target IP */, 592 new byte[ETHER_ADDR_LEN] /* target HW address */, 593 INADDR_ANY.getAddress() /* sender IP */, (short) ARP_REQUEST); 594 mPacketReader.sendResponse(packet); 595 } 596 597 private void startIpClientProvisioning(final boolean isDhcpLeaseCacheEnabled, 598 final boolean shouldReplyRapidCommitAck, final boolean isPreconnectionEnabled, 599 final boolean isDhcpIpConflictDetectEnabled, 600 final boolean isHostnameConfigurationEnabled, final String hostname, 601 final String displayName, final ScanResultInfo scanResultInfo) 602 throws RemoteException { 603 ProvisioningConfiguration.Builder prov = new ProvisioningConfiguration.Builder() 604 .withoutIpReachabilityMonitor() 605 .withLayer2Information(new Layer2Information(TEST_L2KEY, TEST_CLUSTER, 606 MacAddress.fromString(TEST_DEFAULT_BSSID))) 607 .withoutIPv6(); 608 if (isPreconnectionEnabled) prov.withPreconnection(); 609 if (displayName != null) prov.withDisplayName(displayName); 610 if (scanResultInfo != null) prov.withScanResultInfo(scanResultInfo); 611 612 mDependencies.setDhcpLeaseCacheEnabled(isDhcpLeaseCacheEnabled); 613 mDependencies.setDhcpRapidCommitEnabled(shouldReplyRapidCommitAck); 614 mDependencies.setDhcpIpConflictDetectEnabled(isDhcpIpConflictDetectEnabled); 615 mDependencies.setHostnameConfiguration(isHostnameConfigurationEnabled, hostname); 616 mIpc.startProvisioning(prov.build()); 617 if (!isPreconnectionEnabled) { 618 verify(mCb, timeout(TEST_TIMEOUT_MS)).setFallbackMulticastFilter(false); 619 } 620 verify(mCb, never()).onProvisioningFailure(any()); 621 } 622 623 private void startIpClientProvisioning(final boolean isDhcpLeaseCacheEnabled, 624 final boolean isDhcpRapidCommitEnabled, final boolean isPreconnectionEnabled, 625 final boolean isDhcpIpConflictDetectEnabled) 626 throws RemoteException { 627 startIpClientProvisioning(isDhcpLeaseCacheEnabled, isDhcpRapidCommitEnabled, 628 isPreconnectionEnabled, isDhcpIpConflictDetectEnabled, 629 false /* isHostnameConfigurationEnabled */, null /* hostname */, 630 null /* displayName */, null /* ScanResultInfo */); 631 } 632 633 private void assertIpMemoryStoreNetworkAttributes(final Integer leaseTimeSec, 634 final long startTime, final int mtu) { 635 final ArgumentCaptor<NetworkAttributes> networkAttributes = 636 ArgumentCaptor.forClass(NetworkAttributes.class); 637 638 verify(mIpMemoryStore, timeout(TEST_TIMEOUT_MS)) 639 .storeNetworkAttributes(eq(TEST_L2KEY), networkAttributes.capture(), any()); 640 final NetworkAttributes naValueCaptured = networkAttributes.getValue(); 641 assertEquals(CLIENT_ADDR, naValueCaptured.assignedV4Address); 642 if (leaseTimeSec == null || leaseTimeSec.intValue() == DhcpPacket.INFINITE_LEASE) { 643 assertEquals(Long.MAX_VALUE, naValueCaptured.assignedV4AddressExpiry.longValue()); 644 } else { 645 // check the lease expiry's scope 646 final long upperBound = startTime + 7_200_000; // start timestamp + 2h 647 final long lowerBound = startTime + 3_600_000; // start timestamp + 1h 648 final long expiry = naValueCaptured.assignedV4AddressExpiry; 649 assertTrue(upperBound > expiry); 650 assertTrue(lowerBound < expiry); 651 } 652 assertEquals(Collections.singletonList(SERVER_ADDR), naValueCaptured.dnsAddresses); 653 assertEquals(new Integer(mtu), naValueCaptured.mtu); 654 } 655 656 private void assertIpMemoryNeverStoreNetworkAttributes() { 657 verify(mIpMemoryStore, never()).storeNetworkAttributes(any(), any(), any()); 658 } 659 660 private void assertHostname(final boolean isHostnameConfigurationEnabled, 661 final String hostname, final String hostnameAfterTransliteration, 662 final List<DhcpPacket> packetList) throws Exception { 663 for (DhcpPacket packet : packetList) { 664 if (!isHostnameConfigurationEnabled || hostname == null) { 665 assertNoHostname(packet.getHostname()); 666 } else { 667 assertEquals(packet.getHostname(), hostnameAfterTransliteration); 668 } 669 } 670 } 671 672 private void assertNoHostname(String hostname) { 673 if (ShimUtils.isAtLeastR()) { 674 assertNull(hostname); 675 } else { 676 // Until Q, if no hostname is set, the device falls back to the hostname set via 677 // system property, to avoid breaking Q devices already launched with that setup. 678 assertEquals(SystemProperties.get("net.hostname"), hostname); 679 } 680 } 681 682 // Helper method to complete DHCP 2-way or 4-way handshake 683 private List<DhcpPacket> performDhcpHandshake(final boolean isSuccessLease, 684 final Integer leaseTimeSec, final boolean isDhcpLeaseCacheEnabled, 685 final boolean shouldReplyRapidCommitAck, final int mtu, 686 final boolean isDhcpIpConflictDetectEnabled, 687 final boolean isHostnameConfigurationEnabled, final String hostname, 688 final String captivePortalApiUrl, final String displayName, 689 final ScanResultInfo scanResultInfo) throws Exception { 690 startIpClientProvisioning(isDhcpLeaseCacheEnabled, shouldReplyRapidCommitAck, 691 false /* isPreconnectionEnabled */, isDhcpIpConflictDetectEnabled, 692 isHostnameConfigurationEnabled, hostname, displayName, scanResultInfo); 693 return handleDhcpPackets(isSuccessLease, leaseTimeSec, shouldReplyRapidCommitAck, mtu, 694 captivePortalApiUrl); 695 } 696 697 private List<DhcpPacket> handleDhcpPackets(final boolean isSuccessLease, 698 final Integer leaseTimeSec, final boolean shouldReplyRapidCommitAck, final int mtu, 699 final String captivePortalApiUrl) throws Exception { 700 final List<DhcpPacket> packetList = new ArrayList<>(); 701 DhcpPacket packet; 702 while ((packet = getNextDhcpPacket()) != null) { 703 packetList.add(packet); 704 if (packet instanceof DhcpDiscoverPacket) { 705 if (shouldReplyRapidCommitAck) { 706 mPacketReader.sendResponse(buildDhcpAckPacket(packet, CLIENT_ADDR, leaseTimeSec, 707 (short) mtu, true /* rapidCommit */, captivePortalApiUrl)); 708 } else { 709 mPacketReader.sendResponse(buildDhcpOfferPacket(packet, leaseTimeSec, 710 (short) mtu, captivePortalApiUrl)); 711 } 712 } else if (packet instanceof DhcpRequestPacket) { 713 final ByteBuffer byteBuffer = isSuccessLease 714 ? buildDhcpAckPacket(packet, CLIENT_ADDR, leaseTimeSec, (short) mtu, 715 false /* rapidCommit */, captivePortalApiUrl) 716 : buildDhcpNakPacket(packet); 717 mPacketReader.sendResponse(byteBuffer); 718 } else { 719 fail("invalid DHCP packet"); 720 } 721 722 // wait for reply to DHCPOFFER packet if disabling rapid commit option 723 if (shouldReplyRapidCommitAck || !(packet instanceof DhcpDiscoverPacket)) { 724 return packetList; 725 } 726 } 727 fail("No DHCPREQUEST received on interface"); 728 return packetList; 729 } 730 731 private List<DhcpPacket> performDhcpHandshake(final boolean isSuccessLease, 732 final Integer leaseTimeSec, final boolean isDhcpLeaseCacheEnabled, 733 final boolean isDhcpRapidCommitEnabled, final int mtu, 734 final boolean isDhcpIpConflictDetectEnabled) throws Exception { 735 return performDhcpHandshake(isSuccessLease, leaseTimeSec, isDhcpLeaseCacheEnabled, 736 isDhcpRapidCommitEnabled, mtu, isDhcpIpConflictDetectEnabled, 737 false /* isHostnameConfigurationEnabled */, null /* hostname */, 738 null /* captivePortalApiUrl */, null /* displayName */, null /* scanResultInfo */); 739 } 740 741 private List<DhcpPacket> performDhcpHandshake() throws Exception { 742 return performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S, 743 false /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */, 744 TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */); 745 } 746 747 private DhcpPacket getNextDhcpPacket() throws ParseException { 748 byte[] packet; 749 while ((packet = mPacketReader.popPacket(PACKET_TIMEOUT_MS)) != null) { 750 if (!isDhcpPacket(packet)) continue; 751 return DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L2); 752 } 753 fail("No expected DHCP packet received on interface within timeout"); 754 return null; 755 } 756 757 private DhcpPacket getReplyFromDhcpLease(final NetworkAttributes na, boolean timeout) 758 throws Exception { 759 doAnswer(invocation -> { 760 if (timeout) return null; 761 ((OnNetworkAttributesRetrievedListener) invocation.getArgument(1)) 762 .onNetworkAttributesRetrieved(new Status(SUCCESS), TEST_L2KEY, na); 763 return null; 764 }).when(mIpMemoryStore).retrieveNetworkAttributes(eq(TEST_L2KEY), any()); 765 startIpClientProvisioning(true /* isDhcpLeaseCacheEnabled */, 766 false /* shouldReplyRapidCommitAck */, false /* isPreconnectionEnabled */, 767 false /* isDhcpIpConflictDetectEnabled */); 768 return getNextDhcpPacket(); 769 } 770 771 private void removeTapInterface(final FileDescriptor fd) { 772 try { 773 Os.close(fd); 774 } catch (ErrnoException e) { 775 fail("Fail to close file descriptor: " + e); 776 } 777 } 778 779 private void verifyAfterIpClientShutdown() throws RemoteException { 780 final LinkProperties emptyLp = new LinkProperties(); 781 emptyLp.setInterfaceName(mIfaceName); 782 verify(mCb, timeout(TEST_TIMEOUT_MS)).onLinkPropertiesChange(emptyLp); 783 } 784 785 // Verify IPv4-only provisioning success. No need to verify IPv4 provisioning when below cases 786 // happen: 787 // 1. if there's a failure lease, onProvisioningSuccess() won't be called; 788 // 2. if duplicated IPv4 address detection is enabled, verify TIMEOUT will affect ARP packets 789 // capture running in other test cases. 790 // 3. if IPv6 is enabled, e.g. withoutIPv6() isn't called when starting provisioning. 791 private void verifyIPv4OnlyProvisioningSuccess(final Collection<InetAddress> addresses) 792 throws Exception { 793 final ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class); 794 verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(captor.capture()); 795 LinkProperties lp = captor.getValue(); 796 assertNotNull(lp); 797 assertNotEquals(0, lp.getDnsServers().size()); 798 assertEquals(addresses.size(), lp.getAddresses().size()); 799 assertTrue(lp.getAddresses().containsAll(addresses)); 800 } 801 802 private void doRestoreInitialMtuTest(final boolean shouldChangeMtu, 803 final boolean shouldRemoveTapInterface) throws Exception { 804 final long currentTime = System.currentTimeMillis(); 805 int mtu = TEST_DEFAULT_MTU; 806 807 if (shouldChangeMtu) mtu = TEST_MIN_MTU; 808 performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S, 809 true /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */, 810 mtu, false /* isDhcpIpConflictDetectEnabled */); 811 verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR)); 812 assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, mtu); 813 814 if (shouldChangeMtu) { 815 // Pretend that ConnectivityService set the MTU. 816 mNetd.interfaceSetMtu(mIfaceName, mtu); 817 assertEquals(NetworkInterface.getByName(mIfaceName).getMTU(), mtu); 818 } 819 820 // Sometimes, IpClient receives an update with an empty LinkProperties during startup, 821 // when the link-local address is deleted after interface bringup. Reset expectations 822 // here to ensure that verifyAfterIpClientShutdown does not fail because it sees two 823 // empty LinkProperties changes instead of one. 824 reset(mCb); 825 826 if (shouldRemoveTapInterface) removeTapInterface(mTapFd); 827 try { 828 mIpc.shutdown(); 829 awaitIpClientShutdown(); 830 if (shouldRemoveTapInterface) { 831 verify(mNetd, never()).interfaceSetMtu(mIfaceName, TEST_DEFAULT_MTU); 832 } else { 833 // Verify that MTU indeed has been restored or not. 834 verify(mNetd, times(shouldChangeMtu ? 1 : 0)) 835 .interfaceSetMtu(mIfaceName, TEST_DEFAULT_MTU); 836 } 837 verifyAfterIpClientShutdown(); 838 } catch (Exception e) { 839 fail("Exception should not have been thrown after shutdown: " + e); 840 } 841 } 842 843 private DhcpPacket assertDiscoverPacketOnPreconnectionStart() throws Exception { 844 final ArgumentCaptor<List<Layer2PacketParcelable>> l2PacketList = 845 ArgumentCaptor.forClass(List.class); 846 847 verify(mCb, timeout(TEST_TIMEOUT_MS)).onPreconnectionStart(l2PacketList.capture()); 848 final byte[] payload = l2PacketList.getValue().get(0).payload; 849 DhcpPacket packet = DhcpPacket.decodeFullPacket(payload, payload.length, ENCAP_L2); 850 assertTrue(packet instanceof DhcpDiscoverPacket); 851 assertArrayEquals(INADDR_BROADCAST.getAddress(), 852 Arrays.copyOfRange(payload, IPV4_DST_ADDR_OFFSET, IPV4_DST_ADDR_OFFSET + 4)); 853 return packet; 854 } 855 856 private void doIpClientProvisioningWithPreconnectionTest( 857 final boolean shouldReplyRapidCommitAck, final boolean shouldAbortPreconnection, 858 final boolean shouldFirePreconnectionTimeout, 859 final boolean timeoutBeforePreconnectionComplete) throws Exception { 860 final long currentTime = System.currentTimeMillis(); 861 final ArgumentCaptor<InterfaceConfigurationParcel> ifConfig = 862 ArgumentCaptor.forClass(InterfaceConfigurationParcel.class); 863 864 startIpClientProvisioning(true /* isDhcpLeaseCacheEnabled */, 865 shouldReplyRapidCommitAck, true /* isDhcpPreConnectionEnabled */, 866 false /* isDhcpIpConflictDetectEnabled */); 867 DhcpPacket packet = assertDiscoverPacketOnPreconnectionStart(); 868 final int preconnDiscoverTransId = packet.getTransactionId(); 869 870 if (shouldAbortPreconnection) { 871 if (shouldFirePreconnectionTimeout && timeoutBeforePreconnectionComplete) { 872 mDependencies.mDhcpClient.sendMessage(DhcpClient.CMD_TIMEOUT); 873 } 874 875 mIpc.notifyPreconnectionComplete(false /* abort */); 876 HandlerUtilsKt.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS); 877 878 if (shouldFirePreconnectionTimeout && !timeoutBeforePreconnectionComplete) { 879 mDependencies.mDhcpClient.sendMessage(DhcpClient.CMD_TIMEOUT); 880 } 881 882 // Either way should get DhcpClient go back to INIT state, and broadcast 883 // DISCOVER with new transaction ID. 884 packet = getNextDhcpPacket(); 885 assertTrue(packet instanceof DhcpDiscoverPacket); 886 assertTrue(packet.getTransactionId() != preconnDiscoverTransId); 887 } else if (shouldFirePreconnectionTimeout && timeoutBeforePreconnectionComplete) { 888 // If timeout fires before success preconnection, DhcpClient will go back to INIT state, 889 // and broadcast DISCOVER with new transaction ID. 890 mDependencies.mDhcpClient.sendMessage(DhcpClient.CMD_TIMEOUT); 891 packet = getNextDhcpPacket(); 892 assertTrue(packet instanceof DhcpDiscoverPacket); 893 assertTrue(packet.getTransactionId() != preconnDiscoverTransId); 894 // any old response would be ignored due to mismatched transaction ID. 895 } 896 897 final short mtu = (short) TEST_DEFAULT_MTU; 898 if (!shouldReplyRapidCommitAck) { 899 mPacketReader.sendResponse(buildDhcpOfferPacket(packet, TEST_LEASE_DURATION_S, mtu, 900 null /* captivePortalUrl */)); 901 packet = getNextDhcpPacket(); 902 assertTrue(packet instanceof DhcpRequestPacket); 903 } 904 mPacketReader.sendResponse(buildDhcpAckPacket(packet, CLIENT_ADDR, TEST_LEASE_DURATION_S, 905 mtu, shouldReplyRapidCommitAck, null /* captivePortalUrl */)); 906 907 if (!shouldAbortPreconnection) { 908 mIpc.notifyPreconnectionComplete(true /* success */); 909 HandlerUtilsKt.waitForIdle(mDependencies.mDhcpClient.getHandler(), TEST_TIMEOUT_MS); 910 911 // If timeout fires after successful preconnection, right now DhcpClient will have 912 // already entered BOUND state, the delayed CMD_TIMEOUT command would be ignored. So 913 // this case should be very rare, because the timeout alarm is cancelled when state 914 // machine exits from Preconnecting state. 915 if (shouldFirePreconnectionTimeout && !timeoutBeforePreconnectionComplete) { 916 mDependencies.mDhcpClient.sendMessage(DhcpClient.CMD_TIMEOUT); 917 } 918 } 919 verify(mCb, timeout(TEST_TIMEOUT_MS)).setFallbackMulticastFilter(false); 920 921 final LinkAddress ipAddress = new LinkAddress(CLIENT_ADDR, PREFIX_LENGTH); 922 verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceSetCfg(ifConfig.capture()); 923 assertEquals(ifConfig.getValue().ifName, mIfaceName); 924 assertEquals(ifConfig.getValue().ipv4Addr, ipAddress.getAddress().getHostAddress()); 925 assertEquals(ifConfig.getValue().prefixLength, PREFIX_LENGTH); 926 assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU); 927 } 928 929 private ArpPacket getNextArpPacket(final int timeout) throws Exception { 930 byte[] packet; 931 while ((packet = mPacketReader.popPacket(timeout)) != null) { 932 final ArpPacket arpPacket = parseArpPacketOrNull(packet); 933 if (arpPacket != null) return arpPacket; 934 } 935 return null; 936 } 937 938 private ArpPacket getNextArpPacket() throws Exception { 939 final ArpPacket packet = getNextArpPacket(PACKET_TIMEOUT_MS); 940 assertNotNull("No expected ARP packet received on interface within timeout", packet); 941 return packet; 942 } 943 944 private void assertArpPacket(final ArpPacket packet) { 945 assertEquals(packet.opCode, ARP_REQUEST); 946 assertEquals(packet.targetIp, CLIENT_ADDR); 947 assertTrue(Arrays.equals(packet.senderHwAddress.toByteArray(), mClientMac)); 948 } 949 950 private void assertArpProbe(final ArpPacket packet) { 951 assertArpPacket(packet); 952 assertEquals(packet.senderIp, INADDR_ANY); 953 } 954 955 private void assertArpAnnounce(final ArpPacket packet) { 956 assertArpPacket(packet); 957 assertEquals(packet.senderIp, CLIENT_ADDR); 958 } 959 960 private void doIpAddressConflictDetectionTest(final boolean causeIpAddressConflict, 961 final boolean shouldReplyRapidCommitAck, final boolean isDhcpIpConflictDetectEnabled, 962 final boolean shouldResponseArpReply) throws Exception { 963 final long currentTime = System.currentTimeMillis(); 964 965 performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S, 966 true /* isDhcpLeaseCacheEnabled */, shouldReplyRapidCommitAck, 967 TEST_DEFAULT_MTU, isDhcpIpConflictDetectEnabled); 968 969 // If we receive an ARP packet here, it's guaranteed to be from IP conflict detection, 970 // because at this time the test interface does not have an IP address and therefore 971 // won't send ARP for anything. 972 if (causeIpAddressConflict) { 973 final ArpPacket arpProbe = getNextArpPacket(); 974 assertArpProbe(arpProbe); 975 976 if (shouldResponseArpReply) { 977 sendArpReply(mClientMac); 978 } else { 979 sendArpProbe(); 980 } 981 final DhcpPacket packet = getNextDhcpPacket(); 982 assertTrue(packet instanceof DhcpDeclinePacket); 983 assertEquals(packet.mServerIdentifier, SERVER_ADDR); 984 assertEquals(packet.mRequestedIp, CLIENT_ADDR); 985 986 verify(mCb, never()).onProvisioningFailure(any()); 987 assertIpMemoryNeverStoreNetworkAttributes(); 988 } else if (isDhcpIpConflictDetectEnabled) { 989 int arpPacketCount = 0; 990 final List<ArpPacket> packetList = new ArrayList<ArpPacket>(); 991 // Total sent ARP packets should be 5 (3 ARP Probes + 2 ARP Announcements) 992 ArpPacket packet; 993 while ((packet = getNextArpPacket(TEST_TIMEOUT_MS)) != null) { 994 packetList.add(packet); 995 } 996 assertEquals(5, packetList.size()); 997 assertArpProbe(packetList.get(0)); 998 assertArpAnnounce(packetList.get(3)); 999 } else { 1000 verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR)); 1001 assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, 1002 TEST_DEFAULT_MTU); 1003 } 1004 } 1005 1006 @Test 1007 public void testInterfaceParams() throws Exception { 1008 InterfaceParams params = InterfaceParams.getByName(mIfaceName); 1009 assertNotNull(params); 1010 assertEquals(mIfaceName, params.name); 1011 assertTrue(params.index > 0); 1012 assertNotNull(params.macAddr); 1013 assertTrue(params.hasMacAddress); 1014 1015 // Sanity check. 1016 params = InterfaceParams.getByName("lo"); 1017 assertNotNull(params); 1018 assertEquals("lo", params.name); 1019 assertTrue(params.index > 0); 1020 assertNotNull(params.macAddr); 1021 assertFalse(params.hasMacAddress); 1022 } 1023 1024 @Test 1025 public void testDhcpInit() throws Exception { 1026 startIpClientProvisioning(false /* isDhcpLeaseCacheEnabled */, 1027 false /* shouldReplyRapidCommitAck */, false /* isPreconnectionEnabled */, 1028 false /* isDhcpIpConflictDetectEnabled */); 1029 final DhcpPacket packet = getNextDhcpPacket(); 1030 assertTrue(packet instanceof DhcpDiscoverPacket); 1031 } 1032 1033 @Test 1034 public void testHandleSuccessDhcpLease() throws Exception { 1035 final long currentTime = System.currentTimeMillis(); 1036 performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S, 1037 true /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */, 1038 TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */); 1039 verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR)); 1040 assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU); 1041 } 1042 1043 @Test 1044 public void testHandleFailureDhcpLease() throws Exception { 1045 performDhcpHandshake(false /* isSuccessLease */, TEST_LEASE_DURATION_S, 1046 true /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */, 1047 TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */); 1048 1049 verify(mCb, never()).onProvisioningSuccess(any()); 1050 assertIpMemoryNeverStoreNetworkAttributes(); 1051 } 1052 1053 @Test 1054 public void testHandleInfiniteLease() throws Exception { 1055 final long currentTime = System.currentTimeMillis(); 1056 performDhcpHandshake(true /* isSuccessLease */, INFINITE_LEASE, 1057 true /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */, 1058 TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */); 1059 verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR)); 1060 assertIpMemoryStoreNetworkAttributes(INFINITE_LEASE, currentTime, TEST_DEFAULT_MTU); 1061 } 1062 1063 @Test 1064 public void testHandleNoLease() throws Exception { 1065 final long currentTime = System.currentTimeMillis(); 1066 performDhcpHandshake(true /* isSuccessLease */, null /* no lease time */, 1067 true /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */, 1068 TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */); 1069 verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR)); 1070 assertIpMemoryStoreNetworkAttributes(null, currentTime, TEST_DEFAULT_MTU); 1071 } 1072 1073 @Test @IgnoreAfter(Build.VERSION_CODES.Q) // INIT-REBOOT is enabled on R. 1074 public void testHandleDisableInitRebootState() throws Exception { 1075 performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S, 1076 false /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */, 1077 TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */); 1078 verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR)); 1079 assertIpMemoryNeverStoreNetworkAttributes(); 1080 } 1081 1082 @Test 1083 public void testHandleRapidCommitOption() throws Exception { 1084 final long currentTime = System.currentTimeMillis(); 1085 performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S, 1086 true /* isDhcpLeaseCacheEnabled */, true /* shouldReplyRapidCommitAck */, 1087 TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */); 1088 verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR)); 1089 assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU); 1090 } 1091 1092 @Test 1093 public void testDhcpClientStartWithCachedInfiniteLease() throws Exception { 1094 final DhcpPacket packet = getReplyFromDhcpLease( 1095 new NetworkAttributes.Builder() 1096 .setAssignedV4Address(CLIENT_ADDR) 1097 .setAssignedV4AddressExpiry(Long.MAX_VALUE) // lease is always valid 1098 .setMtu(new Integer(TEST_DEFAULT_MTU)) 1099 .setCluster(TEST_CLUSTER) 1100 .setDnsAddresses(Collections.singletonList(SERVER_ADDR)) 1101 .build(), false /* timeout */); 1102 assertTrue(packet instanceof DhcpRequestPacket); 1103 } 1104 1105 @Test 1106 public void testDhcpClientStartWithCachedExpiredLease() throws Exception { 1107 final DhcpPacket packet = getReplyFromDhcpLease( 1108 new NetworkAttributes.Builder() 1109 .setAssignedV4Address(CLIENT_ADDR) 1110 .setAssignedV4AddressExpiry(EXPIRED_LEASE) 1111 .setMtu(new Integer(TEST_DEFAULT_MTU)) 1112 .setCluster(TEST_CLUSTER) 1113 .setDnsAddresses(Collections.singletonList(SERVER_ADDR)) 1114 .build(), false /* timeout */); 1115 assertTrue(packet instanceof DhcpDiscoverPacket); 1116 } 1117 1118 @Test 1119 public void testDhcpClientStartWithNullRetrieveNetworkAttributes() throws Exception { 1120 final DhcpPacket packet = getReplyFromDhcpLease(null /* na */, false /* timeout */); 1121 assertTrue(packet instanceof DhcpDiscoverPacket); 1122 } 1123 1124 @Test 1125 public void testDhcpClientStartWithTimeoutRetrieveNetworkAttributes() throws Exception { 1126 final DhcpPacket packet = getReplyFromDhcpLease( 1127 new NetworkAttributes.Builder() 1128 .setAssignedV4Address(CLIENT_ADDR) 1129 .setAssignedV4AddressExpiry(System.currentTimeMillis() + 3_600_000) 1130 .setMtu(new Integer(TEST_DEFAULT_MTU)) 1131 .setCluster(TEST_CLUSTER) 1132 .setDnsAddresses(Collections.singletonList(SERVER_ADDR)) 1133 .build(), true /* timeout */); 1134 assertTrue(packet instanceof DhcpDiscoverPacket); 1135 } 1136 1137 @Test 1138 public void testDhcpClientStartWithCachedLeaseWithoutIPAddress() throws Exception { 1139 final DhcpPacket packet = getReplyFromDhcpLease( 1140 new NetworkAttributes.Builder() 1141 .setMtu(new Integer(TEST_DEFAULT_MTU)) 1142 .setCluster(TEST_CLUSTER) 1143 .setDnsAddresses(Collections.singletonList(SERVER_ADDR)) 1144 .build(), false /* timeout */); 1145 assertTrue(packet instanceof DhcpDiscoverPacket); 1146 } 1147 1148 @Test 1149 public void testDhcpClientRapidCommitEnabled() throws Exception { 1150 startIpClientProvisioning(true /* isDhcpLeaseCacheEnabled */, 1151 true /* shouldReplyRapidCommitAck */, false /* isPreconnectionEnabled */, 1152 false /* isDhcpIpConflictDetectEnabled */); 1153 final DhcpPacket packet = getNextDhcpPacket(); 1154 assertTrue(packet instanceof DhcpDiscoverPacket); 1155 } 1156 1157 @Test @IgnoreUpTo(Build.VERSION_CODES.Q) 1158 public void testDhcpServerInLinkProperties() throws Exception { 1159 assumeTrue(ConstantsShim.VERSION > Build.VERSION_CODES.Q); 1160 1161 performDhcpHandshake(); 1162 ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class); 1163 verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(captor.capture()); 1164 assertEquals(SERVER_ADDR, captor.getValue().getDhcpServerAddress()); 1165 } 1166 1167 @Test 1168 public void testRestoreInitialInterfaceMtu() throws Exception { 1169 doRestoreInitialMtuTest(true /* shouldChangeMtu */, false /* shouldRemoveTapInterface */); 1170 } 1171 1172 @Test 1173 public void testRestoreInitialInterfaceMtu_WithoutMtuChange() throws Exception { 1174 doRestoreInitialMtuTest(false /* shouldChangeMtu */, false /* shouldRemoveTapInterface */); 1175 } 1176 1177 @Test 1178 public void testRestoreInitialInterfaceMtu_WithException() throws Exception { 1179 doThrow(new RemoteException("NetdNativeService::interfaceSetMtu")).when(mNetd) 1180 .interfaceSetMtu(mIfaceName, TEST_DEFAULT_MTU); 1181 1182 doRestoreInitialMtuTest(true /* shouldChangeMtu */, false /* shouldRemoveTapInterface */); 1183 assertEquals(NetworkInterface.getByName(mIfaceName).getMTU(), TEST_MIN_MTU); 1184 } 1185 1186 @Test 1187 public void testRestoreInitialInterfaceMtu_NotFoundInterfaceWhenStopping() throws Exception { 1188 doRestoreInitialMtuTest(true /* shouldChangeMtu */, true /* shouldRemoveTapInterface */); 1189 } 1190 1191 @Test 1192 public void testRestoreInitialInterfaceMtu_NotFoundInterfaceWhenStartingProvisioning() 1193 throws Exception { 1194 removeTapInterface(mTapFd); 1195 ProvisioningConfiguration config = new ProvisioningConfiguration.Builder() 1196 .withoutIpReachabilityMonitor() 1197 .withoutIPv6() 1198 .build(); 1199 1200 mIpc.startProvisioning(config); 1201 verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningFailure(any()); 1202 verify(mCb, never()).setNeighborDiscoveryOffload(true); 1203 } 1204 1205 @Test 1206 public void testRestoreInitialInterfaceMtu_stopIpClientAndRestart() throws Exception { 1207 long currentTime = System.currentTimeMillis(); 1208 1209 performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S, 1210 true /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */, 1211 TEST_MIN_MTU, false /* isDhcpIpConflictDetectEnabled */); 1212 verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR)); 1213 assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_MIN_MTU); 1214 1215 // Pretend that ConnectivityService set the MTU. 1216 mNetd.interfaceSetMtu(mIfaceName, TEST_MIN_MTU); 1217 assertEquals(NetworkInterface.getByName(mIfaceName).getMTU(), TEST_MIN_MTU); 1218 1219 reset(mCb); 1220 reset(mIpMemoryStore); 1221 1222 // Stop IpClient and then restart provisioning immediately. 1223 mIpc.stop(); 1224 currentTime = System.currentTimeMillis(); 1225 // Intend to set mtu option to 0, then verify that won't influence interface mtu restore. 1226 performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S, 1227 true /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */, 1228 0 /* mtu */, false /* isDhcpIpConflictDetectEnabled */); 1229 verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR)); 1230 assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, 0 /* mtu */); 1231 assertEquals(NetworkInterface.getByName(mIfaceName).getMTU(), TEST_DEFAULT_MTU); 1232 } 1233 1234 private boolean isRouterSolicitation(final byte[] packetBytes) { 1235 ByteBuffer packet = ByteBuffer.wrap(packetBytes); 1236 return packet.getShort(ETHER_TYPE_OFFSET) == (short) ETH_P_IPV6 1237 && packet.get(ETHER_HEADER_LEN + IPV6_PROTOCOL_OFFSET) == (byte) IPPROTO_ICMPV6 1238 && packet.get(ETHER_HEADER_LEN + IPV6_HEADER_LEN) 1239 == (byte) ICMPV6_ROUTER_SOLICITATION; 1240 } 1241 1242 private void waitForRouterSolicitation() throws ParseException { 1243 byte[] packet; 1244 while ((packet = mPacketReader.popPacket(PACKET_TIMEOUT_MS)) != null) { 1245 if (isRouterSolicitation(packet)) return; 1246 } 1247 fail("No router solicitation received on interface within timeout"); 1248 } 1249 1250 private void sendRouterAdvertisement(boolean waitForRs, short lifetime) throws Exception { 1251 final String dnsServer = "2001:4860:4860::64"; 1252 final ByteBuffer pio = buildPioOption(3600, 1800, "2001:db8:1::/64"); 1253 ByteBuffer rdnss = buildRdnssOption(3600, dnsServer); 1254 ByteBuffer ra = buildRaPacket(lifetime, pio, rdnss); 1255 1256 if (waitForRs) { 1257 waitForRouterSolicitation(); 1258 } 1259 1260 mPacketReader.sendResponse(ra); 1261 } 1262 1263 private void sendBasicRouterAdvertisement(boolean waitForRs) throws Exception { 1264 sendRouterAdvertisement(waitForRs, (short) 1800); 1265 } 1266 1267 private void sendRouterAdvertisementWithZeroLifetime() throws Exception { 1268 sendRouterAdvertisement(false /* waitForRs */, (short) 0); 1269 } 1270 1271 // TODO: move this and the following method to a common location and use them in ApfTest. 1272 private static ByteBuffer buildPioOption(int valid, int preferred, String prefixString) 1273 throws Exception { 1274 final int optLen = 4; 1275 IpPrefix prefix = new IpPrefix(prefixString); 1276 ByteBuffer option = ByteBuffer.allocate(optLen * ICMPV6_ND_OPTION_LENGTH_SCALING_FACTOR); 1277 option.put((byte) ICMPV6_ND_OPTION_PIO); // Type 1278 option.put((byte) optLen); // Length in 8-byte units 1279 option.put((byte) prefix.getPrefixLength()); // Prefix length 1280 option.put((byte) 0b11000000); // L = 1, A = 1 1281 option.putInt(valid); 1282 option.putInt(preferred); 1283 option.putInt(0); // Reserved 1284 option.put(prefix.getRawAddress()); 1285 option.flip(); 1286 return option; 1287 } 1288 1289 private static ByteBuffer buildRdnssOption(int lifetime, String... servers) throws Exception { 1290 final int optLen = 1 + 2 * servers.length; 1291 ByteBuffer option = ByteBuffer.allocate(optLen * ICMPV6_ND_OPTION_LENGTH_SCALING_FACTOR); 1292 option.put((byte) ICMPV6_ND_OPTION_RDNSS); // Type 1293 option.put((byte) optLen); // Length in 8-byte units 1294 option.putShort((short) 0); // Reserved 1295 option.putInt(lifetime); // Lifetime 1296 for (String server : servers) { 1297 option.put(InetAddress.getByName(server).getAddress()); 1298 } 1299 option.flip(); 1300 return option; 1301 } 1302 1303 // HACK: these functions are here because IpUtils#transportChecksum is private. Even if we made 1304 // that public, it won't be available on Q devices, and this test needs to run on Q devices. 1305 // TODO: move the IpUtils code to frameworks/lib/net and link it statically. 1306 private static int checksumFold(int sum) { 1307 while (sum > 0xffff) { 1308 sum = (sum >> 16) + (sum & 0xffff); 1309 } 1310 return sum; 1311 } 1312 1313 private static short checksumAdjust(short checksum, short oldWord, short newWord) { 1314 checksum = (short) ~checksum; 1315 int tempSum = checksumFold(uint16(checksum) + uint16(newWord) + 0xffff - uint16(oldWord)); 1316 return (short) ~tempSum; 1317 } 1318 1319 public static int uint16(short s) { 1320 return s & 0xffff; 1321 } 1322 1323 private static short icmpv6Checksum(ByteBuffer buf, int ipOffset, int transportOffset, 1324 int transportLen) { 1325 // The ICMPv6 checksum is the same as the TCP checksum, except the pseudo-header uses 1326 // 58 (ICMPv6) instead of 6 (TCP). Calculate the TCP checksum, and then do an incremental 1327 // checksum adjustment for the change in the next header byte. 1328 short checksum = IpUtils.tcpChecksum(buf, ipOffset, transportOffset, transportLen); 1329 return checksumAdjust(checksum, (short) IPPROTO_TCP, (short) IPPROTO_ICMPV6); 1330 } 1331 1332 private static ByteBuffer buildRaPacket(short lifetime, ByteBuffer... options) 1333 throws Exception { 1334 final MacAddress srcMac = MacAddress.fromString("33:33:00:00:00:01"); 1335 final MacAddress dstMac = MacAddress.fromString("01:02:03:04:05:06"); 1336 final byte[] routerLinkLocal = InetAddresses.parseNumericAddress("fe80::1").getAddress(); 1337 final byte[] allNodes = InetAddresses.parseNumericAddress("ff02::1").getAddress(); 1338 1339 final ByteBuffer packet = ByteBuffer.allocate(TEST_DEFAULT_MTU); 1340 int icmpLen = ICMPV6_RA_HEADER_LEN; 1341 1342 // Ethernet header. 1343 packet.put(srcMac.toByteArray()); 1344 packet.put(dstMac.toByteArray()); 1345 packet.putShort((short) ETHER_TYPE_IPV6); 1346 1347 // IPv6 header. 1348 packet.putInt(0x600abcde); // Version, traffic class, flowlabel 1349 packet.putShort((short) 0); // Length, TBD 1350 packet.put((byte) IPPROTO_ICMPV6); // Next header 1351 packet.put((byte) 0xff); // Hop limit 1352 packet.put(routerLinkLocal); // Source address 1353 packet.put(allNodes); // Destination address 1354 1355 // Router advertisement. 1356 packet.put((byte) ICMPV6_ROUTER_ADVERTISEMENT); // ICMP type 1357 packet.put((byte) 0); // ICMP code 1358 packet.putShort((short) 0); // Checksum, TBD 1359 packet.put((byte) 0); // Hop limit, unspecified 1360 packet.put((byte) 0); // M=0, O=0 1361 packet.putShort(lifetime); // Router lifetime 1362 packet.putInt(0); // Reachable time, unspecified 1363 packet.putInt(100); // Retrans time 100ms. 1364 1365 for (ByteBuffer option : options) { 1366 packet.put(option); 1367 option.clear(); // So we can reuse it in a future packet. 1368 icmpLen += option.capacity(); 1369 } 1370 1371 // Populate length and checksum fields. 1372 final int transportOffset = ETHER_HEADER_LEN + IPV6_HEADER_LEN; 1373 final short checksum = icmpv6Checksum(packet, ETHER_HEADER_LEN, transportOffset, icmpLen); 1374 packet.putShort(ETHER_HEADER_LEN + IPV6_LEN_OFFSET, (short) icmpLen); 1375 packet.putShort(transportOffset + ICMPV6_CHECKSUM_OFFSET, checksum); 1376 1377 packet.flip(); 1378 return packet; 1379 } 1380 1381 private static ByteBuffer buildRaPacket(ByteBuffer... options) throws Exception { 1382 return buildRaPacket((short) 1800, options); 1383 } 1384 1385 private void disableIpv6ProvisioningDelays() throws Exception { 1386 // Speed up the test by disabling DAD and removing router_solicitation_delay. 1387 // We don't need to restore the default value because the interface is removed in tearDown. 1388 // TODO: speed up further by not waiting for RS but keying off first IPv6 packet. 1389 mNetd.setProcSysNet(INetd.IPV6, INetd.CONF, mIfaceName, "router_solicitation_delay", "0"); 1390 mNetd.setProcSysNet(INetd.IPV6, INetd.CONF, mIfaceName, "dad_transmits", "0"); 1391 } 1392 1393 private void assertHasAddressThat(String msg, LinkProperties lp, 1394 Predicate<LinkAddress> condition) { 1395 for (LinkAddress addr : lp.getLinkAddresses()) { 1396 if (condition.test(addr)) { 1397 return; 1398 } 1399 } 1400 fail(msg + " not found in: " + lp); 1401 } 1402 1403 private boolean hasFlag(LinkAddress addr, int flag) { 1404 return (addr.getFlags() & flag) == flag; 1405 } 1406 1407 private boolean isPrivacyAddress(LinkAddress addr) { 1408 return addr.isGlobalPreferred() && hasFlag(addr, IFA_F_TEMPORARY); 1409 } 1410 1411 private boolean isStablePrivacyAddress(LinkAddress addr) { 1412 // TODO: move away from getting address updates from netd and make this work on Q as well. 1413 final int flag = ShimUtils.isAtLeastR() ? IFA_F_STABLE_PRIVACY : 0; 1414 return addr.isGlobalPreferred() && hasFlag(addr, flag); 1415 } 1416 1417 private LinkProperties doIpv6OnlyProvisioning(InOrder inOrder, ByteBuffer ra) throws Exception { 1418 waitForRouterSolicitation(); 1419 mPacketReader.sendResponse(ra); 1420 1421 // The lambda below needs to write a LinkProperties to a local variable, but lambdas cannot 1422 // write to non-final local variables. So declare a final variable to write to. 1423 final AtomicReference<LinkProperties> lpRef = new AtomicReference<>(); 1424 1425 ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class); 1426 verifyWithTimeout(inOrder, mCb).onProvisioningSuccess(captor.capture()); 1427 lpRef.set(captor.getValue()); 1428 1429 // Sometimes provisioning completes as soon as the link-local and the stable address appear, 1430 // before the privacy address appears. If so, wait here for the LinkProperties update that 1431 // contains all three address. Otherwise, future calls to verify() might get confused. 1432 if (captor.getValue().getLinkAddresses().size() == 2) { 1433 verifyWithTimeout(inOrder, mCb).onLinkPropertiesChange(argThat(lp -> { 1434 lpRef.set(lp); 1435 return lp.getLinkAddresses().size() == 3; 1436 })); 1437 } 1438 1439 LinkProperties lp = lpRef.get(); 1440 assertEquals("Should have 3 IPv6 addresses after provisioning: " + lp, 1441 3, lp.getLinkAddresses().size()); 1442 assertHasAddressThat("link-local address", lp, x -> x.getAddress().isLinkLocalAddress()); 1443 assertHasAddressThat("privacy address", lp, this::isPrivacyAddress); 1444 assertHasAddressThat("stable privacy address", lp, this::isStablePrivacyAddress); 1445 1446 return lp; 1447 } 1448 1449 @Test 1450 public void testRaRdnss() throws Exception { 1451 ProvisioningConfiguration config = new ProvisioningConfiguration.Builder() 1452 .withoutIpReachabilityMonitor() 1453 .withoutIPv4() 1454 .build(); 1455 mIpc.startProvisioning(config); 1456 1457 InOrder inOrder = inOrder(mCb); 1458 ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class); 1459 1460 final String dnsServer = "2001:4860:4860::64"; 1461 final String lowlifeDnsServer = "2001:4860:4860::6464"; 1462 1463 final ByteBuffer pio = buildPioOption(600, 300, "2001:db8:1::/64"); 1464 ByteBuffer rdnss1 = buildRdnssOption(60, lowlifeDnsServer); 1465 ByteBuffer rdnss2 = buildRdnssOption(600, dnsServer); 1466 ByteBuffer ra = buildRaPacket(pio, rdnss1, rdnss2); 1467 1468 LinkProperties lp = doIpv6OnlyProvisioning(inOrder, ra); 1469 1470 // Expect that DNS servers with lifetimes below CONFIG_MIN_RDNSS_LIFETIME are not accepted. 1471 assertNotNull(lp); 1472 assertEquals(1, lp.getDnsServers().size()); 1473 assertTrue(lp.getDnsServers().contains(InetAddress.getByName(dnsServer))); 1474 1475 // If the RDNSS lifetime is above the minimum, the DNS server is accepted. 1476 rdnss1 = buildRdnssOption(68, lowlifeDnsServer); 1477 ra = buildRaPacket(pio, rdnss1, rdnss2); 1478 mPacketReader.sendResponse(ra); 1479 inOrder.verify(mCb, timeout(TEST_TIMEOUT_MS)).onLinkPropertiesChange(captor.capture()); 1480 lp = captor.getValue(); 1481 assertNotNull(lp); 1482 assertEquals(2, lp.getDnsServers().size()); 1483 assertTrue(lp.getDnsServers().contains(InetAddress.getByName(dnsServer))); 1484 assertTrue(lp.getDnsServers().contains(InetAddress.getByName(lowlifeDnsServer))); 1485 1486 // Expect that setting RDNSS lifetime of 0 causes loss of provisioning. 1487 rdnss1 = buildRdnssOption(0, dnsServer); 1488 rdnss2 = buildRdnssOption(0, lowlifeDnsServer); 1489 ra = buildRaPacket(pio, rdnss1, rdnss2); 1490 mPacketReader.sendResponse(ra); 1491 1492 inOrder.verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningFailure(captor.capture()); 1493 lp = captor.getValue(); 1494 assertNotNull(lp); 1495 assertEquals(0, lp.getDnsServers().size()); 1496 reset(mCb); 1497 } 1498 1499 private void expectNat64PrefixUpdate(InOrder inOrder, IpPrefix expected) throws Exception { 1500 inOrder.verify(mCb, timeout(TEST_TIMEOUT_MS)).onLinkPropertiesChange( 1501 argThat(lp -> Objects.equals(expected, lp.getNat64Prefix()))); 1502 1503 } 1504 1505 private void expectNoNat64PrefixUpdate(InOrder inOrder, IpPrefix unchanged) throws Exception { 1506 inOrder.verify(mCb, timeout(TEST_TIMEOUT_MS).times(0)).onLinkPropertiesChange(argThat( 1507 lp -> !Objects.equals(unchanged, lp.getNat64Prefix()))); 1508 1509 } 1510 1511 @Test @IgnoreUpTo(Build.VERSION_CODES.Q) 1512 public void testPref64Option() throws Exception { 1513 assumeTrue(ConstantsShim.VERSION > Build.VERSION_CODES.Q); 1514 1515 ProvisioningConfiguration config = new ProvisioningConfiguration.Builder() 1516 .withoutIpReachabilityMonitor() 1517 .withoutIPv4() 1518 .build(); 1519 mIpc.startProvisioning(config); 1520 1521 final String dnsServer = "2001:4860:4860::64"; 1522 final IpPrefix prefix = new IpPrefix("64:ff9b::/96"); 1523 final IpPrefix otherPrefix = new IpPrefix("2001:db8:64::/96"); 1524 1525 final ByteBuffer pio = buildPioOption(600, 300, "2001:db8:1::/64"); 1526 ByteBuffer rdnss = buildRdnssOption(600, dnsServer); 1527 ByteBuffer pref64 = new StructNdOptPref64(prefix, 600).toByteBuffer(); 1528 ByteBuffer ra = buildRaPacket(pio, rdnss, pref64); 1529 1530 // The NAT64 prefix might be detected before or after provisioning success. 1531 // Don't test order between these two events. 1532 LinkProperties lp = doIpv6OnlyProvisioning(null /*inOrder*/, ra); 1533 expectAlarmSet(null /*inOrder*/, "PREF64", 600); 1534 1535 // From now on expect events in order. 1536 InOrder inOrder = inOrder(mCb, mAlarm); 1537 if (lp.getNat64Prefix() != null) { 1538 assertEquals(prefix, lp.getNat64Prefix()); 1539 } else { 1540 expectNat64PrefixUpdate(inOrder, prefix); 1541 } 1542 1543 // Increase the lifetime and expect the prefix not to change. 1544 pref64 = new StructNdOptPref64(prefix, 1800).toByteBuffer(); 1545 ra = buildRaPacket(pio, rdnss, pref64); 1546 mPacketReader.sendResponse(ra); 1547 OnAlarmListener pref64Alarm = expectAlarmSet(inOrder, "PREF64", 1800); 1548 expectNoNat64PrefixUpdate(inOrder, prefix); 1549 reset(mCb, mAlarm); 1550 1551 // Reduce the lifetime and expect to reschedule expiry. 1552 pref64 = new StructNdOptPref64(prefix, 1500).toByteBuffer(); 1553 ra = buildRaPacket(pio, rdnss, pref64); 1554 mPacketReader.sendResponse(ra); 1555 pref64Alarm = expectAlarmSet(inOrder, "PREF64", 1496); 1556 expectNoNat64PrefixUpdate(inOrder, prefix); 1557 reset(mCb, mAlarm); 1558 1559 // Withdraw the prefix and expect it to be set to null. 1560 pref64 = new StructNdOptPref64(prefix, 0).toByteBuffer(); 1561 ra = buildRaPacket(pio, rdnss, pref64); 1562 mPacketReader.sendResponse(ra); 1563 expectAlarmCancelled(inOrder, pref64Alarm); 1564 expectNat64PrefixUpdate(inOrder, null); 1565 reset(mCb, mAlarm); 1566 1567 // Re-announce the prefix. 1568 pref64 = new StructNdOptPref64(prefix, 600).toByteBuffer(); 1569 ra = buildRaPacket(pio, rdnss, pref64); 1570 mPacketReader.sendResponse(ra); 1571 expectAlarmSet(inOrder, "PREF64", 600); 1572 expectNat64PrefixUpdate(inOrder, prefix); 1573 reset(mCb, mAlarm); 1574 1575 // Announce two prefixes. Don't expect any update because if there is already a NAT64 1576 // prefix, any new prefix is ignored. 1577 ByteBuffer otherPref64 = new StructNdOptPref64(otherPrefix, 1200).toByteBuffer(); 1578 ra = buildRaPacket(pio, rdnss, pref64, otherPref64); 1579 mPacketReader.sendResponse(ra); 1580 expectAlarmSet(inOrder, "PREF64", 600); 1581 expectNoNat64PrefixUpdate(inOrder, prefix); 1582 reset(mCb, mAlarm); 1583 1584 // Withdraw the old prefix and continue to announce the new one. Expect a prefix change. 1585 pref64 = new StructNdOptPref64(prefix, 0).toByteBuffer(); 1586 ra = buildRaPacket(pio, rdnss, pref64, otherPref64); 1587 mPacketReader.sendResponse(ra); 1588 expectAlarmCancelled(inOrder, pref64Alarm); 1589 // Need a different OnAlarmListener local variable because posting it to the handler in the 1590 // lambda below requires it to be final. 1591 final OnAlarmListener lastAlarm = expectAlarmSet(inOrder, "PREF64", 1200); 1592 expectNat64PrefixUpdate(inOrder, otherPrefix); 1593 reset(mCb, mAlarm); 1594 1595 // Simulate prefix expiry. 1596 mIpc.getHandler().post(() -> lastAlarm.onAlarm()); 1597 expectAlarmCancelled(inOrder, pref64Alarm); 1598 expectNat64PrefixUpdate(inOrder, null); 1599 1600 // Announce a non-/96 prefix and expect it to be ignored. 1601 IpPrefix invalidPrefix = new IpPrefix("64:ff9b::/64"); 1602 pref64 = new StructNdOptPref64(invalidPrefix, 1200).toByteBuffer(); 1603 ra = buildRaPacket(pio, rdnss, pref64); 1604 mPacketReader.sendResponse(ra); 1605 expectNoNat64PrefixUpdate(inOrder, invalidPrefix); 1606 1607 // Re-announce the prefix. 1608 pref64 = new StructNdOptPref64(prefix, 600).toByteBuffer(); 1609 ra = buildRaPacket(pio, rdnss, pref64); 1610 mPacketReader.sendResponse(ra); 1611 final OnAlarmListener clearAlarm = expectAlarmSet(inOrder, "PREF64", 600); 1612 expectNat64PrefixUpdate(inOrder, prefix); 1613 reset(mCb, mAlarm); 1614 1615 // Check that the alarm is cancelled when IpClient is stopped. 1616 mIpc.stop(); 1617 HandlerUtilsKt.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS); 1618 expectAlarmCancelled(inOrder, clearAlarm); 1619 expectNat64PrefixUpdate(inOrder, null); 1620 1621 // Check that even if the alarm was already in the message queue while it was cancelled, it 1622 // is safely ignored. 1623 mIpc.getHandler().post(() -> clearAlarm.onAlarm()); 1624 HandlerUtilsKt.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS); 1625 } 1626 1627 private void addIpAddressAndWaitForIt(final String iface) throws Exception { 1628 final CountDownLatch latch = new CountDownLatch(1); 1629 1630 final String addr1 = "192.0.2.99"; 1631 final String addr2 = "192.0.2.3"; 1632 final int prefixLength = 26; 1633 1634 // Add two IPv4 addresses to the specified interface, and proceed when the NetworkObserver 1635 // has seen the second one. This ensures that every other NetworkObserver registered with 1636 // mNetworkObserverRegistry - in particular, IpClient's - has seen the addition of the first 1637 // address. 1638 final LinkAddress trigger = new LinkAddress(addr2 + "/" + prefixLength); 1639 NetworkObserver observer = new NetworkObserver() { 1640 @Override 1641 public void onInterfaceAddressUpdated(LinkAddress address, String ifName) { 1642 if (ifName.equals(iface) && address.isSameAddressAs(trigger)) { 1643 latch.countDown(); 1644 } 1645 } 1646 }; 1647 1648 mNetworkObserverRegistry.registerObserverForNonblockingCallback(observer); 1649 try { 1650 mNetd.interfaceAddAddress(iface, addr1, prefixLength); 1651 mNetd.interfaceAddAddress(iface, addr2, prefixLength); 1652 assertTrue("Trigger IP address " + addr2 + " not seen after " + TEST_TIMEOUT_MS + "ms", 1653 latch.await(TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS)); 1654 } finally { 1655 mNetworkObserverRegistry.unregisterObserver(observer); 1656 } 1657 1658 // Wait for IpClient to process the addition of the address. 1659 HandlerUtilsKt.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS); 1660 } 1661 1662 private void doIPv4OnlyProvisioningAndExitWithLeftAddress() throws Exception { 1663 final long currentTime = System.currentTimeMillis(); 1664 performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S, 1665 true /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */, 1666 TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */); 1667 verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR)); 1668 assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU); 1669 1670 // Stop IpClient and expect a final LinkProperties callback with an empty LP. 1671 mIpc.stop(); 1672 verify(mCb, timeout(TEST_TIMEOUT_MS)).onLinkPropertiesChange(argThat( 1673 x -> x.getAddresses().size() == 0 1674 && x.getRoutes().size() == 0 1675 && x.getDnsServers().size() == 0)); 1676 reset(mCb); 1677 1678 // Pretend that something else (e.g., Tethering) used the interface and left an IP address 1679 // configured on it. When IpClient starts, it must clear this address before proceeding. 1680 // The address must be noticed before startProvisioning is called, or IpClient will 1681 // immediately declare provisioning success due to the presence of an IPv4 address. 1682 // The address must be IPv4 because IpClient clears IPv6 addresses on startup. 1683 // 1684 // TODO: once IpClient gets IP addresses directly from netlink instead of from netd, it 1685 // may be sufficient to call waitForIdle to see if IpClient has seen the address. 1686 addIpAddressAndWaitForIt(mIfaceName); 1687 } 1688 1689 @Test 1690 public void testIpClientClearingIpAddressState() throws Exception { 1691 doIPv4OnlyProvisioningAndExitWithLeftAddress(); 1692 1693 ProvisioningConfiguration config = new ProvisioningConfiguration.Builder() 1694 .withoutIpReachabilityMonitor() 1695 .build(); 1696 mIpc.startProvisioning(config); 1697 1698 sendBasicRouterAdvertisement(true /*waitForRs*/); 1699 1700 // Check that the IPv4 addresses configured earlier are not in LinkProperties... 1701 ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class); 1702 verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(captor.capture()); 1703 assertFalse(captor.getValue().hasIpv4Address()); 1704 1705 // ... or configured on the interface. 1706 InterfaceConfigurationParcel cfg = mNetd.interfaceGetCfg(mIfaceName); 1707 assertEquals("0.0.0.0", cfg.ipv4Addr); 1708 } 1709 1710 @Test 1711 public void testIpClientClearingIpAddressState_enablePreconnection() throws Exception { 1712 doIPv4OnlyProvisioningAndExitWithLeftAddress(); 1713 1714 // Enter ClearingIpAddressesState to clear the remaining IPv4 addresses and transition to 1715 // PreconnectionState instead of RunningState. 1716 startIpClientProvisioning(false /* isDhcpLeaseCacheEnabled */, 1717 false /* shouldReplyRapidCommitAck */, true /* isDhcpPreConnectionEnabled */, 1718 false /* isDhcpIpConflictDetectEnabled */); 1719 assertDiscoverPacketOnPreconnectionStart(); 1720 1721 // Force to enter RunningState. 1722 mIpc.notifyPreconnectionComplete(false /* abort */); 1723 HandlerUtilsKt.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS); 1724 } 1725 1726 @Test 1727 public void testDhcpClientPreconnection_success() throws Exception { 1728 doIpClientProvisioningWithPreconnectionTest(true /* shouldReplyRapidCommitAck */, 1729 false /* shouldAbortPreconnection */, false /* shouldFirePreconnectionTimeout */, 1730 false /* timeoutBeforePreconnectionComplete */); 1731 } 1732 1733 @Test 1734 public void testDhcpClientPreconnection_SuccessWithoutRapidCommit() throws Exception { 1735 doIpClientProvisioningWithPreconnectionTest(false /* shouldReplyRapidCommitAck */, 1736 false /* shouldAbortPreconnection */, false /* shouldFirePreconnectionTimeout */, 1737 false /* timeoutBeforePreconnectionComplete */); 1738 } 1739 1740 @Test 1741 public void testDhcpClientPreconnection_Abort() throws Exception { 1742 doIpClientProvisioningWithPreconnectionTest(true /* shouldReplyRapidCommitAck */, 1743 true /* shouldAbortPreconnection */, false /* shouldFirePreconnectionTimeout */, 1744 false /* timeoutBeforePreconnectionComplete */); 1745 } 1746 1747 @Test 1748 public void testDhcpClientPreconnection_AbortWithoutRapiCommit() throws Exception { 1749 doIpClientProvisioningWithPreconnectionTest(false /* shouldReplyRapidCommitAck */, 1750 true /* shouldAbortPreconnection */, false /* shouldFirePreconnectionTimeout */, 1751 false /* timeoutBeforePreconnectionComplete */); 1752 } 1753 1754 @Test 1755 public void testDhcpClientPreconnection_TimeoutBeforeAbort() throws Exception { 1756 doIpClientProvisioningWithPreconnectionTest(true /* shouldReplyRapidCommitAck */, 1757 true /* shouldAbortPreconnection */, true /* shouldFirePreconnectionTimeout */, 1758 true /* timeoutBeforePreconnectionComplete */); 1759 } 1760 1761 @Test 1762 public void testDhcpClientPreconnection_TimeoutBeforeAbortWithoutRapidCommit() 1763 throws Exception { 1764 doIpClientProvisioningWithPreconnectionTest(false /* shouldReplyRapidCommitAck */, 1765 true /* shouldAbortPreconnection */, true /* shouldFirePreconnectionTimeout */, 1766 true /* timeoutBeforePreconnectionComplete */); 1767 } 1768 1769 @Test 1770 public void testDhcpClientPreconnection_TimeoutafterAbort() throws Exception { 1771 doIpClientProvisioningWithPreconnectionTest(true /* shouldReplyRapidCommitAck */, 1772 true /* shouldAbortPreconnection */, true /* shouldFirePreconnectionTimeout */, 1773 false /* timeoutBeforePreconnectionComplete */); 1774 } 1775 1776 @Test 1777 public void testDhcpClientPreconnection_TimeoutAfterAbortWithoutRapidCommit() throws Exception { 1778 doIpClientProvisioningWithPreconnectionTest(false /* shouldReplyRapidCommitAck */, 1779 true /* shouldAbortPreconnection */, true /* shouldFirePreconnectionTimeout */, 1780 false /* timeoutBeforePreconnectionComplete */); 1781 } 1782 1783 @Test 1784 public void testDhcpClientPreconnection_TimeoutBeforeSuccess() throws Exception { 1785 doIpClientProvisioningWithPreconnectionTest(true /* shouldReplyRapidCommitAck */, 1786 false /* shouldAbortPreconnection */, true /* shouldFirePreconnectionTimeout */, 1787 true /* timeoutBeforePreconnectionComplete */); 1788 } 1789 1790 @Test 1791 public void testDhcpClientPreconnection_TimeoutBeforeSuccessWithoutRapidCommit() 1792 throws Exception { 1793 doIpClientProvisioningWithPreconnectionTest(false /* shouldReplyRapidCommitAck */, 1794 false /* shouldAbortPreconnection */, true /* shouldFirePreconnectionTimeout */, 1795 true /* timeoutBeforePreconnectionComplete */); 1796 } 1797 1798 @Test 1799 public void testDhcpClientPreconnection_TimeoutAfterSuccess() throws Exception { 1800 doIpClientProvisioningWithPreconnectionTest(true /* shouldReplyRapidCommitAck */, 1801 false /* shouldAbortPreconnection */, true /* shouldFirePreconnectionTimeout */, 1802 false /* timeoutBeforePreconnectionComplete */); 1803 } 1804 1805 @Test 1806 public void testDhcpClientPreconnection_TimeoutAfterSuccessWithoutRapidCommit() 1807 throws Exception { 1808 doIpClientProvisioningWithPreconnectionTest(false /* shouldReplyRapidCommitAck */, 1809 false /* shouldAbortPreconnection */, true /* shouldFirePreconnectionTimeout */, 1810 false /* timeoutBeforePreconnectionComplete */); 1811 } 1812 1813 @Test 1814 public void testDhcpClientPreconnection_WithoutLayer2InfoWhenStartingProv() throws Exception { 1815 // For FILS connection, current bssid (also l2key and cluster) is still null when 1816 // starting provisioning since the L2 link hasn't been established yet. Ensure that 1817 // IpClient won't crash even if initializing an Layer2Info class with null members. 1818 ProvisioningConfiguration.Builder prov = new ProvisioningConfiguration.Builder() 1819 .withoutIpReachabilityMonitor() 1820 .withoutIPv6() 1821 .withPreconnection() 1822 .withLayer2Information(new Layer2Information(null /* l2key */, null /* cluster */, 1823 null /* bssid */)); 1824 1825 mIpc.startProvisioning(prov.build()); 1826 assertDiscoverPacketOnPreconnectionStart(); 1827 verify(mCb).setNeighborDiscoveryOffload(true); 1828 1829 // Force IpClient transition to RunningState from PreconnectionState. 1830 mIpc.notifyPreconnectionComplete(false /* success */); 1831 HandlerUtilsKt.waitForIdle(mDependencies.mDhcpClient.getHandler(), TEST_TIMEOUT_MS); 1832 verify(mCb, timeout(TEST_TIMEOUT_MS)).setFallbackMulticastFilter(false); 1833 } 1834 1835 @Test 1836 public void testDhcpDecline_conflictByArpReply() throws Exception { 1837 doIpAddressConflictDetectionTest(true /* causeIpAddressConflict */, 1838 false /* shouldReplyRapidCommitAck */, true /* isDhcpIpConflictDetectEnabled */, 1839 true /* shouldResponseArpReply */); 1840 } 1841 1842 @Test 1843 public void testDhcpDecline_conflictByArpProbe() throws Exception { 1844 doIpAddressConflictDetectionTest(true /* causeIpAddressConflict */, 1845 false /* shouldReplyRapidCommitAck */, true /* isDhcpIpConflictDetectEnabled */, 1846 false /* shouldResponseArpReply */); 1847 } 1848 1849 @Test 1850 public void testDhcpDecline_EnableFlagWithoutIpConflict() throws Exception { 1851 doIpAddressConflictDetectionTest(false /* causeIpAddressConflict */, 1852 false /* shouldReplyRapidCommitAck */, true /* isDhcpIpConflictDetectEnabled */, 1853 false /* shouldResponseArpReply */); 1854 } 1855 1856 @Test 1857 public void testDhcpDecline_WithoutIpConflict() throws Exception { 1858 doIpAddressConflictDetectionTest(false /* causeIpAddressConflict */, 1859 false /* shouldReplyRapidCommitAck */, false /* isDhcpIpConflictDetectEnabled */, 1860 false /* shouldResponseArpReply */); 1861 } 1862 1863 @Test 1864 public void testDhcpDecline_WithRapidCommitWithoutIpConflict() throws Exception { 1865 doIpAddressConflictDetectionTest(false /* causeIpAddressConflict */, 1866 true /* shouldReplyRapidCommitAck */, false /* isDhcpIpConflictDetectEnabled */, 1867 false /* shouldResponseArpReply */); 1868 } 1869 1870 @Test 1871 public void testDhcpDecline_WithRapidCommitConflictByArpReply() throws Exception { 1872 doIpAddressConflictDetectionTest(true /* causeIpAddressConflict */, 1873 true /* shouldReplyRapidCommitAck */, true /* isDhcpIpConflictDetectEnabled */, 1874 true /* shouldResponseArpReply */); 1875 } 1876 1877 @Test 1878 public void testDhcpDecline_WithRapidCommitConflictByArpProbe() throws Exception { 1879 doIpAddressConflictDetectionTest(true /* causeIpAddressConflict */, 1880 true /* shouldReplyRapidCommitAck */, true /* isDhcpIpConflictDetectEnabled */, 1881 false /* shouldResponseArpReply */); 1882 } 1883 1884 @Test 1885 public void testDhcpDecline_EnableFlagWithRapidCommitWithoutIpConflict() throws Exception { 1886 doIpAddressConflictDetectionTest(false /* causeIpAddressConflict */, 1887 true /* shouldReplyRapidCommitAck */, true /* isDhcpIpConflictDetectEnabled */, 1888 false /* shouldResponseArpReply */); 1889 } 1890 1891 @Test 1892 public void testHostname_enableConfig() throws Exception { 1893 final long currentTime = System.currentTimeMillis(); 1894 final List<DhcpPacket> sentPackets = performDhcpHandshake(true /* isSuccessLease */, 1895 TEST_LEASE_DURATION_S, true /* isDhcpLeaseCacheEnabled */, 1896 false /* isDhcpRapidCommitEnabled */, TEST_DEFAULT_MTU, 1897 false /* isDhcpIpConflictDetectEnabled */, 1898 true /* isHostnameConfigurationEnabled */, TEST_HOST_NAME /* hostname */, 1899 null /* captivePortalApiUrl */, null /* displayName */, null /* scanResultInfo */); 1900 assertEquals(2, sentPackets.size()); 1901 verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR)); 1902 assertHostname(true, TEST_HOST_NAME, TEST_HOST_NAME_TRANSLITERATION, sentPackets); 1903 assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU); 1904 } 1905 1906 @Test 1907 public void testHostname_disableConfig() throws Exception { 1908 final long currentTime = System.currentTimeMillis(); 1909 final List<DhcpPacket> sentPackets = performDhcpHandshake(true /* isSuccessLease */, 1910 TEST_LEASE_DURATION_S, true /* isDhcpLeaseCacheEnabled */, 1911 false /* isDhcpRapidCommitEnabled */, TEST_DEFAULT_MTU, 1912 false /* isDhcpIpConflictDetectEnabled */, 1913 false /* isHostnameConfigurationEnabled */, TEST_HOST_NAME, 1914 null /* captivePortalApiUrl */, null /* displayName */, null /* scanResultInfo */); 1915 assertEquals(2, sentPackets.size()); 1916 verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR)); 1917 assertHostname(false, TEST_HOST_NAME, TEST_HOST_NAME_TRANSLITERATION, sentPackets); 1918 assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU); 1919 } 1920 1921 @Test 1922 public void testHostname_enableConfigWithNullHostname() throws Exception { 1923 final long currentTime = System.currentTimeMillis(); 1924 final List<DhcpPacket> sentPackets = performDhcpHandshake(true /* isSuccessLease */, 1925 TEST_LEASE_DURATION_S, true /* isDhcpLeaseCacheEnabled */, 1926 false /* isDhcpRapidCommitEnabled */, TEST_DEFAULT_MTU, 1927 false /* isDhcpIpConflictDetectEnabled */, 1928 true /* isHostnameConfigurationEnabled */, null /* hostname */, 1929 null /* captivePortalApiUrl */, null /* displayName */, null /* scanResultInfo */); 1930 assertEquals(2, sentPackets.size()); 1931 verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR)); 1932 assertHostname(true, null /* hostname */, null /* hostnameAfterTransliteration */, 1933 sentPackets); 1934 assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU); 1935 } 1936 1937 private void runDhcpClientCaptivePortalApiTest(boolean featureEnabled, 1938 boolean serverSendsOption) throws Exception { 1939 startIpClientProvisioning(false /* isDhcpLeaseCacheEnabled */, 1940 false /* shouldReplyRapidCommitAck */, false /* isPreConnectionEnabled */, 1941 false /* isDhcpIpConflictDetectEnabled */); 1942 final DhcpPacket discover = getNextDhcpPacket(); 1943 assertTrue(discover instanceof DhcpDiscoverPacket); 1944 assertEquals(featureEnabled, discover.hasRequestedParam(DhcpPacket.DHCP_CAPTIVE_PORTAL)); 1945 1946 // Send Offer and handle Request -> Ack 1947 final String serverSentUrl = serverSendsOption ? TEST_CAPTIVE_PORTAL_URL : null; 1948 mPacketReader.sendResponse(buildDhcpOfferPacket(discover, TEST_LEASE_DURATION_S, 1949 (short) TEST_DEFAULT_MTU, serverSentUrl)); 1950 final int testMtu = 1345; 1951 handleDhcpPackets(true /* isSuccessLease */, TEST_LEASE_DURATION_S, 1952 false /* shouldReplyRapidCommitAck */, testMtu, serverSentUrl); 1953 1954 final Uri expectedUrl = featureEnabled && serverSendsOption 1955 ? Uri.parse(TEST_CAPTIVE_PORTAL_URL) : null; 1956 // Wait for LinkProperties containing DHCP-obtained info, such as MTU 1957 final ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class); 1958 verify(mCb, timeout(TEST_TIMEOUT_MS)).onLinkPropertiesChange( 1959 argThat(lp -> lp.getMtu() == testMtu)); 1960 1961 // Ensure that the URL was set as expected in the callbacks. 1962 // Can't verify the URL up to Q as there is no such attribute in LinkProperties. 1963 if (!ShimUtils.isAtLeastR()) return; 1964 verify(mCb).onLinkPropertiesChange(captor.capture()); 1965 assertTrue(captor.getAllValues().stream().anyMatch( 1966 lp -> Objects.equals(expectedUrl, lp.getCaptivePortalApiUrl()))); 1967 } 1968 1969 @Test 1970 public void testDhcpClientCaptivePortalApiEnabled() throws Exception { 1971 // Only run the test on platforms / builds where the API is enabled 1972 assumeTrue(CaptivePortalDataShimImpl.isSupported()); 1973 runDhcpClientCaptivePortalApiTest(true /* featureEnabled */, true /* serverSendsOption */); 1974 } 1975 1976 @Test 1977 public void testDhcpClientCaptivePortalApiEnabled_NoUrl() throws Exception { 1978 // Only run the test on platforms / builds where the API is enabled 1979 assumeTrue(CaptivePortalDataShimImpl.isSupported()); 1980 runDhcpClientCaptivePortalApiTest(true /* featureEnabled */, false /* serverSendsOption */); 1981 } 1982 1983 @Test 1984 public void testDhcpClientCaptivePortalApiDisabled() throws Exception { 1985 // Only run the test on platforms / builds where the API is disabled 1986 assumeFalse(CaptivePortalDataShimImpl.isSupported()); 1987 runDhcpClientCaptivePortalApiTest(false /* featureEnabled */, true /* serverSendsOption */); 1988 } 1989 1990 private ScanResultInfo makeScanResultInfo(final int id, final String ssid, 1991 final String bssid, final byte[] oui, final byte type, final byte[] data) { 1992 final ByteBuffer payload = ByteBuffer.allocate(4 + data.length); 1993 payload.put(oui); 1994 payload.put(type); 1995 payload.put(data); 1996 payload.flip(); 1997 final ScanResultInfo.InformationElement ie = 1998 new ScanResultInfo.InformationElement(id /* IE id */, payload); 1999 return new ScanResultInfo(ssid, bssid, Collections.singletonList(ie)); 2000 } 2001 2002 private ScanResultInfo makeScanResultInfo(final String ssid, final String bssid) { 2003 byte[] data = new byte[10]; 2004 new Random().nextBytes(data); 2005 return makeScanResultInfo(0xdd, ssid, bssid, TEST_AP_OUI, (byte) 0x06, data); 2006 } 2007 2008 private void doUpstreamHotspotDetectionTest(final int id, final String displayName, 2009 final String ssid, final byte[] oui, final byte type, final byte[] data, 2010 final boolean expectMetered) throws Exception { 2011 final ScanResultInfo info = makeScanResultInfo(id, ssid, TEST_DEFAULT_BSSID, oui, type, 2012 data); 2013 final long currentTime = System.currentTimeMillis(); 2014 final List<DhcpPacket> sentPackets = performDhcpHandshake(true /* isSuccessLease */, 2015 TEST_LEASE_DURATION_S, true /* isDhcpLeaseCacheEnabled */, 2016 false /* isDhcpRapidCommitEnabled */, TEST_DEFAULT_MTU, 2017 false /* isDhcpIpConflictDetectEnabled */, 2018 false /* isHostnameConfigurationEnabled */, null /* hostname */, 2019 null /* captivePortalApiUrl */, displayName, info /* scanResultInfo */); 2020 assertEquals(2, sentPackets.size()); 2021 verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR)); 2022 2023 ArgumentCaptor<DhcpResultsParcelable> captor = 2024 ArgumentCaptor.forClass(DhcpResultsParcelable.class); 2025 verify(mCb, timeout(TEST_TIMEOUT_MS)).onNewDhcpResults(captor.capture()); 2026 DhcpResults lease = fromStableParcelable(captor.getValue()); 2027 assertNotNull(lease); 2028 assertEquals(lease.getIpAddress().getAddress(), CLIENT_ADDR); 2029 assertEquals(lease.getGateway(), SERVER_ADDR); 2030 assertEquals(1, lease.getDnsServers().size()); 2031 assertTrue(lease.getDnsServers().contains(SERVER_ADDR)); 2032 assertEquals(lease.getServerAddress(), SERVER_ADDR); 2033 assertEquals(lease.getMtu(), TEST_DEFAULT_MTU); 2034 2035 if (expectMetered) { 2036 assertEquals(lease.vendorInfo, DhcpPacket.VENDOR_INFO_ANDROID_METERED); 2037 } else { 2038 assertNull(lease.vendorInfo); 2039 } 2040 2041 assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU); 2042 } 2043 2044 @Test 2045 public void testUpstreamHotspotDetection() throws Exception { 2046 byte[] data = new byte[10]; 2047 new Random().nextBytes(data); 2048 doUpstreamHotspotDetectionTest(0xdd, "\"ssid\"", "ssid", 2049 new byte[] { (byte) 0x00, (byte) 0x17, (byte) 0xF2 }, (byte) 0x06, data, 2050 true /* expectMetered */); 2051 } 2052 2053 @Test 2054 public void testUpstreamHotspotDetection_incorrectIeId() throws Exception { 2055 byte[] data = new byte[10]; 2056 new Random().nextBytes(data); 2057 doUpstreamHotspotDetectionTest(0xdc, "\"ssid\"", "ssid", 2058 new byte[] { (byte) 0x00, (byte) 0x17, (byte) 0xF2 }, (byte) 0x06, data, 2059 false /* expectMetered */); 2060 } 2061 2062 @Test 2063 public void testUpstreamHotspotDetection_incorrectOUI() throws Exception { 2064 byte[] data = new byte[10]; 2065 new Random().nextBytes(data); 2066 doUpstreamHotspotDetectionTest(0xdd, "\"ssid\"", "ssid", 2067 new byte[] { (byte) 0x00, (byte) 0x1A, (byte) 0x11 }, (byte) 0x06, data, 2068 false /* expectMetered */); 2069 } 2070 2071 @Test 2072 public void testUpstreamHotspotDetection_incorrectSsid() throws Exception { 2073 byte[] data = new byte[10]; 2074 new Random().nextBytes(data); 2075 doUpstreamHotspotDetectionTest(0xdd, "\"another ssid\"", "ssid", 2076 new byte[] { (byte) 0x00, (byte) 0x17, (byte) 0xF2 }, (byte) 0x06, data, 2077 false /* expectMetered */); 2078 } 2079 2080 @Test 2081 public void testUpstreamHotspotDetection_incorrectType() throws Exception { 2082 byte[] data = new byte[10]; 2083 new Random().nextBytes(data); 2084 doUpstreamHotspotDetectionTest(0xdd, "\"ssid\"", "ssid", 2085 new byte[] { (byte) 0x00, (byte) 0x17, (byte) 0xF2 }, (byte) 0x0a, data, 2086 false /* expectMetered */); 2087 } 2088 2089 @Test 2090 public void testUpstreamHotspotDetection_zeroLengthData() throws Exception { 2091 byte[] data = new byte[0]; 2092 doUpstreamHotspotDetectionTest(0xdd, "\"ssid\"", "ssid", 2093 new byte[] { (byte) 0x00, (byte) 0x17, (byte) 0xF2 }, (byte) 0x06, data, 2094 true /* expectMetered */); 2095 } 2096 2097 private void doDhcpRoamingTest(final boolean hasMismatchedIpAddress, final String displayName, 2098 final String ssid, final String bssid, final boolean expectRoaming) throws Exception { 2099 long currentTime = System.currentTimeMillis(); 2100 final ScanResultInfo scanResultInfo = makeScanResultInfo(ssid, bssid); 2101 2102 doAnswer(invocation -> { 2103 // we don't rely on the Init-Reboot state to renew previous cached IP lease. 2104 // Just return null and force state machine enter INIT state. 2105 final String l2Key = invocation.getArgument(0); 2106 ((OnNetworkAttributesRetrievedListener) invocation.getArgument(1)) 2107 .onNetworkAttributesRetrieved(new Status(SUCCESS), l2Key, null); 2108 return null; 2109 }).when(mIpMemoryStore).retrieveNetworkAttributes(eq(TEST_L2KEY), any()); 2110 2111 performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S, 2112 true /* isDhcpLeaseCacheEnabled */, false /* isDhcpRapidCommitEnabled */, 2113 TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */, 2114 true /* isHostnameConfigurationEnabled */, null /* hostname */, 2115 null /* captivePortalApiUrl */, displayName, scanResultInfo); 2116 verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR)); 2117 assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU); 2118 2119 // simulate the roaming by updating bssid. 2120 final Layer2InformationParcelable roamingInfo = new Layer2InformationParcelable(); 2121 roamingInfo.bssid = MacAddress.fromString(TEST_DHCP_ROAM_BSSID); 2122 roamingInfo.l2Key = TEST_DHCP_ROAM_L2KEY; 2123 roamingInfo.cluster = TEST_DHCP_ROAM_CLUSTER; 2124 mIpc.updateLayer2Information(roamingInfo); 2125 2126 currentTime = System.currentTimeMillis(); 2127 reset(mIpMemoryStore); 2128 reset(mCb); 2129 if (!expectRoaming) { 2130 assertIpMemoryNeverStoreNetworkAttributes(); 2131 return; 2132 } 2133 // check DHCPREQUEST broadcast sent to renew IP address. 2134 DhcpPacket packet; 2135 packet = getNextDhcpPacket(); 2136 assertTrue(packet instanceof DhcpRequestPacket); 2137 assertEquals(packet.mClientIp, CLIENT_ADDR); // client IP 2138 assertNull(packet.mRequestedIp); // requested IP option 2139 assertNull(packet.mServerIdentifier); // server ID 2140 2141 mPacketReader.sendResponse(buildDhcpAckPacket(packet, 2142 hasMismatchedIpAddress ? CLIENT_ADDR_NEW : CLIENT_ADDR, TEST_LEASE_DURATION_S, 2143 (short) TEST_DEFAULT_MTU, false /* rapidcommit */, null /* captivePortalUrl */)); 2144 HandlerUtilsKt.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS); 2145 if (hasMismatchedIpAddress) { 2146 // notifyFailure 2147 ArgumentCaptor<DhcpResultsParcelable> captor = 2148 ArgumentCaptor.forClass(DhcpResultsParcelable.class); 2149 verify(mCb, timeout(TEST_TIMEOUT_MS)).onNewDhcpResults(captor.capture()); 2150 DhcpResults lease = fromStableParcelable(captor.getValue()); 2151 assertNull(lease); 2152 2153 // roll back to INIT state. 2154 packet = getNextDhcpPacket(); 2155 assertTrue(packet instanceof DhcpDiscoverPacket); 2156 } else { 2157 assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, 2158 TEST_DEFAULT_MTU); 2159 } 2160 } 2161 2162 @Test 2163 public void testDhcpRoaming() throws Exception { 2164 doDhcpRoamingTest(false /* hasMismatchedIpAddress */, "\"0001docomo\"" /* display name */, 2165 TEST_DHCP_ROAM_SSID, TEST_DEFAULT_BSSID, true /* expectRoaming */); 2166 } 2167 2168 @Test 2169 public void testDhcpRoaming_invalidBssid() throws Exception { 2170 doDhcpRoamingTest(false /* hasMismatchedIpAddress */, "\"0001docomo\"" /* display name */, 2171 TEST_DHCP_ROAM_SSID, TEST_DHCP_ROAM_BSSID, false /* expectRoaming */); 2172 } 2173 2174 @Test 2175 public void testDhcpRoaming_invalidSsid() throws Exception { 2176 doDhcpRoamingTest(false /* hasMismatchedIpAddress */, "\"0001docomo\"" /* display name */, 2177 TEST_DEFAULT_SSID, TEST_DEFAULT_BSSID, false /* expectRoaming */); 2178 } 2179 2180 @Test 2181 public void testDhcpRoaming_invalidDisplayName() throws Exception { 2182 doDhcpRoamingTest(false /* hasMismatchedIpAddress */, "\"test-ssid\"" /* display name */, 2183 TEST_DHCP_ROAM_SSID, TEST_DEFAULT_BSSID, false /* expectRoaming */); 2184 } 2185 2186 @Test 2187 public void testDhcpRoaming_mismatchedLeasedIpAddress() throws Exception { 2188 doDhcpRoamingTest(true /* hasMismatchedIpAddress */, "\"0001docomo\"" /* display name */, 2189 TEST_DHCP_ROAM_SSID, TEST_DEFAULT_BSSID, true /* expectRoaming */); 2190 } 2191 2192 private void doDualStackProvisioning() throws Exception { 2193 when(mCm.shouldAvoidBadWifi()).thenReturn(true); 2194 2195 final ProvisioningConfiguration config = new ProvisioningConfiguration.Builder() 2196 .withoutIpReachabilityMonitor() 2197 .build(); 2198 // Accelerate DHCP handshake to shorten test duration, not strictly necessary. 2199 mDependencies.setDhcpRapidCommitEnabled(true); 2200 mIpc.startProvisioning(config); 2201 2202 final InOrder inOrder = inOrder(mCb); 2203 final CompletableFuture<LinkProperties> lpFuture = new CompletableFuture<>(); 2204 final String dnsServer = "2001:4860:4860::64"; 2205 final ByteBuffer pio = buildPioOption(3600, 1800, "2001:db8:1::/64"); 2206 final ByteBuffer rdnss = buildRdnssOption(3600, dnsServer); 2207 final ByteBuffer ra = buildRaPacket(pio, rdnss); 2208 2209 doIpv6OnlyProvisioning(inOrder, ra); 2210 2211 // Start IPv4 provisioning and wait until entire provisioning completes. 2212 handleDhcpPackets(true /* isSuccessLease */, TEST_LEASE_DURATION_S, 2213 true /* shouldReplyRapidCommitAck */, TEST_DEFAULT_MTU, null /* serverSentUrl */); 2214 verify(mCb, timeout(TEST_TIMEOUT_MS).atLeastOnce()).onLinkPropertiesChange(argThat(x -> { 2215 if (!x.isIpv4Provisioned() || !x.isIpv6Provisioned()) return false; 2216 lpFuture.complete(x); 2217 return true; 2218 })); 2219 2220 final LinkProperties lp = lpFuture.get(TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS); 2221 assertNotNull(lp); 2222 assertTrue(lp.getDnsServers().contains(InetAddress.getByName(dnsServer))); 2223 assertTrue(lp.getDnsServers().contains(SERVER_ADDR)); 2224 2225 reset(mCb); 2226 } 2227 2228 @Test 2229 public void testIgnoreIpv6ProvisioningLoss() throws Exception { 2230 doDualStackProvisioning(); 2231 2232 final CompletableFuture<LinkProperties> lpFuture = new CompletableFuture<>(); 2233 2234 // Send RA with 0-lifetime and wait until all IPv6-related default route and DNS servers 2235 // have been removed, then verify if there is IPv4-only info left in the LinkProperties. 2236 sendRouterAdvertisementWithZeroLifetime(); 2237 verify(mCb, timeout(TEST_TIMEOUT_MS).atLeastOnce()).onLinkPropertiesChange( 2238 argThat(x -> { 2239 final boolean isOnlyIPv4Provisioned = (x.getLinkAddresses().size() == 1 2240 && x.getDnsServers().size() == 1 2241 && x.getAddresses().get(0) instanceof Inet4Address 2242 && x.getDnsServers().get(0) instanceof Inet4Address); 2243 2244 if (!isOnlyIPv4Provisioned) return false; 2245 lpFuture.complete(x); 2246 return true; 2247 })); 2248 final LinkProperties lp = lpFuture.get(TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS); 2249 assertNotNull(lp); 2250 assertEquals(lp.getAddresses().get(0), CLIENT_ADDR); 2251 assertEquals(lp.getDnsServers().get(0), SERVER_ADDR); 2252 } 2253 2254 @Test 2255 public void testDualStackProvisioning() throws Exception { 2256 doDualStackProvisioning(); 2257 2258 verify(mCb, never()).onProvisioningFailure(any()); 2259 } 2260 } 2261