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