Home | History | Annotate | Download | only in tethering
      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 
    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 
    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 
    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 
    251     private boolean started() {
    252         return mConfigInitialized && mControlInitialized;
    253     }
    254 
    255     private class OffloadTetheringStatsProvider extends ITetheringStatsProvider.Stub {
    256         @Override
    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
    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 
    298     private String currentUpstreamInterface() {
    299         return (mUpstreamLinkProperties != null)
    300                 ? mUpstreamLinkProperties.getInterfaceName() : null;
    301     }
    302 
    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 
    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 
    339     private void updateStatsForCurrentUpstream() {
    340         maybeUpdateStats(currentUpstreamInterface());
    341     }
    342 
    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 
    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 
    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 
    379     public void setLocalPrefixes(Set<IpPrefix> localPrefixes) {
    380         mExemptPrefixes = localPrefixes;
    381 
    382         if (!started()) return;
    383         computeAndPushLocalPrefixes(UpdateType.IF_NEEDED);
    384     }
    385 
    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 
    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 
    418     private void pushAllDownstreamState() {
    419         for (LinkProperties lp : mDownstreams.values()) {
    420             pushDownstreamState(null, lp);
    421         }
    422     }
    423 
    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 
    436     private boolean isOffloadDisabled() {
    437         final int defaultDisposition = mHwInterface.getDefaultTetherOffloadDisabled();
    438         return (Settings.Global.getInt(
    439                 mContentResolver, TETHER_OFFLOAD_DISABLED, defaultDisposition) != 0);
    440     }
    441 
    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 
    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.
    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 
    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 
    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 
    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 
    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 
    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 
    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