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.server.connectivity.tethering; 18 19 import static android.net.NetworkStats.SET_DEFAULT; 20 import static android.net.NetworkStats.STATS_PER_UID; 21 import static android.net.NetworkStats.TAG_NONE; 22 import static android.net.NetworkStats.UID_ALL; 23 import static android.net.TrafficStats.UID_TETHERING; 24 import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED; 25 26 import android.content.ContentResolver; 27 import android.net.ITetheringStatsProvider; 28 import android.net.IpPrefix; 29 import android.net.LinkAddress; 30 import android.net.LinkProperties; 31 import android.net.NetworkStats; 32 import android.net.RouteInfo; 33 import android.net.netlink.ConntrackMessage; 34 import android.net.netlink.NetlinkConstants; 35 import android.net.netlink.NetlinkSocket; 36 import android.net.util.IpUtils; 37 import android.net.util.SharedLog; 38 import android.os.Handler; 39 import android.os.Looper; 40 import android.os.INetworkManagementService; 41 import android.os.RemoteException; 42 import android.os.SystemClock; 43 import android.provider.Settings; 44 import android.system.ErrnoException; 45 import android.system.OsConstants; 46 import android.text.TextUtils; 47 48 import com.android.internal.util.IndentingPrintWriter; 49 import com.android.server.connectivity.tethering.OffloadHardwareInterface.ForwardedStats; 50 51 import java.net.Inet4Address; 52 import java.net.Inet6Address; 53 import java.net.InetAddress; 54 import java.util.ArrayList; 55 import java.util.Collections; 56 import java.util.HashMap; 57 import java.util.HashSet; 58 import java.util.List; 59 import java.util.Map; 60 import java.util.Objects; 61 import java.util.Set; 62 import java.util.concurrent.ConcurrentHashMap; 63 import java.util.concurrent.TimeUnit; 64 65 /** 66 * A class to encapsulate the business logic of programming the tethering 67 * hardware offload interface. 68 * 69 * @hide 70 */ 71 public class OffloadController { 72 private static final String TAG = OffloadController.class.getSimpleName(); 73 private static final boolean DBG = false; 74 private static final String ANYIP = "0.0.0.0"; 75 private static final ForwardedStats EMPTY_STATS = new ForwardedStats(); 76 77 private static enum UpdateType { IF_NEEDED, FORCE }; 78 79 private final Handler mHandler; 80 private final OffloadHardwareInterface mHwInterface; 81 private final ContentResolver mContentResolver; 82 private final INetworkManagementService mNms; 83 private final ITetheringStatsProvider mStatsProvider; 84 private final SharedLog mLog; 85 private final HashMap<String, LinkProperties> mDownstreams; 86 private boolean mConfigInitialized; 87 private boolean mControlInitialized; 88 private LinkProperties mUpstreamLinkProperties; 89 // The complete set of offload-exempt prefixes passed in via Tethering from 90 // all upstream and downstream sources. 91 private Set<IpPrefix> mExemptPrefixes; 92 // A strictly "smaller" set of prefixes, wherein offload-approved prefixes 93 // (e.g. downstream on-link prefixes) have been removed and replaced with 94 // prefixes representing only the locally-assigned IP addresses. 95 private Set<String> mLastLocalPrefixStrs; 96 97 // Maps upstream interface names to offloaded traffic statistics. 98 // Always contains the latest value received from the hardware for each interface, regardless of 99 // whether offload is currently running on that interface. 100 private ConcurrentHashMap<String, ForwardedStats> mForwardedStats = 101 new ConcurrentHashMap<>(16, 0.75F, 1); 102 103 // Maps upstream interface names to interface quotas. 104 // Always contains the latest value received from the framework for each interface, regardless 105 // of whether offload is currently running (or is even supported) on that interface. Only 106 // includes upstream interfaces that have a quota set. 107 private HashMap<String, Long> mInterfaceQuotas = new HashMap<>(); 108 109 private int mNatUpdateCallbacksReceived; 110 private int mNatUpdateNetlinkErrors; 111 OffloadController(Handler h, OffloadHardwareInterface hwi, ContentResolver contentResolver, INetworkManagementService nms, SharedLog log)112 public OffloadController(Handler h, OffloadHardwareInterface hwi, 113 ContentResolver contentResolver, INetworkManagementService nms, SharedLog log) { 114 mHandler = h; 115 mHwInterface = hwi; 116 mContentResolver = contentResolver; 117 mNms = nms; 118 mStatsProvider = new OffloadTetheringStatsProvider(); 119 mLog = log.forSubComponent(TAG); 120 mDownstreams = new HashMap<>(); 121 mExemptPrefixes = new HashSet<>(); 122 mLastLocalPrefixStrs = new HashSet<>(); 123 124 try { 125 mNms.registerTetheringStatsProvider(mStatsProvider, getClass().getSimpleName()); 126 } catch (RemoteException e) { 127 mLog.e("Cannot register offload stats provider: " + e); 128 } 129 } 130 start()131 public boolean start() { 132 if (started()) return true; 133 134 if (isOffloadDisabled()) { 135 mLog.i("tethering offload disabled"); 136 return false; 137 } 138 139 if (!mConfigInitialized) { 140 mConfigInitialized = mHwInterface.initOffloadConfig(); 141 if (!mConfigInitialized) { 142 mLog.i("tethering offload config not supported"); 143 stop(); 144 return false; 145 } 146 } 147 148 mControlInitialized = mHwInterface.initOffloadControl( 149 // OffloadHardwareInterface guarantees that these callback 150 // methods are called on the handler passed to it, which is the 151 // same as mHandler, as coordinated by the setup in Tethering. 152 new OffloadHardwareInterface.ControlCallback() { 153 @Override 154 public void onStarted() { 155 if (!started()) return; 156 mLog.log("onStarted"); 157 } 158 159 @Override 160 public void onStoppedError() { 161 if (!started()) return; 162 mLog.log("onStoppedError"); 163 } 164 165 @Override 166 public void onStoppedUnsupported() { 167 if (!started()) return; 168 mLog.log("onStoppedUnsupported"); 169 // Poll for statistics and trigger a sweep of tethering 170 // stats by observers. This might not succeed, but it's 171 // worth trying anyway. We need to do this because from 172 // this point on we continue with software forwarding, 173 // and we need to synchronize stats and limits between 174 // software and hardware forwarding. 175 updateStatsForAllUpstreams(); 176 forceTetherStatsPoll(); 177 } 178 179 @Override 180 public void onSupportAvailable() { 181 if (!started()) return; 182 mLog.log("onSupportAvailable"); 183 184 // [1] Poll for statistics and trigger a sweep of stats 185 // by observers. We need to do this to ensure that any 186 // limits set take into account any software tethering 187 // traffic that has been happening in the meantime. 188 updateStatsForAllUpstreams(); 189 forceTetherStatsPoll(); 190 // [2] (Re)Push all state. 191 computeAndPushLocalPrefixes(UpdateType.FORCE); 192 pushAllDownstreamState(); 193 pushUpstreamParameters(null); 194 } 195 196 @Override 197 public void onStoppedLimitReached() { 198 if (!started()) return; 199 mLog.log("onStoppedLimitReached"); 200 201 // We cannot reliably determine on which interface the limit was reached, 202 // because the HAL interface does not specify it. We cannot just use the 203 // current upstream, because that might have changed since the time that 204 // the HAL queued the callback. 205 // TODO: rev the HAL so that it provides an interface name. 206 207 // Fetch current stats, so that when our notification reaches 208 // NetworkStatsService and triggers a poll, we will respond with 209 // current data (which will be above the limit that was reached). 210 // Note that if we just changed upstream, this is unnecessary but harmless. 211 // The stats for the previous upstream were already updated on this thread 212 // just after the upstream was changed, so they are also up-to-date. 213 updateStatsForCurrentUpstream(); 214 forceTetherStatsPoll(); 215 } 216 217 @Override 218 public void onNatTimeoutUpdate(int proto, 219 String srcAddr, int srcPort, 220 String dstAddr, int dstPort) { 221 if (!started()) return; 222 updateNatTimeout(proto, srcAddr, srcPort, dstAddr, dstPort); 223 } 224 }); 225 226 final boolean isStarted = started(); 227 if (!isStarted) { 228 mLog.i("tethering offload control not supported"); 229 stop(); 230 } else { 231 mLog.log("tethering offload started"); 232 mNatUpdateCallbacksReceived = 0; 233 mNatUpdateNetlinkErrors = 0; 234 } 235 return isStarted; 236 } 237 stop()238 public void stop() { 239 // Completely stops tethering offload. After this method is called, it is no longer safe to 240 // call any HAL method, no callbacks from the hardware will be delivered, and any in-flight 241 // callbacks must be ignored. Offload may be started again by calling start(). 242 final boolean wasStarted = started(); 243 updateStatsForCurrentUpstream(); 244 mUpstreamLinkProperties = null; 245 mHwInterface.stopOffloadControl(); 246 mControlInitialized = false; 247 mConfigInitialized = false; 248 if (wasStarted) mLog.log("tethering offload stopped"); 249 } 250 started()251 private boolean started() { 252 return mConfigInitialized && mControlInitialized; 253 } 254 255 private class OffloadTetheringStatsProvider extends ITetheringStatsProvider.Stub { 256 @Override getTetherStats(int how)257 public NetworkStats getTetherStats(int how) { 258 // getTetherStats() is the only function in OffloadController that can be called from 259 // a different thread. Do not attempt to update stats by querying the offload HAL 260 // synchronously from a different thread than our Handler thread. http://b/64771555. 261 Runnable updateStats = () -> { updateStatsForCurrentUpstream(); }; 262 if (Looper.myLooper() == mHandler.getLooper()) { 263 updateStats.run(); 264 } else { 265 mHandler.post(updateStats); 266 } 267 268 NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0); 269 NetworkStats.Entry entry = new NetworkStats.Entry(); 270 entry.set = SET_DEFAULT; 271 entry.tag = TAG_NONE; 272 entry.uid = (how == STATS_PER_UID) ? UID_TETHERING : UID_ALL; 273 274 for (Map.Entry<String, ForwardedStats> kv : mForwardedStats.entrySet()) { 275 ForwardedStats value = kv.getValue(); 276 entry.iface = kv.getKey(); 277 entry.rxBytes = value.rxBytes; 278 entry.txBytes = value.txBytes; 279 stats.addValues(entry); 280 } 281 282 return stats; 283 } 284 285 @Override setInterfaceQuota(String iface, long quotaBytes)286 public void setInterfaceQuota(String iface, long quotaBytes) { 287 mHandler.post(() -> { 288 if (quotaBytes == ITetheringStatsProvider.QUOTA_UNLIMITED) { 289 mInterfaceQuotas.remove(iface); 290 } else { 291 mInterfaceQuotas.put(iface, quotaBytes); 292 } 293 maybeUpdateDataLimit(iface); 294 }); 295 } 296 } 297 currentUpstreamInterface()298 private String currentUpstreamInterface() { 299 return (mUpstreamLinkProperties != null) 300 ? mUpstreamLinkProperties.getInterfaceName() : null; 301 } 302 maybeUpdateStats(String iface)303 private void maybeUpdateStats(String iface) { 304 if (TextUtils.isEmpty(iface)) { 305 return; 306 } 307 308 // Always called on the handler thread. 309 // 310 // Use get()/put() instead of updating ForwardedStats in place because we can be called 311 // concurrently with getTetherStats. In combination with the guarantees provided by 312 // ConcurrentHashMap, this ensures that getTetherStats always gets the most recent copy of 313 // the stats for each interface, and does not observe partial writes where rxBytes is 314 // updated and txBytes is not. 315 ForwardedStats diff = mHwInterface.getForwardedStats(iface); 316 ForwardedStats base = mForwardedStats.get(iface); 317 if (base != null) { 318 diff.add(base); 319 } 320 mForwardedStats.put(iface, diff); 321 // diff is a new object, just created by getForwardedStats(). Therefore, anyone reading from 322 // mForwardedStats (i.e., any caller of getTetherStats) will see the new stats immediately. 323 } 324 maybeUpdateDataLimit(String iface)325 private boolean maybeUpdateDataLimit(String iface) { 326 // setDataLimit may only be called while offload is occurring on this upstream. 327 if (!started() || !TextUtils.equals(iface, currentUpstreamInterface())) { 328 return true; 329 } 330 331 Long limit = mInterfaceQuotas.get(iface); 332 if (limit == null) { 333 limit = Long.MAX_VALUE; 334 } 335 336 return mHwInterface.setDataLimit(iface, limit); 337 } 338 updateStatsForCurrentUpstream()339 private void updateStatsForCurrentUpstream() { 340 maybeUpdateStats(currentUpstreamInterface()); 341 } 342 updateStatsForAllUpstreams()343 private void updateStatsForAllUpstreams() { 344 // In practice, there should only ever be a single digit number of 345 // upstream interfaces over the lifetime of an active tethering session. 346 // Roughly speaking, imagine a very ambitious one or two of each of the 347 // following interface types: [ "rmnet_data", "wlan", "eth", "rndis" ]. 348 for (Map.Entry<String, ForwardedStats> kv : mForwardedStats.entrySet()) { 349 maybeUpdateStats(kv.getKey()); 350 } 351 } 352 forceTetherStatsPoll()353 private void forceTetherStatsPoll() { 354 try { 355 mNms.tetherLimitReached(mStatsProvider); 356 } catch (RemoteException e) { 357 mLog.e("Cannot report data limit reached: " + e); 358 } 359 } 360 setUpstreamLinkProperties(LinkProperties lp)361 public void setUpstreamLinkProperties(LinkProperties lp) { 362 if (!started() || Objects.equals(mUpstreamLinkProperties, lp)) return; 363 364 final String prevUpstream = currentUpstreamInterface(); 365 366 mUpstreamLinkProperties = (lp != null) ? new LinkProperties(lp) : null; 367 // Make sure we record this interface in the ForwardedStats map. 368 final String iface = currentUpstreamInterface(); 369 if (!TextUtils.isEmpty(iface)) mForwardedStats.putIfAbsent(iface, EMPTY_STATS); 370 371 // TODO: examine return code and decide what to do if programming 372 // upstream parameters fails (probably just wait for a subsequent 373 // onOffloadEvent() callback to tell us offload is available again and 374 // then reapply all state). 375 computeAndPushLocalPrefixes(UpdateType.IF_NEEDED); 376 pushUpstreamParameters(prevUpstream); 377 } 378 setLocalPrefixes(Set<IpPrefix> localPrefixes)379 public void setLocalPrefixes(Set<IpPrefix> localPrefixes) { 380 mExemptPrefixes = localPrefixes; 381 382 if (!started()) return; 383 computeAndPushLocalPrefixes(UpdateType.IF_NEEDED); 384 } 385 notifyDownstreamLinkProperties(LinkProperties lp)386 public void notifyDownstreamLinkProperties(LinkProperties lp) { 387 final String ifname = lp.getInterfaceName(); 388 final LinkProperties oldLp = mDownstreams.put(ifname, new LinkProperties(lp)); 389 if (Objects.equals(oldLp, lp)) return; 390 391 if (!started()) return; 392 pushDownstreamState(oldLp, lp); 393 } 394 pushDownstreamState(LinkProperties oldLp, LinkProperties newLp)395 private void pushDownstreamState(LinkProperties oldLp, LinkProperties newLp) { 396 final String ifname = newLp.getInterfaceName(); 397 final List<RouteInfo> oldRoutes = 398 (oldLp != null) ? oldLp.getRoutes() : Collections.EMPTY_LIST; 399 final List<RouteInfo> newRoutes = newLp.getRoutes(); 400 401 // For each old route, if not in new routes: remove. 402 for (RouteInfo ri : oldRoutes) { 403 if (shouldIgnoreDownstreamRoute(ri)) continue; 404 if (!newRoutes.contains(ri)) { 405 mHwInterface.removeDownstreamPrefix(ifname, ri.getDestination().toString()); 406 } 407 } 408 409 // For each new route, if not in old routes: add. 410 for (RouteInfo ri : newRoutes) { 411 if (shouldIgnoreDownstreamRoute(ri)) continue; 412 if (!oldRoutes.contains(ri)) { 413 mHwInterface.addDownstreamPrefix(ifname, ri.getDestination().toString()); 414 } 415 } 416 } 417 pushAllDownstreamState()418 private void pushAllDownstreamState() { 419 for (LinkProperties lp : mDownstreams.values()) { 420 pushDownstreamState(null, lp); 421 } 422 } 423 removeDownstreamInterface(String ifname)424 public void removeDownstreamInterface(String ifname) { 425 final LinkProperties lp = mDownstreams.remove(ifname); 426 if (lp == null) return; 427 428 if (!started()) return; 429 430 for (RouteInfo route : lp.getRoutes()) { 431 if (shouldIgnoreDownstreamRoute(route)) continue; 432 mHwInterface.removeDownstreamPrefix(ifname, route.getDestination().toString()); 433 } 434 } 435 isOffloadDisabled()436 private boolean isOffloadDisabled() { 437 final int defaultDisposition = mHwInterface.getDefaultTetherOffloadDisabled(); 438 return (Settings.Global.getInt( 439 mContentResolver, TETHER_OFFLOAD_DISABLED, defaultDisposition) != 0); 440 } 441 pushUpstreamParameters(String prevUpstream)442 private boolean pushUpstreamParameters(String prevUpstream) { 443 final String iface = currentUpstreamInterface(); 444 445 if (TextUtils.isEmpty(iface)) { 446 final boolean rval = mHwInterface.setUpstreamParameters("", ANYIP, ANYIP, null); 447 // Update stats after we've told the hardware to stop forwarding so 448 // we don't miss packets. 449 maybeUpdateStats(prevUpstream); 450 return rval; 451 } 452 453 // A stacked interface cannot be an upstream for hardware offload. 454 // Consequently, we examine only the primary interface name, look at 455 // getAddresses() rather than getAllAddresses(), and check getRoutes() 456 // rather than getAllRoutes(). 457 final ArrayList<String> v6gateways = new ArrayList<>(); 458 String v4addr = null; 459 String v4gateway = null; 460 461 for (InetAddress ip : mUpstreamLinkProperties.getAddresses()) { 462 if (ip instanceof Inet4Address) { 463 v4addr = ip.getHostAddress(); 464 break; 465 } 466 } 467 468 // Find the gateway addresses of all default routes of either address family. 469 for (RouteInfo ri : mUpstreamLinkProperties.getRoutes()) { 470 if (!ri.hasGateway()) continue; 471 472 final String gateway = ri.getGateway().getHostAddress(); 473 if (ri.isIPv4Default()) { 474 v4gateway = gateway; 475 } else if (ri.isIPv6Default()) { 476 v6gateways.add(gateway); 477 } 478 } 479 480 boolean success = mHwInterface.setUpstreamParameters( 481 iface, v4addr, v4gateway, (v6gateways.isEmpty() ? null : v6gateways)); 482 483 if (!success) { 484 return success; 485 } 486 487 // Update stats after we've told the hardware to change routing so we don't miss packets. 488 maybeUpdateStats(prevUpstream); 489 490 // Data limits can only be set once offload is running on the upstream. 491 success = maybeUpdateDataLimit(iface); 492 if (!success) { 493 // If we failed to set a data limit, don't use this upstream, because we don't want to 494 // blow through the data limit that we were told to apply. 495 mLog.log("Setting data limit for " + iface + " failed, disabling offload."); 496 stop(); 497 } 498 499 return success; 500 } 501 computeAndPushLocalPrefixes(UpdateType how)502 private boolean computeAndPushLocalPrefixes(UpdateType how) { 503 final boolean force = (how == UpdateType.FORCE); 504 final Set<String> localPrefixStrs = computeLocalPrefixStrings( 505 mExemptPrefixes, mUpstreamLinkProperties); 506 if (!force && mLastLocalPrefixStrs.equals(localPrefixStrs)) return true; 507 508 mLastLocalPrefixStrs = localPrefixStrs; 509 return mHwInterface.setLocalPrefixes(new ArrayList<>(localPrefixStrs)); 510 } 511 512 // TODO: Factor in downstream LinkProperties once that information is available. computeLocalPrefixStrings( Set<IpPrefix> localPrefixes, LinkProperties upstreamLinkProperties)513 private static Set<String> computeLocalPrefixStrings( 514 Set<IpPrefix> localPrefixes, LinkProperties upstreamLinkProperties) { 515 // Create an editable copy. 516 final Set<IpPrefix> prefixSet = new HashSet<>(localPrefixes); 517 518 // TODO: If a downstream interface (not currently passed in) is reusing 519 // the /64 of the upstream (64share) then: 520 // 521 // [a] remove that /64 from the local prefixes 522 // [b] add in /128s for IP addresses on the downstream interface 523 // [c] add in /128s for IP addresses on the upstream interface 524 // 525 // Until downstream information is available here, simply add /128s from 526 // the upstream network; they'll just be redundant with their /64. 527 if (upstreamLinkProperties != null) { 528 for (LinkAddress linkAddr : upstreamLinkProperties.getLinkAddresses()) { 529 if (!linkAddr.isGlobalPreferred()) continue; 530 final InetAddress ip = linkAddr.getAddress(); 531 if (!(ip instanceof Inet6Address)) continue; 532 prefixSet.add(new IpPrefix(ip, 128)); 533 } 534 } 535 536 final HashSet<String> localPrefixStrs = new HashSet<>(); 537 for (IpPrefix pfx : prefixSet) localPrefixStrs.add(pfx.toString()); 538 return localPrefixStrs; 539 } 540 shouldIgnoreDownstreamRoute(RouteInfo route)541 private static boolean shouldIgnoreDownstreamRoute(RouteInfo route) { 542 // Ignore any link-local routes. 543 if (!route.getDestinationLinkAddress().isGlobalPreferred()) return true; 544 545 return false; 546 } 547 dump(IndentingPrintWriter pw)548 public void dump(IndentingPrintWriter pw) { 549 if (isOffloadDisabled()) { 550 pw.println("Offload disabled"); 551 return; 552 } 553 final boolean isStarted = started(); 554 pw.println("Offload HALs " + (isStarted ? "started" : "not started")); 555 LinkProperties lp = mUpstreamLinkProperties; 556 String upstream = (lp != null) ? lp.getInterfaceName() : null; 557 pw.println("Current upstream: " + upstream); 558 pw.println("Exempt prefixes: " + mLastLocalPrefixStrs); 559 pw.println("NAT timeout update callbacks received during the " 560 + (isStarted ? "current" : "last") 561 + " offload session: " 562 + mNatUpdateCallbacksReceived); 563 pw.println("NAT timeout update netlink errors during the " 564 + (isStarted ? "current" : "last") 565 + " offload session: " 566 + mNatUpdateNetlinkErrors); 567 } 568 updateNatTimeout( int proto, String srcAddr, int srcPort, String dstAddr, int dstPort)569 private void updateNatTimeout( 570 int proto, String srcAddr, int srcPort, String dstAddr, int dstPort) { 571 final String protoName = protoNameFor(proto); 572 if (protoName == null) { 573 mLog.e("Unknown NAT update callback protocol: " + proto); 574 return; 575 } 576 577 final Inet4Address src = parseIPv4Address(srcAddr); 578 if (src == null) { 579 mLog.e("Failed to parse IPv4 address: " + srcAddr); 580 return; 581 } 582 583 if (!IpUtils.isValidUdpOrTcpPort(srcPort)) { 584 mLog.e("Invalid src port: " + srcPort); 585 return; 586 } 587 588 final Inet4Address dst = parseIPv4Address(dstAddr); 589 if (dst == null) { 590 mLog.e("Failed to parse IPv4 address: " + dstAddr); 591 return; 592 } 593 594 if (!IpUtils.isValidUdpOrTcpPort(dstPort)) { 595 mLog.e("Invalid dst port: " + dstPort); 596 return; 597 } 598 599 mNatUpdateCallbacksReceived++; 600 final String natDescription = String.format("%s (%s, %s) -> (%s, %s)", 601 protoName, srcAddr, srcPort, dstAddr, dstPort); 602 if (DBG) { 603 mLog.log("NAT timeout update: " + natDescription); 604 } 605 606 final int timeoutSec = connectionTimeoutUpdateSecondsFor(proto); 607 final byte[] msg = ConntrackMessage.newIPv4TimeoutUpdateRequest( 608 proto, src, srcPort, dst, dstPort, timeoutSec); 609 610 try { 611 NetlinkSocket.sendOneShotKernelMessage(OsConstants.NETLINK_NETFILTER, msg); 612 } catch (ErrnoException e) { 613 mNatUpdateNetlinkErrors++; 614 mLog.e("Error updating NAT conntrack entry >" + natDescription + "<: " + e 615 + ", msg: " + NetlinkConstants.hexify(msg)); 616 mLog.log("NAT timeout update callbacks received: " + mNatUpdateCallbacksReceived); 617 mLog.log("NAT timeout update netlink errors: " + mNatUpdateNetlinkErrors); 618 } 619 } 620 parseIPv4Address(String addrString)621 private static Inet4Address parseIPv4Address(String addrString) { 622 try { 623 final InetAddress ip = InetAddress.parseNumericAddress(addrString); 624 // TODO: Consider other sanitization steps here, including perhaps: 625 // not eql to 0.0.0.0 626 // not within 169.254.0.0/16 627 // not within ::ffff:0.0.0.0/96 628 // not within ::/96 629 // et cetera. 630 if (ip instanceof Inet4Address) { 631 return (Inet4Address) ip; 632 } 633 } catch (IllegalArgumentException iae) {} 634 return null; 635 } 636 protoNameFor(int proto)637 private static String protoNameFor(int proto) { 638 // OsConstants values are not constant expressions; no switch statement. 639 if (proto == OsConstants.IPPROTO_UDP) { 640 return "UDP"; 641 } else if (proto == OsConstants.IPPROTO_TCP) { 642 return "TCP"; 643 } 644 return null; 645 } 646 connectionTimeoutUpdateSecondsFor(int proto)647 private static int connectionTimeoutUpdateSecondsFor(int proto) { 648 // TODO: Replace this with more thoughtful work, perhaps reading from 649 // and maybe writing to any required 650 // 651 // /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_* 652 // /proc/sys/net/netfilter/nf_conntrack_udp_timeout{,_stream} 653 // 654 // entries. TBD. 655 if (proto == OsConstants.IPPROTO_TCP) { 656 // Cf. /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_established 657 return 432000; 658 } else { 659 // Cf. /proc/sys/net/netfilter/nf_conntrack_udp_timeout_stream 660 return 180; 661 } 662 } 663 } 664