Merge "Add netdata prefixes to tun interface routing table." into main

This commit is contained in:
Treehugger Robot 2024-05-15 19:34:04 +00:00 • committed by Gerrit Code Review
commit 43b62ae5ff
4 changed files with 146 additions and 7 deletions

View file

@ -122,6 +122,7 @@ import com.android.server.thread.openthread.IOtDaemonCallback;
import com.android.server.thread.openthread.IOtStatusReceiver;
import com.android.server.thread.openthread.Ipv6AddressInfo;
import com.android.server.thread.openthread.MeshcopTxtAttributes;
import com.android.server.thread.openthread.OnMeshPrefixConfig;
import com.android.server.thread.openthread.OtDaemonState;
import libcore.util.HexEncoding;
@ -1234,6 +1235,18 @@ final class ThreadNetworkControllerService extends IThreadNetworkController.Stub
}
}
private void handlePrefixChanged(List<OnMeshPrefixConfig> onMeshPrefixConfigList) {
checkOnHandlerThread();
mTunIfController.updatePrefixes(onMeshPrefixConfigList);
// The OT daemon can send link property updates before the networkAgent is
// registered
if (mNetworkAgent != null) {
mNetworkAgent.sendLinkProperties(mTunIfController.getLinkProperties());
}
}
private void sendLocalNetworkConfig() {
if (mNetworkAgent == null) {
return;
@ -1581,5 +1594,10 @@ final class ThreadNetworkControllerService extends IThreadNetworkController.Stub
public void onBackboneRouterStateChanged(BackboneRouterState state) {
mHandler.post(() -> handleMulticastForwardingChanged(state));
}
@Override
public void onPrefixChanged(List<OnMeshPrefixConfig> onMeshPrefixConfigList) {
mHandler.post(() -> handlePrefixChanged(onMeshPrefixConfigList));
}
}
}

View file

