1 /* 2 * Copyright (C) 2017 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.provider.Settings.Global.TETHER_OFFLOAD_DISABLED; 28 29 import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_1_0; 30 import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_1_1; 31 import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_NONE; 32 import static com.android.networkstack.tethering.TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS; 33 34 import android.annotation.NonNull; 35 import android.annotation.Nullable; 36 import android.app.usage.NetworkStatsManager; 37 import android.content.ContentResolver; 38 import android.net.InetAddresses; 39 import android.net.IpPrefix; 40 import android.net.LinkAddress; 41 import android.net.LinkProperties; 42 import android.net.NetworkStats; 43 import android.net.NetworkStats.Entry; 44 import android.net.RouteInfo; 45 import android.net.netlink.ConntrackMessage; 46 import android.net.netlink.NetlinkConstants; 47 import android.net.netlink.NetlinkSocket; 48 import android.net.netstats.provider.NetworkStatsProvider; 49 import android.net.util.SharedLog; 50 import android.os.Handler; 51 import android.provider.Settings; 52 import android.system.ErrnoException; 53 import android.system.OsConstants; 54 import android.text.TextUtils; 55 import android.util.Log; 56 57 import com.android.internal.annotations.VisibleForTesting; 58 import com.android.internal.util.IndentingPrintWriter; 59 import com.android.networkstack.tethering.OffloadHardwareInterface.ForwardedStats; 60 61 import java.net.Inet4Address; 62 import java.net.Inet6Address; 63 import java.net.InetAddress; 64 import java.util.ArrayList; 65 import java.util.Collections; 66 import java.util.HashMap; 67 import java.util.HashSet; 68 import java.util.List; 69 import java.util.Map; 70 import java.util.Objects; 71 import java.util.Set; 72 import java.util.concurrent.ConcurrentHashMap; 73 74 /** 75 * A class to encapsulate the business logic of programming the tethering 76 * hardware offload interface. 77 * 78 * @hide 79 */ 80 public class OffloadController { 81 private static final String TAG = OffloadController.class.getSimpleName(); 82 private static final boolean DBG = false; 83 private static final String ANYIP = "0.0.0.0"; 84 private static final ForwardedStats EMPTY_STATS = new ForwardedStats(); 85 86 @VisibleForTesting 87 enum StatsType { 88 STATS_PER_IFACE, 89 STATS_PER_UID, 90 } 91 92 private enum UpdateType { IF_NEEDED, FORCE }; 93 94 private final Handler mHandler; 95 private final OffloadHardwareInterface mHwInterface; 96 private final ContentResolver mContentResolver; 97 @Nullable 98 private final OffloadTetheringStatsProvider mStatsProvider; 99 private final SharedLog mLog; 100 private final HashMap<String, LinkProperties> mDownstreams; 101 private boolean mConfigInitialized; 102 @OffloadHardwareInterface.OffloadHalVersion 103 private int mControlHalVersion; 104 private LinkProperties mUpstreamLinkProperties; 105 // The complete set of offload-exempt prefixes passed in via Tethering from 106 // all upstream and downstream sources. 107 private Set<IpPrefix> mExemptPrefixes; 108 // A strictly "smaller" set of prefixes, wherein offload-approved prefixes 109 // (e.g. downstream on-link prefixes) have been removed and replaced with 110 // prefixes representing only the locally-assigned IP addresses. 111 private Set<String> mLastLocalPrefixStrs; 112 113 // Maps upstream interface names to offloaded traffic statistics. 114 // Always contains the latest value received from the hardware for each interface, regardless of 115 // whether offload is currently running on that interface. 116 private ConcurrentHashMap<String, ForwardedStats> mForwardedStats = 117 new ConcurrentHashMap<>(16, 0.75F, 1); 118 119 private static class InterfaceQuota { 120 public final long warningBytes; 121 public final long limitBytes; 122 123 public static InterfaceQuota MAX_VALUE = new InterfaceQuota(Long.MAX_VALUE, Long.MAX_VALUE); 124 InterfaceQuota(long warningBytes, long limitBytes)125 InterfaceQuota(long warningBytes, long limitBytes) { 126 this.warningBytes = warningBytes; 127 this.limitBytes = limitBytes; 128 } 129 130 @Override equals(Object o)131 public boolean equals(Object o) { 132 if (this == o) return true; 133 if (!(o instanceof InterfaceQuota)) return false; 134 InterfaceQuota that = (InterfaceQuota) o; 135 return warningBytes == that.warningBytes 136 && limitBytes == that.limitBytes; 137 } 138 139 @Override hashCode()140 public int hashCode() { 141 return (int) (warningBytes * 3 + limitBytes * 5); 142 } 143 144 @Override toString()145 public String toString() { 146 return "InterfaceQuota{" + "warning=" + warningBytes + ", limit=" + limitBytes + '}'; 147 } 148 } 149 150 // Maps upstream interface names to interface quotas. 151 // Always contains the latest value received from the framework for each interface, regardless 152 // of whether offload is currently running (or is even supported) on that interface. Only 153 // includes upstream interfaces that have a quota set. 154 private HashMap<String, InterfaceQuota> mInterfaceQuotas = new HashMap<>(); 155 156 // Tracking remaining alert quota. Unlike limit quota is subject to interface, the alert 157 // quota is interface independent and global for tether offload. Note that this is only 158 // accessed on the handler thread and in the constructor. 159 private long mRemainingAlertQuota = QUOTA_UNLIMITED; 160 // Runnable that used to schedule the next stats poll. 161 private final Runnable mScheduledPollingTask = () -> { 162 updateStatsForCurrentUpstream(); 163 maybeSchedulePollingStats(); 164 }; 165 166 private int mNatUpdateCallbacksReceived; 167 private int mNatUpdateNetlinkErrors; 168 169 @NonNull 170 private final Dependencies mDeps; 171 172 // TODO: Put more parameters in constructor into dependency object. 173 interface Dependencies { 174 @NonNull getTetherConfig()175 TetheringConfiguration getTetherConfig(); 176 } 177 OffloadController(Handler h, OffloadHardwareInterface hwi, ContentResolver contentResolver, NetworkStatsManager nsm, SharedLog log, @NonNull Dependencies deps)178 public OffloadController(Handler h, OffloadHardwareInterface hwi, 179 ContentResolver contentResolver, NetworkStatsManager nsm, SharedLog log, 180 @NonNull Dependencies deps) { 181 mHandler = h; 182 mHwInterface = hwi; 183 mContentResolver = contentResolver; 184 mLog = log.forSubComponent(TAG); 185 mDownstreams = new HashMap<>(); 186 mExemptPrefixes = new HashSet<>(); 187 mLastLocalPrefixStrs = new HashSet<>(); 188 OffloadTetheringStatsProvider provider = new OffloadTetheringStatsProvider(); 189 try { 190 nsm.registerNetworkStatsProvider(getClass().getSimpleName(), provider); 191 } catch (RuntimeException e) { 192 Log.wtf(TAG, "Cannot register offload stats provider: " + e); 193 provider = null; 194 } 195 mStatsProvider = provider; 196 mDeps = deps; 197 } 198 199 /** Start hardware offload. */ start()200 public boolean start() { 201 if (started()) return true; 202 203 if (isOffloadDisabled()) { 204 mLog.i("tethering offload disabled"); 205 return false; 206 } 207 208 if (!mConfigInitialized) { 209 mConfigInitialized = mHwInterface.initOffloadConfig(); 210 if (!mConfigInitialized) { 211 mLog.i("tethering offload config not supported"); 212 stop(); 213 return false; 214 } 215 } 216 217 mControlHalVersion = mHwInterface.initOffloadControl( 218 // OffloadHardwareInterface guarantees that these callback 219 // methods are called on the handler passed to it, which is the 220 // same as mHandler, as coordinated by the setup in Tethering. 221 new OffloadHardwareInterface.ControlCallback() { 222 @Override 223 public void onStarted() { 224 if (!started()) return; 225 mLog.log("onStarted"); 226 } 227 228 @Override 229 public void onStoppedError() { 230 if (!started()) return; 231 mLog.log("onStoppedError"); 232 } 233 234 @Override 235 public void onStoppedUnsupported() { 236 if (!started()) return; 237 mLog.log("onStoppedUnsupported"); 238 // Poll for statistics and trigger a sweep of tethering 239 // stats by observers. This might not succeed, but it's 240 // worth trying anyway. We need to do this because from 241 // this point on we continue with software forwarding, 242 // and we need to synchronize stats and limits between 243 // software and hardware forwarding. 244 updateStatsForAllUpstreams(); 245 if (mStatsProvider != null) mStatsProvider.pushTetherStats(); 246 } 247 248 @Override 249 public void onSupportAvailable() { 250 if (!started()) return; 251 mLog.log("onSupportAvailable"); 252 253 // [1] Poll for statistics and trigger a sweep of stats 254 // by observers. We need to do this to ensure that any 255 // limits set take into account any software tethering 256 // traffic that has been happening in the meantime. 257 updateStatsForAllUpstreams(); 258 if (mStatsProvider != null) mStatsProvider.pushTetherStats(); 259 // [2] (Re)Push all state. 260 computeAndPushLocalPrefixes(UpdateType.FORCE); 261 pushAllDownstreamState(); 262 pushUpstreamParameters(null); 263 } 264 265 @Override 266 public void onStoppedLimitReached() { 267 if (!started()) return; 268 mLog.log("onStoppedLimitReached"); 269 270 // We cannot reliably determine on which interface the limit was reached, 271 // because the HAL interface does not specify it. We cannot just use the 272 // current upstream, because that might have changed since the time that 273 // the HAL queued the callback. 274 // TODO: rev the HAL so that it provides an interface name. 275 276 updateStatsForCurrentUpstream(); 277 if (mStatsProvider != null) { 278 mStatsProvider.pushTetherStats(); 279 // Push stats to service does not cause the service react to it 280 // immediately. Inform the service about limit reached. 281 mStatsProvider.notifyLimitReached(); 282 } 283 } 284 285 @Override 286 public void onWarningReached() { 287 if (!started()) return; 288 mLog.log("onWarningReached"); 289 290 updateStatsForCurrentUpstream(); 291 if (mStatsProvider != null) { 292 mStatsProvider.pushTetherStats(); 293 mStatsProvider.notifyWarningReached(); 294 } 295 } 296 297 @Override 298 public void onNatTimeoutUpdate(int proto, 299 String srcAddr, int srcPort, 300 String dstAddr, int dstPort) { 301 if (!started()) return; 302 updateNatTimeout(proto, srcAddr, srcPort, dstAddr, dstPort); 303 } 304 }); 305 306 final boolean isStarted = started(); 307 if (!isStarted) { 308 mLog.i("tethering offload control not supported"); 309 stop(); 310 } else { 311 mLog.log("tethering offload started, version: " 312 + OffloadHardwareInterface.halVerToString(mControlHalVersion)); 313 mNatUpdateCallbacksReceived = 0; 314 mNatUpdateNetlinkErrors = 0; 315 maybeSchedulePollingStats(); 316 } 317 return isStarted; 318 } 319 320 /** Stop hardware offload. */ stop()321 public void stop() { 322 // Completely stops tethering offload. After this method is called, it is no longer safe to 323 // call any HAL method, no callbacks from the hardware will be delivered, and any in-flight 324 // callbacks must be ignored. Offload may be started again by calling start(). 325 final boolean wasStarted = started(); 326 updateStatsForCurrentUpstream(); 327 mUpstreamLinkProperties = null; 328 mHwInterface.stopOffloadControl(); 329 mControlHalVersion = OFFLOAD_HAL_VERSION_NONE; 330 mConfigInitialized = false; 331 if (mHandler.hasCallbacks(mScheduledPollingTask)) { 332 mHandler.removeCallbacks(mScheduledPollingTask); 333 } 334 if (wasStarted) mLog.log("tethering offload stopped"); 335 } 336 started()337 private boolean started() { 338 return mConfigInitialized && mControlHalVersion != OFFLOAD_HAL_VERSION_NONE; 339 } 340 341 @VisibleForTesting 342 class OffloadTetheringStatsProvider extends NetworkStatsProvider { 343 // These stats must only ever be touched on the handler thread. 344 @NonNull 345 private NetworkStats mIfaceStats = new NetworkStats(0L, 0); 346 @NonNull 347 private NetworkStats mUidStats = new NetworkStats(0L, 0); 348 349 /** 350 * A helper function that collect tether stats from local hashmap. Note that this does not 351 * invoke binder call. 352 */ 353 @VisibleForTesting 354 @NonNull getTetherStats(@onNull StatsType how)355 NetworkStats getTetherStats(@NonNull StatsType how) { 356 NetworkStats stats = new NetworkStats(0L, 0); 357 final int uid = (how == StatsType.STATS_PER_UID) ? UID_TETHERING : UID_ALL; 358 359 for (final Map.Entry<String, ForwardedStats> kv : mForwardedStats.entrySet()) { 360 final ForwardedStats value = kv.getValue(); 361 final Entry entry = new Entry(kv.getKey(), uid, SET_DEFAULT, TAG_NONE, METERED_NO, 362 ROAMING_NO, DEFAULT_NETWORK_NO, value.rxBytes, 0L, value.txBytes, 0L, 0L); 363 stats = stats.addEntry(entry); 364 } 365 366 return stats; 367 } 368 369 @Override onSetLimit(String iface, long quotaBytes)370 public void onSetLimit(String iface, long quotaBytes) { 371 onSetWarningAndLimit(iface, QUOTA_UNLIMITED, quotaBytes); 372 } 373 374 @Override onSetWarningAndLimit(@onNull String iface, long warningBytes, long limitBytes)375 public void onSetWarningAndLimit(@NonNull String iface, 376 long warningBytes, long limitBytes) { 377 // Listen for all iface is necessary since upstream might be changed after limit 378 // is set. 379 mHandler.post(() -> { 380 final InterfaceQuota curIfaceQuota = mInterfaceQuotas.get(iface); 381 final InterfaceQuota newIfaceQuota = new InterfaceQuota( 382 warningBytes == QUOTA_UNLIMITED ? Long.MAX_VALUE : warningBytes, 383 limitBytes == QUOTA_UNLIMITED ? Long.MAX_VALUE : limitBytes); 384 385 // If the quota is set to unlimited, the value set to HAL is Long.MAX_VALUE, 386 // which is ~8.4 x 10^6 TiB, no one can actually reach it. Thus, it is not 387 // useful to set it multiple times. 388 // Otherwise, the quota needs to be updated to tell HAL to re-count from now even 389 // if the quota is the same as the existing one. 390 if (null == curIfaceQuota && InterfaceQuota.MAX_VALUE.equals(newIfaceQuota)) { 391 return; 392 } 393 394 if (InterfaceQuota.MAX_VALUE.equals(newIfaceQuota)) { 395 mInterfaceQuotas.remove(iface); 396 } else { 397 mInterfaceQuotas.put(iface, newIfaceQuota); 398 } 399 maybeUpdateDataWarningAndLimit(iface); 400 }); 401 } 402 403 /** 404 * Push stats to service, but does not cause a force polling. Note that this can only be 405 * called on the handler thread. 406 */ pushTetherStats()407 public void pushTetherStats() { 408 // TODO: remove the accumulated stats and report the diff from HAL directly. 409 final NetworkStats ifaceDiff = 410 getTetherStats(StatsType.STATS_PER_IFACE).subtract(mIfaceStats); 411 final NetworkStats uidDiff = 412 getTetherStats(StatsType.STATS_PER_UID).subtract(mUidStats); 413 try { 414 notifyStatsUpdated(0 /* token */, ifaceDiff, uidDiff); 415 mIfaceStats = mIfaceStats.add(ifaceDiff); 416 mUidStats = mUidStats.add(uidDiff); 417 } catch (RuntimeException e) { 418 mLog.e("Cannot report network stats: ", e); 419 } 420 } 421 422 @Override onRequestStatsUpdate(int token)423 public void onRequestStatsUpdate(int token) { 424 // Do not attempt to update stats by querying the offload HAL 425 // synchronously from a different thread than the Handler thread. http://b/64771555. 426 mHandler.post(() -> { 427 updateStatsForCurrentUpstream(); 428 pushTetherStats(); 429 }); 430 } 431 432 @Override onSetAlert(long quotaBytes)433 public void onSetAlert(long quotaBytes) { 434 // Ignore set alert calls from HAL V1.1 since the hardware supports set warning now. 435 // Thus, the software polling mechanism is not needed. 436 if (!useStatsPolling()) { 437 return; 438 } 439 // Post it to handler thread since it access remaining quota bytes. 440 mHandler.post(() -> { 441 updateAlertQuota(quotaBytes); 442 maybeSchedulePollingStats(); 443 }); 444 } 445 } 446 currentUpstreamInterface()447 private String currentUpstreamInterface() { 448 return (mUpstreamLinkProperties != null) 449 ? mUpstreamLinkProperties.getInterfaceName() : null; 450 } 451 maybeUpdateStats(String iface)452 private void maybeUpdateStats(String iface) { 453 if (TextUtils.isEmpty(iface)) { 454 return; 455 } 456 457 // Always called on the handler thread. 458 // 459 // Use get()/put() instead of updating ForwardedStats in place because we can be called 460 // concurrently with getTetherStats. In combination with the guarantees provided by 461 // ConcurrentHashMap, this ensures that getTetherStats always gets the most recent copy of 462 // the stats for each interface, and does not observe partial writes where rxBytes is 463 // updated and txBytes is not. 464 ForwardedStats diff = mHwInterface.getForwardedStats(iface); 465 final long usedAlertQuota = diff.rxBytes + diff.txBytes; 466 ForwardedStats base = mForwardedStats.get(iface); 467 if (base != null) { 468 diff.add(base); 469 } 470 471 // Update remaining alert quota if it is still positive. 472 if (mRemainingAlertQuota > 0 && usedAlertQuota > 0) { 473 // Trim to zero if overshoot. 474 final long newQuota = Math.max(mRemainingAlertQuota - usedAlertQuota, 0); 475 updateAlertQuota(newQuota); 476 } 477 478 mForwardedStats.put(iface, diff); 479 // diff is a new object, just created by getForwardedStats(). Therefore, anyone reading from 480 // mForwardedStats (i.e., any caller of getTetherStats) will see the new stats immediately. 481 } 482 483 /** 484 * Update remaining alert quota, fire the {@link NetworkStatsProvider#notifyAlertReached()} 485 * callback when it reaches zero. This can be invoked either from service setting the alert, or 486 * {@code maybeUpdateStats} when updating stats. Note that this can be only called on 487 * handler thread. 488 * 489 * @param newQuota non-negative value to indicate the new quota, or 490 * {@link NetworkStatsProvider#QUOTA_UNLIMITED} to indicate there is no 491 * quota. 492 */ updateAlertQuota(long newQuota)493 private void updateAlertQuota(long newQuota) { 494 if (newQuota < QUOTA_UNLIMITED) { 495 throw new IllegalArgumentException("invalid quota value " + newQuota); 496 } 497 if (mRemainingAlertQuota == newQuota) return; 498 499 mRemainingAlertQuota = newQuota; 500 if (mRemainingAlertQuota == 0) { 501 mLog.i("notifyAlertReached"); 502 if (mStatsProvider != null) mStatsProvider.notifyAlertReached(); 503 } 504 } 505 506 /** 507 * Schedule polling if needed, this will be stopped if offload has been 508 * stopped or remaining quota reaches zero or upstream is empty. 509 * Note that this can be only called on handler thread. 510 */ maybeSchedulePollingStats()511 private void maybeSchedulePollingStats() { 512 if (!isPollingStatsNeeded()) return; 513 514 if (mHandler.hasCallbacks(mScheduledPollingTask)) { 515 mHandler.removeCallbacks(mScheduledPollingTask); 516 } 517 mHandler.postDelayed(mScheduledPollingTask, 518 mDeps.getTetherConfig().getOffloadPollInterval()); 519 } 520 isPollingStatsNeeded()521 private boolean isPollingStatsNeeded() { 522 return started() && mRemainingAlertQuota > 0 523 && useStatsPolling() 524 && !TextUtils.isEmpty(currentUpstreamInterface()) 525 && mDeps.getTetherConfig() != null 526 && mDeps.getTetherConfig().getOffloadPollInterval() 527 >= DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS; 528 } 529 useStatsPolling()530 private boolean useStatsPolling() { 531 return mControlHalVersion == OFFLOAD_HAL_VERSION_1_0; 532 } 533 maybeUpdateDataWarningAndLimit(String iface)534 private boolean maybeUpdateDataWarningAndLimit(String iface) { 535 // setDataLimit or setDataWarningAndLimit may only be called while offload is occurring 536 // on this upstream. 537 if (!started() || !TextUtils.equals(iface, currentUpstreamInterface())) { 538 return true; 539 } 540 541 final InterfaceQuota quota = mInterfaceQuotas.getOrDefault(iface, InterfaceQuota.MAX_VALUE); 542 final boolean ret; 543 if (mControlHalVersion >= OFFLOAD_HAL_VERSION_1_1) { 544 ret = mHwInterface.setDataWarningAndLimit(iface, quota.warningBytes, quota.limitBytes); 545 } else { 546 ret = mHwInterface.setDataLimit(iface, quota.limitBytes); 547 } 548 return ret; 549 } 550 updateStatsForCurrentUpstream()551 private void updateStatsForCurrentUpstream() { 552 maybeUpdateStats(currentUpstreamInterface()); 553 } 554 updateStatsForAllUpstreams()555 private void updateStatsForAllUpstreams() { 556 // In practice, there should only ever be a single digit number of 557 // upstream interfaces over the lifetime of an active tethering session. 558 // Roughly speaking, imagine a very ambitious one or two of each of the 559 // following interface types: [ "rmnet_data", "wlan", "eth", "rndis" ]. 560 for (Map.Entry<String, ForwardedStats> kv : mForwardedStats.entrySet()) { 561 maybeUpdateStats(kv.getKey()); 562 } 563 } 564 565 /** Set current tethering upstream LinkProperties. */ setUpstreamLinkProperties(LinkProperties lp)566 public void setUpstreamLinkProperties(LinkProperties lp) { 567 if (!started() || Objects.equals(mUpstreamLinkProperties, lp)) return; 568 569 final String prevUpstream = currentUpstreamInterface(); 570 571 mUpstreamLinkProperties = (lp != null) ? new LinkProperties(lp) : null; 572 // Make sure we record this interface in the ForwardedStats map. 573 final String iface = currentUpstreamInterface(); 574 if (!TextUtils.isEmpty(iface)) mForwardedStats.putIfAbsent(iface, EMPTY_STATS); 575 576 maybeSchedulePollingStats(); 577 578 // TODO: examine return code and decide what to do if programming 579 // upstream parameters fails (probably just wait for a subsequent 580 // onOffloadEvent() callback to tell us offload is available again and 581 // then reapply all state). 582 computeAndPushLocalPrefixes(UpdateType.IF_NEEDED); 583 pushUpstreamParameters(prevUpstream); 584 } 585 586 /** Set local prefixes. */ setLocalPrefixes(Set<IpPrefix> localPrefixes)587 public void setLocalPrefixes(Set<IpPrefix> localPrefixes) { 588 mExemptPrefixes = localPrefixes; 589 590 if (!started()) return; 591 computeAndPushLocalPrefixes(UpdateType.IF_NEEDED); 592 } 593 594 /** Update current downstream LinkProperties. */ notifyDownstreamLinkProperties(LinkProperties lp)595 public void notifyDownstreamLinkProperties(LinkProperties lp) { 596 final String ifname = lp.getInterfaceName(); 597 final LinkProperties oldLp = mDownstreams.put(ifname, new LinkProperties(lp)); 598 if (Objects.equals(oldLp, lp)) return; 599 600 if (!started()) return; 601 pushDownstreamState(oldLp, lp); 602 } 603 pushDownstreamState(LinkProperties oldLp, LinkProperties newLp)604 private void pushDownstreamState(LinkProperties oldLp, LinkProperties newLp) { 605 final String ifname = newLp.getInterfaceName(); 606 final List<RouteInfo> oldRoutes = 607 (oldLp != null) ? oldLp.getRoutes() : Collections.EMPTY_LIST; 608 final List<RouteInfo> newRoutes = newLp.getRoutes(); 609 610 // For each old route, if not in new routes: remove. 611 for (RouteInfo ri : oldRoutes) { 612 if (shouldIgnoreDownstreamRoute(ri)) continue; 613 if (!newRoutes.contains(ri)) { 614 mHwInterface.removeDownstreamPrefix(ifname, ri.getDestination().toString()); 615 } 616 } 617 618 // For each new route, if not in old routes: add. 619 for (RouteInfo ri : newRoutes) { 620 if (shouldIgnoreDownstreamRoute(ri)) continue; 621 if (!oldRoutes.contains(ri)) { 622 mHwInterface.addDownstreamPrefix(ifname, ri.getDestination().toString()); 623 } 624 } 625 } 626 pushAllDownstreamState()627 private void pushAllDownstreamState() { 628 for (LinkProperties lp : mDownstreams.values()) { 629 pushDownstreamState(null, lp); 630 } 631 } 632 633 /** Remove downstream interface from offload hardware. */ removeDownstreamInterface(String ifname)634 public void removeDownstreamInterface(String ifname) { 635 final LinkProperties lp = mDownstreams.remove(ifname); 636 if (lp == null) return; 637 638 if (!started()) return; 639 640 for (RouteInfo route : lp.getRoutes()) { 641 if (shouldIgnoreDownstreamRoute(route)) continue; 642 mHwInterface.removeDownstreamPrefix(ifname, route.getDestination().toString()); 643 } 644 } 645 isOffloadDisabled()646 private boolean isOffloadDisabled() { 647 final int defaultDisposition = mHwInterface.getDefaultTetherOffloadDisabled(); 648 return (Settings.Global.getInt( 649 mContentResolver, TETHER_OFFLOAD_DISABLED, defaultDisposition) != 0); 650 } 651 pushUpstreamParameters(String prevUpstream)652 private boolean pushUpstreamParameters(String prevUpstream) { 653 final String iface = currentUpstreamInterface(); 654 655 if (TextUtils.isEmpty(iface)) { 656 final boolean rval = mHwInterface.setUpstreamParameters("", ANYIP, ANYIP, null); 657 // Update stats after we've told the hardware to stop forwarding so 658 // we don't miss packets. 659 maybeUpdateStats(prevUpstream); 660 return rval; 661 } 662 663 // A stacked interface cannot be an upstream for hardware offload. 664 // Consequently, we examine only the primary interface name, look at 665 // getAddresses() rather than getAllAddresses(), and check getRoutes() 666 // rather than getAllRoutes(). 667 final ArrayList<String> v6gateways = new ArrayList<>(); 668 String v4addr = null; 669 String v4gateway = null; 670 671 for (InetAddress ip : mUpstreamLinkProperties.getAddresses()) { 672 if (ip instanceof Inet4Address) { 673 v4addr = ip.getHostAddress(); 674 break; 675 } 676 } 677 678 // Find the gateway addresses of all default routes of either address family. 679 for (RouteInfo ri : mUpstreamLinkProperties.getRoutes()) { 680 if (!ri.hasGateway()) continue; 681 682 final String gateway = ri.getGateway().getHostAddress(); 683 final InetAddress address = ri.getDestination().getAddress(); 684 if (ri.isDefaultRoute() && address instanceof Inet4Address) { 685 v4gateway = gateway; 686 } else if (ri.isDefaultRoute() && address instanceof Inet6Address) { 687 v6gateways.add(gateway); 688 } 689 } 690 691 boolean success = mHwInterface.setUpstreamParameters( 692 iface, v4addr, v4gateway, (v6gateways.isEmpty() ? null : v6gateways)); 693 694 if (!success) { 695 return success; 696 } 697 698 // Update stats after we've told the hardware to change routing so we don't miss packets. 699 maybeUpdateStats(prevUpstream); 700 701 // Data limits can only be set once offload is running on the upstream. 702 success = maybeUpdateDataWarningAndLimit(iface); 703 if (!success) { 704 // If we failed to set a data limit, don't use this upstream, because we don't want to 705 // blow through the data limit that we were told to apply. 706 mLog.log("Setting data limit for " + iface + " failed, disabling offload."); 707 stop(); 708 } 709 710 return success; 711 } 712 computeAndPushLocalPrefixes(UpdateType how)713 private boolean computeAndPushLocalPrefixes(UpdateType how) { 714 final boolean force = (how == UpdateType.FORCE); 715 final Set<String> localPrefixStrs = computeLocalPrefixStrings( 716 mExemptPrefixes, mUpstreamLinkProperties); 717 if (!force && mLastLocalPrefixStrs.equals(localPrefixStrs)) return true; 718 719 mLastLocalPrefixStrs = localPrefixStrs; 720 return mHwInterface.setLocalPrefixes(new ArrayList<>(localPrefixStrs)); 721 } 722 723 // TODO: Factor in downstream LinkProperties once that information is available. computeLocalPrefixStrings( Set<IpPrefix> localPrefixes, LinkProperties upstreamLinkProperties)724 private static Set<String> computeLocalPrefixStrings( 725 Set<IpPrefix> localPrefixes, LinkProperties upstreamLinkProperties) { 726 // Create an editable copy. 727 final Set<IpPrefix> prefixSet = new HashSet<>(localPrefixes); 728 729 // TODO: If a downstream interface (not currently passed in) is reusing 730 // the /64 of the upstream (64share) then: 731 // 732 // [a] remove that /64 from the local prefixes 733 // [b] add in /128s for IP addresses on the downstream interface 734 // [c] add in /128s for IP addresses on the upstream interface 735 // 736 // Until downstream information is available here, simply add /128s from 737 // the upstream network; they'll just be redundant with their /64. 738 if (upstreamLinkProperties != null) { 739 for (LinkAddress linkAddr : upstreamLinkProperties.getLinkAddresses()) { 740 if (!linkAddr.isGlobalPreferred()) continue; 741 final InetAddress ip = linkAddr.getAddress(); 742 if (!(ip instanceof Inet6Address)) continue; 743 prefixSet.add(new IpPrefix(ip, 128)); 744 } 745 } 746 747 final HashSet<String> localPrefixStrs = new HashSet<>(); 748 for (IpPrefix pfx : prefixSet) localPrefixStrs.add(pfx.toString()); 749 return localPrefixStrs; 750 } 751 shouldIgnoreDownstreamRoute(RouteInfo route)752 private static boolean shouldIgnoreDownstreamRoute(RouteInfo route) { 753 // Ignore any link-local routes. 754 final IpPrefix destination = route.getDestination(); 755 final LinkAddress linkAddr = new LinkAddress(destination.getAddress(), 756 destination.getPrefixLength()); 757 if (!linkAddr.isGlobalPreferred()) return true; 758 759 return false; 760 } 761 762 /** Dump information. */ dump(IndentingPrintWriter pw)763 public void dump(IndentingPrintWriter pw) { 764 if (isOffloadDisabled()) { 765 pw.println("Offload disabled"); 766 return; 767 } 768 final boolean isStarted = started(); 769 pw.println("Offload HALs " + (isStarted ? "started" : "not started")); 770 pw.println("Offload Control HAL version: " 771 + OffloadHardwareInterface.halVerToString(mControlHalVersion)); 772 LinkProperties lp = mUpstreamLinkProperties; 773 String upstream = (lp != null) ? lp.getInterfaceName() : null; 774 pw.println("Current upstream: " + upstream); 775 pw.println("Exempt prefixes: " + mLastLocalPrefixStrs); 776 pw.println("NAT timeout update callbacks received during the " 777 + (isStarted ? "current" : "last") 778 + " offload session: " 779 + mNatUpdateCallbacksReceived); 780 pw.println("NAT timeout update netlink errors during the " 781 + (isStarted ? "current" : "last") 782 + " offload session: " 783 + mNatUpdateNetlinkErrors); 784 } 785 updateNatTimeout( int proto, String srcAddr, int srcPort, String dstAddr, int dstPort)786 private void updateNatTimeout( 787 int proto, String srcAddr, int srcPort, String dstAddr, int dstPort) { 788 final String protoName = protoNameFor(proto); 789 if (protoName == null) { 790 mLog.e("Unknown NAT update callback protocol: " + proto); 791 return; 792 } 793 794 final Inet4Address src = parseIPv4Address(srcAddr); 795 if (src == null) { 796 mLog.e("Failed to parse IPv4 address: " + srcAddr); 797 return; 798 } 799 800 if (!isValidUdpOrTcpPort(srcPort)) { 801 mLog.e("Invalid src port: " + srcPort); 802 return; 803 } 804 805 final Inet4Address dst = parseIPv4Address(dstAddr); 806 if (dst == null) { 807 mLog.e("Failed to parse IPv4 address: " + dstAddr); 808 return; 809 } 810 811 if (!isValidUdpOrTcpPort(dstPort)) { 812 mLog.e("Invalid dst port: " + dstPort); 813 return; 814 } 815 816 mNatUpdateCallbacksReceived++; 817 final String natDescription = String.format("%s (%s, %s) -> (%s, %s)", 818 protoName, srcAddr, srcPort, dstAddr, dstPort); 819 if (DBG) { 820 mLog.log("NAT timeout update: " + natDescription); 821 } 822 823 final int timeoutSec = connectionTimeoutUpdateSecondsFor(proto); 824 final byte[] msg = ConntrackMessage.newIPv4TimeoutUpdateRequest( 825 proto, src, srcPort, dst, dstPort, timeoutSec); 826 827 try { 828 NetlinkSocket.sendOneShotKernelMessage(OsConstants.NETLINK_NETFILTER, msg); 829 } catch (ErrnoException e) { 830 mNatUpdateNetlinkErrors++; 831 mLog.e("Error updating NAT conntrack entry >" + natDescription + "<: " + e 832 + ", msg: " + NetlinkConstants.hexify(msg)); 833 mLog.log("NAT timeout update callbacks received: " + mNatUpdateCallbacksReceived); 834 mLog.log("NAT timeout update netlink errors: " + mNatUpdateNetlinkErrors); 835 } 836 } 837 parseIPv4Address(String addrString)838 private static Inet4Address parseIPv4Address(String addrString) { 839 try { 840 final InetAddress ip = InetAddresses.parseNumericAddress(addrString); 841 // TODO: Consider other sanitization steps here, including perhaps: 842 // not eql to 0.0.0.0 843 // not within 169.254.0.0/16 844 // not within ::ffff:0.0.0.0/96 845 // not within ::/96 846 // et cetera. 847 if (ip instanceof Inet4Address) { 848 return (Inet4Address) ip; 849 } 850 } catch (IllegalArgumentException iae) { } 851 return null; 852 } 853 protoNameFor(int proto)854 private static String protoNameFor(int proto) { 855 // OsConstants values are not constant expressions; no switch statement. 856 if (proto == OsConstants.IPPROTO_UDP) { 857 return "UDP"; 858 } else if (proto == OsConstants.IPPROTO_TCP) { 859 return "TCP"; 860 } 861 return null; 862 } 863 connectionTimeoutUpdateSecondsFor(int proto)864 private static int connectionTimeoutUpdateSecondsFor(int proto) { 865 // TODO: Replace this with more thoughtful work, perhaps reading from 866 // and maybe writing to any required 867 // 868 // /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_* 869 // /proc/sys/net/netfilter/nf_conntrack_udp_timeout{,_stream} 870 // 871 // entries. TBD. 872 if (proto == OsConstants.IPPROTO_TCP) { 873 // Cf. /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_established 874 return 432000; 875 } else { 876 // Cf. /proc/sys/net/netfilter/nf_conntrack_udp_timeout_stream 877 return 180; 878 } 879 } 880 isValidUdpOrTcpPort(int port)881 private static boolean isValidUdpOrTcpPort(int port) { 882 return port > 0 && port < 65536; 883 } 884 } 885