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

Commit 818da784 authored by Mark Chien's avatar Mark Chien Committed by Gerrit Code Review
Browse files

Merge "Make the IP subnet persistent till reboot"

parents 1c7bc039 6626f7bc
Loading
Loading
Loading
Loading
+4 −4
Original line number Original line Diff line number Diff line
@@ -616,7 +616,7 @@ public class IpServer extends StateMachine {
        if (VDBG) Log.d(TAG, "configureIPv4(" + enabled + ")");
        if (VDBG) Log.d(TAG, "configureIPv4(" + enabled + ")");


        if (enabled) {
        if (enabled) {
            mIpv4Address = requestIpv4Address();
            mIpv4Address = requestIpv4Address(true /* useLastAddress */);
        }
        }


        if (mIpv4Address == null) {
        if (mIpv4Address == null) {
@@ -661,14 +661,14 @@ public class IpServer extends StateMachine {
        return configureDhcp(enabled, mIpv4Address, mStaticIpv4ClientAddr);
        return configureDhcp(enabled, mIpv4Address, mStaticIpv4ClientAddr);
    }
    }


    private LinkAddress requestIpv4Address() {
    private LinkAddress requestIpv4Address(final boolean useLastAddress) {
        if (mStaticIpv4ServerAddr != null) return mStaticIpv4ServerAddr;
        if (mStaticIpv4ServerAddr != null) return mStaticIpv4ServerAddr;


        if (mInterfaceType == TetheringManager.TETHERING_BLUETOOTH) {
        if (mInterfaceType == TetheringManager.TETHERING_BLUETOOTH) {
            return new LinkAddress(BLUETOOTH_IFACE_ADDR);
            return new LinkAddress(BLUETOOTH_IFACE_ADDR);
        }
        }


        return mPrivateAddressCoordinator.requestDownstreamAddress(this);
        return mPrivateAddressCoordinator.requestDownstreamAddress(this, useLastAddress);
    }
    }


    private boolean startIPv6() {
    private boolean startIPv6() {
@@ -957,7 +957,7 @@ public class IpServer extends StateMachine {
        }
        }


        final LinkAddress deprecatedLinkAddress = mIpv4Address;
        final LinkAddress deprecatedLinkAddress = mIpv4Address;
        mIpv4Address = requestIpv4Address();
        mIpv4Address = requestIpv4Address(false);
        if (mIpv4Address == null) {
        if (mIpv4Address == null) {
            mLog.e("Fail to request a new downstream prefix");
            mLog.e("Fail to request a new downstream prefix");
            return;
            return;
+53 −23
Original line number Original line Diff line number Diff line
@@ -16,7 +16,9 @@
package com.android.networkstack.tethering;
package com.android.networkstack.tethering;


import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.TetheringManager.TETHERING_BLUETOOTH;
import static android.net.TetheringManager.TETHERING_WIFI_P2P;
import static android.net.TetheringManager.TETHERING_WIFI_P2P;
import static android.net.util.PrefixUtils.asIpPrefix;


import static java.util.Arrays.asList;
import static java.util.Arrays.asList;


@@ -26,9 +28,9 @@ import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkAddress;
import android.net.Network;
import android.net.Network;
import android.net.ip.IpServer;
import android.net.ip.IpServer;
import android.net.util.PrefixUtils;
import android.util.ArrayMap;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.ArraySet;
import android.util.SparseArray;


import androidx.annotation.Nullable;
import androidx.annotation.Nullable;


@@ -58,9 +60,6 @@ public class PrivateAddressCoordinator {


    private static final int MAX_UBYTE = 256;
    private static final int MAX_UBYTE = 256;
    private static final int BYTE_MASK = 0xff;
    private static final int BYTE_MASK = 0xff;
    // reserved for bluetooth tethering.
    private static final int BLUETOOTH_RESERVED = 44;
    private static final int WIFI_P2P_RESERVED = 49;
    private static final byte DEFAULT_ID = (byte) 42;
    private static final byte DEFAULT_ID = (byte) 42;


    // Upstream monitor would be stopped when tethering is down. When tethering restart, downstream
    // Upstream monitor would be stopped when tethering is down. When tethering restart, downstream
@@ -75,9 +74,12 @@ public class PrivateAddressCoordinator {
    // Tethering use 192.168.0.0/16 that has 256 contiguous class C network numbers.
    // Tethering use 192.168.0.0/16 that has 256 contiguous class C network numbers.
    private static final String DEFAULT_TETHERING_PREFIX = "192.168.0.0/16";
    private static final String DEFAULT_TETHERING_PREFIX = "192.168.0.0/16";
    private static final String LEGACY_WIFI_P2P_IFACE_ADDRESS = "192.168.49.1/24";
    private static final String LEGACY_WIFI_P2P_IFACE_ADDRESS = "192.168.49.1/24";
    private static final String LEGACY_BLUETOOTH_IFACE_ADDRESS = "192.168.44.1/24";
    private final IpPrefix mTetheringPrefix;
    private final IpPrefix mTetheringPrefix;
    private final ConnectivityManager mConnectivityMgr;
    private final ConnectivityManager mConnectivityMgr;
    private final TetheringConfiguration mConfig;
    private final TetheringConfiguration mConfig;
    // keyed by downstream type(TetheringManager.TETHERING_*).
    private final SparseArray<LinkAddress> mCachedAddresses;


    public PrivateAddressCoordinator(Context context, TetheringConfiguration config) {
    public PrivateAddressCoordinator(Context context, TetheringConfiguration config) {
        mDownstreams = new ArraySet<>();
        mDownstreams = new ArraySet<>();
@@ -86,6 +88,10 @@ public class PrivateAddressCoordinator {
        mConnectivityMgr = (ConnectivityManager) context.getSystemService(
        mConnectivityMgr = (ConnectivityManager) context.getSystemService(
                Context.CONNECTIVITY_SERVICE);
                Context.CONNECTIVITY_SERVICE);
        mConfig = config;
        mConfig = config;
        mCachedAddresses = new SparseArray<>();
        // Reserved static addresses for bluetooth and wifi p2p.
        mCachedAddresses.put(TETHERING_BLUETOOTH, new LinkAddress(LEGACY_BLUETOOTH_IFACE_ADDRESS));
        mCachedAddresses.put(TETHERING_WIFI_P2P, new LinkAddress(LEGACY_WIFI_P2P_IFACE_ADDRESS));
    }
    }


    /**
    /**
@@ -94,7 +100,8 @@ public class PrivateAddressCoordinator {
     * UpstreamNetworkState must have an already populated LinkProperties.
     * UpstreamNetworkState must have an already populated LinkProperties.
     */
     */
    public void updateUpstreamPrefix(final UpstreamNetworkState ns) {
    public void updateUpstreamPrefix(final UpstreamNetworkState ns) {
        // Do not support VPN as upstream
        // Do not support VPN as upstream. Normally, networkCapabilities is not expected to be null,
        // but just checking to be sure.
        if (ns.networkCapabilities != null && ns.networkCapabilities.hasTransport(TRANSPORT_VPN)) {
        if (ns.networkCapabilities != null && ns.networkCapabilities.hasTransport(TRANSPORT_VPN)) {
            removeUpstreamPrefix(ns.network);
            removeUpstreamPrefix(ns.network);
            return;
            return;
@@ -116,7 +123,7 @@ public class PrivateAddressCoordinator {
        for (LinkAddress address : linkAddresses) {
        for (LinkAddress address : linkAddresses) {
            if (!address.isIpv4()) continue;
            if (!address.isIpv4()) continue;


            list.add(PrefixUtils.asIpPrefix(address));
            list.add(asIpPrefix(address));
        }
        }


        return list;
        return list;
@@ -155,21 +162,23 @@ public class PrivateAddressCoordinator {
        mUpstreamPrefixMap.removeAll(toBeRemoved);
        mUpstreamPrefixMap.removeAll(toBeRemoved);
    }
    }


    private boolean isReservedSubnet(final int subnet) {
        return subnet == BLUETOOTH_RESERVED || subnet == WIFI_P2P_RESERVED;
    }

    /**
    /**
     * Pick a random available address and mark its prefix as in use for the provided IpServer,
     * Pick a random available address and mark its prefix as in use for the provided IpServer,
     * returns null if there is no available address.
     * returns null if there is no available address.
     */
     */
    @Nullable
    @Nullable
    public LinkAddress requestDownstreamAddress(final IpServer ipServer) {
    public LinkAddress requestDownstreamAddress(final IpServer ipServer, boolean useLastAddress) {
        if (mConfig.shouldEnableWifiP2pDedicatedIp()
        if (mConfig.shouldEnableWifiP2pDedicatedIp()
                && ipServer.interfaceType() == TETHERING_WIFI_P2P) {
                && ipServer.interfaceType() == TETHERING_WIFI_P2P) {
            return new LinkAddress(LEGACY_WIFI_P2P_IFACE_ADDRESS);
            return new LinkAddress(LEGACY_WIFI_P2P_IFACE_ADDRESS);
        }
        }


        final LinkAddress cachedAddress = mCachedAddresses.get(ipServer.interfaceType());
        if (useLastAddress && cachedAddress != null
                && !isConflictWithUpstream(asIpPrefix(cachedAddress))) {
            return cachedAddress;
        }

        // Address would be 192.168.[subAddress]/24.
        // Address would be 192.168.[subAddress]/24.
        final byte[] bytes = mTetheringPrefix.getRawAddress();
        final byte[] bytes = mTetheringPrefix.getRawAddress();
        final int subAddress = getRandomSubAddr();
        final int subAddress = getRandomSubAddr();
@@ -177,9 +186,8 @@ public class PrivateAddressCoordinator {
        bytes[3] = getSanitizedAddressSuffix(subAddress, (byte) 0, (byte) 1, (byte) 0xff);
        bytes[3] = getSanitizedAddressSuffix(subAddress, (byte) 0, (byte) 1, (byte) 0xff);
        for (int i = 0; i < MAX_UBYTE; i++) {
        for (int i = 0; i < MAX_UBYTE; i++) {
            final int newSubNet = (subNet + i) & BYTE_MASK;
            final int newSubNet = (subNet + i) & BYTE_MASK;
            if (isReservedSubnet(newSubNet)) continue;

            bytes[2] = (byte) newSubNet;
            bytes[2] = (byte) newSubNet;

            final InetAddress addr;
            final InetAddress addr;
            try {
            try {
                addr = InetAddress.getByAddress(bytes);
                addr = InetAddress.getByAddress(bytes);
@@ -187,20 +195,23 @@ public class PrivateAddressCoordinator {
                throw new IllegalStateException("Invalid address, shouldn't happen.", e);
                throw new IllegalStateException("Invalid address, shouldn't happen.", e);
            }
            }


            final IpPrefix prefix = new IpPrefix(addr, PREFIX_LENGTH);
            if (isConflict(new IpPrefix(addr, PREFIX_LENGTH))) continue;
            // Check whether this prefix is in use.
            if (isDownstreamPrefixInUse(prefix)) continue;
            // Check whether this prefix is conflict with any current upstream network.
            if (isConflictWithUpstream(prefix)) continue;


            mDownstreams.add(ipServer);
            mDownstreams.add(ipServer);
            return new LinkAddress(addr, PREFIX_LENGTH);
            final LinkAddress newAddress = new LinkAddress(addr, PREFIX_LENGTH);
            mCachedAddresses.put(ipServer.interfaceType(), newAddress);
            return newAddress;
        }
        }


        // No available address.
        // No available address.
        return null;
        return null;
    }
    }


    private boolean isConflict(final IpPrefix prefix) {
        // Check whether this prefix is in use or conflict with any current upstream network.
        return isDownstreamPrefixInUse(prefix) || isConflictWithUpstream(prefix);
    }

    /** Get random sub address value. Return value is in 0 ~ 0xffff. */
    /** Get random sub address value. Return value is in 0 ~ 0xffff. */
    @VisibleForTesting
    @VisibleForTesting
    public int getRandomSubAddr() {
    public int getRandomSubAddr() {
@@ -244,13 +255,24 @@ public class PrivateAddressCoordinator {
        return prefix1.contains(prefix2.getAddress());
        return prefix1.contains(prefix2.getAddress());
    }
    }


    private boolean isDownstreamPrefixInUse(final IpPrefix source) {
    // InUse Prefixes are prefixes of mCachedAddresses which are active downstream addresses, last
    // downstream addresses(reserved for next time) and static addresses(e.g. bluetooth, wifi p2p).
    private boolean isDownstreamPrefixInUse(final IpPrefix prefix) {
        // This class always generates downstream prefixes with the same prefix length, so
        // This class always generates downstream prefixes with the same prefix length, so
        // prefixes cannot be contained in each other. They can only be equal to each other.
        // prefixes cannot be contained in each other. They can only be equal to each other.
        for (int i = 0; i < mCachedAddresses.size(); i++) {
            if (prefix.equals(asIpPrefix(mCachedAddresses.valueAt(i)))) return true;
        }

        // IpServer may use manually-defined address (mStaticIpv4ServerAddr) which does not include
        // in mCachedAddresses.
        for (IpServer downstream : mDownstreams) {
        for (IpServer downstream : mDownstreams) {
            final IpPrefix prefix = getDownstreamPrefix(downstream);
            final IpPrefix target = getDownstreamPrefix(downstream);
            if (source.equals(prefix)) return true;
            if (target == null) continue;

            if (isConflictPrefix(prefix, target)) return true;
        }
        }

        return false;
        return false;
    }
    }


@@ -258,7 +280,7 @@ public class PrivateAddressCoordinator {
        final LinkAddress address = downstream.getAddress();
        final LinkAddress address = downstream.getAddress();
        if (address == null) return null;
        if (address == null) return null;


        return PrefixUtils.asIpPrefix(address);
        return asIpPrefix(address);
    }
    }


    void dump(final IndentingPrintWriter pw) {
    void dump(final IndentingPrintWriter pw) {
@@ -268,11 +290,19 @@ public class PrivateAddressCoordinator {
            pw.println(mUpstreamPrefixMap.keyAt(i) + " - " + mUpstreamPrefixMap.valueAt(i));
            pw.println(mUpstreamPrefixMap.keyAt(i) + " - " + mUpstreamPrefixMap.valueAt(i));
        }
        }
        pw.decreaseIndent();
        pw.decreaseIndent();

        pw.println("mDownstreams:");
        pw.println("mDownstreams:");
        pw.increaseIndent();
        pw.increaseIndent();
        for (IpServer ipServer : mDownstreams) {
        for (IpServer ipServer : mDownstreams) {
            pw.println(ipServer.interfaceType() + " - " + ipServer.getAddress());
            pw.println(ipServer.interfaceType() + " - " + ipServer.getAddress());
        }
        }
        pw.decreaseIndent();
        pw.decreaseIndent();

        pw.println("mCachedAddresses:");
        pw.increaseIndent();
        for (int i = 0; i < mCachedAddresses.size(); i++) {
            pw.println(mCachedAddresses.keyAt(i) + " - " + mCachedAddresses.valueAt(i));
        }
        pw.decreaseIndent();
    }
    }
}
}
+11 −7
Original line number Original line Diff line number Diff line
@@ -47,6 +47,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doAnswer;
@@ -230,7 +231,8 @@ public class IpServerTest {
            dispatchTetherConnectionChanged(upstreamIface, lp, 0);
            dispatchTetherConnectionChanged(upstreamIface, lp, 0);
        }
        }
        reset(mNetd, mCallback, mAddressCoordinator);
        reset(mNetd, mCallback, mAddressCoordinator);
        when(mAddressCoordinator.requestDownstreamAddress(any())).thenReturn(mTestAddress);
        when(mAddressCoordinator.requestDownstreamAddress(any(), anyBoolean())).thenReturn(
                mTestAddress);
    }
    }


    private void setUpDhcpServer() throws Exception {
    private void setUpDhcpServer() throws Exception {
@@ -250,7 +252,8 @@ public class IpServerTest {
    @Before public void setUp() throws Exception {
    @Before public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
        MockitoAnnotations.initMocks(this);
        when(mSharedLog.forSubComponent(anyString())).thenReturn(mSharedLog);
        when(mSharedLog.forSubComponent(anyString())).thenReturn(mSharedLog);
        when(mAddressCoordinator.requestDownstreamAddress(any())).thenReturn(mTestAddress);
        when(mAddressCoordinator.requestDownstreamAddress(any(), anyBoolean())).thenReturn(
                mTestAddress);
        when(mTetherConfig.isBpfOffloadEnabled()).thenReturn(true /* default value */);
        when(mTetherConfig.isBpfOffloadEnabled()).thenReturn(true /* default value */);


        mBpfCoordinator = spy(new BpfCoordinator(
        mBpfCoordinator = spy(new BpfCoordinator(
@@ -372,7 +375,7 @@ public class IpServerTest {


        dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
        dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
        InOrder inOrder = inOrder(mCallback, mNetd, mAddressCoordinator);
        InOrder inOrder = inOrder(mCallback, mNetd, mAddressCoordinator);
        inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any());
        inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any(), eq(true));
        inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg ->
        inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg ->
                  IFACE_NAME.equals(cfg.ifName) && assertContainsFlag(cfg.flags, IF_STATE_UP)));
                  IFACE_NAME.equals(cfg.ifName) && assertContainsFlag(cfg.flags, IF_STATE_UP)));
        inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME);
        inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME);
@@ -393,7 +396,7 @@ public class IpServerTest {


        dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_LOCAL_ONLY);
        dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_LOCAL_ONLY);
        InOrder inOrder = inOrder(mCallback, mNetd, mAddressCoordinator);
        InOrder inOrder = inOrder(mCallback, mNetd, mAddressCoordinator);
        inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any());
        inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any(), eq(true));
        inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg ->
        inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg ->
                  IFACE_NAME.equals(cfg.ifName) && assertNotContainsFlag(cfg.flags, IF_STATE_UP)));
                  IFACE_NAME.equals(cfg.ifName) && assertNotContainsFlag(cfg.flags, IF_STATE_UP)));
        inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME);
        inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME);
