summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-08-03 17:05:08 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-08-03 17:05:08 +0000
commit72285ef2923a82ee3c00c1447df6087327e20715 (patch)
tree2d72576be7778ff441a64c94aadcbb9e3f20b309
parent824772c5d18d1f1d47ff4ec1ee6a741fd1cc4661 (diff)
parent243dc1f73a53395a359075ae3540c1686cc00356 (diff)
downloadnet-android13-mainline-go-networking-release.tar.gz
Snap for 8902501 from 243dc1f73a53395a359075ae3540c1686cc00356 to mainline-go-networking-releaseaml_go_net_330913000android13-mainline-go-networking-release
Change-Id: Ice1743a55951b714b34eed3017ad922be67f3fe6
-rw-r--r--common/device/com/android/net/module/util/structs/Icmpv4Header.java43
-rw-r--r--common/framework/com/android/net/module/util/NetworkStackConstants.java29
-rw-r--r--common/native/bpf_headers/include/bpf/bpf_helpers.h7
-rw-r--r--common/native/bpf_headers/include/bpf/bpf_map_def.h10
-rw-r--r--common/testutils/devicetests/com/android/testutils/CompatUtil.kt8
-rw-r--r--common/testutils/devicetests/com/android/testutils/DevSdkIgnoreRule.kt85
-rw-r--r--common/testutils/devicetests/com/android/testutils/DevSdkIgnoreRunner.kt2
-rw-r--r--common/testutils/devicetests/com/android/testutils/PacketResponder.kt1
-rw-r--r--common/testutils/devicetests/com/android/testutils/RouterAdvertisementResponder.java116
-rw-r--r--common/testutils/devicetests/com/android/testutils/filters/CtsNetTestCasesMaxTargetSdk30.kt22
10 files changed, 241 insertions, 82 deletions
diff --git a/common/device/com/android/net/module/util/structs/Icmpv4Header.java b/common/device/com/android/net/module/util/structs/Icmpv4Header.java
new file mode 100644
index 00000000..454bb074
--- /dev/null
+++ b/common/device/com/android/net/module/util/structs/Icmpv4Header.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 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.net.module.util.structs;
+
+import com.android.net.module.util.Struct;
+import com.android.net.module.util.Struct.Field;
+import com.android.net.module.util.Struct.Type;
+
+/**
+ * ICMPv4 header as per https://tools.ietf.org/html/rfc792.
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Code | Checksum |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+public class Icmpv4Header extends Struct {
+ @Field(order = 0, type = Type.U8)
+ public short type;
+ @Field(order = 1, type = Type.U8)
+ public short code;
+ @Field(order = 2, type = Type.S16)
+ public short checksum;
+ public Icmpv4Header(final short type, final short code, final short checksum) {
+ this.type = type;
+ this.code = code;
+ this.checksum = checksum;
+ }
+}
diff --git a/common/framework/com/android/net/module/util/NetworkStackConstants.java b/common/framework/com/android/net/module/util/NetworkStackConstants.java
index 353fe692..1ad9c359 100644
--- a/common/framework/com/android/net/module/util/NetworkStackConstants.java
+++ b/common/framework/com/android/net/module/util/NetworkStackConstants.java
@@ -228,6 +228,35 @@ public final class NetworkStackConstants {
public static final int TAG_SYSTEM_PROBE = 0xFFFFFF81;
public static final int TAG_SYSTEM_DNS = 0xFFFFFF82;
+ /**
+ * A test URL used to override configuration settings and overlays for the network validation
+ * HTTPS URL, when set in {@link android.provider.DeviceConfig} configuration.
+ *
+ * <p>This URL will be ignored if the host is not "localhost" (it can only be used to test with
+ * a local test server), and must not be set in production scenarios (as enforced by CTS tests).
+ *
+ * <p>{@link #TEST_URL_EXPIRATION_TIME} must also be set to use this setting.
+ */
+ public static final String TEST_CAPTIVE_PORTAL_HTTPS_URL = "test_captive_portal_https_url";
+ /**
+ * A test URL used to override configuration settings and overlays for the network validation
+ * HTTP URL, when set in {@link android.provider.DeviceConfig} configuration.
+ *
+ * <p>This URL will be ignored if the host is not "localhost" (it can only be used to test with
+ * a local test server), and must not be set in production scenarios (as enforced by CTS tests).
+ *
+ * <p>{@link #TEST_URL_EXPIRATION_TIME} must also be set to use this setting.
+ */
+ public static final String TEST_CAPTIVE_PORTAL_HTTP_URL = "test_captive_portal_http_url";
+ /**
+ * Expiration time of the test URL, in ms, relative to {@link System#currentTimeMillis()}.
+ *
+ * <p>After this expiration time, test URLs will be ignored. They will also be ignored if
+ * the expiration time is more than 10 minutes in the future, to avoid misconfiguration
+ * following test runs.
+ */
+ public static final String TEST_URL_EXPIRATION_TIME = "test_url_expiration_time";
+
// TODO: Move to Inet4AddressUtils
// See aosp/1455936: NetworkStackConstants can't depend on it as it causes jarjar-related issues
// for users of both the net-utils-device-common and net-utils-framework-common libraries.
diff --git a/common/native/bpf_headers/include/bpf/bpf_helpers.h b/common/native/bpf_headers/include/bpf/bpf_helpers.h
index 236318d6..35d9f31d 100644
--- a/common/native/bpf_headers/include/bpf/bpf_helpers.h
+++ b/common/native/bpf_headers/include/bpf/bpf_helpers.h
@@ -46,9 +46,10 @@
*
* While this will work outside of mainline too, there just is no point to
* using it when the .o and the bpfloader ship in sync with each other.
+ * In which case it's just best to use the default.
*/
#ifndef BPFLOADER_MIN_VER
-#define BPFLOADER_MIN_VER DEFAULT_BPFLOADER_MIN_VER
+#define BPFLOADER_MIN_VER COMPILE_FOR_BPFLOADER_VERSION
#endif
#ifndef BPFLOADER_MAX_VER
@@ -192,8 +193,8 @@ static int (*bpf_map_delete_elem_unsafe)(const struct bpf_map_def* map,
#ifndef DEFAULT_BPF_MAP_UID
#define DEFAULT_BPF_MAP_UID AID_ROOT
-#elif BPFLOADER_MIN_VER < 21u
-#error "Bpf Map UID must be left at default of AID_ROOT for BpfLoader prior to v0.21"
+#elif BPFLOADER_MIN_VER < 28u
+#error "Bpf Map UID must be left at default of AID_ROOT for BpfLoader prior to v0.28"
#endif
#define DEFINE_BPF_MAP_UGM(the_map, TYPE, KeyType, ValueType, num_entries, usr, grp, md) \
diff --git a/common/native/bpf_headers/include/bpf/bpf_map_def.h b/common/native/bpf_headers/include/bpf/bpf_map_def.h
index 14a02959..02caf07e 100644
--- a/common/native/bpf_headers/include/bpf/bpf_map_def.h
+++ b/common/native/bpf_headers/include/bpf/bpf_map_def.h
@@ -48,6 +48,10 @@
#define DEFAULT_SIZEOF_BPF_MAP_DEF 32 // v0.0 struct: enum (uint sized) + 7 uint
#define DEFAULT_SIZEOF_BPF_PROG_DEF 20 // v0.0 struct: 4 uint + bool + 3 byte alignment pad
+// By default, unless otherwise specified, allow the use of features only supported by v0.28,
+// which first added working support for map uid != root
+#define COMPILE_FOR_BPFLOADER_VERSION 28u
+
/*
* The bpf_{map,prog}_def structures are compiled for different architectures.
* Once by the BPF compiler for the BPF architecture, and once by a C++
@@ -143,7 +147,7 @@ struct bpf_map_def {
// unsigned int inner_map_idx;
// unsigned int numa_node;
- unsigned int uid; // uid_t
+ unsigned int zero; // uid_t, for compat with old (buggy) bpfloader must be AID_ROOT == 0
unsigned int gid; // gid_t
unsigned int mode; // mode_t
@@ -171,13 +175,15 @@ struct bpf_map_def {
bool shared; // use empty string as 'file' component of pin path - allows cross .o map sharing
char pad0[3]; // manually pad up to 4 byte alignment, may be used for extensions in the future
+
+ unsigned int uid; // uid_t
};
_Static_assert(sizeof(((struct bpf_map_def *)0)->selinux_context) == 32, "must be 32 bytes");
_Static_assert(sizeof(((struct bpf_map_def *)0)->pin_subdir) == 32, "must be 32 bytes");
// This needs to be updated whenever the above structure definition is expanded.
-_Static_assert(sizeof(struct bpf_map_def) == 116, "sizeof struct bpf_map_def != 116");
+_Static_assert(sizeof(struct bpf_map_def) == 120, "sizeof struct bpf_map_def != 120");
_Static_assert(__alignof__(struct bpf_map_def) == 4, "__alignof__ struct bpf_map_def != 4");
_Static_assert(_Alignof(struct bpf_map_def) == 4, "_Alignof struct bpf_map_def != 4");
diff --git a/common/testutils/devicetests/com/android/testutils/CompatUtil.kt b/common/testutils/devicetests/com/android/testutils/CompatUtil.kt
index ff8c6689..82f1d9b9 100644
--- a/common/testutils/devicetests/com/android/testutils/CompatUtil.kt
+++ b/common/testutils/devicetests/com/android/testutils/CompatUtil.kt
@@ -17,7 +17,7 @@
package com.android.testutils
import android.net.NetworkSpecifier
-import android.os.Build
+import com.android.modules.utils.build.SdkLevel.isAtLeastS
/**
* Test utility to create [NetworkSpecifier]s on different SDK versions.
@@ -26,7 +26,7 @@ object CompatUtil {
@JvmStatic
fun makeTestNetworkSpecifier(ifName: String): NetworkSpecifier {
// Until R, there was no TestNetworkSpecifier, StringNetworkSpecifier was used instead
- if (isDevSdkInRange(minExclusive = null, maxInclusive = Build.VERSION_CODES.R)) {
+ if (!isAtLeastS()) {
return makeNetworkSpecifierInternal("android.net.StringNetworkSpecifier", ifName)
}
// TestNetworkSpecifier is not part of the SDK in some branches using this utility
@@ -37,7 +37,7 @@ object CompatUtil {
@JvmStatic
fun makeEthernetNetworkSpecifier(ifName: String): NetworkSpecifier {
// Until R, there was no EthernetNetworkSpecifier, StringNetworkSpecifier was used instead
- if (isDevSdkInRange(minExclusive = null, maxInclusive = Build.VERSION_CODES.R)) {
+ if (!isAtLeastS()) {
return makeNetworkSpecifierInternal("android.net.StringNetworkSpecifier", ifName)
}
// EthernetNetworkSpecifier is not part of the SDK in some branches using this utility
@@ -50,4 +50,4 @@ object CompatUtil {
return Class.forName(clazz)
.getConstructor(String::class.java).newInstance(specifier) as NetworkSpecifier
}
-} \ No newline at end of file
+}
diff --git a/common/testutils/devicetests/com/android/testutils/DevSdkIgnoreRule.kt b/common/testutils/devicetests/com/android/testutils/DevSdkIgnoreRule.kt
index 3fcf8011..21e84daf 100644
--- a/common/testutils/devicetests/com/android/testutils/DevSdkIgnoreRule.kt
+++ b/common/testutils/devicetests/com/android/testutils/DevSdkIgnoreRule.kt
@@ -18,58 +18,41 @@ package com.android.testutils
import android.os.Build
import androidx.test.InstrumentationRegistry
-import com.android.modules.utils.build.SdkLevel
-import kotlin.test.fail
+import com.android.modules.utils.build.UnboundedSdkLevel
import org.junit.Assume.assumeTrue
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement
import java.util.regex.Pattern
-// TODO: Remove it when Build.VERSION_CODES.SC_V2 is available
-const val SC_V2 = 32
+@Deprecated("Use Build.VERSION_CODES", ReplaceWith("Build.VERSION_CODES.S_V2"))
+const val SC_V2 = Build.VERSION_CODES.S_V2
private val MAX_TARGET_SDK_ANNOTATION_RE = Pattern.compile("MaxTargetSdk([0-9]+)$")
private val targetSdk = InstrumentationRegistry.getContext().applicationInfo.targetSdkVersion
+private fun isDevSdkInRange(minExclusive: String?, maxInclusive: String?): Boolean {
+ return (minExclusive == null || !UnboundedSdkLevel.isAtMost(minExclusive)) &&
+ (maxInclusive == null || UnboundedSdkLevel.isAtMost(maxInclusive))
+}
+
/**
- * Returns true if the development SDK version of the device is in the provided range.
+ * Returns true if the development SDK version of the device is in the provided annotation range.
*
- * If the device is not using a release SDK, the development SDK is considered to be higher than
- * [Build.VERSION.SDK_INT].
+ * If the device is not using a release SDK, the development SDK differs from
+ * [Build.VERSION.SDK_INT], and is indicated by the device codenames; see [UnboundedSdkLevel].
*/
-fun isDevSdkInRange(minExclusive: Int?, maxInclusive: Int?): Boolean {
- return (minExclusive == null || isDevSdkAfter(minExclusive)) &&
- (maxInclusive == null || isDevSdkUpTo(maxInclusive))
-}
-
-private fun isDevSdkAfter(minExclusive: Int): Boolean {
- // A development build for T typically has SDK_INT = 30 (R) or SDK_INT = 31 (S), so SDK_INT
- // alone cannot be used to check the SDK version.
- // For recent SDKs that still have development builds used for testing, use SdkLevel utilities
- // instead of SDK_INT.
- return when (minExclusive) {
- // TODO: Use Build.VERSION_CODES.SC_V2 when it is available
- SC_V2 -> SdkLevel.isAtLeastT()
- // TODO: To use SdkLevel.isAtLeastSv2 when available
- Build.VERSION_CODES.S -> fail("Do you expect to ignore the test until T? Use SC_V2 instead")
- Build.VERSION_CODES.R -> SdkLevel.isAtLeastS()
- // Development builds of SDK versions <= R are not used anymore
- else -> Build.VERSION.SDK_INT > minExclusive
- }
-}
-
-private fun isDevSdkUpTo(maxInclusive: Int): Boolean {
- return when (maxInclusive) {
- // TODO: Use Build.VERSION_CODES.SC_V2 when it is available
- SC_V2 -> !SdkLevel.isAtLeastT()
- // TODO: To use SdkLevel.isAtLeastSv2 when available
- Build.VERSION_CODES.S ->
- fail("Do you expect to ignore the test before T? Use SC_V2 instead")
- Build.VERSION_CODES.R -> !SdkLevel.isAtLeastS()
- // Development builds of SDK versions <= R are not used anymore
- else -> Build.VERSION.SDK_INT <= maxInclusive
- }
+fun isDevSdkInRange(
+ ignoreUpTo: DevSdkIgnoreRule.IgnoreUpTo?,
+ ignoreAfter: DevSdkIgnoreRule.IgnoreAfter?
+): Boolean {
+ val minExclusive =
+ if (ignoreUpTo?.value == 0) ignoreUpTo.codename
+ else ignoreUpTo?.value?.toString()
+ val maxInclusive =
+ if (ignoreAfter?.value == 0) ignoreAfter.codename
+ else ignoreAfter?.value?.toString()
+ return isDevSdkInRange(minExclusive, maxInclusive)
}
private fun getMaxTargetSdk(description: Description): Int? {
@@ -86,13 +69,23 @@ private fun getMaxTargetSdk(description: Description): Int? {
* If the device is not using a release SDK, the development SDK is considered to be higher than
* [Build.VERSION.SDK_INT].
*
- * @param ignoreClassUpTo Skip all tests in the class if the device dev SDK is <= this value.
- * @param ignoreClassAfter Skip all tests in the class if the device dev SDK is > this value.
+ * @param ignoreClassUpTo Skip all tests in the class if the device dev SDK is <= this codename or
+ * SDK level.
+ * @param ignoreClassAfter Skip all tests in the class if the device dev SDK is > this codename or
+ * SDK level.
*/
class DevSdkIgnoreRule @JvmOverloads constructor(
- private val ignoreClassUpTo: Int? = null,
- private val ignoreClassAfter: Int? = null
+ private val ignoreClassUpTo: String? = null,
+ private val ignoreClassAfter: String? = null
) : TestRule {
+ /**
+ * @param ignoreClassUpTo Skip all tests in the class if the device dev SDK is <= this value.
+ * @param ignoreClassAfter Skip all tests in the class if the device dev SDK is > this value.
+ */
+ @JvmOverloads
+ constructor(ignoreClassUpTo: Int?, ignoreClassAfter: Int? = null) : this(
+ ignoreClassUpTo?.toString(), ignoreClassAfter?.toString())
+
override fun apply(base: Statement, description: Description): Statement {
return IgnoreBySdkStatement(base, description)
}
@@ -103,7 +96,7 @@ class DevSdkIgnoreRule @JvmOverloads constructor(
* If the device is not using a release SDK, the development SDK is considered to be higher
* than [Build.VERSION.SDK_INT].
*/
- annotation class IgnoreAfter(val value: Int)
+ annotation class IgnoreAfter(val value: Int = 0, val codename: String = "")
/**
* Ignore the test for any development SDK that lower than or equal to [value].
@@ -111,7 +104,7 @@ class DevSdkIgnoreRule @JvmOverloads constructor(
* If the device is not using a release SDK, the development SDK is considered to be higher
* than [Build.VERSION.SDK_INT].
*/
- annotation class IgnoreUpTo(val value: Int)
+ annotation class IgnoreUpTo(val value: Int = 0, val codename: String = "")
private inner class IgnoreBySdkStatement(
private val base: Statement,
@@ -124,7 +117,7 @@ class DevSdkIgnoreRule @JvmOverloads constructor(
val devSdkMessage = "Skipping test for build ${Build.VERSION.CODENAME} " +
"with SDK ${Build.VERSION.SDK_INT}"
assumeTrue(devSdkMessage, isDevSdkInRange(ignoreClassUpTo, ignoreClassAfter))
- assumeTrue(devSdkMessage, isDevSdkInRange(ignoreUpTo?.value, ignoreAfter?.value))
+ assumeTrue(devSdkMessage, isDevSdkInRange(ignoreUpTo, ignoreAfter))
val maxTargetSdk = getMaxTargetSdk(description)
if (maxTargetSdk != null) {
diff --git a/common/testutils/devicetests/com/android/testutils/DevSdkIgnoreRunner.kt b/common/testutils/devicetests/com/android/testutils/DevSdkIgnoreRunner.kt
index da7bf97b..2e73666b 100644
--- a/common/testutils/devicetests/com/android/testutils/DevSdkIgnoreRunner.kt
+++ b/common/testutils/devicetests/com/android/testutils/DevSdkIgnoreRunner.kt
@@ -52,7 +52,7 @@ class DevSdkIgnoreRunner(private val klass: Class<*>) : Runner(), Filterable, So
val ignoreAfter = it.getAnnotation(IgnoreAfter::class.java)
val ignoreUpTo = it.getAnnotation(IgnoreUpTo::class.java)
- if (isDevSdkInRange(ignoreUpTo?.value, ignoreAfter?.value)) AndroidJUnit4(klass) else null
+ if (isDevSdkInRange(ignoreUpTo, ignoreAfter)) AndroidJUnit4(klass) else null
}
override fun run(notifier: RunNotifier) {
diff --git a/common/testutils/devicetests/com/android/testutils/PacketResponder.kt b/common/testutils/devicetests/com/android/testutils/PacketResponder.kt
index daf29e43..964c6c60 100644
--- a/common/testutils/devicetests/com/android/testutils/PacketResponder.kt
+++ b/common/testutils/devicetests/com/android/testutils/PacketResponder.kt
@@ -54,6 +54,7 @@ abstract class PacketResponder(
*/
fun stop() {
replyThread.interrupt()
+ replyThread.join()
}
private inner class ReplyThread(name: String) : Thread(name) {
diff --git a/common/testutils/devicetests/com/android/testutils/RouterAdvertisementResponder.java b/common/testutils/devicetests/com/android/testutils/RouterAdvertisementResponder.java
index 4ad93d85..478161da 100644
--- a/common/testutils/devicetests/com/android/testutils/RouterAdvertisementResponder.java
+++ b/common/testutils/devicetests/com/android/testutils/RouterAdvertisementResponder.java
@@ -18,12 +18,15 @@ package com.android.testutils;
import static android.system.OsConstants.IPPROTO_ICMPV6;
-import static com.android.net.module.util.NetworkStackConstants.ETHER_ADDR_LEN;
-import static com.android.net.module.util.NetworkStackConstants.ETHER_SRC_ADDR_OFFSET;
import static com.android.net.module.util.NetworkStackConstants.ETHER_TYPE_IPV6;
+import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ND_OPTION_SLLA;
+import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ND_OPTION_TLLA;
+import static com.android.net.module.util.NetworkStackConstants.ICMPV6_NEIGHBOR_SOLICITATION;
import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ROUTER_SOLICITATION;
import static com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_ALL_NODES_MULTICAST;
-import static com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_ALL_ROUTERS_MULTICAST;
+import static com.android.net.module.util.NetworkStackConstants.NEIGHBOR_ADVERTISEMENT_FLAG_OVERRIDE;
+import static com.android.net.module.util.NetworkStackConstants.NEIGHBOR_ADVERTISEMENT_FLAG_ROUTER;
+import static com.android.net.module.util.NetworkStackConstants.NEIGHBOR_ADVERTISEMENT_FLAG_SOLICITED;
import static com.android.net.module.util.NetworkStackConstants.PIO_FLAG_AUTONOMOUS;
import static com.android.net.module.util.NetworkStackConstants.PIO_FLAG_ON_LINK;
@@ -31,6 +34,8 @@ import android.net.InetAddresses;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.MacAddress;
+import android.util.ArrayMap;
+import android.util.Log;
import android.util.Pair;
import com.android.net.module.util.Ipv6Utils;
@@ -38,18 +43,19 @@ import com.android.net.module.util.Struct;
import com.android.net.module.util.structs.EthernetHeader;
import com.android.net.module.util.structs.Icmpv6Header;
import com.android.net.module.util.structs.Ipv6Header;
+import com.android.net.module.util.structs.LlaOption;
+import com.android.net.module.util.structs.NsHeader;
import com.android.net.module.util.structs.PrefixInformationOption;
import com.android.net.module.util.structs.RdnssOption;
import java.io.IOException;
import java.net.Inet6Address;
import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
+import java.util.Map;
/**
- * RA responder class useful for tests that require a provisioned interface.
+ * ND (RA & NA) responder class useful for tests that require a provisioned IPv6 interface.
+ * TODO: rename to NdResponder
*/
public class RouterAdvertisementResponder extends PacketResponder {
private static final String TAG = "RouterAdvertisementResponder";
@@ -57,26 +63,28 @@ public class RouterAdvertisementResponder extends PacketResponder {
private static final Inet6Address DNS_SERVER =
(Inet6Address) InetAddresses.parseNumericAddress("2001:4860:4860::64");
private final TapPacketReader mPacketReader;
- private final List<Pair<MacAddress, Inet6Address>> mRouterList = new ArrayList<>();
+ // Maps IPv6 address to MacAddress and isRouter boolean.
+ private final Map<Inet6Address, Pair<MacAddress, Boolean>> mNeighborMap = new ArrayMap<>();
public RouterAdvertisementResponder(TapPacketReader packetReader) {
- super(packetReader, RouterAdvertisementResponder::isRouterSolicitation, TAG);
+ super(packetReader, RouterAdvertisementResponder::isRsOrNs, TAG);
mPacketReader = packetReader;
}
- private static boolean isRouterSolicitation(byte[] packet) {
+ /** Returns true if the packet is a router solicitation or neighbor solicitation message. */
+ private static boolean isRsOrNs(byte[] packet) {
final ByteBuffer buffer = ByteBuffer.wrap(packet);
final EthernetHeader ethHeader = Struct.parse(EthernetHeader.class, buffer);
if (ethHeader.etherType != ETHER_TYPE_IPV6) {
return false;
}
final Ipv6Header ipv6Header = Struct.parse(Ipv6Header.class, buffer);
- if (ipv6Header.nextHeader != IPPROTO_ICMPV6
- || !ipv6Header.dstIp.equals(IPV6_ADDR_ALL_ROUTERS_MULTICAST)) {
+ if (ipv6Header.nextHeader != IPPROTO_ICMPV6) {
return false;
}
final Icmpv6Header icmpv6Header = Struct.parse(Icmpv6Header.class, buffer);
- return icmpv6Header.type == ICMPV6_ROUTER_SOLICITATION;
+ return icmpv6Header.type == ICMPV6_ROUTER_SOLICITATION
+ || icmpv6Header.type == ICMPV6_NEIGHBOR_SOLICITATION;
}
/**
@@ -85,7 +93,16 @@ public class RouterAdvertisementResponder extends PacketResponder {
* @param ip the link-local address of the router.
*/
public void addRouterEntry(MacAddress mac, Inet6Address ip) {
- mRouterList.add(new Pair<>(mac, ip));
+ mNeighborMap.put(ip, new Pair<>(mac, true));
+ }
+
+ /**
+ * Adds a new neighbor to be advertised.
+ * @param mac the mac address of the neighbor.
+ * @param ip the link-local address of the neighbor.
+ */
+ public void addNeighborEntry(MacAddress mac, Inet6Address ip) {
+ mNeighborMap.put(ip, new Pair<>(mac, false));
}
private ByteBuffer buildPrefixOption() {
@@ -99,25 +116,72 @@ public class RouterAdvertisementResponder extends PacketResponder {
return RdnssOption.build(3600/*lifetime, must be at least 120*/, DNS_SERVER);
}
+ private ByteBuffer buildSllaOption(MacAddress srcMac) {
+ return LlaOption.build((byte) ICMPV6_ND_OPTION_SLLA, srcMac);
+ }
+
private ByteBuffer buildRaPacket(MacAddress srcMac, MacAddress dstMac, Inet6Address srcIp) {
return Ipv6Utils.buildRaPacket(srcMac, dstMac, srcIp, IPV6_ADDR_ALL_NODES_MULTICAST,
(byte) 0 /*M=0, O=0*/, 3600 /*lifetime*/, 0 /*reachableTime, unspecified*/,
- 0/*retransTimer, unspecified*/, buildPrefixOption(), buildRdnssOption());
+ 0/*retransTimer, unspecified*/, buildPrefixOption(), buildRdnssOption(),
+ buildSllaOption(srcMac));
+ }
+
+ private static void sendResponse(TapPacketReader reader, ByteBuffer buffer) {
+ try {
+ reader.sendResponse(buffer);
+ } catch (IOException e) {
+ // Throwing an exception here will crash the test process. Let's stick to logging, as
+ // the test will fail either way.
+ Log.e(TAG, "Failed to send buffer", e);
+ }
+ }
+
+ private void replyToRouterSolicitation(TapPacketReader reader, MacAddress dstMac) {
+ for (Map.Entry<Inet6Address, Pair<MacAddress, Boolean>> it : mNeighborMap.entrySet()) {
+ final boolean isRouter = it.getValue().second;
+ if (!isRouter) {
+ continue;
+ }
+ final ByteBuffer raResponse = buildRaPacket(it.getValue().first, dstMac, it.getKey());
+ sendResponse(reader, raResponse);
+ }
+ }
+
+ private void replyToNeighborSolicitation(TapPacketReader reader, MacAddress dstMac,
+ Inet6Address dstIp, Inet6Address targetIp) {
+ final Pair<MacAddress, Boolean> neighbor = mNeighborMap.get(targetIp);
+ if (neighbor == null) {
+ return;
+ }
+
+ final MacAddress srcMac = neighbor.first;
+ final boolean isRouter = neighbor.second;
+ int flags = NEIGHBOR_ADVERTISEMENT_FLAG_SOLICITED | NEIGHBOR_ADVERTISEMENT_FLAG_OVERRIDE;
+ if (isRouter) {
+ flags |= NEIGHBOR_ADVERTISEMENT_FLAG_ROUTER;
+ }
+
+ final ByteBuffer tlla = LlaOption.build((byte) ICMPV6_ND_OPTION_TLLA, srcMac);
+ final ByteBuffer naResponse = Ipv6Utils.buildNaPacket(srcMac, dstMac, targetIp, dstIp,
+ flags, targetIp, tlla);
+ sendResponse(reader, naResponse);
}
@Override
protected void replyToPacket(byte[] packet, TapPacketReader reader) {
- final MacAddress srcMac = MacAddress.fromBytes(
- Arrays.copyOfRange(packet, ETHER_SRC_ADDR_OFFSET,
- ETHER_SRC_ADDR_OFFSET + ETHER_ADDR_LEN));
-
- for (Pair<MacAddress, Inet6Address> it : mRouterList) {
- final ByteBuffer raResponse = buildRaPacket(it.first, srcMac, it.second);
- try {
- reader.sendResponse(raResponse);
- } catch (IOException e) {
- throw new RuntimeException("Failed to send RA");
- }
+ final ByteBuffer buf = ByteBuffer.wrap(packet);
+ // Messages are filtered by parent class, so it is safe to assume that packet is either an
+ // RS or NS.
+ final EthernetHeader ethHdr = Struct.parse(EthernetHeader.class, buf);
+ final Ipv6Header ipv6Hdr = Struct.parse(Ipv6Header.class, buf);
+ final Icmpv6Header icmpv6Header = Struct.parse(Icmpv6Header.class, buf);
+
+ if (icmpv6Header.type == ICMPV6_ROUTER_SOLICITATION) {
+ replyToRouterSolicitation(reader, ethHdr.srcMac);
+ } else if (icmpv6Header.type == ICMPV6_NEIGHBOR_SOLICITATION) {
+ final NsHeader nsHeader = Struct.parse(NsHeader.class, buf);
+ replyToNeighborSolicitation(reader, ethHdr.srcMac, ipv6Hdr.srcIp, nsHeader.target);
}
}
}
diff --git a/common/testutils/devicetests/com/android/testutils/filters/CtsNetTestCasesMaxTargetSdk30.kt b/common/testutils/devicetests/com/android/testutils/filters/CtsNetTestCasesMaxTargetSdk30.kt
new file mode 100644
index 00000000..843c41e6
--- /dev/null
+++ b/common/testutils/devicetests/com/android/testutils/filters/CtsNetTestCasesMaxTargetSdk30.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2022 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.testutils.filters
+
+/**
+ * Only run this test in the CtsNetTestCasesMaxTargetSdk30 suite.
+ */
+annotation class CtsNetTestCasesMaxTargetSdk30(val reason: String)