Dynamically set mesh-local addresses preferred lifetime.
When there is an active OMR address(not deprecated), set the mesh-local addresses as non-preferred. When there is no active OMR address, set the mesh-local addresses as preferred. Bug: 323624146 Test: atest ThreadNetworkIntegrationTests Change-Id: I969c20ddadfd2d0bd97499da48efc9b32cfa7e0b
This commit is contained in:
parent
312c70c459
commit
79b202ac9f
5 changed files with 167 additions and 50 deletions
|
|
@ -73,7 +73,6 @@ import android.content.IntentFilter;
|
|||
import android.content.res.Resources;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.InetAddresses;
|
||||
import android.net.LinkAddress;
|
||||
import android.net.LinkProperties;
|
||||
import android.net.LocalNetworkConfig;
|
||||
import android.net.LocalNetworkInfo;
|
||||
|
|
@ -106,7 +105,6 @@ import android.os.HandlerThread;
|
|||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.RemoteException;
|
||||
import android.os.SystemClock;
|
||||
import android.os.UserManager;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
|
|
@ -129,8 +127,6 @@ import libcore.util.HexEncoding;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.SecureRandom;
|
||||
import java.time.Instant;
|
||||
|
|
@ -260,38 +256,6 @@ final class ThreadNetworkControllerService extends IThreadNetworkController.Stub
|
|||
countryCodeSupplier);
|
||||
}
|
||||
|
||||
private static Inet6Address bytesToInet6Address(byte[] addressBytes) {
|
||||
try {
|
||||
return (Inet6Address) Inet6Address.getByAddress(addressBytes);
|
||||
} catch (UnknownHostException e) {
|
||||
// This is unlikely to happen unless the Thread daemon is critically broken
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static InetAddress addressInfoToInetAddress(Ipv6AddressInfo addressInfo) {
|
||||
return bytesToInet6Address(addressInfo.address);
|
||||
}
|
||||
|
||||
private static LinkAddress newLinkAddress(Ipv6AddressInfo addressInfo) {
|
||||
long deprecationTimeMillis =
|
||||
addressInfo.isPreferred
|
||||
? LinkAddress.LIFETIME_PERMANENT
|
||||
: SystemClock.elapsedRealtime();
|
||||
|
||||
InetAddress address = addressInfoToInetAddress(addressInfo);
|
||||
|
||||
// flags and scope will be adjusted automatically depending on the address and
|
||||
// its lifetimes.
|
||||
return new LinkAddress(
|
||||
address,
|
||||
addressInfo.prefixLength,
|
||||
0 /* flags */,
|
||||
0 /* scope */,
|
||||
deprecationTimeMillis,
|
||||
LinkAddress.LIFETIME_PERMANENT /* expirationTime */);
|
||||
}
|
||||
|
||||
private NetworkRequest newUpstreamNetworkRequest() {
|
||||
NetworkRequest.Builder builder = new NetworkRequest.Builder().clearCapabilities();
|
||||
|
||||
|
|
@ -1172,20 +1136,10 @@ final class ThreadNetworkControllerService extends IThreadNetworkController.Stub
|
|||
}
|
||||
}
|
||||
|
||||
private void handleAddressChanged(Ipv6AddressInfo addressInfo, boolean isAdded) {
|
||||
private void handleAddressChanged(List<Ipv6AddressInfo> addressInfoList) {
|
||||
checkOnHandlerThread();
|
||||
InetAddress address = addressInfoToInetAddress(addressInfo);
|
||||
if (address.isMulticastAddress()) {
|
||||
Log.i(TAG, "Ignoring multicast address " + address.getHostAddress());
|
||||
return;
|
||||
}
|
||||
|
||||
LinkAddress linkAddress = newLinkAddress(addressInfo);
|
||||
if (isAdded) {
|
||||
mTunIfController.addAddress(linkAddress);
|
||||
} else {
|
||||
mTunIfController.removeAddress(linkAddress);
|
||||
}
|
||||
mTunIfController.updateAddresses(addressInfoList);
|
||||
|
||||
// The OT daemon can send link property updates before the networkAgent is
|
||||
// registered
|
||||
|
|
@ -1534,8 +1488,8 @@ final class ThreadNetworkControllerService extends IThreadNetworkController.Stub
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onAddressChanged(Ipv6AddressInfo addressInfo, boolean isAdded) {
|
||||
mHandler.post(() -> handleAddressChanged(addressInfo, isAdded));
|
||||
public void onAddressChanged(List<Ipv6AddressInfo> addressInfoList) {
|
||||
mHandler.post(() -> handleAddressChanged(addressInfoList));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -29,12 +29,19 @@ import android.system.Os;
|
|||
import android.system.OsConstants;
|
||||
import android.util.Log;
|
||||
|
||||
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 java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/** Controller for virtual/tunnel network interfaces. */
|
||||
public class TunInterfaceController {
|
||||
|
|
@ -178,6 +185,38 @@ public class TunInterfaceController {
|
|||
}
|
||||
}
|
||||
|
||||
public void updateAddresses(List<Ipv6AddressInfo> addressInfoList) {
|
||||
final List<LinkAddress> newLinkAddresses = new ArrayList<>();
|
||||
boolean hasActiveOmrAddress = false;
|
||||
|
||||
for (Ipv6AddressInfo addressInfo : addressInfoList) {
|
||||
if (addressInfo.isActiveOmr) {
|
||||
hasActiveOmrAddress = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (Ipv6AddressInfo addressInfo : addressInfoList) {
|
||||
InetAddress address = addressInfoToInetAddress(addressInfo);
|
||||
if (address.isMulticastAddress()) {
|
||||
// TODO: Logging here will create repeated logs for a single multicast address, and
|
||||
// it currently is not mandatory for debugging. Add log for ignored multicast
|
||||
// address when necessary.
|
||||
continue;
|
||||
}
|
||||
newLinkAddresses.add(newLinkAddress(addressInfo, hasActiveOmrAddress));
|
||||
}
|
||||
|
||||
final CompareResult<LinkAddress> addressDiff =
|
||||
new CompareResult<>(mLinkProperties.getAllLinkAddresses(), newLinkAddresses);
|
||||
for (LinkAddress linkAddress : addressDiff.removed) {
|
||||
removeAddress(linkAddress);
|
||||
}
|
||||
for (LinkAddress linkAddress : addressDiff.added) {
|
||||
addAddress(linkAddress);
|
||||
}
|
||||
}
|
||||
|
||||
private RouteInfo getRouteForAddress(LinkAddress linkAddress) {
|
||||
return new RouteInfo(
|
||||
new IpPrefix(linkAddress.getAddress(), linkAddress.getPrefixLength()),
|
||||
|
|
@ -195,4 +234,44 @@ public class TunInterfaceController {
|
|||
Log.e(TAG, "Failed to set Thread TUN interface down");
|
||||
}
|
||||
}
|
||||
|
||||
private static InetAddress addressInfoToInetAddress(Ipv6AddressInfo addressInfo) {
|
||||
return bytesToInet6Address(addressInfo.address);
|
||||
}
|
||||
|
||||
private static Inet6Address bytesToInet6Address(byte[] addressBytes) {
|
||||
try {
|
||||
return (Inet6Address) Inet6Address.getByAddress(addressBytes);
|
||||
} catch (UnknownHostException e) {
|
||||
// This is unlikely to happen unless the Thread daemon is critically broken
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static LinkAddress newLinkAddress(
|
||||
Ipv6AddressInfo addressInfo, boolean hasActiveOmrAddress) {
|
||||
// Mesh-local addresses and OMR address have the same scope, to distinguish them we set
|
||||
// mesh-local addresses as deprecated when there is an active OMR address.
|
||||
// For OMR address and link-local address we only use the value isPreferred set by
|
||||
// ot-daemon.
|
||||
boolean isPreferred = addressInfo.isPreferred;
|
||||
if (addressInfo.isMeshLocal && hasActiveOmrAddress) {
|
||||
isPreferred = false;
|
||||
}
|
||||
|
||||
final long deprecationTimeMillis =
|
||||
isPreferred ? LinkAddress.LIFETIME_PERMANENT : SystemClock.elapsedRealtime();
|
||||
|
||||
final InetAddress address = addressInfoToInetAddress(addressInfo);
|
||||
|
||||
// flags and scope will be adjusted automatically depending on the address and
|
||||
// its lifetimes.
|
||||
return new LinkAddress(
|
||||
address,
|
||||
addressInfo.prefixLength,
|
||||
0 /* flags */,
|
||||
0 /* scope */,
|
||||
deprecationTimeMillis,
|
||||
LinkAddress.LIFETIME_PERMANENT /* expirationTime */);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package android.net.thread;
|
|||
|
||||
import static android.Manifest.permission.MANAGE_TEST_NETWORKS;
|
||||
import static android.net.thread.utils.IntegrationTestUtils.JOIN_TIMEOUT;
|
||||
import static android.net.thread.utils.IntegrationTestUtils.getIpv6LinkAddresses;
|
||||
import static android.net.thread.utils.IntegrationTestUtils.isExpectedIcmpv6Packet;
|
||||
import static android.net.thread.utils.IntegrationTestUtils.isFromIpv6Source;
|
||||
import static android.net.thread.utils.IntegrationTestUtils.isInMulticastGroup;
|
||||
|
|
@ -33,6 +34,7 @@ import static com.android.testutils.TestNetworkTrackerKt.initTestNetwork;
|
|||
import static com.android.testutils.TestPermissionUtil.runAsShell;
|
||||
|
||||
import static com.google.common.io.BaseEncoding.base16;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
|
@ -43,6 +45,8 @@ import static java.util.Objects.requireNonNull;
|
|||
|
||||
import android.content.Context;
|
||||
import android.net.InetAddresses;
|
||||
import android.net.IpPrefix;
|
||||
import android.net.LinkAddress;
|
||||
import android.net.LinkProperties;
|
||||
import android.net.MacAddress;
|
||||
import android.net.thread.utils.FullThreadDevice;
|
||||
|
|
@ -55,6 +59,7 @@ import android.net.thread.utils.ThreadFeatureCheckerRule.RequiresThreadFeature;
|
|||
import android.net.thread.utils.ThreadNetworkControllerWrapper;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.SystemClock;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.filters.LargeTest;
|
||||
|
|
@ -251,6 +256,21 @@ public class BorderRoutingTest {
|
|||
assertEquals("bbbbbbbb", ftd.udpReceive());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unicastRouting_meshLocalAddressesAreNotPreferred() throws Exception {
|
||||
// When BR is enabled, there will be OMR address, so the mesh-local addresses are expected
|
||||
// to be deprecated.
|
||||
List<LinkAddress> linkAddresses = getIpv6LinkAddresses("thread-wpan");
|
||||
IpPrefix meshLocalPrefix = DEFAULT_DATASET.getMeshLocalPrefix();
|
||||
|
||||
for (LinkAddress address : linkAddresses) {
|
||||
if (meshLocalPrefix.contains(address.getAddress())) {
|
||||
assertThat(address.getDeprecationTime()).isAtMost(SystemClock.elapsedRealtime());
|
||||
assertThat(address.isPreferred()).isFalse();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresIpv6MulticastRouting
|
||||
public void multicastRouting_ftdSubscribedMulticastAddress_infraLinkJoinsMulticastGroup()
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import static android.net.thread.ThreadNetworkController.DEVICE_ROLE_LEADER;
|
|||
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.waitFor;
|
||||
|
||||
import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
|
||||
|
|
@ -30,6 +31,8 @@ import static com.google.common.io.BaseEncoding.base16;
|
|||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.IpPrefix;
|
||||
import android.net.LinkAddress;
|
||||
import android.net.thread.utils.FullThreadDevice;
|
||||
import android.net.thread.utils.OtDaemonController;
|
||||
import android.net.thread.utils.ThreadFeatureCheckerRule;
|
||||
|
|
@ -201,6 +204,26 @@ public class ThreadIntegrationTest {
|
|||
assertThat(udpReply).isEqualTo("Hello,Thread");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void joinNetworkWithBrDisabled_meshLocalAddressesArePreferred() throws Exception {
|
||||
// When BR feature is disabled, there is no OMR address, so the mesh-local addresses are
|
||||
// expected to be preferred.
|
||||
mOtCtl.executeCommand("br disable");
|
||||
mController.joinAndWait(DEFAULT_DATASET);
|
||||
|
||||
IpPrefix meshLocalPrefix = DEFAULT_DATASET.getMeshLocalPrefix();
|
||||
List<LinkAddress> linkAddresses = getIpv6LinkAddresses("thread-wpan");
|
||||
for (LinkAddress address : linkAddresses) {
|
||||
if (meshLocalPrefix.contains(address.getAddress())) {
|
||||
assertThat(address.getDeprecationTime())
|
||||
.isGreaterThan(SystemClock.elapsedRealtime());
|
||||
assertThat(address.isPreferred()).isTrue();
|
||||
}
|
||||
}
|
||||
|
||||
mOtCtl.executeCommand("br enable");
|
||||
}
|
||||
|
||||
// TODO (b/323300829): add more tests for integration with linux platform and
|
||||
// ConnectivityService
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
|
|||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
|
||||
import android.net.InetAddresses;
|
||||
import android.net.LinkAddress;
|
||||
import android.net.TestNetworkInterface;
|
||||
import android.net.nsd.NsdManager;
|
||||
import android.net.nsd.NsdServiceInfo;
|
||||
|
|
@ -291,6 +293,20 @@ public final class IntegrationTestUtils {
|
|||
return false;
|
||||
}
|
||||
|
||||
public static List<LinkAddress> getIpv6LinkAddresses(String interfaceName) throws IOException {
|
||||
List<LinkAddress> addresses = new ArrayList<>();
|
||||
final String cmd = " ip -6 addr show dev " + interfaceName;
|
||||
final String output = runShellCommandOrThrow(cmd);
|
||||
|
||||
for (final String line : output.split("\\n")) {
|
||||
if (line.contains("inet6")) {
|
||||
addresses.add(parseAddressLine(line));
|
||||
}
|
||||
}
|
||||
|
||||
return addresses;
|
||||
}
|
||||
|
||||
/** Return the first discovered service of {@code serviceType}. */
|
||||
public static NsdServiceInfo discoverService(NsdManager nsdManager, String serviceType)
|
||||
throws Exception {
|
||||
|
|
@ -392,4 +408,29 @@ public final class IntegrationTestUtils {
|
|||
@Override
|
||||
public void onServiceInfoCallbackUnregistered() {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a line of output from "ip -6 addr show" into a {@link LinkAddress}.
|
||||
*
|
||||
* <p>Example line: "inet6 2001:db8:1:1::1/64 scope global deprecated"
|
||||
*/
|
||||
private static LinkAddress parseAddressLine(String line) {
|
||||
String[] parts = line.trim().split("\\s+");
|
||||
String addressString = parts[1];
|
||||
String[] pieces = addressString.split("/", 2);
|
||||
int prefixLength = Integer.parseInt(pieces[1]);
|
||||
final InetAddress address = InetAddresses.parseNumericAddress(pieces[0]);
|
||||
long deprecationTimeMillis =
|
||||
line.contains("deprecated")
|
||||
? SystemClock.elapsedRealtime()
|
||||
: LinkAddress.LIFETIME_PERMANENT;
|
||||
|
||||
return new LinkAddress(
|
||||
address,
|
||||
prefixLength,
|
||||
0 /* flags */,
|
||||
0 /* scope */,
|
||||
deprecationTimeMillis,
|
||||
LinkAddress.LIFETIME_PERMANENT /* expirationTime */);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue