diff options
8 files changed, 322 insertions, 104 deletions
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java index 0a9dba72ce82..1fb944fda7c9 100644 --- a/services/core/java/com/android/server/connectivity/Tethering.java +++ b/services/core/java/com/android/server/connectivity/Tethering.java @@ -57,6 +57,7 @@ import android.net.NetworkRequest; import android.net.NetworkState; import android.net.NetworkUtils; import android.net.RouteInfo; +import android.net.util.PrefixUtils; import android.net.util.SharedLog; import android.net.wifi.WifiManager; import android.os.Binder; @@ -216,10 +217,10 @@ public class Tethering extends BaseNetworkObserver { mContext.getContentResolver(), mLog); mUpstreamNetworkMonitor = new UpstreamNetworkMonitor( - mContext, mTetherMasterSM, mLog, TetherMasterSM.EVENT_UPSTREAM_CALLBACK ); + mContext, mTetherMasterSM, mLog, TetherMasterSM.EVENT_UPSTREAM_CALLBACK); mForwardedDownstreams = new HashSet<>(); mSimChange = new SimChangeListener( - mContext, mTetherMasterSM.getHandler(), () -> reevaluateSimCardProvisioning()); + mContext, smHandler, () -> reevaluateSimCardProvisioning()); mStateReceiver = new StateReceiver(); IntentFilter filter = new IntentFilter(); @@ -227,13 +228,13 @@ public class Tethering extends BaseNetworkObserver { filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); - mContext.registerReceiver(mStateReceiver, filter, null, mTetherMasterSM.getHandler()); + mContext.registerReceiver(mStateReceiver, filter, null, smHandler); filter = new IntentFilter(); filter.addAction(Intent.ACTION_MEDIA_SHARED); filter.addAction(Intent.ACTION_MEDIA_UNSHARED); filter.addDataScheme("file"); - mContext.registerReceiver(mStateReceiver, filter, null, mTetherMasterSM.getHandler()); + mContext.registerReceiver(mStateReceiver, filter, null, smHandler); // load device config info updateConfiguration(); @@ -1142,12 +1143,6 @@ public class Tethering extends BaseNetworkObserver { } } - private void startOffloadController() { - mOffloadController.start(); - mOffloadController.updateExemptPrefixes( - mUpstreamNetworkMonitor.getOffloadExemptPrefixes()); - } - class TetherMasterSM extends StateMachine { private static final int BASE_MASTER = Protocol.BASE_TETHERING; // an interface SM has requested Tethering/Local Hotspot @@ -1165,14 +1160,14 @@ public class Tethering extends BaseNetworkObserver { static final int CMD_CLEAR_ERROR = BASE_MASTER + 6; static final int EVENT_IFACE_UPDATE_LINKPROPERTIES = BASE_MASTER + 7; - private State mInitialState; - private State mTetherModeAliveState; + private final State mInitialState; + private final State mTetherModeAliveState; - private State mSetIpForwardingEnabledErrorState; - private State mSetIpForwardingDisabledErrorState; - private State mStartTetheringErrorState; - private State mStopTetheringErrorState; - private State mSetDnsForwardersErrorState; + private final State mSetIpForwardingEnabledErrorState; + private final State mSetIpForwardingDisabledErrorState; + private final State mStartTetheringErrorState; + private final State mStopTetheringErrorState; + private final State mSetDnsForwardersErrorState; // This list is a little subtle. It contains all the interfaces that currently are // requesting tethering, regardless of whether these interfaces are still members of @@ -1212,22 +1207,46 @@ public class Tethering extends BaseNetworkObserver { mNotifyList = new ArrayList<>(); mIPv6TetheringCoordinator = new IPv6TetheringCoordinator(mNotifyList, mLog); + setInitialState(mInitialState); } + private void startOffloadController() { + mOffloadController.start(); + sendOffloadExemptPrefixes(); + } + + private void sendOffloadExemptPrefixes() { + sendOffloadExemptPrefixes(mUpstreamNetworkMonitor.getLocalPrefixes()); + } + + private void sendOffloadExemptPrefixes(Set<IpPrefix> localPrefixes) { + // Add in well-known minimum set. + PrefixUtils.addNonForwardablePrefixes(localPrefixes); + // Add tragically hardcoded prefixes. + localPrefixes.add(PrefixUtils.DEFAULT_WIFI_P2P_PREFIX); + + // Add prefixes for all downstreams, regardless of IP serving mode. + for (TetherInterfaceStateMachine tism : mNotifyList) { + localPrefixes.addAll(PrefixUtils.localPrefixesFrom(tism.linkProperties())); + } + + mOffloadController.setLocalPrefixes(localPrefixes); + } + class InitialState extends State { @Override public boolean processMessage(Message message) { logMessage(this, message.what); switch (message.what) { case EVENT_IFACE_SERVING_STATE_ACTIVE: - TetherInterfaceStateMachine who = (TetherInterfaceStateMachine)message.obj; + TetherInterfaceStateMachine who = (TetherInterfaceStateMachine) message.obj; if (VDBG) Log.d(TAG, "Tether Mode requested by " + who); handleInterfaceServingStateActive(message.arg1, who); transitionTo(mTetherModeAliveState); break; case EVENT_IFACE_SERVING_STATE_INACTIVE: - who = (TetherInterfaceStateMachine)message.obj; + who = (TetherInterfaceStateMachine) message.obj; if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who); handleInterfaceServingStateInactive(who); break; @@ -1422,8 +1441,8 @@ public class Tethering extends BaseNetworkObserver { } private void handleUpstreamNetworkMonitorCallback(int arg1, Object o) { - if (arg1 == UpstreamNetworkMonitor.NOTIFY_EXEMPT_PREFIXES) { - mOffloadController.updateExemptPrefixes((Set<IpPrefix>) o); + if (arg1 == UpstreamNetworkMonitor.NOTIFY_LOCAL_PREFIXES) { + sendOffloadExemptPrefixes((Set<IpPrefix>) o); return; } @@ -1527,7 +1546,7 @@ public class Tethering extends BaseNetworkObserver { boolean retValue = true; switch (message.what) { case EVENT_IFACE_SERVING_STATE_ACTIVE: { - TetherInterfaceStateMachine who = (TetherInterfaceStateMachine)message.obj; + TetherInterfaceStateMachine who = (TetherInterfaceStateMachine) message.obj; if (VDBG) Log.d(TAG, "Tether Mode requested by " + who); handleInterfaceServingStateActive(message.arg1, who); who.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED, @@ -1541,7 +1560,7 @@ public class Tethering extends BaseNetworkObserver { break; } case EVENT_IFACE_SERVING_STATE_INACTIVE: { - TetherInterfaceStateMachine who = (TetherInterfaceStateMachine)message.obj; + TetherInterfaceStateMachine who = (TetherInterfaceStateMachine) message.obj; if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who); handleInterfaceServingStateInactive(who); @@ -1573,6 +1592,9 @@ public class Tethering extends BaseNetworkObserver { mOffloadController.notifyDownstreamLinkProperties(newLp); } else { mOffloadController.removeDownstreamInterface(newLp.getInterfaceName()); + // Another interface might be in local-only hotspot mode; + // resend all local prefixes to the OffloadController. + sendOffloadExemptPrefixes(); } break; } @@ -1614,7 +1636,7 @@ public class Tethering extends BaseNetworkObserver { boolean retValue = true; switch (message.what) { case EVENT_IFACE_SERVING_STATE_ACTIVE: - TetherInterfaceStateMachine who = (TetherInterfaceStateMachine)message.obj; + TetherInterfaceStateMachine who = (TetherInterfaceStateMachine) message.obj; who.sendMessage(mErrorNotification); break; case CMD_CLEAR_ERROR: diff --git a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java index 2b0ded94eacf..b47386705a36 100644 --- a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java +++ b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java @@ -20,6 +20,7 @@ import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED; import android.content.ContentResolver; import android.net.IpPrefix; +import android.net.LinkAddress; import android.net.LinkProperties; import android.net.RouteInfo; import android.net.util.SharedLog; @@ -27,8 +28,11 @@ import android.os.Handler; import android.provider.Settings; import java.net.Inet4Address; +import java.net.Inet6Address; import java.net.InetAddress; import java.util.ArrayList; +import java.util.HashSet; +import java.util.Objects; import java.util.Set; /** @@ -47,7 +51,13 @@ public class OffloadController { private boolean mConfigInitialized; private boolean mControlInitialized; private LinkProperties mUpstreamLinkProperties; + // The complete set of offload-exempt prefixes passed in via Tethering from + // all upstream and downstream sources. private Set<IpPrefix> mExemptPrefixes; + // A strictly "smaller" set of prefixes, wherein offload-approved prefixes + // (e.g. downstream on-link prefixes) have been removed and replaced with + // prefixes representing only the locally-assigned IP addresses. + private Set<String> mLastLocalPrefixStrs; public OffloadController(Handler h, OffloadHardwareInterface hwi, ContentResolver contentResolver, SharedLog log) { @@ -55,6 +65,8 @@ public class OffloadController { mHwInterface = hwi; mContentResolver = contentResolver; mLog = log.forSubComponent(TAG); + mExemptPrefixes = new HashSet<>(); + mLastLocalPrefixStrs = new HashSet<>(); } public void start() { @@ -134,25 +146,22 @@ public class OffloadController { } public void setUpstreamLinkProperties(LinkProperties lp) { - if (!started()) return; + if (!started() || Objects.equals(mUpstreamLinkProperties, lp)) return; mUpstreamLinkProperties = (lp != null) ? new LinkProperties(lp) : null; // TODO: examine return code and decide what to do if programming // upstream parameters fails (probably just wait for a subsequent // onOffloadEvent() callback to tell us offload is available again and // then reapply all state). + computeAndPushLocalPrefixes(); pushUpstreamParameters(); } - public void updateExemptPrefixes(Set<IpPrefix> exemptPrefixes) { + public void setLocalPrefixes(Set<IpPrefix> localPrefixes) { if (!started()) return; - mExemptPrefixes = exemptPrefixes; - // TODO: - // - add IP addresses from all downstream link properties - // - add routes from all non-tethering downstream link properties - // - remove any 64share prefixes - // - push this to the HAL + mExemptPrefixes = localPrefixes; + computeAndPushLocalPrefixes(); } public void notifyDownstreamLinkProperties(LinkProperties lp) { @@ -215,4 +224,42 @@ public class OffloadController { return mHwInterface.setUpstreamParameters( iface, v4addr, v4gateway, (v6gateways.isEmpty() ? null : v6gateways)); } + + private boolean computeAndPushLocalPrefixes() { + final Set<String> localPrefixStrs = computeLocalPrefixStrings( + mExemptPrefixes, mUpstreamLinkProperties); + if (mLastLocalPrefixStrs.equals(localPrefixStrs)) return true; + + mLastLocalPrefixStrs = localPrefixStrs; + return mHwInterface.setLocalPrefixes(new ArrayList<>(localPrefixStrs)); + } + + // TODO: Factor in downstream LinkProperties once that information is available. + private static Set<String> computeLocalPrefixStrings( + Set<IpPrefix> localPrefixes, LinkProperties upstreamLinkProperties) { + // Create an editable copy. + final Set<IpPrefix> prefixSet = new HashSet<>(localPrefixes); + + // TODO: If a downstream interface (not currently passed in) is reusing + // the /64 of the upstream (64share) then: + // + // [a] remove that /64 from the local prefixes + // [b] add in /128s for IP addresses on the downstream interface + // [c] add in /128s for IP addresses on the upstream interface + // + // Until downstream information is available here, simply add /128s from + // the upstream network; they'll just be redundant with their /64. + if (upstreamLinkProperties != null) { + for (LinkAddress linkAddr : upstreamLinkProperties.getLinkAddresses()) { + if (!linkAddr.isGlobalPreferred()) continue; + final InetAddress ip = linkAddr.getAddress(); + if (!(ip instanceof Inet6Address)) continue; + prefixSet.add(new IpPrefix(ip, 128)); + } + } + + final HashSet<String> localPrefixStrs = new HashSet<>(); + for (IpPrefix pfx : prefixSet) localPrefixStrs.add(pfx.toString()); + return localPrefixStrs; + } } diff --git a/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java b/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java index b648f5182242..4df566f03d6d 100644 --- a/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java +++ b/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java @@ -168,6 +168,26 @@ public class OffloadHardwareInterface { return stats; } + public boolean setLocalPrefixes(ArrayList<String> localPrefixes) { + final String logmsg = String.format("setLocalPrefixes([%s])", + String.join(",", localPrefixes)); + + final CbResults results = new CbResults(); + try { + mOffloadControl.setLocalPrefixes(localPrefixes, + (boolean success, String errMsg) -> { + results.success = success; + results.errMsg = errMsg; + }); + } catch (RemoteException e) { + record(logmsg, e); + return false; + } + + record(logmsg, results); + return results.success; + } + public boolean setUpstreamParameters( String iface, String v4addr, String v4gateway, ArrayList<String> v6gws) { iface = (iface != null) ? iface : NO_INTERFACE_NAME; diff --git a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java index 4bac69ce7495..69678df4543d 100644 --- a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java +++ b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java @@ -161,6 +161,8 @@ public class TetherInterfaceStateMachine extends StateMachine { public int lastError() { return mLastError; } + public LinkProperties linkProperties() { return new LinkProperties(mLinkProperties); } + public void stop() { sendMessage(CMD_INTERFACE_DOWN); } public void unwanted() { sendMessage(CMD_TETHER_UNREQUESTED); } diff --git a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java index eb66767bfdfa..c5f752807cb7 100644 --- a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java +++ b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java @@ -34,6 +34,7 @@ import android.net.NetworkCapabilities; import android.net.NetworkRequest; import android.net.NetworkState; import android.net.util.NetworkConstants; +import android.net.util.PrefixUtils; import android.net.util.SharedLog; import android.util.Log; @@ -72,16 +73,11 @@ public class UpstreamNetworkMonitor { private static final boolean DBG = false; private static final boolean VDBG = false; - private static final IpPrefix[] MINIMUM_LOCAL_PREFIXES_SET = { - prefix("127.0.0.0/8"), prefix("169.254.0.0/16"), - prefix("::/3"), prefix("fe80::/64"), prefix("fc00::/7"), prefix("ff00::/8"), - }; - public static final int EVENT_ON_AVAILABLE = 1; public static final int EVENT_ON_CAPABILITIES = 2; public static final int EVENT_ON_LINKPROPERTIES = 3; public static final int EVENT_ON_LOST = 4; - public static final int NOTIFY_EXEMPT_PREFIXES = 10; + public static final int NOTIFY_LOCAL_PREFIXES = 10; private static final int CALLBACK_LISTEN_ALL = 1; private static final int CALLBACK_TRACK_DEFAULT = 2; @@ -93,7 +89,7 @@ public class UpstreamNetworkMonitor { private final Handler mHandler; private final int mWhat; private final HashMap<Network, NetworkState> mNetworkMap = new HashMap<>(); - private HashSet<IpPrefix> mOffloadExemptPrefixes; + private HashSet<IpPrefix> mLocalPrefixes; private ConnectivityManager mCM; private NetworkCallback mListenAllCallback; private NetworkCallback mDefaultNetworkCallback; @@ -107,7 +103,7 @@ public class UpstreamNetworkMonitor { mHandler = mTarget.getHandler(); mLog = log.forSubComponent(TAG); mWhat = what; - mOffloadExemptPrefixes = allOffloadExemptPrefixes(mNetworkMap.values()); + mLocalPrefixes = new HashSet<>(); } @VisibleForTesting @@ -223,8 +219,8 @@ public class UpstreamNetworkMonitor { return typeStatePair.ns; } - public Set<IpPrefix> getOffloadExemptPrefixes() { - return (Set<IpPrefix>) mOffloadExemptPrefixes.clone(); + public Set<IpPrefix> getLocalPrefixes() { + return (Set<IpPrefix>) mLocalPrefixes.clone(); } private void handleAvailable(int callbackType, Network network) { @@ -360,11 +356,11 @@ public class UpstreamNetworkMonitor { notifyTarget(EVENT_ON_LOST, mNetworkMap.remove(network)); } - private void recomputeOffloadExemptPrefixes() { - final HashSet<IpPrefix> exemptPrefixes = allOffloadExemptPrefixes(mNetworkMap.values()); - if (!mOffloadExemptPrefixes.equals(exemptPrefixes)) { - mOffloadExemptPrefixes = exemptPrefixes; - notifyTarget(NOTIFY_EXEMPT_PREFIXES, exemptPrefixes.clone()); + private void recomputeLocalPrefixes() { + final HashSet<IpPrefix> localPrefixes = allLocalPrefixes(mNetworkMap.values()); + if (!mLocalPrefixes.equals(localPrefixes)) { + mLocalPrefixes = localPrefixes; + notifyTarget(NOTIFY_LOCAL_PREFIXES, localPrefixes.clone()); } } @@ -402,7 +398,7 @@ public class UpstreamNetworkMonitor { @Override public void onLinkPropertiesChanged(Network network, LinkProperties newLp) { handleLinkProp(network, newLp); - recomputeOffloadExemptPrefixes(); + recomputeLocalPrefixes(); } // TODO: Handle onNetworkSuspended(); @@ -411,7 +407,7 @@ public class UpstreamNetworkMonitor { @Override public void onLost(Network network) { handleLost(mCallbackType, network); - recomputeOffloadExemptPrefixes(); + recomputeLocalPrefixes(); } } @@ -460,35 +456,15 @@ public class UpstreamNetworkMonitor { return result; } - private static HashSet<IpPrefix> allOffloadExemptPrefixes(Iterable<NetworkState> netStates) { + private static HashSet<IpPrefix> allLocalPrefixes(Iterable<NetworkState> netStates) { final HashSet<IpPrefix> prefixSet = new HashSet<>(); - addDefaultLocalPrefixes(prefixSet); - for (NetworkState ns : netStates) { - addOffloadExemptPrefixes(prefixSet, ns.linkProperties); + final LinkProperties lp = ns.linkProperties; + if (lp == null) continue; + prefixSet.addAll(PrefixUtils.localPrefixesFrom(lp)); } return prefixSet; } - - private static void addDefaultLocalPrefixes(Set<IpPrefix> prefixSet) { - Collections.addAll(prefixSet, MINIMUM_LOCAL_PREFIXES_SET); - } - - private static void addOffloadExemptPrefixes(Set<IpPrefix> prefixSet, LinkProperties lp) { - if (lp == null) return; - - for (LinkAddress linkAddr : lp.getAllLinkAddresses()) { - prefixSet.add(new IpPrefix(linkAddr.getAddress(), linkAddr.getPrefixLength())); - } - - // TODO: Consider adding other non-default routes associated with this - // network. Traffic to these destinations should perhaps not go through - // the Internet (upstream). - } - - private static IpPrefix prefix(String prefixStr) { - return new IpPrefix(prefixStr); - } } diff --git a/services/net/java/android/net/util/PrefixUtils.java b/services/net/java/android/net/util/PrefixUtils.java new file mode 100644 index 000000000000..962aab459a19 --- /dev/null +++ b/services/net/java/android/net/util/PrefixUtils.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.util; + +import android.net.IpPrefix; +import android.net.LinkAddress; +import android.net.LinkProperties; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + + +/** + * @hide + */ +public class PrefixUtils { + private static final IpPrefix[] MIN_NON_FORWARDABLE_PREFIXES = { + pfx("127.0.0.0/8"), // IPv4 loopback + pfx("169.254.0.0/16"), // IPv4 link-local, RFC3927#section-8 + pfx("::/3"), + pfx("fe80::/64"), // IPv6 link-local + pfx("fc00::/7"), // IPv6 ULA + pfx("ff02::/8"), // IPv6 link-local multicast + }; + + public static final IpPrefix DEFAULT_WIFI_P2P_PREFIX = pfx("192.168.49.0/24"); + + public static Set<IpPrefix> getNonForwardablePrefixes() { + final HashSet<IpPrefix> prefixes = new HashSet<>(); + addNonForwardablePrefixes(prefixes); + return prefixes; + } + + public static void addNonForwardablePrefixes(Set<IpPrefix> prefixes) { + Collections.addAll(prefixes, MIN_NON_FORWARDABLE_PREFIXES); + } + + public static Set<IpPrefix> localPrefixesFrom(LinkProperties lp) { + final HashSet<IpPrefix> localPrefixes = new HashSet<>(); + if (lp == null) return localPrefixes; + + for (LinkAddress addr : lp.getAllLinkAddresses()) { + if (addr.getAddress().isLinkLocalAddress()) continue; + localPrefixes.add(asIpPrefix(addr)); + } + // TODO: Add directly-connected routes as well (ones from which we did + // not also form a LinkAddress)? + + return localPrefixes; + } + + public static IpPrefix asIpPrefix(LinkAddress addr) { + return new IpPrefix(addr.getAddress(), addr.getPrefixLength()); + } + + private static IpPrefix pfx(String prefixStr) { + return new IpPrefix(prefixStr); + } +} diff --git a/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java b/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java index 0dedf703f3bb..0e4a36ccfc7e 100644 --- a/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java +++ b/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java @@ -31,6 +31,7 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.content.pm.ApplicationInfo; +import android.net.IpPrefix; import android.net.LinkAddress; import android.net.LinkProperties; import android.net.RouteInfo; @@ -45,6 +46,8 @@ import com.android.internal.util.test.FakeSettingsProvider; import java.net.InetAddress; import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; import org.junit.After; import org.junit.Before; @@ -182,17 +185,43 @@ public class OffloadControllerTest { any(OffloadHardwareInterface.ControlCallback.class)); inOrder.verifyNoMoreInteractions(); + // In reality, the UpstreamNetworkMonitor would have passed down to us + // a covering set of local prefixes representing a minimum essential + // set plus all the prefixes on networks with network agents. + // + // We simulate that there, and then add upstream elements one by one + // and watch what happens. + final Set<IpPrefix> minimumLocalPrefixes = new HashSet<>(); + for (String s : new String[]{ + "127.0.0.0/8", "192.0.2.0/24", "fe80::/64", "2001:db8::/64"}) { + minimumLocalPrefixes.add(new IpPrefix(s)); + } + offload.setLocalPrefixes(minimumLocalPrefixes); + inOrder.verify(mHardware, times(1)).setLocalPrefixes(mStringArrayCaptor.capture()); + ArrayList<String> localPrefixes = mStringArrayCaptor.getValue(); + assertEquals(4, localPrefixes.size()); + assertTrue(localPrefixes.contains("127.0.0.0/8")); + assertTrue(localPrefixes.contains("192.0.2.0/24")); + assertTrue(localPrefixes.contains("fe80::/64")); + assertTrue(localPrefixes.contains("2001:db8::/64")); + inOrder.verifyNoMoreInteractions(); + offload.setUpstreamLinkProperties(null); - inOrder.verify(mHardware, times(1)).setUpstreamParameters( - eq(null), eq(null), eq(null), eq(null)); + // No change in local addresses means no call to setLocalPrefixes(). + inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture()); + // This LinkProperties value does not differ from the default upstream. + // There should be no extraneous call to setUpstreamParameters(). + inOrder.verify(mHardware, never()).setUpstreamParameters( + anyObject(), anyObject(), anyObject(), anyObject()); inOrder.verifyNoMoreInteractions(); - reset(mHardware); final LinkProperties lp = new LinkProperties(); final String testIfName = "rmnet_data17"; lp.setInterfaceName(testIfName); offload.setUpstreamLinkProperties(lp); + // No change in local addresses means no call to setLocalPrefixes(). + inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture()); inOrder.verify(mHardware, times(1)).setUpstreamParameters( eq(testIfName), eq(null), eq(null), eq(null)); inOrder.verifyNoMoreInteractions(); @@ -200,7 +229,15 @@ public class OffloadControllerTest { final String ipv4Addr = "192.0.2.5"; final String linkAddr = ipv4Addr + "/24"; lp.addLinkAddress(new LinkAddress(linkAddr)); + lp.addRoute(new RouteInfo(new IpPrefix("192.0.2.0/24"))); offload.setUpstreamLinkProperties(lp); + // IPv4 prefixes and addresses on the upstream are simply left as whole + // prefixes (already passed in from UpstreamNetworkMonitor code). If a + // tethering client sends traffic to the IPv4 default router or other + // clients on the upstream this will not be hardware-forwarded, and that + // should be fine for now. Ergo: no change in local addresses, no call + // to setLocalPrefixes(). + inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture()); inOrder.verify(mHardware, times(1)).setUpstreamParameters( eq(testIfName), eq(ipv4Addr), eq(null), eq(null)); inOrder.verifyNoMoreInteractions(); @@ -208,6 +245,8 @@ public class OffloadControllerTest { final String ipv4Gateway = "192.0.2.1"; lp.addRoute(new RouteInfo(InetAddress.getByName(ipv4Gateway))); offload.setUpstreamLinkProperties(lp); + // No change in local addresses means no call to setLocalPrefixes(). + inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture()); inOrder.verify(mHardware, times(1)).setUpstreamParameters( eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), eq(null)); inOrder.verifyNoMoreInteractions(); @@ -215,6 +254,8 @@ public class OffloadControllerTest { final String ipv6Gw1 = "fe80::cafe"; lp.addRoute(new RouteInfo(InetAddress.getByName(ipv6Gw1))); offload.setUpstreamLinkProperties(lp); + // No change in local addresses means no call to setLocalPrefixes(). + inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture()); inOrder.verify(mHardware, times(1)).setUpstreamParameters( eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), mStringArrayCaptor.capture()); ArrayList<String> v6gws = mStringArrayCaptor.getValue(); @@ -225,6 +266,8 @@ public class OffloadControllerTest { final String ipv6Gw2 = "fe80::d00d"; lp.addRoute(new RouteInfo(InetAddress.getByName(ipv6Gw2))); offload.setUpstreamLinkProperties(lp); + // No change in local addresses means no call to setLocalPrefixes(). + inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture()); inOrder.verify(mHardware, times(1)).setUpstreamParameters( eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), mStringArrayCaptor.capture()); v6gws = mStringArrayCaptor.getValue(); @@ -240,6 +283,8 @@ public class OffloadControllerTest { stacked.addRoute(new RouteInfo(InetAddress.getByName("fe80::bad:f00"))); assertTrue(lp.addStackedLink(stacked)); offload.setUpstreamLinkProperties(lp); + // No change in local addresses means no call to setLocalPrefixes(). + inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture()); inOrder.verify(mHardware, times(1)).setUpstreamParameters( eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), mStringArrayCaptor.capture()); v6gws = mStringArrayCaptor.getValue(); @@ -247,5 +292,43 @@ public class OffloadControllerTest { assertTrue(v6gws.contains(ipv6Gw1)); assertTrue(v6gws.contains(ipv6Gw2)); inOrder.verifyNoMoreInteractions(); + + // Add in some IPv6 upstream info. When there is a tethered downstream + // making use of the IPv6 prefix we would expect to see the /64 route + // removed from "local prefixes" and /128s added for the upstream IPv6 + // addresses. This is not yet implemented, and for now we simply + // expect to see these /128s. + lp.addRoute(new RouteInfo(new IpPrefix("2001:db8::/64"))); + // "2001:db8::/64" plus "assigned" ASCII in hex + lp.addLinkAddress(new LinkAddress("2001:db8::6173:7369:676e:6564/64")); + // "2001:db8::/64" plus "random" ASCII in hex + lp.addLinkAddress(new LinkAddress("2001:db8::7261:6e64:6f6d/64")); + offload.setUpstreamLinkProperties(lp); + inOrder.verify(mHardware, times(1)).setLocalPrefixes(mStringArrayCaptor.capture()); + localPrefixes = mStringArrayCaptor.getValue(); + assertEquals(6, localPrefixes.size()); + assertTrue(localPrefixes.contains("127.0.0.0/8")); + assertTrue(localPrefixes.contains("192.0.2.0/24")); + assertTrue(localPrefixes.contains("fe80::/64")); + assertTrue(localPrefixes.contains("2001:db8::/64")); + assertTrue(localPrefixes.contains("2001:db8::6173:7369:676e:6564/128")); + assertTrue(localPrefixes.contains("2001:db8::7261:6e64:6f6d/128")); + // The relevant parts of the LinkProperties have not changed, but at the + // moment we do not de-dup upstream LinkProperties this carefully. + inOrder.verify(mHardware, times(1)).setUpstreamParameters( + eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), mStringArrayCaptor.capture()); + v6gws = mStringArrayCaptor.getValue(); + assertEquals(2, v6gws.size()); + assertTrue(v6gws.contains(ipv6Gw1)); + assertTrue(v6gws.contains(ipv6Gw2)); + inOrder.verifyNoMoreInteractions(); + + // Completely identical LinkProperties updates are de-duped. + offload.setUpstreamLinkProperties(lp); + // This LinkProperties value does not differ from the default upstream. + // There should be no extraneous call to setUpstreamParameters(). + inOrder.verify(mHardware, never()).setUpstreamParameters( + anyObject(), anyObject(), anyObject(), anyObject()); + inOrder.verifyNoMoreInteractions(); } } diff --git a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java index 69c93b14a486..c3b9defdec4e 100644 --- a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java +++ b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java @@ -324,19 +324,14 @@ public class UpstreamNetworkMonitorTest { } @Test - public void testOffloadExemptPrefixes() throws Exception { + public void testLocalPrefixes() throws Exception { mUNM.start(); - // [0] Test minimum set of exempt prefixes. - Set<IpPrefix> exempt = mUNM.getOffloadExemptPrefixes(); - final String[] MINSET = { - "127.0.0.0/8", "169.254.0.0/16", - "::/3", "fe80::/64", "fc00::/7", "ff00::/8", - }; - assertPrefixSet(exempt, INCLUDES, MINSET); + // [0] Test minimum set of local prefixes. + Set<IpPrefix> local = mUNM.getLocalPrefixes(); + assertTrue(local.isEmpty()); + final Set<String> alreadySeen = new HashSet<>(); - Collections.addAll(alreadySeen, MINSET); - assertEquals(alreadySeen.size(), exempt.size()); // [1] Pretend Wi-Fi connects. final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, TRANSPORT_WIFI); @@ -355,15 +350,15 @@ public class UpstreamNetworkMonitorTest { wifiAgent.fakeConnect(); wifiAgent.sendLinkProperties(wifiLp); - exempt = mUNM.getOffloadExemptPrefixes(); - assertPrefixSet(exempt, INCLUDES, alreadySeen); + local = mUNM.getLocalPrefixes(); + assertPrefixSet(local, INCLUDES, alreadySeen); final String[] wifiLinkPrefixes = { - // Excludes link-local as that's already tested within MINSET. + // Link-local prefixes are excluded and dealt with elsewhere. "100.112.96.0/20", "2001:db8:4:fd00::/64", "fd6a:a640:60bf:e985::/64", }; - assertPrefixSet(exempt, INCLUDES, wifiLinkPrefixes); + assertPrefixSet(local, INCLUDES, wifiLinkPrefixes); Collections.addAll(alreadySeen, wifiLinkPrefixes); - assertEquals(alreadySeen.size(), exempt.size()); + assertEquals(alreadySeen.size(), local.size()); // [2] Pretend mobile connects. final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR); @@ -379,12 +374,12 @@ public class UpstreamNetworkMonitorTest { cellAgent.fakeConnect(); cellAgent.sendLinkProperties(cellLp); - exempt = mUNM.getOffloadExemptPrefixes(); - assertPrefixSet(exempt, INCLUDES, alreadySeen); + local = mUNM.getLocalPrefixes(); + assertPrefixSet(local, INCLUDES, alreadySeen); final String[] cellLinkPrefixes = { "10.102.211.32/27", "2001:db8:0:1::/64" }; - assertPrefixSet(exempt, INCLUDES, cellLinkPrefixes); + assertPrefixSet(local, INCLUDES, cellLinkPrefixes); Collections.addAll(alreadySeen, cellLinkPrefixes); - assertEquals(alreadySeen.size(), exempt.size()); + assertEquals(alreadySeen.size(), local.size()); // [3] Pretend DUN connects. final TestNetworkAgent dunAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR); @@ -401,21 +396,20 @@ public class UpstreamNetworkMonitorTest { dunAgent.fakeConnect(); dunAgent.sendLinkProperties(dunLp); - exempt = mUNM.getOffloadExemptPrefixes(); - assertPrefixSet(exempt, INCLUDES, alreadySeen); + local = mUNM.getLocalPrefixes(); + assertPrefixSet(local, INCLUDES, alreadySeen); final String[] dunLinkPrefixes = { "192.0.2.32/27", "2001:db8:1:2::/64" }; - assertPrefixSet(exempt, INCLUDES, dunLinkPrefixes); + assertPrefixSet(local, INCLUDES, dunLinkPrefixes); Collections.addAll(alreadySeen, dunLinkPrefixes); - assertEquals(alreadySeen.size(), exempt.size()); + assertEquals(alreadySeen.size(), local.size()); // [4] Pretend Wi-Fi disconnected. It's addresses/prefixes should no // longer be included (should be properly removed). wifiAgent.fakeDisconnect(); - exempt = mUNM.getOffloadExemptPrefixes(); - assertPrefixSet(exempt, INCLUDES, MINSET); - assertPrefixSet(exempt, EXCLUDES, wifiLinkPrefixes); - assertPrefixSet(exempt, INCLUDES, cellLinkPrefixes); - assertPrefixSet(exempt, INCLUDES, dunLinkPrefixes); + local = mUNM.getLocalPrefixes(); + assertPrefixSet(local, EXCLUDES, wifiLinkPrefixes); + assertPrefixSet(local, INCLUDES, cellLinkPrefixes); + assertPrefixSet(local, INCLUDES, dunLinkPrefixes); } private void assertSatisfiesLegacyType(int legacyType, NetworkState ns) { |