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