Passing isLocalRoute to netd add,remove and update route.

ConnectivityService would be centralized location for deciding local
network so for this effort passing isLocalRoute parameter to
routecontroller in netd for route addition, removal and modification.

Bug: 394308861
Test: Manually tested.
Test: atest ConnectivityCoverageTests
Test: atest ConnectivityCoverageTests:android.net.connectivity.com.android.server.connectivityservice.CSLocalRouteTest
Test: atest netd_integration_test
Flag: EXEMPT Using device config kill-switch
Change-Id: I9f11882665375bba10fa369c2e25f374b5e63661
This commit is contained in:
Sarup Dalwani 2025-07-02 17:45:00 +00:00
commit 169a584691
2 changed files with 172 additions and 21 deletions

View file

@ -81,6 +81,7 @@ import static android.net.ConnectivityManager.TYPE_WIFI_P2P;
import static android.net.ConnectivityManager.getNetworkTypeName;
import static android.net.ConnectivityManager.isNetworkTypeValid;
import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
import static android.net.INetd.LOCAL_NET_ID;
import static android.net.INetd.PERMISSION_INTERNET;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_PRIVDNS;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL;
@ -149,6 +150,7 @@ import static com.android.net.module.util.BpfUtils.BPF_CGROUP_UDP4_RECVMSG;
import static com.android.net.module.util.BpfUtils.BPF_CGROUP_UDP4_SENDMSG;
import static com.android.net.module.util.BpfUtils.BPF_CGROUP_UDP6_RECVMSG;
import static com.android.net.module.util.BpfUtils.BPF_CGROUP_UDP6_SENDMSG;
import static com.android.net.module.util.NetdUtils.toRouteInfoParcel;
import static com.android.net.module.util.NetworkMonitorUtils.isPrivateDnsValidationRequired;
import static com.android.net.module.util.NetworkStackConstants.IPV4_LOCAL_PREFIXES;
import static com.android.net.module.util.NetworkStackConstants.MULTICAST_AND_BROADCAST_PREFIXES;
@ -267,6 +269,7 @@ import android.net.QosFilter;
import android.net.QosSocketFilter;
import android.net.QosSocketInfo;
import android.net.RouteInfo;
import android.net.RouteInfoParcel;
import android.net.SocketKeepalive;
import android.net.TetheringManager;
import android.net.TransportInfo;
@ -362,7 +365,6 @@ import com.android.net.module.util.LinkPropertiesUtils;
import com.android.net.module.util.LinkPropertiesUtils.CompareOrUpdateResult;
import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
import com.android.net.module.util.LocationPermissionChecker;
import com.android.net.module.util.NetdUtils;
import com.android.net.module.util.PerUidCounter;
import com.android.net.module.util.PermissionUtils;
import com.android.net.module.util.RoutingCoordinatorService;
@ -3620,7 +3622,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
final String nextHop = bestRoute.hasGateway()
? bestRoute.getGateway().getHostAddress() : "";
try {
mNetd.networkAddLegacyRoute(netId, bestRoute.getInterface(), dst, nextHop , uid);
mNetd.networkAddLegacyRoute(netId, bestRoute.getInterface(), dst, nextHop, uid);
} catch (RemoteException | ServiceSpecificException e) {
if (DBG) loge("Exception trying to add a route: " + e);
return false;
@ -10515,8 +10517,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
for (LinkProperties linkProperty : linkPropertiesDiff.added) {
final List<IpPrefix> unicastLocalPrefixesToBeAdded = new ArrayList<>();
for (LinkAddress linkAddress : linkProperty.getLinkAddresses()) {
unicastLocalPrefixesToBeAdded.addAll(
getLocalNetworkPrefixesForAddress(linkAddress));
unicastLocalPrefixesToBeAdded.addAll(getLocalNetworkPrefixesForAddress(
linkAddress.getAddress(), linkAddress.getPrefixLength()));
}
addLocalAddressesToBpfMap(linkProperty.getInterfaceName(),
unicastLocalPrefixesToBeAdded, linkProperty);
@ -10535,8 +10537,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
linkProperty.getInterfaceName(), Collections.emptyList());
for (LinkAddress linkAddress : linkProperty.getLinkAddresses()) {
unicastLocalPrefixesToBeRemoved.addAll(
getLocalNetworkPrefixesForAddress(linkAddress));
unicastLocalPrefixesToBeRemoved.addAll(getLocalNetworkPrefixesForAddress(
linkAddress.getAddress(), linkAddress.getPrefixLength()));
}
// This is to ensure if 10.0.10.0/24 was added and 10.0.11.0/24 was removed both will
@ -10551,23 +10553,24 @@ public class ConnectivityService extends IConnectivityManager.Stub
/**
* Filters IpPrefix that are local prefixes and LinkAddress is part of them.
* @param linkAddress link address used for filtering
* @param prefix address used for filtering
* @param prefixLength prefix length of address
* @return list of IpPrefix that are local addresses.
*/
private List<IpPrefix> getLocalNetworkPrefixesForAddress(LinkAddress linkAddress) {
public static List<IpPrefix> getLocalNetworkPrefixesForAddress(InetAddress prefix,
int prefixLength) {
List<IpPrefix> localPrefixes = new ArrayList<>();
if (linkAddress.isIpv6()) {
if (prefix instanceof Inet6Address) {
// For IPv6, if the prefix length is greater than zero then they are part of local
// network
if (linkAddress.getPrefixLength() != 0) {
if (prefixLength != 0) {
localPrefixes.add(
new IpPrefix(linkAddress.getAddress(), linkAddress.getPrefixLength()));
new IpPrefix(prefix, prefixLength));
}
} else {
// For IPv4, if the linkAddress is part of IpPrefix adding prefix to result.
for (IpPrefix ipv4LocalPrefix : IPV4_LOCAL_PREFIXES) {
if (ipv4LocalPrefix.containsPrefix(
new IpPrefix(linkAddress.getAddress(), linkAddress.getPrefixLength()))) {
if (ipv4LocalPrefix.containsPrefix(new IpPrefix(prefix, prefixLength))) {
localPrefixes.add(ipv4LocalPrefix);
}
}
@ -10695,8 +10698,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (route.hasGateway()) continue;
if (VDBG || DDBG) log("Adding Route [" + route + "] to network " + netId);
try {
mRoutingCoordinatorService.addRouteParcel(netId,
NetdUtils.toRouteInfoParcel(route));
final RouteInfoParcel routeParcel = toRouteInfoParcel(route);
routeParcel.isLocalRoute = isLocalRoute(routeParcel, netId);
mRoutingCoordinatorService.addRouteParcel(netId, routeParcel);
} catch (Exception e) {
if ((route.getDestination().getAddress() instanceof Inet4Address) || VDBG) {
loge("Exception in addRoute for non-gateway: " + e);
@ -10707,8 +10711,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (!route.hasGateway()) continue;
if (VDBG || DDBG) log("Adding Route [" + route + "] to network " + netId);
try {
mRoutingCoordinatorService.addRouteParcel(netId,
NetdUtils.toRouteInfoParcel(route));
final RouteInfoParcel routeParcel = toRouteInfoParcel(route);
routeParcel.isLocalRoute = isLocalRoute(routeParcel, netId);
mRoutingCoordinatorService.addRouteParcel(netId, routeParcel);
} catch (Exception e) {
if ((route.getGateway() instanceof Inet4Address) || VDBG) {
loge("Exception in addRoute for gateway: " + e);
@ -10719,8 +10724,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
for (RouteInfo route : routeDiff.removed) {
if (VDBG || DDBG) log("Removing Route [" + route + "] from network " + netId);
try {
mRoutingCoordinatorService.removeRouteParcel(netId,
NetdUtils.toRouteInfoParcel(route));
final RouteInfoParcel routeParcel = toRouteInfoParcel(route);
routeParcel.isLocalRoute = isLocalRoute(routeParcel, netId);
mRoutingCoordinatorService.removeRouteParcel(netId, routeParcel);
} catch (Exception e) {
loge("Exception in removeRoute: " + e);
}
@ -10729,8 +10735,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
for (RouteInfo route : routeDiff.updated) {
if (VDBG || DDBG) log("Updating Route [" + route + "] from network " + netId);
try {
mRoutingCoordinatorService.updateRouteParcel(netId,
NetdUtils.toRouteInfoParcel(route));
final RouteInfoParcel routeParcel = toRouteInfoParcel(route);
routeParcel.isLocalRoute = isLocalRoute(routeParcel, netId);
mRoutingCoordinatorService.updateRouteParcel(netId, routeParcel);
} catch (Exception e) {
loge("Exception in updateRoute: " + e);
}
@ -10757,6 +10764,23 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
/**
* Decides if the route on netId is local.
* @param route route information
* @param netId network id.
* @return true if route is local network route.
*/
public static boolean isLocalRoute(RouteInfoParcel route, int netId) {
if (route.nextHop != INetd.NEXTHOP_NONE) return false;
// LOCAL_NET_ID does not support local routes. It only looks up the
// LOCAL_NETWORK routing table, which has no corresponding local table for
// local routes to be added to.
if (netId == LOCAL_NET_ID) return false;
IpPrefix routeDestination = new IpPrefix(route.destination);
return !getLocalNetworkPrefixesForAddress(routeDestination.getAddress(),
routeDestination.getPrefixLength()).isEmpty();
}
private void updateVpnFiltering(@NonNull LinkProperties newLp, @Nullable LinkProperties oldLp,
@NonNull NetworkAgentInfo nai) {
final String oldIface = getVpnIsolationInterface(nai, nai.networkCapabilities, oldLp);

View file

@ -0,0 +1,127 @@
/*
* Copyright (C) 2023 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.server.connectivityservice
import android.net.INetd
import android.net.InetAddresses
import android.net.IpPrefix
import android.net.RouteInfo
import android.os.Build
import androidx.test.filters.SmallTest
import com.android.net.module.util.NetdUtils
import com.android.server.ConnectivityService
import com.android.testutils.DevSdkIgnoreRule
import com.android.testutils.DevSdkIgnoreRunner
import java.net.Inet6Address
import org.junit.Assert
import org.junit.Test
import org.junit.runner.RunWith
private const val WIFI_IFNAME = "wlan0"
private const val NON_LOCAL_NET_ID = 102
@DevSdkIgnoreRunner.MonitorThreadLeak
@RunWith(DevSdkIgnoreRunner::class)
@SmallTest
@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
class CSLocalRouteTest {
private val LOCAL_IPV6_IP_ADDRESS_PREFIX = IpPrefix("2001:db8:1234:abcd::/64")
private val LOCAL_IPV4_IP_ADDRESS_PREFIX = IpPrefix("10.0.0.0/24")
private val IPV4_GATEWAY = InetAddresses.parseNumericAddress("10.0.0.1")
private val IPV6_GATEWAY = InetAddresses.parseNumericAddress("fe80::1") as Inet6Address
private val NON_LOCAL_IPV4_IP_ADDRESS_PREFIX = IpPrefix("8.8.0.0/24")
@Test
fun testIsLocalRouteForLocalIPv6_TrueForLocalRoute() {
val route = NetdUtils.toRouteInfoParcel(RouteInfo(
LOCAL_IPV6_IP_ADDRESS_PREFIX,
null,
WIFI_IFNAME
))
Assert.assertTrue(ConnectivityService.isLocalRoute(route, NON_LOCAL_NET_ID))
}
@Test
fun testIsLocalRouteWithNextHopForLocalIPv6_FalseForLocalRoute() {
val route = NetdUtils.toRouteInfoParcel(RouteInfo(
LOCAL_IPV6_IP_ADDRESS_PREFIX,
IPV6_GATEWAY,
WIFI_IFNAME
))
Assert.assertFalse(ConnectivityService.isLocalRoute(route, NON_LOCAL_NET_ID))
}
@Test
fun testIsLocalRouteWithLocalNetIdForLocalIPv6_FalseForLocalRoute() {
val route = NetdUtils.toRouteInfoParcel(RouteInfo(
LOCAL_IPV6_IP_ADDRESS_PREFIX,
null,
WIFI_IFNAME
))
Assert.assertFalse(ConnectivityService.isLocalRoute(route, INetd.LOCAL_NET_ID))
}
@Test
fun testIsLocalRouteForLocalIPv4_TrueForLocalRoute() {
val route = NetdUtils.toRouteInfoParcel(RouteInfo(
LOCAL_IPV4_IP_ADDRESS_PREFIX,
null,
WIFI_IFNAME
))
Assert.assertTrue(ConnectivityService.isLocalRoute(route, NON_LOCAL_NET_ID))
}
@Test
fun testIsLocalRouteForNonLocalIPv4_FalseForLocalRoute() {
val route = NetdUtils.toRouteInfoParcel(RouteInfo(
NON_LOCAL_IPV4_IP_ADDRESS_PREFIX,
null,
WIFI_IFNAME
))
Assert.assertFalse(ConnectivityService.isLocalRoute(route, NON_LOCAL_NET_ID))
}
@Test
fun testIsLocalRouteWithNextHopForLocalIPv4_FalseForLocalRoute() {
val route = NetdUtils.toRouteInfoParcel(RouteInfo(
LOCAL_IPV4_IP_ADDRESS_PREFIX,
IPV4_GATEWAY,
WIFI_IFNAME
))
Assert.assertFalse(ConnectivityService.isLocalRoute(route, NON_LOCAL_NET_ID))
}
@Test
fun testIsLocalRouteWithNextHopForNonLocalIPv4_FalseForLocalRoute() {
val route = NetdUtils.toRouteInfoParcel(RouteInfo(
NON_LOCAL_IPV4_IP_ADDRESS_PREFIX,
IPV4_GATEWAY,
WIFI_IFNAME
))
Assert.assertFalse(ConnectivityService.isLocalRoute(route, NON_LOCAL_NET_ID))
}
@Test
fun testIsLocalRouteWithLocalNetIdForLocalIPv4_FalseForLocalRoute() {
val route = NetdUtils.toRouteInfoParcel(RouteInfo(
LOCAL_IPV4_IP_ADDRESS_PREFIX,
null,
WIFI_IFNAME
))
Assert.assertFalse(ConnectivityService.isLocalRoute(route, INetd.LOCAL_NET_ID))
}
}