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