diff options
author | Mark Chien <markchien@google.com> | 2019-10-25 06:41:49 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2019-10-25 06:41:49 +0000 |
commit | 634c6a512f16c9f5a7e9a4b04a9ef76f50c464d2 (patch) | |
tree | cdd5469163fcdb32962b20aa4712f30f02d07326 /services/net/java | |
parent | 18bece7b322104a5cfb3542f94c03f5d743d38b8 (diff) | |
parent | 08cc03051ada5fb55f8d16b94305af5a87b7783b (diff) |
Merge "[Tether03] Migrate IpServer into module"
Diffstat (limited to 'services/net/java')
5 files changed, 0 insertions, 2007 deletions
diff --git a/services/net/java/android/net/dhcp/DhcpServerCallbacks.java b/services/net/java/android/net/dhcp/DhcpServerCallbacks.java deleted file mode 100644 index 7c41377985d3..000000000000 --- a/services/net/java/android/net/dhcp/DhcpServerCallbacks.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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.dhcp; - -/** - * Convenience wrapper around IDhcpServerCallbacks.Stub that implements getInterfaceVersion(). - * @hide - */ -public abstract class DhcpServerCallbacks extends IDhcpServerCallbacks.Stub { - /** - * Get the version of the aidl interface implemented by the callbacks. - */ - @Override - public int getInterfaceVersion() { - return IDhcpServerCallbacks.VERSION; - } -} diff --git a/services/net/java/android/net/dhcp/DhcpServingParamsParcelExt.java b/services/net/java/android/net/dhcp/DhcpServingParamsParcelExt.java deleted file mode 100644 index 1fe2328f1cdb..000000000000 --- a/services/net/java/android/net/dhcp/DhcpServingParamsParcelExt.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * 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.dhcp; - -import static android.net.shared.Inet4AddressUtils.inet4AddressToIntHTH; - -import android.annotation.NonNull; -import android.net.LinkAddress; - -import com.google.android.collect.Sets; - -import java.net.Inet4Address; -import java.util.Collection; -import java.util.Set; - -/** - * Subclass of {@link DhcpServingParamsParcel} with additional utility methods for building. - * - * <p>This utility class does not check for validity of the parameters: invalid parameters are - * reported by the receiving module when unparceling the parcel. - * - * @see DhcpServingParams - * @hide - */ -public class DhcpServingParamsParcelExt extends DhcpServingParamsParcel { - public static final int MTU_UNSET = 0; - - /** - * Set the server address and served prefix for the DHCP server. - * - * <p>This parameter is required. - */ - public DhcpServingParamsParcelExt setServerAddr(@NonNull LinkAddress serverAddr) { - this.serverAddr = inet4AddressToIntHTH((Inet4Address) serverAddr.getAddress()); - this.serverAddrPrefixLength = serverAddr.getPrefixLength(); - return this; - } - - /** - * Set the default routers to be advertised to DHCP clients. - * - * <p>Each router must be inside the served prefix. This may be an empty set, but it must - * always be set explicitly. - */ - public DhcpServingParamsParcelExt setDefaultRouters(@NonNull Set<Inet4Address> defaultRouters) { - this.defaultRouters = toIntArray(defaultRouters); - return this; - } - - /** - * Set the default routers to be advertised to DHCP clients. - * - * <p>Each router must be inside the served prefix. This may be an empty list of routers, - * but it must always be set explicitly. - */ - public DhcpServingParamsParcelExt setDefaultRouters(@NonNull Inet4Address... defaultRouters) { - return setDefaultRouters(Sets.newArraySet(defaultRouters)); - } - - /** - * Convenience method to build the parameters with no default router. - * - * <p>Equivalent to calling {@link #setDefaultRouters(Inet4Address...)} with no address. - */ - public DhcpServingParamsParcelExt setNoDefaultRouter() { - return setDefaultRouters(); - } - - /** - * Set the DNS servers to be advertised to DHCP clients. - * - * <p>This may be an empty set, but it must always be set explicitly. - */ - public DhcpServingParamsParcelExt setDnsServers(@NonNull Set<Inet4Address> dnsServers) { - this.dnsServers = toIntArray(dnsServers); - return this; - } - - /** - * Set the DNS servers to be advertised to DHCP clients. - * - * <p>This may be an empty list of servers, but it must always be set explicitly. - */ - public DhcpServingParamsParcelExt setDnsServers(@NonNull Inet4Address... dnsServers) { - return setDnsServers(Sets.newArraySet(dnsServers)); - } - - /** - * Convenience method to build the parameters with no DNS server. - * - * <p>Equivalent to calling {@link #setDnsServers(Inet4Address...)} with no address. - */ - public DhcpServingParamsParcelExt setNoDnsServer() { - return setDnsServers(); - } - - /** - * Set excluded addresses that the DHCP server is not allowed to assign to clients. - * - * <p>This parameter is optional. DNS servers and default routers are always excluded - * and do not need to be set here. - */ - public DhcpServingParamsParcelExt setExcludedAddrs(@NonNull Set<Inet4Address> excludedAddrs) { - this.excludedAddrs = toIntArray(excludedAddrs); - return this; - } - - /** - * Set excluded addresses that the DHCP server is not allowed to assign to clients. - * - * <p>This parameter is optional. DNS servers and default routers are always excluded - * and do not need to be set here. - */ - public DhcpServingParamsParcelExt setExcludedAddrs(@NonNull Inet4Address... excludedAddrs) { - return setExcludedAddrs(Sets.newArraySet(excludedAddrs)); - } - - /** - * Set the lease time for leases assigned by the DHCP server. - * - * <p>This parameter is required. - */ - public DhcpServingParamsParcelExt setDhcpLeaseTimeSecs(long dhcpLeaseTimeSecs) { - this.dhcpLeaseTimeSecs = dhcpLeaseTimeSecs; - return this; - } - - /** - * Set the link MTU to be advertised to DHCP clients. - * - * <p>If set to {@link #MTU_UNSET}, no MTU will be advertised to clients. This parameter - * is optional and defaults to {@link #MTU_UNSET}. - */ - public DhcpServingParamsParcelExt setLinkMtu(int linkMtu) { - this.linkMtu = linkMtu; - return this; - } - - /** - * Set whether the DHCP server should send the ANDROID_METERED vendor-specific option. - * - * <p>If not set, the default value is false. - */ - public DhcpServingParamsParcelExt setMetered(boolean metered) { - this.metered = metered; - return this; - } - - private static int[] toIntArray(@NonNull Collection<Inet4Address> addrs) { - int[] res = new int[addrs.size()]; - int i = 0; - for (Inet4Address addr : addrs) { - res[i] = inet4AddressToIntHTH(addr); - i++; - } - return res; - } -} diff --git a/services/net/java/android/net/ip/IpServer.java b/services/net/java/android/net/ip/IpServer.java deleted file mode 100644 index 3d79bba7bbe3..000000000000 --- a/services/net/java/android/net/ip/IpServer.java +++ /dev/null @@ -1,1008 +0,0 @@ -/* - * Copyright (C) 2016 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.ip; - -import static android.net.NetworkUtils.numericToInetAddress; -import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS; -import static android.net.util.NetworkConstants.FF; -import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH; -import static android.net.util.NetworkConstants.asByte; - -import android.net.ConnectivityManager; -import android.net.INetd; -import android.net.INetworkStackStatusCallback; -import android.net.INetworkStatsService; -import android.net.InterfaceConfiguration; -import android.net.IpPrefix; -import android.net.LinkAddress; -import android.net.LinkProperties; -import android.net.NetworkStackClient; -import android.net.RouteInfo; -import android.net.dhcp.DhcpServerCallbacks; -import android.net.dhcp.DhcpServingParamsParcel; -import android.net.dhcp.DhcpServingParamsParcelExt; -import android.net.dhcp.IDhcpServer; -import android.net.ip.RouterAdvertisementDaemon.RaParams; -import android.net.util.InterfaceParams; -import android.net.util.InterfaceSet; -import android.net.util.NetdService; -import android.net.util.SharedLog; -import android.os.INetworkManagementService; -import android.os.Looper; -import android.os.Message; -import android.os.RemoteException; -import android.os.ServiceSpecificException; -import android.util.Log; -import android.util.Slog; -import android.util.SparseArray; - -import com.android.internal.util.MessageUtils; -import com.android.internal.util.Protocol; -import com.android.internal.util.State; -import com.android.internal.util.StateMachine; - -import java.net.Inet4Address; -import java.net.Inet6Address; -import java.net.InetAddress; -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 - * interface, e.g. for tethering or "local-only hotspot" mode. - * - * @hide - */ -public class IpServer extends StateMachine { - public static final int STATE_UNAVAILABLE = 0; - public static final int STATE_AVAILABLE = 1; - public static final int STATE_TETHERED = 2; - public static final int STATE_LOCAL_ONLY = 3; - - public static String getStateString(int state) { - switch (state) { - case STATE_UNAVAILABLE: return "UNAVAILABLE"; - case STATE_AVAILABLE: return "AVAILABLE"; - case STATE_TETHERED: return "TETHERED"; - case STATE_LOCAL_ONLY: return "LOCAL_ONLY"; - } - return "UNKNOWN: " + state; - } - - private static final byte DOUG_ADAMS = (byte) 42; - - private static final String USB_NEAR_IFACE_ADDR = "192.168.42.129"; - private static final int USB_PREFIX_LENGTH = 24; - private static final String WIFI_HOST_IFACE_ADDR = "192.168.43.1"; - private static final int WIFI_HOST_IFACE_PREFIX_LENGTH = 24; - private static final String WIFI_P2P_IFACE_ADDR = "192.168.49.1"; - private static final int WIFI_P2P_IFACE_PREFIX_LENGTH = 24; - - // TODO: have PanService use some visible version of this constant - private static final String BLUETOOTH_IFACE_ADDR = "192.168.44.1"; - private static final int BLUETOOTH_DHCP_PREFIX_LENGTH = 24; - - // TODO: have this configurable - private static final int DHCP_LEASE_TIME_SECS = 3600; - - private final static String TAG = "IpServer"; - private final static boolean DBG = false; - private final static boolean VDBG = false; - private static final Class[] messageClasses = { - IpServer.class - }; - private static final SparseArray<String> sMagicDecoderRing = - MessageUtils.findMessageNames(messageClasses); - - public static class Callback { - /** - * Notify that |who| has changed its tethering state. - * - * @param who the calling instance of IpServer - * @param state one of STATE_* - * @param lastError one of ConnectivityManager.TETHER_ERROR_* - */ - public void updateInterfaceState(IpServer who, int state, int lastError) {} - - /** - * Notify that |who| has new LinkProperties. - * - * @param who the calling instance of IpServer - * @param newLp the new LinkProperties to report - */ - public void updateLinkProperties(IpServer who, LinkProperties newLp) {} - } - - public static class Dependencies { - public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) { - return new RouterAdvertisementDaemon(ifParams); - } - - public InterfaceParams getInterfaceParams(String ifName) { - return InterfaceParams.getByName(ifName); - } - - public INetd getNetdService() { - return NetdService.getInstance(); - } - - /** - * Create a DhcpServer instance to be used by IpServer. - */ - public void makeDhcpServer(String ifName, DhcpServingParamsParcel params, - DhcpServerCallbacks cb) { - NetworkStackClient.getInstance().makeDhcpServer(ifName, params, cb); - } - } - - private static final int BASE_IFACE = Protocol.BASE_TETHERING + 100; - // request from the user that it wants to tether - public static final int CMD_TETHER_REQUESTED = BASE_IFACE + 2; - // request from the user that it wants to untether - public static final int CMD_TETHER_UNREQUESTED = BASE_IFACE + 3; - // notification that this interface is down - public static final int CMD_INTERFACE_DOWN = BASE_IFACE + 4; - // notification from the master SM that it had trouble enabling IP Forwarding - public static final int CMD_IP_FORWARDING_ENABLE_ERROR = BASE_IFACE + 7; - // notification from the master SM that it had trouble disabling IP Forwarding - public static final int CMD_IP_FORWARDING_DISABLE_ERROR = BASE_IFACE + 8; - // notification from the master SM that it had trouble starting tethering - public static final int CMD_START_TETHERING_ERROR = BASE_IFACE + 9; - // notification from the master SM that it had trouble stopping tethering - public static final int CMD_STOP_TETHERING_ERROR = BASE_IFACE + 10; - // notification from the master SM that it had trouble setting the DNS forwarders - public static final int CMD_SET_DNS_FORWARDERS_ERROR = BASE_IFACE + 11; - // the upstream connection has changed - public static final int CMD_TETHER_CONNECTION_CHANGED = BASE_IFACE + 12; - // new IPv6 tethering parameters need to be processed - public static final int CMD_IPV6_TETHER_UPDATE = BASE_IFACE + 13; - - private final State mInitialState; - private final State mLocalHotspotState; - private final State mTetheredState; - private final State mUnavailableState; - - private final SharedLog mLog; - private final INetworkManagementService mNMService; - private final INetd mNetd; - private final INetworkStatsService mStatsService; - private final Callback mCallback; - private final InterfaceController mInterfaceCtrl; - - private final String mIfaceName; - private final int mInterfaceType; - private final LinkProperties mLinkProperties; - private final boolean mUsingLegacyDhcp; - - private final Dependencies mDeps; - - private int mLastError; - private int mServingMode; - 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 - // to us. By comparison, mLinkProperties contains the addresses and directly - // connected routes that have been formed from these properties iff. we have - // succeeded in configuring them and are able to announce them within Router - // Advertisements (otherwise, we do not add them to mLinkProperties at all). - private LinkProperties mLastIPv6LinkProperties; - private RouterAdvertisementDaemon mRaDaemon; - - // To be accessed only on the handler thread - private int mDhcpServerStartIndex = 0; - private IDhcpServer mDhcpServer; - private RaParams mLastRaParams; - - public IpServer( - String ifaceName, Looper looper, int interfaceType, SharedLog log, - INetworkManagementService nMService, INetworkStatsService statsService, - Callback callback, boolean usingLegacyDhcp, Dependencies deps) { - super(ifaceName, looper); - mLog = log.forSubComponent(ifaceName); - mNMService = nMService; - mNetd = deps.getNetdService(); - mStatsService = statsService; - mCallback = callback; - mInterfaceCtrl = new InterfaceController(ifaceName, mNetd, mLog); - mIfaceName = ifaceName; - mInterfaceType = interfaceType; - mLinkProperties = new LinkProperties(); - mUsingLegacyDhcp = usingLegacyDhcp; - mDeps = deps; - resetLinkProperties(); - mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR; - mServingMode = STATE_AVAILABLE; - - mInitialState = new InitialState(); - mLocalHotspotState = new LocalHotspotState(); - mTetheredState = new TetheredState(); - mUnavailableState = new UnavailableState(); - addState(mInitialState); - addState(mLocalHotspotState); - addState(mTetheredState); - addState(mUnavailableState); - - setInitialState(mInitialState); - } - - public String interfaceName() { return mIfaceName; } - - public int interfaceType() { return mInterfaceType; } - - public int lastError() { return mLastError; } - - public int servingMode() { return mServingMode; } - - public LinkProperties linkProperties() { return new LinkProperties(mLinkProperties); } - - public void stop() { sendMessage(CMD_INTERFACE_DOWN); } - - public void unwanted() { sendMessage(CMD_TETHER_UNREQUESTED); } - - /** - * Internals. - */ - - private boolean startIPv4() { return configureIPv4(true); } - - /** - * Convenience wrapper around INetworkStackStatusCallback to run callbacks on the IpServer - * handler. - * - * <p>Different instances of this class can be created for each call to IDhcpServer methods, - * with different implementations of the callback, to differentiate handling of success/error in - * each call. - */ - private abstract class OnHandlerStatusCallback extends INetworkStackStatusCallback.Stub { - @Override - public void onStatusAvailable(int statusCode) { - getHandler().post(() -> callback(statusCode)); - } - - public abstract void callback(int statusCode); - - @Override - public int getInterfaceVersion() { - return this.VERSION; - } - } - - private class DhcpServerCallbacksImpl extends DhcpServerCallbacks { - private final int mStartIndex; - - private DhcpServerCallbacksImpl(int startIndex) { - mStartIndex = startIndex; - } - - @Override - public void onDhcpServerCreated(int statusCode, IDhcpServer server) throws RemoteException { - getHandler().post(() -> { - // We are on the handler thread: mDhcpServerStartIndex can be read safely. - if (mStartIndex != mDhcpServerStartIndex) { - // This start request is obsolete. When the |server| binder token goes out of - // scope, the garbage collector will finalize it, which causes the network stack - // process garbage collector to collect the server itself. - return; - } - - if (statusCode != STATUS_SUCCESS) { - mLog.e("Error obtaining DHCP server: " + statusCode); - handleError(); - return; - } - - mDhcpServer = server; - try { - mDhcpServer.start(new OnHandlerStatusCallback() { - @Override - public void callback(int startStatusCode) { - if (startStatusCode != STATUS_SUCCESS) { - mLog.e("Error starting DHCP server: " + startStatusCode); - handleError(); - } - } - }); - } catch (RemoteException e) { - e.rethrowFromSystemServer(); - } - }); - } - - private void handleError() { - mLastError = ConnectivityManager.TETHER_ERROR_DHCPSERVER_ERROR; - transitionTo(mInitialState); - } - } - - private boolean startDhcp(Inet4Address addr, int prefixLen) { - if (mUsingLegacyDhcp) { - return true; - } - final DhcpServingParamsParcel params; - params = new DhcpServingParamsParcelExt() - .setDefaultRouters(addr) - .setDhcpLeaseTimeSecs(DHCP_LEASE_TIME_SECS) - .setDnsServers(addr) - .setServerAddr(new LinkAddress(addr, prefixLen)) - .setMetered(true); - // TODO: also advertise link MTU - - mDhcpServerStartIndex++; - mDeps.makeDhcpServer( - mIfaceName, params, new DhcpServerCallbacksImpl(mDhcpServerStartIndex)); - return true; - } - - private void stopDhcp() { - // Make all previous start requests obsolete so servers are not started later - mDhcpServerStartIndex++; - - if (mDhcpServer != null) { - try { - mDhcpServer.stop(new OnHandlerStatusCallback() { - @Override - public void callback(int statusCode) { - if (statusCode != STATUS_SUCCESS) { - mLog.e("Error stopping DHCP server: " + statusCode); - mLastError = ConnectivityManager.TETHER_ERROR_DHCPSERVER_ERROR; - // Not much more we can do here - } - } - }); - mDhcpServer = null; - } catch (RemoteException e) { - e.rethrowFromSystemServer(); - } - } - } - - private boolean configureDhcp(boolean enable, Inet4Address addr, int prefixLen) { - if (enable) { - return startDhcp(addr, prefixLen); - } else { - stopDhcp(); - return true; - } - } - - private void stopIPv4() { - configureIPv4(false); - // NOTE: All of configureIPv4() will be refactored out of existence - // into calls to InterfaceController, shared with startIPv4(). - mInterfaceCtrl.clearIPv4Address(); - } - - // TODO: Refactor this in terms of calls to InterfaceController. - private boolean configureIPv4(boolean enabled) { - if (VDBG) Log.d(TAG, "configureIPv4(" + enabled + ")"); - - // TODO: Replace this hard-coded information with dynamically selected - // config passed down to us by a higher layer IP-coordinating element. - String ipAsString = null; - int prefixLen = 0; - if (mInterfaceType == ConnectivityManager.TETHERING_USB) { - ipAsString = USB_NEAR_IFACE_ADDR; - prefixLen = USB_PREFIX_LENGTH; - } else if (mInterfaceType == ConnectivityManager.TETHERING_WIFI) { - ipAsString = getRandomWifiIPv4Address(); - prefixLen = WIFI_HOST_IFACE_PREFIX_LENGTH; - } else if (mInterfaceType == ConnectivityManager.TETHERING_WIFI_P2P) { - ipAsString = WIFI_P2P_IFACE_ADDR; - prefixLen = WIFI_P2P_IFACE_PREFIX_LENGTH; - } else { - // BT configures the interface elsewhere: only start DHCP. - final Inet4Address srvAddr = (Inet4Address) numericToInetAddress(BLUETOOTH_IFACE_ADDR); - return configureDhcp(enabled, srvAddr, BLUETOOTH_DHCP_PREFIX_LENGTH); - } - - final LinkAddress linkAddr; - try { - final InterfaceConfiguration ifcg = mNMService.getInterfaceConfig(mIfaceName); - if (ifcg == null) { - mLog.e("Received null interface config"); - return false; - } - - InetAddress addr = numericToInetAddress(ipAsString); - linkAddr = new LinkAddress(addr, prefixLen); - ifcg.setLinkAddress(linkAddr); - if (mInterfaceType == ConnectivityManager.TETHERING_WIFI) { - // The WiFi stack has ownership of the interface up/down state. - // It is unclear whether the Bluetooth or USB stacks will manage their own - // state. - ifcg.ignoreInterfaceUpDownStatus(); - } else { - if (enabled) { - ifcg.setInterfaceUp(); - } else { - ifcg.setInterfaceDown(); - } - } - ifcg.clearFlag("running"); - - // TODO: this may throw if the interface is already gone. Do proper handling and - // simplify the DHCP server start/stop. - mNMService.setInterfaceConfig(mIfaceName, ifcg); - - if (!configureDhcp(enabled, (Inet4Address) addr, prefixLen)) { - return false; - } - } catch (Exception e) { - mLog.e("Error configuring interface " + e); - if (!enabled) { - try { - // Calling stopDhcp several times is fine - stopDhcp(); - } catch (Exception dhcpError) { - mLog.e("Error stopping DHCP", dhcpError); - } - } - return false; - } - - // Directly-connected route. - final RouteInfo route = new RouteInfo(linkAddr); - if (enabled) { - mLinkProperties.addLinkAddress(linkAddr); - mLinkProperties.addRoute(route); - } else { - mLinkProperties.removeLinkAddress(linkAddr); - mLinkProperties.removeRoute(route); - } - return true; - } - - private String getRandomWifiIPv4Address() { - try { - byte[] bytes = numericToInetAddress(WIFI_HOST_IFACE_ADDR).getAddress(); - bytes[3] = getRandomSanitizedByte(DOUG_ADAMS, asByte(0), asByte(1), FF); - return InetAddress.getByAddress(bytes).getHostAddress(); - } catch (Exception e) { - return WIFI_HOST_IFACE_ADDR; - } - } - - private boolean startIPv6() { - mInterfaceParams = mDeps.getInterfaceParams(mIfaceName); - if (mInterfaceParams == null) { - mLog.e("Failed to find InterfaceParams"); - stopIPv6(); - return false; - } - - mRaDaemon = mDeps.getRouterAdvertisementDaemon(mInterfaceParams); - if (!mRaDaemon.start()) { - stopIPv6(); - return false; - } - - return true; - } - - private void stopIPv6() { - mInterfaceParams = null; - setRaParams(null); - - if (mRaDaemon != null) { - mRaDaemon.stop(); - mRaDaemon = null; - } - } - - // IPv6TetheringCoordinator sends updates with carefully curated IPv6-only - // LinkProperties. These have extraneous data filtered out and only the - // necessary prefixes included (per its prefix distribution policy). - // - // TODO: Evaluate using a data structure than is more directly suited to - // communicating only the relevant information. - private void updateUpstreamIPv6LinkProperties(LinkProperties v6only) { - if (mRaDaemon == null) return; - - // Avoid unnecessary work on spurious updates. - if (Objects.equals(mLastIPv6LinkProperties, v6only)) { - return; - } - - RaParams params = null; - - if (v6only != null) { - params = new RaParams(); - params.mtu = v6only.getMtu(); - params.hasDefaultRoute = v6only.hasIpv6DefaultRoute(); - - if (params.hasDefaultRoute) params.hopLimit = getHopLimit(v6only.getInterfaceName()); - - for (LinkAddress linkAddr : v6only.getLinkAddresses()) { - if (linkAddr.getPrefixLength() != RFC7421_PREFIX_LENGTH) continue; - - final IpPrefix prefix = new IpPrefix( - linkAddr.getAddress(), linkAddr.getPrefixLength()); - params.prefixes.add(prefix); - - final Inet6Address dnsServer = getLocalDnsIpFor(prefix); - if (dnsServer != null) { - params.dnses.add(dnsServer); - } - } - } - // If v6only is null, we pass in null to setRaParams(), which handles - // deprecation of any existing RA data. - - setRaParams(params); - mLastIPv6LinkProperties = v6only; - } - - private void configureLocalIPv6Routes( - HashSet<IpPrefix> deprecatedPrefixes, HashSet<IpPrefix> newPrefixes) { - // [1] Remove the routes that are deprecated. - if (!deprecatedPrefixes.isEmpty()) { - final ArrayList<RouteInfo> toBeRemoved = - getLocalRoutesFor(mIfaceName, deprecatedPrefixes); - try { - final int removalFailures = mNMService.removeRoutesFromLocalNetwork(toBeRemoved); - if (removalFailures > 0) { - mLog.e(String.format("Failed to remove %d IPv6 routes from local table.", - removalFailures)); - } - } catch (RemoteException e) { - mLog.e("Failed to remove IPv6 routes from local table: " + e); - } - - for (RouteInfo route : toBeRemoved) mLinkProperties.removeRoute(route); - } - - // [2] Add only the routes that have not previously been added. - if (newPrefixes != null && !newPrefixes.isEmpty()) { - HashSet<IpPrefix> addedPrefixes = (HashSet) newPrefixes.clone(); - if (mLastRaParams != null) { - addedPrefixes.removeAll(mLastRaParams.prefixes); - } - - if (!addedPrefixes.isEmpty()) { - final ArrayList<RouteInfo> toBeAdded = - getLocalRoutesFor(mIfaceName, addedPrefixes); - try { - // It's safe to call addInterfaceToLocalNetwork() even if - // the interface is already in the local_network. Note also - // that adding routes that already exist does not cause an - // error (EEXIST is silently ignored). - mNMService.addInterfaceToLocalNetwork(mIfaceName, toBeAdded); - } catch (Exception e) { - mLog.e("Failed to add IPv6 routes to local table: " + e); - } - - for (RouteInfo route : toBeAdded) mLinkProperties.addRoute(route); - } - } - } - - private void configureLocalIPv6Dns( - HashSet<Inet6Address> deprecatedDnses, HashSet<Inet6Address> newDnses) { - // TODO: Is this really necessary? Can we not fail earlier if INetd cannot be located? - if (mNetd == null) { - if (newDnses != null) newDnses.clear(); - mLog.e("No netd service instance available; not setting local IPv6 addresses"); - return; - } - - // [1] Remove deprecated local DNS IP addresses. - if (!deprecatedDnses.isEmpty()) { - for (Inet6Address dns : deprecatedDnses) { - if (!mInterfaceCtrl.removeAddress(dns, RFC7421_PREFIX_LENGTH)) { - mLog.e("Failed to remove local dns IP " + dns); - } - - mLinkProperties.removeLinkAddress(new LinkAddress(dns, RFC7421_PREFIX_LENGTH)); - } - } - - // [2] Add only the local DNS IP addresses that have not previously been added. - if (newDnses != null && !newDnses.isEmpty()) { - final HashSet<Inet6Address> addedDnses = (HashSet) newDnses.clone(); - if (mLastRaParams != null) { - addedDnses.removeAll(mLastRaParams.dnses); - } - - for (Inet6Address dns : addedDnses) { - if (!mInterfaceCtrl.addAddress(dns, RFC7421_PREFIX_LENGTH)) { - mLog.e("Failed to add local dns IP " + dns); - newDnses.remove(dns); - } - - mLinkProperties.addLinkAddress(new LinkAddress(dns, RFC7421_PREFIX_LENGTH)); - } - } - - try { - mNetd.tetherApplyDnsInterfaces(); - } catch (ServiceSpecificException | RemoteException e) { - mLog.e("Failed to update local DNS caching server"); - if (newDnses != null) newDnses.clear(); - } - } - - private byte getHopLimit(String upstreamIface) { - try { - int upstreamHopLimit = Integer.parseUnsignedInt( - mNetd.getProcSysNet(INetd.IPV6, INetd.CONF, upstreamIface, "hop_limit")); - // Add one hop to account for this forwarding device - upstreamHopLimit++; - // Cap the hop limit to 255. - return (byte) Integer.min(upstreamHopLimit, 255); - } catch (Exception e) { - mLog.e("Failed to find upstream interface hop limit", e); - } - return RaParams.DEFAULT_HOPLIMIT; - } - - private void setRaParams(RaParams newParams) { - if (mRaDaemon != null) { - final RaParams deprecatedParams = - RaParams.getDeprecatedRaParams(mLastRaParams, newParams); - - configureLocalIPv6Routes(deprecatedParams.prefixes, - (newParams != null) ? newParams.prefixes : null); - - configureLocalIPv6Dns(deprecatedParams.dnses, - (newParams != null) ? newParams.dnses : null); - - mRaDaemon.buildNewRa(deprecatedParams, newParams); - } - - mLastRaParams = newParams; - } - - private void logMessage(State state, int what) { - mLog.log(state.getName() + " got " + sMagicDecoderRing.get(what, Integer.toString(what))); - } - - private void sendInterfaceState(int newInterfaceState) { - mServingMode = newInterfaceState; - mCallback.updateInterfaceState(this, newInterfaceState, mLastError); - sendLinkProperties(); - } - - private void sendLinkProperties() { - mCallback.updateLinkProperties(this, new LinkProperties(mLinkProperties)); - } - - private void resetLinkProperties() { - mLinkProperties.clear(); - mLinkProperties.setInterfaceName(mIfaceName); - } - - class InitialState extends State { - @Override - public void enter() { - sendInterfaceState(STATE_AVAILABLE); - } - - @Override - public boolean processMessage(Message message) { - logMessage(this, message.what); - switch (message.what) { - case CMD_TETHER_REQUESTED: - mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR; - switch (message.arg1) { - case STATE_LOCAL_ONLY: - transitionTo(mLocalHotspotState); - break; - case STATE_TETHERED: - transitionTo(mTetheredState); - break; - default: - mLog.e("Invalid tethering interface serving state specified."); - } - break; - case CMD_INTERFACE_DOWN: - transitionTo(mUnavailableState); - break; - case CMD_IPV6_TETHER_UPDATE: - updateUpstreamIPv6LinkProperties((LinkProperties) message.obj); - break; - default: - return NOT_HANDLED; - } - return HANDLED; - } - } - - class BaseServingState extends State { - @Override - public void enter() { - if (!startIPv4()) { - mLastError = ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR; - return; - } - - try { - mNMService.tetherInterface(mIfaceName); - } catch (Exception e) { - mLog.e("Error Tethering: " + e); - mLastError = ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR; - return; - } - - if (!startIPv6()) { - mLog.e("Failed to startIPv6"); - // TODO: Make this a fatal error once Bluetooth IPv6 is sorted. - return; - } - } - - @Override - public void exit() { - // Note that at this point, we're leaving the tethered state. We can fail any - // of these operations, but it doesn't really change that we have to try them - // all in sequence. - stopIPv6(); - - try { - mNMService.untetherInterface(mIfaceName); - } catch (Exception e) { - mLastError = ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR; - mLog.e("Failed to untether interface: " + e); - } - - stopIPv4(); - - resetLinkProperties(); - } - - @Override - public boolean processMessage(Message message) { - logMessage(this, message.what); - switch (message.what) { - case CMD_TETHER_UNREQUESTED: - transitionTo(mInitialState); - if (DBG) Log.d(TAG, "Untethered (unrequested)" + mIfaceName); - break; - case CMD_INTERFACE_DOWN: - transitionTo(mUnavailableState); - if (DBG) Log.d(TAG, "Untethered (ifdown)" + mIfaceName); - break; - case CMD_IPV6_TETHER_UPDATE: - updateUpstreamIPv6LinkProperties((LinkProperties) message.obj); - sendLinkProperties(); - break; - case CMD_IP_FORWARDING_ENABLE_ERROR: - case CMD_IP_FORWARDING_DISABLE_ERROR: - case CMD_START_TETHERING_ERROR: - case CMD_STOP_TETHERING_ERROR: - case CMD_SET_DNS_FORWARDERS_ERROR: - mLastError = ConnectivityManager.TETHER_ERROR_MASTER_ERROR; - transitionTo(mInitialState); - break; - default: - return false; - } - return true; - } - } - - // Handling errors in BaseServingState.enter() by transitioning is - // problematic because transitioning during a multi-state jump yields - // a Log.wtf(). Ultimately, there should be only one ServingState, - // and forwarding and NAT rules should be handled by a coordinating - // functional element outside of IpServer. - class LocalHotspotState extends BaseServingState { - @Override - public void enter() { - super.enter(); - if (mLastError != ConnectivityManager.TETHER_ERROR_NO_ERROR) { - transitionTo(mInitialState); - } - - if (DBG) Log.d(TAG, "Local hotspot " + mIfaceName); - sendInterfaceState(STATE_LOCAL_ONLY); - } - - @Override - public boolean processMessage(Message message) { - if (super.processMessage(message)) return true; - - logMessage(this, message.what); - switch (message.what) { - case CMD_TETHER_REQUESTED: - mLog.e("CMD_TETHER_REQUESTED while in local-only hotspot mode."); - break; - case CMD_TETHER_CONNECTION_CHANGED: - // Ignored in local hotspot state. - break; - default: - return false; - } - return true; - } - } - - // Handling errors in BaseServingState.enter() by transitioning is - // problematic because transitioning during a multi-state jump yields - // a Log.wtf(). Ultimately, there should be only one ServingState, - // and forwarding and NAT rules should be handled by a coordinating - // functional element outside of IpServer. - class TetheredState extends BaseServingState { - @Override - public void enter() { - super.enter(); - if (mLastError != ConnectivityManager.TETHER_ERROR_NO_ERROR) { - transitionTo(mInitialState); - } - - if (DBG) Log.d(TAG, "Tethered " + mIfaceName); - sendInterfaceState(STATE_TETHERED); - } - - @Override - public void exit() { - cleanupUpstream(); - super.exit(); - } - - private void cleanupUpstream() { - if (mUpstreamIfaceSet == null) return; - - for (String ifname : mUpstreamIfaceSet.ifnames) cleanupUpstreamInterface(ifname); - mUpstreamIfaceSet = null; - } - - private void cleanupUpstreamInterface(String upstreamIface) { - // Note that we don't care about errors here. - // Sometimes interfaces are gone before we get - // to remove their rules, which generates errors. - // Just do the best we can. - try { - // About to tear down NAT; gather remaining statistics. - mStatsService.forceUpdate(); - } catch (Exception e) { - if (VDBG) Log.e(TAG, "Exception in forceUpdate: " + e.toString()); - } - try { - mNMService.stopInterfaceForwarding(mIfaceName, upstreamIface); - } catch (Exception e) { - if (VDBG) Log.e(TAG, "Exception in removeInterfaceForward: " + e.toString()); - } - try { - mNMService.disableNat(mIfaceName, upstreamIface); - } catch (Exception e) { - if (VDBG) Log.e(TAG, "Exception in disableNat: " + e.toString()); - } - } - - @Override - public boolean processMessage(Message message) { - if (super.processMessage(message)) return true; - - logMessage(this, message.what); - switch (message.what) { - case CMD_TETHER_REQUESTED: - mLog.e("CMD_TETHER_REQUESTED while already tethering."); - break; - case CMD_TETHER_CONNECTION_CHANGED: - final InterfaceSet newUpstreamIfaceSet = (InterfaceSet) message.obj; - if (noChangeInUpstreamIfaceSet(newUpstreamIfaceSet)) { - if (VDBG) Log.d(TAG, "Connection changed noop - dropping"); - break; - } - - if (newUpstreamIfaceSet == null) { - cleanupUpstream(); - 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, ifname); - mNMService.startInterfaceForwarding(mIfaceName, ifname); - } catch (Exception e) { - mLog.e("Exception enabling NAT: " + e); - cleanupUpstream(); - mLastError = ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR; - transitionTo(mInitialState); - return true; - } - } - 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; - } - } - - /** - * This state is terminal for the per interface state machine. At this - * point, the master state machine should have removed this interface - * specific state machine from its list of possible recipients of - * tethering requests. The state machine itself will hang around until - * the garbage collector finds it. - */ - class UnavailableState extends State { - @Override - public void enter() { - mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR; - sendInterfaceState(STATE_UNAVAILABLE); - } - } - - // Accumulate routes representing "prefixes to be assigned to the local - // interface", for subsequent modification of local_network routing. - private static ArrayList<RouteInfo> getLocalRoutesFor( - String ifname, HashSet<IpPrefix> prefixes) { - final ArrayList<RouteInfo> localRoutes = new ArrayList<RouteInfo>(); - for (IpPrefix ipp : prefixes) { - localRoutes.add(new RouteInfo(ipp, null, ifname)); - } - return localRoutes; - } - - // Given a prefix like 2001:db8::/64 return an address like 2001:db8::1. - private static Inet6Address getLocalDnsIpFor(IpPrefix localPrefix) { - final byte[] dnsBytes = localPrefix.getRawAddress(); - dnsBytes[dnsBytes.length - 1] = getRandomSanitizedByte(DOUG_ADAMS, asByte(0), asByte(1)); - try { - return Inet6Address.getByAddress(null, dnsBytes, 0); - } catch (UnknownHostException e) { - Slog.wtf(TAG, "Failed to construct Inet6Address from: " + localPrefix); - return null; - } - } - - private static byte getRandomSanitizedByte(byte dflt, byte... excluded) { - final byte random = (byte) (new Random()).nextInt(); - for (int value : excluded) { - if (random == value) return dflt; - } - return random; - } -} diff --git a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java b/services/net/java/android/net/ip/RouterAdvertisementDaemon.java deleted file mode 100644 index 59aea21f46c8..000000000000 --- a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java +++ /dev/null @@ -1,744 +0,0 @@ -/* - * Copyright (C) 2016 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.ip; - -import static android.net.util.NetworkConstants.IPV6_MIN_MTU; -import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH; -import static android.system.OsConstants.AF_INET6; -import static android.system.OsConstants.IPPROTO_ICMPV6; -import static android.system.OsConstants.SOCK_RAW; -import static android.system.OsConstants.SOL_SOCKET; -import static android.system.OsConstants.SO_BINDTODEVICE; -import static android.system.OsConstants.SO_SNDTIMEO; - -import android.net.IpPrefix; -import android.net.LinkAddress; -import android.net.NetworkUtils; -import android.net.TrafficStats; -import android.net.util.InterfaceParams; -import android.system.ErrnoException; -import android.system.Os; -import android.system.StructTimeval; -import android.util.Log; - -import com.android.internal.annotations.GuardedBy; -import com.android.internal.util.TrafficStatsConstants; - -import libcore.io.IoBridge; - -import java.io.FileDescriptor; -import java.io.IOException; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.SocketException; -import java.net.UnknownHostException; -import java.nio.BufferOverflowException; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Map; -import java.util.Random; -import java.util.Set; -import java.util.concurrent.atomic.AtomicInteger; - - -/** - * Basic IPv6 Router Advertisement Daemon. - * - * TODO: - * - * - Rewrite using Handler (and friends) so that AlarmManager can deliver - * "kick" messages when it's time to send a multicast RA. - * - * @hide - */ -public class RouterAdvertisementDaemon { - private static final String TAG = RouterAdvertisementDaemon.class.getSimpleName(); - private static final byte ICMPV6_ND_ROUTER_SOLICIT = asByte(133); - private static final byte ICMPV6_ND_ROUTER_ADVERT = asByte(134); - private static final int MIN_RA_HEADER_SIZE = 16; - - // Summary of various timers and lifetimes. - private static final int MIN_RTR_ADV_INTERVAL_SEC = 300; - private static final int MAX_RTR_ADV_INTERVAL_SEC = 600; - // In general, router, prefix, and DNS lifetimes are all advised to be - // greater than or equal to 3 * MAX_RTR_ADV_INTERVAL. Here, we double - // that to allow for multicast packet loss. - // - // This MAX_RTR_ADV_INTERVAL_SEC and DEFAULT_LIFETIME are also consistent - // with the https://tools.ietf.org/html/rfc7772#section-4 discussion of - // "approximately 7 RAs per hour". - private static final int DEFAULT_LIFETIME = 6 * MAX_RTR_ADV_INTERVAL_SEC; - // From https://tools.ietf.org/html/rfc4861#section-10 . - private static final int MIN_DELAY_BETWEEN_RAS_SEC = 3; - // Both initial and final RAs, but also for changes in RA contents. - // From https://tools.ietf.org/html/rfc4861#section-10 . - private static final int MAX_URGENT_RTR_ADVERTISEMENTS = 5; - - private static final int DAY_IN_SECONDS = 86_400; - - private static final byte[] ALL_NODES = new byte[] { - (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 - }; - - private final InterfaceParams mInterface; - private final InetSocketAddress mAllNodes; - - // This lock is to protect the RA from being updated while being - // transmitted on another thread (multicast or unicast). - // - // TODO: This should be handled with a more RCU-like approach. - private final Object mLock = new Object(); - @GuardedBy("mLock") - private final byte[] mRA = new byte[IPV6_MIN_MTU]; - @GuardedBy("mLock") - private int mRaLength; - @GuardedBy("mLock") - private final DeprecatedInfoTracker mDeprecatedInfoTracker; - @GuardedBy("mLock") - private RaParams mRaParams; - - private volatile FileDescriptor mSocket; - private volatile MulticastTransmitter mMulticastTransmitter; - private volatile UnicastResponder mUnicastResponder; - - public static class RaParams { - // Tethered traffic will have the hop limit properly decremented. - // Consequently, set the hoplimit greater by one than the upstream - // unicast hop limit. - // - // TODO: Dynamically pass down the IPV6_UNICAST_HOPS value from the - // upstream interface for more correct behaviour. - static final byte DEFAULT_HOPLIMIT = 65; - - public boolean hasDefaultRoute; - public byte hopLimit; - public int mtu; - public HashSet<IpPrefix> prefixes; - public HashSet<Inet6Address> dnses; - - public RaParams() { - hasDefaultRoute = false; - hopLimit = DEFAULT_HOPLIMIT; - mtu = IPV6_MIN_MTU; - prefixes = new HashSet<IpPrefix>(); - dnses = new HashSet<Inet6Address>(); - } - - public RaParams(RaParams other) { - hasDefaultRoute = other.hasDefaultRoute; - hopLimit = other.hopLimit; - mtu = other.mtu; - prefixes = (HashSet) other.prefixes.clone(); - dnses = (HashSet) other.dnses.clone(); - } - - // Returns the subset of RA parameters that become deprecated when - // moving from announcing oldRa to announcing newRa. - // - // Currently only tracks differences in |prefixes| and |dnses|. - public static RaParams getDeprecatedRaParams(RaParams oldRa, RaParams newRa) { - RaParams newlyDeprecated = new RaParams(); - - if (oldRa != null) { - for (IpPrefix ipp : oldRa.prefixes) { - if (newRa == null || !newRa.prefixes.contains(ipp)) { - newlyDeprecated.prefixes.add(ipp); - } - } - - for (Inet6Address dns : oldRa.dnses) { - if (newRa == null || !newRa.dnses.contains(dns)) { - newlyDeprecated.dnses.add(dns); - } - } - } - - return newlyDeprecated; - } - } - - private static class DeprecatedInfoTracker { - private final HashMap<IpPrefix, Integer> mPrefixes = new HashMap<>(); - private final HashMap<Inet6Address, Integer> mDnses = new HashMap<>(); - - Set<IpPrefix> getPrefixes() { return mPrefixes.keySet(); } - - void putPrefixes(Set<IpPrefix> prefixes) { - for (IpPrefix ipp : prefixes) { - mPrefixes.put(ipp, MAX_URGENT_RTR_ADVERTISEMENTS); - } - } - - void removePrefixes(Set<IpPrefix> prefixes) { - for (IpPrefix ipp : prefixes) { - mPrefixes.remove(ipp); - } - } - - Set<Inet6Address> getDnses() { return mDnses.keySet(); } - - void putDnses(Set<Inet6Address> dnses) { - for (Inet6Address dns : dnses) { - mDnses.put(dns, MAX_URGENT_RTR_ADVERTISEMENTS); - } - } - - void removeDnses(Set<Inet6Address> dnses) { - for (Inet6Address dns : dnses) { - mDnses.remove(dns); - } - } - - boolean isEmpty() { return mPrefixes.isEmpty() && mDnses.isEmpty(); } - - private boolean decrementCounters() { - boolean removed = decrementCounter(mPrefixes); - removed |= decrementCounter(mDnses); - return removed; - } - - private <T> boolean decrementCounter(HashMap<T, Integer> map) { - boolean removed = false; - - for (Iterator<Map.Entry<T, Integer>> it = map.entrySet().iterator(); - it.hasNext();) { - Map.Entry<T, Integer> kv = it.next(); - if (kv.getValue() == 0) { - it.remove(); - removed = true; - } else { - kv.setValue(kv.getValue() - 1); - } - } - - return removed; - } - } - - - public RouterAdvertisementDaemon(InterfaceParams ifParams) { - mInterface = ifParams; - mAllNodes = new InetSocketAddress(getAllNodesForScopeId(mInterface.index), 0); - mDeprecatedInfoTracker = new DeprecatedInfoTracker(); - } - - public void buildNewRa(RaParams deprecatedParams, RaParams newParams) { - synchronized (mLock) { - if (deprecatedParams != null) { - mDeprecatedInfoTracker.putPrefixes(deprecatedParams.prefixes); - mDeprecatedInfoTracker.putDnses(deprecatedParams.dnses); - } - - if (newParams != null) { - // Process information that is no longer deprecated. - mDeprecatedInfoTracker.removePrefixes(newParams.prefixes); - mDeprecatedInfoTracker.removeDnses(newParams.dnses); - } - - mRaParams = newParams; - assembleRaLocked(); - } - - maybeNotifyMulticastTransmitter(); - } - - public boolean start() { - if (!createSocket()) { - return false; - } - - mMulticastTransmitter = new MulticastTransmitter(); - mMulticastTransmitter.start(); - - mUnicastResponder = new UnicastResponder(); - mUnicastResponder.start(); - - return true; - } - - public void stop() { - closeSocket(); - // Wake up mMulticastTransmitter thread to interrupt a potential 1 day sleep before - // the thread's termination. - maybeNotifyMulticastTransmitter(); - mMulticastTransmitter = null; - mUnicastResponder = null; - } - - @GuardedBy("mLock") - private void assembleRaLocked() { - final ByteBuffer ra = ByteBuffer.wrap(mRA); - ra.order(ByteOrder.BIG_ENDIAN); - - final boolean haveRaParams = (mRaParams != null); - boolean shouldSendRA = false; - - try { - putHeader(ra, haveRaParams && mRaParams.hasDefaultRoute, - haveRaParams ? mRaParams.hopLimit : RaParams.DEFAULT_HOPLIMIT); - putSlla(ra, mInterface.macAddr.toByteArray()); - mRaLength = ra.position(); - - // https://tools.ietf.org/html/rfc5175#section-4 says: - // - // "MUST NOT be added to a Router Advertisement message - // if no flags in the option are set." - // - // putExpandedFlagsOption(ra); - - if (haveRaParams) { - putMtu(ra, mRaParams.mtu); - mRaLength = ra.position(); - - for (IpPrefix ipp : mRaParams.prefixes) { - putPio(ra, ipp, DEFAULT_LIFETIME, DEFAULT_LIFETIME); - mRaLength = ra.position(); - shouldSendRA = true; - } - - if (mRaParams.dnses.size() > 0) { - putRdnss(ra, mRaParams.dnses, DEFAULT_LIFETIME); - mRaLength = ra.position(); - shouldSendRA = true; - } - } - - for (IpPrefix ipp : mDeprecatedInfoTracker.getPrefixes()) { - putPio(ra, ipp, 0, 0); - mRaLength = ra.position(); - shouldSendRA = true; - } - - final Set<Inet6Address> deprecatedDnses = mDeprecatedInfoTracker.getDnses(); - if (!deprecatedDnses.isEmpty()) { - putRdnss(ra, deprecatedDnses, 0); - mRaLength = ra.position(); - shouldSendRA = true; - } - } catch (BufferOverflowException e) { - // The packet up to mRaLength is valid, since it has been updated - // progressively as the RA was built. Log an error, and continue - // on as best as possible. - Log.e(TAG, "Could not construct new RA: " + e); - } - - // We have nothing worth announcing; indicate as much to maybeSendRA(). - if (!shouldSendRA) { - mRaLength = 0; - } - } - - private void maybeNotifyMulticastTransmitter() { - final MulticastTransmitter m = mMulticastTransmitter; - if (m != null) { - m.hup(); - } - } - - private static Inet6Address getAllNodesForScopeId(int scopeId) { - try { - return Inet6Address.getByAddress("ff02::1", ALL_NODES, scopeId); - } catch (UnknownHostException uhe) { - Log.wtf(TAG, "Failed to construct ff02::1 InetAddress: " + uhe); - return null; - } - } - - private static byte asByte(int value) { return (byte) value; } - private static short asShort(int value) { return (short) value; } - - private static void putHeader(ByteBuffer ra, boolean hasDefaultRoute, byte hopLimit) { - /** - Router Advertisement Message Format - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Type | Code | Checksum | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Cur Hop Limit |M|O|H|Prf|P|R|R| Router Lifetime | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Reachable Time | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Retrans Timer | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Options ... - +-+-+-+-+-+-+-+-+-+-+-+- - */ - ra.put(ICMPV6_ND_ROUTER_ADVERT) - .put(asByte(0)) - .putShort(asShort(0)) - .put(hopLimit) - // RFC 4191 "high" preference, iff. advertising a default route. - .put(hasDefaultRoute ? asByte(0x08) : asByte(0)) - .putShort(hasDefaultRoute ? asShort(DEFAULT_LIFETIME) : asShort(0)) - .putInt(0) - .putInt(0); - } - - private static void putSlla(ByteBuffer ra, byte[] slla) { - /** - Source/Target Link-layer Address - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Type | Length | Link-Layer Address ... - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - if (slla == null || slla.length != 6) { - // Only IEEE 802.3 6-byte addresses are supported. - return; - } - final byte ND_OPTION_SLLA = 1; - final byte SLLA_NUM_8OCTETS = 1; - ra.put(ND_OPTION_SLLA) - .put(SLLA_NUM_8OCTETS) - .put(slla); - } - - private static void putExpandedFlagsOption(ByteBuffer ra) { - /** - Router Advertisement Expanded Flags Option - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Type | Length | Bit fields available .. - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - ... for assignment | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - - final byte ND_OPTION_EFO = 26; - final byte EFO_NUM_8OCTETS = 1; - - ra.put(ND_OPTION_EFO) - .put(EFO_NUM_8OCTETS) - .putShort(asShort(0)) - .putInt(0); - } - - private static void putMtu(ByteBuffer ra, int mtu) { - /** - MTU - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Type | Length | Reserved | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | MTU | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - final byte ND_OPTION_MTU = 5; - final byte MTU_NUM_8OCTETS = 1; - ra.put(ND_OPTION_MTU) - .put(MTU_NUM_8OCTETS) - .putShort(asShort(0)) - .putInt((mtu < IPV6_MIN_MTU) ? IPV6_MIN_MTU : mtu); - } - - private static void putPio(ByteBuffer ra, IpPrefix ipp, - int validTime, int preferredTime) { - /** - Prefix Information - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Type | Length | Prefix Length |L|A| Reserved1 | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Valid Lifetime | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Preferred Lifetime | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Reserved2 | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | | - + + - | | - + Prefix + - | | - + + - | | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - final int prefixLength = ipp.getPrefixLength(); - if (prefixLength != 64) { - return; - } - final byte ND_OPTION_PIO = 3; - final byte PIO_NUM_8OCTETS = 4; - - if (validTime < 0) validTime = 0; - if (preferredTime < 0) preferredTime = 0; - if (preferredTime > validTime) preferredTime = validTime; - - final byte[] addr = ipp.getAddress().getAddress(); - ra.put(ND_OPTION_PIO) - .put(PIO_NUM_8OCTETS) - .put(asByte(prefixLength)) - .put(asByte(0xc0)) /* L & A set */ - .putInt(validTime) - .putInt(preferredTime) - .putInt(0) - .put(addr); - } - - private static void putRio(ByteBuffer ra, IpPrefix ipp) { - /** - Route Information Option - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Type | Length | Prefix Length |Resvd|Prf|Resvd| - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Route Lifetime | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Prefix (Variable Length) | - . . - . . - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - final int prefixLength = ipp.getPrefixLength(); - if (prefixLength > 64) { - return; - } - final byte ND_OPTION_RIO = 24; - final byte RIO_NUM_8OCTETS = asByte( - (prefixLength == 0) ? 1 : (prefixLength <= 8) ? 2 : 3); - - final byte[] addr = ipp.getAddress().getAddress(); - ra.put(ND_OPTION_RIO) - .put(RIO_NUM_8OCTETS) - .put(asByte(prefixLength)) - .put(asByte(0x18)) - .putInt(DEFAULT_LIFETIME); - - // Rely upon an IpPrefix's address being properly zeroed. - if (prefixLength > 0) { - ra.put(addr, 0, (prefixLength <= 64) ? 8 : 16); - } - } - - private static void putRdnss(ByteBuffer ra, Set<Inet6Address> dnses, int lifetime) { - /** - Recursive DNS Server (RDNSS) Option - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Type | Length | Reserved | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Lifetime | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | | - : Addresses of IPv6 Recursive DNS Servers : - | | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - - final HashSet<Inet6Address> filteredDnses = new HashSet<>(); - for (Inet6Address dns : dnses) { - if ((new LinkAddress(dns, RFC7421_PREFIX_LENGTH)).isGlobalPreferred()) { - filteredDnses.add(dns); - } - } - if (filteredDnses.isEmpty()) return; - - final byte ND_OPTION_RDNSS = 25; - final byte RDNSS_NUM_8OCTETS = asByte(dnses.size() * 2 + 1); - ra.put(ND_OPTION_RDNSS) - .put(RDNSS_NUM_8OCTETS) - .putShort(asShort(0)) - .putInt(lifetime); - - for (Inet6Address dns : filteredDnses) { - // NOTE: If the full of list DNS servers doesn't fit in the packet, - // this code will cause a buffer overflow and the RA won't include - // this instance of the option at all. - // - // TODO: Consider looking at ra.remaining() to determine how many - // DNS servers will fit, and adding only those. - ra.put(dns.getAddress()); - } - } - - private boolean createSocket() { - final int SEND_TIMEOUT_MS = 300; - - final int oldTag = TrafficStats.getAndSetThreadStatsTag( - TrafficStatsConstants.TAG_SYSTEM_NEIGHBOR); - try { - mSocket = Os.socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); - // Setting SNDTIMEO is purely for defensive purposes. - Os.setsockoptTimeval( - mSocket, SOL_SOCKET, SO_SNDTIMEO, StructTimeval.fromMillis(SEND_TIMEOUT_MS)); - Os.setsockoptIfreq(mSocket, SOL_SOCKET, SO_BINDTODEVICE, mInterface.name); - NetworkUtils.protectFromVpn(mSocket); - NetworkUtils.setupRaSocket(mSocket, mInterface.index); - } catch (ErrnoException | IOException e) { - Log.e(TAG, "Failed to create RA daemon socket: " + e); - return false; - } finally { - TrafficStats.setThreadStatsTag(oldTag); - } - - return true; - } - - private void closeSocket() { - if (mSocket != null) { - try { - IoBridge.closeAndSignalBlockedThreads(mSocket); - } catch (IOException ignored) {} - } - mSocket = null; - } - - private boolean isSocketValid() { - final FileDescriptor s = mSocket; - return (s != null) && s.valid(); - } - - private boolean isSuitableDestination(InetSocketAddress dest) { - if (mAllNodes.equals(dest)) { - return true; - } - - final InetAddress destip = dest.getAddress(); - return (destip instanceof Inet6Address) && - destip.isLinkLocalAddress() && - (((Inet6Address) destip).getScopeId() == mInterface.index); - } - - private void maybeSendRA(InetSocketAddress dest) { - if (dest == null || !isSuitableDestination(dest)) { - dest = mAllNodes; - } - - try { - synchronized (mLock) { - if (mRaLength < MIN_RA_HEADER_SIZE) { - // No actual RA to send. - return; - } - Os.sendto(mSocket, mRA, 0, mRaLength, 0, dest); - } - Log.d(TAG, "RA sendto " + dest.getAddress().getHostAddress()); - } catch (ErrnoException | SocketException e) { - if (isSocketValid()) { - Log.e(TAG, "sendto error: " + e); - } - } - } - - private final class UnicastResponder extends Thread { - private final InetSocketAddress solicitor = new InetSocketAddress(); - // The recycled buffer for receiving Router Solicitations from clients. - // If the RS is larger than IPV6_MIN_MTU the packets are truncated. - // This is fine since currently only byte 0 is examined anyway. - private final byte mSolication[] = new byte[IPV6_MIN_MTU]; - - @Override - public void run() { - while (isSocketValid()) { - try { - // Blocking receive. - final int rval = Os.recvfrom( - mSocket, mSolication, 0, mSolication.length, 0, solicitor); - // Do the least possible amount of validation. - if (rval < 1 || mSolication[0] != ICMPV6_ND_ROUTER_SOLICIT) { - continue; - } - } catch (ErrnoException | SocketException e) { - if (isSocketValid()) { - Log.e(TAG, "recvfrom error: " + e); - } - continue; - } - - maybeSendRA(solicitor); - } - } - } - - // TODO: Consider moving this to run on a provided Looper as a Handler, - // with WakeupMessage-style messages providing the timer driven input. - private final class MulticastTransmitter extends Thread { - private final Random mRandom = new Random(); - private final AtomicInteger mUrgentAnnouncements = new AtomicInteger(0); - - @Override - public void run() { - while (isSocketValid()) { - try { - Thread.sleep(getNextMulticastTransmitDelayMs()); - } catch (InterruptedException ignored) { - // Stop sleeping, immediately send an RA, and continue. - } - - maybeSendRA(mAllNodes); - synchronized (mLock) { - if (mDeprecatedInfoTracker.decrementCounters()) { - // At least one deprecated PIO has been removed; - // reassemble the RA. - assembleRaLocked(); - } - } - } - } - - public void hup() { - // Set to one fewer that the desired number, because as soon as - // the thread interrupt is processed we immediately send an RA - // and mUrgentAnnouncements is not examined until the subsequent - // sleep interval computation (i.e. this way we send 3 and not 4). - mUrgentAnnouncements.set(MAX_URGENT_RTR_ADVERTISEMENTS - 1); - interrupt(); - } - - private int getNextMulticastTransmitDelaySec() { - boolean deprecationInProgress = false; - synchronized (mLock) { - if (mRaLength < MIN_RA_HEADER_SIZE) { - // No actual RA to send; just sleep for 1 day. - return DAY_IN_SECONDS; - } - deprecationInProgress = !mDeprecatedInfoTracker.isEmpty(); - } - - final int urgentPending = mUrgentAnnouncements.getAndDecrement(); - if ((urgentPending > 0) || deprecationInProgress) { - return MIN_DELAY_BETWEEN_RAS_SEC; - } - - return MIN_RTR_ADV_INTERVAL_SEC + mRandom.nextInt( - MAX_RTR_ADV_INTERVAL_SEC - MIN_RTR_ADV_INTERVAL_SEC); - } - - private long getNextMulticastTransmitDelayMs() { - return 1000 * (long) getNextMulticastTransmitDelaySec(); - } - } -} diff --git a/services/net/java/android/net/util/InterfaceSet.java b/services/net/java/android/net/util/InterfaceSet.java deleted file mode 100644 index 9f26fa17ef5c..000000000000 --- a/services/net/java/android/net/util/InterfaceSet.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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); - } -} |