Loading services/core/java/com/android/server/connectivity/Tethering.java +20 −26 Original line number Diff line number Diff line Loading @@ -56,6 +56,7 @@ import android.net.NetworkInfo; import android.net.NetworkState; import android.net.NetworkUtils; import android.net.RouteInfo; import android.net.util.InterfaceSet; import android.net.util.PrefixUtils; import android.net.util.SharedLog; import android.net.util.VersionedBroadcastListener; Loading Loading @@ -98,6 +99,7 @@ import com.android.server.connectivity.tethering.SimChangeListener; import com.android.server.connectivity.tethering.TetherInterfaceStateMachine; import com.android.server.connectivity.tethering.TetheringConfiguration; import com.android.server.connectivity.tethering.TetheringDependencies; import com.android.server.connectivity.tethering.TetheringInterfaceUtils; import com.android.server.connectivity.tethering.UpstreamNetworkMonitor; import com.android.server.net.BaseNetworkObserver; Loading Loading @@ -184,7 +186,7 @@ public class Tethering extends BaseNetworkObserver { private final TetheringDependencies mDeps; private volatile TetheringConfiguration mConfig; private String mCurrentUpstreamIface; private InterfaceSet mCurrentUpstreamIfaceSet; private Notification.Builder mTetheredNotificationBuilder; private int mLastNotificationId; Loading Loading @@ -1162,12 +1164,11 @@ public class Tethering extends BaseNetworkObserver { } // Needed because the canonical source of upstream truth is just the // upstream interface name, |mCurrentUpstreamIface|. This is ripe for // future simplification, once the upstream Network is canonical. // upstream interface set, |mCurrentUpstreamIfaceSet|. private boolean pertainsToCurrentUpstream(NetworkState ns) { if (ns != null && ns.linkProperties != null && mCurrentUpstreamIface != null) { if (ns != null && ns.linkProperties != null && mCurrentUpstreamIfaceSet != null) { for (String ifname : ns.linkProperties.getAllInterfaceNames()) { if (mCurrentUpstreamIface.equals(ifname)) { if (mCurrentUpstreamIfaceSet.ifnames.contains(ifname)) { return true; } } Loading Loading @@ -1362,31 +1363,27 @@ public class Tethering extends BaseNetworkObserver { } protected void setUpstreamNetwork(NetworkState ns) { String iface = null; InterfaceSet ifaces = null; if (ns != null) { // Find the interface with the default IPv4 route. It may be the // interface described by linkProperties, or one of the interfaces // stacked on top of it. mLog.i("Looking for default routes on: " + ns.linkProperties); final String iface4 = getIPv4DefaultRouteInterface(ns); final String iface6 = getIPv6DefaultRouteInterface(ns); mLog.i("IPv4/IPv6 upstream interface(s): " + iface4 + "/" + iface6); iface = (iface4 != null) ? iface4 : null /* TODO: iface6 */; ifaces = TetheringInterfaceUtils.getTetheringInterfaces(ns); mLog.i("Found upstream interface(s): " + ifaces); } if (iface != null) { if (ifaces != null) { setDnsForwarders(ns.network, ns.linkProperties); } notifyDownstreamsOfNewUpstreamIface(iface); notifyDownstreamsOfNewUpstreamIface(ifaces); if (ns != null && pertainsToCurrentUpstream(ns)) { // If we already have NetworkState for this network examine // it immediately, because there likely will be no second // EVENT_ON_AVAILABLE (it was already received). handleNewUpstreamNetworkState(ns); } else if (mCurrentUpstreamIface == null) { // There are no available upstream networks, or none that // have an IPv4 default route (current metric for success). } else if (mCurrentUpstreamIfaceSet == null) { // There are no available upstream networks. handleNewUpstreamNetworkState(null); } } Loading @@ -1413,12 +1410,10 @@ public class Tethering extends BaseNetworkObserver { } } protected void notifyDownstreamsOfNewUpstreamIface(String ifaceName) { mLog.log("Notifying downstreams of upstream=" + ifaceName); mCurrentUpstreamIface = ifaceName; protected void notifyDownstreamsOfNewUpstreamIface(InterfaceSet ifaces) { mCurrentUpstreamIfaceSet = ifaces; for (TetherInterfaceStateMachine sm : mNotifyList) { sm.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED, ifaceName); sm.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED, ifaces); } } Loading Loading @@ -1490,7 +1485,7 @@ public class Tethering extends BaseNetworkObserver { // For example, after CONNECTIVITY_ACTION listening is removed, here // is where we could observe a Wi-Fi network becoming available and // passing validation. if (mCurrentUpstreamIface == null) { if (mCurrentUpstreamIfaceSet == null) { // If we have no upstream interface, try to run through upstream // selection again. If, for example, IPv4 connectivity has shown up // after IPv6 (e.g., 464xlat became available) we want the chance to Loading @@ -1514,8 +1509,7 @@ public class Tethering extends BaseNetworkObserver { handleNewUpstreamNetworkState(ns); break; case UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES: setDnsForwarders(ns.network, ns.linkProperties); handleNewUpstreamNetworkState(ns); chooseUpstreamType(false); break; case UpstreamNetworkMonitor.EVENT_ON_LOST: // TODO: Re-evaluate possible upstreams. Currently upstream Loading Loading @@ -1588,7 +1582,7 @@ public class Tethering extends BaseNetworkObserver { if (VDBG) Log.d(TAG, "Tether Mode requested by " + who); handleInterfaceServingStateActive(message.arg1, who); who.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED, mCurrentUpstreamIface); mCurrentUpstreamIfaceSet); // If there has been a change and an upstream is now // desired, kick off the selection process. final boolean previousUpstreamWanted = updateUpstreamWanted(); Loading Loading @@ -1866,7 +1860,7 @@ public class Tethering extends BaseNetworkObserver { pw.println(" - lastError = " + tetherState.lastError); } pw.println("Upstream wanted: " + upstreamWanted()); pw.println("Current upstream interface: " + mCurrentUpstreamIface); pw.println("Current upstream interface(s): " + mCurrentUpstreamIfaceSet); pw.decreaseIndent(); } Loading services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java +1 −67 Original line number Diff line number Diff line Loading @@ -30,10 +30,8 @@ import android.util.Log; import java.net.Inet6Address; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedList; import java.util.Random; Loading Loading @@ -119,7 +117,7 @@ public class IPv6TetheringCoordinator { if (VDBG) { Log.d(TAG, "updateUpstreamNetworkState: " + toDebugString(ns)); } if (!canTetherIPv6(ns, mLog)) { if (TetheringInterfaceUtils.getIPv6Interface(ns) == null) { stopIPv6TetheringOnAllInterfaces(); setUpstreamNetworkState(null); return; Loading Loading @@ -208,70 +206,6 @@ public class IPv6TetheringCoordinator { return null; } private static boolean canTetherIPv6(NetworkState ns, SharedLog sharedLog) { // Broadly speaking: // // [1] does the upstream have an IPv6 default route? // // and // // [2] does the upstream have one or more global IPv6 /64s // dedicated to this device? // // In lieu of Prefix Delegation and other evaluation of whether a // prefix may or may not be dedicated to this device, for now just // check whether the upstream is TRANSPORT_CELLULAR. This works // because "[t]he 3GPP network allocates each default bearer a unique // /64 prefix", per RFC 6459, Section 5.2. final boolean canTether = (ns != null) && (ns.network != null) && (ns.linkProperties != null) && (ns.networkCapabilities != null) && // At least one upstream DNS server: ns.linkProperties.isProvisioned() && // Minimal amount of IPv6 provisioning: ns.linkProperties.hasIPv6DefaultRoute() && ns.linkProperties.hasGlobalIPv6Address() && // Temporary approximation of "dedicated prefix": ns.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR); // For now, we do not support separate IPv4 and IPv6 upstreams (e.g. // tethering with 464xlat involved). TODO: Rectify this shortcoming, // likely by calling NetworkManagementService#startInterfaceForwarding() // for all upstream interfaces. RouteInfo v4default = null; RouteInfo v6default = null; if (canTether) { for (RouteInfo r : ns.linkProperties.getAllRoutes()) { if (r.isIPv4Default()) { v4default = r; } else if (r.isIPv6Default()) { v6default = r; } if (v4default != null && v6default != null) { break; } } } final boolean supportedConfiguration = (v4default != null) && (v6default != null) && (v4default.getInterface() != null) && v4default.getInterface().equals(v6default.getInterface()); final boolean outcome = canTether && supportedConfiguration; if (ns == null) { sharedLog.log("No available upstream."); } else { sharedLog.log(String.format("IPv6 tethering is %s for upstream: %s", (outcome ? "available" : "not available"), toDebugString(ns))); } return outcome; } private static LinkProperties getIPv6OnlyLinkProperties(LinkProperties lp) { final LinkProperties v6only = new LinkProperties(); if (lp == null) { Loading services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java +49 −17 Original line number Diff line number Diff line Loading @@ -31,7 +31,7 @@ import android.net.ip.InterfaceController; import android.net.ip.RouterAdvertisementDaemon; import android.net.ip.RouterAdvertisementDaemon.RaParams; import android.net.util.InterfaceParams; import android.net.util.NetdService; import android.net.util.InterfaceSet; import android.net.util.SharedLog; import android.os.INetworkManagementService; import android.os.Looper; Loading @@ -49,12 +49,12 @@ import com.android.internal.util.StateMachine; import java.net.Inet6Address; import java.net.InetAddress; import java.net.SocketException; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.HashSet; import java.util.Objects; import java.util.Random; import java.util.Set; /** * Provides the interface to IP-layer serving functionality for a given network Loading Loading @@ -121,7 +121,7 @@ public class TetherInterfaceStateMachine extends StateMachine { private int mLastError; private int mServingMode; private String mMyUpstreamIfaceName; // may change over time private InterfaceSet mUpstreamIfaceSet; // may change over time private InterfaceParams mInterfaceParams; // TODO: De-duplicate this with mLinkProperties above. Currently, these link // properties are those selected by the IPv6TetheringCoordinator and relayed Loading Loading @@ -622,10 +622,10 @@ public class TetherInterfaceStateMachine extends StateMachine { } private void cleanupUpstream() { if (mMyUpstreamIfaceName == null) return; if (mUpstreamIfaceSet == null) return; cleanupUpstreamInterface(mMyUpstreamIfaceName); mMyUpstreamIfaceName = null; for (String ifname : mUpstreamIfaceSet.ifnames) cleanupUpstreamInterface(ifname); mUpstreamIfaceSet = null; } private void cleanupUpstreamInterface(String upstreamIface) { Loading Loading @@ -661,34 +661,66 @@ public class TetherInterfaceStateMachine extends StateMachine { mLog.e("CMD_TETHER_REQUESTED while already tethering."); break; case CMD_TETHER_CONNECTION_CHANGED: String newUpstreamIfaceName = (String)(message.obj); if ((mMyUpstreamIfaceName == null && newUpstreamIfaceName == null) || (mMyUpstreamIfaceName != null && mMyUpstreamIfaceName.equals(newUpstreamIfaceName))) { final InterfaceSet newUpstreamIfaceSet = (InterfaceSet) message.obj; if (noChangeInUpstreamIfaceSet(newUpstreamIfaceSet)) { if (VDBG) Log.d(TAG, "Connection changed noop - dropping"); break; } if (newUpstreamIfaceSet == null) { cleanupUpstream(); if (newUpstreamIfaceName != null) { break; } for (String removed : upstreamInterfacesRemoved(newUpstreamIfaceSet)) { cleanupUpstreamInterface(removed); } final Set<String> added = upstreamInterfacesAdd(newUpstreamIfaceSet); // This makes the call to cleanupUpstream() in the error // path for any interface neatly cleanup all the interfaces. mUpstreamIfaceSet = newUpstreamIfaceSet; for (String ifname : added) { try { mNMService.enableNat(mIfaceName, newUpstreamIfaceName); mNMService.startInterfaceForwarding(mIfaceName, newUpstreamIfaceName); mNMService.enableNat(mIfaceName, ifname); mNMService.startInterfaceForwarding(mIfaceName, ifname); } catch (Exception e) { mLog.e("Exception enabling NAT: " + e); cleanupUpstreamInterface(newUpstreamIfaceName); cleanupUpstream(); mLastError = ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR; transitionTo(mInitialState); return true; } } mMyUpstreamIfaceName = newUpstreamIfaceName; break; default: return false; } return true; } private boolean noChangeInUpstreamIfaceSet(InterfaceSet newIfaces) { if (mUpstreamIfaceSet == null && newIfaces == null) return true; if (mUpstreamIfaceSet != null && newIfaces != null) { return mUpstreamIfaceSet.equals(newIfaces); } return false; } private Set<String> upstreamInterfacesRemoved(InterfaceSet newIfaces) { if (mUpstreamIfaceSet == null) return new HashSet<>(); final HashSet<String> removed = new HashSet<>(mUpstreamIfaceSet.ifnames); removed.removeAll(newIfaces.ifnames); return removed; } private Set<String> upstreamInterfacesAdd(InterfaceSet newIfaces) { final HashSet<String> added = new HashSet<>(newIfaces.ifnames); if (mUpstreamIfaceSet != null) added.removeAll(mUpstreamIfaceSet.ifnames); return added; } } /** Loading services/core/java/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java 0 → 100644 +90 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 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 com.android.server.connectivity.tethering; import android.annotation.Nullable; import android.net.LinkProperties; import android.net.NetworkCapabilities; import android.net.NetworkState; import android.net.RouteInfo; import android.net.util.InterfaceSet; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; /** * @hide */ public final class TetheringInterfaceUtils { /** * Get upstream interfaces for tethering based on default routes for IPv4/IPv6. * @return null if there is no usable interface, or a set of at least one interface otherwise. */ public static @Nullable InterfaceSet getTetheringInterfaces(NetworkState ns) { if (ns == null) { return null; } final LinkProperties lp = ns.linkProperties; final String if4 = getInterfaceForDestination(lp, Inet4Address.ANY); final String if6 = getIPv6Interface(ns); return (if4 == null && if6 == null) ? null : new InterfaceSet(if4, if6); } /** * Get the upstream interface for IPv6 tethering. * @return null if there is no usable interface, or the interface name otherwise. */ public static @Nullable String getIPv6Interface(NetworkState ns) { // Broadly speaking: // // [1] does the upstream have an IPv6 default route? // // and // // [2] does the upstream have one or more global IPv6 /64s // dedicated to this device? // // In lieu of Prefix Delegation and other evaluation of whether a // prefix may or may not be dedicated to this device, for now just // check whether the upstream is TRANSPORT_CELLULAR. This works // because "[t]he 3GPP network allocates each default bearer a unique // /64 prefix", per RFC 6459, Section 5.2. final boolean canTether = (ns != null) && (ns.network != null) && (ns.linkProperties != null) && (ns.networkCapabilities != null) && // At least one upstream DNS server: ns.linkProperties.hasIPv6DnsServer() && // Minimal amount of IPv6 provisioning: ns.linkProperties.hasGlobalIPv6Address() && // Temporary approximation of "dedicated prefix": ns.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR); return canTether ? getInterfaceForDestination(ns.linkProperties, Inet6Address.ANY) : null; } private static String getInterfaceForDestination(LinkProperties lp, InetAddress dst) { final RouteInfo ri = (lp != null) ? RouteInfo.selectBestRoute(lp.getAllRoutes(), dst) : null; return (ri != null) ? ri.getInterface() : null; } } services/net/java/android/net/util/InterfaceSet.java 0 → 100644 +52 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 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 java.util.Collections; import java.util.HashSet; import java.util.Set; import java.util.StringJoiner; /** * @hide */ public class InterfaceSet { public final Set<String> ifnames; public InterfaceSet(String... names) { final Set<String> nameSet = new HashSet<>(); for (String name : names) { if (name != null) nameSet.add(name); } ifnames = Collections.unmodifiableSet(nameSet); } @Override public String toString() { final StringJoiner sj = new StringJoiner(",", "[", "]"); for (String ifname : ifnames) sj.add(ifname); return sj.toString(); } @Override public boolean equals(Object obj) { return obj != null && obj instanceof InterfaceSet && ifnames.equals(((InterfaceSet)obj).ifnames); } } Loading
services/core/java/com/android/server/connectivity/Tethering.java +20 −26 Original line number Diff line number Diff line Loading @@ -56,6 +56,7 @@ import android.net.NetworkInfo; import android.net.NetworkState; import android.net.NetworkUtils; import android.net.RouteInfo; import android.net.util.InterfaceSet; import android.net.util.PrefixUtils; import android.net.util.SharedLog; import android.net.util.VersionedBroadcastListener; Loading Loading @@ -98,6 +99,7 @@ import com.android.server.connectivity.tethering.SimChangeListener; import com.android.server.connectivity.tethering.TetherInterfaceStateMachine; import com.android.server.connectivity.tethering.TetheringConfiguration; import com.android.server.connectivity.tethering.TetheringDependencies; import com.android.server.connectivity.tethering.TetheringInterfaceUtils; import com.android.server.connectivity.tethering.UpstreamNetworkMonitor; import com.android.server.net.BaseNetworkObserver; Loading Loading @@ -184,7 +186,7 @@ public class Tethering extends BaseNetworkObserver { private final TetheringDependencies mDeps; private volatile TetheringConfiguration mConfig; private String mCurrentUpstreamIface; private InterfaceSet mCurrentUpstreamIfaceSet; private Notification.Builder mTetheredNotificationBuilder; private int mLastNotificationId; Loading Loading @@ -1162,12 +1164,11 @@ public class Tethering extends BaseNetworkObserver { } // Needed because the canonical source of upstream truth is just the // upstream interface name, |mCurrentUpstreamIface|. This is ripe for // future simplification, once the upstream Network is canonical. // upstream interface set, |mCurrentUpstreamIfaceSet|. private boolean pertainsToCurrentUpstream(NetworkState ns) { if (ns != null && ns.linkProperties != null && mCurrentUpstreamIface != null) { if (ns != null && ns.linkProperties != null && mCurrentUpstreamIfaceSet != null) { for (String ifname : ns.linkProperties.getAllInterfaceNames()) { if (mCurrentUpstreamIface.equals(ifname)) { if (mCurrentUpstreamIfaceSet.ifnames.contains(ifname)) { return true; } } Loading Loading @@ -1362,31 +1363,27 @@ public class Tethering extends BaseNetworkObserver { } protected void setUpstreamNetwork(NetworkState ns) { String iface = null; InterfaceSet ifaces = null; if (ns != null) { // Find the interface with the default IPv4 route. It may be the // interface described by linkProperties, or one of the interfaces // stacked on top of it. mLog.i("Looking for default routes on: " + ns.linkProperties); final String iface4 = getIPv4DefaultRouteInterface(ns); final String iface6 = getIPv6DefaultRouteInterface(ns); mLog.i("IPv4/IPv6 upstream interface(s): " + iface4 + "/" + iface6); iface = (iface4 != null) ? iface4 : null /* TODO: iface6 */; ifaces = TetheringInterfaceUtils.getTetheringInterfaces(ns); mLog.i("Found upstream interface(s): " + ifaces); } if (iface != null) { if (ifaces != null) { setDnsForwarders(ns.network, ns.linkProperties); } notifyDownstreamsOfNewUpstreamIface(iface); notifyDownstreamsOfNewUpstreamIface(ifaces); if (ns != null && pertainsToCurrentUpstream(ns)) { // If we already have NetworkState for this network examine // it immediately, because there likely will be no second // EVENT_ON_AVAILABLE (it was already received). handleNewUpstreamNetworkState(ns); } else if (mCurrentUpstreamIface == null) { // There are no available upstream networks, or none that // have an IPv4 default route (current metric for success). } else if (mCurrentUpstreamIfaceSet == null) { // There are no available upstream networks. handleNewUpstreamNetworkState(null); } } Loading @@ -1413,12 +1410,10 @@ public class Tethering extends BaseNetworkObserver { } } protected void notifyDownstreamsOfNewUpstreamIface(String ifaceName) { mLog.log("Notifying downstreams of upstream=" + ifaceName); mCurrentUpstreamIface = ifaceName; protected void notifyDownstreamsOfNewUpstreamIface(InterfaceSet ifaces) { mCurrentUpstreamIfaceSet = ifaces; for (TetherInterfaceStateMachine sm : mNotifyList) { sm.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED, ifaceName); sm.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED, ifaces); } } Loading Loading @@ -1490,7 +1485,7 @@ public class Tethering extends BaseNetworkObserver { // For example, after CONNECTIVITY_ACTION listening is removed, here // is where we could observe a Wi-Fi network becoming available and // passing validation. if (mCurrentUpstreamIface == null) { if (mCurrentUpstreamIfaceSet == null) { // If we have no upstream interface, try to run through upstream // selection again. If, for example, IPv4 connectivity has shown up // after IPv6 (e.g., 464xlat became available) we want the chance to Loading @@ -1514,8 +1509,7 @@ public class Tethering extends BaseNetworkObserver { handleNewUpstreamNetworkState(ns); break; case UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES: setDnsForwarders(ns.network, ns.linkProperties); handleNewUpstreamNetworkState(ns); chooseUpstreamType(false); break; case UpstreamNetworkMonitor.EVENT_ON_LOST: // TODO: Re-evaluate possible upstreams. Currently upstream Loading Loading @@ -1588,7 +1582,7 @@ public class Tethering extends BaseNetworkObserver { if (VDBG) Log.d(TAG, "Tether Mode requested by " + who); handleInterfaceServingStateActive(message.arg1, who); who.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED, mCurrentUpstreamIface); mCurrentUpstreamIfaceSet); // If there has been a change and an upstream is now // desired, kick off the selection process. final boolean previousUpstreamWanted = updateUpstreamWanted(); Loading Loading @@ -1866,7 +1860,7 @@ public class Tethering extends BaseNetworkObserver { pw.println(" - lastError = " + tetherState.lastError); } pw.println("Upstream wanted: " + upstreamWanted()); pw.println("Current upstream interface: " + mCurrentUpstreamIface); pw.println("Current upstream interface(s): " + mCurrentUpstreamIfaceSet); pw.decreaseIndent(); } Loading
services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java +1 −67 Original line number Diff line number Diff line Loading @@ -30,10 +30,8 @@ import android.util.Log; import java.net.Inet6Address; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedList; import java.util.Random; Loading Loading @@ -119,7 +117,7 @@ public class IPv6TetheringCoordinator { if (VDBG) { Log.d(TAG, "updateUpstreamNetworkState: " + toDebugString(ns)); } if (!canTetherIPv6(ns, mLog)) { if (TetheringInterfaceUtils.getIPv6Interface(ns) == null) { stopIPv6TetheringOnAllInterfaces(); setUpstreamNetworkState(null); return; Loading Loading @@ -208,70 +206,6 @@ public class IPv6TetheringCoordinator { return null; } private static boolean canTetherIPv6(NetworkState ns, SharedLog sharedLog) { // Broadly speaking: // // [1] does the upstream have an IPv6 default route? // // and // // [2] does the upstream have one or more global IPv6 /64s // dedicated to this device? // // In lieu of Prefix Delegation and other evaluation of whether a // prefix may or may not be dedicated to this device, for now just // check whether the upstream is TRANSPORT_CELLULAR. This works // because "[t]he 3GPP network allocates each default bearer a unique // /64 prefix", per RFC 6459, Section 5.2. final boolean canTether = (ns != null) && (ns.network != null) && (ns.linkProperties != null) && (ns.networkCapabilities != null) && // At least one upstream DNS server: ns.linkProperties.isProvisioned() && // Minimal amount of IPv6 provisioning: ns.linkProperties.hasIPv6DefaultRoute() && ns.linkProperties.hasGlobalIPv6Address() && // Temporary approximation of "dedicated prefix": ns.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR); // For now, we do not support separate IPv4 and IPv6 upstreams (e.g. // tethering with 464xlat involved). TODO: Rectify this shortcoming, // likely by calling NetworkManagementService#startInterfaceForwarding() // for all upstream interfaces. RouteInfo v4default = null; RouteInfo v6default = null; if (canTether) { for (RouteInfo r : ns.linkProperties.getAllRoutes()) { if (r.isIPv4Default()) { v4default = r; } else if (r.isIPv6Default()) { v6default = r; } if (v4default != null && v6default != null) { break; } } } final boolean supportedConfiguration = (v4default != null) && (v6default != null) && (v4default.getInterface() != null) && v4default.getInterface().equals(v6default.getInterface()); final boolean outcome = canTether && supportedConfiguration; if (ns == null) { sharedLog.log("No available upstream."); } else { sharedLog.log(String.format("IPv6 tethering is %s for upstream: %s", (outcome ? "available" : "not available"), toDebugString(ns))); } return outcome; } private static LinkProperties getIPv6OnlyLinkProperties(LinkProperties lp) { final LinkProperties v6only = new LinkProperties(); if (lp == null) { Loading
services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java +49 −17 Original line number Diff line number Diff line Loading @@ -31,7 +31,7 @@ import android.net.ip.InterfaceController; import android.net.ip.RouterAdvertisementDaemon; import android.net.ip.RouterAdvertisementDaemon.RaParams; import android.net.util.InterfaceParams; import android.net.util.NetdService; import android.net.util.InterfaceSet; import android.net.util.SharedLog; import android.os.INetworkManagementService; import android.os.Looper; Loading @@ -49,12 +49,12 @@ import com.android.internal.util.StateMachine; import java.net.Inet6Address; import java.net.InetAddress; import java.net.SocketException; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.HashSet; import java.util.Objects; import java.util.Random; import java.util.Set; /** * Provides the interface to IP-layer serving functionality for a given network Loading Loading @@ -121,7 +121,7 @@ public class TetherInterfaceStateMachine extends StateMachine { private int mLastError; private int mServingMode; private String mMyUpstreamIfaceName; // may change over time private InterfaceSet mUpstreamIfaceSet; // may change over time private InterfaceParams mInterfaceParams; // TODO: De-duplicate this with mLinkProperties above. Currently, these link // properties are those selected by the IPv6TetheringCoordinator and relayed Loading Loading @@ -622,10 +622,10 @@ public class TetherInterfaceStateMachine extends StateMachine { } private void cleanupUpstream() { if (mMyUpstreamIfaceName == null) return; if (mUpstreamIfaceSet == null) return; cleanupUpstreamInterface(mMyUpstreamIfaceName); mMyUpstreamIfaceName = null; for (String ifname : mUpstreamIfaceSet.ifnames) cleanupUpstreamInterface(ifname); mUpstreamIfaceSet = null; } private void cleanupUpstreamInterface(String upstreamIface) { Loading Loading @@ -661,34 +661,66 @@ public class TetherInterfaceStateMachine extends StateMachine { mLog.e("CMD_TETHER_REQUESTED while already tethering."); break; case CMD_TETHER_CONNECTION_CHANGED: String newUpstreamIfaceName = (String)(message.obj); if ((mMyUpstreamIfaceName == null && newUpstreamIfaceName == null) || (mMyUpstreamIfaceName != null && mMyUpstreamIfaceName.equals(newUpstreamIfaceName))) { final InterfaceSet newUpstreamIfaceSet = (InterfaceSet) message.obj; if (noChangeInUpstreamIfaceSet(newUpstreamIfaceSet)) { if (VDBG) Log.d(TAG, "Connection changed noop - dropping"); break; } if (newUpstreamIfaceSet == null) { cleanupUpstream(); if (newUpstreamIfaceName != null) { break; } for (String removed : upstreamInterfacesRemoved(newUpstreamIfaceSet)) { cleanupUpstreamInterface(removed); } final Set<String> added = upstreamInterfacesAdd(newUpstreamIfaceSet); // This makes the call to cleanupUpstream() in the error // path for any interface neatly cleanup all the interfaces. mUpstreamIfaceSet = newUpstreamIfaceSet; for (String ifname : added) { try { mNMService.enableNat(mIfaceName, newUpstreamIfaceName); mNMService.startInterfaceForwarding(mIfaceName, newUpstreamIfaceName); mNMService.enableNat(mIfaceName, ifname); mNMService.startInterfaceForwarding(mIfaceName, ifname); } catch (Exception e) { mLog.e("Exception enabling NAT: " + e); cleanupUpstreamInterface(newUpstreamIfaceName); cleanupUpstream(); mLastError = ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR; transitionTo(mInitialState); return true; } } mMyUpstreamIfaceName = newUpstreamIfaceName; break; default: return false; } return true; } private boolean noChangeInUpstreamIfaceSet(InterfaceSet newIfaces) { if (mUpstreamIfaceSet == null && newIfaces == null) return true; if (mUpstreamIfaceSet != null && newIfaces != null) { return mUpstreamIfaceSet.equals(newIfaces); } return false; } private Set<String> upstreamInterfacesRemoved(InterfaceSet newIfaces) { if (mUpstreamIfaceSet == null) return new HashSet<>(); final HashSet<String> removed = new HashSet<>(mUpstreamIfaceSet.ifnames); removed.removeAll(newIfaces.ifnames); return removed; } private Set<String> upstreamInterfacesAdd(InterfaceSet newIfaces) { final HashSet<String> added = new HashSet<>(newIfaces.ifnames); if (mUpstreamIfaceSet != null) added.removeAll(mUpstreamIfaceSet.ifnames); return added; } } /** Loading
services/core/java/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java 0 → 100644 +90 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 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 com.android.server.connectivity.tethering; import android.annotation.Nullable; import android.net.LinkProperties; import android.net.NetworkCapabilities; import android.net.NetworkState; import android.net.RouteInfo; import android.net.util.InterfaceSet; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; /** * @hide */ public final class TetheringInterfaceUtils { /** * Get upstream interfaces for tethering based on default routes for IPv4/IPv6. * @return null if there is no usable interface, or a set of at least one interface otherwise. */ public static @Nullable InterfaceSet getTetheringInterfaces(NetworkState ns) { if (ns == null) { return null; } final LinkProperties lp = ns.linkProperties; final String if4 = getInterfaceForDestination(lp, Inet4Address.ANY); final String if6 = getIPv6Interface(ns); return (if4 == null && if6 == null) ? null : new InterfaceSet(if4, if6); } /** * Get the upstream interface for IPv6 tethering. * @return null if there is no usable interface, or the interface name otherwise. */ public static @Nullable String getIPv6Interface(NetworkState ns) { // Broadly speaking: // // [1] does the upstream have an IPv6 default route? // // and // // [2] does the upstream have one or more global IPv6 /64s // dedicated to this device? // // In lieu of Prefix Delegation and other evaluation of whether a // prefix may or may not be dedicated to this device, for now just // check whether the upstream is TRANSPORT_CELLULAR. This works // because "[t]he 3GPP network allocates each default bearer a unique // /64 prefix", per RFC 6459, Section 5.2. final boolean canTether = (ns != null) && (ns.network != null) && (ns.linkProperties != null) && (ns.networkCapabilities != null) && // At least one upstream DNS server: ns.linkProperties.hasIPv6DnsServer() && // Minimal amount of IPv6 provisioning: ns.linkProperties.hasGlobalIPv6Address() && // Temporary approximation of "dedicated prefix": ns.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR); return canTether ? getInterfaceForDestination(ns.linkProperties, Inet6Address.ANY) : null; } private static String getInterfaceForDestination(LinkProperties lp, InetAddress dst) { final RouteInfo ri = (lp != null) ? RouteInfo.selectBestRoute(lp.getAllRoutes(), dst) : null; return (ri != null) ? ri.getInterface() : null; } }
services/net/java/android/net/util/InterfaceSet.java 0 → 100644 +52 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 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 java.util.Collections; import java.util.HashSet; import java.util.Set; import java.util.StringJoiner; /** * @hide */ public class InterfaceSet { public final Set<String> ifnames; public InterfaceSet(String... names) { final Set<String> nameSet = new HashSet<>(); for (String name : names) { if (name != null) nameSet.add(name); } ifnames = Collections.unmodifiableSet(nameSet); } @Override public String toString() { final StringJoiner sj = new StringJoiner(",", "[", "]"); for (String ifname : ifnames) sj.add(ifname); return sj.toString(); } @Override public boolean equals(Object obj) { return obj != null && obj instanceof InterfaceSet && ifnames.equals(((InterfaceSet)obj).ifnames); } }