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