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