@@ -607,7 +610,7 @@ public class IpServerTest {
        final ArgumentCaptor<LinkProperties> lpCaptor =
        final ArgumentCaptor<LinkProperties> lpCaptor =
                ArgumentCaptor.forClass(LinkProperties.class);
                ArgumentCaptor.forClass(LinkProperties.class);
        InOrder inOrder = inOrder(mNetd, mCallback, mAddressCoordinator);
        InOrder inOrder = inOrder(mNetd, mCallback, mAddressCoordinator);
        inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any());
        inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any(), eq(true));
        inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
        inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
        // One for ipv4 route, one for ipv6 link local route.
        // One for ipv4 route, one for ipv6 link local route.
        inOrder.verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME),
        inOrder.verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME),
@@ -620,11 +623,12 @@ public class IpServerTest {
        // Simulate the DHCP server receives DHCPDECLINE on MirrorLink and then signals
        // Simulate the DHCP server receives DHCPDECLINE on MirrorLink and then signals
        // onNewPrefixRequest callback.
        // onNewPrefixRequest callback.
        final LinkAddress newAddress = new LinkAddress("192.168.100.125/24");
        final LinkAddress newAddress = new LinkAddress("192.168.100.125/24");
        when(mAddressCoordinator.requestDownstreamAddress(any())).thenReturn(newAddress);
        when(mAddressCoordinator.requestDownstreamAddress(any(), anyBoolean())).thenReturn(
                newAddress);
        eventCallbacks.onNewPrefixRequest(new IpPrefix("192.168.42.0/24"));
        eventCallbacks.onNewPrefixRequest(new IpPrefix("192.168.42.0/24"));
        mLooper.dispatchAll();
        mLooper.dispatchAll();


        inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any());
        inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any(), eq(false));
        inOrder.verify(mNetd).tetherApplyDnsInterfaces();
        inOrder.verify(mNetd).tetherApplyDnsInterfaces();
        inOrder.verify(mCallback).updateLinkProperties(eq(mIpServer), lpCaptor.capture());
        inOrder.verify(mCallback).updateLinkProperties(eq(mIpServer), lpCaptor.capture());
        verifyNoMoreInteractions(mCallback);
        verifyNoMoreInteractions(mCallback);
+92 −70

File changed.

Preview size limit exceeded, changes collapsed.