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