diff options
author | Patrick Rohr <prohr@google.com> | 2022-04-04 12:17:39 +0200 |
---|---|---|
committer | Patrick Rohr <prohr@google.com> | 2022-04-08 13:52:15 +0200 |
commit | 6df5cec92dae8cd43b3b229a6a056163ac571079 (patch) | |
tree | e31d4d526a8d0425c6d39dd586711da5f04d00f3 /common | |
parent | 1ffa0f9b2dfa59ab02acc9b25bda5e578cb24e31 (diff) | |
download | net-6df5cec92dae8cd43b3b229a6a056163ac571079.tar.gz |
Create RouterAdvertisementResponder
This is useful for tests that need a provisioned tap interface.
Test: TH
Change-Id: I9a2eb3d8697bbcbfdb65563da609534b929a0715
Diffstat (limited to 'common')
-rw-r--r-- | common/testutils/devicetests/com/android/testutils/RouterAdvertisementResponder.java | 123 |
1 files changed, 123 insertions, 0 deletions
diff --git a/common/testutils/devicetests/com/android/testutils/RouterAdvertisementResponder.java b/common/testutils/devicetests/com/android/testutils/RouterAdvertisementResponder.java new file mode 100644 index 00000000..4ad93d85 --- /dev/null +++ b/common/testutils/devicetests/com/android/testutils/RouterAdvertisementResponder.java @@ -0,0 +1,123 @@ +/* + * 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; + +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_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.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.Pair; + +import com.android.net.module.util.Ipv6Utils; +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.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; + +/** + * RA responder class useful for tests that require a provisioned interface. + */ +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<>(); + + public RouterAdvertisementResponder(TapPacketReader packetReader) { + super(packetReader, RouterAdvertisementResponder::isRouterSolicitation, TAG); + mPacketReader = packetReader; + } + + private static boolean isRouterSolicitation(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)) { + return false; + } + final Icmpv6Header icmpv6Header = Struct.parse(Icmpv6Header.class, buffer); + return icmpv6Header.type == ICMPV6_ROUTER_SOLICITATION; + } + + /** + * Adds a new router to be advertised. + * @param mac the mac address of the router. + * @param ip the link-local address of the router. + */ + public void addRouterEntry(MacAddress mac, Inet6Address ip) { + mRouterList.add(new Pair<>(mac, ip)); + } + + 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*/); + } + + private ByteBuffer buildRdnssOption() { + return RdnssOption.build(3600/*lifetime, must be at least 120*/, DNS_SERVER); + } + + 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()); + } + + @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"); + } + } + } +} |