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

Commit a02847ec authored by Lorenzo Colitti's avatar Lorenzo Colitti Committed by Gerrit Code Review
Browse files

Merge changes from topic "nat64"

* changes:
  Catch ServiceSpecificException instead of IllegalStateException.
  Manage NAT64 prefix discovery lifecycle in the framework.
  Track NAT64 in the framework and start clatd iff NAT64 detected
  Change Nat464Xlat lifecycle.
  Minor improvements to verifyTcpBufferSizeChange.
parents 0d99ab3f b313e0c8
Loading
Loading
Loading
Loading
+38 −8
Original line number Diff line number Diff line
@@ -71,6 +71,8 @@ import android.net.INetworkMonitorCallbacks;
import android.net.INetworkPolicyListener;
import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
import android.net.InetAddresses;
import android.net.IpPrefix;
import android.net.LinkProperties;
import android.net.LinkProperties.CompareResult;
import android.net.MatchAllNetworkSpecifier;
@@ -1740,6 +1742,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
                }
            }
        }

        @Override
        public void onNat64PrefixEvent(int netId, boolean added,
                                       String prefixString, int prefixLength) {
            mHandler.post(() -> handleNat64PrefixEvent(netId, added, prefixString, prefixLength));
        }
    };

    @VisibleForTesting
