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