• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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