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

Commit db6befbd authored by Mark Chien's avatar Mark Chien
Browse files

Tethering: ensure downstream prefix do not conflict with upstream

- Add New class PrivateAddressCoordinator to coordinate the private
  address conflict problem.
- Downstream prefix would be random in 192.168.0.0/24 ~
  192.168.255.0/24.
- If new upstream prefix is conflict with existing downstream prefix,
  downstream would be kicked out and it would request a new one.
- The last conflict upstream prefixes would be blacklist. Avoid to
select downstream prefix which is conflict with prefixes in blacklist.

Bug: 130879722
Test: -build, flash, boot
      -atest TetheringTests

Merged-In: Ib45b87bcd9eeb5da03fb7ec90b1af9ca53998cf5
Change-Id: Ib45b87bcd9eeb5da03fb7ec90b1af9ca53998cf5
parent 7d4bad92
Loading
Loading
Loading
Loading
+80 −89
Original line number Original line Diff line number Diff line
@@ -16,14 +16,13 @@


package android.net.ip;
package android.net.ip;


import static android.net.InetAddresses.parseNumericAddress;
import static android.net.RouteInfo.RTN_UNICAST;
import static android.net.RouteInfo.RTN_UNICAST;
import static android.net.TetheringManager.TetheringRequest.checkStaticAddressConfiguration;
import static android.net.TetheringManager.TetheringRequest.checkStaticAddressConfiguration;
import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH;
import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH;
import static android.net.util.NetworkConstants.FF;
import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH;
import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH;
import static android.net.util.NetworkConstants.asByte;
import static android.net.util.NetworkConstants.asByte;
import static android.net.util.PrefixUtils.asIpPrefix;
import static android.net.util.TetheringMessageBase.BASE_IPSERVER;
import static android.net.util.TetheringMessageBase.BASE_IPSERVER;
import static android.system.OsConstants.RT_SCOPE_UNIVERSE;
import static android.system.OsConstants.RT_SCOPE_UNIVERSE;


@@ -66,11 +65,11 @@ import androidx.annotation.Nullable;
import com.android.internal.util.MessageUtils;
import com.android.internal.util.MessageUtils;
import com.android.internal.util.State;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.internal.util.StateMachine;
import com.android.networkstack.tethering.PrivateAddressCoordinator;


import java.io.IOException;
import java.io.IOException;
import java.net.Inet4Address;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.NetworkInterface;
import java.net.UnknownHostException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.ArrayList;
@@ -108,27 +107,8 @@ public class IpServer extends StateMachine {


    private static final byte DOUG_ADAMS = (byte) 42;
    private static final byte DOUG_ADAMS = (byte) 42;


    private static final String USB_NEAR_IFACE_ADDR = "192.168.42.129";
    private static final int USB_PREFIX_LENGTH = 24;
    private static final String WIFI_HOST_IFACE_ADDR = "192.168.43.1";
    private static final int WIFI_HOST_IFACE_PREFIX_LENGTH = 24;
    private static final String WIFI_P2P_IFACE_ADDR = "192.168.49.1";
    private static final int WIFI_P2P_IFACE_PREFIX_LENGTH = 24;
    private static final String ETHERNET_IFACE_ADDR = "192.168.50.1";
    private static final int ETHERNET_IFACE_PREFIX_LENGTH = 24;

    // TODO: remove this constant after introducing PrivateAddressCoordinator.
    private static final List<IpPrefix> NCM_PREFIXES = Collections.unmodifiableList(
            Arrays.asList(
                    new IpPrefix("192.168.42.0/24"),
                    new IpPrefix("192.168.51.0/24"),
                    new IpPrefix("192.168.52.0/24"),
                    new IpPrefix("192.168.53.0/24")
    ));

    // TODO: have PanService use some visible version of this constant
    // TODO: have PanService use some visible version of this constant
    private static final String BLUETOOTH_IFACE_ADDR = "192.168.44.1";
    private static final String BLUETOOTH_IFACE_ADDR = "192.168.44.1/24";
    private static final int BLUETOOTH_DHCP_PREFIX_LENGTH = 24;


    // TODO: have this configurable
    // TODO: have this configurable
    private static final int DHCP_LEASE_TIME_SECS = 3600;
    private static final int DHCP_LEASE_TIME_SECS = 3600;
@@ -167,6 +147,14 @@ public class IpServer extends StateMachine {
         * Notify that the DHCP leases changed in one of the IpServers.
         * Notify that the DHCP leases changed in one of the IpServers.
         */
         */
        public void dhcpLeasesChanged() { }
        public void dhcpLeasesChanged() { }

        /**
         * Request Tethering change.
         *
         * @param tetheringType the downstream type of this IpServer.
         * @param enabled enable or disable tethering.
         */
        public void requestEnableTethering(int tetheringType, boolean enabled) { }
    }
    }


    /** Capture IpServer dependencies, for injection. */
    /** Capture IpServer dependencies, for injection. */
