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