Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 293eb120 authored by Remi NGUYEN VAN's avatar Remi NGUYEN VAN Committed by android-build-merger
Browse files

Merge "Use InterfaceSet for upstream interfaces." am: e18fd9ff

am: 36d3bf83

Change-Id: Ic8602dd029b5a9626aaf6651315b82ab6327037c
parents 4de8a23d 36d3bf83
Loading
Loading
Loading
Loading
+20 −26
Original line number Diff line number Diff line
@@ -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;
@@ -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;

@@ -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;

@@ -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;
                }
            }
@@ -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);
            }
        }
@@ -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);
            }
        }

@@ -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
@@ -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
@@ -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();
@@ -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();
        }

+1 −67
Original line number Diff line number Diff line
@@ -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;

@@ -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;
@@ -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) {
+49 −17
Original line number Diff line number Diff line
@@ -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;
@@ -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
@@ -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
@@ -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) {
@@ -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;
        }
    }

    /**
+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;
    }
}
+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