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