• 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.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED;
28 import static android.system.OsConstants.ETH_P_IP;
29 import static android.system.OsConstants.ETH_P_IPV6;
30 
31 import static com.android.networkstack.tethering.BpfUtils.DOWNSTREAM;
32 import static com.android.networkstack.tethering.BpfUtils.UPSTREAM;
33 import static com.android.networkstack.tethering.TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS;
34 import static com.android.networkstack.tethering.UpstreamNetworkState.isVcnInterface;
35 import static com.android.networkstack.tethering.util.TetheringUtils.getTetheringJniLibraryName;
36 
37 import android.app.usage.NetworkStatsManager;
38 import android.net.INetd;
39 import android.net.MacAddress;
40 import android.net.NetworkStats;
41 import android.net.NetworkStats.Entry;
42 import android.net.TetherOffloadRuleParcel;
43 import android.net.ip.ConntrackMonitor;
44 import android.net.ip.ConntrackMonitor.ConntrackEventConsumer;
45 import android.net.ip.IpServer;
46 import android.net.netstats.provider.NetworkStatsProvider;
47 import android.net.util.SharedLog;
48 import android.os.Handler;
49 import android.os.SystemClock;
50 import android.system.ErrnoException;
51 import android.system.OsConstants;
52 import android.text.TextUtils;
53 import android.util.ArraySet;
54 import android.util.Base64;
55 import android.util.Log;
56 import android.util.SparseArray;
57 
58 import androidx.annotation.NonNull;
59 import androidx.annotation.Nullable;
60 
61 import com.android.internal.annotations.VisibleForTesting;
62 import com.android.internal.util.IndentingPrintWriter;
63 import com.android.modules.utils.build.SdkLevel;
64 import com.android.net.module.util.BpfMap;
65 import com.android.net.module.util.CollectionUtils;
66 import com.android.net.module.util.InterfaceParams;
67 import com.android.net.module.util.NetworkStackConstants;
68 import com.android.net.module.util.Struct;
69 import com.android.net.module.util.Struct.U32;
70 import com.android.net.module.util.bpf.Tether4Key;
71 import com.android.net.module.util.bpf.Tether4Value;
72 import com.android.net.module.util.bpf.TetherStatsKey;
73 import com.android.net.module.util.bpf.TetherStatsValue;
74 import com.android.net.module.util.netlink.ConntrackMessage;
75 import com.android.net.module.util.netlink.NetlinkConstants;
76 import com.android.net.module.util.netlink.NetlinkSocket;
77 import com.android.networkstack.tethering.apishim.common.BpfCoordinatorShim;
78 import com.android.networkstack.tethering.util.TetheringUtils.ForwardedStats;
79 
80 import java.io.IOException;
81 import java.net.Inet4Address;
82 import java.net.Inet6Address;
83 import java.net.InetAddress;
84 import java.net.UnknownHostException;
85 import java.util.ArrayList;
86 import java.util.Arrays;
87 import java.util.Collection;
88 import java.util.HashMap;
89 import java.util.HashSet;
90 import java.util.LinkedHashMap;
91 import java.util.LinkedHashSet;
92 import java.util.Map;
93 import java.util.Objects;
94 import java.util.Set;
95 
96 /**
97  *  This coordinator is responsible for providing BPF offload relevant functionality.
98  *  - Get tethering stats.
99  *  - Set data limit.
100  *  - Set global alert.
101  *  - Add/remove forwarding rules.
102  *
103  * @hide
104  */
105 public class BpfCoordinator {
106     // Ensure the JNI code is loaded. In production this will already have been loaded by
107     // TetherService, but for tests it needs to be either loaded here or loaded by every test.
108     // TODO: is there a better way?
109     static {
getTetheringJniLibraryName()110         System.loadLibrary(getTetheringJniLibraryName());
111     }
112 
113     private static final String TAG = BpfCoordinator.class.getSimpleName();
114     private static final int DUMP_TIMEOUT_MS = 10_000;
115     private static final MacAddress NULL_MAC_ADDRESS = MacAddress.fromString(
116             "00:00:00:00:00:00");
117     private static final String TETHER_DOWNSTREAM4_MAP_PATH = makeMapPath(DOWNSTREAM, 4);
118     private static final String TETHER_UPSTREAM4_MAP_PATH = makeMapPath(UPSTREAM, 4);
119     private static final String TETHER_DOWNSTREAM6_FS_PATH = makeMapPath(DOWNSTREAM, 6);
120     private static final String TETHER_UPSTREAM6_FS_PATH = makeMapPath(UPSTREAM, 6);
121     private static final String TETHER_STATS_MAP_PATH = makeMapPath("stats");
122     private static final String TETHER_LIMIT_MAP_PATH = makeMapPath("limit");
123     private static final String TETHER_ERROR_MAP_PATH = makeMapPath("error");
124     private static final String TETHER_DEV_MAP_PATH = makeMapPath("dev");
125     private static final String DUMPSYS_RAWMAP_ARG_STATS = "--stats";
126     private static final String DUMPSYS_RAWMAP_ARG_UPSTREAM4 = "--upstream4";
127 
128     // Using "," as a separator is safe because base64 characters are [0-9a-zA-Z/=+].
129     private static final String DUMP_BASE64_DELIMITER = ",";
130 
131     /** The names of all the BPF counters defined in bpf_tethering.h. */
132     public static final String[] sBpfCounterNames = getBpfCounterNames();
133 
makeMapPath(String which)134     private static String makeMapPath(String which) {
135         return "/sys/fs/bpf/tethering/map_offload_tether_" + which + "_map";
136     }
137 
makeMapPath(boolean downstream, int ipVersion)138     private static String makeMapPath(boolean downstream, int ipVersion) {
139         return makeMapPath((downstream ? "downstream" : "upstream") + ipVersion);
140     }
141 
142     @VisibleForTesting
143     static final int CONNTRACK_TIMEOUT_UPDATE_INTERVAL_MS = 60_000;
144     @VisibleForTesting
145     static final int NF_CONNTRACK_TCP_TIMEOUT_ESTABLISHED = 432_000;
146     @VisibleForTesting
147     static final int NF_CONNTRACK_UDP_TIMEOUT_STREAM = 180;
148 
149     // List of TCP port numbers which aren't offloaded because the packets require the netfilter
150     // conntrack helper. See also TetherController::setForwardRules in netd.
151     @VisibleForTesting
152     static final short [] NON_OFFLOADED_UPSTREAM_IPV4_TCP_PORTS = new short [] {
153             21 /* ftp */, 1723 /* pptp */};
154 
155     @VisibleForTesting
156     enum StatsType {
157         STATS_PER_IFACE,
158         STATS_PER_UID,
159     }
160 
161     @NonNull
162     private final Handler mHandler;
163     @NonNull
164     private final INetd mNetd;
165     @NonNull
166     private final SharedLog mLog;
167     @NonNull
168     private final Dependencies mDeps;
169     @NonNull
170     private final ConntrackMonitor mConntrackMonitor;
171     @Nullable
172     private final BpfTetherStatsProvider mStatsProvider;
173     @NonNull
174     private final BpfCoordinatorShim mBpfCoordinatorShim;
175     @NonNull
176     private final BpfConntrackEventConsumer mBpfConntrackEventConsumer;
177 
178     // True if BPF offload is supported, false otherwise. The BPF offload could be disabled by
179     // a runtime resource overlay package or device configuration. This flag is only initialized
180     // in the constructor because it is hard to unwind all existing change once device
181     // configuration is changed. Especially the forwarding rules. Keep the same setting
182     // to make it simpler. See also TetheringConfiguration.
183     private final boolean mIsBpfEnabled;
184 
185     // Tracks whether BPF tethering is started or not. This is set by tethering before it
186     // starts the first IpServer and is cleared by tethering shortly before the last IpServer
187     // is stopped. Note that rule updates (especially deletions, but sometimes additions as
188     // well) may arrive when this is false. If they do, they must be communicated to netd.
189     // Changes in data limits may also arrive when this is false, and if they do, they must
190     // also be communicated to netd.
191     private boolean mPollingStarted = false;
192 
193     // Tracking remaining alert quota. Unlike limit quota is subject to interface, the alert
194     // quota is interface independent and global for tether offload.
195     private long mRemainingAlertQuota = QUOTA_UNLIMITED;
196 
197     // Maps upstream interface index to offloaded traffic statistics.
198     // Always contains the latest total bytes/packets, since each upstream was started, received
199     // from the BPF maps for each interface.
200     private final SparseArray<ForwardedStats> mStats = new SparseArray<>();
201 
202     // Maps upstream interface names to interface quotas.
203     // Always contains the latest value received from the framework for each interface, regardless
204     // of whether offload is currently running (or is even supported) on that interface. Only
205     // includes interfaces that have a quota set. Note that this map is used for storing the quota
206     // which is set from the service. Because the service uses the interface name to present the
207     // interface, this map uses the interface name to be the mapping index.
208     private final HashMap<String, Long> mInterfaceQuotas = new HashMap<>();
209 
210     // Maps upstream interface index to interface names.
211     // Store all interface name since boot. Used for lookup what interface name it is from the
212     // tether stats got from netd because netd reports interface index to present an interface.
213     // TODO: Remove the unused interface name.
214     private final SparseArray<String> mInterfaceNames = new SparseArray<>();
215 
216     // Map of downstream rule maps. Each of these maps represents the IPv6 forwarding rules for a
217     // given downstream. Each map:
218     // - Is owned by the IpServer that is responsible for that downstream.
219     // - Must only be modified by that IpServer.
220     // - Is created when the IpServer adds its first rule, and deleted when the IpServer deletes
221     //   its last rule (or clears its rules).
222     // TODO: Perhaps seal the map and rule operations which communicates with netd into a class.
223     // TODO: Does this need to be a LinkedHashMap or can it just be a HashMap? Also, could it be
224     // a ConcurrentHashMap, in order to avoid the copies in tetherOffloadRuleClear
225     // and tetherOffloadRuleUpdate?
226     // TODO: Perhaps use one-dimensional map and access specific downstream rules via downstream
227     // index. For doing that, IpServer must guarantee that it always has a valid IPv6 downstream
228     // interface index while calling function to clear all rules. IpServer may be calling clear
229     // rules function without a valid IPv6 downstream interface index even if it may have one
230     // before. IpServer would need to call getInterfaceParams() in the constructor instead of when
231     // startIpv6() is called, and make mInterfaceParams final.
232     private final HashMap<IpServer, LinkedHashMap<Inet6Address, Ipv6ForwardingRule>>
233             mIpv6ForwardingRules = new LinkedHashMap<>();
234 
235     // Map of downstream client maps. Each of these maps represents the IPv4 clients for a given
236     // downstream. Needed to build IPv4 forwarding rules when conntrack events are received.
237     // Each map:
238     // - Is owned by the IpServer that is responsible for that downstream.
239     // - Must only be modified by that IpServer.
240     // - Is created when the IpServer adds its first client, and deleted when the IpServer deletes
241     //   its last client.
242     // Note that relying on the client address for finding downstream is okay for now because the
243     // client address is unique. See PrivateAddressCoordinator#requestDownstreamAddress.
244     // TODO: Refactor if any possible that the client address is not unique.
245     private final HashMap<IpServer, HashMap<Inet4Address, ClientInfo>>
246             mTetherClients = new HashMap<>();
247 
248     // Set for which downstream is monitoring the conntrack netlink message.
249     private final Set<IpServer> mMonitoringIpServers = new HashSet<>();
250 
251     // Map of upstream interface IPv4 address to interface index.
252     // TODO: consider making the key to be unique because the upstream address is not unique. It
253     // is okay for now because there have only one upstream generally.
254     private final HashMap<Inet4Address, Integer> mIpv4UpstreamIndices = new HashMap<>();
255 
256     // Map for upstream and downstream pair.
257     private final HashMap<String, HashSet<String>> mForwardingPairs = new HashMap<>();
258 
259     // Set for upstream and downstream device map. Used for caching BPF dev map status and
260     // reduce duplicate adding or removing map operations. Use LinkedHashSet because the test
261     // BpfCoordinatorTest needs predictable iteration order.
262     private final Set<Integer> mDeviceMapSet = new LinkedHashSet<>();
263 
264     // Tracks the last IPv4 upstream index. Support single upstream only.
265     // TODO: Support multi-upstream interfaces.
266     private int mLastIPv4UpstreamIfindex = 0;
267 
268     // Runnable that used by scheduling next polling of stats.
269     private final Runnable mScheduledPollingStats = () -> {
270         updateForwardedStats();
271         maybeSchedulePollingStats();
272     };
273 
274     // Runnable that used by scheduling next refreshing of conntrack timeout.
275     private final Runnable mScheduledConntrackTimeoutUpdate = () -> {
276         refreshAllConntrackTimeouts();
277         maybeScheduleConntrackTimeoutUpdate();
278     };
279 
280     // TODO: add BpfMap<TetherDownstream64Key, TetherDownstream64Value> retrieving function.
281     @VisibleForTesting
282     public abstract static class Dependencies {
283         /** Get handler. */
getHandler()284         @NonNull public abstract Handler getHandler();
285 
286         /** Get netd. */
getNetd()287         @NonNull public abstract INetd getNetd();
288 
289         /** Get network stats manager. */
getNetworkStatsManager()290         @NonNull public abstract NetworkStatsManager getNetworkStatsManager();
291 
292         /** Get shared log. */
getSharedLog()293         @NonNull public abstract SharedLog getSharedLog();
294 
295         /** Get tethering configuration. */
getTetherConfig()296         @Nullable public abstract TetheringConfiguration getTetherConfig();
297 
298         /** Get conntrack monitor. */
getConntrackMonitor(ConntrackEventConsumer consumer)299         @NonNull public ConntrackMonitor getConntrackMonitor(ConntrackEventConsumer consumer) {
300             return new ConntrackMonitor(getHandler(), getSharedLog(), consumer);
301         }
302 
303         /** Get interface information for a given interface. */
getInterfaceParams(String ifName)304         @NonNull public InterfaceParams getInterfaceParams(String ifName) {
305             return InterfaceParams.getByName(ifName);
306         }
307 
308         /**
309          * Represents an estimate of elapsed time since boot in nanoseconds.
310          */
elapsedRealtimeNanos()311         public long elapsedRealtimeNanos() {
312             return SystemClock.elapsedRealtimeNanos();
313         }
314 
315         /**
316          * Check OS Build at least S.
317          *
318          * TODO: move to BpfCoordinatorShim once the test doesn't need the mocked OS build for
319          * testing different code flows concurrently.
320          */
isAtLeastS()321         public boolean isAtLeastS() {
322             return SdkLevel.isAtLeastS();
323         }
324 
325         /** Get downstream4 BPF map. */
getBpfDownstream4Map()326         @Nullable public BpfMap<Tether4Key, Tether4Value> getBpfDownstream4Map() {
327             if (!isAtLeastS()) return null;
328             try {
329                 return new BpfMap<>(TETHER_DOWNSTREAM4_MAP_PATH,
330                     BpfMap.BPF_F_RDWR, Tether4Key.class, Tether4Value.class);
331             } catch (ErrnoException e) {
332                 Log.e(TAG, "Cannot create downstream4 map: " + e);
333                 return null;
334             }
335         }
336 
337         /** Get upstream4 BPF map. */
getBpfUpstream4Map()338         @Nullable public BpfMap<Tether4Key, Tether4Value> getBpfUpstream4Map() {
339             if (!isAtLeastS()) return null;
340             try {
341                 return new BpfMap<>(TETHER_UPSTREAM4_MAP_PATH,
342                     BpfMap.BPF_F_RDWR, Tether4Key.class, Tether4Value.class);
343             } catch (ErrnoException e) {
344                 Log.e(TAG, "Cannot create upstream4 map: " + e);
345                 return null;
346             }
347         }
348 
349         /** Get downstream6 BPF map. */
getBpfDownstream6Map()350         @Nullable public BpfMap<TetherDownstream6Key, Tether6Value> getBpfDownstream6Map() {
351             if (!isAtLeastS()) return null;
352             try {
353                 return new BpfMap<>(TETHER_DOWNSTREAM6_FS_PATH,
354                     BpfMap.BPF_F_RDWR, TetherDownstream6Key.class, Tether6Value.class);
355             } catch (ErrnoException e) {
356                 Log.e(TAG, "Cannot create downstream6 map: " + e);
357                 return null;
358             }
359         }
360 
361         /** Get upstream6 BPF map. */
getBpfUpstream6Map()362         @Nullable public BpfMap<TetherUpstream6Key, Tether6Value> getBpfUpstream6Map() {
363             if (!isAtLeastS()) return null;
364             try {
365                 return new BpfMap<>(TETHER_UPSTREAM6_FS_PATH, BpfMap.BPF_F_RDWR,
366                         TetherUpstream6Key.class, Tether6Value.class);
367             } catch (ErrnoException e) {
368                 Log.e(TAG, "Cannot create upstream6 map: " + e);
369                 return null;
370             }
371         }
372 
373         /** Get stats BPF map. */
getBpfStatsMap()374         @Nullable public BpfMap<TetherStatsKey, TetherStatsValue> getBpfStatsMap() {
375             if (!isAtLeastS()) return null;
376             try {
377                 return new BpfMap<>(TETHER_STATS_MAP_PATH,
378                     BpfMap.BPF_F_RDWR, TetherStatsKey.class, TetherStatsValue.class);
379             } catch (ErrnoException e) {
380                 Log.e(TAG, "Cannot create stats map: " + e);
381                 return null;
382             }
383         }
384 
385         /** Get limit BPF map. */
getBpfLimitMap()386         @Nullable public BpfMap<TetherLimitKey, TetherLimitValue> getBpfLimitMap() {
387             if (!isAtLeastS()) return null;
388             try {
389                 return new BpfMap<>(TETHER_LIMIT_MAP_PATH,
390                     BpfMap.BPF_F_RDWR, TetherLimitKey.class, TetherLimitValue.class);
391             } catch (ErrnoException e) {
392                 Log.e(TAG, "Cannot create limit map: " + e);
393                 return null;
394             }
395         }
396 
397         /** Get dev BPF map. */
getBpfDevMap()398         @Nullable public BpfMap<TetherDevKey, TetherDevValue> getBpfDevMap() {
399             if (!isAtLeastS()) return null;
400             try {
401                 return new BpfMap<>(TETHER_DEV_MAP_PATH,
402                     BpfMap.BPF_F_RDWR, TetherDevKey.class, TetherDevValue.class);
403             } catch (ErrnoException e) {
404                 Log.e(TAG, "Cannot create dev map: " + e);
405                 return null;
406             }
407         }
408     }
409 
410     @VisibleForTesting
BpfCoordinator(@onNull Dependencies deps)411     public BpfCoordinator(@NonNull Dependencies deps) {
412         mDeps = deps;
413         mHandler = mDeps.getHandler();
414         mNetd = mDeps.getNetd();
415         mLog = mDeps.getSharedLog().forSubComponent(TAG);
416         mIsBpfEnabled = isBpfEnabled();
417 
418         // The conntrack consummer needs to be initialized in BpfCoordinator constructor because it
419         // have to access the data members of BpfCoordinator which is not a static class. The
420         // consumer object is also needed for initializing the conntrack monitor which may be
421         // mocked for testing.
422         mBpfConntrackEventConsumer = new BpfConntrackEventConsumer();
423         mConntrackMonitor = mDeps.getConntrackMonitor(mBpfConntrackEventConsumer);
424 
425         BpfTetherStatsProvider provider = new BpfTetherStatsProvider();
426         try {
427             mDeps.getNetworkStatsManager().registerNetworkStatsProvider(
428                     getClass().getSimpleName(), provider);
429         } catch (RuntimeException e) {
430             // TODO: Perhaps not allow to use BPF offload because the reregistration failure
431             // implied that no data limit could be applies on a metered upstream if any.
432             Log.wtf(TAG, "Cannot register offload stats provider: " + e);
433             provider = null;
434         }
435         mStatsProvider = provider;
436 
437         mBpfCoordinatorShim = BpfCoordinatorShim.getBpfCoordinatorShim(deps);
438         if (!mBpfCoordinatorShim.isInitialized()) {
439             mLog.e("Bpf shim not initialized");
440         }
441     }
442 
443     /**
444      * Start BPF tethering offload stats polling when the first upstream is started.
445      * Note that this can be only called on handler thread.
446      * TODO: Perhaps check BPF support before starting.
447      * TODO: Start the stats polling only if there is any client on the downstream.
448      */
startPolling()449     public void startPolling() {
450         if (mPollingStarted) return;
451 
452         if (!isUsingBpf()) {
453             mLog.i("BPF is not using");
454             return;
455         }
456 
457         mPollingStarted = true;
458         maybeSchedulePollingStats();
459         maybeScheduleConntrackTimeoutUpdate();
460 
461         mLog.i("Polling started");
462     }
463 
464     /**
465      * Stop BPF tethering offload stats polling.
466      * The data limit cleanup and the tether stats maps cleanup are not implemented here.
467      * These cleanups rely on all IpServers calling #tetherOffloadRuleRemove. After the
468      * last rule is removed from the upstream, #tetherOffloadRuleRemove does the cleanup
469      * functionality.
470      * Note that this can be only called on handler thread.
471      */
stopPolling()472     public void stopPolling() {
473         if (!mPollingStarted) return;
474 
475         // Stop scheduled polling conntrack timeout.
476         if (mHandler.hasCallbacks(mScheduledConntrackTimeoutUpdate)) {
477             mHandler.removeCallbacks(mScheduledConntrackTimeoutUpdate);
478         }
479         // Stop scheduled polling stats and poll the latest stats from BPF maps.
480         if (mHandler.hasCallbacks(mScheduledPollingStats)) {
481             mHandler.removeCallbacks(mScheduledPollingStats);
482         }
483         updateForwardedStats();
484         mPollingStarted = false;
485 
486         mLog.i("Polling stopped");
487     }
488 
isUsingBpf()489     private boolean isUsingBpf() {
490         return mIsBpfEnabled && mBpfCoordinatorShim.isInitialized();
491     }
492 
493     /**
494      * Start conntrack message monitoring.
495      * Note that this can be only called on handler thread.
496      *
497      * TODO: figure out a better logging for non-interesting conntrack message.
498      * For example, the following logging is an IPCTNL_MSG_CT_GET message but looks scary.
499      * +---------------------------------------------------------------------------+
500      * | ERROR unparsable netlink msg: 1400000001010103000000000000000002000000    |
501      * +------------------+--------------------------------------------------------+
502      * |                  | struct nlmsghdr                                        |
503      * | 14000000         | length = 20                                            |
504      * | 0101             | type = NFNL_SUBSYS_CTNETLINK << 8 | IPCTNL_MSG_CT_GET  |
505      * | 0103             | flags                                                  |
506      * | 00000000         | seqno = 0                                              |
507      * | 00000000         | pid = 0                                                |
508      * |                  | struct nfgenmsg                                        |
509      * | 02               | nfgen_family  = AF_INET                                |
510      * | 00               | version = NFNETLINK_V0                                 |
511      * | 0000             | res_id                                                 |
512      * +------------------+--------------------------------------------------------+
513      * See NetlinkMonitor#handlePacket, NetlinkMessage#parseNfMessage.
514      */
startMonitoring(@onNull final IpServer ipServer)515     public void startMonitoring(@NonNull final IpServer ipServer) {
516         // TODO: Wrap conntrackMonitor starting function into mBpfCoordinatorShim.
517         if (!isUsingBpf() || !mDeps.isAtLeastS()) return;
518 
519         if (mMonitoringIpServers.contains(ipServer)) {
520             Log.wtf(TAG, "The same downstream " + ipServer.interfaceName()
521                     + " should not start monitoring twice.");
522             return;
523         }
524 
525         if (mMonitoringIpServers.isEmpty()) {
526             mConntrackMonitor.start();
527             mLog.i("Monitoring started");
528         }
529 
530         mMonitoringIpServers.add(ipServer);
531     }
532 
533     /**
534      * Stop conntrack event monitoring.
535      * Note that this can be only called on handler thread.
536      */
stopMonitoring(@onNull final IpServer ipServer)537     public void stopMonitoring(@NonNull final IpServer ipServer) {
538         // TODO: Wrap conntrackMonitor stopping function into mBpfCoordinatorShim.
539         if (!isUsingBpf() || !mDeps.isAtLeastS()) return;
540 
541         mMonitoringIpServers.remove(ipServer);
542 
543         if (!mMonitoringIpServers.isEmpty()) return;
544 
545         mConntrackMonitor.stop();
546         mLog.i("Monitoring stopped");
547     }
548 
549     /**
550      * Add forwarding rule. After adding the first rule on a given upstream, must add the data
551      * limit on the given upstream.
552      * Note that this can be only called on handler thread.
553      */
tetherOffloadRuleAdd( @onNull final IpServer ipServer, @NonNull final Ipv6ForwardingRule rule)554     public void tetherOffloadRuleAdd(
555             @NonNull final IpServer ipServer, @NonNull final Ipv6ForwardingRule rule) {
556         if (!isUsingBpf()) return;
557 
558         // TODO: Perhaps avoid to add a duplicate rule.
559         if (!mBpfCoordinatorShim.tetherOffloadRuleAdd(rule)) return;
560 
561         if (!mIpv6ForwardingRules.containsKey(ipServer)) {
562             mIpv6ForwardingRules.put(ipServer, new LinkedHashMap<Inet6Address,
563                     Ipv6ForwardingRule>());
564         }
565         LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules = mIpv6ForwardingRules.get(ipServer);
566 
567         // Add upstream and downstream interface index to dev map.
568         maybeAddDevMap(rule.upstreamIfindex, rule.downstreamIfindex);
569 
570         // When the first rule is added to an upstream, setup upstream forwarding and data limit.
571         maybeSetLimit(rule.upstreamIfindex);
572 
573         if (!isAnyRuleFromDownstreamToUpstream(rule.downstreamIfindex, rule.upstreamIfindex)) {
574             final int downstream = rule.downstreamIfindex;
575             final int upstream = rule.upstreamIfindex;
576             // TODO: support upstream forwarding on non-point-to-point interfaces.
577             // TODO: get the MTU from LinkProperties and update the rules when it changes.
578             if (!mBpfCoordinatorShim.startUpstreamIpv6Forwarding(downstream, upstream, rule.srcMac,
579                     NULL_MAC_ADDRESS, NULL_MAC_ADDRESS, NetworkStackConstants.ETHER_MTU)) {
580                 mLog.e("Failed to enable upstream IPv6 forwarding from "
581                         + mInterfaceNames.get(downstream) + " to " + mInterfaceNames.get(upstream));
582             }
583         }
584 
585         // Must update the adding rule after calling #isAnyRuleOnUpstream because it needs to
586         // check if it is about adding a first rule for a given upstream.
587         rules.put(rule.address, rule);
588     }
589 
590     /**
591      * Remove forwarding rule. After removing the last rule on a given upstream, must clear
592      * data limit, update the last tether stats and remove the tether stats in the BPF maps.
593      * Note that this can be only called on handler thread.
594      */
tetherOffloadRuleRemove( @onNull final IpServer ipServer, @NonNull final Ipv6ForwardingRule rule)595     public void tetherOffloadRuleRemove(
596             @NonNull final IpServer ipServer, @NonNull final Ipv6ForwardingRule rule) {
597         if (!isUsingBpf()) return;
598 
599         if (!mBpfCoordinatorShim.tetherOffloadRuleRemove(rule)) return;
600 
601         LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules = mIpv6ForwardingRules.get(ipServer);
602         if (rules == null) return;
603 
604         // Must remove rules before calling #isAnyRuleOnUpstream because it needs to check if
605         // the last rule is removed for a given upstream. If no rule is removed, return early.
606         // Avoid unnecessary work on a non-existent rule which may have never been added or
607         // removed already.
608         if (rules.remove(rule.address) == null) return;
609 
610         // Remove the downstream entry if it has no more rule.
611         if (rules.isEmpty()) {
612             mIpv6ForwardingRules.remove(ipServer);
613         }
614 
615         // If no more rules between this upstream and downstream, stop upstream forwarding.
616         if (!isAnyRuleFromDownstreamToUpstream(rule.downstreamIfindex, rule.upstreamIfindex)) {
617             final int downstream = rule.downstreamIfindex;
618             final int upstream = rule.upstreamIfindex;
619             if (!mBpfCoordinatorShim.stopUpstreamIpv6Forwarding(downstream, upstream,
620                     rule.srcMac)) {
621                 mLog.e("Failed to disable upstream IPv6 forwarding from "
622                         + mInterfaceNames.get(downstream) + " to " + mInterfaceNames.get(upstream));
623             }
624         }
625 
626         // Do cleanup functionality if there is no more rule on the given upstream.
627         maybeClearLimit(rule.upstreamIfindex);
628     }
629 
630     /**
631      * Clear all forwarding rules for a given downstream.
632      * Note that this can be only called on handler thread.
633      * TODO: rename to tetherOffloadRuleClear6 because of IPv6 only.
634      */
tetherOffloadRuleClear(@onNull final IpServer ipServer)635     public void tetherOffloadRuleClear(@NonNull final IpServer ipServer) {
636         if (!isUsingBpf()) return;
637 
638         final LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules = mIpv6ForwardingRules.get(
639                 ipServer);
640         if (rules == null) return;
641 
642         // Need to build a rule list because the rule map may be changed in the iteration.
643         for (final Ipv6ForwardingRule rule : new ArrayList<Ipv6ForwardingRule>(rules.values())) {
644             tetherOffloadRuleRemove(ipServer, rule);
645         }
646     }
647 
648     /**
649      * Update existing forwarding rules to new upstream for a given downstream.
650      * Note that this can be only called on handler thread.
651      */
tetherOffloadRuleUpdate(@onNull final IpServer ipServer, int newUpstreamIfindex)652     public void tetherOffloadRuleUpdate(@NonNull final IpServer ipServer, int newUpstreamIfindex) {
653         if (!isUsingBpf()) return;
654 
655         final LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules = mIpv6ForwardingRules.get(
656                 ipServer);
657         if (rules == null) return;
658 
659         // Need to build a rule list because the rule map may be changed in the iteration.
660         // First remove all the old rules, then add all the new rules. This is because the upstream
661         // forwarding code in tetherOffloadRuleAdd cannot support rules on two upstreams at the
662         // same time. Deleting the rules first ensures that upstream forwarding is disabled on the
663         // old upstream when the last rule is removed from it, and re-enabled on the new upstream
664         // when the first rule is added to it.
665         // TODO: Once the IPv6 client processing code has moved from IpServer to BpfCoordinator, do
666         // something smarter.
667         final ArrayList<Ipv6ForwardingRule> rulesCopy = new ArrayList<>(rules.values());
668         for (final Ipv6ForwardingRule rule : rulesCopy) {
669             // Remove the old rule before adding the new one because the map uses the same key for
670             // both rules. Reversing the processing order causes that the new rule is removed as
671             // unexpected.
672             // TODO: Add new rule first to reduce the latency which has no rule.
673             tetherOffloadRuleRemove(ipServer, rule);
674         }
675         for (final Ipv6ForwardingRule rule : rulesCopy) {
676             tetherOffloadRuleAdd(ipServer, rule.onNewUpstream(newUpstreamIfindex));
677         }
678     }
679 
680     /**
681      * Add upstream name to lookup table. The lookup table is used for tether stats interface name
682      * lookup because the netd only reports interface index in BPF tether stats but the service
683      * expects the interface name in NetworkStats object.
684      * Note that this can be only called on handler thread.
685      */
addUpstreamNameToLookupTable(int upstreamIfindex, @NonNull String upstreamIface)686     public void addUpstreamNameToLookupTable(int upstreamIfindex, @NonNull String upstreamIface) {
687         if (!isUsingBpf()) return;
688 
689         if (upstreamIfindex == 0 || TextUtils.isEmpty(upstreamIface)) return;
690 
691         if (isVcnInterface(upstreamIface)) return;
692 
693         // The same interface index to name mapping may be added by different IpServer objects or
694         // re-added by reconnection on the same upstream interface. Ignore the duplicate one.
695         final String iface = mInterfaceNames.get(upstreamIfindex);
696         if (iface == null) {
697             mInterfaceNames.put(upstreamIfindex, upstreamIface);
698         } else if (!TextUtils.equals(iface, upstreamIface)) {
699             Log.wtf(TAG, "The upstream interface name " + upstreamIface
700                     + " is different from the existing interface name "
701                     + iface + " for index " + upstreamIfindex);
702         }
703     }
704 
705     /**
706      * Add downstream client.
707      * Note that this can be only called on handler thread.
708      */
tetherOffloadClientAdd(@onNull final IpServer ipServer, @NonNull final ClientInfo client)709     public void tetherOffloadClientAdd(@NonNull final IpServer ipServer,
710             @NonNull final ClientInfo client) {
711         if (!isUsingBpf()) return;
712 
713         if (!mTetherClients.containsKey(ipServer)) {
714             mTetherClients.put(ipServer, new HashMap<Inet4Address, ClientInfo>());
715         }
716 
717         HashMap<Inet4Address, ClientInfo> clients = mTetherClients.get(ipServer);
718         clients.put(client.clientAddress, client);
719     }
720 
721     /**
722      * Remove a downstream client and its rules if any.
723      * Note that this can be only called on handler thread.
724      */
tetherOffloadClientRemove(@onNull final IpServer ipServer, @NonNull final ClientInfo client)725     public void tetherOffloadClientRemove(@NonNull final IpServer ipServer,
726             @NonNull final ClientInfo client) {
727         if (!isUsingBpf()) return;
728 
729         // No clients on the downstream, return early.
730         HashMap<Inet4Address, ClientInfo> clients = mTetherClients.get(ipServer);
731         if (clients == null) return;
732 
733         // No client is removed, return early.
734         if (clients.remove(client.clientAddress) == null) return;
735 
736         // Remove the client's rules. Removing the client implies that its rules are not used
737         // anymore.
738         tetherOffloadRuleClear(client);
739 
740         // Remove the downstream entry if it has no more client.
741         if (clients.isEmpty()) {
742             mTetherClients.remove(ipServer);
743         }
744     }
745 
746     /**
747      * Clear all downstream clients and their rules if any.
748      * Note that this can be only called on handler thread.
749      */
tetherOffloadClientClear(@onNull final IpServer ipServer)750     public void tetherOffloadClientClear(@NonNull final IpServer ipServer) {
751         if (!isUsingBpf()) return;
752 
753         final HashMap<Inet4Address, ClientInfo> clients = mTetherClients.get(ipServer);
754         if (clients == null) return;
755 
756         // Need to build a client list because the client map may be changed in the iteration.
757         for (final ClientInfo c : new ArrayList<ClientInfo>(clients.values())) {
758             tetherOffloadClientRemove(ipServer, c);
759         }
760     }
761 
762     /**
763      * Clear all forwarding IPv4 rules for a given client.
764      * Note that this can be only called on handler thread.
765      */
tetherOffloadRuleClear(@onNull final ClientInfo clientInfo)766     private void tetherOffloadRuleClear(@NonNull final ClientInfo clientInfo) {
767         // TODO: consider removing the rules in #tetherOffloadRuleForEach once BpfMap#forEach
768         // can guarantee that deleting some pass-in rules in the BPF map iteration can still
769         // walk through every entry.
770         final Inet4Address clientAddr = clientInfo.clientAddress;
771         final Set<Integer> upstreamIndiceSet = new ArraySet<Integer>();
772         final Set<Tether4Key> deleteUpstreamRuleKeys = new ArraySet<Tether4Key>();
773         final Set<Tether4Key> deleteDownstreamRuleKeys = new ArraySet<Tether4Key>();
774 
775         // Find the rules which are related with the given client.
776         mBpfCoordinatorShim.tetherOffloadRuleForEach(UPSTREAM, (k, v) -> {
777             if (Arrays.equals(k.src4, clientAddr.getAddress())) {
778                 deleteUpstreamRuleKeys.add(k);
779             }
780         });
781         mBpfCoordinatorShim.tetherOffloadRuleForEach(DOWNSTREAM, (k, v) -> {
782             if (Arrays.equals(v.dst46, toIpv4MappedAddressBytes(clientAddr))) {
783                 deleteDownstreamRuleKeys.add(k);
784                 upstreamIndiceSet.add((int) k.iif);
785             }
786         });
787 
788         // The rules should be paired on upstream and downstream map because they are added by
789         // conntrack events which have bidirectional information.
790         // TODO: Consider figuring out a way to fix. Probably delete all rules to fallback.
791         if (deleteUpstreamRuleKeys.size() != deleteDownstreamRuleKeys.size()) {
792             Log.wtf(TAG, "The deleting rule numbers are different on upstream4 and downstream4 ("
793                     + "upstream: " + deleteUpstreamRuleKeys.size() + ", "
794                     + "downstream: " + deleteDownstreamRuleKeys.size() + ").");
795             return;
796         }
797 
798         // Delete the rules which are related with the given client.
799         for (final Tether4Key k : deleteUpstreamRuleKeys) {
800             mBpfCoordinatorShim.tetherOffloadRuleRemove(UPSTREAM, k);
801         }
802         for (final Tether4Key k : deleteDownstreamRuleKeys) {
803             mBpfCoordinatorShim.tetherOffloadRuleRemove(DOWNSTREAM, k);
804         }
805 
806         // Cleanup each upstream interface by a set which avoids duplicated work on the same
807         // upstream interface. Cleaning up the same interface twice (or more) here may raise
808         // an exception because all related information were removed in the first deletion.
809         for (final int upstreamIndex : upstreamIndiceSet) {
810             maybeClearLimit(upstreamIndex);
811         }
812     }
813 
814     /**
815      * Clear all forwarding IPv4 rules for a given downstream. Needed because the client may still
816      * connect on the downstream but the existing rules are not required anymore. Ex: upstream
817      * changed.
818      */
tetherOffloadRule4Clear(@onNull final IpServer ipServer)819     private void tetherOffloadRule4Clear(@NonNull final IpServer ipServer) {
820         if (!isUsingBpf()) return;
821 
822         final HashMap<Inet4Address, ClientInfo> clients = mTetherClients.get(ipServer);
823         if (clients == null) return;
824 
825         // The value should be unique as its key because currently the key was using from its
826         // client address of ClientInfo. See #tetherOffloadClientAdd.
827         for (final ClientInfo client : clients.values()) {
828             tetherOffloadRuleClear(client);
829         }
830     }
831 
isValidUpstreamIpv4Address(@onNull final InetAddress addr)832     private boolean isValidUpstreamIpv4Address(@NonNull final InetAddress addr) {
833         if (!(addr instanceof Inet4Address)) return false;
834         Inet4Address v4 = (Inet4Address) addr;
835         if (v4.isAnyLocalAddress() || v4.isLinkLocalAddress()
836                 || v4.isLoopbackAddress() || v4.isMulticastAddress()) {
837             return false;
838         }
839         return true;
840     }
841 
842     /**
843      * Call when UpstreamNetworkState may be changed.
844      * If upstream has ipv4 for tethering, update this new UpstreamNetworkState
845      * to BpfCoordinator for building upstream interface index mapping. Otherwise,
846      * clear the all existing rules if any.
847      *
848      * Note that this can be only called on handler thread.
849      */
updateUpstreamNetworkState(UpstreamNetworkState ns)850     public void updateUpstreamNetworkState(UpstreamNetworkState ns) {
851         if (!isUsingBpf()) return;
852 
853         int upstreamIndex = 0;
854 
855         // This will not work on a network that is using 464xlat because hasIpv4Address will not be
856         // true.
857         // TODO: need to consider 464xlat.
858         if (ns != null && ns.linkProperties != null && ns.linkProperties.hasIpv4Address()) {
859             // TODO: support ether ip upstream interface.
860             final String ifaceName = ns.linkProperties.getInterfaceName();
861             final InterfaceParams params = mDeps.getInterfaceParams(ifaceName);
862             final boolean isVcn = isVcnInterface(ifaceName);
863             if (!isVcn && params != null && !params.hasMacAddress /* raw ip upstream only */) {
864                 upstreamIndex = params.index;
865             }
866         }
867         if (mLastIPv4UpstreamIfindex == upstreamIndex) return;
868 
869         // Clear existing rules if upstream interface is changed. The existing rules should be
870         // cleared before upstream index mapping is cleared. It can avoid that ipServer or
871         // conntrack event may use the non-existing upstream interfeace index to build a removing
872         // key while removeing the rules. Can't notify each IpServer to clear the rules as
873         // IPv6TetheringCoordinator#updateUpstreamNetworkState because the IpServer may not
874         // handle the upstream changing notification before changing upstream index mapping.
875         if (mLastIPv4UpstreamIfindex != 0) {
876             // Clear all forwarding IPv4 rules for all downstreams.
877             for (final IpServer ipserver : mTetherClients.keySet()) {
878                 tetherOffloadRule4Clear(ipserver);
879             }
880         }
881 
882         // Don't update mLastIPv4UpstreamIfindex before clearing existing rules if any. Need that
883         // to tell if it is required to clean the out-of-date rules.
884         mLastIPv4UpstreamIfindex = upstreamIndex;
885 
886         // If link properties are valid, build the upstream information mapping. Otherwise, clear
887         // the upstream interface index mapping, to ensure that any conntrack events that arrive
888         // after the upstream is lost do not incorrectly add rules pointing at the upstream.
889         if (upstreamIndex == 0) {
890             mIpv4UpstreamIndices.clear();
891             return;
892         }
893         Collection<InetAddress> addresses = ns.linkProperties.getAddresses();
894         for (final InetAddress addr: addresses) {
895             if (isValidUpstreamIpv4Address(addr)) {
896                 mIpv4UpstreamIndices.put((Inet4Address) addr, upstreamIndex);
897             }
898         }
899     }
900 
901     /**
902      * Attach BPF program
903      *
904      * TODO: consider error handling if the attach program failed.
905      */
maybeAttachProgram(@onNull String intIface, @NonNull String extIface)906     public void maybeAttachProgram(@NonNull String intIface, @NonNull String extIface) {
907         if (isVcnInterface(extIface)) return;
908 
909         if (forwardingPairExists(intIface, extIface)) return;
910 
911         boolean firstDownstreamForThisUpstream = !isAnyForwardingPairOnUpstream(extIface);
912         forwardingPairAdd(intIface, extIface);
913 
914         mBpfCoordinatorShim.attachProgram(intIface, UPSTREAM);
915         // Attach if the upstream is the first time to be used in a forwarding pair.
916         if (firstDownstreamForThisUpstream) {
917             mBpfCoordinatorShim.attachProgram(extIface, DOWNSTREAM);
918         }
919     }
920 
921     /**
922      * Detach BPF program
923      */
maybeDetachProgram(@onNull String intIface, @NonNull String extIface)924     public void maybeDetachProgram(@NonNull String intIface, @NonNull String extIface) {
925         forwardingPairRemove(intIface, extIface);
926 
927         // Detaching program may fail because the interface has been removed already.
928         mBpfCoordinatorShim.detachProgram(intIface);
929         // Detach if no more forwarding pair is using the upstream.
930         if (!isAnyForwardingPairOnUpstream(extIface)) {
931             mBpfCoordinatorShim.detachProgram(extIface);
932         }
933     }
934 
935     // TODO: make mInterfaceNames accessible to the shim and move this code to there.
getIfName(long ifindex)936     private String getIfName(long ifindex) {
937         return mInterfaceNames.get((int) ifindex, Long.toString(ifindex));
938     }
939 
940     /**
941      * Dump information.
942      * Block the function until all the data are dumped on the handler thread or timed-out. The
943      * reason is that dumpsys invokes this function on the thread of caller and the data may only
944      * be allowed to be accessed on the handler thread.
945      */
dump(@onNull IndentingPrintWriter pw)946     public void dump(@NonNull IndentingPrintWriter pw) {
947         pw.println("mIsBpfEnabled: " + mIsBpfEnabled);
948         pw.println("Polling " + (mPollingStarted ? "started" : "not started"));
949         pw.println("Stats provider " + (mStatsProvider != null
950                 ? "registered" : "not registered"));
951         pw.println("Upstream quota: " + mInterfaceQuotas.toString());
952         pw.println("Polling interval: " + getPollingInterval() + " ms");
953         pw.println("Bpf shim: " + mBpfCoordinatorShim.toString());
954 
955         pw.println("Forwarding stats:");
956         pw.increaseIndent();
957         if (mStats.size() == 0) {
958             pw.println("<empty>");
959         } else {
960             dumpStats(pw);
961         }
962         pw.decreaseIndent();
963 
964         pw.println("BPF stats:");
965         pw.increaseIndent();
966         dumpBpfStats(pw);
967         pw.decreaseIndent();
968         pw.println();
969 
970         pw.println("Forwarding rules:");
971         pw.increaseIndent();
972         dumpIpv6UpstreamRules(pw);
973         dumpIpv6ForwardingRules(pw);
974         dumpIpv4ForwardingRules(pw);
975         pw.decreaseIndent();
976         pw.println();
977 
978         pw.println("Device map:");
979         pw.increaseIndent();
980         dumpDevmap(pw);
981         pw.decreaseIndent();
982 
983         pw.println("Client Information:");
984         pw.increaseIndent();
985         if (mTetherClients.isEmpty()) {
986             pw.println("<empty>");
987         } else {
988             pw.println(mTetherClients.toString());
989         }
990         pw.decreaseIndent();
991 
992         pw.println("IPv4 Upstream Indices:");
993         pw.increaseIndent();
994         if (mIpv4UpstreamIndices.isEmpty()) {
995             pw.println("<empty>");
996         } else {
997             pw.println(mIpv4UpstreamIndices.toString());
998         }
999         pw.decreaseIndent();
1000 
1001         pw.println();
1002         pw.println("Forwarding counters:");
1003         pw.increaseIndent();
1004         dumpCounters(pw);
1005         pw.decreaseIndent();
1006     }
1007 
dumpStats(@onNull IndentingPrintWriter pw)1008     private void dumpStats(@NonNull IndentingPrintWriter pw) {
1009         for (int i = 0; i < mStats.size(); i++) {
1010             final int upstreamIfindex = mStats.keyAt(i);
1011             final ForwardedStats stats = mStats.get(upstreamIfindex);
1012             pw.println(String.format("%d(%s) - %s", upstreamIfindex, mInterfaceNames.get(
1013                     upstreamIfindex), stats.toString()));
1014         }
1015     }
dumpBpfStats(@onNull IndentingPrintWriter pw)1016     private void dumpBpfStats(@NonNull IndentingPrintWriter pw) {
1017         try (BpfMap<TetherStatsKey, TetherStatsValue> map = mDeps.getBpfStatsMap()) {
1018             if (map == null) {
1019                 pw.println("No BPF stats map");
1020                 return;
1021             }
1022             if (map.isEmpty()) {
1023                 pw.println("<empty>");
1024             }
1025             map.forEach((k, v) -> {
1026                 pw.println(String.format("%s: %s", k, v));
1027             });
1028         } catch (ErrnoException | IOException e) {
1029             pw.println("Error dumping BPF stats map: " + e);
1030         }
1031     }
1032 
dumpIpv6ForwardingRules(@onNull IndentingPrintWriter pw)1033     private void dumpIpv6ForwardingRules(@NonNull IndentingPrintWriter pw) {
1034         if (mIpv6ForwardingRules.size() == 0) {
1035             pw.println("No IPv6 rules");
1036             return;
1037         }
1038 
1039         for (Map.Entry<IpServer, LinkedHashMap<Inet6Address, Ipv6ForwardingRule>> entry :
1040                 mIpv6ForwardingRules.entrySet()) {
1041             IpServer ipServer = entry.getKey();
1042             // The rule downstream interface index is paired with the interface name from
1043             // IpServer#interfaceName. See #startIPv6, #updateIpv6ForwardingRules in IpServer.
1044             final String downstreamIface = ipServer.interfaceName();
1045             pw.println("[" + downstreamIface + "]: iif(iface) oif(iface) v6addr srcmac dstmac");
1046 
1047             pw.increaseIndent();
1048             LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules = entry.getValue();
1049             for (Ipv6ForwardingRule rule : rules.values()) {
1050                 final int upstreamIfindex = rule.upstreamIfindex;
1051                 pw.println(String.format("%d(%s) %d(%s) %s %s %s", upstreamIfindex,
1052                         mInterfaceNames.get(upstreamIfindex), rule.downstreamIfindex,
1053                         downstreamIface, rule.address.getHostAddress(), rule.srcMac, rule.dstMac));
1054             }
1055             pw.decreaseIndent();
1056         }
1057     }
1058 
ipv6UpstreamRuletoString(TetherUpstream6Key key, Tether6Value value)1059     private String ipv6UpstreamRuletoString(TetherUpstream6Key key, Tether6Value value) {
1060         return String.format("%d(%s) %s -> %d(%s) %04x %s %s",
1061                 key.iif, getIfName(key.iif), key.dstMac, value.oif, getIfName(value.oif),
1062                 value.ethProto, value.ethSrcMac, value.ethDstMac);
1063     }
1064 
dumpIpv6UpstreamRules(IndentingPrintWriter pw)1065     private void dumpIpv6UpstreamRules(IndentingPrintWriter pw) {
1066         try (BpfMap<TetherUpstream6Key, Tether6Value> map = mDeps.getBpfUpstream6Map()) {
1067             if (map == null) {
1068                 pw.println("No IPv6 upstream");
1069                 return;
1070             }
1071             if (map.isEmpty()) {
1072                 pw.println("No IPv6 upstream rules");
1073                 return;
1074             }
1075             map.forEach((k, v) -> pw.println(ipv6UpstreamRuletoString(k, v)));
1076         } catch (ErrnoException | IOException e) {
1077             pw.println("Error dumping IPv6 upstream map: " + e);
1078         }
1079     }
1080 
bpfMapEntryToBase64String( final K key, final V value)1081     private <K extends Struct, V extends Struct> String bpfMapEntryToBase64String(
1082             final K key, final V value) {
1083         final byte[] keyBytes = key.writeToBytes();
1084         final String keyBase64Str = Base64.encodeToString(keyBytes, Base64.DEFAULT)
1085                 .replace("\n", "");
1086         final byte[] valueBytes = value.writeToBytes();
1087         final String valueBase64Str = Base64.encodeToString(valueBytes, Base64.DEFAULT)
1088                 .replace("\n", "");
1089 
1090         return keyBase64Str + DUMP_BASE64_DELIMITER + valueBase64Str;
1091     }
1092 
dumpRawMap(BpfMap<K, V> map, IndentingPrintWriter pw)1093     private <K extends Struct, V extends Struct> void dumpRawMap(BpfMap<K, V> map,
1094             IndentingPrintWriter pw) throws ErrnoException {
1095         if (map == null) {
1096             pw.println("No BPF support");
1097             return;
1098         }
1099         if (map.isEmpty()) {
1100             pw.println("No entries");
1101             return;
1102         }
1103         map.forEach((k, v) -> pw.println(bpfMapEntryToBase64String(k, v)));
1104     }
1105 
1106     /**
1107      * Dump raw BPF map in base64 encoded strings. For test only.
1108      * Only allow to dump one map path once.
1109      * Format:
1110      * $ dumpsys tethering bpfRawMap --<map name>
1111      */
dumpRawMap(@onNull IndentingPrintWriter pw, @Nullable String[] args)1112     public void dumpRawMap(@NonNull IndentingPrintWriter pw, @Nullable String[] args) {
1113         // TODO: consider checking the arg order that <map name> is after "bpfRawMap". Probably
1114         // it is okay for now because this is used by test only and test is supposed to use
1115         // expected argument order.
1116         // TODO: dump downstream4 map.
1117         if (CollectionUtils.contains(args, DUMPSYS_RAWMAP_ARG_STATS)) {
1118             try (BpfMap<TetherStatsKey, TetherStatsValue> statsMap = mDeps.getBpfStatsMap()) {
1119                 dumpRawMap(statsMap, pw);
1120             } catch (ErrnoException | IOException e) {
1121                 pw.println("Error dumping stats map: " + e);
1122             }
1123             return;
1124         }
1125         if (CollectionUtils.contains(args, DUMPSYS_RAWMAP_ARG_UPSTREAM4)) {
1126             try (BpfMap<Tether4Key, Tether4Value> upstreamMap = mDeps.getBpfUpstream4Map()) {
1127                 dumpRawMap(upstreamMap, pw);
1128             } catch (ErrnoException | IOException e) {
1129                 pw.println("Error dumping IPv4 map: " + e);
1130             }
1131             return;
1132         }
1133     }
1134 
l4protoToString(int proto)1135     private String l4protoToString(int proto) {
1136         if (proto == OsConstants.IPPROTO_TCP) {
1137             return "tcp";
1138         } else if (proto == OsConstants.IPPROTO_UDP) {
1139             return "udp";
1140         }
1141         return String.format("unknown(%d)", proto);
1142     }
1143 
ipv4RuleToString(long now, boolean downstream, Tether4Key key, Tether4Value value)1144     private String ipv4RuleToString(long now, boolean downstream,
1145             Tether4Key key, Tether4Value value) {
1146         final String src4, public4, dst4;
1147         final int publicPort;
1148         try {
1149             src4 = InetAddress.getByAddress(key.src4).getHostAddress();
1150             if (downstream) {
1151                 public4 = InetAddress.getByAddress(key.dst4).getHostAddress();
1152                 publicPort = key.dstPort;
1153             } else {
1154                 public4 = InetAddress.getByAddress(value.src46).getHostAddress();
1155                 publicPort = value.srcPort;
1156             }
1157             dst4 = InetAddress.getByAddress(value.dst46).getHostAddress();
1158         } catch (UnknownHostException impossible) {
1159             throw new AssertionError("IP address array not valid IPv4 address!");
1160         }
1161 
1162         final String ageStr = (value.lastUsed == 0) ? "-"
1163                 : String.format("%dms", (now - value.lastUsed) / 1_000_000);
1164         return String.format("%s [%s] %d(%s) %s:%d -> %d(%s) %s:%d -> %s:%d [%s] %s",
1165                 l4protoToString(key.l4proto), key.dstMac, key.iif, getIfName(key.iif),
1166                 src4, key.srcPort, value.oif, getIfName(value.oif),
1167                 public4, publicPort, dst4, value.dstPort, value.ethDstMac, ageStr);
1168     }
1169 
dumpIpv4ForwardingRuleMap(long now, boolean downstream, BpfMap<Tether4Key, Tether4Value> map, IndentingPrintWriter pw)1170     private void dumpIpv4ForwardingRuleMap(long now, boolean downstream,
1171             BpfMap<Tether4Key, Tether4Value> map, IndentingPrintWriter pw) throws ErrnoException {
1172         if (map == null) {
1173             pw.println("No IPv4 support");
1174             return;
1175         }
1176         if (map.isEmpty()) {
1177             pw.println("No rules");
1178             return;
1179         }
1180         map.forEach((k, v) -> pw.println(ipv4RuleToString(now, downstream, k, v)));
1181     }
1182 
dumpIpv4ForwardingRules(IndentingPrintWriter pw)1183     private void dumpIpv4ForwardingRules(IndentingPrintWriter pw) {
1184         final long now = SystemClock.elapsedRealtimeNanos();
1185 
1186         try (BpfMap<Tether4Key, Tether4Value> upstreamMap = mDeps.getBpfUpstream4Map();
1187                 BpfMap<Tether4Key, Tether4Value> downstreamMap = mDeps.getBpfDownstream4Map()) {
1188             pw.println("IPv4 Upstream: proto [inDstMac] iif(iface) src -> nat -> "
1189                     + "dst [outDstMac] age");
1190             pw.increaseIndent();
1191             dumpIpv4ForwardingRuleMap(now, UPSTREAM, upstreamMap, pw);
1192             pw.decreaseIndent();
1193 
1194             pw.println("IPv4 Downstream: proto [inDstMac] iif(iface) src -> nat -> "
1195                     + "dst [outDstMac] age");
1196             pw.increaseIndent();
1197             dumpIpv4ForwardingRuleMap(now, DOWNSTREAM, downstreamMap, pw);
1198             pw.decreaseIndent();
1199         } catch (ErrnoException | IOException e) {
1200             pw.println("Error dumping IPv4 map: " + e);
1201         }
1202     }
1203 
dumpCounters(@onNull IndentingPrintWriter pw)1204     private void dumpCounters(@NonNull IndentingPrintWriter pw) {
1205         if (!mDeps.isAtLeastS()) {
1206             pw.println("No counter support");
1207             return;
1208         }
1209         try (BpfMap<U32, U32> map = new BpfMap<>(TETHER_ERROR_MAP_PATH, BpfMap.BPF_F_RDONLY,
1210                 U32.class, U32.class)) {
1211 
1212             map.forEach((k, v) -> {
1213                 String counterName;
1214                 try {
1215                     counterName = sBpfCounterNames[(int) k.val];
1216                 } catch (IndexOutOfBoundsException e) {
1217                     // Should never happen because this code gets the counter name from the same
1218                     // include file as the BPF program that increments the counter.
1219                     Log.wtf(TAG, "Unknown tethering counter type " + k.val);
1220                     counterName = Long.toString(k.val);
1221                 }
1222                 if (v.val > 0) pw.println(String.format("%s: %d", counterName, v.val));
1223             });
1224         } catch (ErrnoException | IOException e) {
1225             pw.println("Error dumping counter map: " + e);
1226         }
1227     }
1228 
dumpDevmap(@onNull IndentingPrintWriter pw)1229     private void dumpDevmap(@NonNull IndentingPrintWriter pw) {
1230         try (BpfMap<TetherDevKey, TetherDevValue> map = mDeps.getBpfDevMap()) {
1231             if (map == null) {
1232                 pw.println("No devmap support");
1233                 return;
1234             }
1235             if (map.isEmpty()) {
1236                 pw.println("<empty>");
1237                 return;
1238             }
1239             pw.println("ifindex (iface) -> ifindex (iface)");
1240             pw.increaseIndent();
1241             map.forEach((k, v) -> {
1242                 // Only get upstream interface name. Just do the best to make the index readable.
1243                 // TODO: get downstream interface name because the index is either upstream or
1244                 // downstream interface in dev map.
1245                 pw.println(String.format("%d (%s) -> %d (%s)", k.ifIndex, getIfName(k.ifIndex),
1246                         v.ifIndex, getIfName(v.ifIndex)));
1247             });
1248         } catch (ErrnoException | IOException e) {
1249             pw.println("Error dumping dev map: " + e);
1250         }
1251         pw.decreaseIndent();
1252     }
1253 
1254     /** IPv6 forwarding rule class. */
1255     public static class Ipv6ForwardingRule {
1256         // The upstream6 and downstream6 rules are built as the following tables. Only raw ip
1257         // upstream interface is supported.
1258         // TODO: support ether ip upstream interface.
1259         //
1260         // NAT network topology:
1261         //
1262         //         public network (rawip)                 private network
1263         //                   |                 UE                |
1264         // +------------+    V    +------------+------------+    V    +------------+
1265         // |   Sever    +---------+  Upstream  | Downstream +---------+   Client   |
1266         // +------------+         +------------+------------+         +------------+
1267         //
1268         // upstream6 key and value:
1269         //
1270         // +------+-------------+
1271         // | TetherUpstream6Key |
1272         // +------+------+------+
1273         // |field |iif   |dstMac|
1274         // |      |      |      |
1275         // +------+------+------+
1276         // |value |downst|downst|
1277         // |      |ream  |ream  |
1278         // +------+------+------+
1279         //
1280         // +------+----------------------------------+
1281         // |      |Tether6Value                      |
1282         // +------+------+------+------+------+------+
1283         // |field |oif   |ethDst|ethSrc|ethPro|pmtu  |
1284         // |      |      |mac   |mac   |to    |      |
1285         // +------+------+------+------+------+------+
1286         // |value |upstre|--    |--    |ETH_P_|1500  |
1287         // |      |am    |      |      |IP    |      |
1288         // +------+------+------+------+------+------+
1289         //
1290         // downstream6 key and value:
1291         //
1292         // +------+--------------------+
1293         // |      |TetherDownstream6Key|
1294         // +------+------+------+------+
1295         // |field |iif   |dstMac|neigh6|
1296         // |      |      |      |      |
1297         // +------+------+------+------+
1298         // |value |upstre|--    |client|
1299         // |      |am    |      |      |
1300         // +------+------+------+------+
1301         //
1302         // +------+----------------------------------+
1303         // |      |Tether6Value                      |
1304         // +------+------+------+------+------+------+
1305         // |field |oif   |ethDst|ethSrc|ethPro|pmtu  |
1306         // |      |      |mac   |mac   |to    |      |
1307         // +------+------+------+------+------+------+
1308         // |value |downst|client|downst|ETH_P_|1500  |
1309         // |      |ream  |      |ream  |IP    |      |
1310         // +------+------+------+------+------+------+
1311         //
1312         public final int upstreamIfindex;
1313         public final int downstreamIfindex;
1314 
1315         // TODO: store a ClientInfo object instead of storing address, srcMac, and dstMac directly.
1316         @NonNull
1317         public final Inet6Address address;
1318         @NonNull
1319         public final MacAddress srcMac;
1320         @NonNull
1321         public final MacAddress dstMac;
1322 
Ipv6ForwardingRule(int upstreamIfindex, int downstreamIfIndex, @NonNull Inet6Address address, @NonNull MacAddress srcMac, @NonNull MacAddress dstMac)1323         public Ipv6ForwardingRule(int upstreamIfindex, int downstreamIfIndex,
1324                 @NonNull Inet6Address address, @NonNull MacAddress srcMac,
1325                 @NonNull MacAddress dstMac) {
1326             this.upstreamIfindex = upstreamIfindex;
1327             this.downstreamIfindex = downstreamIfIndex;
1328             this.address = address;
1329             this.srcMac = srcMac;
1330             this.dstMac = dstMac;
1331         }
1332 
1333         /** Return a new rule object which updates with new upstream index. */
1334         @NonNull
onNewUpstream(int newUpstreamIfindex)1335         public Ipv6ForwardingRule onNewUpstream(int newUpstreamIfindex) {
1336             return new Ipv6ForwardingRule(newUpstreamIfindex, downstreamIfindex, address, srcMac,
1337                     dstMac);
1338         }
1339 
1340         /**
1341          * Don't manipulate TetherOffloadRuleParcel directly because implementing onNewUpstream()
1342          * would be error-prone due to generated stable AIDL classes not having a copy constructor.
1343          */
1344         @NonNull
toTetherOffloadRuleParcel()1345         public TetherOffloadRuleParcel toTetherOffloadRuleParcel() {
1346             final TetherOffloadRuleParcel parcel = new TetherOffloadRuleParcel();
1347             parcel.inputInterfaceIndex = upstreamIfindex;
1348             parcel.outputInterfaceIndex = downstreamIfindex;
1349             parcel.destination = address.getAddress();
1350             parcel.prefixLength = 128;
1351             parcel.srcL2Address = srcMac.toByteArray();
1352             parcel.dstL2Address = dstMac.toByteArray();
1353             return parcel;
1354         }
1355 
1356         /**
1357          * Return a TetherDownstream6Key object built from the rule.
1358          */
1359         @NonNull
makeTetherDownstream6Key()1360         public TetherDownstream6Key makeTetherDownstream6Key() {
1361             return new TetherDownstream6Key(upstreamIfindex, NULL_MAC_ADDRESS,
1362                     address.getAddress());
1363         }
1364 
1365         /**
1366          * Return a Tether6Value object built from the rule.
1367          */
1368         @NonNull
makeTether6Value()1369         public Tether6Value makeTether6Value() {
1370             return new Tether6Value(downstreamIfindex, dstMac, srcMac, ETH_P_IPV6,
1371                     NetworkStackConstants.ETHER_MTU);
1372         }
1373 
1374         @Override
equals(Object o)1375         public boolean equals(Object o) {
1376             if (!(o instanceof Ipv6ForwardingRule)) return false;
1377             Ipv6ForwardingRule that = (Ipv6ForwardingRule) o;
1378             return this.upstreamIfindex == that.upstreamIfindex
1379                     && this.downstreamIfindex == that.downstreamIfindex
1380                     && Objects.equals(this.address, that.address)
1381                     && Objects.equals(this.srcMac, that.srcMac)
1382                     && Objects.equals(this.dstMac, that.dstMac);
1383         }
1384 
1385         @Override
hashCode()1386         public int hashCode() {
1387             // TODO: if this is ever used in production code, don't pass ifindices
1388             // to Objects.hash() to avoid autoboxing overhead.
1389             return Objects.hash(upstreamIfindex, downstreamIfindex, address, srcMac, dstMac);
1390         }
1391     }
1392 
1393     /** Tethering client information class. */
1394     public static class ClientInfo {
1395         public final int downstreamIfindex;
1396 
1397         @NonNull
1398         public final MacAddress downstreamMac;
1399         @NonNull
1400         public final Inet4Address clientAddress;
1401         @NonNull
1402         public final MacAddress clientMac;
1403 
ClientInfo(int downstreamIfindex, @NonNull MacAddress downstreamMac, @NonNull Inet4Address clientAddress, @NonNull MacAddress clientMac)1404         public ClientInfo(int downstreamIfindex,
1405                 @NonNull MacAddress downstreamMac, @NonNull Inet4Address clientAddress,
1406                 @NonNull MacAddress clientMac) {
1407             this.downstreamIfindex = downstreamIfindex;
1408             this.downstreamMac = downstreamMac;
1409             this.clientAddress = clientAddress;
1410             this.clientMac = clientMac;
1411         }
1412 
1413         @Override
equals(Object o)1414         public boolean equals(Object o) {
1415             if (!(o instanceof ClientInfo)) return false;
1416             ClientInfo that = (ClientInfo) o;
1417             return this.downstreamIfindex == that.downstreamIfindex
1418                     && Objects.equals(this.downstreamMac, that.downstreamMac)
1419                     && Objects.equals(this.clientAddress, that.clientAddress)
1420                     && Objects.equals(this.clientMac, that.clientMac);
1421         }
1422 
1423         @Override
hashCode()1424         public int hashCode() {
1425             return Objects.hash(downstreamIfindex, downstreamMac, clientAddress, clientMac);
1426         }
1427 
1428         @Override
toString()1429         public String toString() {
1430             return String.format("downstream: %d (%s), client: %s (%s)",
1431                     downstreamIfindex, downstreamMac, clientAddress, clientMac);
1432         }
1433     }
1434 
1435     /**
1436      * A BPF tethering stats provider to provide network statistics to the system.
1437      * Note that this class' data may only be accessed on the handler thread.
1438      */
1439     @VisibleForTesting
1440     class BpfTetherStatsProvider extends NetworkStatsProvider {
1441         // The offloaded traffic statistics per interface that has not been reported since the
1442         // last call to pushTetherStats. Only the interfaces that were ever tethering upstreams
1443         // and has pending tether stats delta are included in this NetworkStats object.
1444         private NetworkStats mIfaceStats = new NetworkStats(0L, 0);
1445 
1446         // The same stats as above, but counts network stats per uid.
1447         private NetworkStats mUidStats = new NetworkStats(0L, 0);
1448 
1449         @Override
onRequestStatsUpdate(int token)1450         public void onRequestStatsUpdate(int token) {
1451             mHandler.post(() -> pushTetherStats());
1452         }
1453 
1454         @Override
onSetAlert(long quotaBytes)1455         public void onSetAlert(long quotaBytes) {
1456             mHandler.post(() -> updateAlertQuota(quotaBytes));
1457         }
1458 
1459         @Override
onSetLimit(@onNull String iface, long quotaBytes)1460         public void onSetLimit(@NonNull String iface, long quotaBytes) {
1461             if (quotaBytes < QUOTA_UNLIMITED) {
1462                 throw new IllegalArgumentException("invalid quota value " + quotaBytes);
1463             }
1464 
1465             mHandler.post(() -> {
1466                 final Long curIfaceQuota = mInterfaceQuotas.get(iface);
1467 
1468                 if (null == curIfaceQuota && QUOTA_UNLIMITED == quotaBytes) return;
1469 
1470                 if (quotaBytes == QUOTA_UNLIMITED) {
1471                     mInterfaceQuotas.remove(iface);
1472                 } else {
1473                     mInterfaceQuotas.put(iface, quotaBytes);
1474                 }
1475                 maybeUpdateDataLimit(iface);
1476             });
1477         }
1478 
1479         @VisibleForTesting
pushTetherStats()1480         void pushTetherStats() {
1481             try {
1482                 // The token is not used for now. See b/153606961.
1483                 notifyStatsUpdated(0 /* token */, mIfaceStats, mUidStats);
1484 
1485                 // Clear the accumulated tether stats delta after reported. Note that create a new
1486                 // empty object because NetworkStats#clear is @hide.
1487                 mIfaceStats = new NetworkStats(0L, 0);
1488                 mUidStats = new NetworkStats(0L, 0);
1489             } catch (RuntimeException e) {
1490                 mLog.e("Cannot report network stats: ", e);
1491             }
1492         }
1493 
accumulateDiff(@onNull NetworkStats ifaceDiff, @NonNull NetworkStats uidDiff)1494         private void accumulateDiff(@NonNull NetworkStats ifaceDiff,
1495                 @NonNull NetworkStats uidDiff) {
1496             mIfaceStats = mIfaceStats.add(ifaceDiff);
1497             mUidStats = mUidStats.add(uidDiff);
1498         }
1499     }
1500 
1501     @Nullable
getClientInfo(@onNull Inet4Address clientAddress)1502     private ClientInfo getClientInfo(@NonNull Inet4Address clientAddress) {
1503         for (HashMap<Inet4Address, ClientInfo> clients : mTetherClients.values()) {
1504             for (ClientInfo client : clients.values()) {
1505                 if (clientAddress.equals(client.clientAddress)) {
1506                     return client;
1507                 }
1508             }
1509         }
1510         return null;
1511     }
1512 
1513     @NonNull
1514     @VisibleForTesting
toIpv4MappedAddressBytes(Inet4Address ia4)1515     static byte[] toIpv4MappedAddressBytes(Inet4Address ia4) {
1516         final byte[] addr4 = ia4.getAddress();
1517         final byte[] addr6 = new byte[16];
1518         addr6[10] = (byte) 0xff;
1519         addr6[11] = (byte) 0xff;
1520         addr6[12] = addr4[0];
1521         addr6[13] = addr4[1];
1522         addr6[14] = addr4[2];
1523         addr6[15] = addr4[3];
1524         return addr6;
1525     }
1526 
1527     // TODO: parse CTA_PROTOINFO of conntrack event in ConntrackMonitor. For TCP, only add rules
1528     // while TCP status is established.
1529     @VisibleForTesting
1530     class BpfConntrackEventConsumer implements ConntrackEventConsumer {
1531         // The upstream4 and downstream4 rules are built as the following tables. Only raw ip
1532         // upstream interface is supported. Note that the field "lastUsed" is only updated by
1533         // BPF program which records the last used time for a given rule.
1534         // TODO: support ether ip upstream interface.
1535         //
1536         // NAT network topology:
1537         //
1538         //         public network (rawip)                 private network
1539         //                   |                 UE                |
1540         // +------------+    V    +------------+------------+    V    +------------+
1541         // |   Sever    +---------+  Upstream  | Downstream +---------+   Client   |
1542         // +------------+         +------------+------------+         +------------+
1543         //
1544         // upstream4 key and value:
1545         //
1546         // +------+------------------------------------------------+
1547         // |      |      TetherUpstream4Key                        |
1548         // +------+------+------+------+------+------+------+------+
1549         // |field |iif   |dstMac|l4prot|src4  |dst4  |srcPor|dstPor|
1550         // |      |      |      |o     |      |      |t     |t     |
1551         // +------+------+------+------+------+------+------+------+
1552         // |value |downst|downst|tcp/  |client|server|client|server|
1553         // |      |ream  |ream  |udp   |      |      |      |      |
1554         // +------+------+------+------+------+------+------+------+
1555         //
1556         // +------+---------------------------------------------------------------------+
1557         // |      |      TetherUpstream4Value                                           |
1558         // +------+------+------+------+------+------+------+------+------+------+------+
1559         // |field |oif   |ethDst|ethSrc|ethPro|pmtu  |src46 |dst46 |srcPor|dstPor|lastUs|
1560         // |      |      |mac   |mac   |to    |      |      |      |t     |t     |ed    |
1561         // +------+------+------+------+------+------+------+------+------+------+------+
1562         // |value |upstre|--    |--    |ETH_P_|1500  |upstre|server|upstre|server|--    |
1563         // |      |am    |      |      |IP    |      |am    |      |am    |      |      |
1564         // +------+------+------+------+------+------+------+------+------+------+------+
1565         //
1566         // downstream4 key and value:
1567         //
1568         // +------+------------------------------------------------+
1569         // |      |      TetherDownstream4Key                      |
1570         // +------+------+------+------+------+------+------+------+
1571         // |field |iif   |dstMac|l4prot|src4  |dst4  |srcPor|dstPor|
1572         // |      |      |      |o     |      |      |t     |t     |
1573         // +------+------+------+------+------+------+------+------+
1574         // |value |upstre|--    |tcp/  |server|upstre|server|upstre|
1575         // |      |am    |      |udp   |      |am    |      |am    |
1576         // +------+------+------+------+------+------+------+------+
1577         //
1578         // +------+---------------------------------------------------------------------+
1579         // |      |      TetherDownstream4Value                                         |
1580         // +------+------+------+------+------+------+------+------+------+------+------+
1581         // |field |oif   |ethDst|ethSrc|ethPro|pmtu  |src46 |dst46 |srcPor|dstPor|lastUs|
1582         // |      |      |mac   |mac   |to    |      |      |      |t     |t     |ed    |
1583         // +------+------+------+------+------+------+------+------+------+------+------+
1584         // |value |downst|client|downst|ETH_P_|1500  |server|client|server|client|--    |
1585         // |      |ream  |      |ream  |IP    |      |      |      |      |      |      |
1586         // +------+------+------+------+------+------+------+------+------+------+------+
1587         //
1588         @NonNull
makeTetherUpstream4Key( @onNull ConntrackEvent e, @NonNull ClientInfo c)1589         private Tether4Key makeTetherUpstream4Key(
1590                 @NonNull ConntrackEvent e, @NonNull ClientInfo c) {
1591             return new Tether4Key(c.downstreamIfindex, c.downstreamMac,
1592                     e.tupleOrig.protoNum, e.tupleOrig.srcIp.getAddress(),
1593                     e.tupleOrig.dstIp.getAddress(), e.tupleOrig.srcPort, e.tupleOrig.dstPort);
1594         }
1595 
1596         @NonNull
makeTetherDownstream4Key( @onNull ConntrackEvent e, @NonNull ClientInfo c, int upstreamIndex)1597         private Tether4Key makeTetherDownstream4Key(
1598                 @NonNull ConntrackEvent e, @NonNull ClientInfo c, int upstreamIndex) {
1599             return new Tether4Key(upstreamIndex, NULL_MAC_ADDRESS /* dstMac (rawip) */,
1600                     e.tupleReply.protoNum, e.tupleReply.srcIp.getAddress(),
1601                     e.tupleReply.dstIp.getAddress(), e.tupleReply.srcPort, e.tupleReply.dstPort);
1602         }
1603 
1604         @NonNull
makeTetherUpstream4Value(@onNull ConntrackEvent e, int upstreamIndex)1605         private Tether4Value makeTetherUpstream4Value(@NonNull ConntrackEvent e,
1606                 int upstreamIndex) {
1607             return new Tether4Value(upstreamIndex,
1608                     NULL_MAC_ADDRESS /* ethDstMac (rawip) */,
1609                     NULL_MAC_ADDRESS /* ethSrcMac (rawip) */, ETH_P_IP,
1610                     NetworkStackConstants.ETHER_MTU, toIpv4MappedAddressBytes(e.tupleReply.dstIp),
1611                     toIpv4MappedAddressBytes(e.tupleReply.srcIp), e.tupleReply.dstPort,
1612                     e.tupleReply.srcPort, 0 /* lastUsed, filled by bpf prog only */);
1613         }
1614 
1615         @NonNull
makeTetherDownstream4Value(@onNull ConntrackEvent e, @NonNull ClientInfo c, int upstreamIndex)1616         private Tether4Value makeTetherDownstream4Value(@NonNull ConntrackEvent e,
1617                 @NonNull ClientInfo c, int upstreamIndex) {
1618             return new Tether4Value(c.downstreamIfindex,
1619                     c.clientMac, c.downstreamMac, ETH_P_IP, NetworkStackConstants.ETHER_MTU,
1620                     toIpv4MappedAddressBytes(e.tupleOrig.dstIp),
1621                     toIpv4MappedAddressBytes(e.tupleOrig.srcIp),
1622                     e.tupleOrig.dstPort, e.tupleOrig.srcPort,
1623                     0 /* lastUsed, filled by bpf prog only */);
1624         }
1625 
allowOffload(ConntrackEvent e)1626         private boolean allowOffload(ConntrackEvent e) {
1627             if (e.tupleOrig.protoNum != OsConstants.IPPROTO_TCP) return true;
1628             return !CollectionUtils.contains(
1629                     NON_OFFLOADED_UPSTREAM_IPV4_TCP_PORTS, e.tupleOrig.dstPort);
1630         }
1631 
accept(ConntrackEvent e)1632         public void accept(ConntrackEvent e) {
1633             if (!allowOffload(e)) return;
1634 
1635             final ClientInfo tetherClient = getClientInfo(e.tupleOrig.srcIp);
1636             if (tetherClient == null) return;
1637 
1638             final Integer upstreamIndex = mIpv4UpstreamIndices.get(e.tupleReply.dstIp);
1639             if (upstreamIndex == null) return;
1640 
1641             final Tether4Key upstream4Key = makeTetherUpstream4Key(e, tetherClient);
1642             final Tether4Key downstream4Key = makeTetherDownstream4Key(e, tetherClient,
1643                     upstreamIndex);
1644 
1645             if (e.msgType == (NetlinkConstants.NFNL_SUBSYS_CTNETLINK << 8
1646                     | NetlinkConstants.IPCTNL_MSG_CT_DELETE)) {
1647                 final boolean deletedUpstream = mBpfCoordinatorShim.tetherOffloadRuleRemove(
1648                         UPSTREAM, upstream4Key);
1649                 final boolean deletedDownstream = mBpfCoordinatorShim.tetherOffloadRuleRemove(
1650                         DOWNSTREAM, downstream4Key);
1651 
1652                 if (!deletedUpstream && !deletedDownstream) {
1653                     // The rules may have been already removed by losing client or losing upstream.
1654                     return;
1655                 }
1656 
1657                 if (deletedUpstream != deletedDownstream) {
1658                     Log.wtf(TAG, "The bidirectional rules should be removed concurrently ("
1659                             + "upstream: " + deletedUpstream
1660                             + ", downstream: " + deletedDownstream + ")");
1661                     return;
1662                 }
1663 
1664                 maybeClearLimit(upstreamIndex);
1665                 return;
1666             }
1667 
1668             final Tether4Value upstream4Value = makeTetherUpstream4Value(e, upstreamIndex);
1669             final Tether4Value downstream4Value = makeTetherDownstream4Value(e, tetherClient,
1670                     upstreamIndex);
1671 
1672             maybeAddDevMap(upstreamIndex, tetherClient.downstreamIfindex);
1673             maybeSetLimit(upstreamIndex);
1674             mBpfCoordinatorShim.tetherOffloadRuleAdd(UPSTREAM, upstream4Key, upstream4Value);
1675             mBpfCoordinatorShim.tetherOffloadRuleAdd(DOWNSTREAM, downstream4Key, downstream4Value);
1676         }
1677     }
1678 
isBpfEnabled()1679     private boolean isBpfEnabled() {
1680         final TetheringConfiguration config = mDeps.getTetherConfig();
1681         return (config != null) ? config.isBpfOffloadEnabled() : true /* default value */;
1682     }
1683 
getInterfaceIndexFromRules(@onNull String ifName)1684     private int getInterfaceIndexFromRules(@NonNull String ifName) {
1685         for (LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules : mIpv6ForwardingRules
1686                 .values()) {
1687             for (Ipv6ForwardingRule rule : rules.values()) {
1688                 final int upstreamIfindex = rule.upstreamIfindex;
1689                 if (TextUtils.equals(ifName, mInterfaceNames.get(upstreamIfindex))) {
1690                     return upstreamIfindex;
1691                 }
1692             }
1693         }
1694         return 0;
1695     }
1696 
getQuotaBytes(@onNull String iface)1697     private long getQuotaBytes(@NonNull String iface) {
1698         final Long limit = mInterfaceQuotas.get(iface);
1699         final long quotaBytes = (limit != null) ? limit : QUOTA_UNLIMITED;
1700 
1701         return quotaBytes;
1702     }
1703 
sendDataLimitToBpfMap(int ifIndex, long quotaBytes)1704     private boolean sendDataLimitToBpfMap(int ifIndex, long quotaBytes) {
1705         if (ifIndex == 0) {
1706             Log.wtf(TAG, "Invalid interface index.");
1707             return false;
1708         }
1709 
1710         return mBpfCoordinatorShim.tetherOffloadSetInterfaceQuota(ifIndex, quotaBytes);
1711     }
1712 
1713     // Handle the data limit update from the service which is the stats provider registered for.
maybeUpdateDataLimit(@onNull String iface)1714     private void maybeUpdateDataLimit(@NonNull String iface) {
1715         // Set data limit only on a given upstream which has at least one rule. If we can't get
1716         // an interface index for a given interface name, it means either there is no rule for
1717         // a given upstream or the interface name is not an upstream which is monitored by the
1718         // coordinator.
1719         final int ifIndex = getInterfaceIndexFromRules(iface);
1720         if (ifIndex == 0) return;
1721 
1722         final long quotaBytes = getQuotaBytes(iface);
1723         sendDataLimitToBpfMap(ifIndex, quotaBytes);
1724     }
1725 
1726     // Handle the data limit update while adding forwarding rules.
updateDataLimit(int ifIndex)1727     private boolean updateDataLimit(int ifIndex) {
1728         final String iface = mInterfaceNames.get(ifIndex);
1729         if (iface == null) {
1730             mLog.e("Fail to get the interface name for index " + ifIndex);
1731             return false;
1732         }
1733         final long quotaBytes = getQuotaBytes(iface);
1734         return sendDataLimitToBpfMap(ifIndex, quotaBytes);
1735     }
1736 
maybeSetLimit(int upstreamIfindex)1737     private void maybeSetLimit(int upstreamIfindex) {
1738         if (isAnyRuleOnUpstream(upstreamIfindex)
1739                 || mBpfCoordinatorShim.isAnyIpv4RuleOnUpstream(upstreamIfindex)) {
1740             return;
1741         }
1742 
1743         // If failed to set a data limit, probably should not use this upstream, because
1744         // the upstream may not want to blow through the data limit that was told to apply.
1745         // TODO: Perhaps stop the coordinator.
1746         boolean success = updateDataLimit(upstreamIfindex);
1747         if (!success) {
1748             final String iface = mInterfaceNames.get(upstreamIfindex);
1749             mLog.e("Setting data limit for " + iface + " failed.");
1750         }
1751     }
1752 
1753     // TODO: This should be also called while IpServer wants to clear all IPv4 rules. Relying on
1754     // conntrack event can't cover this case.
maybeClearLimit(int upstreamIfindex)1755     private void maybeClearLimit(int upstreamIfindex) {
1756         if (isAnyRuleOnUpstream(upstreamIfindex)
1757                 || mBpfCoordinatorShim.isAnyIpv4RuleOnUpstream(upstreamIfindex)) {
1758             return;
1759         }
1760 
1761         final TetherStatsValue statsValue =
1762                 mBpfCoordinatorShim.tetherOffloadGetAndClearStats(upstreamIfindex);
1763         if (statsValue == null) {
1764             Log.wtf(TAG, "Fail to cleanup tether stats for upstream index " + upstreamIfindex);
1765             return;
1766         }
1767 
1768         SparseArray<TetherStatsValue> tetherStatsList = new SparseArray<TetherStatsValue>();
1769         tetherStatsList.put(upstreamIfindex, statsValue);
1770 
1771         // Update the last stats delta and delete the local cache for a given upstream.
1772         updateQuotaAndStatsFromSnapshot(tetherStatsList);
1773         mStats.remove(upstreamIfindex);
1774     }
1775 
1776     // TODO: Rename to isAnyIpv6RuleOnUpstream and define an isAnyRuleOnUpstream method that called
1777     // both isAnyIpv6RuleOnUpstream and mBpfCoordinatorShim.isAnyIpv4RuleOnUpstream.
isAnyRuleOnUpstream(int upstreamIfindex)1778     private boolean isAnyRuleOnUpstream(int upstreamIfindex) {
1779         for (LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules : mIpv6ForwardingRules
1780                 .values()) {
1781             for (Ipv6ForwardingRule rule : rules.values()) {
1782                 if (upstreamIfindex == rule.upstreamIfindex) return true;
1783             }
1784         }
1785         return false;
1786     }
1787 
isAnyRuleFromDownstreamToUpstream(int downstreamIfindex, int upstreamIfindex)1788     private boolean isAnyRuleFromDownstreamToUpstream(int downstreamIfindex, int upstreamIfindex) {
1789         for (LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules : mIpv6ForwardingRules
1790                 .values()) {
1791             for (Ipv6ForwardingRule rule : rules.values()) {
1792                 if (downstreamIfindex == rule.downstreamIfindex
1793                         && upstreamIfindex == rule.upstreamIfindex) {
1794                     return true;
1795                 }
1796             }
1797         }
1798         return false;
1799     }
1800 
1801     // TODO: remove the index from map while the interface has been removed because the map size
1802     // is 64 entries. See packages\modules\Connectivity\Tethering\bpf_progs\offload.c.
maybeAddDevMap(int upstreamIfindex, int downstreamIfindex)1803     private void maybeAddDevMap(int upstreamIfindex, int downstreamIfindex) {
1804         for (Integer index : new Integer[] {upstreamIfindex, downstreamIfindex}) {
1805             if (mDeviceMapSet.contains(index)) continue;
1806             if (mBpfCoordinatorShim.addDevMap(index)) mDeviceMapSet.add(index);
1807         }
1808     }
1809 
forwardingPairAdd(@onNull String intIface, @NonNull String extIface)1810     private void forwardingPairAdd(@NonNull String intIface, @NonNull String extIface) {
1811         if (!mForwardingPairs.containsKey(extIface)) {
1812             mForwardingPairs.put(extIface, new HashSet<String>());
1813         }
1814         mForwardingPairs.get(extIface).add(intIface);
1815     }
1816 
forwardingPairRemove(@onNull String intIface, @NonNull String extIface)1817     private void forwardingPairRemove(@NonNull String intIface, @NonNull String extIface) {
1818         HashSet<String> downstreams = mForwardingPairs.get(extIface);
1819         if (downstreams == null) return;
1820         if (!downstreams.remove(intIface)) return;
1821 
1822         if (downstreams.isEmpty()) {
1823             mForwardingPairs.remove(extIface);
1824         }
1825     }
1826 
forwardingPairExists(@onNull String intIface, @NonNull String extIface)1827     private boolean forwardingPairExists(@NonNull String intIface, @NonNull String extIface) {
1828         if (!mForwardingPairs.containsKey(extIface)) return false;
1829 
1830         return mForwardingPairs.get(extIface).contains(intIface);
1831     }
1832 
isAnyForwardingPairOnUpstream(@onNull String extIface)1833     private boolean isAnyForwardingPairOnUpstream(@NonNull String extIface) {
1834         return mForwardingPairs.containsKey(extIface);
1835     }
1836 
1837     @NonNull
buildNetworkStats(@onNull StatsType type, int ifIndex, @NonNull final ForwardedStats diff)1838     private NetworkStats buildNetworkStats(@NonNull StatsType type, int ifIndex,
1839             @NonNull final ForwardedStats diff) {
1840         NetworkStats stats = new NetworkStats(0L, 0);
1841         final String iface = mInterfaceNames.get(ifIndex);
1842         if (iface == null) {
1843             // TODO: Use Log.wtf once the coordinator owns full control of tether stats from netd.
1844             // For now, netd may add the empty stats for the upstream which is not monitored by
1845             // the coordinator. Silently ignore it.
1846             return stats;
1847         }
1848         final int uid = (type == StatsType.STATS_PER_UID) ? UID_TETHERING : UID_ALL;
1849         // Note that the argument 'metered', 'roaming' and 'defaultNetwork' are not recorded for
1850         // network stats snapshot. See NetworkStatsRecorder#recordSnapshotLocked.
1851         return stats.addEntry(new Entry(iface, uid, SET_DEFAULT, TAG_NONE, METERED_NO,
1852                 ROAMING_NO, DEFAULT_NETWORK_NO, diff.rxBytes, diff.rxPackets,
1853                 diff.txBytes, diff.txPackets, 0L /* operations */));
1854     }
1855 
updateAlertQuota(long newQuota)1856     private void updateAlertQuota(long newQuota) {
1857         if (newQuota < QUOTA_UNLIMITED) {
1858             throw new IllegalArgumentException("invalid quota value " + newQuota);
1859         }
1860         if (mRemainingAlertQuota == newQuota) return;
1861 
1862         mRemainingAlertQuota = newQuota;
1863         if (mRemainingAlertQuota == 0) {
1864             mLog.i("onAlertReached");
1865             if (mStatsProvider != null) mStatsProvider.notifyAlertReached();
1866         }
1867     }
1868 
updateQuotaAndStatsFromSnapshot( @onNull final SparseArray<TetherStatsValue> tetherStatsList)1869     private void updateQuotaAndStatsFromSnapshot(
1870             @NonNull final SparseArray<TetherStatsValue> tetherStatsList) {
1871         long usedAlertQuota = 0;
1872         for (int i = 0; i < tetherStatsList.size(); i++) {
1873             final Integer ifIndex = tetherStatsList.keyAt(i);
1874             final TetherStatsValue tetherStats = tetherStatsList.valueAt(i);
1875             final ForwardedStats curr = new ForwardedStats(tetherStats);
1876             final ForwardedStats base = mStats.get(ifIndex);
1877             final ForwardedStats diff = (base != null) ? curr.subtract(base) : curr;
1878             usedAlertQuota += diff.rxBytes + diff.txBytes;
1879 
1880             // Update the local cache for counting tether stats delta.
1881             mStats.put(ifIndex, curr);
1882 
1883             // Update the accumulated tether stats delta to the stats provider for the service
1884             // querying.
1885             if (mStatsProvider != null) {
1886                 try {
1887                     mStatsProvider.accumulateDiff(
1888                             buildNetworkStats(StatsType.STATS_PER_IFACE, ifIndex, diff),
1889                             buildNetworkStats(StatsType.STATS_PER_UID, ifIndex, diff));
1890                 } catch (ArrayIndexOutOfBoundsException e) {
1891                     Log.wtf(TAG, "Fail to update the accumulated stats delta for interface index "
1892                             + ifIndex + " : ", e);
1893                 }
1894             }
1895         }
1896 
1897         if (mRemainingAlertQuota > 0 && usedAlertQuota > 0) {
1898             // Trim to zero if overshoot.
1899             final long newQuota = Math.max(mRemainingAlertQuota - usedAlertQuota, 0);
1900             updateAlertQuota(newQuota);
1901         }
1902 
1903         // TODO: Count the used limit quota for notifying data limit reached.
1904     }
1905 
updateForwardedStats()1906     private void updateForwardedStats() {
1907         final SparseArray<TetherStatsValue> tetherStatsList =
1908                 mBpfCoordinatorShim.tetherOffloadGetStats();
1909 
1910         if (tetherStatsList == null) {
1911             mLog.e("Problem fetching tethering stats");
1912             return;
1913         }
1914 
1915         updateQuotaAndStatsFromSnapshot(tetherStatsList);
1916     }
1917 
1918     @VisibleForTesting
getPollingInterval()1919     int getPollingInterval() {
1920         // The valid range of interval is DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS..max_long.
1921         // Ignore the config value is less than the minimum polling interval. Note that the
1922         // minimum interval definition is invoked as OffloadController#isPollingStatsNeeded does.
1923         // TODO: Perhaps define a minimum polling interval constant.
1924         final TetheringConfiguration config = mDeps.getTetherConfig();
1925         final int configInterval = (config != null) ? config.getOffloadPollInterval() : 0;
1926         return Math.max(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS, configInterval);
1927     }
1928 
1929     @Nullable
parseIPv4Address(byte[] addrBytes)1930     private Inet4Address parseIPv4Address(byte[] addrBytes) {
1931         try {
1932             final InetAddress ia = Inet4Address.getByAddress(addrBytes);
1933             if (ia instanceof Inet4Address) return (Inet4Address) ia;
1934         } catch (UnknownHostException e) {
1935             mLog.e("Failed to parse IPv4 address: " + e);
1936         }
1937         return null;
1938     }
1939 
1940     // Update CTA_TUPLE_ORIG timeout for a given conntrack entry. Note that there will also be
1941     // coming a conntrack event to notify updated timeout.
updateConntrackTimeout(byte proto, Inet4Address src4, short srcPort, Inet4Address dst4, short dstPort)1942     private void updateConntrackTimeout(byte proto, Inet4Address src4, short srcPort,
1943             Inet4Address dst4, short dstPort) {
1944         if (src4 == null || dst4 == null) {
1945             mLog.e("Either source or destination IPv4 address is invalid ("
1946                     + "proto: " + proto + ", "
1947                     + "src4: " + src4 + ", "
1948                     + "srcPort: " + Short.toUnsignedInt(srcPort) + ", "
1949                     + "dst4: " + dst4 + ", "
1950                     + "dstPort: " + Short.toUnsignedInt(dstPort) + ")");
1951             return;
1952         }
1953 
1954         // TODO: consider acquiring the timeout setting from nf_conntrack_* variables.
1955         // - proc/sys/net/netfilter/nf_conntrack_tcp_timeout_established
1956         // - proc/sys/net/netfilter/nf_conntrack_udp_timeout_stream
1957         // See kernel document nf_conntrack-sysctl.txt.
1958         final int timeoutSec = (proto == OsConstants.IPPROTO_TCP)
1959                 ? NF_CONNTRACK_TCP_TIMEOUT_ESTABLISHED
1960                 : NF_CONNTRACK_UDP_TIMEOUT_STREAM;
1961         final byte[] msg = ConntrackMessage.newIPv4TimeoutUpdateRequest(
1962                 proto, src4, (int) srcPort, dst4, (int) dstPort, timeoutSec);
1963         try {
1964             NetlinkSocket.sendOneShotKernelMessage(OsConstants.NETLINK_NETFILTER, msg);
1965         } catch (ErrnoException e) {
1966             // Lower the log level for the entry not existing. The conntrack entry may have been
1967             // deleted and not handled by the conntrack event monitor yet. In other words, the
1968             // rule has not been deleted from the BPF map yet. Deleting a non-existent entry may
1969             // happen during the conntrack timeout refreshing iteration. Note that ENOENT may be
1970             // a real error but is hard to distinguish.
1971             // TODO: Figure out a better way to handle this.
1972             final String errMsg = "Failed to update conntrack entry ("
1973                     + "proto: " + proto + ", "
1974                     + "src4: " + src4 + ", "
1975                     + "srcPort: " + Short.toUnsignedInt(srcPort) + ", "
1976                     + "dst4: " + dst4 + ", "
1977                     + "dstPort: " + Short.toUnsignedInt(dstPort) + "), "
1978                     + "msg: " + NetlinkConstants.hexify(msg) + ", "
1979                     + "e: " + e;
1980             if (OsConstants.ENOENT == e.errno) {
1981                 mLog.w(errMsg);
1982             } else {
1983                 mLog.e(errMsg);
1984             }
1985         }
1986     }
1987 
refreshAllConntrackTimeouts()1988     private void refreshAllConntrackTimeouts() {
1989         final long now = mDeps.elapsedRealtimeNanos();
1990 
1991         // TODO: Consider ignoring TCP traffic on upstream and monitor on downstream only
1992         // because TCP is a bidirectional traffic. Probably don't need to extend timeout by
1993         // both directions for TCP.
1994         mBpfCoordinatorShim.tetherOffloadRuleForEach(UPSTREAM, (k, v) -> {
1995             if ((now - v.lastUsed) / 1_000_000 < CONNTRACK_TIMEOUT_UPDATE_INTERVAL_MS) {
1996                 updateConntrackTimeout((byte) k.l4proto,
1997                         parseIPv4Address(k.src4), (short) k.srcPort,
1998                         parseIPv4Address(k.dst4), (short) k.dstPort);
1999             }
2000         });
2001 
2002         // Reverse the source and destination {address, port} from downstream value because
2003         // #updateConntrackTimeout refresh the timeout of netlink attribute CTA_TUPLE_ORIG
2004         // which is opposite direction for downstream map value.
2005         mBpfCoordinatorShim.tetherOffloadRuleForEach(DOWNSTREAM, (k, v) -> {
2006             if ((now - v.lastUsed) / 1_000_000 < CONNTRACK_TIMEOUT_UPDATE_INTERVAL_MS) {
2007                 updateConntrackTimeout((byte) k.l4proto,
2008                         parseIPv4Address(v.dst46), (short) v.dstPort,
2009                         parseIPv4Address(v.src46), (short) v.srcPort);
2010             }
2011         });
2012     }
2013 
maybeSchedulePollingStats()2014     private void maybeSchedulePollingStats() {
2015         if (!mPollingStarted) return;
2016 
2017         if (mHandler.hasCallbacks(mScheduledPollingStats)) {
2018             mHandler.removeCallbacks(mScheduledPollingStats);
2019         }
2020 
2021         mHandler.postDelayed(mScheduledPollingStats, getPollingInterval());
2022     }
2023 
maybeScheduleConntrackTimeoutUpdate()2024     private void maybeScheduleConntrackTimeoutUpdate() {
2025         if (!mPollingStarted) return;
2026 
2027         if (mHandler.hasCallbacks(mScheduledConntrackTimeoutUpdate)) {
2028             mHandler.removeCallbacks(mScheduledConntrackTimeoutUpdate);
2029         }
2030 
2031         mHandler.postDelayed(mScheduledConntrackTimeoutUpdate,
2032                 CONNTRACK_TIMEOUT_UPDATE_INTERVAL_MS);
2033     }
2034 
2035     // Return forwarding rule map. This is used for testing only.
2036     // Note that this can be only called on handler thread.
2037     @NonNull
2038     @VisibleForTesting
2039     final HashMap<IpServer, LinkedHashMap<Inet6Address, Ipv6ForwardingRule>>
getForwardingRulesForTesting()2040             getForwardingRulesForTesting() {
2041         return mIpv6ForwardingRules;
2042     }
2043 
2044     // Return upstream interface name map. This is used for testing only.
2045     // Note that this can be only called on handler thread.
2046     @NonNull
2047     @VisibleForTesting
getInterfaceNamesForTesting()2048     final SparseArray<String> getInterfaceNamesForTesting() {
2049         return mInterfaceNames;
2050     }
2051 
2052     // Return BPF conntrack event consumer. This is used for testing only.
2053     // Note that this can be only called on handler thread.
2054     @NonNull
2055     @VisibleForTesting
getBpfConntrackEventConsumerForTesting()2056     final BpfConntrackEventConsumer getBpfConntrackEventConsumerForTesting() {
2057         return mBpfConntrackEventConsumer;
2058     }
2059 
2060     // Return tethering client information. This is used for testing only.
2061     @NonNull
2062     @VisibleForTesting
2063     final HashMap<IpServer, HashMap<Inet4Address, ClientInfo>>
getTetherClientsForTesting()2064             getTetherClientsForTesting() {
2065         return mTetherClients;
2066     }
2067 
getBpfCounterNames()2068     private static native String[] getBpfCounterNames();
2069 }
2070