summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-06-08 17:28:31 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-06-08 17:28:31 +0000
commit82ea1ce979f30172399702a717210615e2f5a147 (patch)
tree799cd95e915f619f14c686b98aa964babdabb87e
parent46f19e4a25ffc7cc8fb688e54692bbb47ebceb52 (diff)
parentacea63e2786d776ed23915f5d3af31a7d14eb622 (diff)
downloadnet-android13-platform-release.tar.gz
Change-Id: I1aa46039907e7b975361f9132aad25b2dcfbfdcb
-rw-r--r--common/testutils/devicetests/com/android/testutils/RouterAdvertisementResponder.java138
1 files changed, 107 insertions, 31 deletions
diff --git a/common/testutils/devicetests/com/android/testutils/RouterAdvertisementResponder.java b/common/testutils/devicetests/com/android/testutils/RouterAdvertisementResponder.java
index 4ad93d85..51df9351 100644
--- a/common/testutils/devicetests/com/android/testutils/RouterAdvertisementResponder.java
+++ b/common/testutils/devicetests/com/android/testutils/RouterAdvertisementResponder.java
@@ -18,19 +18,21 @@ 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_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;
import android.net.InetAddresses;
import android.net.IpPrefix;
-import android.net.LinkAddress;
import android.net.MacAddress;
+import android.util.ArrayMap;
import android.util.Pair;
import com.android.net.module.util.Ipv6Utils;
@@ -38,45 +40,64 @@ 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;
+import java.util.Objects;
+import java.util.Random;
/**
- * 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";
- private static final LinkAddress SLAAC_PREFIX = new LinkAddress("2001:db8::/64");
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<>();
+ private final IpPrefix mPrefix;
- public RouterAdvertisementResponder(TapPacketReader packetReader) {
- super(packetReader, RouterAdvertisementResponder::isRouterSolicitation, TAG);
+ public RouterAdvertisementResponder(TapPacketReader packetReader, IpPrefix prefix) {
+ super(packetReader, RouterAdvertisementResponder::isRsOrNs, TAG);
mPacketReader = packetReader;
+ mPrefix = Objects.requireNonNull(prefix);
+ }
+
+ public RouterAdvertisementResponder(TapPacketReader packetReader) {
+ this(packetReader, makeRandomPrefix());
}
- private static boolean isRouterSolicitation(byte[] packet) {
+ private static IpPrefix makeRandomPrefix() {
+ final byte[] prefixBytes = new IpPrefix("2001:db8::/64").getAddress().getAddress();
+ final Random r = new Random();
+ for (int i = 4; i < 8; i++) {
+ prefixBytes[i] = (byte) r.nextInt();
+ }
+ return new IpPrefix(prefixBytes, 64);
+ }
+
+ /** 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,14 +106,29 @@ 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));
+ }
+
+ /**
+ * @return the prefix that is announced in the Router Advertisements sent by this object.
+ */
+ public IpPrefix getPrefix() {
+ return mPrefix;
}
private ByteBuffer buildPrefixOption() {
return PrefixInformationOption.build(
- new IpPrefix(SLAAC_PREFIX.getAddress(), SLAAC_PREFIX.getPrefixLength()),
- (byte) (PIO_FLAG_ON_LINK | PIO_FLAG_AUTONOMOUS), 3600/*valid lifetime*/,
- 3600/*preferred lifetime*/);
+ mPrefix, (byte) (PIO_FLAG_ON_LINK | PIO_FLAG_AUTONOMOUS),
+ 3600 /* valid lifetime */, 3600 /* preferred lifetime */);
}
private ByteBuffer buildRdnssOption() {
@@ -105,19 +141,59 @@ public class RouterAdvertisementResponder extends PacketResponder {
0/*retransTimer, unspecified*/, buildPrefixOption(), buildRdnssOption());
}
+ private static void sendResponse(TapPacketReader reader, ByteBuffer buffer) {
+ try {
+ reader.sendResponse(buffer);
+ } catch (IOException e) {
+ throw new RuntimeException("Failed to send buffer.");
+ }
+ }
+
+ 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);
}
}
}