@@ -196,6 +184,7 @@ public class IpServer extends StateMachine {
                return 0;
                return 0;
            }
            }
        }
        }

        /** Create a DhcpServer instance to be used by IpServer. */
        /** Create a DhcpServer instance to be used by IpServer. */
        public abstract void makeDhcpServer(String ifName, DhcpServingParamsParcel params,
        public abstract void makeDhcpServer(String ifName, DhcpServingParamsParcel params,
                DhcpServerCallbacks cb);
                DhcpServerCallbacks cb);
@@ -225,16 +214,20 @@ public class IpServer extends StateMachine {
    public static final int CMD_NEIGHBOR_EVENT              = BASE_IPSERVER + 11;
    public static final int CMD_NEIGHBOR_EVENT              = BASE_IPSERVER + 11;
    // request from DHCP server that it wants to have a new prefix
    // request from DHCP server that it wants to have a new prefix
    public static final int CMD_NEW_PREFIX_REQUEST          = BASE_IPSERVER + 12;
    public static final int CMD_NEW_PREFIX_REQUEST          = BASE_IPSERVER + 12;
    // request from PrivateAddressCoordinator to restart tethering.
    public static final int CMD_NOTIFY_PREFIX_CONFLICT      = BASE_IPSERVER + 13;


    private final State mInitialState;
    private final State mInitialState;
    private final State mLocalHotspotState;
    private final State mLocalHotspotState;
    private final State mTetheredState;
    private final State mTetheredState;
    private final State mUnavailableState;
    private final State mUnavailableState;
    private final State mWaitingForRestartState;


    private final SharedLog mLog;
    private final SharedLog mLog;
    private final INetd mNetd;
    private final INetd mNetd;
    private final Callback mCallback;
    private final Callback mCallback;
    private final InterfaceController mInterfaceCtrl;
    private final InterfaceController mInterfaceCtrl;
    private final PrivateAddressCoordinator mPrivateAddressCoordinator;


    private final String mIfaceName;
    private final String mIfaceName;
    private final int mInterfaceType;
    private final int mInterfaceType;
@@ -261,7 +254,6 @@ public class IpServer extends StateMachine {
    private int mDhcpServerStartIndex = 0;
    private int mDhcpServerStartIndex = 0;
    private IDhcpServer mDhcpServer;
    private IDhcpServer mDhcpServer;
    private RaParams mLastRaParams;
    private RaParams mLastRaParams;
    private LinkAddress mIpv4Address;


    private LinkAddress mStaticIpv4ServerAddr;
    private LinkAddress mStaticIpv4ServerAddr;
    private LinkAddress mStaticIpv4ClientAddr;
    private LinkAddress mStaticIpv4ClientAddr;
@@ -316,12 +308,14 @@ public class IpServer extends StateMachine {


    private final IpNeighborMonitor mIpNeighborMonitor;
    private final IpNeighborMonitor mIpNeighborMonitor;


    private LinkAddress mIpv4Address;

    // TODO: Add a dependency object to pass the data members or variables from the tethering
    // TODO: Add a dependency object to pass the data members or variables from the tethering
    // object. It helps to reduce the arguments of the constructor.
    // object. It helps to reduce the arguments of the constructor.
    public IpServer(
    public IpServer(
            String ifaceName, Looper looper, int interfaceType, SharedLog log,
            String ifaceName, Looper looper, int interfaceType, SharedLog log,
            INetd netd, Callback callback, boolean usingLegacyDhcp, boolean usingBpfOffload,
            INetd netd, Callback callback, boolean usingLegacyDhcp, boolean usingBpfOffload,
            Dependencies deps) {
            PrivateAddressCoordinator addressCoordinator, Dependencies deps) {
        super(ifaceName, looper);
        super(ifaceName, looper);
        mLog = log.forSubComponent(ifaceName);
        mLog = log.forSubComponent(ifaceName);
        mNetd = netd;
        mNetd = netd;
@@ -332,6 +326,7 @@ public class IpServer extends StateMachine {
        mLinkProperties = new LinkProperties();
        mLinkProperties = new LinkProperties();
        mUsingLegacyDhcp = usingLegacyDhcp;
        mUsingLegacyDhcp = usingLegacyDhcp;
        mUsingBpfOffload = usingBpfOffload;
        mUsingBpfOffload = usingBpfOffload;
        mPrivateAddressCoordinator = addressCoordinator;
        mDeps = deps;
        mDeps = deps;
        resetLinkProperties();
        resetLinkProperties();
        mLastError = TetheringManager.TETHER_ERROR_NO_ERROR;
        mLastError = TetheringManager.TETHER_ERROR_NO_ERROR;
@@ -352,9 +347,11 @@ public class IpServer extends StateMachine {
        mLocalHotspotState = new LocalHotspotState();
        mLocalHotspotState = new LocalHotspotState();
        mTetheredState = new TetheredState();
        mTetheredState = new TetheredState();
        mUnavailableState = new UnavailableState();
        mUnavailableState = new UnavailableState();
        mWaitingForRestartState = new WaitingForRestartState();
        addState(mInitialState);
        addState(mInitialState);
        addState(mLocalHotspotState);
        addState(mLocalHotspotState);
        addState(mTetheredState);
        addState(mTetheredState);
        addState(mWaitingForRestartState, mTetheredState);
        addState(mUnavailableState);
        addState(mUnavailableState);


        setInitialState(mInitialState);
        setInitialState(mInitialState);
@@ -387,6 +384,11 @@ public class IpServer extends StateMachine {
        return new LinkProperties(mLinkProperties);
        return new LinkProperties(mLinkProperties);
    }
    }


    /** The address which IpServer is using. */
    public LinkAddress getAddress() {
        return mIpv4Address;
    }

    /**
    /**
     * Get the latest list of DHCP leases that was reported. Must be called on the IpServer looper
     * Get the latest list of DHCP leases that was reported. Must be called on the IpServer looper
     * thread.
     * thread.
@@ -617,6 +619,7 @@ public class IpServer extends StateMachine {
        // NOTE: All of configureIPv4() will be refactored out of existence
        // NOTE: All of configureIPv4() will be refactored out of existence
        // into calls to InterfaceController, shared with startIPv4().
        // into calls to InterfaceController, shared with startIPv4().
        mInterfaceCtrl.clearIPv4Address();
        mInterfaceCtrl.clearIPv4Address();
        mPrivateAddressCoordinator.releaseDownstream(this);
        mIpv4Address = null;
        mIpv4Address = null;
        mStaticIpv4ServerAddr = null;
        mStaticIpv4ServerAddr = null;
        mStaticIpv4ClientAddr = null;
        mStaticIpv4ClientAddr = null;
@@ -625,42 +628,23 @@ public class IpServer extends StateMachine {
    private boolean configureIPv4(boolean enabled) {
    private boolean configureIPv4(boolean enabled) {
        if (VDBG) Log.d(TAG, "configureIPv4(" + enabled + ")");
        if (VDBG) Log.d(TAG, "configureIPv4(" + enabled + ")");


        // TODO: Replace this hard-coded information with dynamically selected
        if (enabled) {
        // config passed down to us by a higher layer IP-coordinating element.
            mIpv4Address = requestIpv4Address();
        final Inet4Address srvAddr;
        }
        int prefixLen = 0;

        try {
        if (mIpv4Address == null) {
            if (mStaticIpv4ServerAddr != null) {
            mLog.e("No available ipv4 address");
                srvAddr = (Inet4Address) mStaticIpv4ServerAddr.getAddress();
            return false;
                prefixLen = mStaticIpv4ServerAddr.getPrefixLength();
        }
            } else if (mInterfaceType == TetheringManager.TETHERING_USB

                    || mInterfaceType == TetheringManager.TETHERING_NCM) {
        if (mInterfaceType == TetheringManager.TETHERING_BLUETOOTH) {
                srvAddr = (Inet4Address) parseNumericAddress(USB_NEAR_IFACE_ADDR);
                prefixLen = USB_PREFIX_LENGTH;
            } else if (mInterfaceType == TetheringManager.TETHERING_WIFI) {
                srvAddr = (Inet4Address) parseNumericAddress(getRandomWifiIPv4Address());
                prefixLen = WIFI_HOST_IFACE_PREFIX_LENGTH;
            } else if (mInterfaceType == TetheringManager.TETHERING_WIFI_P2P) {
                srvAddr = (Inet4Address) parseNumericAddress(WIFI_P2P_IFACE_ADDR);
                prefixLen = WIFI_P2P_IFACE_PREFIX_LENGTH;
            } else if (mInterfaceType == TetheringManager.TETHERING_ETHERNET) {
                // TODO: randomize address for tethering too, similarly to wifi
                srvAddr = (Inet4Address) parseNumericAddress(ETHERNET_IFACE_ADDR);
                prefixLen = ETHERNET_IFACE_PREFIX_LENGTH;
            } else {
            // BT configures the interface elsewhere: only start DHCP.
            // BT configures the interface elsewhere: only start DHCP.
            // TODO: make all tethering types behave the same way, and delete the bluetooth
            // TODO: make all tethering types behave the same way, and delete the bluetooth
            // code that calls into NetworkManagementService directly.
            // code that calls into NetworkManagementService directly.
                srvAddr = (Inet4Address) parseNumericAddress(BLUETOOTH_IFACE_ADDR);
                mIpv4Address = new LinkAddress(srvAddr, BLUETOOTH_DHCP_PREFIX_LENGTH);
            return configureDhcp(enabled, mIpv4Address, null /* clientAddress */);
            return configureDhcp(enabled, mIpv4Address, null /* clientAddress */);
        }
        }
            mIpv4Address = new LinkAddress(srvAddr, prefixLen);

        } catch (IllegalArgumentException e) {
        final IpPrefix ipv4Prefix = asIpPrefix(mIpv4Address);
            mLog.e("Error selecting ipv4 address", e);
            if (!enabled) stopDhcp();
            return false;
        }


        final Boolean setIfaceUp;
        final Boolean setIfaceUp;
        if (mInterfaceType == TetheringManager.TETHERING_WIFI
        if (mInterfaceType == TetheringManager.TETHERING_WIFI
@@ -688,21 +672,14 @@ public class IpServer extends StateMachine {
        return configureDhcp(enabled, mIpv4Address, mStaticIpv4ClientAddr);
        return configureDhcp(enabled, mIpv4Address, mStaticIpv4ClientAddr);
    }
    }


    private Inet4Address getRandomIPv4Address(@NonNull final byte[] rawAddr) {
    private LinkAddress requestIpv4Address() {
        final byte[] ipv4Addr = rawAddr;
        if (mStaticIpv4ServerAddr != null) return mStaticIpv4ServerAddr;
        ipv4Addr[3] = getRandomSanitizedByte(DOUG_ADAMS, asByte(0), asByte(1), FF);

        try {
        if (mInterfaceType == TetheringManager.TETHERING_BLUETOOTH) {
            return (Inet4Address) InetAddress.getByAddress(ipv4Addr);
            return new LinkAddress(BLUETOOTH_IFACE_ADDR);
        } catch (UnknownHostException e) {
            mLog.e("Failed to construct Inet4Address from raw IPv4 addr");
            return null;
        }
        }
        }


    private String getRandomWifiIPv4Address() {
        return mPrivateAddressCoordinator.requestDownstreamAddress(this);
        final Inet4Address ipv4Addr =
                getRandomIPv4Address(parseNumericAddress(WIFI_HOST_IFACE_ADDR).getAddress());
        return ipv4Addr != null ? ipv4Addr.getHostAddress() : WIFI_HOST_IFACE_ADDR;
    }
    }


    private boolean startIPv6() {
    private boolean startIPv6() {
@@ -978,19 +955,6 @@ public class IpServer extends StateMachine {
        }
        }
    }
    }


    // TODO: call PrivateAddressCoordinator.requestDownstreamAddress instead of this temporary
    // logic.
    private Inet4Address requestDownstreamAddress(@NonNull final IpPrefix currentPrefix) {
        final int oldIndex = NCM_PREFIXES.indexOf(currentPrefix);
        if (oldIndex == -1) {
            mLog.e("current prefix isn't supported for NCM link: " + currentPrefix);
            return null;
        }

        final IpPrefix newPrefix = NCM_PREFIXES.get((oldIndex + 1) % NCM_PREFIXES.size());
        return getRandomIPv4Address(newPrefix.getRawAddress());
    }

    private void handleNewPrefixRequest(@NonNull final IpPrefix currentPrefix) {
    private void handleNewPrefixRequest(@NonNull final IpPrefix currentPrefix) {
        if (!currentPrefix.contains(mIpv4Address.getAddress())
        if (!currentPrefix.contains(mIpv4Address.getAddress())
                || currentPrefix.getPrefixLength() != mIpv4Address.getPrefixLength()) {
                || currentPrefix.getPrefixLength() != mIpv4Address.getPrefixLength()) {
@@ -999,12 +963,12 @@ public class IpServer extends StateMachine {
        }
        }


        final LinkAddress deprecatedLinkAddress = mIpv4Address;
        final LinkAddress deprecatedLinkAddress = mIpv4Address;
        final Inet4Address srvAddr = requestDownstreamAddress(currentPrefix);
        mIpv4Address = requestIpv4Address();
        if (srvAddr == null) {
        if (mIpv4Address == null) {
            mLog.e("Fail to request a new downstream prefix");
            mLog.e("Fail to request a new downstream prefix");
            return;
            return;
        }
        }
        mIpv4Address = new LinkAddress(srvAddr, currentPrefix.getPrefixLength());
        final Inet4Address srvAddr = (Inet4Address) mIpv4Address.getAddress();


        // Add new IPv4 address on the interface.
        // Add new IPv4 address on the interface.
        if (!mInterfaceCtrl.addAddress(srvAddr, currentPrefix.getPrefixLength())) {
        if (!mInterfaceCtrl.addAddress(srvAddr, currentPrefix.getPrefixLength())) {
@@ -1162,7 +1126,7 @@ public class IpServer extends StateMachine {
            }
            }


            try {
            try {
                NetdUtils.tetherInterface(mNetd, mIfaceName, PrefixUtils.asIpPrefix(mIpv4Address));
                NetdUtils.tetherInterface(mNetd, mIfaceName, asIpPrefix(mIpv4Address));
            } catch (RemoteException | ServiceSpecificException | IllegalStateException e) {
            } catch (RemoteException | ServiceSpecificException | IllegalStateException e) {
                mLog.e("Error Tethering", e);
                mLog.e("Error Tethering", e);
                mLastError = TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR;
                mLastError = TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR;
@@ -1222,6 +1186,11 @@ public class IpServer extends StateMachine {
                case CMD_NEW_PREFIX_REQUEST:
                case CMD_NEW_PREFIX_REQUEST:
                    handleNewPrefixRequest((IpPrefix) message.obj);
                    handleNewPrefixRequest((IpPrefix) message.obj);
                    break;
                    break;
                case CMD_NOTIFY_PREFIX_CONFLICT:
                    mLog.i("restart tethering: " + mInterfaceType);
                    mCallback.requestEnableTethering(mInterfaceType, false /* enabled */);
                    transitionTo(mWaitingForRestartState);
                    break;
                default:
                default:
                    return false;
                    return false;
            }
            }
@@ -1403,6 +1372,28 @@ public class IpServer extends StateMachine {
        }
        }
    }
    }


    class WaitingForRestartState extends State {
        @Override
        public boolean processMessage(Message message) {
            logMessage(this, message.what);
            switch (message.what) {
                case CMD_TETHER_UNREQUESTED:
                    transitionTo(mInitialState);
                    mLog.i("Untethered (unrequested) and restarting " + mIfaceName);
                    mCallback.requestEnableTethering(mInterfaceType, true /* enabled */);
                    break;
                case CMD_INTERFACE_DOWN:
                    transitionTo(mUnavailableState);
                    mLog.i("Untethered (interface down) and restarting" + mIfaceName);
                    mCallback.requestEnableTethering(mInterfaceType, true /* enabled */);
                    break;
                default:
                    return false;
            }
            return true;
        }
    }

    // Accumulate routes representing "prefixes to be assigned to the local
    // Accumulate routes representing "prefixes to be assigned to the local
    // interface", for subsequent modification of local_network routing.
    // interface", for subsequent modification of local_network routing.
    private static ArrayList<RouteInfo> getLocalRoutesFor(
    private static ArrayList<RouteInfo> getLocalRoutesFor(
+254 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2020 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.networkstack.tethering;

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
import android.net.ip.IpServer;
import android.net.util.PrefixUtils;
import android.util.ArrayMap;
import android.util.ArraySet;

import androidx.annotation.Nullable;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;

/**
 * This class coordinate IP addresses conflict problem.
 *
 * Tethering downstream IP addresses may conflict with network assigned addresses. This
 * coordinator is responsible for recording all of network assigned addresses and dispatched
 * free address to downstream interfaces.
 *
 * This class is not thread-safe and should be accessed on the same tethering internal thread.
 * @hide
 */
public class PrivateAddressCoordinator {
    public static final int PREFIX_LENGTH = 24;

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

    // Upstream monitor would be stopped when tethering is down. When tethering restart, downstream
    // address may be requested before coordinator get current upstream notification. To ensure
    // coordinator do not select conflict downstream prefix, mUpstreamPrefixMap would not be cleared
    // when tethering is down. Instead coordinator would remove all depcreted upstreams from
    // mUpstreamPrefixMap when tethering is starting. See #maybeRemoveDeprectedUpstreams().
    private final ArrayMap<Network, List<IpPrefix>> mUpstreamPrefixMap;
    private final ArraySet<IpServer> mDownstreams;
    // IANA has reserved the following three blocks of the IP address space for private intranets:
    // 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16
    // 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 final IpPrefix mTetheringPrefix;
    private final ConnectivityManager mConnectivityMgr;

    public PrivateAddressCoordinator(Context context) {
        mDownstreams = new ArraySet<>();
        mUpstreamPrefixMap = new ArrayMap<>();
        mTetheringPrefix = new IpPrefix(DEFAULT_TETHERING_PREFIX);
        mConnectivityMgr = (ConnectivityManager) context.getSystemService(
                Context.CONNECTIVITY_SERVICE);
    }

    /**
     * Record a new upstream IpPrefix which may conflict with tethering downstreams.
     * The downstreams will be notified if a conflict is found.
     */
    public void updateUpstreamPrefix(final Network network, final LinkProperties lp) {
        final ArrayList<IpPrefix> ipv4Prefixes = getIpv4Prefixes(lp.getAllLinkAddresses());
        if (ipv4Prefixes.isEmpty()) {
            removeUpstreamPrefix(network);
            return;
        }

        mUpstreamPrefixMap.put(network, ipv4Prefixes);
        handleMaybePrefixConflict(ipv4Prefixes);
    }

    private ArrayList<IpPrefix> getIpv4Prefixes(final List<LinkAddress> linkAddresses) {
        final ArrayList<IpPrefix> list = new ArrayList<>();
        for (LinkAddress address : linkAddresses) {
            if (!address.isIpv4()) continue;

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

        return list;
    }

    private void handleMaybePrefixConflict(final List<IpPrefix> prefixes) {
        for (IpServer downstream : mDownstreams) {
            final IpPrefix target = getDownstreamPrefix(downstream);
            if (target == null) continue;

            for (IpPrefix source : prefixes) {
                if (isConflictPrefix(source, target)) {
                    downstream.sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
                    break;
                }
            }
        }
    }

    /** Remove IpPrefix records corresponding to input network. */
    public void removeUpstreamPrefix(final Network network) {
        mUpstreamPrefixMap.remove(network);
    }

    private void maybeRemoveDeprectedUpstreams() {
        if (!mDownstreams.isEmpty() || mUpstreamPrefixMap.isEmpty()) return;

        final ArrayList<Network> toBeRemoved = new ArrayList<>();
        List<Network> allNetworks = Arrays.asList(mConnectivityMgr.getAllNetworks());
        for (int i = 0; i < mUpstreamPrefixMap.size(); i++) {
            final Network network = mUpstreamPrefixMap.keyAt(i);
            if (!allNetworks.contains(network)) toBeRemoved.add(network);
        }

        mUpstreamPrefixMap.removeAll(toBeRemoved);
    }

    /**
     * Pick a random available address and mark its prefix as in use for the provided IpServer,
     * returns null if there is no available address.
     */
    @Nullable
    public LinkAddress requestDownstreamAddress(final IpServer ipServer) {
        maybeRemoveDeprectedUpstreams();

        // Address would be 192.168.[subAddress]/24.
        final byte[] bytes = mTetheringPrefix.getRawAddress();
        final int subAddress = getRandomSubAddr();
        final int subNet = (subAddress >> 8) & BYTE_MASK;
        bytes[3] = getSanitizedAddressSuffix(subAddress, (byte) 0, (byte) 1, (byte) 0xff);
        for (int i = 0; i < MAX_UBYTE; i++) {
            final int newSubNet = (subNet + i) & BYTE_MASK;
            if (newSubNet == BLUETOOTH_RESERVED) continue;

            bytes[2] = (byte) newSubNet;
            final InetAddress addr;
            try {
                addr = InetAddress.getByAddress(bytes);
            } catch (UnknownHostException e) {
                throw new IllegalStateException("Invalid address, shouldn't happen.", e);
            }

            final IpPrefix prefix = new IpPrefix(addr, PREFIX_LENGTH);
            // 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);
            return new LinkAddress(addr, PREFIX_LENGTH);
        }

        // No available address.
        return null;
    }

    /** Get random sub address value. Return value is in 0 ~ 0xffff. */
    @VisibleForTesting
    public int getRandomSubAddr() {
        return ((new Random()).nextInt()) & 0xffff; // subNet is in 0 ~ 0xffff.
    }

    private byte getSanitizedAddressSuffix(final int source, byte... excluded) {
        final byte subId = (byte) (source & BYTE_MASK);
        for (byte value : excluded) {
            if (subId == value) return DEFAULT_ID;
        }

        return subId;
    }

    /** Release downstream record for IpServer. */
    public void releaseDownstream(final IpServer ipServer) {
        mDownstreams.remove(ipServer);
    }

    /** Clear current upstream prefixes records. */
    public void clearUpstreamPrefixes() {
        mUpstreamPrefixMap.clear();
    }

    private boolean isConflictWithUpstream(final IpPrefix source) {
        for (int i = 0; i < mUpstreamPrefixMap.size(); i++) {
            final List<IpPrefix> list = mUpstreamPrefixMap.valueAt(i);
            for (IpPrefix target : list) {
                if (isConflictPrefix(source, target)) return true;
            }
        }
        return false;
    }

    private boolean isConflictPrefix(final IpPrefix prefix1, final IpPrefix prefix2) {
        if (prefix2.getPrefixLength() < prefix1.getPrefixLength()) {
            return prefix2.contains(prefix1.getAddress());
        }

        return prefix1.contains(prefix2.getAddress());
    }

    private boolean isDownstreamPrefixInUse(final IpPrefix source) {
        // 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.
        for (IpServer downstream : mDownstreams) {
            final IpPrefix prefix = getDownstreamPrefix(downstream);
            if (source.equals(prefix)) return true;
        }
        return false;
    }

    private IpPrefix getDownstreamPrefix(final IpServer downstream) {
        final LinkAddress address = downstream.getAddress();
        if (address == null) return null;

        return PrefixUtils.asIpPrefix(address);
    }

    void dump(final IndentingPrintWriter pw) {
        pw.decreaseIndent();
        pw.println("mUpstreamPrefixMap:");
        pw.increaseIndent();
        for (int i = 0; i < mUpstreamPrefixMap.size(); i++) {
            pw.println(mUpstreamPrefixMap.keyAt(i) + " - " + mUpstreamPrefixMap.valueAt(i));
        }
        pw.decreaseIndent();
        pw.println("mDownstreams:");
        pw.increaseIndent();
        for (IpServer ipServer : mDownstreams) {
            pw.println(ipServer.interfaceType() + " - " + ipServer.getAddress());
        }
        pw.decreaseIndent();
    }
}
+31 −1

File changed.

Preview size limit exceeded, changes collapsed.

+43 −24

File changed.

Preview size limit exceeded, changes collapsed.

+254 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading