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

Commit 9ce81fa0 authored by Anton Hansson's avatar Anton Hansson
Browse files

Merge 3ec3b3 into goog/master

No conflict.

Test: m
Change-Id: Ia6b926fabada16f4939366ff1ddc20c31f948e57
parents e7c5a2ad 3ec3b3ba
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -81,12 +81,12 @@ java_defaults {
    name: "NetworkStackAndroidLibraryDefaults",
    srcs: [
        ":framework-networkstack-shared-srcs",
        ":services-networkstack-shared-srcs",
        ":statslog-networkstack-java-gen",
    ],
    static_libs: [
        "androidx.annotation_annotation",
        "netd_aidl_interface-V2-java",
        "netlink-client",
        "networkstack-client",
        "datastallprotosnano",
        "networkstackprotosnano",
+193 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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 android.net.ip;

import android.net.INetd;
import android.net.InterfaceConfigurationParcel;
import android.net.LinkAddress;
import android.net.util.SharedLog;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.system.OsConstants;

import java.net.Inet4Address;
import java.net.InetAddress;


/**
 * Encapsulates the multiple IP configuration operations performed on an interface.
 *
 * TODO: refactor/eliminate the redundant ways to set and clear addresses.
 *
 * @hide
 */
public class InterfaceController {
    private final static boolean DBG = false;

    private final String mIfName;
    private final INetd mNetd;
    private final SharedLog mLog;

    public InterfaceController(String ifname, INetd netd, SharedLog log) {
        mIfName = ifname;
        mNetd = netd;
        mLog = log;
    }

    private boolean setInterfaceAddress(LinkAddress addr) {
        final InterfaceConfigurationParcel ifConfig = new InterfaceConfigurationParcel();
        ifConfig.ifName = mIfName;
        ifConfig.ipv4Addr = addr.getAddress().getHostAddress();
        ifConfig.prefixLength = addr.getPrefixLength();
        ifConfig.hwAddr = "";
        ifConfig.flags = new String[0];
        try {
            mNetd.interfaceSetCfg(ifConfig);
        } catch (RemoteException | ServiceSpecificException e) {
            logError("Setting IPv4 address to %s/%d failed: %s",
                    ifConfig.ipv4Addr, ifConfig.prefixLength, e);
            return false;
        }
        return true;
    }

    /**
     * Set the IPv4 address of the interface.
     */
    public boolean setIPv4Address(LinkAddress address) {
        if (!(address.getAddress() instanceof Inet4Address)) {
            return false;
        }
        return setInterfaceAddress(address);
    }

    /**
     * Clear the IPv4Address of the interface.
     */
    public boolean clearIPv4Address() {
        return setInterfaceAddress(new LinkAddress("0.0.0.0/0"));
    }

    private boolean setEnableIPv6(boolean enabled) {
        try {
            mNetd.interfaceSetEnableIPv6(mIfName, enabled);
        } catch (RemoteException | ServiceSpecificException e) {
            logError("%s IPv6 failed: %s", (enabled ? "enabling" : "disabling"), e);
            return false;
        }
        return true;
    }

    /**
     * Enable IPv6 on the interface.
     */
    public boolean enableIPv6() {
        return setEnableIPv6(true);
    }

    /**
     * Disable IPv6 on the interface.
     */
    public boolean disableIPv6() {
        return setEnableIPv6(false);
    }

    /**
     * Enable or disable IPv6 privacy extensions on the interface.
     * @param enabled Whether the extensions should be enabled.
     */
    public boolean setIPv6PrivacyExtensions(boolean enabled) {
        try {
            mNetd.interfaceSetIPv6PrivacyExtensions(mIfName, enabled);
        } catch (RemoteException | ServiceSpecificException e) {
            logError("error %s IPv6 privacy extensions: %s",
                    (enabled ? "enabling" : "disabling"), e);
            return false;
        }
        return true;
    }

    /**
     * Set IPv6 address generation mode on the interface.
     *
     * <p>IPv6 should be disabled before changing the mode.
     */
    public boolean setIPv6AddrGenModeIfSupported(int mode) {
        try {
            mNetd.setIPv6AddrGenMode(mIfName, mode);
        } catch (RemoteException e) {
            logError("Unable to set IPv6 addrgen mode: %s", e);
            return false;
        } catch (ServiceSpecificException e) {
            if (e.errorCode != OsConstants.EOPNOTSUPP) {
                logError("Unable to set IPv6 addrgen mode: %s", e);
                return false;
            }
        }
        return true;
    }

    /**
     * Add an address to the interface.
     */
    public boolean addAddress(LinkAddress addr) {
        return addAddress(addr.getAddress(), addr.getPrefixLength());
    }

    /**
     * Add an address to the interface.
     */
    public boolean addAddress(InetAddress ip, int prefixLen) {
        try {
            mNetd.interfaceAddAddress(mIfName, ip.getHostAddress(), prefixLen);
        } catch (ServiceSpecificException | RemoteException e) {
            logError("failed to add %s/%d: %s", ip, prefixLen, e);
            return false;
        }
        return true;
    }

    /**
     * Remove an address from the interface.
     */
    public boolean removeAddress(InetAddress ip, int prefixLen) {
        try {
            mNetd.interfaceDelAddress(mIfName, ip.getHostAddress(), prefixLen);
        } catch (ServiceSpecificException | RemoteException e) {
            logError("failed to remove %s/%d: %s", ip, prefixLen, e);
            return false;
        }
        return true;
    }

    /**
     * Remove all addresses from the interface.
     */
    public boolean clearAllAddresses() {
        try {
            mNetd.interfaceClearAddrs(mIfName);
        } catch (Exception e) {
            logError("Failed to clear addresses: %s", e);
            return false;
        }
        return true;
    }

    private void logError(String fmt, Object... args) {
        mLog.e(String.format(fmt, args));
    }
}
+240 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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 android.net.shared;

import static android.net.shared.ParcelableUtil.fromParcelableArray;
import static android.net.shared.ParcelableUtil.toParcelableArray;
import static android.text.TextUtils.join;

import android.net.InetAddresses;
import android.net.InitialConfigurationParcelable;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.RouteInfo;

import java.net.Inet4Address;
import java.net.InetAddress;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;

/** @hide */
public class InitialConfiguration {
    public final Set<LinkAddress> ipAddresses = new HashSet<>();
    public final Set<IpPrefix> directlyConnectedRoutes = new HashSet<>();
    public final Set<InetAddress> dnsServers = new HashSet<>();

    private static final int RFC6177_MIN_PREFIX_LENGTH = 48;
    private static final int RFC7421_PREFIX_LENGTH = 64;

    public static final InetAddress INET6_ANY = InetAddresses.parseNumericAddress("::");

    /**
     * Create a InitialConfiguration that is a copy of the specified configuration.
     */
    public static InitialConfiguration copy(InitialConfiguration config) {
        if (config == null) {
            return null;
        }
        InitialConfiguration configCopy = new InitialConfiguration();
        configCopy.ipAddresses.addAll(config.ipAddresses);
        configCopy.directlyConnectedRoutes.addAll(config.directlyConnectedRoutes);
        configCopy.dnsServers.addAll(config.dnsServers);
        return configCopy;
    }

    @Override
    public String toString() {
        return String.format(
                "InitialConfiguration(IPs: {%s}, prefixes: {%s}, DNS: {%s})",
                join(", ", ipAddresses), join(", ", directlyConnectedRoutes),
                join(", ", dnsServers));
    }

    /**
     * Tests whether the contents of this IpConfiguration represent a valid configuration.
     */
    public boolean isValid() {
        if (ipAddresses.isEmpty()) {
            return false;
        }

        // For every IP address, there must be at least one prefix containing that address.
        for (LinkAddress addr : ipAddresses) {
            if (!any(directlyConnectedRoutes, (p) -> p.contains(addr.getAddress()))) {
                return false;
            }
        }
        // For every dns server, there must be at least one prefix containing that address.
        for (InetAddress addr : dnsServers) {
            if (!any(directlyConnectedRoutes, (p) -> p.contains(addr))) {
                return false;
            }
        }
        // All IPv6 LinkAddresses have an RFC7421-suitable prefix length
        // (read: compliant with RFC4291#section2.5.4).
        if (any(ipAddresses, not(InitialConfiguration::isPrefixLengthCompliant))) {
            return false;
        }
        // If directlyConnectedRoutes contains an IPv6 default route
        // then ipAddresses MUST contain at least one non-ULA GUA.
        if (any(directlyConnectedRoutes, InitialConfiguration::isIPv6DefaultRoute)
                && all(ipAddresses, not(InitialConfiguration::isIPv6GUA))) {
            return false;
        }
        // The prefix length of routes in directlyConnectedRoutes be within reasonable
        // bounds for IPv6: /48-/64 just as we’d accept in RIOs.
        if (any(directlyConnectedRoutes, not(InitialConfiguration::isPrefixLengthCompliant))) {
            return false;
        }
        // There no more than one IPv4 address
        if (ipAddresses.stream().filter(InitialConfiguration::isIPv4).count() > 1) {
            return false;
        }

        return true;
    }

    /**
     * @return true if the given list of addressess and routes satisfies provisioning for this
     * InitialConfiguration. LinkAddresses and RouteInfo objects are not compared with equality
     * because addresses and routes seen by Netlink will contain additional fields like flags,
     * interfaces, and so on. If this InitialConfiguration has no IP address specified, the
     * provisioning check always fails.
     *
     * If the given list of routes is null, only addresses are taken into considerations.
     */
    public boolean isProvisionedBy(List<LinkAddress> addresses, List<RouteInfo> routes) {
        if (ipAddresses.isEmpty()) {
            return false;
        }

        for (LinkAddress addr : ipAddresses) {
            if (!any(addresses, (addrSeen) -> addr.isSameAddressAs(addrSeen))) {
                return false;
            }
        }

        if (routes != null) {
            for (IpPrefix prefix : directlyConnectedRoutes) {
                if (!any(routes, (routeSeen) -> isDirectlyConnectedRoute(routeSeen, prefix))) {
                    return false;
                }
            }
        }

        return true;
    }

    /**
     * Convert this configuration to a {@link InitialConfigurationParcelable}.
     */
    public InitialConfigurationParcelable toStableParcelable() {
        final InitialConfigurationParcelable p = new InitialConfigurationParcelable();
        p.ipAddresses = ipAddresses.toArray(new LinkAddress[0]);
        p.directlyConnectedRoutes = directlyConnectedRoutes.toArray(new IpPrefix[0]);
        p.dnsServers = toParcelableArray(
                dnsServers, IpConfigurationParcelableUtil::parcelAddress, String.class);
        return p;
    }

    /**
     * Create an instance of {@link InitialConfiguration} based on the contents of the specified
     * {@link InitialConfigurationParcelable}.
     */
    public static InitialConfiguration fromStableParcelable(InitialConfigurationParcelable p) {
        if (p == null) return null;
        final InitialConfiguration config = new InitialConfiguration();
        config.ipAddresses.addAll(Arrays.asList(p.ipAddresses));
        config.directlyConnectedRoutes.addAll(Arrays.asList(p.directlyConnectedRoutes));
        config.dnsServers.addAll(
                fromParcelableArray(p.dnsServers, IpConfigurationParcelableUtil::unparcelAddress));
        return config;
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof InitialConfiguration)) return false;
        final InitialConfiguration other = (InitialConfiguration) obj;
        return ipAddresses.equals(other.ipAddresses)
                && directlyConnectedRoutes.equals(other.directlyConnectedRoutes)
                && dnsServers.equals(other.dnsServers);
    }

    private static boolean isDirectlyConnectedRoute(RouteInfo route, IpPrefix prefix) {
        return !route.hasGateway() && prefix.equals(route.getDestination());
    }

    private static boolean isPrefixLengthCompliant(LinkAddress addr) {
        return isIPv4(addr) || isCompliantIPv6PrefixLength(addr.getPrefixLength());
    }

    private static boolean isPrefixLengthCompliant(IpPrefix prefix) {
        return isIPv4(prefix) || isCompliantIPv6PrefixLength(prefix.getPrefixLength());
    }

    private static boolean isCompliantIPv6PrefixLength(int prefixLength) {
        return (RFC6177_MIN_PREFIX_LENGTH <= prefixLength)
                && (prefixLength <= RFC7421_PREFIX_LENGTH);
    }

    private static boolean isIPv4(IpPrefix prefix) {
        return prefix.getAddress() instanceof Inet4Address;
    }

    private static boolean isIPv4(LinkAddress addr) {
        return addr.getAddress() instanceof Inet4Address;
    }

    private static boolean isIPv6DefaultRoute(IpPrefix prefix) {
        return prefix.getAddress().equals(INET6_ANY);
    }

    private static boolean isIPv6GUA(LinkAddress addr) {
        return addr.isIpv6() && addr.isGlobalPreferred();
    }

    // TODO: extract out into CollectionUtils.

    /**
     * Indicate whether any element of the specified iterable verifies the specified predicate.
     */
    public static <T> boolean any(Iterable<T> coll, Predicate<T> fn) {
        for (T t : coll) {
            if (fn.test(t)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Indicate whether all elements of the specified iterable verifies the specified predicate.
     */
    public static <T> boolean all(Iterable<T> coll, Predicate<T> fn) {
        return !any(coll, not(fn));
    }

    /**
     * Create a predicate that returns the opposite value of the specified predicate.
     */
    public static <T> Predicate<T> not(Predicate<T> fn) {
        return (t) -> !fn.test(t);
    }
}
+79 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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 android.net.shared;

import android.annotation.Nullable;
import android.net.DhcpResults;
import android.net.DhcpResultsParcelable;
import android.net.InetAddresses;

import java.net.Inet4Address;
import java.net.InetAddress;

/**
 * Collection of utility methods to convert to and from stable AIDL parcelables for IpClient
 * configuration classes.
 * @hide
 */
public final class IpConfigurationParcelableUtil {
    /**
     * Convert DhcpResults to a DhcpResultsParcelable.
     */
    public static DhcpResultsParcelable toStableParcelable(@Nullable DhcpResults results) {
        if (results == null) return null;
        final DhcpResultsParcelable p = new DhcpResultsParcelable();
        p.baseConfiguration = results.toStaticIpConfiguration();
        p.leaseDuration = results.leaseDuration;
        p.mtu = results.mtu;
        p.serverAddress = parcelAddress(results.serverAddress);
        p.vendorInfo = results.vendorInfo;
        p.serverHostName = results.serverHostName;
        return p;
    }

    /**
     * Convert a DhcpResultsParcelable to DhcpResults.
     */
    public static DhcpResults fromStableParcelable(@Nullable DhcpResultsParcelable p) {
        if (p == null) return null;
        final DhcpResults results = new DhcpResults(p.baseConfiguration);
        results.leaseDuration = p.leaseDuration;
        results.mtu = p.mtu;
        results.serverAddress = (Inet4Address) unparcelAddress(p.serverAddress);
        results.vendorInfo = p.vendorInfo;
        results.serverHostName = p.serverHostName;
        return results;
    }

    /**
     * Convert InetAddress to String.
     * TODO: have an InetAddressParcelable
     */
    public static String parcelAddress(@Nullable InetAddress addr) {
        if (addr == null) return null;
        return addr.getHostAddress();
    }

    /**
     * Convert String to InetAddress.
     * TODO: have an InetAddressParcelable
     */
    public static InetAddress unparcelAddress(@Nullable String addr) {
        if (addr == null) return null;
        return InetAddresses.parseNumericAddress(addr);
    }
}
+47 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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 android.net.shared;

import android.annotation.Nullable;
import android.net.LinkProperties;
import android.net.ProxyInfo;

/**
 * Collection of utility methods to convert to and from stable AIDL parcelables for LinkProperties
 * and its attributes.
 * @hide
 */
public final class LinkPropertiesParcelableUtil {
    // Temporary methods to facilitate migrating clients away from LinkPropertiesParcelable
    // TODO: remove the following methods after migrating clients.

    /**
     * @deprecated conversion to stable parcelable is no longer necessary.
     */
    @Deprecated
    public static LinkProperties toStableParcelable(@Nullable LinkProperties lp) {
        return lp;
    }

    /**
     * @deprecated conversion to stable parcelable is no longer necessary.
     */
    @Deprecated
    public static ProxyInfo toStableParcelable(@Nullable ProxyInfo info) {
        return info;
    }
}
Loading