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

Commit 646f8993 authored by Erik Kline's avatar Erik Kline Committed by android-build-merger
Browse files

Handle setting and deprecating local DNS IPv6 addresses am: fa37b2f6 am: 5fe2c037

am: a852f032

Change-Id: Idf650b5f6cfbbee607594c70b1de5cd3f1375a44
parents fc44e961 a852f032
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.