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