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