[automerger skipped] Move frameworks/base depedencies into NetworkStack. am: d5664eab03 -s ours am skip reason: Merged-In Ibb315561a8940d0a2af2b44237e39c7e7b1963aa with SHA-1 2ff6ee4131 is already in history Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/modules/NetworkStack/+/15032864 Change-Id: Id22e3e65827b7fb0f53a8b86f8f5613299c9ae22
diff --git a/common/moduleutils/src/android/net/ip/ConntrackMonitor.java b/common/moduleutils/src/android/net/ip/ConntrackMonitor.java index 9189002..6c72984 100644 --- a/common/moduleutils/src/android/net/ip/ConntrackMonitor.java +++ b/common/moduleutils/src/android/net/ip/ConntrackMonitor.java
@@ -51,6 +51,12 @@ public static final int NF_NETLINK_CONNTRACK_UPDATE = 2; public static final int NF_NETLINK_CONNTRACK_DESTROY = 4; + // The socket receive buffer size in bytes. If too many conntrack messages are sent too + // quickly, the conntrack messages can overflow the socket receive buffer. This can happen + // if too many connections are disconnected by losing network and so on. Use a large-enough + // buffer to avoid the error ENOBUFS while listening to the conntrack messages. + private static final int SOCKET_RECV_BUFSIZE = 6 * 1024 * 1024; + /** * A class for describing parsed netfilter conntrack events. */ @@ -176,7 +182,7 @@ public ConntrackMonitor(@NonNull Handler h, @NonNull SharedLog log, @NonNull ConntrackEventConsumer cb) { super(h, log, TAG, OsConstants.NETLINK_NETFILTER, NF_NETLINK_CONNTRACK_NEW - | NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY); + | NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY, SOCKET_RECV_BUFSIZE); mConsumer = cb; }
diff --git a/common/moduleutils/src/android/net/ip/NetlinkMonitor.java b/common/moduleutils/src/android/net/ip/NetlinkMonitor.java index 3d314f1..2025967 100644 --- a/common/moduleutils/src/android/net/ip/NetlinkMonitor.java +++ b/common/moduleutils/src/android/net/ip/NetlinkMonitor.java
@@ -21,6 +21,8 @@ import static android.system.OsConstants.AF_NETLINK; import static android.system.OsConstants.SOCK_DGRAM; import static android.system.OsConstants.SOCK_NONBLOCK; +import static android.system.OsConstants.SOL_SOCKET; +import static android.system.OsConstants.SO_RCVBUF; import android.annotation.NonNull; import android.net.netlink.NetlinkErrorMessage; @@ -56,9 +58,13 @@ protected final String mTag; private final int mFamily; private final int mBindGroups; + private final int mSockRcvbufSize; private static final boolean DBG = false; + // Default socket receive buffer size. This means the specific buffer size is not set. + private static final int DEFAULT_SOCKET_RECV_BUFSIZE = -1; + /** * Constructs a new {@code NetlinkMonitor} instance. * @@ -68,14 +74,23 @@ * @param tag The log tag to use for log messages. * @param family the Netlink socket family to, e.g., {@code NETLINK_ROUTE}. * @param bindGroups the netlink groups to bind to. + * @param sockRcvbufSize the specific socket receive buffer size in bytes. -1 means that don't + * set the specific socket receive buffer size in #createFd and use the default value in + * /proc/sys/net/core/rmem_default file. See SO_RCVBUF in man-pages/socket. */ public NetlinkMonitor(@NonNull Handler h, @NonNull SharedLog log, @NonNull String tag, - int family, int bindGroups) { + int family, int bindGroups, int sockRcvbufSize) { super(h, NetlinkSocket.DEFAULT_RECV_BUFSIZE); mLog = log.forSubComponent(tag); mTag = tag; mFamily = family; mBindGroups = bindGroups; + mSockRcvbufSize = sockRcvbufSize; + } + + public NetlinkMonitor(@NonNull Handler h, @NonNull SharedLog log, @NonNull String tag, + int family, int bindGroups) { + this(h, log, tag, family, bindGroups, DEFAULT_SOCKET_RECV_BUFSIZE); } @Override @@ -84,6 +99,9 @@ try { fd = Os.socket(AF_NETLINK, SOCK_DGRAM | SOCK_NONBLOCK, mFamily); + if (mSockRcvbufSize != DEFAULT_SOCKET_RECV_BUFSIZE) { + Os.setsockoptInt(fd, SOL_SOCKET, SO_RCVBUF, mSockRcvbufSize); + } Os.bind(fd, makeNetlinkSocketAddress(0, mBindGroups)); NetlinkSocket.connectToKernel(fd);
diff --git a/src/android/net/apf/ApfFilter.java b/src/android/net/apf/ApfFilter.java index 34469b8..7a13392 100644 --- a/src/android/net/apf/ApfFilter.java +++ b/src/android/net/apf/ApfFilter.java
@@ -1411,7 +1411,7 @@ // pass // if it's ICMPv6 RS to any: // drop - // if it's ICMPv6 NA to ff02::1 or ff02::2: + // if it's ICMPv6 NA to anything in ff02::/120 // drop // if keepalive ack // drop @@ -1495,7 +1495,7 @@ * <li>Drop all broadcast non-IP non-ARP packets. * <li>Pass all non-ICMPv6 IPv6 packets, * <li>Pass all non-IPv4 and non-IPv6 packets, - * <li>Drop IPv6 ICMPv6 NAs to ff02::1 or ff02::2. + * <li>Drop IPv6 ICMPv6 NAs to anything in ff02::/120. * <li>Drop IPv6 ICMPv6 RSs. * <li>Filter IPv4 packets (see generateIPv4FilterLocked()) * <li>Filter IPv6 packets (see generateIPv6FilterLocked())
diff --git a/src/android/net/ip/IpClient.java b/src/android/net/ip/IpClient.java index a57e99d..445f915 100644 --- a/src/android/net/ip/IpClient.java +++ b/src/android/net/ip/IpClient.java
@@ -61,6 +61,7 @@ import android.net.metrics.IpManagerEvent; import android.net.networkstack.aidl.dhcp.DhcpOption; import android.net.shared.InitialConfiguration; +import android.net.shared.Layer2Information; import android.net.shared.ProvisioningConfiguration; import android.net.shared.ProvisioningConfiguration.ScanResultInfo; import android.net.shared.ProvisioningConfiguration.ScanResultInfo.InformationElement; @@ -147,6 +148,7 @@ * @hide */ public class IpClient extends StateMachine { + private static final String TAG = IpClient.class.getSimpleName(); private static final boolean DBG = false; // For message logging. @@ -872,26 +874,28 @@ false /* defaultEnabled */); } - private void setInitialBssid(final ProvisioningConfiguration req) { - final ScanResultInfo scanResultInfo = req.mScanResultInfo; - mCurrentBssid = null; + @VisibleForTesting + static MacAddress getInitialBssid(final Layer2Information layer2Info, + final ScanResultInfo scanResultInfo, boolean isAtLeastS) { + MacAddress bssid = null; // http://b/185202634 // ScanResultInfo is not populated in some situations. // On S and above, prefer getting the BSSID from the Layer2Info. // On R and below, get the BSSID from the ScanResultInfo and fall back to // getting it from the Layer2Info. This ensures no regressions if any R // devices pass in a null or meaningless BSSID in the Layer2Info. - if (!ShimUtils.isAtLeastS() && scanResultInfo != null) { + if (!isAtLeastS && scanResultInfo != null) { try { - mCurrentBssid = MacAddress.fromString(scanResultInfo.getBssid()); + bssid = MacAddress.fromString(scanResultInfo.getBssid()); } catch (IllegalArgumentException e) { - Log.wtf(mTag, "Invalid BSSID: " + scanResultInfo.getBssid() + Log.wtf(TAG, "Invalid BSSID: " + scanResultInfo.getBssid() + " in provisioning configuration", e); } } - if (mCurrentBssid == null && req.mLayer2Info != null) { - mCurrentBssid = req.mLayer2Info.mBssid; + if (bssid == null && layer2Info != null) { + bssid = layer2Info.mBssid; } + return bssid; } private boolean shouldDisableAcceptRaOnProvisioningLoss() { @@ -922,7 +926,8 @@ return; } - setInitialBssid(req); + mCurrentBssid = getInitialBssid(req.mLayer2Info, req.mScanResultInfo, + ShimUtils.isAtLeastS()); if (req.mLayer2Info != null) { mL2Key = req.mLayer2Info.mL2Key; mCluster = req.mLayer2Info.mCluster;
diff --git a/src/com/android/networkstack/packets/NeighborSolicitation.java b/src/com/android/networkstack/packets/NeighborSolicitation.java new file mode 100644 index 0000000..e743209 --- /dev/null +++ b/src/com/android/networkstack/packets/NeighborSolicitation.java
@@ -0,0 +1,134 @@ +/* + * Copyright (C) 2021 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.networkstack.packets; + +import static com.android.net.module.util.NetworkStackConstants.ETHER_HEADER_LEN; +import static com.android.net.module.util.NetworkStackConstants.ICMPV6_HEADER_MIN_LEN; +import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ND_OPTION_SLLA; +import static com.android.net.module.util.NetworkStackConstants.IPV6_HEADER_LEN; + +import android.net.MacAddress; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +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.LlaOption; +import com.android.net.module.util.structs.NsHeader; + +import java.net.Inet6Address; +import java.nio.ByteBuffer; + +/** + * Defines basic data and operations needed to build and parse Neighbor Solicitation packet. + * + * @hide + */ +public class NeighborSolicitation { + private static final int NS_HEADER_LEN = Struct.getSize(NsHeader.class); + + @NonNull + public final EthernetHeader ethHdr; + @NonNull + public final Ipv6Header ipv6Hdr; + @NonNull + public final Icmpv6Header icmpv6Hdr; + @NonNull + public final NsHeader nsHdr; + @Nullable + public final LlaOption slla; + + public NeighborSolicitation(@NonNull final EthernetHeader ethHdr, + @NonNull final Ipv6Header ipv6Hdr, @NonNull final Icmpv6Header icmpv6Hdr, + @NonNull final NsHeader nsHdr, @Nullable final LlaOption slla) { + this.ethHdr = ethHdr; + this.ipv6Hdr = ipv6Hdr; + this.icmpv6Hdr = icmpv6Hdr; + this.nsHdr = nsHdr; + this.slla = slla; + } + + /** + * Convert a Neighbor Solicitation instance to ByteBuffer. + */ + public ByteBuffer toByteBuffer() { + final int etherHeaderLen = Struct.getSize(EthernetHeader.class); + final int ipv6HeaderLen = Struct.getSize(Ipv6Header.class); + final int icmpv6HeaderLen = Struct.getSize(Icmpv6Header.class); + final int nsHeaderLen = Struct.getSize(NsHeader.class); + final int sllaOptionLen = (slla == null) ? 0 : Struct.getSize(LlaOption.class); + final ByteBuffer packet = ByteBuffer.allocate(etherHeaderLen + ipv6HeaderLen + + icmpv6HeaderLen + nsHeaderLen + sllaOptionLen); + + ethHdr.writeToByteBuffer(packet); + ipv6Hdr.writeToByteBuffer(packet); + icmpv6Hdr.writeToByteBuffer(packet); + nsHdr.writeToByteBuffer(packet); + if (slla != null) { + slla.writeToByteBuffer(packet); + } + packet.flip(); + + return packet; + } + + /** + * Build a Neighbor Solicitation packet from the required specified parameters. + */ + public static ByteBuffer build(@NonNull final MacAddress srcMac, + @NonNull final MacAddress dstMac, @NonNull final Inet6Address srcIp, + @NonNull final Inet6Address dstIp, @NonNull final Inet6Address target) { + final ByteBuffer slla = LlaOption.build((byte) ICMPV6_ND_OPTION_SLLA, srcMac); + return Ipv6Utils.buildNsPacket(srcMac, dstMac, srcIp, dstIp, target, slla); + } + + /** + * Parse a Neighbor Solicitation packet from ByteBuffer. + */ + public static NeighborSolicitation parse(@NonNull final byte[] recvbuf, final int length) + throws ParseException { + if (length < ETHER_HEADER_LEN + IPV6_HEADER_LEN + ICMPV6_HEADER_MIN_LEN + NS_HEADER_LEN + || recvbuf.length < length) { + throw new ParseException("Invalid packet length: " + length); + } + final ByteBuffer packet = ByteBuffer.wrap(recvbuf, 0, length); + + // Parse each header and option in Neighbor Solicitation packet in order. + final EthernetHeader ethHdr = Struct.parse(EthernetHeader.class, packet); + final Ipv6Header ipv6Hdr = Struct.parse(Ipv6Header.class, packet); + final Icmpv6Header icmpv6Hdr = Struct.parse(Icmpv6Header.class, packet); + final NsHeader nsHdr = Struct.parse(NsHeader.class, packet); + final LlaOption slla = (packet.remaining() == 0) + ? null + : Struct.parse(LlaOption.class, packet); + + return new NeighborSolicitation(ethHdr, ipv6Hdr, icmpv6Hdr, nsHdr, slla); + } + + /** + * Thrown when parsing Neighbor Solicitation packet failed. + */ + public static class ParseException extends Exception { + ParseException(String message) { + super(message); + } + } +}
diff --git a/tests/integration/Android.bp b/tests/integration/Android.bp index 7a2a471..3842b50 100644 --- a/tests/integration/Android.bp +++ b/tests/integration/Android.bp
@@ -72,6 +72,7 @@ platform_apis: true, test_suites: ["device-tests"], min_sdk_version: "29", + target_sdk_version: "30", } // Network stack next integration tests. @@ -111,6 +112,7 @@ certificate: "networkstack", platform_apis: true, min_sdk_version: "29", + target_sdk_version: "30", test_suites: ["device-tests", "mts"], test_config: "AndroidTest_Coverage.xml", defaults: ["NetworkStackIntegrationTestsJniDefaults"],
diff --git a/tests/integration/AndroidManifest.xml b/tests/integration/AndroidManifest.xml index 12f5d7d..bfd3735 100644 --- a/tests/integration/AndroidManifest.xml +++ b/tests/integration/AndroidManifest.xml
@@ -16,7 +16,6 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.server.networkstack.integrationtests" android:sharedUserId="android.uid.networkstack"> - <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29" /> <!-- Note: do not add any privileged or signature permissions that are granted to the network stack app. Otherwise, the test APK will install, but when the device is
diff --git a/tests/integration/AndroidManifest_coverage.xml b/tests/integration/AndroidManifest_coverage.xml index 660e42d..fc91e59 100644 --- a/tests/integration/AndroidManifest_coverage.xml +++ b/tests/integration/AndroidManifest_coverage.xml
@@ -16,7 +16,6 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.server.networkstack.coverage" android:sharedUserId="android.uid.networkstack"> - <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29" /> <!-- Note: do not add any privileged or signature permissions that are granted to the network stack app. Otherwise, the test APK will install, but when the device is
diff --git a/tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java b/tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java index c121b3d..e40db77 100644 --- a/tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java +++ b/tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java
@@ -3007,119 +3007,4 @@ assertEquals(0, naList.size()); assertEquals(1, arpList.size()); } - - private void doInitialBssidSetupTest(final Layer2Information layer2Info, - final ScanResultInfo scanResultInfo) throws Exception { - ProvisioningConfiguration.Builder prov = new ProvisioningConfiguration.Builder() - .withoutIpReachabilityMonitor() - .withLayer2Information(layer2Info) - .withScanResultInfo(scanResultInfo) - .withDisplayName("\"0001docomo\"") - .withoutIPv6(); - - setDhcpFeatures(false /* isDhcpLeaseCacheEnabled */, true /* shouldReplyRapidCommitAck */, - false /* isDhcpIpConflictDetectEnabled */, false /* isIPv6OnlyPreferredEnabled */); - startIpClientProvisioning(prov.build()); - - handleDhcpPackets(true /* isSuccessLease */, TEST_LEASE_DURATION_S, - true /* shouldReplyRapidCommitAck */, TEST_DEFAULT_MTU, null /* serverSentUrl */); - verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR)); - forceLayer2Roaming(); - } - - @Test @IgnoreUpTo(Build.VERSION_CODES.R) - public void testSetInitialBssidFromLayer2Info() throws Exception { - final Layer2Information layer2Info = new Layer2Information(TEST_L2KEY, TEST_CLUSTER, - MacAddress.fromString(TEST_DEFAULT_BSSID)); - - doInitialBssidSetupTest(layer2Info, null /* scanResultInfo */); - - // Initial BSSID comes from layer2Info, it's different with target roaming bssid, - // then verify that DHCPREQUEST packet is sent after roaming. - final DhcpPacket packet = getNextDhcpPacket(); - assertTrue(packet instanceof DhcpRequestPacket); - } - - @Test @IgnoreUpTo(Build.VERSION_CODES.R) - public void testSetInitialBssidFromLayer2Info_NullBssid() throws Exception { - final Layer2Information layer2Info = new Layer2Information(TEST_L2KEY, TEST_CLUSTER, - null /* bssid */); - final ScanResultInfo scanResultInfo = - makeScanResultInfo(TEST_DEFAULT_SSID, TEST_DHCP_ROAM_BSSID); - - doInitialBssidSetupTest(layer2Info, scanResultInfo); - - // Initial BSSID comes from layer2Info, it's null, no DHCPREQUEST packet - // will be sent after roaming. - final DhcpPacket packet = getNextDhcpPacket(TEST_TIMEOUT_MS); - assertNull(packet); - } - - @Test @IgnoreUpTo(Build.VERSION_CODES.R) - public void testSetInitialBssidFromLayer2Info_SameRoamingBssid() throws Exception { - final Layer2Information layer2Info = new Layer2Information(TEST_L2KEY, TEST_CLUSTER, - MacAddress.fromString(TEST_DHCP_ROAM_BSSID)); - - doInitialBssidSetupTest(layer2Info, null /* scanResultInfo */); - - // Initial BSSID comes from layer2Info, it's same with target roaming bssid, - // no DHCPREQUEST packet will be sent after roaming. - final DhcpPacket packet = getNextDhcpPacket(TEST_TIMEOUT_MS); - assertNull(packet); - } - - @Test @IgnoreAfter(Build.VERSION_CODES.R) - public void testSetInitialBssidFromScanResultInfo() throws Exception { - final ScanResultInfo scanResultInfo = - makeScanResultInfo(TEST_DEFAULT_SSID, TEST_DEFAULT_BSSID); - - doInitialBssidSetupTest(null /* layer2Info */, scanResultInfo); - - // Initial BSSID comes from ScanResultInfo, it's different with target roaming bssid, - // then verify that DHCPREQUEST packet is sent after roaming. - final DhcpPacket packet = getNextDhcpPacket(); - assertTrue(packet instanceof DhcpRequestPacket); - } - - @Test @IgnoreAfter(Build.VERSION_CODES.R) - public void testSetInitialBssidFromScanResultInfo_SameRoamingBssid() throws Exception { - final ScanResultInfo scanResultInfo = - makeScanResultInfo(TEST_DEFAULT_SSID, TEST_DHCP_ROAM_BSSID); - - doInitialBssidSetupTest(null /* layer2Info */, scanResultInfo); - - // Initial BSSID comes from ScanResultInfo, it's same with target roaming bssid, - // no DHCPREQUEST packet will be sent after roaming. - final DhcpPacket packet = getNextDhcpPacket(TEST_TIMEOUT_MS); - assertNull(packet); - } - - @Test @IgnoreAfter(Build.VERSION_CODES.R) - public void testSetInitialBssidFromScanResultInfo_BrokenInitialBssid() throws Exception { - final ScanResultInfo scanResultInfo = - makeScanResultInfo(TEST_DEFAULT_SSID, "00:11:22:33:44:"); - - doInitialBssidSetupTest(null /* layer2Info */, scanResultInfo); - - // Initial BSSID comes from ScanResultInfo, it's broken MAC address format and fallback - // to null layer2Info, no DHCPREQUEST packet will be sent after roaming. - final DhcpPacket packet = getNextDhcpPacket(TEST_TIMEOUT_MS); - assertNull(packet); - } - - @Test @IgnoreAfter(Build.VERSION_CODES.R) - public void testSetInitialBssidFromScanResultInfo_BrokenInitialBssidFallback() - throws Exception { - final Layer2Information layer2Info = new Layer2Information(TEST_L2KEY, TEST_CLUSTER, - MacAddress.fromString(TEST_DEFAULT_BSSID)); - final ScanResultInfo scanResultInfo = - makeScanResultInfo(TEST_DEFAULT_SSID, "00:11:22:33:44:"); - - doInitialBssidSetupTest(layer2Info, scanResultInfo); - - // Initial BSSID comes from ScanResultInfo, it's broken MAC address format and fallback - // to check layer2Info, then verify DHCPREQUEST packet will be sent after roaming. - final DhcpPacket packet = getNextDhcpPacket(); - assertTrue(packet instanceof DhcpRequestPacket); - } }
diff --git a/tests/unit/Android.bp b/tests/unit/Android.bp index 73a844b..64da600 100644 --- a/tests/unit/Android.bp +++ b/tests/unit/Android.bp
@@ -76,8 +76,8 @@ static_libs: ["NetworkStackApiStableLib"], visibility: [ "//packages/modules/NetworkStack/tests/integration", - "//frameworks/base/packages/Tethering/tests/integration", - "//packages/modules/Connectivity/Tethering/tests/integration", + "//packages/modules/Connectivity/tests:__subpackages__", + "//packages/modules/Connectivity/Tethering/tests:__subpackages__", ] }
diff --git a/tests/unit/src/android/net/ip/IpClientTest.java b/tests/unit/src/android/net/ip/IpClientTest.java index 7260332..c9e486b 100644 --- a/tests/unit/src/android/net/ip/IpClientTest.java +++ b/tests/unit/src/android/net/ip/IpClientTest.java
@@ -21,6 +21,7 @@ import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Mockito.any; @@ -55,7 +56,9 @@ import android.net.ipmemorystore.NetworkAttributes; import android.net.metrics.IpConnectivityLog; import android.net.shared.InitialConfiguration; +import android.net.shared.Layer2Information; import android.net.shared.ProvisioningConfiguration; +import android.net.shared.ProvisioningConfiguration.ScanResultInfo; import android.net.util.InterfaceParams; import android.os.Build; @@ -83,9 +86,12 @@ import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; +import java.nio.ByteBuffer; import java.util.Arrays; +import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.Random; import java.util.Set; @@ -107,6 +113,8 @@ private static final int TEST_TIMEOUT_MS = 400; private static final String TEST_L2KEY = "some l2key"; private static final String TEST_CLUSTER = "some cluster"; + private static final String TEST_SSID = "test_ssid"; + private static final String TEST_BSSID = "00:11:22:33:44:55"; private static final String TEST_GLOBAL_ADDRESS = "1234:4321::548d:2db2:4fcf:ef75/64"; private static final String[] TEST_LOCAL_ADDRESSES = { @@ -709,6 +717,107 @@ verifyShutdown(ipc); } + private ScanResultInfo makeScanResultInfo(final String ssid, final String bssid) { + final ByteBuffer payload = ByteBuffer.allocate(14 /* oui + type + data */); + final byte[] data = new byte[10]; + new Random().nextBytes(data); + payload.put(new byte[] { 0x00, 0x1A, 0x11 }); + payload.put((byte) 0x06); + payload.put(data); + + final ScanResultInfo.InformationElement ie = + new ScanResultInfo.InformationElement(0xdd /* IE id */, payload); + return new ScanResultInfo(ssid, bssid, Collections.singletonList(ie)); + } + + @Test + public void testGetInitialBssidOnSOrAbove() throws Exception { + final IpClient ipc = makeIpClient(TEST_IFNAME); + final Layer2Information layer2Info = new Layer2Information(TEST_L2KEY, TEST_CLUSTER, + MacAddress.fromString(TEST_BSSID)); + final MacAddress bssid = ipc.getInitialBssid(layer2Info, null /* ScanReqsultInfo */, + true /* isAtLeastS */); + assertEquals(bssid, MacAddress.fromString(TEST_BSSID)); + } + + @Test + public void testGetInitialBssidOnSOrAbove_NullBssid() throws Exception { + final IpClient ipc = makeIpClient(TEST_IFNAME); + final Layer2Information layer2Info = new Layer2Information(TEST_L2KEY, TEST_CLUSTER, + null /* bssid */); + final ScanResultInfo scanResultInfo = makeScanResultInfo(TEST_SSID, TEST_BSSID); + final MacAddress bssid = ipc.getInitialBssid(layer2Info, scanResultInfo, + true /* isAtLeastS */); + assertNull(bssid); + } + + @Test + public void testGetInitialBssidOnSOrAbove_NullLayer2Info() throws Exception { + final IpClient ipc = makeIpClient(TEST_IFNAME); + final ScanResultInfo scanResultInfo = makeScanResultInfo(TEST_SSID, TEST_BSSID); + final MacAddress bssid = ipc.getInitialBssid(null /* layer2Info */, scanResultInfo, + true /* isAtLeastS */); + assertNull(bssid); + } + + @Test + public void testGetInitialBssidBeforeS() throws Exception { + final IpClient ipc = makeIpClient(TEST_IFNAME); + final Layer2Information layer2Info = new Layer2Information(TEST_L2KEY, TEST_CLUSTER, + MacAddress.fromString(TEST_BSSID)); + final ScanResultInfo scanResultInfo = makeScanResultInfo(TEST_SSID, TEST_BSSID); + final MacAddress bssid = ipc.getInitialBssid(layer2Info, scanResultInfo, + false /* isAtLeastS */); + assertEquals(bssid, MacAddress.fromString(TEST_BSSID)); + } + + @Test + public void testGetInitialBssidBeforeS_NullLayer2Info() throws Exception { + final IpClient ipc = makeIpClient(TEST_IFNAME); + final ScanResultInfo scanResultInfo = makeScanResultInfo(TEST_SSID, TEST_BSSID); + final MacAddress bssid = ipc.getInitialBssid(null /* layer2Info */, scanResultInfo, + false /* isAtLeastS */); + assertEquals(bssid, MacAddress.fromString(TEST_BSSID)); + } + + @Test + public void testGetInitialBssidBeforeS_BrokenInitialBssid() throws Exception { + final IpClient ipc = makeIpClient(TEST_IFNAME); + final ScanResultInfo scanResultInfo = makeScanResultInfo(TEST_SSID, "00:11:22:33:44:"); + final MacAddress bssid = ipc.getInitialBssid(null /* layer2Info */, scanResultInfo, + false /* isAtLeastS */); + assertNull(bssid); + } + + @Test + public void testGetInitialBssidBeforeS_BrokenInitialBssidFallback() throws Exception { + final IpClient ipc = makeIpClient(TEST_IFNAME); + final Layer2Information layer2Info = new Layer2Information(TEST_L2KEY, TEST_CLUSTER, + MacAddress.fromString(TEST_BSSID)); + final ScanResultInfo scanResultInfo = makeScanResultInfo(TEST_SSID, "00:11:22:33:44:"); + final MacAddress bssid = ipc.getInitialBssid(layer2Info, scanResultInfo, + false /* isAtLeastS */); + assertEquals(bssid, MacAddress.fromString(TEST_BSSID)); + } + + @Test + public void testGetInitialBssidBeforeS_NullScanResultInfoFallback() throws Exception { + final IpClient ipc = makeIpClient(TEST_IFNAME); + final Layer2Information layer2Info = new Layer2Information(TEST_L2KEY, TEST_CLUSTER, + MacAddress.fromString(TEST_BSSID)); + final MacAddress bssid = ipc.getInitialBssid(layer2Info, null /* scanResultInfo */, + false /* isAtLeastS */); + assertEquals(bssid, MacAddress.fromString(TEST_BSSID)); + } + + @Test + public void testGetInitialBssidBeforeS_NullScanResultInfoAndLayer2Info() throws Exception { + final IpClient ipc = makeIpClient(TEST_IFNAME); + final MacAddress bssid = ipc.getInitialBssid(null /* layer2Info */, + null /* scanResultInfo */, false /* isAtLeastS */); + assertNull(bssid); + } + interface Fn<A,B> { B call(A a) throws Exception; }
diff --git a/tests/unit/src/com/android/networkstack/packets/NeighborSolicitationTest.java b/tests/unit/src/com/android/networkstack/packets/NeighborSolicitationTest.java new file mode 100644 index 0000000..c3ff239 --- /dev/null +++ b/tests/unit/src/com/android/networkstack/packets/NeighborSolicitationTest.java
@@ -0,0 +1,270 @@ +/* + * Copyright (C) 2021 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.networkstack.packets; + +import static android.system.OsConstants.ETH_P_IPV6; +import static android.system.OsConstants.IPPROTO_ICMPV6; + +import static com.android.net.module.util.NetworkStackConstants.ICMPV6_NEIGHBOR_SOLICITATION; +import static com.android.testutils.MiscAsserts.assertThrows; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +import android.net.InetAddresses; +import android.net.MacAddress; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.net.Inet6Address; +import java.nio.ByteBuffer; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public final class NeighborSolicitationTest { + private static final Inet6Address TEST_SRC_ADDR = + (Inet6Address) InetAddresses.parseNumericAddress("fe80::d419:d664:df38:2f65"); + private static final Inet6Address TEST_DST_ADDR = + (Inet6Address) InetAddresses.parseNumericAddress("fe80::200:1a:1122:3344"); + private static final Inet6Address TEST_TARGET_ADDR = + (Inet6Address) InetAddresses.parseNumericAddress("fe80::200:1a:1122:3344"); + private static final byte[] TEST_SOURCE_MAC_ADDR = new byte[] { + (byte) 0x06, (byte) 0x5a, (byte) 0xac, (byte) 0x02, (byte) 0x61, (byte) 0x11, + }; + private static final byte[] TEST_DST_MAC_ADDR = new byte[] { + (byte) 0x00, (byte) 0x1a, (byte) 0x11, (byte) 0x22, (byte) 0x33, (byte) 0x44, + }; + private static final byte[] TEST_NEIGHBOR_SOLICITATION = new byte[] { + // dst mac address + (byte) 0x00, (byte) 0x1a, (byte) 0x11, (byte) 0x22, (byte) 0x33, (byte) 0x44, + // src mac address + (byte) 0x06, (byte) 0x5a, (byte) 0xac, (byte) 0x02, (byte) 0x61, (byte) 0x11, + // ether type + (byte) 0x86, (byte) 0xdd, + // version, priority and flow label + (byte) 0x60, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // length + (byte) 0x00, (byte) 0x20, + // next header + (byte) 0x3a, + // hop limit + (byte) 0xff, + // source address + (byte) 0xfe, (byte) 0x80, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0xd4, (byte) 0x19, (byte) 0xd6, (byte) 0x64, + (byte) 0xdf, (byte) 0x38, (byte) 0x2f, (byte) 0x65, + // destination address + (byte) 0xfe, (byte) 0x80, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x1a, + (byte) 0x11, (byte) 0x22, (byte) 0x33, (byte) 0x44, + // ICMP type, code, checksum + (byte) 0x87, (byte) 0x00, (byte) 0x22, (byte) 0x96, + // reserved + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // target address + (byte) 0xfe, (byte) 0x80, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x1a, + (byte) 0x11, (byte) 0x22, (byte) 0x33, (byte) 0x44, + // slla option + (byte) 0x01, (byte) 0x01, + // link-layer address + (byte) 0x06, (byte) 0x5a, (byte) 0xac, (byte) 0x02, + (byte) 0x61, (byte) 0x11, + }; + private static final byte[] TEST_NEIGHBOR_SOLICITATION_WITHOUT_SLLA = new byte[] { + // dst mac address + (byte) 0x00, (byte) 0x1a, (byte) 0x11, (byte) 0x22, (byte) 0x33, (byte) 0x44, + // src mac address + (byte) 0x06, (byte) 0x5a, (byte) 0xac, (byte) 0x02, (byte) 0x61, (byte) 0x11, + // ether type + (byte) 0x86, (byte) 0xdd, + // version, priority and flow label + (byte) 0x60, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // length + (byte) 0x00, (byte) 0x20, + // next header + (byte) 0x3a, + // hop limit + (byte) 0xff, + // source address + (byte) 0xfe, (byte) 0x80, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0xd4, (byte) 0x19, (byte) 0xd6, (byte) 0x64, + (byte) 0xdf, (byte) 0x38, (byte) 0x2f, (byte) 0x65, + // destination address + (byte) 0xfe, (byte) 0x80, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x1a, + (byte) 0x11, (byte) 0x22, (byte) 0x33, (byte) 0x44, + // ICMP type, code, checksum + (byte) 0x87, (byte) 0x00, (byte) 0x22, (byte) 0x96, + // reserved + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // target address + (byte) 0xfe, (byte) 0x80, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x1a, + (byte) 0x11, (byte) 0x22, (byte) 0x33, (byte) 0x44, + }; + private static final byte[] TEST_NEIGHBOR_SOLICITATION_LESS_LENGTH = new byte[] { + // dst mac address + (byte) 0x00, (byte) 0x1a, (byte) 0x11, (byte) 0x22, (byte) 0x33, (byte) 0x44, + // src mac address + (byte) 0x06, (byte) 0x5a, (byte) 0xac, (byte) 0x02, (byte) 0x61, (byte) 0x11, + // ether type + (byte) 0x86, (byte) 0xdd, + // version, priority and flow label + (byte) 0x60, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // length + (byte) 0x00, (byte) 0x20, + // next header + (byte) 0x3a, + // hop limit + (byte) 0xff, + // source address + (byte) 0xfe, (byte) 0x80, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0xd4, (byte) 0x19, (byte) 0xd6, (byte) 0x64, + (byte) 0xdf, (byte) 0x38, (byte) 0x2f, (byte) 0x65, + // destination address + (byte) 0xfe, (byte) 0x80, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x1a, + (byte) 0x11, (byte) 0x22, (byte) 0x33, (byte) 0x44, + // ICMP type, code, checksum + (byte) 0x87, (byte) 0x00, (byte) 0x22, (byte) 0x96, + // reserved + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + }; + private static final byte[] TEST_NEIGHBOR_SOLICITATION_TRUNCATED = new byte[] { + // dst mac address + (byte) 0x00, (byte) 0x1a, (byte) 0x11, (byte) 0x22, (byte) 0x33, (byte) 0x44, + // src mac address + (byte) 0x06, (byte) 0x5a, (byte) 0xac, (byte) 0x02, (byte) 0x61, (byte) 0x11, + // ether type + (byte) 0x86, (byte) 0xdd, + // version, priority and flow label + (byte) 0x60, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // length + (byte) 0x00, (byte) 0x20, + // next header + (byte) 0x3a, + // hop limit + (byte) 0xff, + // source address + (byte) 0xfe, (byte) 0x80, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0xd4, (byte) 0x19, (byte) 0xd6, (byte) 0x64, + (byte) 0xdf, (byte) 0x38, (byte) 0x2f, (byte) 0x65, + // destination address + (byte) 0xfe, (byte) 0x80, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x1a, + (byte) 0x11, (byte) 0x22, (byte) 0x33, (byte) 0x44, + // ICMP type, code, checksum + (byte) 0x87, (byte) 0x00, (byte) 0x22, (byte) 0x96, + // reserved + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // target address + (byte) 0xfe, (byte) 0x80, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x1a, + (byte) 0x11, (byte) 0x22, (byte) 0x33, (byte) 0x44, + // slla option + (byte) 0x01, (byte) 0x01, + // link-layer address + (byte) 0x06, (byte) 0x5a, (byte) 0xac, (byte) 0x02, + }; + + @Test + public void testNeighborSolicitation_build() throws Exception { + final ByteBuffer ns = NeighborSolicitation.build( + MacAddress.fromBytes(TEST_SOURCE_MAC_ADDR), + MacAddress.fromBytes(TEST_DST_MAC_ADDR), + TEST_SRC_ADDR, TEST_DST_ADDR, TEST_TARGET_ADDR); + assertArrayEquals(ns.array(), TEST_NEIGHBOR_SOLICITATION); + } + + private void assertNeighborSolicitation(final NeighborSolicitation ns, boolean hasSllaOption) { + assertArrayEquals(TEST_SOURCE_MAC_ADDR, ns.ethHdr.srcMac.toByteArray()); + assertArrayEquals(TEST_DST_MAC_ADDR, ns.ethHdr.dstMac.toByteArray()); + assertEquals(ETH_P_IPV6, ns.ethHdr.etherType); + assertEquals(IPPROTO_ICMPV6, ns.ipv6Hdr.nextHeader); + assertEquals(0xff, ns.ipv6Hdr.hopLimit); + assertEquals(TEST_DST_ADDR, ns.ipv6Hdr.dstIp); + assertEquals(TEST_SRC_ADDR, ns.ipv6Hdr.srcIp); + assertEquals(ICMPV6_NEIGHBOR_SOLICITATION, ns.icmpv6Hdr.type); + assertEquals(0, ns.icmpv6Hdr.code); + assertEquals(TEST_TARGET_ADDR, ns.nsHdr.target); + if (hasSllaOption) { + assertEquals(MacAddress.fromBytes(TEST_SOURCE_MAC_ADDR), ns.slla.linkLayerAddress); + } + } + + @Test + public void testNeighborSolicitation_parse() throws Exception { + final NeighborSolicitation ns = NeighborSolicitation.parse(TEST_NEIGHBOR_SOLICITATION, + TEST_NEIGHBOR_SOLICITATION.length); + + assertNeighborSolicitation(ns, true /* hasSllaOption */); + assertArrayEquals(TEST_NEIGHBOR_SOLICITATION, ns.toByteBuffer().array()); + } + + @Test + public void testNeighborSolicitation_parseWithoutSllaOption() throws Exception { + final NeighborSolicitation ns = + NeighborSolicitation.parse(TEST_NEIGHBOR_SOLICITATION_WITHOUT_SLLA, + TEST_NEIGHBOR_SOLICITATION_WITHOUT_SLLA.length); + + assertNeighborSolicitation(ns, false /* hasSllaOption */); + assertArrayEquals(TEST_NEIGHBOR_SOLICITATION_WITHOUT_SLLA, ns.toByteBuffer().array()); + } + + @Test + public void testNeighborSolicitation_invalidPacketLength() throws Exception { + assertThrows(NeighborSolicitation.ParseException.class, + () -> NeighborSolicitation.parse(TEST_NEIGHBOR_SOLICITATION, 0)); + } + + @Test + public void testNeighborSolicitation_invalidByteBufferLength() throws Exception { + assertThrows(NeighborSolicitation.ParseException.class, + () -> NeighborSolicitation.parse(TEST_NEIGHBOR_SOLICITATION_TRUNCATED, + TEST_NEIGHBOR_SOLICITATION.length)); + } + + @Test + public void testNeighborSolicitation_lessPacketLength() throws Exception { + assertThrows(NeighborSolicitation.ParseException.class, + () -> NeighborSolicitation.parse(TEST_NEIGHBOR_SOLICITATION_LESS_LENGTH, + TEST_NEIGHBOR_SOLICITATION_LESS_LENGTH.length)); + } + + @Test + public void testNeighborSolicitation_truncatedPacket() throws Exception { + assertThrows(IllegalArgumentException.class, + () -> NeighborSolicitation.parse(TEST_NEIGHBOR_SOLICITATION_TRUNCATED, + TEST_NEIGHBOR_SOLICITATION_TRUNCATED.length)); + } +}