@@ -2767,6 +2775,29 @@ public class ConnectivityService extends IConnectivityManager.Stub
        handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties));
    }

    private void handleNat64PrefixEvent(int netId, boolean added, String prefixString,
            int prefixLength) {
        NetworkAgentInfo nai = mNetworkForNetId.get(netId);
        if (nai == null) return;

        log(String.format("NAT64 prefix %s on netId %d: %s/%d",
                          (added ? "added" : "removed"), netId, prefixString, prefixLength));

        IpPrefix prefix = null;
        if (added) {
            try {
                prefix = new IpPrefix(InetAddresses.parseNumericAddress(prefixString),
                        prefixLength);
            } catch (IllegalArgumentException e) {
                loge("Invalid NAT64 prefix " + prefixString + "/" + prefixLength);
                return;
            }
        }

        nai.clatd.setNat64Prefix(prefix);
        handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties));
    }

    private void updateLingerState(NetworkAgentInfo nai, long now) {
        // 1. Update the linger timer. If it's changed, reschedule or cancel the alarm.
        // 2. If the network was lingering and there are now requests, unlinger it.
@@ -2882,7 +2913,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
            e.rethrowFromSystemServer();
        }
        mNetworkAgentInfos.remove(nai.messenger);
        nai.maybeStopClat();
        nai.clatd.update();
        synchronized (mNetworkForNetId) {
            // Remove the NetworkAgent, but don't mark the netId as
            // available until we've told netd to delete it below.
@@ -5190,11 +5221,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
            LinkProperties oldLp) {
        int netId = networkAgent.network.netId;

        // The NetworkAgentInfo does not know whether clatd is running on its network or not. Before
        // we do anything else, make sure its LinkProperties are accurate.
        if (networkAgent.clatd != null) {
        // The NetworkAgentInfo does not know whether clatd is running on its network or not, or
        // whether there is a NAT64 prefix. Before we do anything else, make sure its LinkProperties
        // are accurate.
        networkAgent.clatd.fixupLinkProperties(oldLp, newLp);
        }

        updateInterfaces(newLp, oldLp, netId, networkAgent.networkCapabilities);
        updateMtu(newLp, oldLp);
@@ -5224,8 +5254,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
            synchronized (networkAgent) {
                networkAgent.linkProperties = newLp;
            }
            // Start or stop clat accordingly to network state.
            networkAgent.updateClat(mNMS);
            // Start or stop DNS64 detection and 464xlat according to network state.
            networkAgent.clatd.update();
            notifyIfacesChangedForNetworkStats();
            if (networkAgent.everConnected) {
                try {
+172 −67
Original line number Diff line number Diff line
@@ -18,19 +18,24 @@ package com.android.server.connectivity;

import android.net.ConnectivityManager;
import android.net.INetd;
import android.net.InetAddresses;
import android.net.InterfaceConfiguration;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.NetworkInfo;
import android.net.RouteInfo;
import android.os.INetworkManagementService;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.util.Slog;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.server.net.BaseNetworkObserver;

import java.net.Inet4Address;
import java.net.Inet6Address;
import java.util.Objects;

/**
@@ -68,14 +73,15 @@ public class Nat464Xlat extends BaseNetworkObserver {

    private enum State {
        IDLE,         // start() not called. Base iface and stacked iface names are null.
        DISCOVERING,  // same as IDLE, except prefix discovery in progress.
        STARTING,     // start() called. Base iface and stacked iface names are known.
        RUNNING,      // start() called, and the stacked iface is known to be up.
        STOPPING;   // stop() called, this Nat464Xlat is still registered as a network observer for
                    // the stacked interface.
    }

    private IpPrefix mNat64Prefix;
    private String mBaseIface;
    private String mIface;
    private Inet6Address mIPv6Address;
    private State mState = State.IDLE;

    public Nat464Xlat(NetworkAgentInfo nai, INetd netd, INetworkManagementService nmService) {
@@ -85,20 +91,51 @@ public class Nat464Xlat extends BaseNetworkObserver {
    }

    /**
     * Determines whether a network requires clat.
     * Whether to attempt 464xlat on this network. This is true for an IPv6-only network that is
     * currently connected and where the NetworkAgent has not disabled 464xlat. It is the signal to
     * enable NAT64 prefix discovery.
     *
     * @param network the NetworkAgentInfo corresponding to the network.
     * @return true if the network requires clat, false otherwise.
     */
    public static boolean requiresClat(NetworkAgentInfo nai) {
    @VisibleForTesting
    protected static boolean requiresClat(NetworkAgentInfo nai) {
        // TODO: migrate to NetworkCapabilities.TRANSPORT_*.
        final boolean supported = ArrayUtils.contains(NETWORK_TYPES, nai.networkInfo.getType());
        final boolean connected = ArrayUtils.contains(NETWORK_STATES, nai.networkInfo.getState());
        // We only run clat on networks that don't have a native IPv4 address.
        final boolean hasIPv4Address =
                (nai.linkProperties != null) && nai.linkProperties.hasIPv4Address();
        final boolean skip464xlat =
                (nai.netMisc() != null) && nai.netMisc().skip464xlat;
        return supported && connected && !hasIPv4Address && !skip464xlat;

        // Only run clat on networks that have a global IPv6 address and don't have a native IPv4
        // address.
        LinkProperties lp = nai.linkProperties;
        final boolean isIpv6OnlyNetwork = (lp != null) && lp.hasGlobalIPv6Address()
                && !lp.hasIPv4Address();

        // If the network tells us it doesn't use clat, respect that.
        final boolean skip464xlat = (nai.netMisc() != null) && nai.netMisc().skip464xlat;

        return supported && connected && isIpv6OnlyNetwork && !skip464xlat;
    }

    /**
     * Whether the clat demon should be started on this network now. This is true if requiresClat is
     * true and a NAT64 prefix has been discovered.
     *
     * @param nai the NetworkAgentInfo corresponding to the network.
     * @return true if the network should start clat, false otherwise.
     */
    @VisibleForTesting
    protected static boolean shouldStartClat(NetworkAgentInfo nai) {
        LinkProperties lp = nai.linkProperties;
        return requiresClat(nai) && lp != null && lp.getNat64Prefix() != null;
    }

    /**
     * @return true if we have started prefix discovery and not yet stopped it (regardless of
     * whether it is still running or has succeeded).
     * A true result corresponds to internal states DISCOVERING, STARTING and RUNNING.
     */
    public boolean isPrefixDiscoveryStarted() {
        return mState == State.DISCOVERING || isStarted();
    }

    /**
@@ -106,7 +143,7 @@ public class Nat464Xlat extends BaseNetworkObserver {
     * A true result corresponds to internal states STARTING and RUNNING.
     */
    public boolean isStarted() {
        return mState != State.IDLE;
        return (mState == State.STARTING || mState == State.RUNNING);
    }

    /**
@@ -123,13 +160,6 @@ public class Nat464Xlat extends BaseNetworkObserver {
        return mState == State.RUNNING;
    }

    /**
     * @return true if clatd has been stopped.
     */
    public boolean isStopping() {
        return mState == State.STOPPING;
    }

    /**
     * Start clatd, register this Nat464Xlat as a network observer for the stacked interface,
     * and set internal state.
@@ -138,18 +168,24 @@ public class Nat464Xlat extends BaseNetworkObserver {
        try {
            mNMService.registerObserver(this);
        } catch (RemoteException e) {
            Slog.e(TAG,
                    "startClat: Can't register interface observer for clat on " + mNetwork.name());
            Slog.e(TAG, "Can't register interface observer for clat on " + mNetwork.name());
            return;
        }

        String addrStr = null;
        try {
            mNetd.clatdStart(baseIface);
        } catch(RemoteException|IllegalStateException e) {
            Slog.e(TAG, "Error starting clatd on " + baseIface, e);
            addrStr = mNetd.clatdStart(baseIface, mNat64Prefix.toString());
        } catch (RemoteException | ServiceSpecificException e) {
            Slog.e(TAG, "Error starting clatd on " + baseIface + ": " + e);
        }
        mIface = CLAT_PREFIX + baseIface;
        mBaseIface = baseIface;
        mState = State.STARTING;
        try {
            mIPv6Address = (Inet6Address) InetAddresses.parseNumericAddress(addrStr);
        } catch (ClassCastException | IllegalArgumentException | NullPointerException e) {
            Slog.e(TAG, "Invalid IPv6 address " + addrStr);
        }
    }

    /**
@@ -160,38 +196,28 @@ public class Nat464Xlat extends BaseNetworkObserver {
        mState = State.RUNNING;
    }

    /**
     * Stop clatd, and turn ND offload on if it had been turned off.
     */
    private void enterStoppingState() {
        try {
            mNetd.clatdStop(mBaseIface);
        } catch(RemoteException|IllegalStateException e) {
            Slog.e(TAG, "Error stopping clatd on " + mBaseIface, e);
        }

        mState = State.STOPPING;
    }

    /**
     * Unregister as a base observer for the stacked interface, and clear internal state.
     */
    private void enterIdleState() {
    private void leaveStartedState() {
        try {
            mNMService.unregisterObserver(this);
        } catch (RemoteException | IllegalStateException e) {
            Slog.e(TAG, "Error unregistering clatd observer on " + mBaseIface, e);
            Slog.e(TAG, "Error unregistering clatd observer on " + mBaseIface + ": " + e);
        }

        mIface = null;
        mBaseIface = null;
        mState = State.IDLE;
        if (requiresClat(mNetwork)) {
            mState = State.DISCOVERING;
        } else {
            stopPrefixDiscovery();
            mState = State.IDLE;
        }
    }

    /**
     * Starts the clat daemon.
     */
    public void start() {
    @VisibleForTesting
    protected void start() {
        if (isStarted()) {
            Slog.e(TAG, "startClat: already started");
            return;
@@ -212,28 +238,92 @@ public class Nat464Xlat extends BaseNetworkObserver {
        enterStartingState(baseIface);
    }

    /**
     * Stops the clat daemon.
     */
    public void stop() {
    @VisibleForTesting
    protected void stop() {
        if (!isStarted()) {
            Slog.e(TAG, "stopClat: already stopped");
            return;
        }

        Slog.i(TAG, "Stopping clatd on " + mBaseIface);
        try {
            mNetd.clatdStop(mBaseIface);
        } catch (RemoteException | ServiceSpecificException e) {
            Slog.e(TAG, "Error stopping clatd on " + mBaseIface + ": " + e);
        }

        String iface = mIface;
        boolean wasRunning = isRunning();

        // Change state before updating LinkProperties. handleUpdateLinkProperties ends up calling
        // fixupLinkProperties, and if at that time the state is still RUNNING, fixupLinkProperties
        // would wrongly inform ConnectivityService that there is still a stacked interface.
        leaveStartedState();

        if (wasRunning) {
            LinkProperties lp = new LinkProperties(mNetwork.linkProperties);
            lp.removeStackedLink(iface);
            mNetwork.connService().handleUpdateLinkProperties(mNetwork, lp);
        }
    }

        boolean wasStarting = isStarting();
        enterStoppingState();
        if (wasStarting) {
            enterIdleState();
    private void startPrefixDiscovery() {
        try {
            mNetd.resolverStartPrefix64Discovery(getNetId());
            mState = State.DISCOVERING;
        } catch (RemoteException | ServiceSpecificException e) {
            Slog.e(TAG, "Error starting prefix discovery on netId " + getNetId() + ": " + e);
        }
    }

    private void stopPrefixDiscovery() {
        try {
            mNetd.resolverStopPrefix64Discovery(getNetId());
        } catch (RemoteException | ServiceSpecificException e) {
            Slog.e(TAG, "Error stopping prefix discovery on netId " + getNetId() + ": " + e);
        }
    }

    /**
     * Starts/stops NAT64 prefix discovery and clatd as necessary.
     */
    public void update() {
        // TODO: turn this class into a proper StateMachine. // http://b/126113090
        if (requiresClat(mNetwork)) {
            if (!isPrefixDiscoveryStarted()) {
                startPrefixDiscovery();
            } else if (shouldStartClat(mNetwork)) {
                // NAT64 prefix detected. Start clatd.
                // TODO: support the NAT64 prefix changing after it's been discovered. There is no
                // need to support this at the moment because it cannot happen without changes to
                // the Dns64Configuration code in netd.
                start();
            } else {
                // NAT64 prefix removed. Stop clatd and go back into DISCOVERING state.
                stop();
            }
        } else {
            // Network no longer requires clat. Stop clat and prefix discovery.
            if (isStarted()) {
                stop();
            } else if (isPrefixDiscoveryStarted()) {
                leaveStartedState();
            }
        }
    }

    public void setNat64Prefix(IpPrefix nat64Prefix) {
        mNat64Prefix = nat64Prefix;
    }

    /**
     * Copies the stacked clat link in oldLp, if any, to the passed LinkProperties.
     * This is necessary because the LinkProperties in mNetwork come from the transport layer, which
     * has no idea that 464xlat is running on top of it.
     */
    public void fixupLinkProperties(LinkProperties oldLp, LinkProperties lp) {
        lp.setNat64Prefix(mNat64Prefix);

        if (!isRunning()) {
            return;
        }
@@ -282,6 +372,20 @@ public class Nat464Xlat extends BaseNetworkObserver {
     * Adds stacked link on base link and transitions to RUNNING state.
     */
    private void handleInterfaceLinkStateChanged(String iface, boolean up) {
        // TODO: if we call start(), then stop(), then start() again, and the
        // interfaceLinkStateChanged notification for the first start is delayed past the first
        // stop, then the code becomes out of sync with system state and will behave incorrectly.
        //
        // This is not trivial to fix because:
        // 1. It is not guaranteed that start() will eventually result in the interface coming up,
        //    because there could be an error starting clat (e.g., if the interface goes down before
        //    the packet socket can be bound).
        // 2. If start is called multiple times, there is nothing in the interfaceLinkStateChanged
        //    notification that says which start() call the interface was created by.
        //
        // Once this code is converted to StateMachine, it will be possible to use deferMessage to
        // ensure it stays in STARTING state until the interfaceLinkStateChanged notification fires,
        // and possibly use a timeout (or provide some guarantees at the lower layer) to address #1.
        if (!isStarting() || !up || !Objects.equals(mIface, iface)) {
            return;
        }
@@ -307,20 +411,16 @@ public class Nat464Xlat extends BaseNetworkObserver {
        if (!Objects.equals(mIface, iface)) {
            return;
        }
        if (!isRunning() && !isStopping()) {
        if (!isRunning()) {
            return;
        }

        Slog.i(TAG, "interface " + iface + " removed");
        if (!isStopping()) {
            // Ensure clatd is stopped if stop() has not been called: this likely means that clatd
            // has crashed.
            enterStoppingState();
        }
        enterIdleState();
        LinkProperties lp = new LinkProperties(mNetwork.linkProperties);
        lp.removeStackedLink(iface);
        mNetwork.connService().handleUpdateLinkProperties(mNetwork, lp);
        // If we're running, and the interface was removed, then we didn't call stop(), and it's
        // likely that clatd crashed. Ensure we call stop() so we can start clatd again. Calling
        // stop() will also update LinkProperties, and if clatd crashed, the LinkProperties update
        // will cause ConnectivityService to call start() again.
        stop();
    }

    @Override
@@ -337,4 +437,9 @@ public class Nat464Xlat extends BaseNetworkObserver {
    public String toString() {
        return "mBaseIface: " + mBaseIface + ", mIface: " + mIface + ", mState: " + mState;
    }

    @VisibleForTesting
    protected int getNetId() {
        return mNetwork.network.netId;
    }
}
+2 −31
Original line number Diff line number Diff line
@@ -236,7 +236,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
    public final AsyncChannel asyncChannel;

    // Used by ConnectivityService to keep track of 464xlat.
    public Nat464Xlat clatd;
    public final Nat464Xlat clatd;

    // Set after asynchronous creation of the NetworkMonitor.
    private volatile INetworkMonitor mNetworkMonitor;
@@ -244,8 +244,6 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
    private static final String TAG = ConnectivityService.class.getSimpleName();
    private static final boolean VDBG = false;
    private final ConnectivityService mConnService;
    private final INetd mNetd;
    private final INetworkManagementService mNMS;
    private final Context mContext;
    private final Handler mHandler;

@@ -260,9 +258,8 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
        linkProperties = lp;
        networkCapabilities = nc;
        currentScore = score;
        clatd = new Nat464Xlat(this, netd, nms);
        mConnService = connService;
        mNetd = netd;
        mNMS = nms;
        mContext = context;
        mHandler = handler;
        networkMisc = misc;
@@ -595,32 +592,6 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
        for (LingerTimer timer : mLingerTimers) { pw.println(timer); }
    }

    public void updateClat(INetworkManagementService netd) {
        if (Nat464Xlat.requiresClat(this)) {
            maybeStartClat();
        } else {
            maybeStopClat();
        }
    }

    /** Ensure clat has started for this network. */
    public void maybeStartClat() {
        if (clatd != null && clatd.isStarted()) {
            return;
        }
        clatd = new Nat464Xlat(this, mNetd, mNMS);
        clatd.start();
    }

    /** Ensure clat has stopped for this network. */
    public void maybeStopClat() {
        if (clatd == null) {
            return;
        }
        clatd.stop();
        clatd = null;
    }

    public String toString() {
        return "NetworkAgentInfo{ ni{" + networkInfo + "}  "
                + "network{" + network + "}  nethandle{" + network.getNetworkHandle() + "}  "
+109 −20

File changed.

Preview size limit exceeded, changes collapsed.

+171 −24

File changed.

Preview size limit exceeded, changes collapsed.