diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-08-03 17:05:08 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-08-03 17:05:08 +0000 |
commit | 72285ef2923a82ee3c00c1447df6087327e20715 (patch) | |
tree | 2d72576be7778ff441a64c94aadcbb9e3f20b309 | |
parent | 824772c5d18d1f1d47ff4ec1ee6a741fd1cc4661 (diff) | |
parent | 243dc1f73a53395a359075ae3540c1686cc00356 (diff) | |
download | net-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
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) |