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