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

Commit c14748b3 authored by Erik Kline's avatar Erik Kline Committed by Android (Google) Code Review
Browse files

Merge "Handle setting and deprecating local DNS IPv6 addresses" into nyc-mr1-dev

parents 75cdd5f6 fa37b2f6
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -1392,6 +1392,8 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering

                    for (Integer netType : mUpstreamIfaceTypes) {
                        NetworkInfo info = cm.getNetworkInfo(netType.intValue());
                        // TODO: if the network is suspended we should consider
                        // that to be the same as connected here.
                        if ((info != null) && info.isConnected()) {
                            upType = netType.intValue();
                            break;
@@ -1465,6 +1467,10 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
                    // 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).
                    handleNewUpstreamNetworkState(null);
                }
            }

@@ -1639,6 +1645,7 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
                chooseUpstreamType(mTryCell);
                mTryCell = !mTryCell;
            }

            @Override
            public void exit() {
                // TODO: examine if we should check the return value.
@@ -1646,7 +1653,9 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
                mUpstreamNetworkMonitor.stop();
                stopListeningForSimChanges();
                notifyTetheredOfNewUpstreamIface(null);
                handleNewUpstreamNetworkState(null);
            }

            @Override
            public boolean processMessage(Message message) {
                maybeLogMessage(this, message.what);
@@ -1734,6 +1743,7 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
                                // reevaluation is triggered via received CONNECTIVITY_ACTION
                                // broadcasts that result in being passed a
                                // TetherMasterSM.CMD_UPSTREAM_CHANGED.
                                handleNewUpstreamNetworkState(null);
                                break;
                            default:
                                break;
+9 −9
Original line number Diff line number Diff line
@@ -55,7 +55,7 @@ public class IPv6TetheringCoordinator {
        if (VDBG) {
            Log.d(TAG, "updateUpstreamNetworkState: " + toDebugString(ns));
        }
        if (ns == null || ns.network == null) {
        if (!canTetherIPv6(ns)) {
            stopIPv6TetheringOnAllInterfaces();
            setUpstreamNetworkState(null);
            return;
@@ -65,8 +65,9 @@ public class IPv6TetheringCoordinator {
            !ns.network.equals(mUpstreamNetworkState.network)) {
            stopIPv6TetheringOnAllInterfaces();
        }

        setUpstreamNetworkState(ns);
        maybeUpdateIPv6TetheringInterfaces();
        updateIPv6TetheringInterfaces();
    }

    private void stopIPv6TetheringOnAllInterfaces() {
@@ -77,9 +78,10 @@ public class IPv6TetheringCoordinator {
    }

    private void setUpstreamNetworkState(NetworkState ns) {
        if (!canTetherIPv6(ns)) {
        if (ns == null) {
            mUpstreamNetworkState = null;
        } else {
            // Make a deep copy of the parts we need.
            mUpstreamNetworkState = new NetworkState(
                    null,
                    new LinkProperties(ns.linkProperties),
@@ -94,19 +96,17 @@ public class IPv6TetheringCoordinator {
        }
    }

    private void maybeUpdateIPv6TetheringInterfaces() {
        if (mUpstreamNetworkState == null) return;

    private void updateIPv6TetheringInterfaces() {
        for (TetherInterfaceStateMachine sm : mNotifyList) {
            final LinkProperties lp = getInterfaceIPv6LinkProperties(sm.interfaceType());
            if (lp != null) {
            sm.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, 0, 0, lp);
            }
            break;
        }
    }

    private LinkProperties getInterfaceIPv6LinkProperties(int interfaceType) {
        if (mUpstreamNetworkState == null) return null;

        // NOTE: Here, in future, we would have policies to decide how to divvy
        // up the available dedicated prefixes among downstream interfaces.
        // At this time we have no such mechanism--we only support tethering
+148 −60
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.server.connectivity.tethering;

import android.net.INetd;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
@@ -27,13 +28,16 @@ import android.net.ip.RouterAdvertisementDaemon.RaParams;
import android.os.INetworkManagementService;
import android.os.RemoteException;
import android.util.Log;
import android.util.Slog;

import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Objects;


/**
@@ -41,13 +45,15 @@ import java.util.HashSet;
 */
class IPv6TetheringInterfaceServices {
    private static final String TAG = IPv6TetheringInterfaceServices.class.getSimpleName();
    private static final IpPrefix LINK_LOCAL_PREFIX = new IpPrefix("fe80::/64");
    private static final int RFC7421_IP_PREFIX_LENGTH = 64;

    private final String mIfName;
    private final INetworkManagementService mNMService;

    private NetworkInterface mNetworkInterface;
    private byte[] mHwAddr;
    private ArrayList<RouteInfo> mLastLocalRoutes;
    private LinkProperties mLastIPv6LinkProperties;
    private RouterAdvertisementDaemon mRaDaemon;
    private RaParams mLastRaParams;

@@ -86,8 +92,7 @@ class IPv6TetheringInterfaceServices {
    public void stop() {
        mNetworkInterface = null;
        mHwAddr = null;
        updateLocalRoutes(null);
        updateRaParams(null);
        setRaParams(null);

        if (mRaDaemon != null) {
            mRaDaemon.stop();
@@ -104,95 +109,178 @@ class IPv6TetheringInterfaceServices {
    public void updateUpstreamIPv6LinkProperties(LinkProperties v6only) {
        if (mRaDaemon == null) return;

        if (v6only == null) {
            updateLocalRoutes(null);
            updateRaParams(null);
        // Avoid unnecessary work on spurious updates.
        if (Objects.equals(mLastIPv6LinkProperties, v6only)) {
            return;
        }

        RaParams params = new RaParams();
        RaParams params = null;

        if (v6only != null) {
            params = new RaParams();
            params.mtu = v6only.getMtu();
            params.hasDefaultRoute = v6only.hasIPv6DefaultRoute();

        ArrayList<RouteInfo> localRoutes = new ArrayList<RouteInfo>();
            for (LinkAddress linkAddr : v6only.getLinkAddresses()) {
            final IpPrefix prefix = new IpPrefix(linkAddr.getAddress(),
                                                 linkAddr.getPrefixLength());

            // Accumulate routes representing "prefixes to be assigned to the
            // local interface", for subsequent addition to the local network
            // in the routing rules.
            localRoutes.add(new RouteInfo(prefix, null, mIfName));
                if (linkAddr.getPrefixLength() != RFC7421_IP_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;
    }

        // We need to be able to send unicast RAs, and clients might like to
        // ping the default router's link-local address, so add that as well.
        localRoutes.add(new RouteInfo(new IpPrefix("fe80::/64"), null, mIfName));

        // TODO: Add a local interface address, update dnsmasq to listen on the
        // new address, and use only that address as a DNS server.
        for (InetAddress dnsServer : v6only.getDnsServers()) {
            if (dnsServer instanceof Inet6Address) {
                params.dnses.add((Inet6Address) dnsServer);
    private void configureLocalRoutes(
            HashSet<IpPrefix> deprecatedPrefixes, HashSet<IpPrefix> newPrefixes) {
        // [1] Remove the routes that are deprecated.
        if (!deprecatedPrefixes.isEmpty()) {
            final ArrayList<RouteInfo> toBeRemoved = getLocalRoutesFor(deprecatedPrefixes);
            try {
                final int removalFailures = mNMService.removeRoutesFromLocalNetwork(toBeRemoved);
                if (removalFailures > 0) {
                    Log.e(TAG, String.format("Failed to remove %d IPv6 routes from local table.",
                            removalFailures));
                }
            } catch (RemoteException e) {
                Log.e(TAG, "Failed to remove IPv6 routes from local table: ", e);
            }
        }

        updateLocalRoutes(localRoutes);
        updateRaParams(params);
        // [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);
            }

    private void updateLocalRoutes(ArrayList<RouteInfo> localRoutes) {
        if (localRoutes != null) {
            // TODO: Compare with mLastLocalRoutes and take appropriate
            // appropriate action on the difference between the two.
            if (mLastRaParams == null || mLastRaParams.prefixes.isEmpty()) {
                // We need to be able to send unicast RAs, and clients might
                // like to ping the default router's link-local address.  Note
                // that we never remove the link-local route from the network
                // until Tethering disables tethering on the interface.
                addedPrefixes.add(LINK_LOCAL_PREFIX);
            }

            if (!localRoutes.isEmpty()) {
            if (!addedPrefixes.isEmpty()) {
                final ArrayList<RouteInfo> toBeAdded = getLocalRoutesFor(addedPrefixes);
                try {
                    mNMService.addInterfaceToLocalNetwork(mIfName, localRoutes);
                    // It's safe to call addInterfaceToLocalNetwork() even if
                    // the interface is already in the local_network.
                    mNMService.addInterfaceToLocalNetwork(mIfName, toBeAdded);
                } catch (RemoteException e) {
                    Log.e(TAG, "Failed to add IPv6 routes to local table: ", e);
                }
            }
        } else {
            if (mLastLocalRoutes != null && !mLastLocalRoutes.isEmpty()) {
        }
    }

    private void configureLocalDns(
            HashSet<Inet6Address> deprecatedDnses, HashSet<Inet6Address> newDnses) {
        INetd netd = getNetdServiceOrNull();
        if (netd == null) {
            if (newDnses != null) newDnses.clear();
            Log.e(TAG, "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) {
                final String dnsString = dns.getHostAddress();
                try {
                    final int removalFailures =
                            mNMService.removeRoutesFromLocalNetwork(mLastLocalRoutes);
                    if (removalFailures > 0) {
                        Log.e(TAG,
                                String.format("Failed to remove %d IPv6 routes from local table.",
                                removalFailures));
                    netd.interfaceDelAddress(mIfName, dnsString, RFC7421_IP_PREFIX_LENGTH);
                } catch (RemoteException e) {
                    Log.e(TAG, "Failed to remove local dns IP: " + dnsString, e);
                }
            }
        }

        // [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) {
                final String dnsString = dns.getHostAddress();
                try {
                    netd.interfaceAddAddress(mIfName, dnsString, RFC7421_IP_PREFIX_LENGTH);
                } catch (RemoteException e) {
                    Log.e(TAG, "Failed to remove IPv6 routes from local table: ", e);
                    Log.e(TAG, "Failed to add local dns IP: " + dnsString, e);
                    newDnses.remove(dns);
                }
            }
        }

        mLastLocalRoutes = localRoutes;
        try {
            netd.tetherApplyDnsInterfaces();
        } catch (RemoteException e) {
            Log.e(TAG, "Failed to update local DNS caching server");
            if (newDnses != null) newDnses.clear();
        }
    }

    private void updateRaParams(RaParams params) {
    private void setRaParams(RaParams newParams) {
        if (mRaDaemon != null) {
            HashSet<IpPrefix> deprecated = null;
            final RaParams deprecatedParams =
                    RaParams.getDeprecatedRaParams(mLastRaParams, newParams);

            if (mLastRaParams != null) {
                deprecated = new HashSet<>();
            configureLocalRoutes(deprecatedParams.prefixes,
                    (newParams != null) ? newParams.prefixes : null);

            configureLocalDns(deprecatedParams.dnses,
                    (newParams != null) ? newParams.dnses : null);

                for (IpPrefix ipp : mLastRaParams.prefixes) {
                    if (params == null || !params.prefixes.contains(ipp)) {
                        deprecated.add(ipp);
            mRaDaemon.buildNewRa(deprecatedParams, newParams);
        }

        mLastRaParams = newParams;
    }

    // Accumulate routes representing "prefixes to be assigned to the local
    // interface", for subsequent modification of local_network routing.
    private ArrayList<RouteInfo> getLocalRoutesFor(HashSet<IpPrefix> prefixes) {
        final ArrayList<RouteInfo> localRoutes = new ArrayList<RouteInfo>();
        for (IpPrefix ipp : prefixes) {
            localRoutes.add(new RouteInfo(ipp, null, mIfName));
        }
        return localRoutes;
    }

            // Currently, we send spurious RAs (5) whenever there's any update.
            // TODO: Compare params with mLastParams to avoid spurious updates.
            mRaDaemon.buildNewRa(params, deprecated);
    private INetd getNetdServiceOrNull() {
        if (mNMService != null) {
            try {
                return mNMService.getNetdService();
            } catch (RemoteException ignored) {
                // This blocks until netd can be reached, but it can return
                // null during a netd crash.
            }
        }
        return null;
    }

        mLastRaParams = params;
    // Given a prefix like 2001:db8::/64 return 2001:db8::1.
    private static Inet6Address getLocalDnsIpFor(IpPrefix localPrefix) {
        final byte[] dnsBytes = localPrefix.getRawAddress();
        dnsBytes[dnsBytes.length - 1] = 0x1;
        try {
            return Inet6Address.getByAddress(null, dnsBytes, 0);
        } catch (UnknownHostException e) {
            Slog.wtf(TAG, "Failed to construct Inet6Address from: " + localPrefix);
            return null;
        }
    }
}
+153 −81

File changed.

Preview size limit exceeded, changes collapsed.