@ -35,6 +35,7 @@ import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
import com.android.net.module.util.netlink.NetlinkUtils;
import com.android.net.module.util.netlink.RtNetlinkAddressMessage;
import com.android.server.thread.openthread.Ipv6AddressInfo;
import com.android.server.thread.openthread.OnMeshPrefixConfig;
import java.io.FileDescriptor;
import java.io.IOException;
@ -66,7 +67,8 @@ public class TunInterfaceController {
private static int sNetlinkSeqNo = 0;
private final MulticastSocket mMulticastSocket; // For join group and leave group
private NetworkInterface mNetworkInterface;
private List<InetAddress> mMulticastAddresses = new ArrayList<>();
private final List<InetAddress> mMulticastAddresses = new ArrayList<>();
private final List<RouteInfo> mNetDataPrefixes = new ArrayList<>();
/** Creates a new {@link TunInterfaceController} instance for given interface. */
public TunInterfaceController(String interfaceName) {
@ -243,13 +245,40 @@ public class TunInterfaceController {
mMulticastAddresses.addAll(newMulticastAddresses);
}
public void updatePrefixes(List<OnMeshPrefixConfig> onMeshPrefixConfigList) {
final List<RouteInfo> newNetDataPrefixes = new ArrayList<>();
for (OnMeshPrefixConfig onMeshPrefixConfig : onMeshPrefixConfigList) {
newNetDataPrefixes.add(getRouteForOnMeshPrefix(onMeshPrefixConfig));
}
final CompareResult<RouteInfo> prefixDiff =
new CompareResult<>(mNetDataPrefixes, newNetDataPrefixes);
for (RouteInfo routeRemoved : prefixDiff.removed) {
mLinkProperties.removeRoute(routeRemoved);
}
for (RouteInfo routeAdded : prefixDiff.added) {
mLinkProperties.addRoute(routeAdded);
}
mNetDataPrefixes.clear();
mNetDataPrefixes.addAll(newNetDataPrefixes);
}
private RouteInfo getRouteForAddress(LinkAddress linkAddress) {
return new RouteInfo(
new IpPrefix(linkAddress.getAddress(), linkAddress.getPrefixLength()),
null,
mIfName,
RouteInfo.RTN_UNICAST,
MTU);
return getRouteForIpPrefix(
new IpPrefix(linkAddress.getAddress(), linkAddress.getPrefixLength()));
}
private RouteInfo getRouteForOnMeshPrefix(OnMeshPrefixConfig onMeshPrefixConfig) {
return getRouteForIpPrefix(
new IpPrefix(
bytesToInet6Address(onMeshPrefixConfig.prefix),
onMeshPrefixConfig.prefixLength));
}
private RouteInfo getRouteForIpPrefix(IpPrefix ipPrefix) {
return new RouteInfo(ipPrefix, null, mIfName, RouteInfo.RTN_UNICAST, MTU);
}
/** Called by {@link ThreadNetworkControllerService} to do clean up when ot-daemon is dead. */

View file

@ -22,6 +22,8 @@ import static android.net.thread.ThreadNetworkController.DEVICE_ROLE_STOPPED;
import static android.net.thread.utils.IntegrationTestUtils.CALLBACK_TIMEOUT;
import static android.net.thread.utils.IntegrationTestUtils.RESTART_JOIN_TIMEOUT;
import static android.net.thread.utils.IntegrationTestUtils.getIpv6LinkAddresses;
import static android.net.thread.utils.IntegrationTestUtils.getPrefixesFromNetData;
import static android.net.thread.utils.IntegrationTestUtils.getThreadNetwork;
import static android.net.thread.utils.IntegrationTestUtils.isInMulticastGroup;
import static android.net.thread.utils.IntegrationTestUtils.waitFor;
@ -34,9 +36,11 @@ import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.InetAddresses;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.thread.utils.FullThreadDevice;
import android.net.thread.utils.OtDaemonController;
import android.net.thread.utils.ThreadFeatureCheckerRule;
@ -76,6 +80,9 @@ public class ThreadIntegrationTest {
// The maximum time for OT addresses to be propagated to the TUN interface "thread-wpan"
private static final Duration TUN_ADDR_UPDATE_TIMEOUT = Duration.ofSeconds(1);
// The maximum time for changes to be propagated to netdata.
private static final Duration NET_DATA_UPDATE_TIMEOUT = Duration.ofSeconds(1);
// A valid Thread Active Operational Dataset generated from OpenThread CLI "dataset init new".
private static final byte[] DEFAULT_DATASET_TLVS =
base16().decode(
@ -90,6 +97,10 @@ public class ThreadIntegrationTest {
private static final Inet6Address GROUP_ADDR_ALL_ROUTERS =
(Inet6Address) InetAddresses.parseNumericAddress("ff02::2");
private static final String TEST_NO_SLAAC_PREFIX = "9101:dead:beef:cafe::/64";
private static final InetAddress TEST_NO_SLAAC_PREFIX_ADDRESS =
InetAddresses.parseNumericAddress("9101:dead:beef:cafe::");
@Rule public final ThreadFeatureCheckerRule mThreadRule = new ThreadFeatureCheckerRule();
private ExecutorService mExecutor;
@ -253,6 +264,49 @@ public class ThreadIntegrationTest {
}
}
@Test
public void addPrefixToNetData_routeIsAddedToTunInterface() throws Exception {
ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
mController.joinAndWait(DEFAULT_DATASET);
// Ftd child doesn't have the ability to add a prefix, so let BR itself add a prefix.
mOtCtl.executeCommand("prefix add " + TEST_NO_SLAAC_PREFIX + " pros med");
mOtCtl.executeCommand("netdata register");
waitFor(
() -> {
String netData = mOtCtl.executeCommand("netdata show");
return getPrefixesFromNetData(netData).contains(TEST_NO_SLAAC_PREFIX);
},
NET_DATA_UPDATE_TIMEOUT);
LinkProperties lp = cm.getLinkProperties(getThreadNetwork(CALLBACK_TIMEOUT));
assertThat(lp).isNotNull();
assertThat(lp.getRoutes().stream().anyMatch(r -> r.matches(TEST_NO_SLAAC_PREFIX_ADDRESS)))
.isTrue();
}
@Test
public void removePrefixFromNetData_routeIsRemovedFromTunInterface() throws Exception {
ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
mController.joinAndWait(DEFAULT_DATASET);
mOtCtl.executeCommand("prefix add " + TEST_NO_SLAAC_PREFIX + " pros med");
mOtCtl.executeCommand("netdata register");
mOtCtl.executeCommand("prefix remove " + TEST_NO_SLAAC_PREFIX);
mOtCtl.executeCommand("netdata register");
waitFor(
() -> {
String netData = mOtCtl.executeCommand("netdata show");
return !getPrefixesFromNetData(netData).contains(TEST_NO_SLAAC_PREFIX);
},
NET_DATA_UPDATE_TIMEOUT);
LinkProperties lp = cm.getLinkProperties(getThreadNetwork(CALLBACK_TIMEOUT));
assertThat(lp).isNotNull();
assertThat(lp.getRoutes().stream().anyMatch(r -> r.matches(TEST_NO_SLAAC_PREFIX_ADDRESS)))
.isFalse();
}
// TODO (b/323300829): add more tests for integration with linux platform and
// ConnectivityService

View file

@ -15,6 +15,7 @@
*/
package android.net.thread.utils;
import static android.net.NetworkCapabilities.NET_CAPABILITY_LOCAL_NETWORK;
import static android.system.OsConstants.IPPROTO_ICMPV6;
import static com.android.compatibility.common.util.SystemUtil.runShellCommandOrThrow;
@ -24,17 +25,24 @@ import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ROUTER_AD
import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;
import android.net.ConnectivityManager;
import android.net.InetAddresses;
import android.net.LinkAddress;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.TestNetworkInterface;
import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
import android.net.thread.ThreadNetworkController;
import android.os.Build;
import android.os.Handler;
import android.os.SystemClock;
import androidx.annotation.NonNull;
import androidx.test.core.app.ApplicationProvider;
import com.android.net.module.util.Struct;
import com.android.net.module.util.structs.Icmpv6Header;
@ -375,6 +383,36 @@ public final class IntegrationTestUtils {
}
}
public static String getPrefixesFromNetData(String netData) {
int startIdx = netData.indexOf("Prefixes:");
int endIdx = netData.indexOf("Routes:");
return netData.substring(startIdx, endIdx);
}
public static Network getThreadNetwork(Duration timeout) throws Exception {
CompletableFuture<Network> networkFuture = new CompletableFuture<>();
ConnectivityManager cm =
ApplicationProvider.getApplicationContext()
.getSystemService(ConnectivityManager.class);
NetworkRequest.Builder networkRequestBuilder =
new NetworkRequest.Builder().addTransportType(NetworkCapabilities.TRANSPORT_THREAD);
// Before V, we need to explicitly set `NET_CAPABILITY_LOCAL_NETWORK` capability to request
// a Thread network.
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
networkRequestBuilder.addCapability(NET_CAPABILITY_LOCAL_NETWORK);
}
NetworkRequest networkRequest = networkRequestBuilder.build();
ConnectivityManager.NetworkCallback networkCallback =
new ConnectivityManager.NetworkCallback() {
@Override
public void onAvailable(Network network) {
networkFuture.complete(network);
}
};
cm.registerNetworkCallback(networkRequest, networkCallback);
return networkFuture.get(timeout.toSeconds(), SECONDS);
}
private static class DefaultDiscoveryListener implements NsdManager.DiscoveryListener {
@Override
public void onStartDiscoveryFailed(String serviceType, int errorCode) {}