1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.networkstack.tethering; 18 19 import static android.net.NetworkStats.DEFAULT_NETWORK_NO; 20 import static android.net.NetworkStats.METERED_NO; 21 import static android.net.NetworkStats.ROAMING_NO; 22 import static android.net.NetworkStats.SET_DEFAULT; 23 import static android.net.NetworkStats.TAG_NONE; 24 import static android.net.NetworkStats.UID_ALL; 25 import static android.net.NetworkStats.UID_TETHERING; 26 import static android.net.ip.ConntrackMonitor.ConntrackEvent; 27 import static android.net.netlink.ConntrackMessage.DYING_MASK; 28 import static android.net.netlink.ConntrackMessage.ESTABLISHED_MASK; 29 import static android.net.netlink.ConntrackMessage.Tuple; 30 import static android.net.netlink.ConntrackMessage.TupleIpv4; 31 import static android.net.netlink.ConntrackMessage.TupleProto; 32 import static android.net.netlink.NetlinkConstants.IPCTNL_MSG_CT_DELETE; 33 import static android.net.netlink.NetlinkConstants.IPCTNL_MSG_CT_NEW; 34 import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED; 35 import static android.system.OsConstants.ETH_P_IP; 36 import static android.system.OsConstants.ETH_P_IPV6; 37 import static android.system.OsConstants.IPPROTO_TCP; 38 import static android.system.OsConstants.IPPROTO_UDP; 39 import static android.system.OsConstants.NETLINK_NETFILTER; 40 41 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; 42 import static com.android.dx.mockito.inline.extended.ExtendedMockito.staticMockMarker; 43 import static com.android.networkstack.tethering.BpfCoordinator.NF_CONNTRACK_TCP_TIMEOUT_ESTABLISHED; 44 import static com.android.networkstack.tethering.BpfCoordinator.NF_CONNTRACK_UDP_TIMEOUT_STREAM; 45 import static com.android.networkstack.tethering.BpfCoordinator.POLLING_CONNTRACK_TIMEOUT_MS; 46 import static com.android.networkstack.tethering.BpfCoordinator.StatsType; 47 import static com.android.networkstack.tethering.BpfCoordinator.StatsType.STATS_PER_IFACE; 48 import static com.android.networkstack.tethering.BpfCoordinator.StatsType.STATS_PER_UID; 49 import static com.android.networkstack.tethering.BpfUtils.DOWNSTREAM; 50 import static com.android.networkstack.tethering.BpfUtils.UPSTREAM; 51 import static com.android.networkstack.tethering.TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS; 52 53 import static org.junit.Assert.assertEquals; 54 import static org.junit.Assert.assertNotNull; 55 import static org.junit.Assert.assertNull; 56 import static org.junit.Assert.assertTrue; 57 import static org.junit.Assert.fail; 58 import static org.mockito.Matchers.any; 59 import static org.mockito.Matchers.anyInt; 60 import static org.mockito.Matchers.anyLong; 61 import static org.mockito.Matchers.anyString; 62 import static org.mockito.Matchers.eq; 63 import static org.mockito.Mockito.argThat; 64 import static org.mockito.Mockito.clearInvocations; 65 import static org.mockito.Mockito.inOrder; 66 import static org.mockito.Mockito.never; 67 import static org.mockito.Mockito.spy; 68 import static org.mockito.Mockito.verify; 69 import static org.mockito.Mockito.when; 70 71 import android.app.usage.NetworkStatsManager; 72 import android.net.INetd; 73 import android.net.InetAddresses; 74 import android.net.LinkAddress; 75 import android.net.LinkProperties; 76 import android.net.MacAddress; 77 import android.net.Network; 78 import android.net.NetworkCapabilities; 79 import android.net.NetworkStats; 80 import android.net.TetherOffloadRuleParcel; 81 import android.net.TetherStatsParcel; 82 import android.net.ip.ConntrackMonitor; 83 import android.net.ip.ConntrackMonitor.ConntrackEventConsumer; 84 import android.net.ip.IpServer; 85 import android.net.netlink.ConntrackMessage; 86 import android.net.netlink.NetlinkConstants; 87 import android.net.netlink.NetlinkSocket; 88 import android.net.util.InterfaceParams; 89 import android.net.util.SharedLog; 90 import android.os.Build; 91 import android.os.Handler; 92 import android.os.test.TestLooper; 93 import android.system.ErrnoException; 94 95 import androidx.annotation.NonNull; 96 import androidx.annotation.Nullable; 97 import androidx.test.filters.SmallTest; 98 import androidx.test.runner.AndroidJUnit4; 99 100 import com.android.dx.mockito.inline.extended.ExtendedMockito; 101 import com.android.net.module.util.NetworkStackConstants; 102 import com.android.net.module.util.Struct; 103 import com.android.networkstack.tethering.BpfCoordinator.BpfConntrackEventConsumer; 104 import com.android.networkstack.tethering.BpfCoordinator.ClientInfo; 105 import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule; 106 import com.android.testutils.DevSdkIgnoreRule; 107 import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter; 108 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; 109 import com.android.testutils.TestableNetworkStatsProviderCbBinder; 110 111 import org.junit.Before; 112 import org.junit.Rule; 113 import org.junit.Test; 114 import org.junit.runner.RunWith; 115 import org.mockito.ArgumentCaptor; 116 import org.mockito.ArgumentMatcher; 117 import org.mockito.InOrder; 118 import org.mockito.Mock; 119 import org.mockito.MockitoAnnotations; 120 import org.mockito.MockitoSession; 121 122 import java.net.Inet4Address; 123 import java.net.Inet6Address; 124 import java.net.InetAddress; 125 import java.util.ArrayList; 126 import java.util.Arrays; 127 import java.util.HashMap; 128 import java.util.LinkedHashMap; 129 import java.util.Map; 130 import java.util.function.BiConsumer; 131 132 @RunWith(AndroidJUnit4.class) 133 @SmallTest 134 public class BpfCoordinatorTest { 135 @Rule 136 public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule(); 137 138 private static final int TEST_NET_ID = 24; 139 140 private static final int UPSTREAM_IFINDEX = 1001; 141 private static final int DOWNSTREAM_IFINDEX = 1002; 142 143 private static final String UPSTREAM_IFACE = "rmnet0"; 144 145 private static final MacAddress DOWNSTREAM_MAC = MacAddress.fromString("12:34:56:78:90:ab"); 146 private static final MacAddress MAC_A = MacAddress.fromString("00:00:00:00:00:0a"); 147 private static final MacAddress MAC_B = MacAddress.fromString("11:22:33:00:00:0b"); 148 149 private static final InetAddress NEIGH_A = InetAddresses.parseNumericAddress("2001:db8::1"); 150 private static final InetAddress NEIGH_B = InetAddresses.parseNumericAddress("2001:db8::2"); 151 152 private static final InterfaceParams UPSTREAM_IFACE_PARAMS = new InterfaceParams( 153 UPSTREAM_IFACE, UPSTREAM_IFINDEX, null /* macAddr, rawip */, 154 NetworkStackConstants.ETHER_MTU); 155 156 // The test fake BPF map class is needed because the test has no privilege to access the BPF 157 // map. All member functions which eventually call JNI to access the real native BPF map need 158 // to be overridden. 159 // TODO: consider moving to an individual file. 160 private class TestBpfMap<K extends Struct, V extends Struct> extends BpfMap<K, V> { 161 private final HashMap<K, V> mMap = new HashMap<K, V>(); 162 TestBpfMap(final Class<K> key, final Class<V> value)163 TestBpfMap(final Class<K> key, final Class<V> value) { 164 super(key, value); 165 } 166 167 @Override forEach(BiConsumer<K, V> action)168 public void forEach(BiConsumer<K, V> action) throws ErrnoException { 169 // TODO: consider using mocked #getFirstKey and #getNextKey to iterate. It helps to 170 // implement the entry deletion in the iteration if required. 171 for (Map.Entry<K, V> entry : mMap.entrySet()) { 172 action.accept(entry.getKey(), entry.getValue()); 173 } 174 } 175 176 @Override updateEntry(K key, V value)177 public void updateEntry(K key, V value) throws ErrnoException { 178 mMap.put(key, value); 179 } 180 181 @Override insertEntry(K key, V value)182 public void insertEntry(K key, V value) throws ErrnoException, 183 IllegalArgumentException { 184 // The entry is created if and only if it doesn't exist. See BpfMap#insertEntry. 185 if (mMap.get(key) != null) { 186 throw new IllegalArgumentException(key + " already exist"); 187 } 188 mMap.put(key, value); 189 } 190 191 @Override deleteEntry(Struct key)192 public boolean deleteEntry(Struct key) throws ErrnoException { 193 return mMap.remove(key) != null; 194 } 195 196 @Override getValue(@onNull K key)197 public V getValue(@NonNull K key) throws ErrnoException { 198 // Return value for a given key. Otherwise, return null without an error ENOENT. 199 // BpfMap#getValue treats that the entry is not found as no error. 200 return mMap.get(key); 201 } 202 203 @Override clear()204 public void clear() throws ErrnoException { 205 // TODO: consider using mocked #getFirstKey and #deleteEntry to implement. 206 mMap.clear(); 207 } 208 }; 209 210 @Mock private NetworkStatsManager mStatsManager; 211 @Mock private INetd mNetd; 212 @Mock private IpServer mIpServer; 213 @Mock private IpServer mIpServer2; 214 @Mock private TetheringConfiguration mTetherConfig; 215 @Mock private ConntrackMonitor mConntrackMonitor; 216 @Mock private BpfMap<Tether4Key, Tether4Value> mBpfDownstream4Map; 217 @Mock private BpfMap<Tether4Key, Tether4Value> mBpfUpstream4Map; 218 @Mock private BpfMap<TetherDownstream6Key, Tether6Value> mBpfDownstream6Map; 219 @Mock private BpfMap<TetherUpstream6Key, Tether6Value> mBpfUpstream6Map; 220 @Mock private BpfMap<TetherDevKey, TetherDevValue> mBpfDevMap; 221 222 // Late init since methods must be called by the thread that created this object. 223 private TestableNetworkStatsProviderCbBinder mTetherStatsProviderCb; 224 private BpfCoordinator.BpfTetherStatsProvider mTetherStatsProvider; 225 226 // Late init since the object must be initialized by the BPF coordinator instance because 227 // it has to access the non-static function of BPF coordinator. 228 private BpfConntrackEventConsumer mConsumer; 229 230 private long mElapsedRealtimeNanos = 0; 231 private final ArgumentCaptor<ArrayList> mStringArrayCaptor = 232 ArgumentCaptor.forClass(ArrayList.class); 233 private final TestLooper mTestLooper = new TestLooper(); 234 private final TestBpfMap<TetherStatsKey, TetherStatsValue> mBpfStatsMap = 235 spy(new TestBpfMap<>(TetherStatsKey.class, TetherStatsValue.class)); 236 private final TestBpfMap<TetherLimitKey, TetherLimitValue> mBpfLimitMap = 237 spy(new TestBpfMap<>(TetherLimitKey.class, TetherLimitValue.class)); 238 private BpfCoordinator.Dependencies mDeps = 239 spy(new BpfCoordinator.Dependencies() { 240 @NonNull 241 public Handler getHandler() { 242 return new Handler(mTestLooper.getLooper()); 243 } 244 245 @NonNull 246 public INetd getNetd() { 247 return mNetd; 248 } 249 250 @NonNull 251 public NetworkStatsManager getNetworkStatsManager() { 252 return mStatsManager; 253 } 254 255 @NonNull 256 public SharedLog getSharedLog() { 257 return new SharedLog("test"); 258 } 259 260 @Nullable 261 public TetheringConfiguration getTetherConfig() { 262 return mTetherConfig; 263 } 264 265 @NonNull 266 public ConntrackMonitor getConntrackMonitor(ConntrackEventConsumer consumer) { 267 return mConntrackMonitor; 268 } 269 270 public long elapsedRealtimeNanos() { 271 return mElapsedRealtimeNanos; 272 } 273 274 @Nullable 275 public BpfMap<Tether4Key, Tether4Value> getBpfDownstream4Map() { 276 return mBpfDownstream4Map; 277 } 278 279 @Nullable 280 public BpfMap<Tether4Key, Tether4Value> getBpfUpstream4Map() { 281 return mBpfUpstream4Map; 282 } 283 284 @Nullable 285 public BpfMap<TetherDownstream6Key, Tether6Value> getBpfDownstream6Map() { 286 return mBpfDownstream6Map; 287 } 288 289 @Nullable 290 public BpfMap<TetherUpstream6Key, Tether6Value> getBpfUpstream6Map() { 291 return mBpfUpstream6Map; 292 } 293 294 @Nullable 295 public BpfMap<TetherStatsKey, TetherStatsValue> getBpfStatsMap() { 296 return mBpfStatsMap; 297 } 298 299 @Nullable 300 public BpfMap<TetherLimitKey, TetherLimitValue> getBpfLimitMap() { 301 return mBpfLimitMap; 302 } 303 304 @Nullable 305 public BpfMap<TetherDevKey, TetherDevValue> getBpfDevMap() { 306 return mBpfDevMap; 307 } 308 }); 309 setUp()310 @Before public void setUp() { 311 MockitoAnnotations.initMocks(this); 312 when(mTetherConfig.isBpfOffloadEnabled()).thenReturn(true /* default value */); 313 } 314 waitForIdle()315 private void waitForIdle() { 316 mTestLooper.dispatchAll(); 317 } 318 319 // TODO: Remove unnecessary calling on R because the BPF map accessing has been moved into 320 // module. setupFunctioningNetdInterface()321 private void setupFunctioningNetdInterface() throws Exception { 322 when(mNetd.tetherOffloadGetStats()).thenReturn(new TetherStatsParcel[0]); 323 } 324 325 @NonNull makeBpfCoordinator()326 private BpfCoordinator makeBpfCoordinator() throws Exception { 327 final BpfCoordinator coordinator = new BpfCoordinator(mDeps); 328 329 mConsumer = coordinator.getBpfConntrackEventConsumerForTesting(); 330 final ArgumentCaptor<BpfCoordinator.BpfTetherStatsProvider> 331 tetherStatsProviderCaptor = 332 ArgumentCaptor.forClass(BpfCoordinator.BpfTetherStatsProvider.class); 333 verify(mStatsManager).registerNetworkStatsProvider(anyString(), 334 tetherStatsProviderCaptor.capture()); 335 mTetherStatsProvider = tetherStatsProviderCaptor.getValue(); 336 assertNotNull(mTetherStatsProvider); 337 mTetherStatsProviderCb = new TestableNetworkStatsProviderCbBinder(); 338 mTetherStatsProvider.setProviderCallbackBinder(mTetherStatsProviderCb); 339 340 return coordinator; 341 } 342 343 @NonNull buildTestEntry(@onNull StatsType how, @NonNull String iface, long rxBytes, long rxPackets, long txBytes, long txPackets)344 private static NetworkStats.Entry buildTestEntry(@NonNull StatsType how, 345 @NonNull String iface, long rxBytes, long rxPackets, long txBytes, long txPackets) { 346 return new NetworkStats.Entry(iface, how == STATS_PER_IFACE ? UID_ALL : UID_TETHERING, 347 SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, rxBytes, 348 rxPackets, txBytes, txPackets, 0L); 349 } 350 351 @NonNull buildTestTetherStatsParcel(@onNull Integer ifIndex, long rxBytes, long rxPackets, long txBytes, long txPackets)352 private static TetherStatsParcel buildTestTetherStatsParcel(@NonNull Integer ifIndex, 353 long rxBytes, long rxPackets, long txBytes, long txPackets) { 354 final TetherStatsParcel parcel = new TetherStatsParcel(); 355 parcel.ifIndex = ifIndex; 356 parcel.rxBytes = rxBytes; 357 parcel.rxPackets = rxPackets; 358 parcel.txBytes = txBytes; 359 parcel.txPackets = txPackets; 360 return parcel; 361 } 362 363 // Update a stats entry or create if not exists. updateStatsEntryToStatsMap(@onNull TetherStatsParcel stats)364 private void updateStatsEntryToStatsMap(@NonNull TetherStatsParcel stats) throws Exception { 365 final TetherStatsKey key = new TetherStatsKey(stats.ifIndex); 366 final TetherStatsValue value = new TetherStatsValue(stats.rxPackets, stats.rxBytes, 367 0L /* rxErrors */, stats.txPackets, stats.txBytes, 0L /* txErrors */); 368 mBpfStatsMap.updateEntry(key, value); 369 } 370 updateStatsEntry(@onNull TetherStatsParcel stats)371 private void updateStatsEntry(@NonNull TetherStatsParcel stats) throws Exception { 372 if (mDeps.isAtLeastS()) { 373 updateStatsEntryToStatsMap(stats); 374 } else { 375 when(mNetd.tetherOffloadGetStats()).thenReturn(new TetherStatsParcel[] {stats}); 376 } 377 } 378 379 // Update specific tether stats list and wait for the stats cache is updated by polling thread 380 // in the coordinator. Beware of that it is only used for the default polling interval. 381 // Note that the mocked tetherOffloadGetStats of netd replaces all stats entries because it 382 // doesn't store the previous entries. updateStatsEntriesAndWaitForUpdate(@onNull TetherStatsParcel[] tetherStatsList)383 private void updateStatsEntriesAndWaitForUpdate(@NonNull TetherStatsParcel[] tetherStatsList) 384 throws Exception { 385 if (mDeps.isAtLeastS()) { 386 for (TetherStatsParcel stats : tetherStatsList) { 387 updateStatsEntry(stats); 388 } 389 } else { 390 when(mNetd.tetherOffloadGetStats()).thenReturn(tetherStatsList); 391 } 392 393 mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); 394 waitForIdle(); 395 } 396 397 // In tests, the stats need to be set before deleting the last rule. 398 // The reason is that BpfCoordinator#tetherOffloadRuleRemove reads the stats 399 // of the deleting interface after the last rule deleted. #tetherOffloadRuleRemove 400 // does the interface cleanup failed if there is no stats for the deleting interface. 401 // Note that the mocked tetherOffloadGetAndClearStats of netd replaces all stats entries 402 // because it doesn't store the previous entries. updateStatsEntryForTetherOffloadGetAndClearStats(TetherStatsParcel stats)403 private void updateStatsEntryForTetherOffloadGetAndClearStats(TetherStatsParcel stats) 404 throws Exception { 405 if (mDeps.isAtLeastS()) { 406 updateStatsEntryToStatsMap(stats); 407 } else { 408 when(mNetd.tetherOffloadGetAndClearStats(stats.ifIndex)).thenReturn(stats); 409 } 410 } 411 clearStatsInvocations()412 private void clearStatsInvocations() { 413 if (mDeps.isAtLeastS()) { 414 clearInvocations(mBpfStatsMap); 415 } else { 416 clearInvocations(mNetd); 417 } 418 } 419 verifyWithOrder(@ullable InOrder inOrder, @NonNull T t)420 private <T> T verifyWithOrder(@Nullable InOrder inOrder, @NonNull T t) { 421 if (inOrder != null) { 422 return inOrder.verify(t); 423 } else { 424 return verify(t); 425 } 426 } 427 verifyTetherOffloadGetStats()428 private void verifyTetherOffloadGetStats() throws Exception { 429 if (mDeps.isAtLeastS()) { 430 verify(mBpfStatsMap).forEach(any()); 431 } else { 432 verify(mNetd).tetherOffloadGetStats(); 433 } 434 } 435 verifyNeverTetherOffloadGetStats()436 private void verifyNeverTetherOffloadGetStats() throws Exception { 437 if (mDeps.isAtLeastS()) { 438 verify(mBpfStatsMap, never()).forEach(any()); 439 } else { 440 verify(mNetd, never()).tetherOffloadGetStats(); 441 } 442 } 443 verifyStartUpstreamIpv6Forwarding(@ullable InOrder inOrder, int downstreamIfIndex, MacAddress downstreamMac, int upstreamIfindex)444 private void verifyStartUpstreamIpv6Forwarding(@Nullable InOrder inOrder, int downstreamIfIndex, 445 MacAddress downstreamMac, int upstreamIfindex) throws Exception { 446 if (!mDeps.isAtLeastS()) return; 447 final TetherUpstream6Key key = new TetherUpstream6Key(downstreamIfIndex, downstreamMac); 448 final Tether6Value value = new Tether6Value(upstreamIfindex, 449 MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS, 450 ETH_P_IPV6, NetworkStackConstants.ETHER_MTU); 451 verifyWithOrder(inOrder, mBpfUpstream6Map).insertEntry(key, value); 452 } 453 verifyStopUpstreamIpv6Forwarding(@ullable InOrder inOrder, int downstreamIfIndex, MacAddress downstreamMac)454 private void verifyStopUpstreamIpv6Forwarding(@Nullable InOrder inOrder, int downstreamIfIndex, 455 MacAddress downstreamMac) 456 throws Exception { 457 if (!mDeps.isAtLeastS()) return; 458 final TetherUpstream6Key key = new TetherUpstream6Key(downstreamIfIndex, downstreamMac); 459 verifyWithOrder(inOrder, mBpfUpstream6Map).deleteEntry(key); 460 } 461 verifyNoUpstreamIpv6ForwardingChange(@ullable InOrder inOrder)462 private void verifyNoUpstreamIpv6ForwardingChange(@Nullable InOrder inOrder) throws Exception { 463 if (!mDeps.isAtLeastS()) return; 464 if (inOrder != null) { 465 inOrder.verify(mBpfUpstream6Map, never()).deleteEntry(any()); 466 inOrder.verify(mBpfUpstream6Map, never()).insertEntry(any(), any()); 467 inOrder.verify(mBpfUpstream6Map, never()).updateEntry(any(), any()); 468 } else { 469 verify(mBpfUpstream6Map, never()).deleteEntry(any()); 470 verify(mBpfUpstream6Map, never()).insertEntry(any(), any()); 471 verify(mBpfUpstream6Map, never()).updateEntry(any(), any()); 472 } 473 } 474 verifyTetherOffloadRuleAdd(@ullable InOrder inOrder, @NonNull Ipv6ForwardingRule rule)475 private void verifyTetherOffloadRuleAdd(@Nullable InOrder inOrder, 476 @NonNull Ipv6ForwardingRule rule) throws Exception { 477 if (mDeps.isAtLeastS()) { 478 verifyWithOrder(inOrder, mBpfDownstream6Map).updateEntry( 479 rule.makeTetherDownstream6Key(), rule.makeTether6Value()); 480 } else { 481 verifyWithOrder(inOrder, mNetd).tetherOffloadRuleAdd(matches(rule)); 482 } 483 } 484 verifyNeverTetherOffloadRuleAdd()485 private void verifyNeverTetherOffloadRuleAdd() throws Exception { 486 if (mDeps.isAtLeastS()) { 487 verify(mBpfDownstream6Map, never()).updateEntry(any(), any()); 488 } else { 489 verify(mNetd, never()).tetherOffloadRuleAdd(any()); 490 } 491 } 492 verifyTetherOffloadRuleRemove(@ullable InOrder inOrder, @NonNull final Ipv6ForwardingRule rule)493 private void verifyTetherOffloadRuleRemove(@Nullable InOrder inOrder, 494 @NonNull final Ipv6ForwardingRule rule) throws Exception { 495 if (mDeps.isAtLeastS()) { 496 verifyWithOrder(inOrder, mBpfDownstream6Map).deleteEntry( 497 rule.makeTetherDownstream6Key()); 498 } else { 499 verifyWithOrder(inOrder, mNetd).tetherOffloadRuleRemove(matches(rule)); 500 } 501 } 502 verifyNeverTetherOffloadRuleRemove()503 private void verifyNeverTetherOffloadRuleRemove() throws Exception { 504 if (mDeps.isAtLeastS()) { 505 verify(mBpfDownstream6Map, never()).deleteEntry(any()); 506 } else { 507 verify(mNetd, never()).tetherOffloadRuleRemove(any()); 508 } 509 } 510 verifyTetherOffloadSetInterfaceQuota(@ullable InOrder inOrder, int ifIndex, long quotaBytes, boolean isInit)511 private void verifyTetherOffloadSetInterfaceQuota(@Nullable InOrder inOrder, int ifIndex, 512 long quotaBytes, boolean isInit) throws Exception { 513 if (mDeps.isAtLeastS()) { 514 final TetherStatsKey key = new TetherStatsKey(ifIndex); 515 verifyWithOrder(inOrder, mBpfStatsMap).getValue(key); 516 if (isInit) { 517 verifyWithOrder(inOrder, mBpfStatsMap).insertEntry(key, new TetherStatsValue( 518 0L /* rxPackets */, 0L /* rxBytes */, 0L /* rxErrors */, 519 0L /* txPackets */, 0L /* txBytes */, 0L /* txErrors */)); 520 } 521 verifyWithOrder(inOrder, mBpfLimitMap).updateEntry(new TetherLimitKey(ifIndex), 522 new TetherLimitValue(quotaBytes)); 523 } else { 524 verifyWithOrder(inOrder, mNetd).tetherOffloadSetInterfaceQuota(ifIndex, quotaBytes); 525 } 526 } 527 verifyNeverTetherOffloadSetInterfaceQuota(@onNull InOrder inOrder)528 private void verifyNeverTetherOffloadSetInterfaceQuota(@NonNull InOrder inOrder) 529 throws Exception { 530 if (mDeps.isAtLeastS()) { 531 inOrder.verify(mBpfStatsMap, never()).getValue(any()); 532 inOrder.verify(mBpfStatsMap, never()).insertEntry(any(), any()); 533 inOrder.verify(mBpfLimitMap, never()).updateEntry(any(), any()); 534 } else { 535 inOrder.verify(mNetd, never()).tetherOffloadSetInterfaceQuota(anyInt(), anyLong()); 536 } 537 } 538 verifyTetherOffloadGetAndClearStats(@onNull InOrder inOrder, int ifIndex)539 private void verifyTetherOffloadGetAndClearStats(@NonNull InOrder inOrder, int ifIndex) 540 throws Exception { 541 if (mDeps.isAtLeastS()) { 542 inOrder.verify(mBpfStatsMap).getValue(new TetherStatsKey(ifIndex)); 543 inOrder.verify(mBpfStatsMap).deleteEntry(new TetherStatsKey(ifIndex)); 544 inOrder.verify(mBpfLimitMap).deleteEntry(new TetherLimitKey(ifIndex)); 545 } else { 546 inOrder.verify(mNetd).tetherOffloadGetAndClearStats(ifIndex); 547 } 548 } 549 550 // S+ and R api minimum tests. 551 // The following tests are used to provide minimum checking for the APIs on different flow. 552 // The auto merge is not enabled on mainline prod. The code flow R may be verified at the 553 // late stage by manual cherry pick. It is risky if the R code flow has broken and be found at 554 // the last minute. 555 // TODO: remove once presubmit tests on R even the code is submitted on S. checkTetherOffloadRuleAddAndRemove(boolean usingApiS)556 private void checkTetherOffloadRuleAddAndRemove(boolean usingApiS) throws Exception { 557 setupFunctioningNetdInterface(); 558 559 // Replace Dependencies#isAtLeastS() for testing R and S+ BPF map apis. Note that |mDeps| 560 // must be mocked before calling #makeBpfCoordinator which use |mDeps| to initialize the 561 // coordinator. 562 doReturn(usingApiS).when(mDeps).isAtLeastS(); 563 final BpfCoordinator coordinator = makeBpfCoordinator(); 564 565 final String mobileIface = "rmnet_data0"; 566 final Integer mobileIfIndex = 100; 567 coordinator.addUpstreamNameToLookupTable(mobileIfIndex, mobileIface); 568 569 // InOrder is required because mBpfStatsMap may be accessed by both 570 // BpfCoordinator#tetherOffloadRuleAdd and BpfCoordinator#tetherOffloadGetAndClearStats. 571 // The #verifyTetherOffloadGetAndClearStats can't distinguish who has ever called 572 // mBpfStatsMap#getValue and get a wrong calling count which counts all. 573 final InOrder inOrder = inOrder(mNetd, mBpfDownstream6Map, mBpfLimitMap, mBpfStatsMap); 574 final Ipv6ForwardingRule rule = buildTestForwardingRule(mobileIfIndex, NEIGH_A, MAC_A); 575 coordinator.tetherOffloadRuleAdd(mIpServer, rule); 576 verifyTetherOffloadRuleAdd(inOrder, rule); 577 verifyTetherOffloadSetInterfaceQuota(inOrder, mobileIfIndex, QUOTA_UNLIMITED, 578 true /* isInit */); 579 580 // Removing the last rule on current upstream immediately sends the cleanup stuff to netd. 581 updateStatsEntryForTetherOffloadGetAndClearStats( 582 buildTestTetherStatsParcel(mobileIfIndex, 0, 0, 0, 0)); 583 coordinator.tetherOffloadRuleRemove(mIpServer, rule); 584 verifyTetherOffloadRuleRemove(inOrder, rule); 585 verifyTetherOffloadGetAndClearStats(inOrder, mobileIfIndex); 586 } 587 588 // TODO: remove once presubmit tests on R even the code is submitted on S. 589 @Test testTetherOffloadRuleAddAndRemoveSdkR()590 public void testTetherOffloadRuleAddAndRemoveSdkR() throws Exception { 591 checkTetherOffloadRuleAddAndRemove(false /* R */); 592 } 593 594 // TODO: remove once presubmit tests on R even the code is submitted on S. 595 @Test testTetherOffloadRuleAddAndRemoveAtLeastSdkS()596 public void testTetherOffloadRuleAddAndRemoveAtLeastSdkS() throws Exception { 597 checkTetherOffloadRuleAddAndRemove(true /* S+ */); 598 } 599 600 // TODO: remove once presubmit tests on R even the code is submitted on S. checkTetherOffloadGetStats(boolean usingApiS)601 private void checkTetherOffloadGetStats(boolean usingApiS) throws Exception { 602 setupFunctioningNetdInterface(); 603 604 doReturn(usingApiS).when(mDeps).isAtLeastS(); 605 final BpfCoordinator coordinator = makeBpfCoordinator(); 606 coordinator.startPolling(); 607 608 final String mobileIface = "rmnet_data0"; 609 final Integer mobileIfIndex = 100; 610 coordinator.addUpstreamNameToLookupTable(mobileIfIndex, mobileIface); 611 612 updateStatsEntriesAndWaitForUpdate(new TetherStatsParcel[] { 613 buildTestTetherStatsParcel(mobileIfIndex, 1000, 100, 2000, 200)}); 614 615 final NetworkStats expectedIfaceStats = new NetworkStats(0L, 1) 616 .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 1000, 100, 2000, 200)); 617 618 final NetworkStats expectedUidStats = new NetworkStats(0L, 1) 619 .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 1000, 100, 2000, 200)); 620 621 mTetherStatsProvider.pushTetherStats(); 622 mTetherStatsProviderCb.expectNotifyStatsUpdated(expectedIfaceStats, expectedUidStats); 623 } 624 625 // TODO: remove once presubmit tests on R even the code is submitted on S. 626 @Test testTetherOffloadGetStatsSdkR()627 public void testTetherOffloadGetStatsSdkR() throws Exception { 628 checkTetherOffloadGetStats(false /* R */); 629 } 630 631 // TODO: remove once presubmit tests on R even the code is submitted on S. 632 @Test testTetherOffloadGetStatsAtLeastSdkS()633 public void testTetherOffloadGetStatsAtLeastSdkS() throws Exception { 634 checkTetherOffloadGetStats(true /* S+ */); 635 } 636 637 @Test testGetForwardedStats()638 public void testGetForwardedStats() throws Exception { 639 setupFunctioningNetdInterface(); 640 641 final BpfCoordinator coordinator = makeBpfCoordinator(); 642 coordinator.startPolling(); 643 644 final String wlanIface = "wlan0"; 645 final Integer wlanIfIndex = 100; 646 final String mobileIface = "rmnet_data0"; 647 final Integer mobileIfIndex = 101; 648 649 // Add interface name to lookup table. In realistic case, the upstream interface name will 650 // be added by IpServer when IpServer has received with a new IPv6 upstream update event. 651 coordinator.addUpstreamNameToLookupTable(wlanIfIndex, wlanIface); 652 coordinator.addUpstreamNameToLookupTable(mobileIfIndex, mobileIface); 653 654 // [1] Both interface stats are changed. 655 // Setup the tether stats of wlan and mobile interface. Note that move forward the time of 656 // the looper to make sure the new tether stats has been updated by polling update thread. 657 updateStatsEntriesAndWaitForUpdate(new TetherStatsParcel[] { 658 buildTestTetherStatsParcel(wlanIfIndex, 1000, 100, 2000, 200), 659 buildTestTetherStatsParcel(mobileIfIndex, 3000, 300, 4000, 400)}); 660 661 final NetworkStats expectedIfaceStats = new NetworkStats(0L, 2) 662 .addEntry(buildTestEntry(STATS_PER_IFACE, wlanIface, 1000, 100, 2000, 200)) 663 .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 3000, 300, 4000, 400)); 664 665 final NetworkStats expectedUidStats = new NetworkStats(0L, 2) 666 .addEntry(buildTestEntry(STATS_PER_UID, wlanIface, 1000, 100, 2000, 200)) 667 .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 3000, 300, 4000, 400)); 668 669 // Force pushing stats update to verify the stats reported. 670 // TODO: Perhaps make #expectNotifyStatsUpdated to use test TetherStatsParcel object for 671 // verifying the notification. 672 mTetherStatsProvider.pushTetherStats(); 673 mTetherStatsProviderCb.expectNotifyStatsUpdated(expectedIfaceStats, expectedUidStats); 674 675 // [2] Only one interface stats is changed. 676 // The tether stats of mobile interface is accumulated and The tether stats of wlan 677 // interface is the same. 678 updateStatsEntriesAndWaitForUpdate(new TetherStatsParcel[] { 679 buildTestTetherStatsParcel(wlanIfIndex, 1000, 100, 2000, 200), 680 buildTestTetherStatsParcel(mobileIfIndex, 3010, 320, 4030, 440)}); 681 682 final NetworkStats expectedIfaceStatsDiff = new NetworkStats(0L, 2) 683 .addEntry(buildTestEntry(STATS_PER_IFACE, wlanIface, 0, 0, 0, 0)) 684 .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 10, 20, 30, 40)); 685 686 final NetworkStats expectedUidStatsDiff = new NetworkStats(0L, 2) 687 .addEntry(buildTestEntry(STATS_PER_UID, wlanIface, 0, 0, 0, 0)) 688 .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 10, 20, 30, 40)); 689 690 // Force pushing stats update to verify that only diff of stats is reported. 691 mTetherStatsProvider.pushTetherStats(); 692 mTetherStatsProviderCb.expectNotifyStatsUpdated(expectedIfaceStatsDiff, 693 expectedUidStatsDiff); 694 695 // [3] Stop coordinator. 696 // Shutdown the coordinator and clear the invocation history, especially the 697 // tetherOffloadGetStats() calls. 698 coordinator.stopPolling(); 699 clearStatsInvocations(); 700 701 // Verify the polling update thread stopped. 702 mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); 703 waitForIdle(); 704 verifyNeverTetherOffloadGetStats(); 705 } 706 707 @Test testOnSetAlert()708 public void testOnSetAlert() throws Exception { 709 setupFunctioningNetdInterface(); 710 711 final BpfCoordinator coordinator = makeBpfCoordinator(); 712 coordinator.startPolling(); 713 714 final String mobileIface = "rmnet_data0"; 715 final Integer mobileIfIndex = 100; 716 coordinator.addUpstreamNameToLookupTable(mobileIfIndex, mobileIface); 717 718 // Verify that set quota to 0 will immediately triggers a callback. 719 mTetherStatsProvider.onSetAlert(0); 720 waitForIdle(); 721 mTetherStatsProviderCb.expectNotifyAlertReached(); 722 723 // Verify that notifyAlertReached never fired if quota is not yet reached. 724 updateStatsEntry(buildTestTetherStatsParcel(mobileIfIndex, 0, 0, 0, 0)); 725 mTetherStatsProvider.onSetAlert(100); 726 mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); 727 waitForIdle(); 728 mTetherStatsProviderCb.assertNoCallback(); 729 730 // Verify that notifyAlertReached fired when quota is reached. 731 updateStatsEntry(buildTestTetherStatsParcel(mobileIfIndex, 50, 0, 50, 0)); 732 mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); 733 waitForIdle(); 734 mTetherStatsProviderCb.expectNotifyAlertReached(); 735 736 // Verify that set quota with UNLIMITED won't trigger any callback. 737 mTetherStatsProvider.onSetAlert(QUOTA_UNLIMITED); 738 mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); 739 waitForIdle(); 740 mTetherStatsProviderCb.assertNoCallback(); 741 } 742 743 // The custom ArgumentMatcher simply comes from IpServerTest. 744 // TODO: move both of them into a common utility class for reusing the code. 745 private static class TetherOffloadRuleParcelMatcher implements 746 ArgumentMatcher<TetherOffloadRuleParcel> { 747 public final int upstreamIfindex; 748 public final int downstreamIfindex; 749 public final Inet6Address address; 750 public final MacAddress srcMac; 751 public final MacAddress dstMac; 752 TetherOffloadRuleParcelMatcher(@onNull Ipv6ForwardingRule rule)753 TetherOffloadRuleParcelMatcher(@NonNull Ipv6ForwardingRule rule) { 754 upstreamIfindex = rule.upstreamIfindex; 755 downstreamIfindex = rule.downstreamIfindex; 756 address = rule.address; 757 srcMac = rule.srcMac; 758 dstMac = rule.dstMac; 759 } 760 matches(@onNull TetherOffloadRuleParcel parcel)761 public boolean matches(@NonNull TetherOffloadRuleParcel parcel) { 762 return upstreamIfindex == parcel.inputInterfaceIndex 763 && (downstreamIfindex == parcel.outputInterfaceIndex) 764 && Arrays.equals(address.getAddress(), parcel.destination) 765 && (128 == parcel.prefixLength) 766 && Arrays.equals(srcMac.toByteArray(), parcel.srcL2Address) 767 && Arrays.equals(dstMac.toByteArray(), parcel.dstL2Address); 768 } 769 toString()770 public String toString() { 771 return String.format("TetherOffloadRuleParcelMatcher(%d, %d, %s, %s, %s", 772 upstreamIfindex, downstreamIfindex, address.getHostAddress(), srcMac, dstMac); 773 } 774 } 775 776 @NonNull matches(@onNull Ipv6ForwardingRule rule)777 private TetherOffloadRuleParcel matches(@NonNull Ipv6ForwardingRule rule) { 778 return argThat(new TetherOffloadRuleParcelMatcher(rule)); 779 } 780 781 @NonNull buildTestForwardingRule( int upstreamIfindex, @NonNull InetAddress address, @NonNull MacAddress dstMac)782 private static Ipv6ForwardingRule buildTestForwardingRule( 783 int upstreamIfindex, @NonNull InetAddress address, @NonNull MacAddress dstMac) { 784 return new Ipv6ForwardingRule(upstreamIfindex, DOWNSTREAM_IFINDEX, (Inet6Address) address, 785 DOWNSTREAM_MAC, dstMac); 786 } 787 788 @Test testRuleMakeTetherDownstream6Key()789 public void testRuleMakeTetherDownstream6Key() throws Exception { 790 final Integer mobileIfIndex = 100; 791 final Ipv6ForwardingRule rule = buildTestForwardingRule(mobileIfIndex, NEIGH_A, MAC_A); 792 793 final TetherDownstream6Key key = rule.makeTetherDownstream6Key(); 794 assertEquals(key.iif, (long) mobileIfIndex); 795 assertEquals(key.dstMac, MacAddress.ALL_ZEROS_ADDRESS); // rawip upstream 796 assertTrue(Arrays.equals(key.neigh6, NEIGH_A.getAddress())); 797 // iif (4) + dstMac(6) + padding(2) + neigh6 (16) = 28. 798 assertEquals(28, key.writeToBytes().length); 799 } 800 801 @Test testRuleMakeTether6Value()802 public void testRuleMakeTether6Value() throws Exception { 803 final Integer mobileIfIndex = 100; 804 final Ipv6ForwardingRule rule = buildTestForwardingRule(mobileIfIndex, NEIGH_A, MAC_A); 805 806 final Tether6Value value = rule.makeTether6Value(); 807 assertEquals(value.oif, DOWNSTREAM_IFINDEX); 808 assertEquals(value.ethDstMac, MAC_A); 809 assertEquals(value.ethSrcMac, DOWNSTREAM_MAC); 810 assertEquals(value.ethProto, ETH_P_IPV6); 811 assertEquals(value.pmtu, NetworkStackConstants.ETHER_MTU); 812 // oif (4) + ethDstMac (6) + ethSrcMac (6) + ethProto (2) + pmtu (2) = 20. 813 assertEquals(20, value.writeToBytes().length); 814 } 815 816 @Test testSetDataLimit()817 public void testSetDataLimit() throws Exception { 818 setupFunctioningNetdInterface(); 819 820 final BpfCoordinator coordinator = makeBpfCoordinator(); 821 822 final String mobileIface = "rmnet_data0"; 823 final Integer mobileIfIndex = 100; 824 coordinator.addUpstreamNameToLookupTable(mobileIfIndex, mobileIface); 825 826 // [1] Default limit. 827 // Set the unlimited quota as default if the service has never applied a data limit for a 828 // given upstream. Note that the data limit only be applied on an upstream which has rules. 829 final Ipv6ForwardingRule rule = buildTestForwardingRule(mobileIfIndex, NEIGH_A, MAC_A); 830 final InOrder inOrder = inOrder(mNetd, mBpfDownstream6Map, mBpfLimitMap, mBpfStatsMap); 831 coordinator.tetherOffloadRuleAdd(mIpServer, rule); 832 verifyTetherOffloadRuleAdd(inOrder, rule); 833 verifyTetherOffloadSetInterfaceQuota(inOrder, mobileIfIndex, QUOTA_UNLIMITED, 834 true /* isInit */); 835 inOrder.verifyNoMoreInteractions(); 836 837 // [2] Specific limit. 838 // Applying the data limit boundary {min, 1gb, max, infinity} on current upstream. 839 for (final long quota : new long[] {0, 1048576000, Long.MAX_VALUE, QUOTA_UNLIMITED}) { 840 mTetherStatsProvider.onSetLimit(mobileIface, quota); 841 waitForIdle(); 842 verifyTetherOffloadSetInterfaceQuota(inOrder, mobileIfIndex, quota, 843 false /* isInit */); 844 inOrder.verifyNoMoreInteractions(); 845 } 846 847 // [3] Invalid limit. 848 // The valid range of quota is 0..max_int64 or -1 (unlimited). 849 final long invalidLimit = Long.MIN_VALUE; 850 try { 851 mTetherStatsProvider.onSetLimit(mobileIface, invalidLimit); 852 waitForIdle(); 853 fail("No exception thrown for invalid limit " + invalidLimit + "."); 854 } catch (IllegalArgumentException expected) { 855 assertEquals(expected.getMessage(), "invalid quota value " + invalidLimit); 856 } 857 } 858 859 // TODO: Test the case in which the rules are changed from different IpServer objects. 860 @Test testSetDataLimitOnRule6Change()861 public void testSetDataLimitOnRule6Change() throws Exception { 862 setupFunctioningNetdInterface(); 863 864 final BpfCoordinator coordinator = makeBpfCoordinator(); 865 866 final String mobileIface = "rmnet_data0"; 867 final Integer mobileIfIndex = 100; 868 coordinator.addUpstreamNameToLookupTable(mobileIfIndex, mobileIface); 869 870 // Applying a data limit to the current upstream does not take any immediate action. 871 // The data limit could be only set on an upstream which has rules. 872 final long limit = 12345; 873 final InOrder inOrder = inOrder(mNetd, mBpfDownstream6Map, mBpfLimitMap, mBpfStatsMap); 874 mTetherStatsProvider.onSetLimit(mobileIface, limit); 875 waitForIdle(); 876 verifyNeverTetherOffloadSetInterfaceQuota(inOrder); 877 878 // Adding the first rule on current upstream immediately sends the quota to netd. 879 final Ipv6ForwardingRule ruleA = buildTestForwardingRule(mobileIfIndex, NEIGH_A, MAC_A); 880 coordinator.tetherOffloadRuleAdd(mIpServer, ruleA); 881 verifyTetherOffloadRuleAdd(inOrder, ruleA); 882 verifyTetherOffloadSetInterfaceQuota(inOrder, mobileIfIndex, limit, true /* isInit */); 883 inOrder.verifyNoMoreInteractions(); 884 885 // Adding the second rule on current upstream does not send the quota to netd. 886 final Ipv6ForwardingRule ruleB = buildTestForwardingRule(mobileIfIndex, NEIGH_B, MAC_B); 887 coordinator.tetherOffloadRuleAdd(mIpServer, ruleB); 888 verifyTetherOffloadRuleAdd(inOrder, ruleB); 889 verifyNeverTetherOffloadSetInterfaceQuota(inOrder); 890 891 // Removing the second rule on current upstream does not send the quota to netd. 892 coordinator.tetherOffloadRuleRemove(mIpServer, ruleB); 893 verifyTetherOffloadRuleRemove(inOrder, ruleB); 894 verifyNeverTetherOffloadSetInterfaceQuota(inOrder); 895 896 // Removing the last rule on current upstream immediately sends the cleanup stuff to netd. 897 updateStatsEntryForTetherOffloadGetAndClearStats( 898 buildTestTetherStatsParcel(mobileIfIndex, 0, 0, 0, 0)); 899 coordinator.tetherOffloadRuleRemove(mIpServer, ruleA); 900 verifyTetherOffloadRuleRemove(inOrder, ruleA); 901 verifyTetherOffloadGetAndClearStats(inOrder, mobileIfIndex); 902 inOrder.verifyNoMoreInteractions(); 903 } 904 905 @Test testTetherOffloadRuleUpdateAndClear()906 public void testTetherOffloadRuleUpdateAndClear() throws Exception { 907 setupFunctioningNetdInterface(); 908 909 final BpfCoordinator coordinator = makeBpfCoordinator(); 910 911 final String ethIface = "eth1"; 912 final String mobileIface = "rmnet_data0"; 913 final Integer ethIfIndex = 100; 914 final Integer mobileIfIndex = 101; 915 coordinator.addUpstreamNameToLookupTable(ethIfIndex, ethIface); 916 coordinator.addUpstreamNameToLookupTable(mobileIfIndex, mobileIface); 917 918 final InOrder inOrder = inOrder(mNetd, mBpfDownstream6Map, mBpfUpstream6Map, mBpfLimitMap, 919 mBpfStatsMap); 920 921 // Before the rule test, here are the additional actions while the rules are changed. 922 // - After adding the first rule on a given upstream, the coordinator adds a data limit. 923 // If the service has never applied the data limit, set an unlimited quota as default. 924 // - After removing the last rule on a given upstream, the coordinator gets the last stats. 925 // Then, it clears the stats and the limit entry from BPF maps. 926 // See tetherOffloadRule{Add, Remove, Clear, Clean}. 927 928 // [1] Adding rules on the upstream Ethernet. 929 // Note that the default data limit is applied after the first rule is added. 930 final Ipv6ForwardingRule ethernetRuleA = buildTestForwardingRule( 931 ethIfIndex, NEIGH_A, MAC_A); 932 final Ipv6ForwardingRule ethernetRuleB = buildTestForwardingRule( 933 ethIfIndex, NEIGH_B, MAC_B); 934 935 coordinator.tetherOffloadRuleAdd(mIpServer, ethernetRuleA); 936 verifyTetherOffloadRuleAdd(inOrder, ethernetRuleA); 937 verifyTetherOffloadSetInterfaceQuota(inOrder, ethIfIndex, QUOTA_UNLIMITED, 938 true /* isInit */); 939 verifyStartUpstreamIpv6Forwarding(inOrder, DOWNSTREAM_IFINDEX, DOWNSTREAM_MAC, ethIfIndex); 940 coordinator.tetherOffloadRuleAdd(mIpServer, ethernetRuleB); 941 verifyTetherOffloadRuleAdd(inOrder, ethernetRuleB); 942 943 // [2] Update the existing rules from Ethernet to cellular. 944 final Ipv6ForwardingRule mobileRuleA = buildTestForwardingRule( 945 mobileIfIndex, NEIGH_A, MAC_A); 946 final Ipv6ForwardingRule mobileRuleB = buildTestForwardingRule( 947 mobileIfIndex, NEIGH_B, MAC_B); 948 updateStatsEntryForTetherOffloadGetAndClearStats( 949 buildTestTetherStatsParcel(ethIfIndex, 10, 20, 30, 40)); 950 951 // Update the existing rules for upstream changes. The rules are removed and re-added one 952 // by one for updating upstream interface index by #tetherOffloadRuleUpdate. 953 coordinator.tetherOffloadRuleUpdate(mIpServer, mobileIfIndex); 954 verifyTetherOffloadRuleRemove(inOrder, ethernetRuleA); 955 verifyTetherOffloadRuleRemove(inOrder, ethernetRuleB); 956 verifyStopUpstreamIpv6Forwarding(inOrder, DOWNSTREAM_IFINDEX, DOWNSTREAM_MAC); 957 verifyTetherOffloadGetAndClearStats(inOrder, ethIfIndex); 958 verifyTetherOffloadRuleAdd(inOrder, mobileRuleA); 959 verifyTetherOffloadSetInterfaceQuota(inOrder, mobileIfIndex, QUOTA_UNLIMITED, 960 true /* isInit */); 961 verifyStartUpstreamIpv6Forwarding(inOrder, DOWNSTREAM_IFINDEX, DOWNSTREAM_MAC, 962 mobileIfIndex); 963 verifyTetherOffloadRuleAdd(inOrder, mobileRuleB); 964 965 // [3] Clear all rules for a given IpServer. 966 updateStatsEntryForTetherOffloadGetAndClearStats( 967 buildTestTetherStatsParcel(mobileIfIndex, 50, 60, 70, 80)); 968 coordinator.tetherOffloadRuleClear(mIpServer); 969 verifyTetherOffloadRuleRemove(inOrder, mobileRuleA); 970 verifyTetherOffloadRuleRemove(inOrder, mobileRuleB); 971 verifyStopUpstreamIpv6Forwarding(inOrder, DOWNSTREAM_IFINDEX, DOWNSTREAM_MAC); 972 verifyTetherOffloadGetAndClearStats(inOrder, mobileIfIndex); 973 974 // [4] Force pushing stats update to verify that the last diff of stats is reported on all 975 // upstreams. 976 mTetherStatsProvider.pushTetherStats(); 977 mTetherStatsProviderCb.expectNotifyStatsUpdated( 978 new NetworkStats(0L, 2) 979 .addEntry(buildTestEntry(STATS_PER_IFACE, ethIface, 10, 20, 30, 40)) 980 .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 50, 60, 70, 80)), 981 new NetworkStats(0L, 2) 982 .addEntry(buildTestEntry(STATS_PER_UID, ethIface, 10, 20, 30, 40)) 983 .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 50, 60, 70, 80))); 984 } 985 checkBpfDisabled()986 private void checkBpfDisabled() throws Exception { 987 // The caller may mock the global dependencies |mDeps| which is used in 988 // #makeBpfCoordinator for testing. 989 // See #testBpfDisabledbyNoBpfDownstream6Map. 990 final BpfCoordinator coordinator = makeBpfCoordinator(); 991 coordinator.startPolling(); 992 993 // The tether stats polling task should not be scheduled. 994 mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); 995 waitForIdle(); 996 verifyNeverTetherOffloadGetStats(); 997 998 // The interface name lookup table can't be added. 999 final String iface = "rmnet_data0"; 1000 final Integer ifIndex = 100; 1001 coordinator.addUpstreamNameToLookupTable(ifIndex, iface); 1002 assertEquals(0, coordinator.getInterfaceNamesForTesting().size()); 1003 1004 // The rule can't be added. 1005 final InetAddress neigh = InetAddresses.parseNumericAddress("2001:db8::1"); 1006 final MacAddress mac = MacAddress.fromString("00:00:00:00:00:0a"); 1007 final Ipv6ForwardingRule rule = buildTestForwardingRule(ifIndex, neigh, mac); 1008 coordinator.tetherOffloadRuleAdd(mIpServer, rule); 1009 verifyNeverTetherOffloadRuleAdd(); 1010 LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules = 1011 coordinator.getForwardingRulesForTesting().get(mIpServer); 1012 assertNull(rules); 1013 1014 // The rule can't be removed. This is not a realistic case because adding rule is not 1015 // allowed. That implies no rule could be removed, cleared or updated. Verify these 1016 // cases just in case. 1017 rules = new LinkedHashMap<Inet6Address, Ipv6ForwardingRule>(); 1018 rules.put(rule.address, rule); 1019 coordinator.getForwardingRulesForTesting().put(mIpServer, rules); 1020 coordinator.tetherOffloadRuleRemove(mIpServer, rule); 1021 verifyNeverTetherOffloadRuleRemove(); 1022 rules = coordinator.getForwardingRulesForTesting().get(mIpServer); 1023 assertNotNull(rules); 1024 assertEquals(1, rules.size()); 1025 1026 // The rule can't be cleared. 1027 coordinator.tetherOffloadRuleClear(mIpServer); 1028 verifyNeverTetherOffloadRuleRemove(); 1029 rules = coordinator.getForwardingRulesForTesting().get(mIpServer); 1030 assertNotNull(rules); 1031 assertEquals(1, rules.size()); 1032 1033 // The rule can't be updated. 1034 coordinator.tetherOffloadRuleUpdate(mIpServer, rule.upstreamIfindex + 1 /* new */); 1035 verifyNeverTetherOffloadRuleRemove(); 1036 verifyNeverTetherOffloadRuleAdd(); 1037 rules = coordinator.getForwardingRulesForTesting().get(mIpServer); 1038 assertNotNull(rules); 1039 assertEquals(1, rules.size()); 1040 } 1041 1042 @Test testBpfDisabledbyConfig()1043 public void testBpfDisabledbyConfig() throws Exception { 1044 setupFunctioningNetdInterface(); 1045 when(mTetherConfig.isBpfOffloadEnabled()).thenReturn(false); 1046 1047 checkBpfDisabled(); 1048 } 1049 1050 @Test 1051 @IgnoreUpTo(Build.VERSION_CODES.R) testBpfDisabledbyNoBpfDownstream6Map()1052 public void testBpfDisabledbyNoBpfDownstream6Map() throws Exception { 1053 setupFunctioningNetdInterface(); 1054 doReturn(null).when(mDeps).getBpfDownstream6Map(); 1055 1056 checkBpfDisabled(); 1057 } 1058 1059 @Test 1060 @IgnoreUpTo(Build.VERSION_CODES.R) testBpfDisabledbyNoBpfUpstream6Map()1061 public void testBpfDisabledbyNoBpfUpstream6Map() throws Exception { 1062 setupFunctioningNetdInterface(); 1063 doReturn(null).when(mDeps).getBpfUpstream6Map(); 1064 1065 checkBpfDisabled(); 1066 } 1067 1068 @Test 1069 @IgnoreUpTo(Build.VERSION_CODES.R) testBpfDisabledbyNoBpfDownstream4Map()1070 public void testBpfDisabledbyNoBpfDownstream4Map() throws Exception { 1071 setupFunctioningNetdInterface(); 1072 doReturn(null).when(mDeps).getBpfDownstream4Map(); 1073 1074 checkBpfDisabled(); 1075 } 1076 1077 @Test 1078 @IgnoreUpTo(Build.VERSION_CODES.R) testBpfDisabledbyNoBpfUpstream4Map()1079 public void testBpfDisabledbyNoBpfUpstream4Map() throws Exception { 1080 setupFunctioningNetdInterface(); 1081 doReturn(null).when(mDeps).getBpfUpstream4Map(); 1082 1083 checkBpfDisabled(); 1084 } 1085 1086 @Test 1087 @IgnoreUpTo(Build.VERSION_CODES.R) testBpfDisabledbyNoBpfStatsMap()1088 public void testBpfDisabledbyNoBpfStatsMap() throws Exception { 1089 setupFunctioningNetdInterface(); 1090 doReturn(null).when(mDeps).getBpfStatsMap(); 1091 1092 checkBpfDisabled(); 1093 } 1094 1095 @Test 1096 @IgnoreUpTo(Build.VERSION_CODES.R) testBpfDisabledbyNoBpfLimitMap()1097 public void testBpfDisabledbyNoBpfLimitMap() throws Exception { 1098 setupFunctioningNetdInterface(); 1099 doReturn(null).when(mDeps).getBpfLimitMap(); 1100 1101 checkBpfDisabled(); 1102 } 1103 1104 @Test 1105 @IgnoreUpTo(Build.VERSION_CODES.R) testBpfMapClear()1106 public void testBpfMapClear() throws Exception { 1107 setupFunctioningNetdInterface(); 1108 1109 final BpfCoordinator coordinator = makeBpfCoordinator(); 1110 verify(mBpfDownstream4Map).clear(); 1111 verify(mBpfUpstream4Map).clear(); 1112 verify(mBpfDownstream6Map).clear(); 1113 verify(mBpfUpstream6Map).clear(); 1114 verify(mBpfStatsMap).clear(); 1115 verify(mBpfLimitMap).clear(); 1116 } 1117 1118 @Test 1119 @IgnoreUpTo(Build.VERSION_CODES.R) testAttachDetachBpfProgram()1120 public void testAttachDetachBpfProgram() throws Exception { 1121 setupFunctioningNetdInterface(); 1122 1123 // Static mocking for BpfUtils. 1124 MockitoSession mockSession = ExtendedMockito.mockitoSession() 1125 .mockStatic(BpfUtils.class) 1126 .startMocking(); 1127 try { 1128 final String intIface1 = "wlan1"; 1129 final String intIface2 = "rndis0"; 1130 final String extIface = "rmnet_data0"; 1131 final String virtualIface = "ipsec0"; 1132 final BpfUtils mockMarkerBpfUtils = staticMockMarker(BpfUtils.class); 1133 final BpfCoordinator coordinator = makeBpfCoordinator(); 1134 1135 // [1] Add the forwarding pair <wlan1, rmnet_data0>. Expect that attach both wlan1 and 1136 // rmnet_data0. 1137 coordinator.maybeAttachProgram(intIface1, extIface); 1138 ExtendedMockito.verify(() -> BpfUtils.attachProgram(extIface, DOWNSTREAM)); 1139 ExtendedMockito.verify(() -> BpfUtils.attachProgram(intIface1, UPSTREAM)); 1140 ExtendedMockito.verifyNoMoreInteractions(mockMarkerBpfUtils); 1141 ExtendedMockito.clearInvocations(mockMarkerBpfUtils); 1142 1143 // [2] Add the forwarding pair <wlan1, rmnet_data0> again. Expect no more action. 1144 coordinator.maybeAttachProgram(intIface1, extIface); 1145 ExtendedMockito.verifyNoMoreInteractions(mockMarkerBpfUtils); 1146 ExtendedMockito.clearInvocations(mockMarkerBpfUtils); 1147 1148 // [3] Add the forwarding pair <rndis0, rmnet_data0>. Expect that attach rndis0 only. 1149 coordinator.maybeAttachProgram(intIface2, extIface); 1150 ExtendedMockito.verify(() -> BpfUtils.attachProgram(intIface2, UPSTREAM)); 1151 ExtendedMockito.verifyNoMoreInteractions(mockMarkerBpfUtils); 1152 ExtendedMockito.clearInvocations(mockMarkerBpfUtils); 1153 1154 // [4] Remove the forwarding pair <rndis0, rmnet_data0>. Expect detach rndis0 only. 1155 coordinator.maybeDetachProgram(intIface2, extIface); 1156 ExtendedMockito.verify(() -> BpfUtils.detachProgram(intIface2)); 1157 ExtendedMockito.verifyNoMoreInteractions(mockMarkerBpfUtils); 1158 ExtendedMockito.clearInvocations(mockMarkerBpfUtils); 1159 1160 // [5] Remove the forwarding pair <wlan1, rmnet_data0>. Expect that detach both wlan1 1161 // and rmnet_data0. 1162 coordinator.maybeDetachProgram(intIface1, extIface); 1163 ExtendedMockito.verify(() -> BpfUtils.detachProgram(extIface)); 1164 ExtendedMockito.verify(() -> BpfUtils.detachProgram(intIface1)); 1165 ExtendedMockito.verifyNoMoreInteractions(mockMarkerBpfUtils); 1166 ExtendedMockito.clearInvocations(mockMarkerBpfUtils); 1167 1168 // [6] Skip attaching if upstream is virtual interface. 1169 coordinator.maybeAttachProgram(intIface1, virtualIface); 1170 ExtendedMockito.verify(() -> BpfUtils.attachProgram(extIface, DOWNSTREAM), never()); 1171 ExtendedMockito.verify(() -> BpfUtils.attachProgram(intIface1, UPSTREAM), never()); 1172 ExtendedMockito.verifyNoMoreInteractions(mockMarkerBpfUtils); 1173 ExtendedMockito.clearInvocations(mockMarkerBpfUtils); 1174 1175 } finally { 1176 mockSession.finishMocking(); 1177 } 1178 } 1179 1180 @Test testTetheringConfigSetPollingInterval()1181 public void testTetheringConfigSetPollingInterval() throws Exception { 1182 setupFunctioningNetdInterface(); 1183 1184 final BpfCoordinator coordinator = makeBpfCoordinator(); 1185 1186 // [1] The default polling interval. 1187 coordinator.startPolling(); 1188 assertEquals(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS, coordinator.getPollingInterval()); 1189 coordinator.stopPolling(); 1190 1191 // [2] Expect the invalid polling interval isn't applied. The valid range of interval is 1192 // DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS..max_long. 1193 for (final int interval 1194 : new int[] {0, 100, DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS - 1}) { 1195 when(mTetherConfig.getOffloadPollInterval()).thenReturn(interval); 1196 coordinator.startPolling(); 1197 assertEquals(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS, coordinator.getPollingInterval()); 1198 coordinator.stopPolling(); 1199 } 1200 1201 // [3] Set a specific polling interval which is larger than default value. 1202 // Use a large polling interval to avoid flaky test because the time forwarding 1203 // approximation is used to verify the scheduled time of the polling thread. 1204 final int pollingInterval = 100_000; 1205 when(mTetherConfig.getOffloadPollInterval()).thenReturn(pollingInterval); 1206 coordinator.startPolling(); 1207 1208 // Expect the specific polling interval to be applied. 1209 assertEquals(pollingInterval, coordinator.getPollingInterval()); 1210 1211 // Start on a new polling time slot. 1212 mTestLooper.moveTimeForward(pollingInterval); 1213 waitForIdle(); 1214 clearStatsInvocations(); 1215 1216 // Move time forward to 90% polling interval time. Expect that the polling thread has not 1217 // scheduled yet. 1218 mTestLooper.moveTimeForward((long) (pollingInterval * 0.9)); 1219 waitForIdle(); 1220 verifyNeverTetherOffloadGetStats(); 1221 1222 // Move time forward to the remaining 10% polling interval time. Expect that the polling 1223 // thread has scheduled. 1224 mTestLooper.moveTimeForward((long) (pollingInterval * 0.1)); 1225 waitForIdle(); 1226 verifyTetherOffloadGetStats(); 1227 } 1228 1229 @Test 1230 @IgnoreUpTo(Build.VERSION_CODES.R) testStartStopConntrackMonitoring()1231 public void testStartStopConntrackMonitoring() throws Exception { 1232 setupFunctioningNetdInterface(); 1233 1234 final BpfCoordinator coordinator = makeBpfCoordinator(); 1235 1236 // [1] Don't stop monitoring if it has never started. 1237 coordinator.stopMonitoring(mIpServer); 1238 verify(mConntrackMonitor, never()).start(); 1239 1240 // [2] Start monitoring. 1241 coordinator.startMonitoring(mIpServer); 1242 verify(mConntrackMonitor).start(); 1243 clearInvocations(mConntrackMonitor); 1244 1245 // [3] Stop monitoring. 1246 coordinator.stopMonitoring(mIpServer); 1247 verify(mConntrackMonitor).stop(); 1248 } 1249 1250 @Test 1251 @IgnoreUpTo(Build.VERSION_CODES.Q) 1252 @IgnoreAfter(Build.VERSION_CODES.R) 1253 // Only run this test on Android R. testStartStopConntrackMonitoring_R()1254 public void testStartStopConntrackMonitoring_R() throws Exception { 1255 setupFunctioningNetdInterface(); 1256 1257 final BpfCoordinator coordinator = makeBpfCoordinator(); 1258 1259 coordinator.startMonitoring(mIpServer); 1260 verify(mConntrackMonitor, never()).start(); 1261 1262 coordinator.stopMonitoring(mIpServer); 1263 verify(mConntrackMonitor, never()).stop(); 1264 } 1265 1266 @Test 1267 @IgnoreUpTo(Build.VERSION_CODES.R) testStartStopConntrackMonitoringWithTwoDownstreamIfaces()1268 public void testStartStopConntrackMonitoringWithTwoDownstreamIfaces() throws Exception { 1269 setupFunctioningNetdInterface(); 1270 1271 final BpfCoordinator coordinator = makeBpfCoordinator(); 1272 1273 // [1] Start monitoring at the first IpServer adding. 1274 coordinator.startMonitoring(mIpServer); 1275 verify(mConntrackMonitor).start(); 1276 clearInvocations(mConntrackMonitor); 1277 1278 // [2] Don't start monitoring at the second IpServer adding. 1279 coordinator.startMonitoring(mIpServer2); 1280 verify(mConntrackMonitor, never()).start(); 1281 1282 // [3] Don't stop monitoring if any downstream interface exists. 1283 coordinator.stopMonitoring(mIpServer2); 1284 verify(mConntrackMonitor, never()).stop(); 1285 1286 // [4] Stop monitoring if no downstream exists. 1287 coordinator.stopMonitoring(mIpServer); 1288 verify(mConntrackMonitor).stop(); 1289 } 1290 1291 // Test network topology: 1292 // 1293 // public network (rawip) private network 1294 // | UE | 1295 // +------------+ V +------------+------------+ V +------------+ 1296 // | Sever +---------+ Upstream | Downstream +---------+ Client | 1297 // +------------+ +------------+------------+ +------------+ 1298 // remote ip public ip private ip 1299 // 140.112.8.116:443 100.81.179.1:62449 192.168.80.12:62449 1300 // 1301 private static final Inet4Address REMOTE_ADDR = 1302 (Inet4Address) InetAddresses.parseNumericAddress("140.112.8.116"); 1303 private static final Inet4Address PUBLIC_ADDR = 1304 (Inet4Address) InetAddresses.parseNumericAddress("100.81.179.1"); 1305 private static final Inet4Address PRIVATE_ADDR = 1306 (Inet4Address) InetAddresses.parseNumericAddress("192.168.80.12"); 1307 1308 // IPv4-mapped IPv6 addresses 1309 // Remote addrress ::ffff:140.112.8.116 1310 // Public addrress ::ffff:100.81.179.1 1311 // Private addrress ::ffff:192.168.80.12 1312 private static final byte[] REMOTE_ADDR_V4MAPPED_BYTES = new byte[] { 1313 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, 1314 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xff, (byte) 0xff, 1315 (byte) 0x8c, (byte) 0x70, (byte) 0x08, (byte) 0x74 }; 1316 private static final byte[] PUBLIC_ADDR_V4MAPPED_BYTES = new byte[] { 1317 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, 1318 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xff, (byte) 0xff, 1319 (byte) 0x64, (byte) 0x51, (byte) 0xb3, (byte) 0x01 }; 1320 private static final byte[] PRIVATE_ADDR_V4MAPPED_BYTES = new byte[] { 1321 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, 1322 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xff, (byte) 0xff, 1323 (byte) 0xc0, (byte) 0xa8, (byte) 0x50, (byte) 0x0c }; 1324 1325 // Generally, public port and private port are the same in the NAT conntrack message. 1326 // TODO: consider using different private port and public port for testing. 1327 private static final short REMOTE_PORT = (short) 443; 1328 private static final short PUBLIC_PORT = (short) 62449; 1329 private static final short PRIVATE_PORT = (short) 62449; 1330 1331 @NonNull makeUpstream4Key(int proto)1332 private Tether4Key makeUpstream4Key(int proto) { 1333 if (proto != IPPROTO_TCP && proto != IPPROTO_UDP) { 1334 fail("Not support protocol " + proto); 1335 } 1336 return new Tether4Key(DOWNSTREAM_IFINDEX, DOWNSTREAM_MAC, (short) proto, 1337 PRIVATE_ADDR.getAddress(), REMOTE_ADDR.getAddress(), PRIVATE_PORT, REMOTE_PORT); 1338 } 1339 1340 @NonNull makeDownstream4Key(int proto)1341 private Tether4Key makeDownstream4Key(int proto) { 1342 if (proto != IPPROTO_TCP && proto != IPPROTO_UDP) { 1343 fail("Not support protocol " + proto); 1344 } 1345 return new Tether4Key(UPSTREAM_IFINDEX, 1346 MacAddress.ALL_ZEROS_ADDRESS /* dstMac (rawip) */, (short) proto, 1347 REMOTE_ADDR.getAddress(), PUBLIC_ADDR.getAddress(), REMOTE_PORT, PUBLIC_PORT); 1348 } 1349 1350 @NonNull makeUpstream4Value()1351 private Tether4Value makeUpstream4Value() { 1352 return new Tether4Value(UPSTREAM_IFINDEX, 1353 MacAddress.ALL_ZEROS_ADDRESS /* ethDstMac (rawip) */, 1354 MacAddress.ALL_ZEROS_ADDRESS /* ethSrcMac (rawip) */, ETH_P_IP, 1355 NetworkStackConstants.ETHER_MTU, PUBLIC_ADDR_V4MAPPED_BYTES, 1356 REMOTE_ADDR_V4MAPPED_BYTES, PUBLIC_PORT, REMOTE_PORT, 0 /* lastUsed */); 1357 } 1358 1359 @NonNull makeDownstream4Value()1360 private Tether4Value makeDownstream4Value() { 1361 return new Tether4Value(DOWNSTREAM_IFINDEX, MAC_A /* client mac */, DOWNSTREAM_MAC, 1362 ETH_P_IP, NetworkStackConstants.ETHER_MTU, REMOTE_ADDR_V4MAPPED_BYTES, 1363 PRIVATE_ADDR_V4MAPPED_BYTES, REMOTE_PORT, PRIVATE_PORT, 0 /* lastUsed */); 1364 } 1365 1366 @NonNull makeDownstream4Key()1367 private Tether4Key makeDownstream4Key() { 1368 return makeDownstream4Key(IPPROTO_TCP); 1369 } 1370 1371 @NonNull makeTestConntrackEvent(short msgType, int proto)1372 private ConntrackEvent makeTestConntrackEvent(short msgType, int proto) { 1373 if (msgType != IPCTNL_MSG_CT_NEW && msgType != IPCTNL_MSG_CT_DELETE) { 1374 fail("Not support message type " + msgType); 1375 } 1376 if (proto != IPPROTO_TCP && proto != IPPROTO_UDP) { 1377 fail("Not support protocol " + proto); 1378 } 1379 1380 final int status = (msgType == IPCTNL_MSG_CT_NEW) ? ESTABLISHED_MASK : DYING_MASK; 1381 final int timeoutSec = (msgType == IPCTNL_MSG_CT_NEW) ? 100 /* nonzero, new */ 1382 : 0 /* unused, delete */; 1383 return new ConntrackEvent( 1384 (short) (NetlinkConstants.NFNL_SUBSYS_CTNETLINK << 8 | msgType), 1385 new Tuple(new TupleIpv4(PRIVATE_ADDR, REMOTE_ADDR), 1386 new TupleProto((byte) proto, PRIVATE_PORT, REMOTE_PORT)), 1387 new Tuple(new TupleIpv4(REMOTE_ADDR, PUBLIC_ADDR), 1388 new TupleProto((byte) proto, REMOTE_PORT, PUBLIC_PORT)), 1389 status, 1390 timeoutSec); 1391 } 1392 setUpstreamInformationTo(final BpfCoordinator coordinator)1393 private void setUpstreamInformationTo(final BpfCoordinator coordinator) { 1394 final LinkProperties lp = new LinkProperties(); 1395 lp.setInterfaceName(UPSTREAM_IFACE); 1396 lp.addLinkAddress(new LinkAddress(PUBLIC_ADDR, 32 /* prefix length */)); 1397 final NetworkCapabilities capabilities = new NetworkCapabilities() 1398 .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); 1399 coordinator.updateUpstreamNetworkState(new UpstreamNetworkState(lp, capabilities, 1400 new Network(TEST_NET_ID))); 1401 } 1402 setDownstreamAndClientInformationTo(final BpfCoordinator coordinator)1403 private void setDownstreamAndClientInformationTo(final BpfCoordinator coordinator) { 1404 final ClientInfo clientInfo = new ClientInfo(DOWNSTREAM_IFINDEX, DOWNSTREAM_MAC, 1405 PRIVATE_ADDR, MAC_A /* client mac */); 1406 coordinator.tetherOffloadClientAdd(mIpServer, clientInfo); 1407 } 1408 initBpfCoordinatorForRule4(final BpfCoordinator coordinator)1409 private void initBpfCoordinatorForRule4(final BpfCoordinator coordinator) throws Exception { 1410 // Needed because addUpstreamIfindexToMap only updates upstream information when polling 1411 // was started. 1412 coordinator.startPolling(); 1413 1414 // Needed because two reasons: (1) BpfConntrackEventConsumer#accept only performs cleanup 1415 // when both upstream and downstream rules are removed. (2) tetherOffloadRuleRemove of 1416 // api31.BpfCoordinatorShimImpl only decreases the count while the entry is deleted. 1417 // In the other words, deleteEntry returns true. 1418 doReturn(true).when(mBpfUpstream4Map).deleteEntry(any()); 1419 doReturn(true).when(mBpfDownstream4Map).deleteEntry(any()); 1420 1421 // Needed because BpfCoordinator#addUpstreamIfindexToMap queries interface parameter for 1422 // interface index. 1423 doReturn(UPSTREAM_IFACE_PARAMS).when(mDeps).getInterfaceParams(UPSTREAM_IFACE); 1424 1425 coordinator.addUpstreamNameToLookupTable(UPSTREAM_IFINDEX, UPSTREAM_IFACE); 1426 setUpstreamInformationTo(coordinator); 1427 setDownstreamAndClientInformationTo(coordinator); 1428 } 1429 1430 // TODO: Test the IPv4 and IPv6 exist concurrently. 1431 // TODO: Test the IPv4 rule delete failed. 1432 @Test 1433 @IgnoreUpTo(Build.VERSION_CODES.R) testSetDataLimitOnRule4Change()1434 public void testSetDataLimitOnRule4Change() throws Exception { 1435 final BpfCoordinator coordinator = makeBpfCoordinator(); 1436 initBpfCoordinatorForRule4(coordinator); 1437 1438 // Applying a data limit to the current upstream does not take any immediate action. 1439 // The data limit could be only set on an upstream which has rules. 1440 final long limit = 12345; 1441 final InOrder inOrder = inOrder(mNetd, mBpfUpstream4Map, mBpfDownstream4Map, mBpfLimitMap, 1442 mBpfStatsMap); 1443 mTetherStatsProvider.onSetLimit(UPSTREAM_IFACE, limit); 1444 waitForIdle(); 1445 verifyNeverTetherOffloadSetInterfaceQuota(inOrder); 1446 1447 // Build TCP and UDP rules for testing. Note that the values of {TCP, UDP} are the same 1448 // because the protocol is not an element of the value. Consider using different address 1449 // or port to make them different for better testing. 1450 // TODO: Make the values of {TCP, UDP} rules different. 1451 final Tether4Key expectedUpstream4KeyTcp = makeUpstream4Key(IPPROTO_TCP); 1452 final Tether4Key expectedDownstream4KeyTcp = makeDownstream4Key(IPPROTO_TCP); 1453 final Tether4Value expectedUpstream4ValueTcp = makeUpstream4Value(); 1454 final Tether4Value expectedDownstream4ValueTcp = makeDownstream4Value(); 1455 1456 final Tether4Key expectedUpstream4KeyUdp = makeUpstream4Key(IPPROTO_UDP); 1457 final Tether4Key expectedDownstream4KeyUdp = makeDownstream4Key(IPPROTO_UDP); 1458 final Tether4Value expectedUpstream4ValueUdp = makeUpstream4Value(); 1459 final Tether4Value expectedDownstream4ValueUdp = makeDownstream4Value(); 1460 1461 // [1] Adding the first rule on current upstream immediately sends the quota. 1462 mConsumer.accept(makeTestConntrackEvent(IPCTNL_MSG_CT_NEW, IPPROTO_TCP)); 1463 verifyTetherOffloadSetInterfaceQuota(inOrder, UPSTREAM_IFINDEX, limit, true /* isInit */); 1464 inOrder.verify(mBpfUpstream4Map) 1465 .insertEntry(eq(expectedUpstream4KeyTcp), eq(expectedUpstream4ValueTcp)); 1466 inOrder.verify(mBpfDownstream4Map) 1467 .insertEntry(eq(expectedDownstream4KeyTcp), eq(expectedDownstream4ValueTcp)); 1468 inOrder.verifyNoMoreInteractions(); 1469 1470 // [2] Adding the second rule on current upstream does not send the quota. 1471 mConsumer.accept(makeTestConntrackEvent(IPCTNL_MSG_CT_NEW, IPPROTO_UDP)); 1472 verifyNeverTetherOffloadSetInterfaceQuota(inOrder); 1473 inOrder.verify(mBpfUpstream4Map) 1474 .insertEntry(eq(expectedUpstream4KeyUdp), eq(expectedUpstream4ValueUdp)); 1475 inOrder.verify(mBpfDownstream4Map) 1476 .insertEntry(eq(expectedDownstream4KeyUdp), eq(expectedDownstream4ValueUdp)); 1477 inOrder.verifyNoMoreInteractions(); 1478 1479 // [3] Removing the second rule on current upstream does not send the quota. 1480 mConsumer.accept(makeTestConntrackEvent(IPCTNL_MSG_CT_DELETE, IPPROTO_UDP)); 1481 verifyNeverTetherOffloadSetInterfaceQuota(inOrder); 1482 inOrder.verify(mBpfUpstream4Map).deleteEntry(eq(expectedUpstream4KeyUdp)); 1483 inOrder.verify(mBpfDownstream4Map).deleteEntry(eq(expectedDownstream4KeyUdp)); 1484 inOrder.verifyNoMoreInteractions(); 1485 1486 // [4] Removing the last rule on current upstream immediately sends the cleanup stuff. 1487 updateStatsEntryForTetherOffloadGetAndClearStats( 1488 buildTestTetherStatsParcel(UPSTREAM_IFINDEX, 0, 0, 0, 0)); 1489 mConsumer.accept(makeTestConntrackEvent(IPCTNL_MSG_CT_DELETE, IPPROTO_TCP)); 1490 inOrder.verify(mBpfUpstream4Map).deleteEntry(eq(expectedUpstream4KeyTcp)); 1491 inOrder.verify(mBpfDownstream4Map).deleteEntry(eq(expectedDownstream4KeyTcp)); 1492 verifyTetherOffloadGetAndClearStats(inOrder, UPSTREAM_IFINDEX); 1493 inOrder.verifyNoMoreInteractions(); 1494 } 1495 1496 @Test 1497 @IgnoreUpTo(Build.VERSION_CODES.R) testAddDevMapRule6()1498 public void testAddDevMapRule6() throws Exception { 1499 final BpfCoordinator coordinator = makeBpfCoordinator(); 1500 1501 coordinator.addUpstreamNameToLookupTable(UPSTREAM_IFINDEX, UPSTREAM_IFACE); 1502 final Ipv6ForwardingRule ruleA = buildTestForwardingRule(UPSTREAM_IFINDEX, NEIGH_A, MAC_A); 1503 final Ipv6ForwardingRule ruleB = buildTestForwardingRule(UPSTREAM_IFINDEX, NEIGH_B, MAC_B); 1504 1505 coordinator.tetherOffloadRuleAdd(mIpServer, ruleA); 1506 verify(mBpfDevMap).updateEntry(eq(new TetherDevKey(UPSTREAM_IFINDEX)), 1507 eq(new TetherDevValue(UPSTREAM_IFINDEX))); 1508 verify(mBpfDevMap).updateEntry(eq(new TetherDevKey(DOWNSTREAM_IFINDEX)), 1509 eq(new TetherDevValue(DOWNSTREAM_IFINDEX))); 1510 clearInvocations(mBpfDevMap); 1511 1512 coordinator.tetherOffloadRuleAdd(mIpServer, ruleB); 1513 verify(mBpfDevMap, never()).updateEntry(any(), any()); 1514 } 1515 1516 @Test 1517 @IgnoreUpTo(Build.VERSION_CODES.R) testAddDevMapRule4()1518 public void testAddDevMapRule4() throws Exception { 1519 final BpfCoordinator coordinator = makeBpfCoordinator(); 1520 initBpfCoordinatorForRule4(coordinator); 1521 1522 mConsumer.accept(makeTestConntrackEvent(IPCTNL_MSG_CT_NEW, IPPROTO_TCP)); 1523 verify(mBpfDevMap).updateEntry(eq(new TetherDevKey(UPSTREAM_IFINDEX)), 1524 eq(new TetherDevValue(UPSTREAM_IFINDEX))); 1525 verify(mBpfDevMap).updateEntry(eq(new TetherDevKey(DOWNSTREAM_IFINDEX)), 1526 eq(new TetherDevValue(DOWNSTREAM_IFINDEX))); 1527 clearInvocations(mBpfDevMap); 1528 1529 mConsumer.accept(makeTestConntrackEvent(IPCTNL_MSG_CT_NEW, IPPROTO_UDP)); 1530 verify(mBpfDevMap, never()).updateEntry(any(), any()); 1531 } 1532 setElapsedRealtimeNanos(long nanoSec)1533 private void setElapsedRealtimeNanos(long nanoSec) { 1534 mElapsedRealtimeNanos = nanoSec; 1535 } 1536 checkRefreshConntrackTimeout(final TestBpfMap<Tether4Key, Tether4Value> bpfMap, final Tether4Key tcpKey, final Tether4Value tcpValue, final Tether4Key udpKey, final Tether4Value udpValue)1537 private void checkRefreshConntrackTimeout(final TestBpfMap<Tether4Key, Tether4Value> bpfMap, 1538 final Tether4Key tcpKey, final Tether4Value tcpValue, final Tether4Key udpKey, 1539 final Tether4Value udpValue) throws Exception { 1540 // Both system elapsed time since boot and the rule last used time are used to measure 1541 // the rule expiration. In this test, all test rules are fixed the last used time to 0. 1542 // Set the different testing elapsed time to make the rule to be valid or expired. 1543 // 1544 // Timeline: 1545 // 0 60 (seconds) 1546 // +---+---+---+---+--...--+---+---+---+---+---+- .. 1547 // | POLLING_CONNTRACK_TIMEOUT_MS | 1548 // +---+---+---+---+--...--+---+---+---+---+---+- .. 1549 // |<- valid diff ->| 1550 // |<- expired diff ->| 1551 // ^ ^ ^ 1552 // last used time elapsed time (valid) elapsed time (expired) 1553 final long validTime = (POLLING_CONNTRACK_TIMEOUT_MS - 1) * 1_000_000L; 1554 final long expiredTime = (POLLING_CONNTRACK_TIMEOUT_MS + 1) * 1_000_000L; 1555 1556 // Static mocking for NetlinkSocket. 1557 MockitoSession mockSession = ExtendedMockito.mockitoSession() 1558 .mockStatic(NetlinkSocket.class) 1559 .startMocking(); 1560 try { 1561 final BpfCoordinator coordinator = makeBpfCoordinator(); 1562 coordinator.startPolling(); 1563 bpfMap.insertEntry(tcpKey, tcpValue); 1564 bpfMap.insertEntry(udpKey, udpValue); 1565 1566 // [1] Don't refresh contrack timeout. 1567 setElapsedRealtimeNanos(expiredTime); 1568 mTestLooper.moveTimeForward(POLLING_CONNTRACK_TIMEOUT_MS); 1569 waitForIdle(); 1570 ExtendedMockito.verifyNoMoreInteractions(staticMockMarker(NetlinkSocket.class)); 1571 ExtendedMockito.clearInvocations(staticMockMarker(NetlinkSocket.class)); 1572 1573 // [2] Refresh contrack timeout. 1574 setElapsedRealtimeNanos(validTime); 1575 mTestLooper.moveTimeForward(POLLING_CONNTRACK_TIMEOUT_MS); 1576 waitForIdle(); 1577 final byte[] expectedNetlinkTcp = ConntrackMessage.newIPv4TimeoutUpdateRequest( 1578 IPPROTO_TCP, PRIVATE_ADDR, (int) PRIVATE_PORT, REMOTE_ADDR, 1579 (int) REMOTE_PORT, NF_CONNTRACK_TCP_TIMEOUT_ESTABLISHED); 1580 final byte[] expectedNetlinkUdp = ConntrackMessage.newIPv4TimeoutUpdateRequest( 1581 IPPROTO_UDP, PRIVATE_ADDR, (int) PRIVATE_PORT, REMOTE_ADDR, 1582 (int) REMOTE_PORT, NF_CONNTRACK_UDP_TIMEOUT_STREAM); 1583 ExtendedMockito.verify(() -> NetlinkSocket.sendOneShotKernelMessage( 1584 eq(NETLINK_NETFILTER), eq(expectedNetlinkTcp))); 1585 ExtendedMockito.verify(() -> NetlinkSocket.sendOneShotKernelMessage( 1586 eq(NETLINK_NETFILTER), eq(expectedNetlinkUdp))); 1587 ExtendedMockito.verifyNoMoreInteractions(staticMockMarker(NetlinkSocket.class)); 1588 ExtendedMockito.clearInvocations(staticMockMarker(NetlinkSocket.class)); 1589 1590 // [3] Don't refresh contrack timeout if polling stopped. 1591 coordinator.stopPolling(); 1592 mTestLooper.moveTimeForward(POLLING_CONNTRACK_TIMEOUT_MS); 1593 waitForIdle(); 1594 ExtendedMockito.verifyNoMoreInteractions(staticMockMarker(NetlinkSocket.class)); 1595 ExtendedMockito.clearInvocations(staticMockMarker(NetlinkSocket.class)); 1596 } finally { 1597 mockSession.finishMocking(); 1598 } 1599 } 1600 1601 @Test 1602 @IgnoreUpTo(Build.VERSION_CODES.R) testRefreshConntrackTimeout_Upstream4Map()1603 public void testRefreshConntrackTimeout_Upstream4Map() throws Exception { 1604 // TODO: Replace the dependencies BPF map with a non-mocked TestBpfMap object. 1605 final TestBpfMap<Tether4Key, Tether4Value> bpfUpstream4Map = 1606 new TestBpfMap<>(Tether4Key.class, Tether4Value.class); 1607 doReturn(bpfUpstream4Map).when(mDeps).getBpfUpstream4Map(); 1608 1609 final Tether4Key tcpKey = makeUpstream4Key(IPPROTO_TCP); 1610 final Tether4Key udpKey = makeUpstream4Key(IPPROTO_UDP); 1611 final Tether4Value tcpValue = makeUpstream4Value(); 1612 final Tether4Value udpValue = makeUpstream4Value(); 1613 1614 checkRefreshConntrackTimeout(bpfUpstream4Map, tcpKey, tcpValue, udpKey, udpValue); 1615 } 1616 1617 @Test 1618 @IgnoreUpTo(Build.VERSION_CODES.R) testRefreshConntrackTimeout_Downstream4Map()1619 public void testRefreshConntrackTimeout_Downstream4Map() throws Exception { 1620 // TODO: Replace the dependencies BPF map with a non-mocked TestBpfMap object. 1621 final TestBpfMap<Tether4Key, Tether4Value> bpfDownstream4Map = 1622 new TestBpfMap<>(Tether4Key.class, Tether4Value.class); 1623 doReturn(bpfDownstream4Map).when(mDeps).getBpfDownstream4Map(); 1624 1625 final Tether4Key tcpKey = makeDownstream4Key(IPPROTO_TCP); 1626 final Tether4Key udpKey = makeDownstream4Key(IPPROTO_UDP); 1627 final Tether4Value tcpValue = makeDownstream4Value(); 1628 final Tether4Value udpValue = makeDownstream4Value(); 1629 1630 checkRefreshConntrackTimeout(bpfDownstream4Map, tcpKey, tcpValue, udpKey, udpValue); 1631 } 1632 } 1633