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_IPV6_ONLY_PREFERRED; 23 import static android.net.dhcp.DhcpPacket.DHCP_MAGIC_COOKIE; 24 import static android.net.dhcp.DhcpPacket.DHCP_SERVER; 25 import static android.net.dhcp.DhcpPacket.ENCAP_L2; 26 import static android.net.dhcp.DhcpPacket.INADDR_BROADCAST; 27 import static android.net.dhcp.DhcpPacket.INFINITE_LEASE; 28 import static android.net.dhcp.DhcpPacket.MIN_V6ONLY_WAIT_MS; 29 import static android.net.dhcp.DhcpResultsParcelableUtil.fromStableParcelable; 30 import static android.net.ipmemorystore.Status.SUCCESS; 31 import static android.system.OsConstants.ETH_P_IPV6; 32 import static android.system.OsConstants.IFA_F_TEMPORARY; 33 import static android.system.OsConstants.IPPROTO_ICMPV6; 34 35 import static com.android.net.module.util.Inet4AddressUtils.getBroadcastAddress; 36 import static com.android.net.module.util.Inet4AddressUtils.getPrefixMaskAsInet4Address; 37 import static com.android.net.module.util.NetworkStackConstants.ARP_REPLY; 38 import static com.android.net.module.util.NetworkStackConstants.ARP_REQUEST; 39 import static com.android.net.module.util.NetworkStackConstants.ETHER_ADDR_LEN; 40 import static com.android.net.module.util.NetworkStackConstants.ETHER_BROADCAST; 41 import static com.android.net.module.util.NetworkStackConstants.ETHER_HEADER_LEN; 42 import static com.android.net.module.util.NetworkStackConstants.ETHER_TYPE_OFFSET; 43 import static com.android.net.module.util.NetworkStackConstants.ICMPV6_NEIGHBOR_ADVERTISEMENT; 44 import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ROUTER_SOLICITATION; 45 import static com.android.net.module.util.NetworkStackConstants.IPV4_ADDR_ANY; 46 import static com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_ALL_NODES_MULTICAST; 47 import static com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_ALL_ROUTERS_MULTICAST; 48 import static com.android.net.module.util.NetworkStackConstants.IPV6_HEADER_LEN; 49 import static com.android.net.module.util.NetworkStackConstants.IPV6_PROTOCOL_OFFSET; 50 import static com.android.net.module.util.NetworkStackConstants.PIO_FLAG_AUTONOMOUS; 51 import static com.android.net.module.util.NetworkStackConstants.PIO_FLAG_ON_LINK; 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.atLeastOnce; 70 import static org.mockito.Mockito.doAnswer; 71 import static org.mockito.Mockito.doThrow; 72 import static org.mockito.Mockito.eq; 73 import static org.mockito.Mockito.inOrder; 74 import static org.mockito.Mockito.mock; 75 import static org.mockito.Mockito.never; 76 import static org.mockito.Mockito.reset; 77 import static org.mockito.Mockito.spy; 78 import static org.mockito.Mockito.timeout; 79 import static org.mockito.Mockito.times; 80 import static org.mockito.Mockito.verify; 81 import static org.mockito.Mockito.when; 82 83 import android.app.AlarmManager; 84 import android.app.AlarmManager.OnAlarmListener; 85 import android.app.Instrumentation; 86 import android.content.ContentResolver; 87 import android.content.Context; 88 import android.content.res.Resources; 89 import android.net.ConnectivityManager; 90 import android.net.DhcpResults; 91 import android.net.DhcpResultsParcelable; 92 import android.net.INetd; 93 import android.net.InetAddresses; 94 import android.net.InterfaceConfigurationParcel; 95 import android.net.IpPrefix; 96 import android.net.Layer2InformationParcelable; 97 import android.net.Layer2PacketParcelable; 98 import android.net.LinkAddress; 99 import android.net.LinkProperties; 100 import android.net.MacAddress; 101 import android.net.NetworkStackIpMemoryStore; 102 import android.net.TestNetworkInterface; 103 import android.net.TestNetworkManager; 104 import android.net.Uri; 105 import android.net.dhcp.DhcpClient; 106 import android.net.dhcp.DhcpDeclinePacket; 107 import android.net.dhcp.DhcpDiscoverPacket; 108 import android.net.dhcp.DhcpPacket; 109 import android.net.dhcp.DhcpPacket.ParseException; 110 import android.net.dhcp.DhcpRequestPacket; 111 import android.net.ipmemorystore.NetworkAttributes; 112 import android.net.ipmemorystore.OnNetworkAttributesRetrievedListener; 113 import android.net.ipmemorystore.Status; 114 import android.net.netlink.StructNdOptPref64; 115 import android.net.networkstack.TestNetworkStackServiceClient; 116 import android.net.networkstack.aidl.dhcp.DhcpOption; 117 import android.net.shared.Layer2Information; 118 import android.net.shared.ProvisioningConfiguration; 119 import android.net.shared.ProvisioningConfiguration.ScanResultInfo; 120 import android.net.util.InterfaceParams; 121 import android.net.util.NetworkStackUtils; 122 import android.os.Build; 123 import android.os.Handler; 124 import android.os.HandlerThread; 125 import android.os.IBinder; 126 import android.os.ParcelFileDescriptor; 127 import android.os.PowerManager; 128 import android.os.RemoteException; 129 import android.os.SystemClock; 130 import android.os.SystemProperties; 131 import android.stats.connectivity.NetworkQuirkEvent; 132 import android.system.ErrnoException; 133 import android.system.Os; 134 135 import androidx.annotation.NonNull; 136 import androidx.test.InstrumentationRegistry; 137 import androidx.test.filters.SmallTest; 138 import androidx.test.runner.AndroidJUnit4; 139 140 import com.android.internal.util.HexDump; 141 import com.android.internal.util.StateMachine; 142 import com.android.net.module.util.ArrayTrackRecord; 143 import com.android.net.module.util.Ipv6Utils; 144 import com.android.net.module.util.structs.PrefixInformationOption; 145 import com.android.net.module.util.structs.RdnssOption; 146 import com.android.networkstack.apishim.CaptivePortalDataShimImpl; 147 import com.android.networkstack.apishim.ConstantsShim; 148 import com.android.networkstack.apishim.common.ShimUtils; 149 import com.android.networkstack.arp.ArpPacket; 150 import com.android.networkstack.metrics.IpProvisioningMetrics; 151 import com.android.networkstack.metrics.NetworkQuirkMetrics; 152 import com.android.networkstack.packets.NeighborAdvertisement; 153 import com.android.server.NetworkObserver; 154 import com.android.server.NetworkObserverRegistry; 155 import com.android.server.NetworkStackService.NetworkStackServiceManager; 156 import com.android.server.connectivity.ipmemorystore.IpMemoryStoreService; 157 import com.android.testutils.DevSdkIgnoreRule; 158 import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter; 159 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; 160 import com.android.testutils.HandlerUtils; 161 import com.android.testutils.TapPacketReader; 162 163 import org.junit.After; 164 import org.junit.Before; 165 import org.junit.Rule; 166 import org.junit.Test; 167 import org.junit.rules.TestName; 168 import org.junit.runner.RunWith; 169 import org.mockito.ArgumentCaptor; 170 import org.mockito.InOrder; 171 import org.mockito.Mock; 172 import org.mockito.MockitoAnnotations; 173 import org.mockito.Spy; 174 175 import java.io.BufferedReader; 176 import java.io.File; 177 import java.io.FileDescriptor; 178 import java.io.FileReader; 179 import java.io.IOException; 180 import java.lang.annotation.ElementType; 181 import java.lang.annotation.Retention; 182 import java.lang.annotation.RetentionPolicy; 183 import java.lang.annotation.Target; 184 import java.lang.reflect.Method; 185 import java.net.Inet4Address; 186 import java.net.Inet6Address; 187 import java.net.InetAddress; 188 import java.net.NetworkInterface; 189 import java.nio.ByteBuffer; 190 import java.util.ArrayList; 191 import java.util.Arrays; 192 import java.util.Collection; 193 import java.util.Collections; 194 import java.util.HashMap; 195 import java.util.List; 196 import java.util.Objects; 197 import java.util.Random; 198 import java.util.concurrent.CompletableFuture; 199 import java.util.concurrent.CountDownLatch; 200 import java.util.concurrent.TimeUnit; 201 import java.util.concurrent.atomic.AtomicReference; 202 import java.util.function.Predicate; 203 204 import kotlin.Lazy; 205 import kotlin.LazyKt; 206 207 /** 208 * Base class for IpClient tests. 209 * 210 * Tests in this class can either be run with signature permissions, or with root access. 211 */ 212 @RunWith(AndroidJUnit4.class) 213 @SmallTest 214 public abstract class IpClientIntegrationTestCommon { 215 private static final int DATA_BUFFER_LEN = 4096; 216 private static final int PACKET_TIMEOUT_MS = 5_000; 217 private static final String TEST_CLUSTER = "some cluster"; 218 private static final int TEST_LEASE_DURATION_S = 3_600; // 1 hour 219 private static final int TEST_IPV6_ONLY_WAIT_S = 1_800; // 30 min 220 private static final int TEST_LOWER_IPV6_ONLY_WAIT_S = (int) (MIN_V6ONLY_WAIT_MS / 1000 - 1); 221 private static final int TEST_ZERO_IPV6_ONLY_WAIT_S = 0; 222 private static final long TEST_MAX_IPV6_ONLY_WAIT_S = 0xffffffffL; 223 protected static final String TEST_L2KEY = "some l2key"; 224 225 // TODO: move to NetlinkConstants, NetworkStackConstants, or OsConstants. 226 private static final int IFA_F_STABLE_PRIVACY = 0x800; 227 228 protected static final long TEST_TIMEOUT_MS = 2_000L; 229 230 @Rule 231 public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule(); 232 @Rule 233 public final TestName mTestNameRule = new TestName(); 234 235 /** 236 * Indicates that a test requires signature permissions to run. 237 * 238 * Such tests can only be run on devices that use known signing keys, so this annotation must be 239 * avoided as much as possible. Consider whether the test can be written to use shell and root 240 * shell permissions, and run against the NetworkStack AIDL interface (IIpClient) instead. 241 */ 242 @Retention(RetentionPolicy.RUNTIME) 243 @Target({ElementType.METHOD}) 244 private @interface SignatureRequiredTest { reason()245 String reason(); 246 } 247 248 /**** BEGIN signature required test members ****/ 249 // Do not use unless the test *really* cannot be written to exercise IIpClient without mocks. 250 // Tests using the below members must be annotated with @SignatureRequiredTest (otherwise the 251 // members will be null), and can only be run on devices that use known signing keys. 252 // The members could technically be moved to the IpClientIntegrationTest subclass together with 253 // the tests requiring signature permissions, but this would make it harder to follow tests in 254 // multiple classes, and harder to migrate tests between signature required and not required. 255 256 @Mock private Context mContext; 257 @Mock private ConnectivityManager mCm; 258 @Mock private Resources mResources; 259 @Mock private AlarmManager mAlarm; 260 @Mock private ContentResolver mContentResolver; 261 @Mock private NetworkStackServiceManager mNetworkStackServiceManager; 262 @Mock private IpMemoryStoreService mIpMemoryStoreService; 263 @Mock private PowerManager.WakeLock mTimeoutWakeLock; 264 @Mock protected NetworkStackIpMemoryStore mIpMemoryStore; 265 @Mock private NetworkQuirkMetrics.Dependencies mNetworkQuirkMetricsDeps; 266 267 @Spy private INetd mNetd; 268 private NetworkObserverRegistry mNetworkObserverRegistry; 269 270 protected IpClient mIpc; 271 protected Dependencies mDependencies; 272 273 /***** END signature required test members *****/ 274 275 private IIpClientCallbacks mCb; 276 private IIpClient mIIpClient; 277 private String mIfaceName; 278 private HandlerThread mPacketReaderThread; 279 private Handler mHandler; 280 private TapPacketReader mPacketReader; 281 private FileDescriptor mTapFd; 282 private byte[] mClientMac; 283 284 private boolean mIsSignatureRequiredTest; 285 286 // ReadHeads for various packet streams. Cannot be initialized in @Before because ReadHead is 287 // single-thread-only, and AndroidJUnitRunner runs @Before and @Test on different threads. 288 // While it looks like these are created only once per test, they are actually created once per 289 // test method because JUnit recreates a fresh test class instance before every test method. 290 private Lazy<ArrayTrackRecord<byte[]>.ReadHead> mDhcpPacketReadHead = 291 LazyKt.lazy(() -> mPacketReader.getReceivedPackets().newReadHead()); 292 private Lazy<ArrayTrackRecord<byte[]>.ReadHead> mArpPacketReadHead = 293 LazyKt.lazy(() -> mPacketReader.getReceivedPackets().newReadHead()); 294 295 // Ethernet header 296 private static final int ETH_HEADER_LEN = 14; 297 298 // IP header 299 private static final int IPV4_HEADER_LEN = 20; 300 private static final int IPV4_SRC_ADDR_OFFSET = ETH_HEADER_LEN + 12; 301 private static final int IPV4_DST_ADDR_OFFSET = IPV4_SRC_ADDR_OFFSET + 4; 302 303 // UDP header 304 private static final int UDP_HEADER_LEN = 8; 305 private static final int UDP_HEADER_OFFSET = ETH_HEADER_LEN + IPV4_HEADER_LEN; 306 private static final int UDP_SRC_PORT_OFFSET = UDP_HEADER_OFFSET + 0; 307 308 // DHCP header 309 private static final int DHCP_HEADER_OFFSET = ETH_HEADER_LEN + IPV4_HEADER_LEN 310 + UDP_HEADER_LEN; 311 private static final int DHCP_MESSAGE_OP_CODE_OFFSET = DHCP_HEADER_OFFSET + 0; 312 private static final int DHCP_TRANSACTION_ID_OFFSET = DHCP_HEADER_OFFSET + 4; 313 private static final int DHCP_OPTION_MAGIC_COOKIE_OFFSET = DHCP_HEADER_OFFSET + 236; 314 315 private static final Inet4Address SERVER_ADDR = 316 (Inet4Address) InetAddresses.parseNumericAddress("192.168.1.100"); 317 private static final Inet4Address CLIENT_ADDR = 318 (Inet4Address) InetAddresses.parseNumericAddress("192.168.1.2"); 319 private static final Inet4Address CLIENT_ADDR_NEW = 320 (Inet4Address) InetAddresses.parseNumericAddress("192.168.1.3"); 321 private static final Inet4Address INADDR_ANY = 322 (Inet4Address) InetAddresses.parseNumericAddress("0.0.0.0"); 323 private static final int PREFIX_LENGTH = 24; 324 private static final Inet4Address NETMASK = getPrefixMaskAsInet4Address(PREFIX_LENGTH); 325 private static final Inet4Address BROADCAST_ADDR = getBroadcastAddress( 326 SERVER_ADDR, PREFIX_LENGTH); 327 private static final String HOSTNAME = "testhostname"; 328 private static final int TEST_DEFAULT_MTU = 1500; 329 private static final int TEST_MIN_MTU = 1280; 330 private static final byte[] SERVER_MAC = new byte[] { 0x00, 0x1A, 0x11, 0x22, 0x33, 0x44 }; 331 private static final String TEST_HOST_NAME = "AOSP on Crosshatch"; 332 private static final String TEST_HOST_NAME_TRANSLITERATION = "AOSP-on-Crosshatch"; 333 private static final String TEST_CAPTIVE_PORTAL_URL = "https://example.com/capportapi"; 334 private static final byte[] TEST_HOTSPOT_OUI = new byte[] { 335 (byte) 0x00, (byte) 0x17, (byte) 0xF2 336 }; 337 private static final byte TEST_VENDOR_SPECIFIC_TYPE = 0x06; 338 339 private static final String TEST_DEFAULT_SSID = "test_ssid"; 340 private static final String TEST_DEFAULT_BSSID = "00:11:22:33:44:55"; 341 private static final String TEST_DHCP_ROAM_SSID = "0001docomo"; 342 private static final String TEST_DHCP_ROAM_BSSID = "00:4e:35:17:98:55"; 343 private static final String TEST_DHCP_ROAM_L2KEY = "roaming_l2key"; 344 private static final String TEST_DHCP_ROAM_CLUSTER = "roaming_cluster"; 345 private static final byte[] TEST_AP_OUI = new byte[] { 0x00, 0x1A, 0x11 }; 346 private static final byte[] TEST_OEM_OUI = new byte[] {(byte) 0x00, (byte) 0x17, (byte) 0xc3}; 347 private static final String TEST_OEM_VENDOR_ID = "vendor-class-identifier"; 348 private static final byte[] TEST_OEM_USER_CLASS_INFO = new byte[] { 349 // Instance of User Class: [0] 350 (byte) 0x03, /* UC_Len_0 */ (byte) 0x11, (byte) 0x22, (byte) 0x33, 351 // Instance of User Class: [1] 352 (byte) 0x03, /* UC_Len_1 */ (byte) 0x44, (byte) 0x55, (byte) 0x66, 353 }; 354 355 protected class Dependencies extends IpClient.Dependencies { 356 // Can't use SparseIntArray, it doesn't have an easy way to know if a key is not present. 357 private HashMap<String, Integer> mIntConfigProperties = new HashMap<>(); 358 private DhcpClient mDhcpClient; 359 private boolean mIsHostnameConfigurationEnabled; 360 private String mHostname; 361 private boolean mIsInterfaceRecovered; 362 setHostnameConfiguration(final boolean enable, final String hostname)363 public void setHostnameConfiguration(final boolean enable, final String hostname) { 364 mIsHostnameConfigurationEnabled = enable; 365 mHostname = hostname; 366 } 367 368 // Enable this flag to simulate the interface has been added back after removing 369 // on the provisioning start. However, the actual tap interface has been removed, 370 // interface parameters query will get null when attempting to restore Interface 371 // MTU. Create a new InterfaceParams instance and return instead just for interface 372 // toggling test case. simulateInterfaceRecover()373 public void simulateInterfaceRecover() { 374 mIsInterfaceRecovered = true; 375 } 376 377 @Override getInterfaceParams(String ifname)378 public InterfaceParams getInterfaceParams(String ifname) { 379 return mIsInterfaceRecovered 380 ? new InterfaceParams(ifname, 1 /* index */, 381 MacAddress.fromString("00:11:22:33:44:55")) 382 : super.getInterfaceParams(ifname); 383 } 384 385 @Override getNetd(Context context)386 public INetd getNetd(Context context) { 387 return mNetd; 388 } 389 390 @Override getIpMemoryStore(Context context, NetworkStackServiceManager nssManager)391 public NetworkStackIpMemoryStore getIpMemoryStore(Context context, 392 NetworkStackServiceManager nssManager) { 393 return mIpMemoryStore; 394 } 395 396 @Override makeDhcpClient(Context context, StateMachine controller, InterfaceParams ifParams, DhcpClient.Dependencies deps)397 public DhcpClient makeDhcpClient(Context context, StateMachine controller, 398 InterfaceParams ifParams, DhcpClient.Dependencies deps) { 399 mDhcpClient = DhcpClient.makeDhcpClient(context, controller, ifParams, deps); 400 return mDhcpClient; 401 } 402 403 @Override isFeatureEnabled(final Context context, final String name, final boolean defaultEnabled)404 public boolean isFeatureEnabled(final Context context, final String name, 405 final boolean defaultEnabled) { 406 return IpClientIntegrationTestCommon.this.isFeatureEnabled(name, defaultEnabled); 407 } 408 409 @Override getDhcpClientDependencies( NetworkStackIpMemoryStore ipMemoryStore, IpProvisioningMetrics metrics)410 public DhcpClient.Dependencies getDhcpClientDependencies( 411 NetworkStackIpMemoryStore ipMemoryStore, IpProvisioningMetrics metrics) { 412 return new DhcpClient.Dependencies(ipMemoryStore, metrics) { 413 @Override 414 public boolean isFeatureEnabled(final Context context, final String name, 415 final boolean defaultEnabled) { 416 return Dependencies.this.isFeatureEnabled(context, name, defaultEnabled); 417 } 418 419 @Override 420 public int getIntDeviceConfig(final String name, int minimumValue, 421 int maximumValue, int defaultValue) { 422 return getDeviceConfigPropertyInt(name, 0 /* default value */); 423 } 424 425 @Override 426 public PowerManager.WakeLock getWakeLock(final PowerManager powerManager) { 427 return mTimeoutWakeLock; 428 } 429 430 @Override 431 public boolean getSendHostnameOption(final Context context) { 432 return mIsHostnameConfigurationEnabled; 433 } 434 435 @Override 436 public String getDeviceName(final Context context) { 437 return mIsHostnameConfigurationEnabled ? mHostname : null; 438 } 439 }; 440 } 441 442 @Override getDeviceConfigPropertyInt(String name, int defaultValue)443 public int getDeviceConfigPropertyInt(String name, int defaultValue) { 444 Integer value = mIntConfigProperties.get(name); 445 if (value == null) { 446 throw new IllegalStateException("Non-mocked device config property " + name); 447 } 448 return value; 449 } 450 setDeviceConfigProperty(String name, int value)451 public void setDeviceConfigProperty(String name, int value) { 452 mIntConfigProperties.put(name, value); 453 } 454 455 @Override getNetworkQuirkMetrics()456 public NetworkQuirkMetrics getNetworkQuirkMetrics() { 457 return new NetworkQuirkMetrics(mNetworkQuirkMetricsDeps); 458 } 459 } 460 461 @NonNull 462 protected abstract IIpClient makeIIpClient( 463 @NonNull String ifaceName, @NonNull IIpClientCallbacks cb); 464 465 protected abstract void setFeatureEnabled(String name, boolean enabled); 466 467 protected abstract boolean isFeatureEnabled(String name, boolean defaultEnabled); 468 469 protected abstract boolean useNetworkStackSignature(); 470 471 protected abstract NetworkAttributes getStoredNetworkAttributes(String l2Key, long timeout); 472 473 protected abstract void assertIpMemoryNeverStoreNetworkAttributes(String l2Key, long timeout); 474 475 protected final boolean testSkipped() { 476 // TODO: split out a test suite for root tests, and fail hard instead of skipping the test 477 // if it is run on devices where TestNetworkStackServiceClient is not supported 478 return !useNetworkStackSignature() 479 && (mIsSignatureRequiredTest || !TestNetworkStackServiceClient.isSupported()); 480 } 481 482 protected void setDhcpFeatures(final boolean isDhcpLeaseCacheEnabled, 483 final boolean isRapidCommitEnabled, final boolean isDhcpIpConflictDetectEnabled, 484 final boolean isIPv6OnlyPreferredEnabled) { 485 setFeatureEnabled(NetworkStackUtils.DHCP_INIT_REBOOT_VERSION, isDhcpLeaseCacheEnabled); 486 setFeatureEnabled(NetworkStackUtils.DHCP_RAPID_COMMIT_VERSION, isRapidCommitEnabled); 487 setFeatureEnabled(NetworkStackUtils.DHCP_IP_CONFLICT_DETECT_VERSION, 488 isDhcpIpConflictDetectEnabled); 489 setFeatureEnabled(NetworkStackUtils.DHCP_IPV6_ONLY_PREFERRED_VERSION, 490 isIPv6OnlyPreferredEnabled); 491 } 492 493 @Before 494 public void setUp() throws Exception { 495 final Method testMethod = IpClientIntegrationTestCommon.class.getMethod( 496 mTestNameRule.getMethodName()); 497 mIsSignatureRequiredTest = testMethod.getAnnotation(SignatureRequiredTest.class) != null; 498 assumeFalse(testSkipped()); 499 500 setUpTapInterface(); 501 mCb = mock(IIpClientCallbacks.class); 502 503 if (useNetworkStackSignature()) { 504 setUpMocks(); 505 setUpIpClient(); 506 } 507 508 mIIpClient = makeIIpClient(mIfaceName, mCb); 509 } 510 511 protected void setUpMocks() throws Exception { 512 MockitoAnnotations.initMocks(this); 513 514 mDependencies = new Dependencies(); 515 when(mContext.getSystemService(eq(Context.ALARM_SERVICE))).thenReturn(mAlarm); 516 when(mContext.getSystemService(eq(ConnectivityManager.class))).thenReturn(mCm); 517 when(mContext.getResources()).thenReturn(mResources); 518 when(mContext.getContentResolver()).thenReturn(mContentResolver); 519 when(mNetworkStackServiceManager.getIpMemoryStoreService()) 520 .thenReturn(mIpMemoryStoreService); 521 522 mDependencies.setDeviceConfigProperty(IpClient.CONFIG_MIN_RDNSS_LIFETIME, 67); 523 mDependencies.setDeviceConfigProperty(DhcpClient.DHCP_RESTART_CONFIG_DELAY, 10); 524 mDependencies.setDeviceConfigProperty(DhcpClient.ARP_FIRST_PROBE_DELAY_MS, 10); 525 mDependencies.setDeviceConfigProperty(DhcpClient.ARP_PROBE_MIN_MS, 10); 526 mDependencies.setDeviceConfigProperty(DhcpClient.ARP_PROBE_MAX_MS, 20); 527 mDependencies.setDeviceConfigProperty(DhcpClient.ARP_FIRST_ANNOUNCE_DELAY_MS, 10); 528 mDependencies.setDeviceConfigProperty(DhcpClient.ARP_ANNOUNCE_INTERVAL_MS, 10); 529 } 530 531 private void awaitIpClientShutdown() throws Exception { 532 verify(mCb, timeout(TEST_TIMEOUT_MS)).onQuit(); 533 } 534 535 @After 536 public void tearDown() throws Exception { 537 if (testSkipped()) return; 538 if (mPacketReader != null) { 539 mHandler.post(() -> mPacketReader.stop()); // Also closes the socket 540 mTapFd = null; 541 } 542 if (mPacketReaderThread != null) { 543 mPacketReaderThread.quitSafely(); 544 } 545 mIIpClient.shutdown(); 546 awaitIpClientShutdown(); 547 } 548 549 private void setUpTapInterface() throws Exception { 550 final Instrumentation inst = InstrumentationRegistry.getInstrumentation(); 551 // Adopt the shell permission identity to create a test TAP interface. 552 inst.getUiAutomation().adoptShellPermissionIdentity(); 553 554 final TestNetworkInterface iface; 555 try { 556 final TestNetworkManager tnm = (TestNetworkManager) 557 inst.getContext().getSystemService(Context.TEST_NETWORK_SERVICE); 558 iface = tnm.createTapInterface(); 559 } finally { 560 // Drop the identity in order to regain the network stack permissions, which the shell 561 // does not have. 562 inst.getUiAutomation().dropShellPermissionIdentity(); 563 } 564 mIfaceName = iface.getInterfaceName(); 565 mClientMac = getIfaceMacAddr(mIfaceName).toByteArray(); 566 mPacketReaderThread = new HandlerThread( 567 IpClientIntegrationTestCommon.class.getSimpleName()); 568 mPacketReaderThread.start(); 569 mHandler = mPacketReaderThread.getThreadHandler(); 570 571 // Detach the FileDescriptor from the ParcelFileDescriptor. 572 // Otherwise, the garbage collector might call the ParcelFileDescriptor's finalizer, which 573 // closes the FileDescriptor and destroys our tap interface. An alternative would be to 574 // make the ParcelFileDescriptor or the TestNetworkInterface a class member so they never 575 // go out of scope. 576 mTapFd = new FileDescriptor(); 577 mTapFd.setInt$(iface.getFileDescriptor().detachFd()); 578 mPacketReader = new TapPacketReader(mHandler, mTapFd, DATA_BUFFER_LEN); 579 mHandler.post(() -> mPacketReader.start()); 580 } 581 582 private MacAddress getIfaceMacAddr(String ifaceName) throws IOException { 583 // InterfaceParams.getByName requires CAP_NET_ADMIN: read the mac address with the shell 584 final String strMacAddr = getOneLineCommandOutput( 585 "su root cat /sys/class/net/" + ifaceName + "/address"); 586 return MacAddress.fromString(strMacAddr); 587 } 588 589 private String getOneLineCommandOutput(String cmd) throws IOException { 590 try (ParcelFileDescriptor fd = InstrumentationRegistry.getInstrumentation() 591 .getUiAutomation().executeShellCommand(cmd); 592 BufferedReader reader = new BufferedReader(new FileReader(fd.getFileDescriptor()))) { 593 return reader.readLine(); 594 } 595 } 596 597 private IpClient makeIpClient() throws Exception { 598 IpClient ipc = new IpClient(mContext, mIfaceName, mCb, mNetworkObserverRegistry, 599 mNetworkStackServiceManager, mDependencies); 600 // Wait for IpClient to enter its initial state. Otherwise, additional setup steps or tests 601 // that mock IpClient's dependencies might interact with those mocks while IpClient is 602 // starting. This would cause UnfinishedStubbingExceptions as mocks cannot be interacted 603 // with while they are being stubbed. 604 HandlerUtils.waitForIdle(ipc.getHandler(), TEST_TIMEOUT_MS); 605 return ipc; 606 } 607 608 private void setUpIpClient() throws Exception { 609 final Instrumentation inst = InstrumentationRegistry.getInstrumentation(); 610 final IBinder netdIBinder = 611 (IBinder) inst.getContext().getSystemService(Context.NETD_SERVICE); 612 mNetd = spy(INetd.Stub.asInterface(netdIBinder)); 613 when(mContext.getSystemService(eq(Context.NETD_SERVICE))).thenReturn(netdIBinder); 614 assertNotNull(mNetd); 615 616 mNetworkObserverRegistry = new NetworkObserverRegistry(); 617 mNetworkObserverRegistry.register(mNetd); 618 mIpc = makeIpClient(); 619 620 // Tell the IpMemoryStore immediately to answer any question about network attributes with a 621 // null response. Otherwise, the DHCP client will wait for two seconds before starting, 622 // while its query to the IpMemoryStore times out. 623 // This does not affect any test that makes the mock memory store return results, because 624 // unlike when(), it is documented that doAnswer() can be called more than once, to change 625 // the behaviour of a mock in the middle of a test. 626 doAnswer(invocation -> { 627 final String l2Key = invocation.getArgument(0); 628 ((OnNetworkAttributesRetrievedListener) invocation.getArgument(1)) 629 .onNetworkAttributesRetrieved(new Status(SUCCESS), l2Key, null); 630 return null; 631 }).when(mIpMemoryStore).retrieveNetworkAttributes(any(), any()); 632 633 disableIpv6ProvisioningDelays(); 634 } 635 636 private <T> T verifyWithTimeout(InOrder inOrder, T t) { 637 if (inOrder != null) { 638 return inOrder.verify(t, timeout(TEST_TIMEOUT_MS)); 639 } else { 640 return verify(t, timeout(TEST_TIMEOUT_MS)); 641 } 642 } 643 644 private void expectAlarmCancelled(InOrder inOrder, OnAlarmListener listener) { 645 inOrder.verify(mAlarm, timeout(TEST_TIMEOUT_MS)).cancel(eq(listener)); 646 } 647 648 private OnAlarmListener expectAlarmSet(InOrder inOrder, String tagMatch, long afterSeconds, 649 Handler handler) { 650 // Allow +/- 3 seconds to prevent flaky tests. 651 final long when = SystemClock.elapsedRealtime() + afterSeconds * 1000; 652 final long min = when - 3 * 1000; 653 final long max = when + 3 * 1000; 654 ArgumentCaptor<OnAlarmListener> captor = ArgumentCaptor.forClass(OnAlarmListener.class); 655 verifyWithTimeout(inOrder, mAlarm).setExact( 656 anyInt(), longThat(x -> x >= min && x <= max), 657 contains(tagMatch), captor.capture(), eq(handler)); 658 return captor.getValue(); 659 } 660 661 private OnAlarmListener expectAlarmSet(InOrder inOrder, String tagMatch, int afterSeconds) { 662 return expectAlarmSet(inOrder, tagMatch, (long) afterSeconds, mIpc.getHandler()); 663 } 664 665 private boolean packetContainsExpectedField(final byte[] packet, final int offset, 666 final byte[] expected) { 667 if (packet.length < offset + expected.length) return false; 668 for (int i = 0; i < expected.length; ++i) { 669 if (packet[offset + i] != expected[i]) return false; 670 } 671 return true; 672 } 673 674 private boolean isDhcpPacket(final byte[] packet) { 675 final ByteBuffer buffer = ByteBuffer.wrap(packet); 676 677 // check the packet length 678 if (packet.length < DHCP_HEADER_OFFSET) return false; 679 680 // check the source port and dest port in UDP header 681 buffer.position(UDP_SRC_PORT_OFFSET); 682 final short udpSrcPort = buffer.getShort(); 683 final short udpDstPort = buffer.getShort(); 684 if (udpSrcPort != DHCP_CLIENT || udpDstPort != DHCP_SERVER) return false; 685 686 // check DHCP message type 687 buffer.position(DHCP_MESSAGE_OP_CODE_OFFSET); 688 final byte dhcpOpCode = buffer.get(); 689 if (dhcpOpCode != DHCP_BOOTREQUEST) return false; 690 691 // check DHCP magic cookie 692 buffer.position(DHCP_OPTION_MAGIC_COOKIE_OFFSET); 693 final int dhcpMagicCookie = buffer.getInt(); 694 if (dhcpMagicCookie != DHCP_MAGIC_COOKIE) return false; 695 696 return true; 697 } 698 699 private ArpPacket parseArpPacketOrNull(final byte[] packet) { 700 try { 701 return ArpPacket.parseArpPacket(packet, packet.length); 702 } catch (ArpPacket.ParseException e) { 703 return null; 704 } 705 } 706 707 private NeighborAdvertisement parseNeighborAdvertisementOrNull(final byte[] packet) { 708 try { 709 return NeighborAdvertisement.parse(packet, packet.length); 710 } catch (NeighborAdvertisement.ParseException e) { 711 return null; 712 } 713 } 714 715 private static ByteBuffer buildDhcpOfferPacket(final DhcpPacket packet, 716 final Inet4Address clientAddress, final Integer leaseTimeSec, final short mtu, 717 final String captivePortalUrl, final Integer ipv6OnlyWaitTime) { 718 return DhcpPacket.buildOfferPacket(DhcpPacket.ENCAP_L2, packet.getTransactionId(), 719 false /* broadcast */, SERVER_ADDR, INADDR_ANY /* relayIp */, 720 clientAddress /* yourIp */, packet.getClientMac(), leaseTimeSec, 721 NETMASK /* netMask */, BROADCAST_ADDR /* bcAddr */, 722 Collections.singletonList(SERVER_ADDR) /* gateways */, 723 Collections.singletonList(SERVER_ADDR) /* dnsServers */, 724 SERVER_ADDR /* dhcpServerIdentifier */, null /* domainName */, HOSTNAME, 725 false /* metered */, mtu, captivePortalUrl, ipv6OnlyWaitTime); 726 } 727 728 private static ByteBuffer buildDhcpOfferPacket(final DhcpPacket packet, 729 final Inet4Address clientAddress, final Integer leaseTimeSec, final short mtu, 730 final String captivePortalUrl) { 731 return buildDhcpOfferPacket(packet, clientAddress, leaseTimeSec, mtu, captivePortalUrl, 732 null /* ipv6OnlyWaitTime */); 733 } 734 735 private static ByteBuffer buildDhcpAckPacket(final DhcpPacket packet, 736 final Inet4Address clientAddress, final Integer leaseTimeSec, final short mtu, 737 final boolean rapidCommit, final String captivePortalApiUrl, 738 final Integer ipv6OnlyWaitTime) { 739 return DhcpPacket.buildAckPacket(DhcpPacket.ENCAP_L2, packet.getTransactionId(), 740 false /* broadcast */, SERVER_ADDR, INADDR_ANY /* relayIp */, 741 clientAddress /* yourIp */, CLIENT_ADDR /* requestIp */, packet.getClientMac(), 742 leaseTimeSec, NETMASK /* netMask */, BROADCAST_ADDR /* bcAddr */, 743 Collections.singletonList(SERVER_ADDR) /* gateways */, 744 Collections.singletonList(SERVER_ADDR) /* dnsServers */, 745 SERVER_ADDR /* dhcpServerIdentifier */, null /* domainName */, HOSTNAME, 746 false /* metered */, mtu, rapidCommit, captivePortalApiUrl, ipv6OnlyWaitTime); 747 } 748 749 private static ByteBuffer buildDhcpAckPacket(final DhcpPacket packet, 750 final Inet4Address clientAddress, final Integer leaseTimeSec, final short mtu, 751 final boolean rapidCommit, final String captivePortalApiUrl) { 752 return buildDhcpAckPacket(packet, clientAddress, leaseTimeSec, mtu, rapidCommit, 753 captivePortalApiUrl, null /* ipv6OnlyWaitTime */); 754 } 755 756 private static ByteBuffer buildDhcpNakPacket(final DhcpPacket packet) { 757 return DhcpPacket.buildNakPacket(DhcpPacket.ENCAP_L2, packet.getTransactionId(), 758 SERVER_ADDR /* serverIp */, INADDR_ANY /* relayIp */, packet.getClientMac(), 759 false /* broadcast */, "duplicated request IP address"); 760 } 761 762 private void sendArpReply(final byte[] clientMac) throws IOException { 763 final ByteBuffer packet = ArpPacket.buildArpPacket(clientMac /* dst */, 764 SERVER_MAC /* src */, INADDR_ANY.getAddress() /* target IP */, 765 clientMac /* target HW address */, CLIENT_ADDR.getAddress() /* sender IP */, 766 (short) ARP_REPLY); 767 mPacketReader.sendResponse(packet); 768 } 769 770 private void sendArpProbe() throws IOException { 771 final ByteBuffer packet = ArpPacket.buildArpPacket(DhcpPacket.ETHER_BROADCAST /* dst */, 772 SERVER_MAC /* src */, CLIENT_ADDR.getAddress() /* target IP */, 773 new byte[ETHER_ADDR_LEN] /* target HW address */, 774 INADDR_ANY.getAddress() /* sender IP */, (short) ARP_REQUEST); 775 mPacketReader.sendResponse(packet); 776 } 777 778 private void startIpClientProvisioning(final ProvisioningConfiguration cfg) throws Exception { 779 mIIpClient.startProvisioning(cfg.toStableParcelable()); 780 } 781 782 private void startIpClientProvisioning(final boolean isDhcpLeaseCacheEnabled, 783 final boolean shouldReplyRapidCommitAck, final boolean isPreconnectionEnabled, 784 final boolean isDhcpIpConflictDetectEnabled, final boolean isIPv6OnlyPreferredEnabled, 785 final String displayName, final ScanResultInfo scanResultInfo, 786 final Layer2Information layer2Info) throws Exception { 787 ProvisioningConfiguration.Builder prov = new ProvisioningConfiguration.Builder() 788 .withoutIpReachabilityMonitor() 789 .withLayer2Information(layer2Info == null 790 ? new Layer2Information(TEST_L2KEY, TEST_CLUSTER, 791 MacAddress.fromString(TEST_DEFAULT_BSSID)) 792 : layer2Info) 793 .withoutIPv6(); 794 if (isPreconnectionEnabled) prov.withPreconnection(); 795 if (displayName != null) prov.withDisplayName(displayName); 796 if (scanResultInfo != null) prov.withScanResultInfo(scanResultInfo); 797 798 setDhcpFeatures(isDhcpLeaseCacheEnabled, shouldReplyRapidCommitAck, 799 isDhcpIpConflictDetectEnabled, isIPv6OnlyPreferredEnabled); 800 801 startIpClientProvisioning(prov.build()); 802 if (!isPreconnectionEnabled) { 803 verify(mCb, timeout(TEST_TIMEOUT_MS)).setFallbackMulticastFilter(false); 804 } 805 verify(mCb, never()).onProvisioningFailure(any()); 806 } 807 808 private void startIpClientProvisioning(final boolean isDhcpLeaseCacheEnabled, 809 final boolean isDhcpRapidCommitEnabled, final boolean isPreconnectionEnabled, 810 final boolean isDhcpIpConflictDetectEnabled, final boolean isIPv6OnlyPreferredEnabled) 811 throws Exception { 812 startIpClientProvisioning(isDhcpLeaseCacheEnabled, isDhcpRapidCommitEnabled, 813 isPreconnectionEnabled, isDhcpIpConflictDetectEnabled, isIPv6OnlyPreferredEnabled, 814 null /* displayName */, null /* ScanResultInfo */, null /* layer2Info */); 815 } 816 817 private void assertIpMemoryStoreNetworkAttributes(final Integer leaseTimeSec, 818 final long startTime, final int mtu) { 819 final NetworkAttributes na = getStoredNetworkAttributes(TEST_L2KEY, TEST_TIMEOUT_MS); 820 assertNotNull(na); 821 assertEquals(CLIENT_ADDR, na.assignedV4Address); 822 if (leaseTimeSec == null || leaseTimeSec.intValue() == DhcpPacket.INFINITE_LEASE) { 823 assertEquals(Long.MAX_VALUE, na.assignedV4AddressExpiry.longValue()); 824 } else { 825 // check the lease expiry's scope 826 final long upperBound = startTime + 7_200_000; // start timestamp + 2h 827 final long lowerBound = startTime + 3_600_000; // start timestamp + 1h 828 final long expiry = na.assignedV4AddressExpiry; 829 assertTrue(upperBound > expiry); 830 assertTrue(lowerBound < expiry); 831 } 832 assertEquals(Collections.singletonList(SERVER_ADDR), na.dnsAddresses); 833 assertEquals(new Integer(mtu), na.mtu); 834 } 835 836 private void assertIpMemoryNeverStoreNetworkAttributes() { 837 assertIpMemoryNeverStoreNetworkAttributes(TEST_L2KEY, TEST_TIMEOUT_MS); 838 } 839 840 private void assertHostname(final boolean isHostnameConfigurationEnabled, 841 final String hostname, final String hostnameAfterTransliteration, 842 final List<DhcpPacket> packetList) throws Exception { 843 for (DhcpPacket packet : packetList) { 844 if (!isHostnameConfigurationEnabled || hostname == null) { 845 assertNoHostname(packet.getHostname()); 846 } else { 847 assertEquals(packet.getHostname(), hostnameAfterTransliteration); 848 } 849 } 850 } 851 852 private void assertNoHostname(String hostname) { 853 if (ShimUtils.isAtLeastR()) { 854 assertNull(hostname); 855 } else { 856 // Until Q, if no hostname is set, the device falls back to the hostname set via 857 // system property, to avoid breaking Q devices already launched with that setup. 858 assertEquals(SystemProperties.get("net.hostname"), hostname); 859 } 860 } 861 862 // Helper method to complete DHCP 2-way or 4-way handshake 863 private List<DhcpPacket> performDhcpHandshake(final boolean isSuccessLease, 864 final Integer leaseTimeSec, final boolean isDhcpLeaseCacheEnabled, 865 final boolean shouldReplyRapidCommitAck, final int mtu, 866 final boolean isDhcpIpConflictDetectEnabled, 867 final boolean isIPv6OnlyPreferredEnabled, 868 final String captivePortalApiUrl, final String displayName, 869 final ScanResultInfo scanResultInfo, final Layer2Information layer2Info) 870 throws Exception { 871 startIpClientProvisioning(isDhcpLeaseCacheEnabled, shouldReplyRapidCommitAck, 872 false /* isPreconnectionEnabled */, isDhcpIpConflictDetectEnabled, 873 isIPv6OnlyPreferredEnabled, displayName, scanResultInfo, layer2Info); 874 return handleDhcpPackets(isSuccessLease, leaseTimeSec, shouldReplyRapidCommitAck, mtu, 875 captivePortalApiUrl); 876 } 877 878 private List<DhcpPacket> handleDhcpPackets(final boolean isSuccessLease, 879 final Integer leaseTimeSec, final boolean shouldReplyRapidCommitAck, final int mtu, 880 final String captivePortalApiUrl) throws Exception { 881 final List<DhcpPacket> packetList = new ArrayList<>(); 882 DhcpPacket packet; 883 while ((packet = getNextDhcpPacket()) != null) { 884 packetList.add(packet); 885 if (packet instanceof DhcpDiscoverPacket) { 886 if (shouldReplyRapidCommitAck) { 887 mPacketReader.sendResponse(buildDhcpAckPacket(packet, CLIENT_ADDR, leaseTimeSec, 888 (short) mtu, true /* rapidCommit */, captivePortalApiUrl)); 889 } else { 890 mPacketReader.sendResponse(buildDhcpOfferPacket(packet, CLIENT_ADDR, 891 leaseTimeSec, (short) mtu, captivePortalApiUrl)); 892 } 893 } else if (packet instanceof DhcpRequestPacket) { 894 final ByteBuffer byteBuffer = isSuccessLease 895 ? buildDhcpAckPacket(packet, CLIENT_ADDR, leaseTimeSec, (short) mtu, 896 false /* rapidCommit */, captivePortalApiUrl) 897 : buildDhcpNakPacket(packet); 898 mPacketReader.sendResponse(byteBuffer); 899 } else { 900 fail("invalid DHCP packet"); 901 } 902 903 // wait for reply to DHCPOFFER packet if disabling rapid commit option 904 if (shouldReplyRapidCommitAck || !(packet instanceof DhcpDiscoverPacket)) { 905 return packetList; 906 } 907 } 908 fail("No DHCPREQUEST received on interface"); 909 return packetList; 910 } 911 912 private List<DhcpPacket> performDhcpHandshake(final boolean isSuccessLease, 913 final Integer leaseTimeSec, final boolean isDhcpLeaseCacheEnabled, 914 final boolean isDhcpRapidCommitEnabled, final int mtu, 915 final boolean isDhcpIpConflictDetectEnabled) throws Exception { 916 return performDhcpHandshake(isSuccessLease, leaseTimeSec, isDhcpLeaseCacheEnabled, 917 isDhcpRapidCommitEnabled, mtu, isDhcpIpConflictDetectEnabled, 918 false /* isIPv6OnlyPreferredEnabled */, 919 null /* captivePortalApiUrl */, null /* displayName */, null /* scanResultInfo */, 920 null /* layer2Info */); 921 } 922 923 private List<DhcpPacket> performDhcpHandshake() throws Exception { 924 return performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S, 925 false /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */, 926 TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */); 927 } 928 929 private DhcpPacket getNextDhcpPacket(final long timeout) throws Exception { 930 byte[] packet; 931 while ((packet = mDhcpPacketReadHead.getValue() 932 .poll(timeout, this::isDhcpPacket)) != null) { 933 final DhcpPacket dhcpPacket = DhcpPacket.decodeFullPacket(packet, packet.length, 934 ENCAP_L2); 935 if (dhcpPacket != null) return dhcpPacket; 936 } 937 return null; 938 } 939 940 private DhcpPacket getNextDhcpPacket() throws Exception { 941 final DhcpPacket packet = getNextDhcpPacket(PACKET_TIMEOUT_MS); 942 assertNotNull("No expected DHCP packet received on interface within timeout", packet); 943 return packet; 944 } 945 946 private DhcpPacket getReplyFromDhcpLease(final NetworkAttributes na, boolean timeout) 947 throws Exception { 948 doAnswer(invocation -> { 949 if (timeout) return null; 950 ((OnNetworkAttributesRetrievedListener) invocation.getArgument(1)) 951 .onNetworkAttributesRetrieved(new Status(SUCCESS), TEST_L2KEY, na); 952 return null; 953 }).when(mIpMemoryStore).retrieveNetworkAttributes(eq(TEST_L2KEY), any()); 954 startIpClientProvisioning(true /* isDhcpLeaseCacheEnabled */, 955 false /* shouldReplyRapidCommitAck */, false /* isPreconnectionEnabled */, 956 false /* isDhcpIpConflictDetectEnabled */, false /* isIPv6OnlyPreferredEnabled */); 957 return getNextDhcpPacket(); 958 } 959 960 private void removeTapInterface(final FileDescriptor fd) { 961 try { 962 Os.close(fd); 963 } catch (ErrnoException e) { 964 fail("Fail to close file descriptor: " + e); 965 } 966 } 967 968 private void verifyAfterIpClientShutdown() throws RemoteException { 969 final LinkProperties emptyLp = new LinkProperties(); 970 emptyLp.setInterfaceName(mIfaceName); 971 verify(mCb, timeout(TEST_TIMEOUT_MS)).onLinkPropertiesChange(emptyLp); 972 } 973 974 // Verify IPv4-only provisioning success. No need to verify IPv4 provisioning when below cases 975 // happen: 976 // 1. if there's a failure lease, onProvisioningSuccess() won't be called; 977 // 2. if duplicated IPv4 address detection is enabled, verify TIMEOUT will affect ARP packets 978 // capture running in other test cases. 979 // 3. if IPv6 is enabled, e.g. withoutIPv6() isn't called when starting provisioning. 980 private void verifyIPv4OnlyProvisioningSuccess(final Collection<InetAddress> addresses) 981 throws Exception { 982 final ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class); 983 verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(captor.capture()); 984 LinkProperties lp = captor.getValue(); 985 assertNotNull(lp); 986 assertNotEquals(0, lp.getDnsServers().size()); 987 assertEquals(addresses.size(), lp.getAddresses().size()); 988 assertTrue(lp.getAddresses().containsAll(addresses)); 989 } 990 991 private void doRestoreInitialMtuTest(final boolean shouldChangeMtu, 992 final boolean shouldRemoveTapInterface) throws Exception { 993 final long currentTime = System.currentTimeMillis(); 994 int mtu = TEST_DEFAULT_MTU; 995 996 if (shouldChangeMtu) mtu = TEST_MIN_MTU; 997 performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S, 998 true /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */, 999 mtu, false /* isDhcpIpConflictDetectEnabled */); 1000 verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR)); 1001 assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, mtu); 1002 1003 if (shouldChangeMtu) { 1004 // Pretend that ConnectivityService set the MTU. 1005 mNetd.interfaceSetMtu(mIfaceName, mtu); 1006 assertEquals(NetworkInterface.getByName(mIfaceName).getMTU(), mtu); 1007 } 1008 1009 // Sometimes, IpClient receives an update with an empty LinkProperties during startup, 1010 // when the link-local address is deleted after interface bringup. Reset expectations 1011 // here to ensure that verifyAfterIpClientShutdown does not fail because it sees two 1012 // empty LinkProperties changes instead of one. 1013 reset(mCb); 1014 1015 if (shouldRemoveTapInterface) removeTapInterface(mTapFd); 1016 try { 1017 mIpc.shutdown(); 1018 awaitIpClientShutdown(); 1019 if (shouldRemoveTapInterface) { 1020 verify(mNetd, never()).interfaceSetMtu(mIfaceName, TEST_DEFAULT_MTU); 1021 } else { 1022 // Verify that MTU indeed has been restored or not. 1023 verify(mNetd, times(shouldChangeMtu ? 1 : 0)) 1024 .interfaceSetMtu(mIfaceName, TEST_DEFAULT_MTU); 1025 } 1026 verifyAfterIpClientShutdown(); 1027 } catch (Exception e) { 1028 fail("Exception should not have been thrown after shutdown: " + e); 1029 } 1030 } 1031 1032 private DhcpPacket assertDiscoverPacketOnPreconnectionStart() throws Exception { 1033 final ArgumentCaptor<List<Layer2PacketParcelable>> l2PacketList = 1034 ArgumentCaptor.forClass(List.class); 1035 1036 verify(mCb, timeout(TEST_TIMEOUT_MS)).onPreconnectionStart(l2PacketList.capture()); 1037 final byte[] payload = l2PacketList.getValue().get(0).payload; 1038 DhcpPacket packet = DhcpPacket.decodeFullPacket(payload, payload.length, ENCAP_L2); 1039 assertTrue(packet instanceof DhcpDiscoverPacket); 1040 assertArrayEquals(INADDR_BROADCAST.getAddress(), 1041 Arrays.copyOfRange(payload, IPV4_DST_ADDR_OFFSET, IPV4_DST_ADDR_OFFSET + 4)); 1042 return packet; 1043 } 1044 1045 private void doIpClientProvisioningWithPreconnectionTest( 1046 final boolean shouldReplyRapidCommitAck, final boolean shouldAbortPreconnection, 1047 final boolean shouldFirePreconnectionTimeout, 1048 final boolean timeoutBeforePreconnectionComplete) throws Exception { 1049 final long currentTime = System.currentTimeMillis(); 1050 final ArgumentCaptor<InterfaceConfigurationParcel> ifConfig = 1051 ArgumentCaptor.forClass(InterfaceConfigurationParcel.class); 1052 1053 startIpClientProvisioning(true /* isDhcpLeaseCacheEnabled */, 1054 shouldReplyRapidCommitAck, true /* isDhcpPreConnectionEnabled */, 1055 false /* isDhcpIpConflictDetectEnabled */, false /* isIPv6OnlyPreferredEnabled */); 1056 DhcpPacket packet = assertDiscoverPacketOnPreconnectionStart(); 1057 final int preconnDiscoverTransId = packet.getTransactionId(); 1058 1059 if (shouldAbortPreconnection) { 1060 if (shouldFirePreconnectionTimeout && timeoutBeforePreconnectionComplete) { 1061 mDependencies.mDhcpClient.sendMessage(DhcpClient.CMD_TIMEOUT); 1062 } 1063 1064 mIpc.notifyPreconnectionComplete(false /* abort */); 1065 HandlerUtils.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS); 1066 1067 if (shouldFirePreconnectionTimeout && !timeoutBeforePreconnectionComplete) { 1068 mDependencies.mDhcpClient.sendMessage(DhcpClient.CMD_TIMEOUT); 1069 } 1070 1071 // Either way should get DhcpClient go back to INIT state, and broadcast 1072 // DISCOVER with new transaction ID. 1073 packet = getNextDhcpPacket(); 1074 assertTrue(packet instanceof DhcpDiscoverPacket); 1075 assertTrue(packet.getTransactionId() != preconnDiscoverTransId); 1076 } else if (shouldFirePreconnectionTimeout && timeoutBeforePreconnectionComplete) { 1077 // If timeout fires before success preconnection, DhcpClient will go back to INIT state, 1078 // and broadcast DISCOVER with new transaction ID. 1079 mDependencies.mDhcpClient.sendMessage(DhcpClient.CMD_TIMEOUT); 1080 packet = getNextDhcpPacket(); 1081 assertTrue(packet instanceof DhcpDiscoverPacket); 1082 assertTrue(packet.getTransactionId() != preconnDiscoverTransId); 1083 // any old response would be ignored due to mismatched transaction ID. 1084 } 1085 1086 final short mtu = (short) TEST_DEFAULT_MTU; 1087 if (!shouldReplyRapidCommitAck) { 1088 mPacketReader.sendResponse(buildDhcpOfferPacket(packet, CLIENT_ADDR, 1089 TEST_LEASE_DURATION_S, mtu, null /* captivePortalUrl */)); 1090 packet = getNextDhcpPacket(); 1091 assertTrue(packet instanceof DhcpRequestPacket); 1092 } 1093 mPacketReader.sendResponse(buildDhcpAckPacket(packet, CLIENT_ADDR, TEST_LEASE_DURATION_S, 1094 mtu, shouldReplyRapidCommitAck, null /* captivePortalUrl */)); 1095 1096 if (!shouldAbortPreconnection) { 1097 mIpc.notifyPreconnectionComplete(true /* success */); 1098 HandlerUtils.waitForIdle(mDependencies.mDhcpClient.getHandler(), TEST_TIMEOUT_MS); 1099 1100 // If timeout fires after successful preconnection, right now DhcpClient will have 1101 // already entered BOUND state, the delayed CMD_TIMEOUT command would be ignored. So 1102 // this case should be very rare, because the timeout alarm is cancelled when state 1103 // machine exits from Preconnecting state. 1104 if (shouldFirePreconnectionTimeout && !timeoutBeforePreconnectionComplete) { 1105 mDependencies.mDhcpClient.sendMessage(DhcpClient.CMD_TIMEOUT); 1106 } 1107 } 1108 verify(mCb, timeout(TEST_TIMEOUT_MS)).setFallbackMulticastFilter(false); 1109 1110 final LinkAddress ipAddress = new LinkAddress(CLIENT_ADDR, PREFIX_LENGTH); 1111 verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceSetCfg(ifConfig.capture()); 1112 assertEquals(ifConfig.getValue().ifName, mIfaceName); 1113 assertEquals(ifConfig.getValue().ipv4Addr, ipAddress.getAddress().getHostAddress()); 1114 assertEquals(ifConfig.getValue().prefixLength, PREFIX_LENGTH); 1115 assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU); 1116 } 1117 1118 private ArpPacket getNextArpPacket(final long timeout) throws Exception { 1119 byte[] packet; 1120 while ((packet = mArpPacketReadHead.getValue().poll(timeout, p -> true)) != null) { 1121 final ArpPacket arpPacket = parseArpPacketOrNull(packet); 1122 if (arpPacket != null) return arpPacket; 1123 } 1124 return null; 1125 } 1126 1127 private ArpPacket getNextArpPacket() throws Exception { 1128 final ArpPacket packet = getNextArpPacket(PACKET_TIMEOUT_MS); 1129 assertNotNull("No expected ARP packet received on interface within timeout", packet); 1130 return packet; 1131 } 1132 1133 private void assertArpPacket(final ArpPacket packet) { 1134 assertEquals(packet.opCode, ARP_REQUEST); 1135 assertEquals(packet.targetIp, CLIENT_ADDR); 1136 assertTrue(Arrays.equals(packet.senderHwAddress.toByteArray(), mClientMac)); 1137 } 1138 1139 private void assertArpProbe(final ArpPacket packet) { 1140 assertArpPacket(packet); 1141 assertEquals(packet.senderIp, INADDR_ANY); 1142 } 1143 1144 private void assertArpAnnounce(final ArpPacket packet) { 1145 assertArpPacket(packet); 1146 assertEquals(packet.senderIp, CLIENT_ADDR); 1147 } 1148 1149 private void assertGratuitousARP(final ArpPacket packet) { 1150 assertEquals(packet.opCode, ARP_REPLY); 1151 assertEquals(packet.senderIp, CLIENT_ADDR); 1152 assertEquals(packet.targetIp, CLIENT_ADDR); 1153 assertTrue(Arrays.equals(packet.senderHwAddress.toByteArray(), mClientMac)); 1154 assertTrue(Arrays.equals(packet.targetHwAddress.toByteArray(), ETHER_BROADCAST)); 1155 } 1156 1157 private void doIpAddressConflictDetectionTest(final boolean causeIpAddressConflict, 1158 final boolean shouldReplyRapidCommitAck, final boolean isDhcpIpConflictDetectEnabled, 1159 final boolean shouldResponseArpReply) throws Exception { 1160 final long currentTime = System.currentTimeMillis(); 1161 1162 performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S, 1163 true /* isDhcpLeaseCacheEnabled */, shouldReplyRapidCommitAck, 1164 TEST_DEFAULT_MTU, isDhcpIpConflictDetectEnabled); 1165 1166 // If we receive an ARP packet here, it's guaranteed to be from IP conflict detection, 1167 // because at this time the test interface does not have an IP address and therefore 1168 // won't send ARP for anything. 1169 if (causeIpAddressConflict) { 1170 final ArpPacket arpProbe = getNextArpPacket(); 1171 assertArpProbe(arpProbe); 1172 1173 if (shouldResponseArpReply) { 1174 sendArpReply(mClientMac); 1175 } else { 1176 sendArpProbe(); 1177 } 1178 final DhcpPacket packet = getNextDhcpPacket(); 1179 assertTrue(packet instanceof DhcpDeclinePacket); 1180 assertEquals(packet.mServerIdentifier, SERVER_ADDR); 1181 assertEquals(packet.mRequestedIp, CLIENT_ADDR); 1182 1183 verify(mCb, never()).onProvisioningFailure(any()); 1184 assertIpMemoryNeverStoreNetworkAttributes(); 1185 } else if (isDhcpIpConflictDetectEnabled) { 1186 int arpPacketCount = 0; 1187 final List<ArpPacket> packetList = new ArrayList<ArpPacket>(); 1188 // Total sent ARP packets should be 5 (3 ARP Probes + 2 ARP Announcements) 1189 ArpPacket packet; 1190 while ((packet = getNextArpPacket(TEST_TIMEOUT_MS)) != null) { 1191 packetList.add(packet); 1192 } 1193 assertEquals(5, packetList.size()); 1194 assertArpProbe(packetList.get(0)); 1195 assertArpAnnounce(packetList.get(3)); 1196 } else { 1197 verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR)); 1198 assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, 1199 TEST_DEFAULT_MTU); 1200 } 1201 } 1202 1203 @Test @SignatureRequiredTest(reason = "InterfaceParams.getByName requires CAP_NET_ADMIN") 1204 public void testInterfaceParams() throws Exception { 1205 InterfaceParams params = InterfaceParams.getByName(mIfaceName); 1206 assertNotNull(params); 1207 assertEquals(mIfaceName, params.name); 1208 assertTrue(params.index > 0); 1209 assertNotNull(params.macAddr); 1210 assertTrue(params.hasMacAddress); 1211 1212 // Check interface "lo". 1213 params = InterfaceParams.getByName("lo"); 1214 assertNotNull(params); 1215 assertEquals("lo", params.name); 1216 assertTrue(params.index > 0); 1217 assertNotNull(params.macAddr); 1218 assertFalse(params.hasMacAddress); 1219 } 1220 1221 @Test 1222 public void testDhcpInit() throws Exception { 1223 startIpClientProvisioning(false /* isDhcpLeaseCacheEnabled */, 1224 false /* shouldReplyRapidCommitAck */, false /* isPreconnectionEnabled */, 1225 false /* isDhcpIpConflictDetectEnabled */, false /* isIPv6OnlyPreferredEnabled */); 1226 final DhcpPacket packet = getNextDhcpPacket(); 1227 assertTrue(packet instanceof DhcpDiscoverPacket); 1228 } 1229 1230 @Test 1231 public void testHandleSuccessDhcpLease() throws Exception { 1232 final long currentTime = System.currentTimeMillis(); 1233 performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S, 1234 true /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */, 1235 TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */); 1236 verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR)); 1237 assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU); 1238 } 1239 1240 @Test 1241 public void testHandleFailureDhcpLease() throws Exception { 1242 performDhcpHandshake(false /* isSuccessLease */, TEST_LEASE_DURATION_S, 1243 true /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */, 1244 TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */); 1245 1246 verify(mCb, never()).onProvisioningSuccess(any()); 1247 assertIpMemoryNeverStoreNetworkAttributes(); 1248 } 1249 1250 @Test 1251 public void testHandleInfiniteLease() throws Exception { 1252 final long currentTime = System.currentTimeMillis(); 1253 performDhcpHandshake(true /* isSuccessLease */, INFINITE_LEASE, 1254 true /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */, 1255 TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */); 1256 verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR)); 1257 assertIpMemoryStoreNetworkAttributes(INFINITE_LEASE, currentTime, TEST_DEFAULT_MTU); 1258 } 1259 1260 @Test 1261 public void testHandleNoLease() throws Exception { 1262 final long currentTime = System.currentTimeMillis(); 1263 performDhcpHandshake(true /* isSuccessLease */, null /* no lease time */, 1264 true /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */, 1265 TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */); 1266 verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR)); 1267 assertIpMemoryStoreNetworkAttributes(null, currentTime, TEST_DEFAULT_MTU); 1268 } 1269 1270 @Test @IgnoreAfter(Build.VERSION_CODES.Q) // INIT-REBOOT is enabled on R. 1271 public void testHandleDisableInitRebootState() throws Exception { 1272 performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S, 1273 false /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */, 1274 TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */); 1275 verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR)); 1276 assertIpMemoryNeverStoreNetworkAttributes(); 1277 } 1278 1279 @Test 1280 public void testHandleRapidCommitOption() throws Exception { 1281 final long currentTime = System.currentTimeMillis(); 1282 performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S, 1283 true /* isDhcpLeaseCacheEnabled */, true /* shouldReplyRapidCommitAck */, 1284 TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */); 1285 verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR)); 1286 assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU); 1287 } 1288 1289 @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") 1290 public void testDhcpClientStartWithCachedInfiniteLease() throws Exception { 1291 final DhcpPacket packet = getReplyFromDhcpLease( 1292 new NetworkAttributes.Builder() 1293 .setAssignedV4Address(CLIENT_ADDR) 1294 .setAssignedV4AddressExpiry(Long.MAX_VALUE) // lease is always valid 1295 .setMtu(new Integer(TEST_DEFAULT_MTU)) 1296 .setCluster(TEST_CLUSTER) 1297 .setDnsAddresses(Collections.singletonList(SERVER_ADDR)) 1298 .build(), false /* timeout */); 1299 assertTrue(packet instanceof DhcpRequestPacket); 1300 } 1301 1302 @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") 1303 public void testDhcpClientStartWithCachedExpiredLease() throws Exception { 1304 final DhcpPacket packet = getReplyFromDhcpLease( 1305 new NetworkAttributes.Builder() 1306 .setAssignedV4Address(CLIENT_ADDR) 1307 .setAssignedV4AddressExpiry(EXPIRED_LEASE) 1308 .setMtu(new Integer(TEST_DEFAULT_MTU)) 1309 .setCluster(TEST_CLUSTER) 1310 .setDnsAddresses(Collections.singletonList(SERVER_ADDR)) 1311 .build(), false /* timeout */); 1312 assertTrue(packet instanceof DhcpDiscoverPacket); 1313 } 1314 1315 @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") 1316 public void testDhcpClientStartWithNullRetrieveNetworkAttributes() throws Exception { 1317 final DhcpPacket packet = getReplyFromDhcpLease(null /* na */, false /* timeout */); 1318 assertTrue(packet instanceof DhcpDiscoverPacket); 1319 } 1320 1321 @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") 1322 public void testDhcpClientStartWithTimeoutRetrieveNetworkAttributes() throws Exception { 1323 final DhcpPacket packet = getReplyFromDhcpLease( 1324 new NetworkAttributes.Builder() 1325 .setAssignedV4Address(CLIENT_ADDR) 1326 .setAssignedV4AddressExpiry(System.currentTimeMillis() + 3_600_000) 1327 .setMtu(new Integer(TEST_DEFAULT_MTU)) 1328 .setCluster(TEST_CLUSTER) 1329 .setDnsAddresses(Collections.singletonList(SERVER_ADDR)) 1330 .build(), true /* timeout */); 1331 assertTrue(packet instanceof DhcpDiscoverPacket); 1332 } 1333 1334 @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") 1335 public void testDhcpClientStartWithCachedLeaseWithoutIPAddress() throws Exception { 1336 final DhcpPacket packet = getReplyFromDhcpLease( 1337 new NetworkAttributes.Builder() 1338 .setMtu(new Integer(TEST_DEFAULT_MTU)) 1339 .setCluster(TEST_CLUSTER) 1340 .setDnsAddresses(Collections.singletonList(SERVER_ADDR)) 1341 .build(), false /* timeout */); 1342 assertTrue(packet instanceof DhcpDiscoverPacket); 1343 } 1344 1345 @Test 1346 public void testDhcpClientRapidCommitEnabled() throws Exception { 1347 startIpClientProvisioning(false /* isDhcpLeaseCacheEnabled */, 1348 true /* shouldReplyRapidCommitAck */, false /* isPreconnectionEnabled */, 1349 false /* isDhcpIpConflictDetectEnabled */, false /* isIPv6OnlyPreferredEnabled */); 1350 final DhcpPacket packet = getNextDhcpPacket(); 1351 assertTrue(packet instanceof DhcpDiscoverPacket); 1352 } 1353 1354 @Test @IgnoreUpTo(Build.VERSION_CODES.Q) 1355 public void testDhcpServerInLinkProperties() throws Exception { 1356 assumeTrue(ConstantsShim.VERSION > Build.VERSION_CODES.Q); 1357 1358 performDhcpHandshake(); 1359 ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class); 1360 verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(captor.capture()); 1361 assertEquals(SERVER_ADDR, captor.getValue().getDhcpServerAddress()); 1362 } 1363 1364 @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") 1365 public void testRestoreInitialInterfaceMtu() throws Exception { 1366 doRestoreInitialMtuTest(true /* shouldChangeMtu */, false /* shouldRemoveTapInterface */); 1367 } 1368 1369 @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") 1370 public void testRestoreInitialInterfaceMtu_WithoutMtuChange() throws Exception { 1371 doRestoreInitialMtuTest(false /* shouldChangeMtu */, false /* shouldRemoveTapInterface */); 1372 } 1373 1374 @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") 1375 public void testRestoreInitialInterfaceMtu_WithException() throws Exception { 1376 doThrow(new RemoteException("NetdNativeService::interfaceSetMtu")).when(mNetd) 1377 .interfaceSetMtu(mIfaceName, TEST_DEFAULT_MTU); 1378 1379 doRestoreInitialMtuTest(true /* shouldChangeMtu */, false /* shouldRemoveTapInterface */); 1380 assertEquals(NetworkInterface.getByName(mIfaceName).getMTU(), TEST_MIN_MTU); 1381 } 1382 1383 @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") 1384 public void testRestoreInitialInterfaceMtu_NotFoundInterfaceWhenStopping() throws Exception { 1385 doRestoreInitialMtuTest(true /* shouldChangeMtu */, true /* shouldRemoveTapInterface */); 1386 } 1387 1388 @Test 1389 public void testRestoreInitialInterfaceMtu_NotFoundInterfaceWhenStartingProvisioning() 1390 throws Exception { 1391 removeTapInterface(mTapFd); 1392 ProvisioningConfiguration config = new ProvisioningConfiguration.Builder() 1393 .withoutIpReachabilityMonitor() 1394 .withoutIPv6() 1395 .build(); 1396 1397 startIpClientProvisioning(config); 1398 verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningFailure(any()); 1399 verify(mCb, never()).setNeighborDiscoveryOffload(true); 1400 } 1401 1402 @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") 1403 public void testRestoreInitialInterfaceMtu_stopIpClientAndRestart() throws Exception { 1404 long currentTime = System.currentTimeMillis(); 1405 1406 performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S, 1407 true /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */, 1408 TEST_MIN_MTU, false /* isDhcpIpConflictDetectEnabled */); 1409 verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR)); 1410 assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_MIN_MTU); 1411 1412 // Pretend that ConnectivityService set the MTU. 1413 mNetd.interfaceSetMtu(mIfaceName, TEST_MIN_MTU); 1414 assertEquals(NetworkInterface.getByName(mIfaceName).getMTU(), TEST_MIN_MTU); 1415 1416 reset(mCb); 1417 reset(mIpMemoryStore); 1418 1419 // Stop IpClient and then restart provisioning immediately. 1420 mIpc.stop(); 1421 currentTime = System.currentTimeMillis(); 1422 // Intend to set mtu option to 0, then verify that won't influence interface mtu restore. 1423 performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S, 1424 true /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */, 1425 0 /* mtu */, false /* isDhcpIpConflictDetectEnabled */); 1426 verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR)); 1427 assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, 0 /* mtu */); 1428 assertEquals(NetworkInterface.getByName(mIfaceName).getMTU(), TEST_DEFAULT_MTU); 1429 } 1430 1431 @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") 1432 public void testRestoreInitialInterfaceMtu_removeInterfaceAndAddback() throws Exception { 1433 doAnswer(invocation -> { 1434 final LinkProperties lp = invocation.getArgument(0); 1435 assertEquals(lp.getInterfaceName(), mIfaceName); 1436 assertEquals(0, lp.getLinkAddresses().size()); 1437 assertEquals(0, lp.getDnsServers().size()); 1438 1439 mDependencies.simulateInterfaceRecover(); 1440 return null; 1441 }).when(mCb).onProvisioningFailure(any()); 1442 1443 final ProvisioningConfiguration config = new ProvisioningConfiguration.Builder() 1444 .withoutIpReachabilityMonitor() 1445 .withoutIPv6() 1446 .build(); 1447 1448 // Intend to remove the tap interface and force IpClient throw provisioning failure 1449 // due to that interface is not found. 1450 removeTapInterface(mTapFd); 1451 assertNull(InterfaceParams.getByName(mIfaceName)); 1452 1453 startIpClientProvisioning(config); 1454 verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningFailure(any()); 1455 1456 // Make sure everything queued by this test was processed (e.g. transition to StoppingState 1457 // from ClearingIpAddressState) and tearDown will check if IpClient exits normally or crash. 1458 HandlerUtils.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS); 1459 } 1460 1461 private boolean isRouterSolicitation(final byte[] packetBytes) { 1462 ByteBuffer packet = ByteBuffer.wrap(packetBytes); 1463 return packet.getShort(ETHER_TYPE_OFFSET) == (short) ETH_P_IPV6 1464 && packet.get(ETHER_HEADER_LEN + IPV6_PROTOCOL_OFFSET) == (byte) IPPROTO_ICMPV6 1465 && packet.get(ETHER_HEADER_LEN + IPV6_HEADER_LEN) 1466 == (byte) ICMPV6_ROUTER_SOLICITATION; 1467 } 1468 1469 private boolean isNeighborAdvertisement(final byte[] packetBytes) { 1470 ByteBuffer packet = ByteBuffer.wrap(packetBytes); 1471 return packet.getShort(ETHER_TYPE_OFFSET) == (short) ETH_P_IPV6 1472 && packet.get(ETHER_HEADER_LEN + IPV6_PROTOCOL_OFFSET) == (byte) IPPROTO_ICMPV6 1473 && packet.get(ETHER_HEADER_LEN + IPV6_HEADER_LEN) 1474 == (byte) ICMPV6_NEIGHBOR_ADVERTISEMENT; 1475 } 1476 1477 private NeighborAdvertisement getNextNeighborAdvertisement() throws ParseException { 1478 final byte[] packet = mPacketReader.popPacket(PACKET_TIMEOUT_MS, 1479 this::isNeighborAdvertisement); 1480 if (packet == null) return null; 1481 1482 final NeighborAdvertisement na = parseNeighborAdvertisementOrNull(packet); 1483 assertNotNull("Invalid neighbour advertisement received", na); 1484 return na; 1485 } 1486 1487 private void waitForRouterSolicitation() throws ParseException { 1488 assertNotNull("No router solicitation received on interface within timeout", 1489 mPacketReader.popPacket(PACKET_TIMEOUT_MS, this::isRouterSolicitation)); 1490 } 1491 1492 private void sendRouterAdvertisement(boolean waitForRs, short lifetime) throws Exception { 1493 final String dnsServer = "2001:4860:4860::64"; 1494 final ByteBuffer pio = buildPioOption(3600, 1800, "2001:db8:1::/64"); 1495 ByteBuffer rdnss = buildRdnssOption(3600, dnsServer); 1496 ByteBuffer ra = buildRaPacket(lifetime, pio, rdnss); 1497 1498 if (waitForRs) { 1499 waitForRouterSolicitation(); 1500 } 1501 1502 mPacketReader.sendResponse(ra); 1503 } 1504 1505 private void sendBasicRouterAdvertisement(boolean waitForRs) throws Exception { 1506 sendRouterAdvertisement(waitForRs, (short) 1800); 1507 } 1508 1509 private void sendRouterAdvertisementWithZeroLifetime() throws Exception { 1510 sendRouterAdvertisement(false /* waitForRs */, (short) 0); 1511 } 1512 1513 // TODO: move this and the following method to a common location and use them in ApfTest. 1514 private static ByteBuffer buildPioOption(int valid, int preferred, String prefixString) 1515 throws Exception { 1516 return PrefixInformationOption.build(new IpPrefix(prefixString), 1517 (byte) (PIO_FLAG_ON_LINK | PIO_FLAG_AUTONOMOUS), valid, preferred); 1518 } 1519 1520 private static ByteBuffer buildRdnssOption(int lifetime, String... servers) throws Exception { 1521 return RdnssOption.build(lifetime, servers); 1522 } 1523 1524 private static ByteBuffer buildRaPacket(short lifetime, ByteBuffer... options) 1525 throws Exception { 1526 final MacAddress dstMac = MacAddress.fromString("33:33:00:00:00:01"); 1527 final MacAddress srcMac = MacAddress.fromString("01:02:03:04:05:06"); 1528 final Inet6Address routerLinkLocal = 1529 (Inet6Address) InetAddresses.parseNumericAddress("fe80::1"); 1530 1531 return Ipv6Utils.buildRaPacket(srcMac, dstMac, routerLinkLocal, 1532 IPV6_ADDR_ALL_NODES_MULTICAST, (byte) 0 /* M=0, O=0 */, lifetime, 1533 0 /* Reachable time, unspecified */, 100 /* Retrans time 100ms */, 1534 options); 1535 } 1536 1537 private static ByteBuffer buildRaPacket(ByteBuffer... options) throws Exception { 1538 return buildRaPacket((short) 1800, options); 1539 } 1540 1541 private void disableIpv6ProvisioningDelays() throws Exception { 1542 // Speed up the test by disabling DAD and removing router_solicitation_delay. 1543 // We don't need to restore the default value because the interface is removed in tearDown. 1544 // TODO: speed up further by not waiting for RS but keying off first IPv6 packet. 1545 mNetd.setProcSysNet(INetd.IPV6, INetd.CONF, mIfaceName, "router_solicitation_delay", "0"); 1546 mNetd.setProcSysNet(INetd.IPV6, INetd.CONF, mIfaceName, "dad_transmits", "0"); 1547 } 1548 1549 private void assertHasAddressThat(String msg, LinkProperties lp, 1550 Predicate<LinkAddress> condition) { 1551 for (LinkAddress addr : lp.getLinkAddresses()) { 1552 if (condition.test(addr)) { 1553 return; 1554 } 1555 } 1556 fail(msg + " not found in: " + lp); 1557 } 1558 1559 private boolean hasFlag(LinkAddress addr, int flag) { 1560 return (addr.getFlags() & flag) == flag; 1561 } 1562 1563 private boolean isPrivacyAddress(LinkAddress addr) { 1564 return addr.isGlobalPreferred() && hasFlag(addr, IFA_F_TEMPORARY); 1565 } 1566 1567 private boolean isStablePrivacyAddress(LinkAddress addr) { 1568 // TODO: move away from getting address updates from netd and make this work on Q as well. 1569 final int flag = ShimUtils.isAtLeastR() ? IFA_F_STABLE_PRIVACY : 0; 1570 return addr.isGlobalPreferred() && hasFlag(addr, flag); 1571 } 1572 1573 private LinkProperties doIpv6OnlyProvisioning() throws Exception { 1574 final InOrder inOrder = inOrder(mCb); 1575 final String dnsServer = "2001:4860:4860::64"; 1576 final ByteBuffer pio = buildPioOption(3600, 1800, "2001:db8:1::/64"); 1577 final ByteBuffer rdnss = buildRdnssOption(3600, dnsServer); 1578 final ByteBuffer ra = buildRaPacket(pio, rdnss); 1579 1580 return doIpv6OnlyProvisioning(inOrder, ra); 1581 } 1582 1583 private LinkProperties doIpv6OnlyProvisioning(InOrder inOrder, ByteBuffer ra) throws Exception { 1584 waitForRouterSolicitation(); 1585 mPacketReader.sendResponse(ra); 1586 1587 // The lambda below needs to write a LinkProperties to a local variable, but lambdas cannot 1588 // write to non-final local variables. So declare a final variable to write to. 1589 final AtomicReference<LinkProperties> lpRef = new AtomicReference<>(); 1590 1591 ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class); 1592 verifyWithTimeout(inOrder, mCb).onProvisioningSuccess(captor.capture()); 1593 lpRef.set(captor.getValue()); 1594 1595 // Sometimes provisioning completes as soon as the link-local and the stable address appear, 1596 // before the privacy address appears. If so, wait here for the LinkProperties update that 1597 // contains all three address. Otherwise, future calls to verify() might get confused. 1598 if (captor.getValue().getLinkAddresses().size() == 2) { 1599 verifyWithTimeout(inOrder, mCb).onLinkPropertiesChange(argThat(lp -> { 1600 lpRef.set(lp); 1601 return lp.getLinkAddresses().size() == 3; 1602 })); 1603 } 1604 1605 LinkProperties lp = lpRef.get(); 1606 assertEquals("Should have 3 IPv6 addresses after provisioning: " + lp, 1607 3, lp.getLinkAddresses().size()); 1608 assertHasAddressThat("link-local address", lp, x -> x.getAddress().isLinkLocalAddress()); 1609 assertHasAddressThat("privacy address", lp, this::isPrivacyAddress); 1610 assertHasAddressThat("stable privacy address", lp, this::isStablePrivacyAddress); 1611 1612 return lp; 1613 } 1614 1615 @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") 1616 public void testRaRdnss() throws Exception { 1617 ProvisioningConfiguration config = new ProvisioningConfiguration.Builder() 1618 .withoutIpReachabilityMonitor() 1619 .withoutIPv4() 1620 .build(); 1621 startIpClientProvisioning(config); 1622 1623 InOrder inOrder = inOrder(mCb); 1624 ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class); 1625 1626 final String dnsServer = "2001:4860:4860::64"; 1627 final String lowlifeDnsServer = "2001:4860:4860::6464"; 1628 1629 final ByteBuffer pio = buildPioOption(600, 300, "2001:db8:1::/64"); 1630 ByteBuffer rdnss1 = buildRdnssOption(60, lowlifeDnsServer); 1631 ByteBuffer rdnss2 = buildRdnssOption(600, dnsServer); 1632 ByteBuffer ra = buildRaPacket(pio, rdnss1, rdnss2); 1633 1634 LinkProperties lp = doIpv6OnlyProvisioning(inOrder, ra); 1635 1636 // Expect that DNS servers with lifetimes below CONFIG_MIN_RDNSS_LIFETIME are not accepted. 1637 assertNotNull(lp); 1638 assertEquals(1, lp.getDnsServers().size()); 1639 assertTrue(lp.getDnsServers().contains(InetAddress.getByName(dnsServer))); 1640 1641 // If the RDNSS lifetime is above the minimum, the DNS server is accepted. 1642 rdnss1 = buildRdnssOption(68, lowlifeDnsServer); 1643 ra = buildRaPacket(pio, rdnss1, rdnss2); 1644 mPacketReader.sendResponse(ra); 1645 inOrder.verify(mCb, timeout(TEST_TIMEOUT_MS)).onLinkPropertiesChange(captor.capture()); 1646 lp = captor.getValue(); 1647 assertNotNull(lp); 1648 assertEquals(2, lp.getDnsServers().size()); 1649 assertTrue(lp.getDnsServers().contains(InetAddress.getByName(dnsServer))); 1650 assertTrue(lp.getDnsServers().contains(InetAddress.getByName(lowlifeDnsServer))); 1651 1652 // Expect that setting RDNSS lifetime of 0 causes loss of provisioning. 1653 rdnss1 = buildRdnssOption(0, dnsServer); 1654 rdnss2 = buildRdnssOption(0, lowlifeDnsServer); 1655 ra = buildRaPacket(pio, rdnss1, rdnss2); 1656 mPacketReader.sendResponse(ra); 1657 1658 inOrder.verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningFailure(captor.capture()); 1659 lp = captor.getValue(); 1660 assertNotNull(lp); 1661 assertEquals(0, lp.getDnsServers().size()); 1662 reset(mCb); 1663 } 1664 1665 private void expectNat64PrefixUpdate(InOrder inOrder, IpPrefix expected) throws Exception { 1666 inOrder.verify(mCb, timeout(TEST_TIMEOUT_MS)).onLinkPropertiesChange( 1667 argThat(lp -> Objects.equals(expected, lp.getNat64Prefix()))); 1668 1669 } 1670 1671 private void expectNoNat64PrefixUpdate(InOrder inOrder, IpPrefix unchanged) throws Exception { 1672 inOrder.verify(mCb, timeout(TEST_TIMEOUT_MS).times(0)).onLinkPropertiesChange(argThat( 1673 lp -> !Objects.equals(unchanged, lp.getNat64Prefix()))); 1674 1675 } 1676 1677 @Test @IgnoreUpTo(Build.VERSION_CODES.Q) 1678 @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") 1679 public void testPref64Option() throws Exception { 1680 assumeTrue(ConstantsShim.VERSION > Build.VERSION_CODES.Q); 1681 1682 ProvisioningConfiguration config = new ProvisioningConfiguration.Builder() 1683 .withoutIpReachabilityMonitor() 1684 .withoutIPv4() 1685 .build(); 1686 startIpClientProvisioning(config); 1687 1688 final String dnsServer = "2001:4860:4860::64"; 1689 final IpPrefix prefix = new IpPrefix("64:ff9b::/96"); 1690 final IpPrefix otherPrefix = new IpPrefix("2001:db8:64::/96"); 1691 1692 final ByteBuffer pio = buildPioOption(600, 300, "2001:db8:1::/64"); 1693 ByteBuffer rdnss = buildRdnssOption(600, dnsServer); 1694 ByteBuffer pref64 = new StructNdOptPref64(prefix, 600).toByteBuffer(); 1695 ByteBuffer ra = buildRaPacket(pio, rdnss, pref64); 1696 1697 // The NAT64 prefix might be detected before or after provisioning success. 1698 // Don't test order between these two events. 1699 LinkProperties lp = doIpv6OnlyProvisioning(null /*inOrder*/, ra); 1700 expectAlarmSet(null /*inOrder*/, "PREF64", 600); 1701 1702 // From now on expect events in order. 1703 InOrder inOrder = inOrder(mCb, mAlarm); 1704 if (lp.getNat64Prefix() != null) { 1705 assertEquals(prefix, lp.getNat64Prefix()); 1706 } else { 1707 expectNat64PrefixUpdate(inOrder, prefix); 1708 } 1709 1710 // Increase the lifetime and expect the prefix not to change. 1711 pref64 = new StructNdOptPref64(prefix, 1800).toByteBuffer(); 1712 ra = buildRaPacket(pio, rdnss, pref64); 1713 mPacketReader.sendResponse(ra); 1714 OnAlarmListener pref64Alarm = expectAlarmSet(inOrder, "PREF64", 1800); 1715 expectNoNat64PrefixUpdate(inOrder, prefix); 1716 reset(mCb, mAlarm); 1717 1718 // Reduce the lifetime and expect to reschedule expiry. 1719 pref64 = new StructNdOptPref64(prefix, 1500).toByteBuffer(); 1720 ra = buildRaPacket(pio, rdnss, pref64); 1721 mPacketReader.sendResponse(ra); 1722 pref64Alarm = expectAlarmSet(inOrder, "PREF64", 1496); 1723 expectNoNat64PrefixUpdate(inOrder, prefix); 1724 reset(mCb, mAlarm); 1725 1726 // Withdraw the prefix and expect it to be set to null. 1727 pref64 = new StructNdOptPref64(prefix, 0).toByteBuffer(); 1728 ra = buildRaPacket(pio, rdnss, pref64); 1729 mPacketReader.sendResponse(ra); 1730 expectAlarmCancelled(inOrder, pref64Alarm); 1731 expectNat64PrefixUpdate(inOrder, null); 1732 reset(mCb, mAlarm); 1733 1734 // Re-announce the prefix. 1735 pref64 = new StructNdOptPref64(prefix, 600).toByteBuffer(); 1736 ra = buildRaPacket(pio, rdnss, pref64); 1737 mPacketReader.sendResponse(ra); 1738 expectAlarmSet(inOrder, "PREF64", 600); 1739 expectNat64PrefixUpdate(inOrder, prefix); 1740 reset(mCb, mAlarm); 1741 1742 // Announce two prefixes. Don't expect any update because if there is already a NAT64 1743 // prefix, any new prefix is ignored. 1744 ByteBuffer otherPref64 = new StructNdOptPref64(otherPrefix, 1200).toByteBuffer(); 1745 ra = buildRaPacket(pio, rdnss, pref64, otherPref64); 1746 mPacketReader.sendResponse(ra); 1747 expectAlarmSet(inOrder, "PREF64", 600); 1748 expectNoNat64PrefixUpdate(inOrder, prefix); 1749 reset(mCb, mAlarm); 1750 1751 // Withdraw the old prefix and continue to announce the new one. Expect a prefix change. 1752 pref64 = new StructNdOptPref64(prefix, 0).toByteBuffer(); 1753 ra = buildRaPacket(pio, rdnss, pref64, otherPref64); 1754 mPacketReader.sendResponse(ra); 1755 expectAlarmCancelled(inOrder, pref64Alarm); 1756 // Need a different OnAlarmListener local variable because posting it to the handler in the 1757 // lambda below requires it to be final. 1758 final OnAlarmListener lastAlarm = expectAlarmSet(inOrder, "PREF64", 1200); 1759 expectNat64PrefixUpdate(inOrder, otherPrefix); 1760 reset(mCb, mAlarm); 1761 1762 // Simulate prefix expiry. 1763 mIpc.getHandler().post(() -> lastAlarm.onAlarm()); 1764 expectAlarmCancelled(inOrder, pref64Alarm); 1765 expectNat64PrefixUpdate(inOrder, null); 1766 1767 // Announce a non-/96 prefix and expect it to be ignored. 1768 IpPrefix invalidPrefix = new IpPrefix("64:ff9b::/64"); 1769 pref64 = new StructNdOptPref64(invalidPrefix, 1200).toByteBuffer(); 1770 ra = buildRaPacket(pio, rdnss, pref64); 1771 mPacketReader.sendResponse(ra); 1772 expectNoNat64PrefixUpdate(inOrder, invalidPrefix); 1773 1774 // Re-announce the prefix. 1775 pref64 = new StructNdOptPref64(prefix, 600).toByteBuffer(); 1776 ra = buildRaPacket(pio, rdnss, pref64); 1777 mPacketReader.sendResponse(ra); 1778 final OnAlarmListener clearAlarm = expectAlarmSet(inOrder, "PREF64", 600); 1779 expectNat64PrefixUpdate(inOrder, prefix); 1780 reset(mCb, mAlarm); 1781 1782 // Check that the alarm is cancelled when IpClient is stopped. 1783 mIpc.stop(); 1784 HandlerUtils.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS); 1785 expectAlarmCancelled(inOrder, clearAlarm); 1786 expectNat64PrefixUpdate(inOrder, null); 1787 1788 // Check that even if the alarm was already in the message queue while it was cancelled, it 1789 // is safely ignored. 1790 mIpc.getHandler().post(() -> clearAlarm.onAlarm()); 1791 HandlerUtils.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS); 1792 } 1793 1794 private void addIpAddressAndWaitForIt(final String iface) throws Exception { 1795 final CountDownLatch latch = new CountDownLatch(1); 1796 1797 final String addr1 = "192.0.2.99"; 1798 final String addr2 = "192.0.2.3"; 1799 final int prefixLength = 26; 1800 1801 // Add two IPv4 addresses to the specified interface, and proceed when the NetworkObserver 1802 // has seen the second one. This ensures that every other NetworkObserver registered with 1803 // mNetworkObserverRegistry - in particular, IpClient's - has seen the addition of the first 1804 // address. 1805 final LinkAddress trigger = new LinkAddress(addr2 + "/" + prefixLength); 1806 NetworkObserver observer = new NetworkObserver() { 1807 @Override 1808 public void onInterfaceAddressUpdated(LinkAddress address, String ifName) { 1809 if (ifName.equals(iface) && address.isSameAddressAs(trigger)) { 1810 latch.countDown(); 1811 } 1812 } 1813 }; 1814 1815 mNetworkObserverRegistry.registerObserverForNonblockingCallback(observer); 1816 try { 1817 mNetd.interfaceAddAddress(iface, addr1, prefixLength); 1818 mNetd.interfaceAddAddress(iface, addr2, prefixLength); 1819 assertTrue("Trigger IP address " + addr2 + " not seen after " + TEST_TIMEOUT_MS + "ms", 1820 latch.await(TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS)); 1821 } finally { 1822 mNetworkObserverRegistry.unregisterObserver(observer); 1823 } 1824 1825 // Wait for IpClient to process the addition of the address. 1826 HandlerUtils.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS); 1827 } 1828 1829 private void doIPv4OnlyProvisioningAndExitWithLeftAddress() throws Exception { 1830 final long currentTime = System.currentTimeMillis(); 1831 performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S, 1832 true /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */, 1833 TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */); 1834 verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR)); 1835 assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU); 1836 1837 // Stop IpClient and expect a final LinkProperties callback with an empty LP. 1838 mIIpClient.stop(); 1839 verify(mCb, timeout(TEST_TIMEOUT_MS)).onLinkPropertiesChange(argThat( 1840 x -> x.getAddresses().size() == 0 1841 && x.getRoutes().size() == 0 1842 && x.getDnsServers().size() == 0)); 1843 reset(mCb); 1844 1845 // Pretend that something else (e.g., Tethering) used the interface and left an IP address 1846 // configured on it. When IpClient starts, it must clear this address before proceeding. 1847 // The address must be noticed before startProvisioning is called, or IpClient will 1848 // immediately declare provisioning success due to the presence of an IPv4 address. 1849 // The address must be IPv4 because IpClient clears IPv6 addresses on startup. 1850 // 1851 // TODO: once IpClient gets IP addresses directly from netlink instead of from netd, it 1852 // may be sufficient to call waitForIdle to see if IpClient has seen the address. 1853 addIpAddressAndWaitForIt(mIfaceName); 1854 } 1855 1856 @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") 1857 public void testIpClientClearingIpAddressState() throws Exception { 1858 doIPv4OnlyProvisioningAndExitWithLeftAddress(); 1859 1860 ProvisioningConfiguration config = new ProvisioningConfiguration.Builder() 1861 .withoutIpReachabilityMonitor() 1862 .build(); 1863 startIpClientProvisioning(config); 1864 1865 sendBasicRouterAdvertisement(true /*waitForRs*/); 1866 1867 // Check that the IPv4 addresses configured earlier are not in LinkProperties... 1868 ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class); 1869 verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(captor.capture()); 1870 assertFalse(captor.getValue().hasIpv4Address()); 1871 1872 // ... or configured on the interface. 1873 InterfaceConfigurationParcel cfg = mNetd.interfaceGetCfg(mIfaceName); 1874 assertEquals("0.0.0.0", cfg.ipv4Addr); 1875 } 1876 1877 @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") 1878 public void testIpClientClearingIpAddressState_enablePreconnection() throws Exception { 1879 doIPv4OnlyProvisioningAndExitWithLeftAddress(); 1880 1881 // Enter ClearingIpAddressesState to clear the remaining IPv4 addresses and transition to 1882 // PreconnectionState instead of RunningState. 1883 startIpClientProvisioning(false /* isDhcpLeaseCacheEnabled */, 1884 false /* shouldReplyRapidCommitAck */, true /* isDhcpPreConnectionEnabled */, 1885 false /* isDhcpIpConflictDetectEnabled */, false /* isIPv6OnlyPreferredEnabled */); 1886 assertDiscoverPacketOnPreconnectionStart(); 1887 1888 // Force to enter RunningState. 1889 mIpc.notifyPreconnectionComplete(false /* abort */); 1890 HandlerUtils.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS); 1891 } 1892 1893 @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") 1894 public void testDhcpClientPreconnection_success() throws Exception { 1895 doIpClientProvisioningWithPreconnectionTest(true /* shouldReplyRapidCommitAck */, 1896 false /* shouldAbortPreconnection */, false /* shouldFirePreconnectionTimeout */, 1897 false /* timeoutBeforePreconnectionComplete */); 1898 } 1899 1900 @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") 1901 public void testDhcpClientPreconnection_SuccessWithoutRapidCommit() throws Exception { 1902 doIpClientProvisioningWithPreconnectionTest(false /* shouldReplyRapidCommitAck */, 1903 false /* shouldAbortPreconnection */, false /* shouldFirePreconnectionTimeout */, 1904 false /* timeoutBeforePreconnectionComplete */); 1905 } 1906 1907 @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") 1908 public void testDhcpClientPreconnection_Abort() throws Exception { 1909 doIpClientProvisioningWithPreconnectionTest(true /* shouldReplyRapidCommitAck */, 1910 true /* shouldAbortPreconnection */, false /* shouldFirePreconnectionTimeout */, 1911 false /* timeoutBeforePreconnectionComplete */); 1912 } 1913 1914 @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") 1915 public void testDhcpClientPreconnection_AbortWithoutRapiCommit() throws Exception { 1916 doIpClientProvisioningWithPreconnectionTest(false /* shouldReplyRapidCommitAck */, 1917 true /* shouldAbortPreconnection */, false /* shouldFirePreconnectionTimeout */, 1918 false /* timeoutBeforePreconnectionComplete */); 1919 } 1920 1921 @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") 1922 public void testDhcpClientPreconnection_TimeoutBeforeAbort() throws Exception { 1923 doIpClientProvisioningWithPreconnectionTest(true /* shouldReplyRapidCommitAck */, 1924 true /* shouldAbortPreconnection */, true /* shouldFirePreconnectionTimeout */, 1925 true /* timeoutBeforePreconnectionComplete */); 1926 } 1927 1928 @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") 1929 public void testDhcpClientPreconnection_TimeoutBeforeAbortWithoutRapidCommit() 1930 throws Exception { 1931 doIpClientProvisioningWithPreconnectionTest(false /* shouldReplyRapidCommitAck */, 1932 true /* shouldAbortPreconnection */, true /* shouldFirePreconnectionTimeout */, 1933 true /* timeoutBeforePreconnectionComplete */); 1934 } 1935 1936 @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") 1937 public void testDhcpClientPreconnection_TimeoutafterAbort() throws Exception { 1938 doIpClientProvisioningWithPreconnectionTest(true /* shouldReplyRapidCommitAck */, 1939 true /* shouldAbortPreconnection */, true /* shouldFirePreconnectionTimeout */, 1940 false /* timeoutBeforePreconnectionComplete */); 1941 } 1942 1943 @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") 1944 public void testDhcpClientPreconnection_TimeoutAfterAbortWithoutRapidCommit() throws Exception { 1945 doIpClientProvisioningWithPreconnectionTest(false /* shouldReplyRapidCommitAck */, 1946 true /* shouldAbortPreconnection */, true /* shouldFirePreconnectionTimeout */, 1947 false /* timeoutBeforePreconnectionComplete */); 1948 } 1949 1950 @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") 1951 public void testDhcpClientPreconnection_TimeoutBeforeSuccess() throws Exception { 1952 doIpClientProvisioningWithPreconnectionTest(true /* shouldReplyRapidCommitAck */, 1953 false /* shouldAbortPreconnection */, true /* shouldFirePreconnectionTimeout */, 1954 true /* timeoutBeforePreconnectionComplete */); 1955 } 1956 1957 @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") 1958 public void testDhcpClientPreconnection_TimeoutBeforeSuccessWithoutRapidCommit() 1959 throws Exception { 1960 doIpClientProvisioningWithPreconnectionTest(false /* shouldReplyRapidCommitAck */, 1961 false /* shouldAbortPreconnection */, true /* shouldFirePreconnectionTimeout */, 1962 true /* timeoutBeforePreconnectionComplete */); 1963 } 1964 1965 @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") 1966 public void testDhcpClientPreconnection_TimeoutAfterSuccess() throws Exception { 1967 doIpClientProvisioningWithPreconnectionTest(true /* shouldReplyRapidCommitAck */, 1968 false /* shouldAbortPreconnection */, true /* shouldFirePreconnectionTimeout */, 1969 false /* timeoutBeforePreconnectionComplete */); 1970 } 1971 1972 @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") 1973 public void testDhcpClientPreconnection_TimeoutAfterSuccessWithoutRapidCommit() 1974 throws Exception { 1975 doIpClientProvisioningWithPreconnectionTest(false /* shouldReplyRapidCommitAck */, 1976 false /* shouldAbortPreconnection */, true /* shouldFirePreconnectionTimeout */, 1977 false /* timeoutBeforePreconnectionComplete */); 1978 } 1979 1980 @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") 1981 public void testDhcpClientPreconnection_WithoutLayer2InfoWhenStartingProv() throws Exception { 1982 // For FILS connection, current bssid (also l2key and cluster) is still null when 1983 // starting provisioning since the L2 link hasn't been established yet. Ensure that 1984 // IpClient won't crash even if initializing an Layer2Info class with null members. 1985 ProvisioningConfiguration.Builder prov = new ProvisioningConfiguration.Builder() 1986 .withoutIpReachabilityMonitor() 1987 .withoutIPv6() 1988 .withPreconnection() 1989 .withLayer2Information(new Layer2Information(null /* l2key */, null /* cluster */, 1990 null /* bssid */)); 1991 1992 startIpClientProvisioning(prov.build()); 1993 assertDiscoverPacketOnPreconnectionStart(); 1994 verify(mCb).setNeighborDiscoveryOffload(true); 1995 1996 // Force IpClient transition to RunningState from PreconnectionState. 1997 mIIpClient.notifyPreconnectionComplete(false /* success */); 1998 HandlerUtils.waitForIdle(mDependencies.mDhcpClient.getHandler(), TEST_TIMEOUT_MS); 1999 verify(mCb, timeout(TEST_TIMEOUT_MS)).setFallbackMulticastFilter(false); 2000 } 2001 2002 @Test 2003 public void testDhcpDecline_conflictByArpReply() throws Exception { 2004 doIpAddressConflictDetectionTest(true /* causeIpAddressConflict */, 2005 false /* shouldReplyRapidCommitAck */, true /* isDhcpIpConflictDetectEnabled */, 2006 true /* shouldResponseArpReply */); 2007 } 2008 2009 @Test 2010 public void testDhcpDecline_conflictByArpProbe() throws Exception { 2011 doIpAddressConflictDetectionTest(true /* causeIpAddressConflict */, 2012 false /* shouldReplyRapidCommitAck */, true /* isDhcpIpConflictDetectEnabled */, 2013 false /* shouldResponseArpReply */); 2014 } 2015 2016 @Test 2017 public void testDhcpDecline_EnableFlagWithoutIpConflict() throws Exception { 2018 doIpAddressConflictDetectionTest(false /* causeIpAddressConflict */, 2019 false /* shouldReplyRapidCommitAck */, true /* isDhcpIpConflictDetectEnabled */, 2020 false /* shouldResponseArpReply */); 2021 } 2022 2023 @Test 2024 public void testDhcpDecline_WithoutIpConflict() throws Exception { 2025 doIpAddressConflictDetectionTest(false /* causeIpAddressConflict */, 2026 false /* shouldReplyRapidCommitAck */, false /* isDhcpIpConflictDetectEnabled */, 2027 false /* shouldResponseArpReply */); 2028 } 2029 2030 @Test 2031 public void testDhcpDecline_WithRapidCommitWithoutIpConflict() throws Exception { 2032 doIpAddressConflictDetectionTest(false /* causeIpAddressConflict */, 2033 true /* shouldReplyRapidCommitAck */, false /* isDhcpIpConflictDetectEnabled */, 2034 false /* shouldResponseArpReply */); 2035 } 2036 2037 @Test 2038 public void testDhcpDecline_WithRapidCommitConflictByArpReply() throws Exception { 2039 doIpAddressConflictDetectionTest(true /* causeIpAddressConflict */, 2040 true /* shouldReplyRapidCommitAck */, true /* isDhcpIpConflictDetectEnabled */, 2041 true /* shouldResponseArpReply */); 2042 } 2043 2044 @Test 2045 public void testDhcpDecline_WithRapidCommitConflictByArpProbe() throws Exception { 2046 doIpAddressConflictDetectionTest(true /* causeIpAddressConflict */, 2047 true /* shouldReplyRapidCommitAck */, true /* isDhcpIpConflictDetectEnabled */, 2048 false /* shouldResponseArpReply */); 2049 } 2050 2051 @Test 2052 public void testDhcpDecline_EnableFlagWithRapidCommitWithoutIpConflict() throws Exception { 2053 doIpAddressConflictDetectionTest(false /* causeIpAddressConflict */, 2054 true /* shouldReplyRapidCommitAck */, true /* isDhcpIpConflictDetectEnabled */, 2055 false /* shouldResponseArpReply */); 2056 } 2057 2058 @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") 2059 public void testHostname_enableConfig() throws Exception { 2060 mDependencies.setHostnameConfiguration(true /* isHostnameConfigurationEnabled */, 2061 TEST_HOST_NAME); 2062 2063 final long currentTime = System.currentTimeMillis(); 2064 final List<DhcpPacket> sentPackets = performDhcpHandshake(true /* isSuccessLease */, 2065 TEST_LEASE_DURATION_S, true /* isDhcpLeaseCacheEnabled */, 2066 false /* isDhcpRapidCommitEnabled */, TEST_DEFAULT_MTU, 2067 false /* isDhcpIpConflictDetectEnabled */); 2068 2069 assertEquals(2, sentPackets.size()); 2070 verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR)); 2071 assertHostname(true, TEST_HOST_NAME, TEST_HOST_NAME_TRANSLITERATION, sentPackets); 2072 assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU); 2073 } 2074 2075 @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") 2076 public void testHostname_disableConfig() throws Exception { 2077 mDependencies.setHostnameConfiguration(false /* isHostnameConfigurationEnabled */, 2078 TEST_HOST_NAME); 2079 2080 final long currentTime = System.currentTimeMillis(); 2081 final List<DhcpPacket> sentPackets = performDhcpHandshake(true /* isSuccessLease */, 2082 TEST_LEASE_DURATION_S, true /* isDhcpLeaseCacheEnabled */, 2083 false /* isDhcpRapidCommitEnabled */, TEST_DEFAULT_MTU, 2084 false /* isDhcpIpConflictDetectEnabled */); 2085 2086 assertEquals(2, sentPackets.size()); 2087 verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR)); 2088 assertHostname(false, TEST_HOST_NAME, TEST_HOST_NAME_TRANSLITERATION, sentPackets); 2089 assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU); 2090 } 2091 2092 @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") 2093 public void testHostname_enableConfigWithNullHostname() throws Exception { 2094 mDependencies.setHostnameConfiguration(true /* isHostnameConfigurationEnabled */, 2095 null /* hostname */); 2096 2097 final long currentTime = System.currentTimeMillis(); 2098 final List<DhcpPacket> sentPackets = performDhcpHandshake(true /* isSuccessLease */, 2099 TEST_LEASE_DURATION_S, true /* isDhcpLeaseCacheEnabled */, 2100 false /* isDhcpRapidCommitEnabled */, TEST_DEFAULT_MTU, 2101 false /* isDhcpIpConflictDetectEnabled */); 2102 2103 assertEquals(2, sentPackets.size()); 2104 verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR)); 2105 assertHostname(true, null /* hostname */, null /* hostnameAfterTransliteration */, 2106 sentPackets); 2107 assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU); 2108 } 2109 2110 private void runDhcpClientCaptivePortalApiTest(boolean featureEnabled, 2111 boolean serverSendsOption) throws Exception { 2112 startIpClientProvisioning(false /* isDhcpLeaseCacheEnabled */, 2113 false /* shouldReplyRapidCommitAck */, false /* isPreConnectionEnabled */, 2114 false /* isDhcpIpConflictDetectEnabled */, false /* isIPv6OnlyPreferredEnabled */); 2115 final DhcpPacket discover = getNextDhcpPacket(); 2116 assertTrue(discover instanceof DhcpDiscoverPacket); 2117 assertEquals(featureEnabled, discover.hasRequestedParam(DhcpPacket.DHCP_CAPTIVE_PORTAL)); 2118 2119 // Send Offer and handle Request -> Ack 2120 final String serverSentUrl = serverSendsOption ? TEST_CAPTIVE_PORTAL_URL : null; 2121 mPacketReader.sendResponse(buildDhcpOfferPacket(discover, CLIENT_ADDR, 2122 TEST_LEASE_DURATION_S, (short) TEST_DEFAULT_MTU, serverSentUrl)); 2123 final int testMtu = 1345; 2124 handleDhcpPackets(true /* isSuccessLease */, TEST_LEASE_DURATION_S, 2125 false /* shouldReplyRapidCommitAck */, testMtu, serverSentUrl); 2126 2127 final Uri expectedUrl = featureEnabled && serverSendsOption 2128 ? Uri.parse(TEST_CAPTIVE_PORTAL_URL) : null; 2129 // LinkProperties will be updated multiple times. Wait for it to contain DHCP-obtained info, 2130 // such as MTU. 2131 final ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class); 2132 verify(mCb, timeout(TEST_TIMEOUT_MS).atLeastOnce()).onLinkPropertiesChange( 2133 argThat(lp -> lp.getMtu() == testMtu)); 2134 2135 // Ensure that the URL was set as expected in the callbacks. 2136 // Can't verify the URL up to Q as there is no such attribute in LinkProperties. 2137 if (!ShimUtils.isAtLeastR()) return; 2138 verify(mCb, atLeastOnce()).onLinkPropertiesChange(captor.capture()); 2139 assertTrue(captor.getAllValues().stream().anyMatch( 2140 lp -> Objects.equals(expectedUrl, lp.getCaptivePortalApiUrl()))); 2141 } 2142 2143 @Test 2144 public void testDhcpClientCaptivePortalApiEnabled() throws Exception { 2145 // Only run the test on platforms / builds where the API is enabled 2146 assumeTrue(CaptivePortalDataShimImpl.isSupported()); 2147 runDhcpClientCaptivePortalApiTest(true /* featureEnabled */, true /* serverSendsOption */); 2148 } 2149 2150 @Test 2151 public void testDhcpClientCaptivePortalApiEnabled_NoUrl() throws Exception { 2152 // Only run the test on platforms / builds where the API is enabled 2153 assumeTrue(CaptivePortalDataShimImpl.isSupported()); 2154 runDhcpClientCaptivePortalApiTest(true /* featureEnabled */, false /* serverSendsOption */); 2155 } 2156 2157 @Test 2158 public void testDhcpClientCaptivePortalApiDisabled() throws Exception { 2159 // Only run the test on platforms / builds where the API is disabled 2160 assumeFalse(CaptivePortalDataShimImpl.isSupported()); 2161 runDhcpClientCaptivePortalApiTest(false /* featureEnabled */, true /* serverSendsOption */); 2162 } 2163 2164 private ScanResultInfo makeScanResultInfo(final int id, final String ssid, 2165 final String bssid, final byte[] oui, final byte type, final byte[] data) { 2166 final ByteBuffer payload = ByteBuffer.allocate(4 + data.length); 2167 payload.put(oui); 2168 payload.put(type); 2169 payload.put(data); 2170 payload.flip(); 2171 final ScanResultInfo.InformationElement ie = 2172 new ScanResultInfo.InformationElement(id /* IE id */, payload); 2173 return new ScanResultInfo(ssid, bssid, Collections.singletonList(ie)); 2174 } 2175 2176 private ScanResultInfo makeScanResultInfo(final int id, final byte[] oui, final byte type) { 2177 byte[] data = new byte[10]; 2178 new Random().nextBytes(data); 2179 return makeScanResultInfo(id, TEST_DEFAULT_SSID, TEST_DEFAULT_BSSID, oui, type, data); 2180 } 2181 2182 private ScanResultInfo makeScanResultInfo(final String ssid, final String bssid) { 2183 byte[] data = new byte[10]; 2184 new Random().nextBytes(data); 2185 return makeScanResultInfo(0xdd, ssid, bssid, TEST_AP_OUI, (byte) 0x06, data); 2186 } 2187 2188 private void doUpstreamHotspotDetectionTest(final int id, final String displayName, 2189 final String ssid, final byte[] oui, final byte type, final byte[] data, 2190 final boolean expectMetered) throws Exception { 2191 final ScanResultInfo info = makeScanResultInfo(id, ssid, TEST_DEFAULT_BSSID, oui, type, 2192 data); 2193 final long currentTime = System.currentTimeMillis(); 2194 final List<DhcpPacket> sentPackets = performDhcpHandshake(true /* isSuccessLease */, 2195 TEST_LEASE_DURATION_S, true /* isDhcpLeaseCacheEnabled */, 2196 false /* isDhcpRapidCommitEnabled */, TEST_DEFAULT_MTU, 2197 false /* isDhcpIpConflictDetectEnabled */, 2198 false /* isIPv6OnlyPreferredEnabled */, 2199 null /* captivePortalApiUrl */, displayName, info /* scanResultInfo */, 2200 null /* layer2Info */); 2201 assertEquals(2, sentPackets.size()); 2202 verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR)); 2203 2204 ArgumentCaptor<DhcpResultsParcelable> captor = 2205 ArgumentCaptor.forClass(DhcpResultsParcelable.class); 2206 verify(mCb, timeout(TEST_TIMEOUT_MS)).onNewDhcpResults(captor.capture()); 2207 DhcpResults lease = fromStableParcelable(captor.getValue()); 2208 assertNotNull(lease); 2209 assertEquals(lease.getIpAddress().getAddress(), CLIENT_ADDR); 2210 assertEquals(lease.getGateway(), SERVER_ADDR); 2211 assertEquals(1, lease.getDnsServers().size()); 2212 assertTrue(lease.getDnsServers().contains(SERVER_ADDR)); 2213 assertEquals(lease.getServerAddress(), SERVER_ADDR); 2214 assertEquals(lease.getMtu(), TEST_DEFAULT_MTU); 2215 2216 if (expectMetered) { 2217 assertEquals(lease.vendorInfo, DhcpPacket.VENDOR_INFO_ANDROID_METERED); 2218 } else { 2219 assertNull(lease.vendorInfo); 2220 } 2221 2222 assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU); 2223 } 2224 2225 @Test 2226 public void testUpstreamHotspotDetection() throws Exception { 2227 byte[] data = new byte[10]; 2228 new Random().nextBytes(data); 2229 doUpstreamHotspotDetectionTest(0xdd, "\"ssid\"", "ssid", 2230 new byte[] { (byte) 0x00, (byte) 0x17, (byte) 0xF2 }, (byte) 0x06, data, 2231 true /* expectMetered */); 2232 } 2233 2234 @Test 2235 public void testUpstreamHotspotDetection_incorrectIeId() throws Exception { 2236 byte[] data = new byte[10]; 2237 new Random().nextBytes(data); 2238 doUpstreamHotspotDetectionTest(0xdc, "\"ssid\"", "ssid", 2239 new byte[] { (byte) 0x00, (byte) 0x17, (byte) 0xF2 }, (byte) 0x06, data, 2240 false /* expectMetered */); 2241 } 2242 2243 @Test 2244 public void testUpstreamHotspotDetection_incorrectOUI() throws Exception { 2245 byte[] data = new byte[10]; 2246 new Random().nextBytes(data); 2247 doUpstreamHotspotDetectionTest(0xdd, "\"ssid\"", "ssid", 2248 new byte[] { (byte) 0x00, (byte) 0x1A, (byte) 0x11 }, (byte) 0x06, data, 2249 false /* expectMetered */); 2250 } 2251 2252 @Test 2253 public void testUpstreamHotspotDetection_incorrectSsid() throws Exception { 2254 byte[] data = new byte[10]; 2255 new Random().nextBytes(data); 2256 doUpstreamHotspotDetectionTest(0xdd, "\"another ssid\"", "ssid", 2257 new byte[] { (byte) 0x00, (byte) 0x17, (byte) 0xF2 }, (byte) 0x06, data, 2258 false /* expectMetered */); 2259 } 2260 2261 @Test 2262 public void testUpstreamHotspotDetection_incorrectType() throws Exception { 2263 byte[] data = new byte[10]; 2264 new Random().nextBytes(data); 2265 doUpstreamHotspotDetectionTest(0xdd, "\"ssid\"", "ssid", 2266 new byte[] { (byte) 0x00, (byte) 0x17, (byte) 0xF2 }, (byte) 0x0a, data, 2267 false /* expectMetered */); 2268 } 2269 2270 @Test 2271 public void testUpstreamHotspotDetection_zeroLengthData() throws Exception { 2272 byte[] data = new byte[0]; 2273 doUpstreamHotspotDetectionTest(0xdd, "\"ssid\"", "ssid", 2274 new byte[] { (byte) 0x00, (byte) 0x17, (byte) 0xF2 }, (byte) 0x06, data, 2275 true /* expectMetered */); 2276 } 2277 2278 private void forceLayer2Roaming() throws Exception { 2279 final Layer2InformationParcelable roamingInfo = new Layer2InformationParcelable(); 2280 roamingInfo.bssid = MacAddress.fromString(TEST_DHCP_ROAM_BSSID); 2281 roamingInfo.l2Key = TEST_DHCP_ROAM_L2KEY; 2282 roamingInfo.cluster = TEST_DHCP_ROAM_CLUSTER; 2283 mIIpClient.updateLayer2Information(roamingInfo); 2284 } 2285 2286 private void doDhcpRoamingTest(final boolean hasMismatchedIpAddress, final String displayName, 2287 final MacAddress bssid, final boolean expectRoaming) throws Exception { 2288 long currentTime = System.currentTimeMillis(); 2289 final Layer2Information layer2Info = new Layer2Information(TEST_L2KEY, TEST_CLUSTER, bssid); 2290 2291 doAnswer(invocation -> { 2292 // we don't rely on the Init-Reboot state to renew previous cached IP lease. 2293 // Just return null and force state machine enter INIT state. 2294 final String l2Key = invocation.getArgument(0); 2295 ((OnNetworkAttributesRetrievedListener) invocation.getArgument(1)) 2296 .onNetworkAttributesRetrieved(new Status(SUCCESS), l2Key, null); 2297 return null; 2298 }).when(mIpMemoryStore).retrieveNetworkAttributes(eq(TEST_L2KEY), any()); 2299 2300 mDependencies.setHostnameConfiguration(true /* isHostnameConfigurationEnabled */, 2301 null /* hostname */); 2302 performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S, 2303 true /* isDhcpLeaseCacheEnabled */, false /* isDhcpRapidCommitEnabled */, 2304 TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */, 2305 false /* isIPv6OnlyPreferredEnabled */, 2306 null /* captivePortalApiUrl */, displayName, null /* scanResultInfo */, 2307 layer2Info); 2308 verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR)); 2309 assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU); 2310 2311 // simulate the roaming by updating bssid. 2312 forceLayer2Roaming(); 2313 2314 currentTime = System.currentTimeMillis(); 2315 reset(mIpMemoryStore); 2316 reset(mCb); 2317 if (!expectRoaming) { 2318 assertIpMemoryNeverStoreNetworkAttributes(); 2319 return; 2320 } 2321 // check DHCPREQUEST broadcast sent to renew IP address. 2322 DhcpPacket packet; 2323 packet = getNextDhcpPacket(); 2324 assertTrue(packet instanceof DhcpRequestPacket); 2325 assertEquals(packet.mClientIp, CLIENT_ADDR); // client IP 2326 assertNull(packet.mRequestedIp); // requested IP option 2327 assertNull(packet.mServerIdentifier); // server ID 2328 2329 mPacketReader.sendResponse(buildDhcpAckPacket(packet, 2330 hasMismatchedIpAddress ? CLIENT_ADDR_NEW : CLIENT_ADDR, TEST_LEASE_DURATION_S, 2331 (short) TEST_DEFAULT_MTU, false /* rapidcommit */, null /* captivePortalUrl */)); 2332 HandlerUtils.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS); 2333 if (hasMismatchedIpAddress) { 2334 // notifyFailure 2335 ArgumentCaptor<DhcpResultsParcelable> captor = 2336 ArgumentCaptor.forClass(DhcpResultsParcelable.class); 2337 verify(mCb, timeout(TEST_TIMEOUT_MS)).onNewDhcpResults(captor.capture()); 2338 DhcpResults lease = fromStableParcelable(captor.getValue()); 2339 assertNull(lease); 2340 2341 // roll back to INIT state. 2342 packet = getNextDhcpPacket(); 2343 assertTrue(packet instanceof DhcpDiscoverPacket); 2344 } else { 2345 assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, 2346 TEST_DEFAULT_MTU); 2347 } 2348 } 2349 2350 @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") 2351 public void testDhcpRoaming() throws Exception { 2352 doDhcpRoamingTest(false /* hasMismatchedIpAddress */, "\"0001docomo\"" /* display name */, 2353 MacAddress.fromString(TEST_DEFAULT_BSSID), true /* expectRoaming */); 2354 } 2355 2356 @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") 2357 public void testDhcpRoaming_invalidBssid() throws Exception { 2358 doDhcpRoamingTest(false /* hasMismatchedIpAddress */, "\"0001docomo\"" /* display name */, 2359 MacAddress.fromString(TEST_DHCP_ROAM_BSSID), false /* expectRoaming */); 2360 } 2361 2362 @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") 2363 public void testDhcpRoaming_nullBssid() throws Exception { 2364 doDhcpRoamingTest(false /* hasMismatchedIpAddress */, "\"0001docomo\"" /* display name */, 2365 null /* BSSID */, false /* expectRoaming */); 2366 } 2367 2368 @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") 2369 public void testDhcpRoaming_invalidDisplayName() throws Exception { 2370 doDhcpRoamingTest(false /* hasMismatchedIpAddress */, "\"test-ssid\"" /* display name */, 2371 MacAddress.fromString(TEST_DEFAULT_BSSID), false /* expectRoaming */); 2372 } 2373 2374 @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") 2375 public void testDhcpRoaming_mismatchedLeasedIpAddress() throws Exception { 2376 doDhcpRoamingTest(true /* hasMismatchedIpAddress */, "\"0001docomo\"" /* display name */, 2377 MacAddress.fromString(TEST_DEFAULT_BSSID), true /* expectRoaming */); 2378 } 2379 2380 private void performDualStackProvisioning() throws Exception { 2381 final InOrder inOrder = inOrder(mCb); 2382 final CompletableFuture<LinkProperties> lpFuture = new CompletableFuture<>(); 2383 final String dnsServer = "2001:4860:4860::64"; 2384 final ByteBuffer pio = buildPioOption(3600, 1800, "2001:db8:1::/64"); 2385 final ByteBuffer rdnss = buildRdnssOption(3600, dnsServer); 2386 final ByteBuffer ra = buildRaPacket(pio, rdnss); 2387 2388 doIpv6OnlyProvisioning(inOrder, ra); 2389 2390 // Start IPv4 provisioning and wait until entire provisioning completes. 2391 handleDhcpPackets(true /* isSuccessLease */, TEST_LEASE_DURATION_S, 2392 true /* shouldReplyRapidCommitAck */, TEST_DEFAULT_MTU, null /* serverSentUrl */); 2393 verify(mCb, timeout(TEST_TIMEOUT_MS).atLeastOnce()).onLinkPropertiesChange(argThat(x -> { 2394 if (!x.isIpv4Provisioned() || !x.isIpv6Provisioned()) return false; 2395 lpFuture.complete(x); 2396 return true; 2397 })); 2398 2399 final LinkProperties lp = lpFuture.get(TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS); 2400 assertNotNull(lp); 2401 assertTrue(lp.getDnsServers().contains(InetAddress.getByName(dnsServer))); 2402 assertTrue(lp.getDnsServers().contains(SERVER_ADDR)); 2403 2404 reset(mCb); 2405 } 2406 2407 private void doDualStackProvisioning(boolean shouldDisableAcceptRa) throws Exception { 2408 when(mCm.shouldAvoidBadWifi()).thenReturn(true); 2409 2410 final ProvisioningConfiguration config = new ProvisioningConfiguration.Builder() 2411 .withoutIpReachabilityMonitor() 2412 .build(); 2413 2414 setFeatureEnabled(NetworkStackUtils.IPCLIENT_DISABLE_ACCEPT_RA_VERSION, 2415 shouldDisableAcceptRa); 2416 // Enable rapid commit to accelerate DHCP handshake to shorten test duration, 2417 // not strictly necessary. 2418 setDhcpFeatures(false /* isDhcpLeaseCacheEnabled */, true /* isRapidCommitEnabled */, 2419 false /* isDhcpIpConflictDetectEnabled */, false /* isIPv6OnlyPreferredEnabled */); 2420 mIpc.startProvisioning(config); 2421 2422 performDualStackProvisioning(); 2423 } 2424 2425 @Test @SignatureRequiredTest(reason = "signature perms are required due to mocked callabck") 2426 public void testIgnoreIpv6ProvisioningLoss_disableIPv6Stack() throws Exception { 2427 doDualStackProvisioning(false /* shouldDisableAcceptRa */); 2428 2429 final CompletableFuture<LinkProperties> lpFuture = new CompletableFuture<>(); 2430 2431 // Send RA with 0-lifetime and wait until all IPv6-related default route and DNS servers 2432 // have been removed, then verify if there is IPv4-only info left in the LinkProperties. 2433 sendRouterAdvertisementWithZeroLifetime(); 2434 verify(mCb, timeout(TEST_TIMEOUT_MS).atLeastOnce()).onLinkPropertiesChange( 2435 argThat(x -> { 2436 final boolean isOnlyIPv4Provisioned = (x.getLinkAddresses().size() == 1 2437 && x.getDnsServers().size() == 1 2438 && x.getAddresses().get(0) instanceof Inet4Address 2439 && x.getDnsServers().get(0) instanceof Inet4Address); 2440 2441 if (!isOnlyIPv4Provisioned) return false; 2442 lpFuture.complete(x); 2443 return true; 2444 })); 2445 final LinkProperties lp = lpFuture.get(TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS); 2446 assertNotNull(lp); 2447 assertEquals(lp.getAddresses().get(0), CLIENT_ADDR); 2448 assertEquals(lp.getDnsServers().get(0), SERVER_ADDR); 2449 2450 final ArgumentCaptor<Integer> quirkEvent = ArgumentCaptor.forClass(Integer.class); 2451 verify(mNetworkQuirkMetricsDeps, timeout(TEST_TIMEOUT_MS)).writeStats(quirkEvent.capture()); 2452 assertEquals((long) quirkEvent.getValue(), 2453 (long) NetworkQuirkEvent.QE_IPV6_PROVISIONING_ROUTER_LOST.ordinal()); 2454 } 2455 2456 @Test @SignatureRequiredTest(reason = "signature perms are required due to mocked callabck") 2457 public void testIgnoreIpv6ProvisioningLoss_disableAcceptRa() throws Exception { 2458 doDualStackProvisioning(true /* shouldDisableAcceptRa */); 2459 2460 final CompletableFuture<LinkProperties> lpFuture = new CompletableFuture<>(); 2461 2462 // Send RA with 0-lifetime and wait until all global IPv6 addresses, IPv6-related default 2463 // route and DNS servers have been removed, then verify if there is IPv4-only, IPv6 link 2464 // local address and route to fe80::/64 info left in the LinkProperties. 2465 sendRouterAdvertisementWithZeroLifetime(); 2466 verify(mCb, timeout(TEST_TIMEOUT_MS).atLeastOnce()).onLinkPropertiesChange( 2467 argThat(x -> { 2468 // Only IPv4 provisioned and IPv6 link-local address 2469 final boolean isIPv6LinkLocalAndIPv4OnlyProvisioned = 2470 (x.getLinkAddresses().size() == 2 2471 && x.getDnsServers().size() == 1 2472 && x.getAddresses().get(0) instanceof Inet4Address 2473 && x.getDnsServers().get(0) instanceof Inet4Address); 2474 2475 if (!isIPv6LinkLocalAndIPv4OnlyProvisioned) return false; 2476 lpFuture.complete(x); 2477 return true; 2478 })); 2479 final LinkProperties lp = lpFuture.get(TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS); 2480 assertNotNull(lp); 2481 assertEquals(lp.getAddresses().get(0), CLIENT_ADDR); 2482 assertEquals(lp.getDnsServers().get(0), SERVER_ADDR); 2483 assertTrue(lp.getAddresses().get(1).isLinkLocalAddress()); 2484 2485 reset(mCb); 2486 2487 // Send an RA to verify that global IPv6 addresses won't be configured on the interface. 2488 sendBasicRouterAdvertisement(false /* waitForRs */); 2489 verify(mCb, timeout(TEST_TIMEOUT_MS).times(0)).onLinkPropertiesChange(any()); 2490 } 2491 2492 @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") 2493 public void testDualStackProvisioning() throws Exception { 2494 doDualStackProvisioning(false /* shouldDisableAcceptRa */); 2495 2496 verify(mCb, never()).onProvisioningFailure(any()); 2497 } 2498 2499 private DhcpPacket verifyDhcpPacketRequestsIPv6OnlyPreferredOption( 2500 Class<? extends DhcpPacket> packetType) throws Exception { 2501 final DhcpPacket packet = getNextDhcpPacket(); 2502 assertTrue(packetType.isInstance(packet)); 2503 assertTrue(packet.hasRequestedParam(DHCP_IPV6_ONLY_PREFERRED)); 2504 return packet; 2505 } 2506 2507 private void doIPv6OnlyPreferredOptionTest(final Integer ipv6OnlyWaitTime, 2508 final Inet4Address clientAddress) throws Exception { 2509 final ProvisioningConfiguration config = new ProvisioningConfiguration.Builder() 2510 .withoutIpReachabilityMonitor() 2511 .build(); 2512 setDhcpFeatures(false /* isDhcpLeaseCacheEnabled */, false /* isRapidCommitEnabled */, 2513 false /* isDhcpIpConflictDetectEnabled */, true /* isIPv6OnlyPreferredEnabled */); 2514 startIpClientProvisioning(config); 2515 2516 final DhcpPacket packet = 2517 verifyDhcpPacketRequestsIPv6OnlyPreferredOption(DhcpDiscoverPacket.class); 2518 2519 // Respond DHCPOFFER with IPv6-Only preferred option and offered address. 2520 mPacketReader.sendResponse(buildDhcpOfferPacket(packet, clientAddress, 2521 TEST_LEASE_DURATION_S, (short) TEST_DEFAULT_MTU, null /* captivePortalUrl */, 2522 ipv6OnlyWaitTime)); 2523 } 2524 2525 private void doDiscoverIPv6OnlyPreferredOptionTest(final int optionSecs, 2526 final long expectedWaitSecs) throws Exception { 2527 doIPv6OnlyPreferredOptionTest(optionSecs, CLIENT_ADDR); 2528 final OnAlarmListener alarm = expectAlarmSet(null /* inOrder */, "TIMEOUT", 2529 expectedWaitSecs, mDependencies.mDhcpClient.getHandler()); 2530 mDependencies.mDhcpClient.getHandler().post(() -> alarm.onAlarm()); 2531 // Implicitly check that the client never sent a DHCPREQUEST to request the offered address. 2532 verifyDhcpPacketRequestsIPv6OnlyPreferredOption(DhcpDiscoverPacket.class); 2533 } 2534 2535 @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") 2536 public void testDiscoverIPv6OnlyPreferredOption() throws Exception { 2537 doDiscoverIPv6OnlyPreferredOptionTest(TEST_IPV6_ONLY_WAIT_S, TEST_IPV6_ONLY_WAIT_S); 2538 } 2539 2540 @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") 2541 public void testDiscoverIPv6OnlyPreferredOption_LowerIPv6OnlyWait() throws Exception { 2542 doDiscoverIPv6OnlyPreferredOptionTest(TEST_LOWER_IPV6_ONLY_WAIT_S, 2543 TEST_LOWER_IPV6_ONLY_WAIT_S); 2544 } 2545 2546 @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") 2547 public void testDiscoverIPv6OnlyPreferredOption_ZeroIPv6OnlyWait() throws Exception { 2548 doDiscoverIPv6OnlyPreferredOptionTest(TEST_ZERO_IPV6_ONLY_WAIT_S, 2549 TEST_LOWER_IPV6_ONLY_WAIT_S); 2550 } 2551 2552 @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") 2553 public void testDiscoverIPv6OnlyPreferredOption_MaxIPv6OnlyWait() throws Exception { 2554 doDiscoverIPv6OnlyPreferredOptionTest((int) TEST_MAX_IPV6_ONLY_WAIT_S, 0xffffffffL); 2555 } 2556 2557 @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") 2558 public void testDiscoverIPv6OnlyPreferredOption_ZeroIPv6OnlyWaitWithOfferedAnyAddress() 2559 throws Exception { 2560 doIPv6OnlyPreferredOptionTest(TEST_ZERO_IPV6_ONLY_WAIT_S, IPV4_ADDR_ANY); 2561 2562 final OnAlarmListener alarm = expectAlarmSet(null /* inOrder */, "TIMEOUT", 300, 2563 mDependencies.mDhcpClient.getHandler()); 2564 mDependencies.mDhcpClient.getHandler().post(() -> alarm.onAlarm()); 2565 2566 verifyDhcpPacketRequestsIPv6OnlyPreferredOption(DhcpDiscoverPacket.class); 2567 } 2568 2569 @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") 2570 public void testDiscoverIPv6OnlyPreferredOption_enabledPreconnection() throws Exception { 2571 final ProvisioningConfiguration config = new ProvisioningConfiguration.Builder() 2572 .withoutIpReachabilityMonitor() 2573 .withPreconnection() 2574 .build(); 2575 2576 setDhcpFeatures(false /* isDhcpLeaseCacheEnabled */, true /* isRapidCommitEnabled */, 2577 false /* isDhcpIpConflictDetectEnabled */, true /* isIPv6OnlyPreferredEnabled */); 2578 startIpClientProvisioning(config); 2579 2580 final DhcpPacket packet = assertDiscoverPacketOnPreconnectionStart(); 2581 verify(mCb).setNeighborDiscoveryOffload(true); 2582 2583 // Force IpClient transition to RunningState from PreconnectionState. 2584 mIpc.notifyPreconnectionComplete(true /* success */); 2585 HandlerUtils.waitForIdle(mDependencies.mDhcpClient.getHandler(), TEST_TIMEOUT_MS); 2586 verify(mCb, timeout(TEST_TIMEOUT_MS)).setFallbackMulticastFilter(false); 2587 2588 // DHCP server SHOULD NOT honor the Rapid-Commit option if the response would 2589 // contain the IPv6-only Preferred option to the client, instead respond with 2590 // a DHCPOFFER. 2591 mPacketReader.sendResponse(buildDhcpOfferPacket(packet, CLIENT_ADDR, TEST_LEASE_DURATION_S, 2592 (short) TEST_DEFAULT_MTU, null /* captivePortalUrl */, TEST_IPV6_ONLY_WAIT_S)); 2593 2594 final OnAlarmListener alarm = expectAlarmSet(null /* inOrder */, "TIMEOUT", 1800, 2595 mDependencies.mDhcpClient.getHandler()); 2596 mDependencies.mDhcpClient.getHandler().post(() -> alarm.onAlarm()); 2597 2598 verifyDhcpPacketRequestsIPv6OnlyPreferredOption(DhcpDiscoverPacket.class); 2599 } 2600 2601 @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") 2602 public void testDiscoverIPv6OnlyPreferredOption_NoIPv6OnlyPreferredOption() throws Exception { 2603 doIPv6OnlyPreferredOptionTest(null /* ipv6OnlyWaitTime */, CLIENT_ADDR); 2604 2605 // The IPv6-only Preferred option SHOULD be included in the Parameter Request List option 2606 // in DHCPREQUEST messages after receiving a DHCPOFFER without this option. 2607 verifyDhcpPacketRequestsIPv6OnlyPreferredOption(DhcpRequestPacket.class); 2608 } 2609 2610 private void startFromInitRebootStateWithIPv6OnlyPreferredOption(final Integer ipv6OnlyWaitTime, 2611 final long expectedWaitSecs) throws Exception { 2612 doAnswer(invocation -> { 2613 ((OnNetworkAttributesRetrievedListener) invocation.getArgument(1)) 2614 .onNetworkAttributesRetrieved(new Status(SUCCESS), TEST_L2KEY, 2615 new NetworkAttributes.Builder() 2616 .setAssignedV4Address(CLIENT_ADDR) 2617 .setAssignedV4AddressExpiry(Long.MAX_VALUE) // lease is always valid 2618 .setMtu(new Integer(TEST_DEFAULT_MTU)) 2619 .setCluster(TEST_CLUSTER) 2620 .setDnsAddresses(Collections.singletonList(SERVER_ADDR)) 2621 .build()); 2622 return null; 2623 }).when(mIpMemoryStore).retrieveNetworkAttributes(eq(TEST_L2KEY), any()); 2624 2625 final ProvisioningConfiguration config = new ProvisioningConfiguration.Builder() 2626 .withoutIpReachabilityMonitor() 2627 .withLayer2Information(new Layer2Information(TEST_L2KEY, TEST_CLUSTER, 2628 MacAddress.fromString(TEST_DEFAULT_BSSID))) 2629 .build(); 2630 2631 setDhcpFeatures(true /* isDhcpLeaseCacheEnabled */, false /* isRapidCommitEnabled */, 2632 false /* isDhcpIpConflictDetectEnabled */, true /* isIPv6OnlyPreferredEnabled */); 2633 startIpClientProvisioning(config); 2634 2635 final DhcpPacket packet = 2636 verifyDhcpPacketRequestsIPv6OnlyPreferredOption(DhcpRequestPacket.class); 2637 2638 // Respond DHCPACK with IPv6-Only preferred option. 2639 mPacketReader.sendResponse(buildDhcpAckPacket(packet, CLIENT_ADDR, 2640 TEST_LEASE_DURATION_S, (short) TEST_DEFAULT_MTU, false /* rapidcommit */, 2641 null /* captivePortalUrl */, ipv6OnlyWaitTime)); 2642 2643 if (ipv6OnlyWaitTime != null) { 2644 expectAlarmSet(null /* inOrder */, "TIMEOUT", expectedWaitSecs, 2645 mDependencies.mDhcpClient.getHandler()); 2646 } 2647 } 2648 2649 @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") 2650 public void testRequestIPv6OnlyPreferredOption() throws Exception { 2651 startFromInitRebootStateWithIPv6OnlyPreferredOption(TEST_IPV6_ONLY_WAIT_S, 2652 TEST_IPV6_ONLY_WAIT_S); 2653 2654 // Client transits to IPv6OnlyPreferredState from INIT-REBOOT state when receiving valid 2655 // IPv6-Only preferred option(default value) in the DHCPACK packet. 2656 assertIpMemoryNeverStoreNetworkAttributes(); 2657 } 2658 2659 @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") 2660 public void testRequestIPv6OnlyPreferredOption_LowerIPv6OnlyWait() throws Exception { 2661 startFromInitRebootStateWithIPv6OnlyPreferredOption(TEST_LOWER_IPV6_ONLY_WAIT_S, 2662 TEST_LOWER_IPV6_ONLY_WAIT_S); 2663 2664 // Client transits to IPv6OnlyPreferredState from INIT-REBOOT state when receiving valid 2665 // IPv6-Only preferred option(less than MIN_V6ONLY_WAIT_MS) in the DHCPACK packet. 2666 assertIpMemoryNeverStoreNetworkAttributes(); 2667 } 2668 2669 @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") 2670 public void testRequestIPv6OnlyPreferredOption_ZeroIPv6OnlyWait() throws Exception { 2671 startFromInitRebootStateWithIPv6OnlyPreferredOption(TEST_ZERO_IPV6_ONLY_WAIT_S, 2672 TEST_LOWER_IPV6_ONLY_WAIT_S); 2673 2674 // Client transits to IPv6OnlyPreferredState from INIT-REBOOT state when receiving valid 2675 // IPv6-Only preferred option(0) in the DHCPACK packet. 2676 assertIpMemoryNeverStoreNetworkAttributes(); 2677 } 2678 2679 @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") 2680 public void testRequestIPv6OnlyPreferredOption_MaxIPv6OnlyWait() throws Exception { 2681 startFromInitRebootStateWithIPv6OnlyPreferredOption((int) TEST_MAX_IPV6_ONLY_WAIT_S, 2682 0xffffffffL); 2683 2684 // Client transits to IPv6OnlyPreferredState from INIT-REBOOT state when receiving valid 2685 // IPv6-Only preferred option(MAX_UNSIGNED_INTEGER: 0xFFFFFFFF) in the DHCPACK packet. 2686 assertIpMemoryNeverStoreNetworkAttributes(); 2687 } 2688 2689 @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") 2690 public void testRequestIPv6OnlyPreferredOption_NoIPv6OnlyPreferredOption() throws Exception { 2691 final long currentTime = System.currentTimeMillis(); 2692 startFromInitRebootStateWithIPv6OnlyPreferredOption(null /* ipv6OnlyWaitTime */, 2693 0 /* expectedWaitSecs */); 2694 2695 // Client processes DHCPACK packet normally and transits to the ConfiguringInterfaceState 2696 // due to the null V6ONLY_WAIT. 2697 assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU); 2698 } 2699 2700 private static int getNumOpenFds() { 2701 return new File("/proc/" + Os.getpid() + "/fd").listFiles().length; 2702 } 2703 2704 private void shutdownAndRecreateIpClient() throws Exception { 2705 mIpc.shutdown(); 2706 awaitIpClientShutdown(); 2707 mIpc = makeIpClient(); 2708 } 2709 2710 @Test @SignatureRequiredTest(reason = "Only counts FDs from the current process. TODO: fix") 2711 public void testNoFdLeaks() throws Exception { 2712 // Shut down and restart IpClient once to ensure that any fds that are opened the first 2713 // time it runs do not cause the test to fail. 2714 doDualStackProvisioning(false /* shouldDisableAcceptRa */); 2715 shutdownAndRecreateIpClient(); 2716 2717 // Unfortunately we cannot use a large number of iterations as it would make the test run 2718 // too slowly. On crosshatch-eng each iteration takes ~250ms. 2719 final int iterations = 10; 2720 final int before = getNumOpenFds(); 2721 for (int i = 0; i < iterations; i++) { 2722 doDualStackProvisioning(false /* shouldDisableAcceptRa */); 2723 shutdownAndRecreateIpClient(); 2724 // The last time this loop runs, mIpc will be shut down in tearDown. 2725 } 2726 final int after = getNumOpenFds(); 2727 2728 // Check that the number of open fds is the same as before. 2729 // If this exact match becomes flaky, we could add some tolerance here (e.g., allow 2-3 2730 // extra fds), since it's likely that any leak would at least leak one FD per loop. 2731 assertEquals("Fd leak after " + iterations + " iterations: ", before, after); 2732 } 2733 2734 // TODO: delete when DhcpOption is @JavaOnlyImmutable. 2735 private static DhcpOption makeDhcpOption(final byte type, final byte[] value) { 2736 final DhcpOption opt = new DhcpOption(); 2737 opt.type = type; 2738 opt.value = value; 2739 return opt; 2740 } 2741 2742 private static final List<DhcpOption> TEST_OEM_DHCP_OPTIONS = Arrays.asList( 2743 // DHCP_USER_CLASS 2744 makeDhcpOption((byte) 77, TEST_OEM_USER_CLASS_INFO), 2745 // DHCP_VENDOR_CLASS_ID 2746 makeDhcpOption((byte) 60, TEST_OEM_VENDOR_ID.getBytes()) 2747 ); 2748 2749 private DhcpPacket doCustomizedDhcpOptionsTest(final List<DhcpOption> options, 2750 final ScanResultInfo info) throws Exception { 2751 ProvisioningConfiguration.Builder prov = new ProvisioningConfiguration.Builder() 2752 .withoutIpReachabilityMonitor() 2753 .withLayer2Information(new Layer2Information(TEST_L2KEY, TEST_CLUSTER, 2754 MacAddress.fromString(TEST_DEFAULT_BSSID))) 2755 .withScanResultInfo(info) 2756 .withDhcpOptions(options) 2757 .withoutIPv6(); 2758 2759 setDhcpFeatures(false /* isDhcpLeaseCacheEnabled */, false /* isRapidCommitEnabled */, 2760 false /* isDhcpIpConflictDetectEnabled */, false /* isIPv6OnlyPreferredEnabled */); 2761 2762 startIpClientProvisioning(prov.build()); 2763 verify(mCb, timeout(TEST_TIMEOUT_MS)).setFallbackMulticastFilter(false); 2764 verify(mCb, never()).onProvisioningFailure(any()); 2765 2766 return getNextDhcpPacket(); 2767 } 2768 2769 @Test 2770 public void testCustomizedDhcpOptions() throws Exception { 2771 final ScanResultInfo info = makeScanResultInfo(0xdd /* vendor-specificIE */, TEST_OEM_OUI, 2772 (byte) 0x17 /* vendor-specific IE type */); 2773 final DhcpPacket packet = doCustomizedDhcpOptionsTest(TEST_OEM_DHCP_OPTIONS, info); 2774 2775 assertTrue(packet instanceof DhcpDiscoverPacket); 2776 assertEquals(packet.mVendorId, TEST_OEM_VENDOR_ID); 2777 assertArrayEquals(packet.mUserClass, TEST_OEM_USER_CLASS_INFO); 2778 } 2779 2780 @Test 2781 public void testCustomizedDhcpOptions_nullDhcpOptions() throws Exception { 2782 final ScanResultInfo info = makeScanResultInfo(0xdd /* vendor-specificIE */, TEST_OEM_OUI, 2783 (byte) 0x17 /* vendor-specific IE type */); 2784 final DhcpPacket packet = doCustomizedDhcpOptionsTest(null /* options */, info); 2785 2786 assertTrue(packet instanceof DhcpDiscoverPacket); 2787 assertEquals(packet.mVendorId, new String("android-dhcp-" + Build.VERSION.RELEASE)); 2788 assertNull(packet.mUserClass); 2789 } 2790 2791 @Test 2792 public void testCustomizedDhcpOptions_nullScanResultInfo() throws Exception { 2793 final DhcpPacket packet = doCustomizedDhcpOptionsTest(TEST_OEM_DHCP_OPTIONS, 2794 null /* scanResultInfo */); 2795 2796 assertTrue(packet instanceof DhcpDiscoverPacket); 2797 assertEquals(packet.mVendorId, new String("android-dhcp-" + Build.VERSION.RELEASE)); 2798 assertNull(packet.mUserClass); 2799 } 2800 2801 @Test 2802 public void testCustomizedDhcpOptions_disallowedOui() throws Exception { 2803 final ScanResultInfo info = makeScanResultInfo(0xdd /* vendor-specificIE */, 2804 new byte[]{ 0x00, 0x11, 0x22} /* oui */, (byte) 0x17 /* vendor-specific IE type */); 2805 final DhcpPacket packet = doCustomizedDhcpOptionsTest(TEST_OEM_DHCP_OPTIONS, info); 2806 2807 assertTrue(packet instanceof DhcpDiscoverPacket); 2808 assertEquals(packet.mVendorId, new String("android-dhcp-" + Build.VERSION.RELEASE)); 2809 assertNull(packet.mUserClass); 2810 } 2811 2812 @Test 2813 public void testCustomizedDhcpOptions_invalidIeId() throws Exception { 2814 final ScanResultInfo info = makeScanResultInfo(0xde /* vendor-specificIE */, TEST_OEM_OUI, 2815 (byte) 0x17 /* vendor-specific IE type */); 2816 final DhcpPacket packet = doCustomizedDhcpOptionsTest(TEST_OEM_DHCP_OPTIONS, info); 2817 2818 assertTrue(packet instanceof DhcpDiscoverPacket); 2819 assertEquals(packet.mVendorId, new String("android-dhcp-" + Build.VERSION.RELEASE)); 2820 assertNull(packet.mUserClass); 2821 } 2822 2823 @Test 2824 public void testCustomizedDhcpOptions_invalidVendorSpecificType() throws Exception { 2825 final ScanResultInfo info = makeScanResultInfo(0xdd /* vendor-specificIE */, TEST_OEM_OUI, 2826 (byte) 0x10 /* vendor-specific IE type */); 2827 final DhcpPacket packet = doCustomizedDhcpOptionsTest(TEST_OEM_DHCP_OPTIONS, info); 2828 2829 assertTrue(packet instanceof DhcpDiscoverPacket); 2830 assertEquals(packet.mVendorId, new String("android-dhcp-" + Build.VERSION.RELEASE)); 2831 assertNull(packet.mUserClass); 2832 } 2833 2834 @Test 2835 public void testCustomizedDhcpOptions_disallowedOption() throws Exception { 2836 final List<DhcpOption> options = Arrays.asList( 2837 makeDhcpOption((byte) 60, TEST_OEM_VENDOR_ID.getBytes()), 2838 makeDhcpOption((byte) 77, TEST_OEM_USER_CLASS_INFO), 2839 // Option 26: MTU 2840 makeDhcpOption((byte) 26, HexDump.toByteArray(TEST_DEFAULT_MTU))); 2841 final ScanResultInfo info = makeScanResultInfo(0xdd /* vendor-specificIE */, TEST_OEM_OUI, 2842 (byte) 0x17 /* vendor-specific IE type */); 2843 final DhcpPacket packet = doCustomizedDhcpOptionsTest(options, info); 2844 2845 assertTrue(packet instanceof DhcpDiscoverPacket); 2846 assertEquals(packet.mVendorId, TEST_OEM_VENDOR_ID); 2847 assertArrayEquals(packet.mUserClass, TEST_OEM_USER_CLASS_INFO); 2848 assertNull(packet.mMtu); 2849 } 2850 2851 @Test 2852 public void testCustomizedDhcpOptions_disallowedParamRequestOption() throws Exception { 2853 final List<DhcpOption> options = Arrays.asList( 2854 makeDhcpOption((byte) 60, TEST_OEM_VENDOR_ID.getBytes()), 2855 makeDhcpOption((byte) 77, TEST_OEM_USER_CLASS_INFO), 2856 // NTP_SERVER 2857 makeDhcpOption((byte) 42, null)); 2858 final ScanResultInfo info = makeScanResultInfo(0xdd /* vendor-specificIE */, TEST_OEM_OUI, 2859 (byte) 0x17 /* vendor-specific IE type */); 2860 final DhcpPacket packet = doCustomizedDhcpOptionsTest(options, info); 2861 2862 assertTrue(packet instanceof DhcpDiscoverPacket); 2863 assertEquals(packet.mVendorId, TEST_OEM_VENDOR_ID); 2864 assertArrayEquals(packet.mUserClass, TEST_OEM_USER_CLASS_INFO); 2865 assertFalse(packet.hasRequestedParam((byte) 42 /* NTP_SERVER */)); 2866 } 2867 2868 private void assertGratuitousNa(final NeighborAdvertisement na) throws Exception { 2869 final MacAddress etherMulticast = 2870 NetworkStackUtils.ipv6MulticastToEthernetMulticast(IPV6_ADDR_ALL_ROUTERS_MULTICAST); 2871 final LinkAddress target = new LinkAddress(na.naHdr.target, 64); 2872 2873 assertEquals(etherMulticast, na.ethHdr.dstMac); 2874 assertEquals(ETH_P_IPV6, na.ethHdr.etherType); 2875 assertEquals(IPPROTO_ICMPV6, na.ipv6Hdr.nextHeader); 2876 assertEquals(0xff, na.ipv6Hdr.hopLimit); 2877 assertTrue(na.ipv6Hdr.srcIp.isLinkLocalAddress()); 2878 assertEquals(IPV6_ADDR_ALL_ROUTERS_MULTICAST, na.ipv6Hdr.dstIp); 2879 assertEquals(ICMPV6_NEIGHBOR_ADVERTISEMENT, na.icmpv6Hdr.type); 2880 assertEquals(0, na.icmpv6Hdr.code); 2881 assertEquals(0, na.naHdr.flags); 2882 assertTrue(target.isGlobalPreferred()); 2883 } 2884 2885 @Test 2886 public void testGratuitousNaForNewGlobalUnicastAddresses() throws Exception { 2887 final ProvisioningConfiguration config = new ProvisioningConfiguration.Builder() 2888 .withoutIpReachabilityMonitor() 2889 .withoutIPv4() 2890 .build(); 2891 2892 setFeatureEnabled(NetworkStackUtils.IPCLIENT_GRATUITOUS_NA_VERSION, 2893 true /* isGratuitousNaEnabled */); 2894 assertTrue(isFeatureEnabled(NetworkStackUtils.IPCLIENT_GRATUITOUS_NA_VERSION, false)); 2895 startIpClientProvisioning(config); 2896 2897 doIpv6OnlyProvisioning(); 2898 2899 final List<NeighborAdvertisement> naList = new ArrayList<>(); 2900 NeighborAdvertisement packet; 2901 while ((packet = getNextNeighborAdvertisement()) != null) { 2902 assertGratuitousNa(packet); 2903 naList.add(packet); 2904 } 2905 assertEquals(2, naList.size()); // privacy address and stable privacy address 2906 } 2907 2908 private void startGratuitousArpAndNaAfterRoamingTest(boolean isGratuitousArpNaRoamingEnabled, 2909 boolean hasIpv4, boolean hasIpv6) throws Exception { 2910 final Layer2Information layer2Info = new Layer2Information(TEST_L2KEY, TEST_CLUSTER, 2911 MacAddress.fromString(TEST_DEFAULT_BSSID)); 2912 final ScanResultInfo scanResultInfo = 2913 makeScanResultInfo(TEST_DEFAULT_SSID, TEST_DEFAULT_BSSID); 2914 final ProvisioningConfiguration.Builder prov = new ProvisioningConfiguration.Builder() 2915 .withoutIpReachabilityMonitor() 2916 .withLayer2Information(layer2Info) 2917 .withScanResultInfo(scanResultInfo) 2918 .withDisplayName("ssid"); 2919 if (!hasIpv4) prov.withoutIPv4(); 2920 if (!hasIpv6) prov.withoutIPv6(); 2921 2922 // Enable rapid commit to accelerate DHCP handshake to shorten test duration, 2923 // not strictly necessary. 2924 setDhcpFeatures(false /* isDhcpLeaseCacheEnabled */, true /* isRapidCommitEnabled */, 2925 false /* isDhcpIpConflictDetectEnabled */, false /* isIPv6OnlyPreferredEnabled */); 2926 if (isGratuitousArpNaRoamingEnabled) { 2927 setFeatureEnabled(NetworkStackUtils.IPCLIENT_GARP_NA_ROAMING_VERSION, true); 2928 assertTrue(isFeatureEnabled(NetworkStackUtils.IPCLIENT_GARP_NA_ROAMING_VERSION, false)); 2929 } 2930 startIpClientProvisioning(prov.build()); 2931 } 2932 2933 private void waitForGratuitousArpAndNaPacket(final List<ArpPacket> arpList, 2934 final List<NeighborAdvertisement> naList) throws Exception { 2935 NeighborAdvertisement na; 2936 ArpPacket garp; 2937 do { 2938 na = getNextNeighborAdvertisement(); 2939 if (na != null) { 2940 assertGratuitousNa(na); 2941 naList.add(na); 2942 } 2943 garp = getNextArpPacket(TEST_TIMEOUT_MS); 2944 if (garp != null) { 2945 assertGratuitousARP(garp); 2946 arpList.add(garp); 2947 } 2948 } while (na != null || garp != null); 2949 } 2950 2951 @Test 2952 public void testGratuitousArpAndNaAfterRoaming() throws Exception { 2953 startGratuitousArpAndNaAfterRoamingTest(true /* isGratuitousArpNaRoamingEnabled */, 2954 true /* hasIpv4 */, true /* hasIpv6 */); 2955 performDualStackProvisioning(); 2956 forceLayer2Roaming(); 2957 2958 final List<ArpPacket> arpList = new ArrayList<>(); 2959 final List<NeighborAdvertisement> naList = new ArrayList<>(); 2960 waitForGratuitousArpAndNaPacket(arpList, naList); 2961 assertEquals(2, naList.size()); // privacy address and stable privacy address 2962 assertEquals(1, arpList.size()); // IPv4 address 2963 } 2964 2965 @Test 2966 public void testGratuitousArpAndNaAfterRoaming_disableExpFlag() throws Exception { 2967 startGratuitousArpAndNaAfterRoamingTest(false /* isGratuitousArpNaRoamingEnabled */, 2968 true /* hasIpv6 */, true /* hasIpv6 */); 2969 performDualStackProvisioning(); 2970 forceLayer2Roaming(); 2971 2972 final List<ArpPacket> arpList = new ArrayList<>(); 2973 final List<NeighborAdvertisement> naList = new ArrayList<>(); 2974 waitForGratuitousArpAndNaPacket(arpList, naList); 2975 assertEquals(0, naList.size()); 2976 assertEquals(0, arpList.size()); 2977 } 2978 2979 @Test 2980 public void testGratuitousArpAndNaAfterRoaming_IPv6OnlyNetwork() throws Exception { 2981 startGratuitousArpAndNaAfterRoamingTest(true /* isGratuitousArpNaRoamingEnabled */, 2982 false /* hasIpv4 */, true /* hasIpv6 */); 2983 doIpv6OnlyProvisioning(); 2984 forceLayer2Roaming(); 2985 2986 final List<ArpPacket> arpList = new ArrayList<>(); 2987 final List<NeighborAdvertisement> naList = new ArrayList<>(); 2988 waitForGratuitousArpAndNaPacket(arpList, naList); 2989 assertEquals(2, naList.size()); 2990 assertEquals(0, arpList.size()); 2991 } 2992 2993 @Test 2994 public void testGratuitousArpAndNaAfterRoaming_IPv4OnlyNetwork() throws Exception { 2995 startGratuitousArpAndNaAfterRoamingTest(true /* isGratuitousArpNaRoamingEnabled */, 2996 true /* hasIpv4 */, false /* hasIpv6 */); 2997 2998 // Start IPv4 provisioning and wait until entire provisioning completes. 2999 handleDhcpPackets(true /* isSuccessLease */, TEST_LEASE_DURATION_S, 3000 true /* shouldReplyRapidCommitAck */, TEST_DEFAULT_MTU, null /* serverSentUrl */); 3001 verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR)); 3002 forceLayer2Roaming(); 3003 3004 final List<ArpPacket> arpList = new ArrayList<>(); 3005 final List<NeighborAdvertisement> naList = new ArrayList<>(); 3006 waitForGratuitousArpAndNaPacket(arpList, naList); 3007 assertEquals(0, naList.size()); 3008 assertEquals(1, arpList.size()); 3009 } 3010 3011 private void doInitialBssidSetupTest(final Layer2Information layer2Info, 3012 final ScanResultInfo scanResultInfo) throws Exception { 3013 ProvisioningConfiguration.Builder prov = new ProvisioningConfiguration.Builder() 3014 .withoutIpReachabilityMonitor() 3015 .withLayer2Information(layer2Info) 3016 .withScanResultInfo(scanResultInfo) 3017 .withDisplayName("\"0001docomo\"") 3018 .withoutIPv6(); 3019 3020 setDhcpFeatures(false /* isDhcpLeaseCacheEnabled */, true /* shouldReplyRapidCommitAck */, 3021 false /* isDhcpIpConflictDetectEnabled */, false /* isIPv6OnlyPreferredEnabled */); 3022 startIpClientProvisioning(prov.build()); 3023 3024 handleDhcpPackets(true /* isSuccessLease */, TEST_LEASE_DURATION_S, 3025 true /* shouldReplyRapidCommitAck */, TEST_DEFAULT_MTU, null /* serverSentUrl */); 3026 verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR)); 3027 forceLayer2Roaming(); 3028 } 3029 3030 @Test @IgnoreUpTo(Build.VERSION_CODES.R) 3031 public void testSetInitialBssidFromLayer2Info() throws Exception { 3032 final Layer2Information layer2Info = new Layer2Information(TEST_L2KEY, TEST_CLUSTER, 3033 MacAddress.fromString(TEST_DEFAULT_BSSID)); 3034 3035 doInitialBssidSetupTest(layer2Info, null /* scanResultInfo */); 3036 3037 // Initial BSSID comes from layer2Info, it's different with target roaming bssid, 3038 // then verify that DHCPREQUEST packet is sent after roaming. 3039 final DhcpPacket packet = getNextDhcpPacket(); 3040 assertTrue(packet instanceof DhcpRequestPacket); 3041 } 3042 3043 @Test @IgnoreUpTo(Build.VERSION_CODES.R) 3044 public void testSetInitialBssidFromLayer2Info_NullBssid() throws Exception { 3045 final Layer2Information layer2Info = new Layer2Information(TEST_L2KEY, TEST_CLUSTER, 3046 null /* bssid */); 3047 final ScanResultInfo scanResultInfo = 3048 makeScanResultInfo(TEST_DEFAULT_SSID, TEST_DHCP_ROAM_BSSID); 3049 3050 doInitialBssidSetupTest(layer2Info, scanResultInfo); 3051 3052 // Initial BSSID comes from layer2Info, it's null, no DHCPREQUEST packet 3053 // will be sent after roaming. 3054 final DhcpPacket packet = getNextDhcpPacket(TEST_TIMEOUT_MS); 3055 assertNull(packet); 3056 } 3057 3058 @Test @IgnoreUpTo(Build.VERSION_CODES.R) 3059 public void testSetInitialBssidFromLayer2Info_SameRoamingBssid() throws Exception { 3060 final Layer2Information layer2Info = new Layer2Information(TEST_L2KEY, TEST_CLUSTER, 3061 MacAddress.fromString(TEST_DHCP_ROAM_BSSID)); 3062 3063 doInitialBssidSetupTest(layer2Info, null /* scanResultInfo */); 3064 3065 // Initial BSSID comes from layer2Info, it's same with target roaming bssid, 3066 // no DHCPREQUEST packet will be sent after roaming. 3067 final DhcpPacket packet = getNextDhcpPacket(TEST_TIMEOUT_MS); 3068 assertNull(packet); 3069 } 3070 3071 @Test @IgnoreAfter(Build.VERSION_CODES.R) 3072 public void testSetInitialBssidFromScanResultInfo() throws Exception { 3073 final ScanResultInfo scanResultInfo = 3074 makeScanResultInfo(TEST_DEFAULT_SSID, TEST_DEFAULT_BSSID); 3075 3076 doInitialBssidSetupTest(null /* layer2Info */, scanResultInfo); 3077 3078 // Initial BSSID comes from ScanResultInfo, it's different with target roaming bssid, 3079 // then verify that DHCPREQUEST packet is sent after roaming. 3080 final DhcpPacket packet = getNextDhcpPacket(); 3081 assertTrue(packet instanceof DhcpRequestPacket); 3082 } 3083 3084 @Test @IgnoreAfter(Build.VERSION_CODES.R) 3085 public void testSetInitialBssidFromScanResultInfo_SameRoamingBssid() throws Exception { 3086 final ScanResultInfo scanResultInfo = 3087 makeScanResultInfo(TEST_DEFAULT_SSID, TEST_DHCP_ROAM_BSSID); 3088 3089 doInitialBssidSetupTest(null /* layer2Info */, scanResultInfo); 3090 3091 // Initial BSSID comes from ScanResultInfo, it's same with target roaming bssid, 3092 // no DHCPREQUEST packet will be sent after roaming. 3093 final DhcpPacket packet = getNextDhcpPacket(TEST_TIMEOUT_MS); 3094 assertNull(packet); 3095 } 3096 3097 @Test @IgnoreAfter(Build.VERSION_CODES.R) 3098 public void testSetInitialBssidFromScanResultInfo_BrokenInitialBssid() throws Exception { 3099 final ScanResultInfo scanResultInfo = 3100 makeScanResultInfo(TEST_DEFAULT_SSID, "00:11:22:33:44:"); 3101 3102 doInitialBssidSetupTest(null /* layer2Info */, scanResultInfo); 3103 3104 // Initial BSSID comes from ScanResultInfo, it's broken MAC address format and fallback 3105 // to null layer2Info, no DHCPREQUEST packet will be sent after roaming. 3106 final DhcpPacket packet = getNextDhcpPacket(TEST_TIMEOUT_MS); 3107 assertNull(packet); 3108 } 3109 3110 @Test @IgnoreAfter(Build.VERSION_CODES.R) 3111 public void testSetInitialBssidFromScanResultInfo_BrokenInitialBssidFallback() 3112 throws Exception { 3113 final Layer2Information layer2Info = new Layer2Information(TEST_L2KEY, TEST_CLUSTER, 3114 MacAddress.fromString(TEST_DEFAULT_BSSID)); 3115 final ScanResultInfo scanResultInfo = 3116 makeScanResultInfo(TEST_DEFAULT_SSID, "00:11:22:33:44:"); 3117 3118 doInitialBssidSetupTest(layer2Info, scanResultInfo); 3119 3120 // Initial BSSID comes from ScanResultInfo, it's broken MAC address format and fallback 3121 // to check layer2Info, then verify DHCPREQUEST packet will be sent after roaming. 3122 final DhcpPacket packet = getNextDhcpPacket(); 3123 assertTrue(packet instanceof DhcpRequestPacket); 3124 } 3125 } 3126