summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/net/Android.mk68
-rw-r--r--tests/net/java/android/net/DnsPacketTest.java159
-rw-r--r--tests/net/java/android/net/apf/ApfTest.java1793
-rw-r--r--tests/net/java/android/net/apf/Bpf2Apf.java327
-rw-r--r--tests/net/java/android/net/dhcp/DhcpPacketTest.java1075
-rw-r--r--tests/net/java/android/net/ip/IpClientTest.java527
-rw-r--r--tests/net/java/android/net/ip/IpReachabilityMonitorTest.java67
-rw-r--r--tests/net/java/android/net/util/ConnectivityPacketSummaryTest.java418
-rw-r--r--tests/net/java/android/net/util/PacketReaderTest.java209
-rw-r--r--tests/net/java/com/android/server/ConnectivityServiceTest.java231
-rw-r--r--tests/net/java/com/android/server/net/ipmemorystore/IpMemoryStoreServiceTest.java133
-rw-r--r--tests/net/jni/apf_jni.cpp248
-rw-r--r--tests/net/res/raw/apf.pcapbin5702 -> 0 bytes
-rw-r--r--tests/net/res/raw/apfPcap.pcapbin101547 -> 0 bytes
-rw-r--r--tests/utils/testutils/java/com/android/test/filters/SelectTest.java338
-rw-r--r--tests/utils/testutils/java/com/android/test/filters/SelectTestTests.java220
16 files changed, 1064 insertions, 4749 deletions
diff --git a/tests/net/Android.mk b/tests/net/Android.mk
index 685067377166..7e1b4008c473 100644
--- a/tests/net/Android.mk
+++ b/tests/net/Android.mk
@@ -32,74 +32,6 @@ LOCAL_COMPATIBILITY_SUITE := device-tests
LOCAL_CERTIFICATE := platform
-# These are not normally accessible from apps so they must be explicitly included.
-LOCAL_JNI_SHARED_LIBRARIES := \
- android.hidl.token@1.0 \
- libartbase \
- libbacktrace \
- libbase \
- libbinder \
- libbinderthreadstate \
- libc++ \
- libcrypto \
- libcutils \
- libdexfile \
- libframeworksnettestsjni \
- libhidl-gen-utils \
- libhidlbase \
- libhidltransport \
- libhwbinder \
- liblog \
- liblzma \
- libnativehelper \
- libpackagelistparser \
- libpcre2 \
- libprocessgroup \
- libselinux \
- libui \
- libutils \
- libvintf \
- libvndksupport \
- libtinyxml2 \
- libunwindstack \
- libutilscallstack \
- libziparchive \
- libz \
- netd_aidl_interface-cpp
-
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
include $(BUILD_PACKAGE)
-
-#########################################################################
-# Build JNI Shared Library
-#########################################################################
-
-LOCAL_PATH:= $(LOCAL_PATH)/jni
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_CFLAGS := -Wall -Wextra -Werror
-
-LOCAL_C_INCLUDES := \
- libpcap \
- hardware/google/apf
-
-LOCAL_SRC_FILES := $(call all-cpp-files-under)
-
-LOCAL_SHARED_LIBRARIES := \
- libbinder \
- liblog \
- libcutils \
- libnativehelper \
- netd_aidl_interface-cpp
-
-LOCAL_STATIC_LIBRARIES := \
- libpcap \
- libapf
-
-LOCAL_MODULE := libframeworksnettestsjni
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/net/java/android/net/DnsPacketTest.java b/tests/net/java/android/net/DnsPacketTest.java
new file mode 100644
index 000000000000..032e52666970
--- /dev/null
+++ b/tests/net/java/android/net/DnsPacketTest.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2019 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 android.net;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class DnsPacketTest {
+ private void assertHeaderParses(DnsPacket.DnsHeader header, int id, int flag,
+ int qCount, int aCount, int nsCount, int arCount) {
+ assertEquals(header.id, id);
+ assertEquals(header.flags, flag);
+ assertEquals(header.getSectionCount(DnsPacket.QDSECTION), qCount);
+ assertEquals(header.getSectionCount(DnsPacket.ANSECTION), aCount);
+ assertEquals(header.getSectionCount(DnsPacket.NSSECTION), nsCount);
+ assertEquals(header.getSectionCount(DnsPacket.ARSECTION), arCount);
+ }
+
+ private void assertSectionParses(DnsPacket.DnsSection section, String dname,
+ int dtype, int dclass, int ttl, byte[] rr) {
+ assertEquals(section.dName, dname);
+ assertEquals(section.nsType, dtype);
+ assertEquals(section.nsClass, dclass);
+ assertEquals(section.ttl, ttl);
+ assertTrue(Arrays.equals(section.getRR(), rr));
+ }
+
+ class TestDnsPacket extends DnsPacket {
+ TestDnsPacket(byte[] data) throws ParseException {
+ super(data);
+ }
+
+ public DnsHeader getHeader() {
+ return mHeader;
+ }
+ public List<DnsSection> getSectionList(int secType) {
+ return mSections[secType];
+ }
+ }
+
+ @Test
+ public void testNullDisallowed() {
+ try {
+ new TestDnsPacket(null);
+ fail("Exception not thrown for null byte array");
+ } catch (DnsPacket.ParseException e) {
+ }
+ }
+
+ @Test
+ public void testV4Answer() throws Exception {
+ final byte[] v4blob = new byte[] {
+ /* Header */
+ 0x55, 0x66, /* Transaction ID */
+ (byte) 0x81, (byte) 0x80, /* Flags */
+ 0x00, 0x01, /* Questions */
+ 0x00, 0x01, /* Answer RRs */
+ 0x00, 0x00, /* Authority RRs */
+ 0x00, 0x00, /* Additional RRs */
+ /* Queries */
+ 0x03, 0x77, 0x77, 0x77, 0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6c, 0x65,
+ 0x03, 0x63, 0x6f, 0x6d, 0x00, /* Name */
+ 0x00, 0x01, /* Type */
+ 0x00, 0x01, /* Class */
+ /* Answers */
+ (byte) 0xc0, 0x0c, /* Name */
+ 0x00, 0x01, /* Type */
+ 0x00, 0x01, /* Class */
+ 0x00, 0x00, 0x01, 0x2b, /* TTL */
+ 0x00, 0x04, /* Data length */
+ (byte) 0xac, (byte) 0xd9, (byte) 0xa1, (byte) 0x84 /* Address */
+ };
+ TestDnsPacket packet = new TestDnsPacket(v4blob);
+
+ // Header part
+ assertHeaderParses(packet.getHeader(), 0x5566, 0x8180, 1, 1, 0, 0);
+
+ // Section part
+ List<DnsPacket.DnsSection> qdSectionList =
+ packet.getSectionList(DnsPacket.QDSECTION);
+ assertEquals(qdSectionList.size(), 1);
+ assertSectionParses(qdSectionList.get(0), "www.google.com", 1, 1, 0, null);
+
+ List<DnsPacket.DnsSection> anSectionList =
+ packet.getSectionList(DnsPacket.ANSECTION);
+ assertEquals(anSectionList.size(), 1);
+ assertSectionParses(anSectionList.get(0), "www.google.com", 1, 1, 0x12b,
+ new byte[]{ (byte) 0xac, (byte) 0xd9, (byte) 0xa1, (byte) 0x84 });
+ }
+
+ @Test
+ public void testV6Answer() throws Exception {
+ final byte[] v6blob = new byte[] {
+ /* Header */
+ 0x77, 0x22, /* Transaction ID */
+ (byte) 0x81, (byte) 0x80, /* Flags */
+ 0x00, 0x01, /* Questions */
+ 0x00, 0x01, /* Answer RRs */
+ 0x00, 0x00, /* Authority RRs */
+ 0x00, 0x00, /* Additional RRs */
+ /* Queries */
+ 0x03, 0x77, 0x77, 0x77, 0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6c, 0x65,
+ 0x03, 0x63, 0x6f, 0x6d, 0x00, /* Name */
+ 0x00, 0x1c, /* Type */
+ 0x00, 0x01, /* Class */
+ /* Answers */
+ (byte) 0xc0, 0x0c, /* Name */
+ 0x00, 0x1c, /* Type */
+ 0x00, 0x01, /* Class */
+ 0x00, 0x00, 0x00, 0x37, /* TTL */
+ 0x00, 0x10, /* Data length */
+ 0x24, 0x04, 0x68, 0x00, 0x40, 0x05, 0x08, 0x0d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x04 /* Address */
+ };
+ TestDnsPacket packet = new TestDnsPacket(v6blob);
+
+ // Header part
+ assertHeaderParses(packet.getHeader(), 0x7722, 0x8180, 1, 1, 0, 0);
+
+ // Section part
+ List<DnsPacket.DnsSection> qdSectionList =
+ packet.getSectionList(DnsPacket.QDSECTION);
+ assertEquals(qdSectionList.size(), 1);
+ assertSectionParses(qdSectionList.get(0), "www.google.com", 28, 1, 0, null);
+
+ List<DnsPacket.DnsSection> anSectionList =
+ packet.getSectionList(DnsPacket.ANSECTION);
+ assertEquals(anSectionList.size(), 1);
+ assertSectionParses(anSectionList.get(0), "www.google.com", 28, 1, 0x37,
+ new byte[]{ 0x24, 0x04, 0x68, 0x00, 0x40, 0x05, 0x08, 0x0d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x04 });
+ }
+}
diff --git a/tests/net/java/android/net/apf/ApfTest.java b/tests/net/java/android/net/apf/ApfTest.java
deleted file mode 100644
index 3c3e7ce3b12a..000000000000
--- a/tests/net/java/android/net/apf/ApfTest.java
+++ /dev/null
@@ -1,1793 +0,0 @@
-/*
- * Copyright (C) 2012 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 android.net.apf;
-
-import static android.net.util.NetworkConstants.ICMPV6_ECHO_REQUEST_TYPE;
-import static android.net.util.NetworkConstants.ICMPV6_ROUTER_ADVERTISEMENT;
-import static android.system.OsConstants.AF_UNIX;
-import static android.system.OsConstants.ARPHRD_ETHER;
-import static android.system.OsConstants.ETH_P_ARP;
-import static android.system.OsConstants.ETH_P_IP;
-import static android.system.OsConstants.ETH_P_IPV6;
-import static android.system.OsConstants.IPPROTO_ICMPV6;
-import static android.system.OsConstants.IPPROTO_UDP;
-import static android.system.OsConstants.SOCK_STREAM;
-
-import static com.android.internal.util.BitUtils.bytesToBEInt;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.verify;
-
-import android.content.Context;
-import android.net.LinkAddress;
-import android.net.LinkProperties;
-import android.net.apf.ApfFilter.ApfConfiguration;
-import android.net.apf.ApfGenerator.IllegalInstructionException;
-import android.net.apf.ApfGenerator.Register;
-import android.net.ip.IpClientCallbacks;
-import android.net.metrics.IpConnectivityLog;
-import android.net.metrics.RaEvent;
-import android.net.util.InterfaceParams;
-import android.os.ConditionVariable;
-import android.os.Parcelable;
-import android.os.SystemClock;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.text.format.DateUtils;
-import android.util.Log;
-
-import com.android.frameworks.tests.net.R;
-import com.android.internal.util.HexDump;
-
-import libcore.io.IoUtils;
-import libcore.io.Streams;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.InetAddress;
-import java.nio.ByteBuffer;
-import java.util.List;
-import java.util.Random;
-
-/**
- * Tests for APF program generator and interpreter.
- *
- * Build, install and run with:
- * runtest frameworks-net -c android.net.apf.ApfTest
- */
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class ApfTest {
- private static final int TIMEOUT_MS = 500;
- private static final int MIN_APF_VERSION = 2;
-
- @Mock IpConnectivityLog mLog;
- @Mock Context mContext;
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- // Load up native shared library containing APF interpreter exposed via JNI.
- System.loadLibrary("frameworksnettestsjni");
- }
-
- private static final String TAG = "ApfTest";
- // Expected return codes from APF interpreter.
- private static final int PASS = 1;
- private static final int DROP = 0;
- // Interpreter will just accept packets without link layer headers, so pad fake packet to at
- // least the minimum packet size.
- private static final int MIN_PKT_SIZE = 15;
-
- private static final ApfCapabilities MOCK_APF_CAPABILITIES =
- new ApfCapabilities(2, 1700, ARPHRD_ETHER);
-
- private static final boolean DROP_MULTICAST = true;
- private static final boolean ALLOW_MULTICAST = false;
-
- private static final boolean DROP_802_3_FRAMES = true;
- private static final boolean ALLOW_802_3_FRAMES = false;
-
- // Constants for opcode encoding
- private static final byte LI_OP = (byte)(13 << 3);
- private static final byte LDDW_OP = (byte)(22 << 3);
- private static final byte STDW_OP = (byte)(23 << 3);
- private static final byte SIZE0 = (byte)(0 << 1);
- private static final byte SIZE8 = (byte)(1 << 1);
- private static final byte SIZE16 = (byte)(2 << 1);
- private static final byte SIZE32 = (byte)(3 << 1);
- private static final byte R1 = 1;
-
- private static ApfConfiguration getDefaultConfig() {
- ApfFilter.ApfConfiguration config = new ApfConfiguration();
- config.apfCapabilities = MOCK_APF_CAPABILITIES;
- config.multicastFilter = ALLOW_MULTICAST;
- config.ieee802_3Filter = ALLOW_802_3_FRAMES;
- config.ethTypeBlackList = new int[0];
- return config;
- }
-
- private static String label(int code) {
- switch (code) {
- case PASS: return "PASS";
- case DROP: return "DROP";
- default: return "UNKNOWN";
- }
- }
-
- private static void assertReturnCodesEqual(int expected, int got) {
- assertEquals(label(expected), label(got));
- }
-
- private void assertVerdict(int expected, byte[] program, byte[] packet, int filterAge) {
- assertReturnCodesEqual(expected, apfSimulate(program, packet, null, filterAge));
- }
-
- private void assertVerdict(int expected, byte[] program, byte[] packet) {
- assertReturnCodesEqual(expected, apfSimulate(program, packet, null, 0));
- }
-
- private void assertPass(byte[] program, byte[] packet, int filterAge) {
- assertVerdict(PASS, program, packet, filterAge);
- }
-
- private void assertPass(byte[] program, byte[] packet) {
- assertVerdict(PASS, program, packet);
- }
-
- private void assertDrop(byte[] program, byte[] packet, int filterAge) {
- assertVerdict(DROP, program, packet, filterAge);
- }
-
- private void assertDrop(byte[] program, byte[] packet) {
- assertVerdict(DROP, program, packet);
- }
-
- private void assertProgramEquals(byte[] expected, byte[] program) throws AssertionError {
- // assertArrayEquals() would only print one byte, making debugging difficult.
- if (!java.util.Arrays.equals(expected, program)) {
- throw new AssertionError(
- "\nexpected: " + HexDump.toHexString(expected) +
- "\nactual: " + HexDump.toHexString(program));
- }
- }
-
- private void assertDataMemoryContents(
- int expected, byte[] program, byte[] packet, byte[] data, byte[] expected_data)
- throws IllegalInstructionException, Exception {
- assertReturnCodesEqual(expected, apfSimulate(program, packet, data, 0 /* filterAge */));
-
- // assertArrayEquals() would only print one byte, making debugging difficult.
- if (!java.util.Arrays.equals(expected_data, data)) {
- throw new Exception(
- "\nprogram: " + HexDump.toHexString(program) +
- "\ndata memory: " + HexDump.toHexString(data) +
- "\nexpected: " + HexDump.toHexString(expected_data));
- }
- }
-
- private void assertVerdict(int expected, ApfGenerator gen, byte[] packet, int filterAge)
- throws IllegalInstructionException {
- assertReturnCodesEqual(expected, apfSimulate(gen.generate(), packet, null,
- filterAge));
- }
-
- private void assertPass(ApfGenerator gen, byte[] packet, int filterAge)
- throws IllegalInstructionException {
- assertVerdict(PASS, gen, packet, filterAge);
- }
-
- private void assertDrop(ApfGenerator gen, byte[] packet, int filterAge)
- throws IllegalInstructionException {
- assertVerdict(DROP, gen, packet, filterAge);
- }
-
- private void assertPass(ApfGenerator gen)
- throws IllegalInstructionException {
- assertVerdict(PASS, gen, new byte[MIN_PKT_SIZE], 0);
- }
-
- private void assertDrop(ApfGenerator gen)
- throws IllegalInstructionException {
- assertVerdict(DROP, gen, new byte[MIN_PKT_SIZE], 0);
- }
-
- /**
- * Test each instruction by generating a program containing the instruction,
- * generating bytecode for that program and running it through the
- * interpreter to verify it functions correctly.
- */
- @Test
- public void testApfInstructions() throws IllegalInstructionException {
- // Empty program should pass because having the program counter reach the
- // location immediately after the program indicates the packet should be
- // passed to the AP.
- ApfGenerator gen = new ApfGenerator(MIN_APF_VERSION);
- assertPass(gen);
-
- // Test jumping to pass label.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addJump(gen.PASS_LABEL);
- byte[] program = gen.generate();
- assertEquals(1, program.length);
- assertEquals((14 << 3) | (0 << 1) | 0, program[0]);
- assertPass(program, new byte[MIN_PKT_SIZE], 0);
-
- // Test jumping to drop label.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addJump(gen.DROP_LABEL);
- program = gen.generate();
- assertEquals(2, program.length);
- assertEquals((14 << 3) | (1 << 1) | 0, program[0]);
- assertEquals(1, program[1]);
- assertDrop(program, new byte[15], 15);
-
- // Test jumping if equal to 0.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addJumpIfR0Equals(0, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test jumping if not equal to 0.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addJumpIfR0NotEquals(0, gen.DROP_LABEL);
- assertPass(gen);
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 1);
- gen.addJumpIfR0NotEquals(0, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test jumping if registers equal.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addJumpIfR0EqualsR1(gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test jumping if registers not equal.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addJumpIfR0NotEqualsR1(gen.DROP_LABEL);
- assertPass(gen);
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 1);
- gen.addJumpIfR0NotEqualsR1(gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test load immediate.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 1234567890);
- gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test add.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addAdd(1234567890);
- gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test subtract.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addAdd(-1234567890);
- gen.addJumpIfR0Equals(-1234567890, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test or.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addOr(1234567890);
- gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test and.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 1234567890);
- gen.addAnd(123456789);
- gen.addJumpIfR0Equals(1234567890 & 123456789, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test left shift.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 1234567890);
- gen.addLeftShift(1);
- gen.addJumpIfR0Equals(1234567890 << 1, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test right shift.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 1234567890);
- gen.addRightShift(1);
- gen.addJumpIfR0Equals(1234567890 >> 1, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test multiply.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 123456789);
- gen.addMul(2);
- gen.addJumpIfR0Equals(123456789 * 2, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test divide.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 1234567890);
- gen.addDiv(2);
- gen.addJumpIfR0Equals(1234567890 / 2, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test divide by zero.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addDiv(0);
- gen.addJump(gen.DROP_LABEL);
- assertPass(gen);
-
- // Test add.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R1, 1234567890);
- gen.addAddR1();
- gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test subtract.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R1, -1234567890);
- gen.addAddR1();
- gen.addJumpIfR0Equals(-1234567890, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test or.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R1, 1234567890);
- gen.addOrR1();
- gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test and.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 1234567890);
- gen.addLoadImmediate(Register.R1, 123456789);
- gen.addAndR1();
- gen.addJumpIfR0Equals(1234567890 & 123456789, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test left shift.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 1234567890);
- gen.addLoadImmediate(Register.R1, 1);
- gen.addLeftShiftR1();
- gen.addJumpIfR0Equals(1234567890 << 1, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test right shift.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 1234567890);
- gen.addLoadImmediate(Register.R1, -1);
- gen.addLeftShiftR1();
- gen.addJumpIfR0Equals(1234567890 >> 1, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test multiply.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 123456789);
- gen.addLoadImmediate(Register.R1, 2);
- gen.addMulR1();
- gen.addJumpIfR0Equals(123456789 * 2, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test divide.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 1234567890);
- gen.addLoadImmediate(Register.R1, 2);
- gen.addDivR1();
- gen.addJumpIfR0Equals(1234567890 / 2, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test divide by zero.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addDivR1();
- gen.addJump(gen.DROP_LABEL);
- assertPass(gen);
-
- // Test byte load.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoad8(Register.R0, 1);
- gen.addJumpIfR0Equals(45, gen.DROP_LABEL);
- assertDrop(gen, new byte[]{123,45,0,0,0,0,0,0,0,0,0,0,0,0,0}, 0);
-
- // Test out of bounds load.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoad8(Register.R0, 16);
- gen.addJumpIfR0Equals(0, gen.DROP_LABEL);
- assertPass(gen, new byte[]{123,45,0,0,0,0,0,0,0,0,0,0,0,0,0}, 0);
-
- // Test half-word load.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoad16(Register.R0, 1);
- gen.addJumpIfR0Equals((45 << 8) | 67, gen.DROP_LABEL);
- assertDrop(gen, new byte[]{123,45,67,0,0,0,0,0,0,0,0,0,0,0,0}, 0);
-
- // Test word load.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoad32(Register.R0, 1);
- gen.addJumpIfR0Equals((45 << 24) | (67 << 16) | (89 << 8) | 12, gen.DROP_LABEL);
- assertDrop(gen, new byte[]{123,45,67,89,12,0,0,0,0,0,0,0,0,0,0}, 0);
-
- // Test byte indexed load.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R1, 1);
- gen.addLoad8Indexed(Register.R0, 0);
- gen.addJumpIfR0Equals(45, gen.DROP_LABEL);
- assertDrop(gen, new byte[]{123,45,0,0,0,0,0,0,0,0,0,0,0,0,0}, 0);
-
- // Test out of bounds indexed load.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R1, 8);
- gen.addLoad8Indexed(Register.R0, 8);
- gen.addJumpIfR0Equals(0, gen.DROP_LABEL);
- assertPass(gen, new byte[]{123,45,0,0,0,0,0,0,0,0,0,0,0,0,0}, 0);
-
- // Test half-word indexed load.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R1, 1);
- gen.addLoad16Indexed(Register.R0, 0);
- gen.addJumpIfR0Equals((45 << 8) | 67, gen.DROP_LABEL);
- assertDrop(gen, new byte[]{123,45,67,0,0,0,0,0,0,0,0,0,0,0,0}, 0);
-
- // Test word indexed load.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R1, 1);
- gen.addLoad32Indexed(Register.R0, 0);
- gen.addJumpIfR0Equals((45 << 24) | (67 << 16) | (89 << 8) | 12, gen.DROP_LABEL);
- assertDrop(gen, new byte[]{123,45,67,89,12,0,0,0,0,0,0,0,0,0,0}, 0);
-
- // Test jumping if greater than.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addJumpIfR0GreaterThan(0, gen.DROP_LABEL);
- assertPass(gen);
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 1);
- gen.addJumpIfR0GreaterThan(0, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test jumping if less than.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addJumpIfR0LessThan(0, gen.DROP_LABEL);
- assertPass(gen);
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addJumpIfR0LessThan(1, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test jumping if any bits set.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addJumpIfR0AnyBitsSet(3, gen.DROP_LABEL);
- assertPass(gen);
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 1);
- gen.addJumpIfR0AnyBitsSet(3, gen.DROP_LABEL);
- assertDrop(gen);
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 3);
- gen.addJumpIfR0AnyBitsSet(3, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test jumping if register greater than.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addJumpIfR0GreaterThanR1(gen.DROP_LABEL);
- assertPass(gen);
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 2);
- gen.addLoadImmediate(Register.R1, 1);
- gen.addJumpIfR0GreaterThanR1(gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test jumping if register less than.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addJumpIfR0LessThanR1(gen.DROP_LABEL);
- assertPass(gen);
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R1, 1);
- gen.addJumpIfR0LessThanR1(gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test jumping if any bits set in register.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R1, 3);
- gen.addJumpIfR0AnyBitsSetR1(gen.DROP_LABEL);
- assertPass(gen);
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R1, 3);
- gen.addLoadImmediate(Register.R0, 1);
- gen.addJumpIfR0AnyBitsSetR1(gen.DROP_LABEL);
- assertDrop(gen);
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R1, 3);
- gen.addLoadImmediate(Register.R0, 3);
- gen.addJumpIfR0AnyBitsSetR1(gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test load from memory.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadFromMemory(Register.R0, 0);
- gen.addJumpIfR0Equals(0, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test store to memory.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R1, 1234567890);
- gen.addStoreToMemory(Register.R1, 12);
- gen.addLoadFromMemory(Register.R0, 12);
- gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test filter age pre-filled memory.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadFromMemory(Register.R0, gen.FILTER_AGE_MEMORY_SLOT);
- gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
- assertDrop(gen, new byte[MIN_PKT_SIZE], 1234567890);
-
- // Test packet size pre-filled memory.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadFromMemory(Register.R0, gen.PACKET_SIZE_MEMORY_SLOT);
- gen.addJumpIfR0Equals(MIN_PKT_SIZE, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test IPv4 header size pre-filled memory.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadFromMemory(Register.R0, gen.IPV4_HEADER_SIZE_MEMORY_SLOT);
- gen.addJumpIfR0Equals(20, gen.DROP_LABEL);
- assertDrop(gen, new byte[]{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0x45}, 0);
-
- // Test not.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 1234567890);
- gen.addNot(Register.R0);
- gen.addJumpIfR0Equals(~1234567890, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test negate.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 1234567890);
- gen.addNeg(Register.R0);
- gen.addJumpIfR0Equals(-1234567890, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test move.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R1, 1234567890);
- gen.addMove(Register.R0);
- gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
- assertDrop(gen);
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 1234567890);
- gen.addMove(Register.R1);
- gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test swap.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R1, 1234567890);
- gen.addSwap();
- gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
- assertDrop(gen);
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 1234567890);
- gen.addSwap();
- gen.addJumpIfR0Equals(0, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test jump if bytes not equal.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 1);
- gen.addJumpIfBytesNotEqual(Register.R0, new byte[]{123}, gen.DROP_LABEL);
- program = gen.generate();
- assertEquals(6, program.length);
- assertEquals((13 << 3) | (1 << 1) | 0, program[0]);
- assertEquals(1, program[1]);
- assertEquals(((20 << 3) | (1 << 1) | 0) - 256, program[2]);
- assertEquals(1, program[3]);
- assertEquals(1, program[4]);
- assertEquals(123, program[5]);
- assertDrop(program, new byte[MIN_PKT_SIZE], 0);
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 1);
- gen.addJumpIfBytesNotEqual(Register.R0, new byte[]{123}, gen.DROP_LABEL);
- byte[] packet123 = {0,123,0,0,0,0,0,0,0,0,0,0,0,0,0};
- assertPass(gen, packet123, 0);
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addJumpIfBytesNotEqual(Register.R0, new byte[]{123}, gen.DROP_LABEL);
- assertDrop(gen, packet123, 0);
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 1);
- gen.addJumpIfBytesNotEqual(Register.R0, new byte[]{1,2,30,4,5}, gen.DROP_LABEL);
- byte[] packet12345 = {0,1,2,3,4,5,0,0,0,0,0,0,0,0,0};
- assertDrop(gen, packet12345, 0);
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 1);
- gen.addJumpIfBytesNotEqual(Register.R0, new byte[]{1,2,3,4,5}, gen.DROP_LABEL);
- assertPass(gen, packet12345, 0);
- }
-
- @Test(expected = ApfGenerator.IllegalInstructionException.class)
- public void testApfGeneratorWantsV2OrGreater() throws Exception {
- // The minimum supported APF version is 2.
- new ApfGenerator(1);
- }
-
- @Test
- public void testApfDataOpcodesWantApfV3() throws IllegalInstructionException, Exception {
- ApfGenerator gen = new ApfGenerator(MIN_APF_VERSION);
- try {
- gen.addStoreData(Register.R0, 0);
- fail();
- } catch (IllegalInstructionException expected) {
- /* pass */
- }
- try {
- gen.addLoadData(Register.R0, 0);
- fail();
- } catch (IllegalInstructionException expected) {
- /* pass */
- }
- }
-
- /**
- * Test that the generator emits immediates using the shortest possible encoding.
- */
- @Test
- public void testImmediateEncoding() throws IllegalInstructionException {
- ApfGenerator gen;
-
- // 0-byte immediate: li R0, 0
- gen = new ApfGenerator(4);
- gen.addLoadImmediate(Register.R0, 0);
- assertProgramEquals(new byte[]{LI_OP | SIZE0}, gen.generate());
-
- // 1-byte immediate: li R0, 42
- gen = new ApfGenerator(4);
- gen.addLoadImmediate(Register.R0, 42);
- assertProgramEquals(new byte[]{LI_OP | SIZE8, 42}, gen.generate());
-
- // 2-byte immediate: li R1, 0x1234
- gen = new ApfGenerator(4);
- gen.addLoadImmediate(Register.R1, 0x1234);
- assertProgramEquals(new byte[]{LI_OP | SIZE16 | R1, 0x12, 0x34}, gen.generate());
-
- // 4-byte immediate: li R0, 0x12345678
- gen = new ApfGenerator(3);
- gen.addLoadImmediate(Register.R0, 0x12345678);
- assertProgramEquals(
- new byte[]{LI_OP | SIZE32, 0x12, 0x34, 0x56, 0x78},
- gen.generate());
- }
-
- /**
- * Test that the generator emits negative immediates using the shortest possible encoding.
- */
- @Test
- public void testNegativeImmediateEncoding() throws IllegalInstructionException {
- ApfGenerator gen;
-
- // 1-byte negative immediate: li R0, -42
- gen = new ApfGenerator(3);
- gen.addLoadImmediate(Register.R0, -42);
- assertProgramEquals(new byte[]{LI_OP | SIZE8, -42}, gen.generate());
-
- // 2-byte negative immediate: li R1, -0x1122
- gen = new ApfGenerator(3);
- gen.addLoadImmediate(Register.R1, -0x1122);
- assertProgramEquals(new byte[]{LI_OP | SIZE16 | R1, (byte)0xEE, (byte)0xDE},
- gen.generate());
-
- // 4-byte negative immediate: li R0, -0x11223344
- gen = new ApfGenerator(3);
- gen.addLoadImmediate(Register.R0, -0x11223344);
- assertProgramEquals(
- new byte[]{LI_OP | SIZE32, (byte)0xEE, (byte)0xDD, (byte)0xCC, (byte)0xBC},
- gen.generate());
- }
-
- /**
- * Test that the generator correctly emits positive and negative immediates for LDDW/STDW.
- */
- @Test
- public void testLoadStoreDataEncoding() throws IllegalInstructionException {
- ApfGenerator gen;
-
- // Load data with no offset: lddw R0, [0 + r1]
- gen = new ApfGenerator(3);
- gen.addLoadData(Register.R0, 0);
- assertProgramEquals(new byte[]{LDDW_OP | SIZE0}, gen.generate());
-
- // Store data with 8bit negative offset: lddw r0, [-42 + r1]
- gen = new ApfGenerator(3);
- gen.addStoreData(Register.R0, -42);
- assertProgramEquals(new byte[]{STDW_OP | SIZE8, -42}, gen.generate());
-
- // Store data to R1 with 16bit negative offset: stdw r1, [-0x1122 + r0]
- gen = new ApfGenerator(3);
- gen.addStoreData(Register.R1, -0x1122);
- assertProgramEquals(new byte[]{STDW_OP | SIZE16 | R1, (byte)0xEE, (byte)0xDE},
- gen.generate());
-
- // Load data to R1 with 32bit negative offset: lddw r1, [0xDEADBEEF + r0]
- gen = new ApfGenerator(3);
- gen.addLoadData(Register.R1, 0xDEADBEEF);
- assertProgramEquals(
- new byte[]{LDDW_OP | SIZE32 | R1, (byte)0xDE, (byte)0xAD, (byte)0xBE, (byte)0xEF},
- gen.generate());
- }
-
- /**
- * Test that the interpreter correctly executes STDW with a negative 8bit offset
- */
- @Test
- public void testApfDataWrite() throws IllegalInstructionException, Exception {
- byte[] packet = new byte[MIN_PKT_SIZE];
- byte[] data = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
- byte[] expected_data = data.clone();
-
- // No memory access instructions: should leave the data segment untouched.
- ApfGenerator gen = new ApfGenerator(3);
- assertDataMemoryContents(PASS, gen.generate(), packet, data, expected_data);
-
- // Expect value 0x87654321 to be stored starting from address -11 from the end of the
- // data buffer, in big-endian order.
- gen = new ApfGenerator(3);
- gen.addLoadImmediate(Register.R0, 0x87654321);
- gen.addLoadImmediate(Register.R1, -5);
- gen.addStoreData(Register.R0, -6); // -5 + -6 = -11 (offset +5 with data_len=16)
- expected_data[5] = (byte)0x87;
- expected_data[6] = (byte)0x65;
- expected_data[7] = (byte)0x43;
- expected_data[8] = (byte)0x21;
- assertDataMemoryContents(PASS, gen.generate(), packet, data, expected_data);
- }
-
- /**
- * Test that the interpreter correctly executes LDDW with a negative 16bit offset
- */
- @Test
- public void testApfDataRead() throws IllegalInstructionException, Exception {
- // Program that DROPs if address 10 (-6) contains 0x87654321.
- ApfGenerator gen = new ApfGenerator(3);
- gen.addLoadImmediate(Register.R1, 1000);
- gen.addLoadData(Register.R0, -1006); // 1000 + -1006 = -6 (offset +10 with data_len=16)
- gen.addJumpIfR0Equals(0x87654321, gen.DROP_LABEL);
- byte[] program = gen.generate();
- byte[] packet = new byte[MIN_PKT_SIZE];
-
- // Content is incorrect (last byte does not match) -> PASS
- byte[] data = new byte[16];
- data[10] = (byte)0x87;
- data[11] = (byte)0x65;
- data[12] = (byte)0x43;
- data[13] = (byte)0x00; // != 0x21
- byte[] expected_data = data.clone();
- assertDataMemoryContents(PASS, program, packet, data, expected_data);
-
- // Fix the last byte -> conditional jump taken -> DROP
- data[13] = (byte)0x21;
- expected_data = data;
- assertDataMemoryContents(DROP, program, packet, data, expected_data);
- }
-
- /**
- * Test that the interpreter correctly executes LDDW followed by a STDW.
- * To cover a few more edge cases, LDDW has a 0bit offset, while STDW has a positive 8bit
- * offset.
- */
- @Test
- public void testApfDataReadModifyWrite() throws IllegalInstructionException, Exception {
- ApfGenerator gen = new ApfGenerator(3);
- gen.addLoadImmediate(Register.R1, -22);
- gen.addLoadData(Register.R0, 0); // Load from address 32 -22 + 0 = 10
- gen.addAdd(0x78453412); // 87654321 + 78453412 = FFAA7733
- gen.addStoreData(Register.R0, 4); // Write back to address 32 -22 + 4 = 14
-
- byte[] packet = new byte[MIN_PKT_SIZE];
- byte[] data = new byte[32];
- data[10] = (byte)0x87;
- data[11] = (byte)0x65;
- data[12] = (byte)0x43;
- data[13] = (byte)0x21;
- byte[] expected_data = data.clone();
- expected_data[14] = (byte)0xFF;
- expected_data[15] = (byte)0xAA;
- expected_data[16] = (byte)0x77;
- expected_data[17] = (byte)0x33;
- assertDataMemoryContents(PASS, gen.generate(), packet, data, expected_data);
- }
-
- @Test
- public void testApfDataBoundChecking() throws IllegalInstructionException, Exception {
- byte[] packet = new byte[MIN_PKT_SIZE];
- byte[] data = new byte[32];
- byte[] expected_data = data;
-
- // Program that DROPs unconditionally. This is our the baseline.
- ApfGenerator gen = new ApfGenerator(3);
- gen.addLoadImmediate(Register.R0, 3);
- gen.addLoadData(Register.R1, 7);
- gen.addJump(gen.DROP_LABEL);
- assertDataMemoryContents(DROP, gen.generate(), packet, data, expected_data);
-
- // Same program as before, but this time we're trying to load past the end of the data.
- gen = new ApfGenerator(3);
- gen.addLoadImmediate(Register.R0, 20);
- gen.addLoadData(Register.R1, 15); // 20 + 15 > 32
- gen.addJump(gen.DROP_LABEL); // Not reached.
- assertDataMemoryContents(PASS, gen.generate(), packet, data, expected_data);
-
- // Subtracting an immediate should work...
- gen = new ApfGenerator(3);
- gen.addLoadImmediate(Register.R0, 20);
- gen.addLoadData(Register.R1, -4);
- gen.addJump(gen.DROP_LABEL);
- assertDataMemoryContents(DROP, gen.generate(), packet, data, expected_data);
-
- // ...and underflowing simply wraps around to the end of the buffer...
- gen = new ApfGenerator(3);
- gen.addLoadImmediate(Register.R0, 20);
- gen.addLoadData(Register.R1, -30);
- gen.addJump(gen.DROP_LABEL);
- assertDataMemoryContents(DROP, gen.generate(), packet, data, expected_data);
-
- // ...but doesn't allow accesses before the start of the buffer
- gen = new ApfGenerator(3);
- gen.addLoadImmediate(Register.R0, 20);
- gen.addLoadData(Register.R1, -1000);
- gen.addJump(gen.DROP_LABEL); // Not reached.
- assertDataMemoryContents(PASS, gen.generate(), packet, data, expected_data);
- }
-
- /**
- * Generate some BPF programs, translate them to APF, then run APF and BPF programs
- * over packet traces and verify both programs filter out the same packets.
- */
- @Test
- public void testApfAgainstBpf() throws Exception {
- String[] tcpdump_filters = new String[]{ "udp", "tcp", "icmp", "icmp6", "udp port 53",
- "arp", "dst 239.255.255.250", "arp or tcp or udp port 53", "net 192.168.1.0/24",
- "arp or icmp6 or portrange 53-54", "portrange 53-54 or portrange 100-50000",
- "tcp[tcpflags] & (tcp-ack|tcp-fin) != 0 and (ip[2:2] > 57 or icmp)" };
- String pcap_filename = stageFile(R.raw.apf);
- for (String tcpdump_filter : tcpdump_filters) {
- byte[] apf_program = Bpf2Apf.convert(compileToBpf(tcpdump_filter));
- assertTrue("Failed to match for filter: " + tcpdump_filter,
- compareBpfApf(tcpdump_filter, pcap_filename, apf_program));
- }
- }
-
- /**
- * Generate APF program, run pcap file though APF filter, then check all the packets in the file
- * should be dropped.
- */
- @Test
- public void testApfFilterPcapFile() throws Exception {
- final byte[] MOCK_PCAP_IPV4_ADDR = {(byte) 172, 16, 7, (byte) 151};
- String pcapFilename = stageFile(R.raw.apfPcap);
- MockIpClientCallback ipClientCallback = new MockIpClientCallback();
- LinkAddress link = new LinkAddress(InetAddress.getByAddress(MOCK_PCAP_IPV4_ADDR), 16);
- LinkProperties lp = new LinkProperties();
- lp.addLinkAddress(link);
-
- ApfConfiguration config = getDefaultConfig();
- ApfCapabilities MOCK_APF_PCAP_CAPABILITIES = new ApfCapabilities(4, 1700, ARPHRD_ETHER);
- config.apfCapabilities = MOCK_APF_PCAP_CAPABILITIES;
- config.multicastFilter = DROP_MULTICAST;
- config.ieee802_3Filter = DROP_802_3_FRAMES;
- TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog);
- apfFilter.setLinkProperties(lp);
- byte[] program = ipClientCallback.getApfProgram();
- byte[] data = new byte[ApfFilter.Counter.totalSize()];
- final boolean result;
-
- result = dropsAllPackets(program, data, pcapFilename);
- Log.i(TAG, "testApfFilterPcapFile(): Data counters: " + HexDump.toHexString(data, false));
-
- assertTrue("Failed to drop all packets by filter. \nAPF counters:" +
- HexDump.toHexString(data, false), result);
- }
-
- private class MockIpClientCallback extends IpClientCallbacks {
- private final ConditionVariable mGotApfProgram = new ConditionVariable();
- private byte[] mLastApfProgram;
-
- @Override
- public void installPacketFilter(byte[] filter) {
- mLastApfProgram = filter;
- mGotApfProgram.open();
- }
-
- public void resetApfProgramWait() {
- mGotApfProgram.close();
- }
-
- public byte[] getApfProgram() {
- assertTrue(mGotApfProgram.block(TIMEOUT_MS));
- return mLastApfProgram;
- }
-
- public void assertNoProgramUpdate() {
- assertFalse(mGotApfProgram.block(TIMEOUT_MS));
- }
- }
-
- private static class TestApfFilter extends ApfFilter {
- public static final byte[] MOCK_MAC_ADDR = {1,2,3,4,5,6};
-
- private FileDescriptor mWriteSocket;
- private final long mFixedTimeMs = SystemClock.elapsedRealtime();
-
- public TestApfFilter(Context context, ApfConfiguration config,
- IpClientCallbacks ipClientCallback, IpConnectivityLog log) throws Exception {
- super(context, config, InterfaceParams.getByName("lo"), ipClientCallback, log);
- }
-
- // Pretend an RA packet has been received and show it to ApfFilter.
- public void pretendPacketReceived(byte[] packet) throws IOException, ErrnoException {
- // ApfFilter's ReceiveThread will be waiting to read this.
- Os.write(mWriteSocket, packet, 0, packet.length);
- }
-
- @Override
- protected long currentTimeSeconds() {
- return mFixedTimeMs / DateUtils.SECOND_IN_MILLIS;
- }
-
- @Override
- void maybeStartFilter() {
- mHardwareAddress = MOCK_MAC_ADDR;
- installNewProgramLocked();
-
- // Create two sockets, "readSocket" and "mWriteSocket" and connect them together.
- FileDescriptor readSocket = new FileDescriptor();
- mWriteSocket = new FileDescriptor();
- try {
- Os.socketpair(AF_UNIX, SOCK_STREAM, 0, mWriteSocket, readSocket);
- } catch (ErrnoException e) {
- fail();
- return;
- }
- // Now pass readSocket to ReceiveThread as if it was setup to read raw RAs.
- // This allows us to pretend RA packets have been recieved via pretendPacketReceived().
- mReceiveThread = new ReceiveThread(readSocket);
- mReceiveThread.start();
- }
-
- @Override
- public void shutdown() {
- super.shutdown();
- IoUtils.closeQuietly(mWriteSocket);
- }
- }
-
- private static final int ETH_HEADER_LEN = 14;
- private static final int ETH_DEST_ADDR_OFFSET = 0;
- private static final int ETH_ETHERTYPE_OFFSET = 12;
- private static final byte[] ETH_BROADCAST_MAC_ADDRESS =
- {(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff };
-
- private static final int IPV4_VERSION_IHL_OFFSET = ETH_HEADER_LEN + 0;
- private static final int IPV4_PROTOCOL_OFFSET = ETH_HEADER_LEN + 9;
- private static final int IPV4_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 16;
- private static final byte[] IPV4_BROADCAST_ADDRESS =
- {(byte) 255, (byte) 255, (byte) 255, (byte) 255};
-
- private static final int IPV6_NEXT_HEADER_OFFSET = ETH_HEADER_LEN + 6;
- private static final int IPV6_HEADER_LEN = 40;
- private static final int IPV6_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 24;
- // The IPv6 all nodes address ff02::1
- private static final byte[] IPV6_ALL_NODES_ADDRESS =
- { (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
- private static final byte[] IPV6_ALL_ROUTERS_ADDRESS =
- { (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 };
-
- private static final int ICMP6_TYPE_OFFSET = ETH_HEADER_LEN + IPV6_HEADER_LEN;
- private static final int ICMP6_ROUTER_SOLICITATION = 133;
- private static final int ICMP6_ROUTER_ADVERTISEMENT = 134;
- private static final int ICMP6_NEIGHBOR_SOLICITATION = 135;
- private static final int ICMP6_NEIGHBOR_ANNOUNCEMENT = 136;
-
- private static final int ICMP6_RA_HEADER_LEN = 16;
- private static final int ICMP6_RA_ROUTER_LIFETIME_OFFSET =
- ETH_HEADER_LEN + IPV6_HEADER_LEN + 6;
- private static final int ICMP6_RA_CHECKSUM_OFFSET =
- ETH_HEADER_LEN + IPV6_HEADER_LEN + 2;
- private static final int ICMP6_RA_OPTION_OFFSET =
- ETH_HEADER_LEN + IPV6_HEADER_LEN + ICMP6_RA_HEADER_LEN;
-
- private static final int ICMP6_PREFIX_OPTION_TYPE = 3;
- private static final int ICMP6_PREFIX_OPTION_LEN = 32;
- private static final int ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET = 4;
- private static final int ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET = 8;
-
- // From RFC6106: Recursive DNS Server option
- private static final int ICMP6_RDNSS_OPTION_TYPE = 25;
- // From RFC6106: DNS Search List option
- private static final int ICMP6_DNSSL_OPTION_TYPE = 31;
-
- // From RFC4191: Route Information option
- private static final int ICMP6_ROUTE_INFO_OPTION_TYPE = 24;
- // Above three options all have the same format:
- private static final int ICMP6_4_BYTE_OPTION_LEN = 8;
- private static final int ICMP6_4_BYTE_LIFETIME_OFFSET = 4;
- private static final int ICMP6_4_BYTE_LIFETIME_LEN = 4;
-
- private static final int UDP_HEADER_LEN = 8;
- private static final int UDP_DESTINATION_PORT_OFFSET = ETH_HEADER_LEN + 22;
-
- private static final int DHCP_CLIENT_PORT = 68;
- private static final int DHCP_CLIENT_MAC_OFFSET = ETH_HEADER_LEN + UDP_HEADER_LEN + 48;
-
- private static final int ARP_HEADER_OFFSET = ETH_HEADER_LEN;
- private static final byte[] ARP_IPV4_REQUEST_HEADER = {
- 0, 1, // Hardware type: Ethernet (1)
- 8, 0, // Protocol type: IP (0x0800)
- 6, // Hardware size: 6
- 4, // Protocol size: 4
- 0, 1 // Opcode: request (1)
- };
- private static final byte[] ARP_IPV4_REPLY_HEADER = {
- 0, 1, // Hardware type: Ethernet (1)
- 8, 0, // Protocol type: IP (0x0800)
- 6, // Hardware size: 6
- 4, // Protocol size: 4
- 0, 2 // Opcode: reply (2)
- };
- private static final int ARP_SOURCE_IP_ADDRESS_OFFSET = ARP_HEADER_OFFSET + 14;
- private static final int ARP_TARGET_IP_ADDRESS_OFFSET = ARP_HEADER_OFFSET + 24;
-
- private static final byte[] MOCK_IPV4_ADDR = {10, 0, 0, 1};
- private static final byte[] MOCK_BROADCAST_IPV4_ADDR = {10, 0, 31, (byte) 255}; // prefix = 19
- private static final byte[] MOCK_MULTICAST_IPV4_ADDR = {(byte) 224, 0, 0, 1};
- private static final byte[] ANOTHER_IPV4_ADDR = {10, 0, 0, 2};
- private static final byte[] IPV4_SOURCE_ADDR = {10, 0, 0, 3};
- private static final byte[] ANOTHER_IPV4_SOURCE_ADDR = {(byte) 192, 0, 2, 1};
- private static final byte[] BUG_PROBE_SOURCE_ADDR1 = {0, 0, 1, 2};
- private static final byte[] BUG_PROBE_SOURCE_ADDR2 = {3, 4, 0, 0};
- private static final byte[] IPV4_ANY_HOST_ADDR = {0, 0, 0, 0};
-
- // Helper to initialize a default apfFilter.
- private ApfFilter setupApfFilter(IpClientCallbacks ipClientCallback, ApfConfiguration config)
- throws Exception {
- LinkAddress link = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 19);
- LinkProperties lp = new LinkProperties();
- lp.addLinkAddress(link);
- TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog);
- apfFilter.setLinkProperties(lp);
- return apfFilter;
- }
-
- @Test
- public void testApfFilterIPv4() throws Exception {
- MockIpClientCallback ipClientCallback = new MockIpClientCallback();
- LinkAddress link = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 19);
- LinkProperties lp = new LinkProperties();
- lp.addLinkAddress(link);
-
- ApfConfiguration config = getDefaultConfig();
- config.multicastFilter = DROP_MULTICAST;
- TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog);
- apfFilter.setLinkProperties(lp);
-
- byte[] program = ipClientCallback.getApfProgram();
-
- // Verify empty packet of 100 zero bytes is passed
- ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
- assertPass(program, packet.array());
-
- // Verify unicast IPv4 packet is passed
- put(packet, ETH_DEST_ADDR_OFFSET, TestApfFilter.MOCK_MAC_ADDR);
- packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
- put(packet, IPV4_DEST_ADDR_OFFSET, MOCK_IPV4_ADDR);
- assertPass(program, packet.array());
-
- // Verify L2 unicast to IPv4 broadcast addresses is dropped (b/30231088)
- put(packet, IPV4_DEST_ADDR_OFFSET, IPV4_BROADCAST_ADDRESS);
- assertDrop(program, packet.array());
- put(packet, IPV4_DEST_ADDR_OFFSET, MOCK_BROADCAST_IPV4_ADDR);
- assertDrop(program, packet.array());
-
- // Verify multicast/broadcast IPv4, not DHCP to us, is dropped
- put(packet, ETH_DEST_ADDR_OFFSET, ETH_BROADCAST_MAC_ADDRESS);
- assertDrop(program, packet.array());
- packet.put(IPV4_VERSION_IHL_OFFSET, (byte)0x45);
- assertDrop(program, packet.array());
- packet.put(IPV4_PROTOCOL_OFFSET, (byte)IPPROTO_UDP);
- assertDrop(program, packet.array());
- packet.putShort(UDP_DESTINATION_PORT_OFFSET, (short)DHCP_CLIENT_PORT);
- assertDrop(program, packet.array());
- put(packet, IPV4_DEST_ADDR_OFFSET, MOCK_MULTICAST_IPV4_ADDR);
- assertDrop(program, packet.array());
- put(packet, IPV4_DEST_ADDR_OFFSET, MOCK_BROADCAST_IPV4_ADDR);
- assertDrop(program, packet.array());
- put(packet, IPV4_DEST_ADDR_OFFSET, IPV4_BROADCAST_ADDRESS);
- assertDrop(program, packet.array());
-
- // Verify broadcast IPv4 DHCP to us is passed
- put(packet, DHCP_CLIENT_MAC_OFFSET, TestApfFilter.MOCK_MAC_ADDR);
- assertPass(program, packet.array());
-
- // Verify unicast IPv4 DHCP to us is passed
- put(packet, ETH_DEST_ADDR_OFFSET, TestApfFilter.MOCK_MAC_ADDR);
- assertPass(program, packet.array());
-
- apfFilter.shutdown();
- }
-
- @Test
- public void testApfFilterIPv6() throws Exception {
- MockIpClientCallback ipClientCallback = new MockIpClientCallback();
- ApfConfiguration config = getDefaultConfig();
- TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog);
- byte[] program = ipClientCallback.getApfProgram();
-
- // Verify empty IPv6 packet is passed
- ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
- packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6);
- assertPass(program, packet.array());
-
- // Verify empty ICMPv6 packet is passed
- packet.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_ICMPV6);
- assertPass(program, packet.array());
-
- // Verify empty ICMPv6 NA packet is passed
- packet.put(ICMP6_TYPE_OFFSET, (byte)ICMP6_NEIGHBOR_ANNOUNCEMENT);
- assertPass(program, packet.array());
-
- // Verify ICMPv6 NA to ff02::1 is dropped
- put(packet, IPV6_DEST_ADDR_OFFSET, IPV6_ALL_NODES_ADDRESS);
- assertDrop(program, packet.array());
-
- // Verify ICMPv6 RS to any is dropped
- packet.put(ICMP6_TYPE_OFFSET, (byte)ICMP6_ROUTER_SOLICITATION);
- assertDrop(program, packet.array());
- put(packet, IPV6_DEST_ADDR_OFFSET, IPV6_ALL_ROUTERS_ADDRESS);
- assertDrop(program, packet.array());
-
- apfFilter.shutdown();
- }
-
- @Test
- public void testApfFilterMulticast() throws Exception {
- final byte[] unicastIpv4Addr = {(byte)192,0,2,63};
- final byte[] broadcastIpv4Addr = {(byte)192,0,2,(byte)255};
- final byte[] multicastIpv4Addr = {(byte)224,0,0,1};
- final byte[] multicastIpv6Addr = {(byte)0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,(byte)0xfb};
-
- MockIpClientCallback ipClientCallback = new MockIpClientCallback();
- LinkAddress link = new LinkAddress(InetAddress.getByAddress(unicastIpv4Addr), 24);
- LinkProperties lp = new LinkProperties();
- lp.addLinkAddress(link);
-
- ApfConfiguration config = getDefaultConfig();
- config.ieee802_3Filter = DROP_802_3_FRAMES;
- TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog);
- apfFilter.setLinkProperties(lp);
-
- byte[] program = ipClientCallback.getApfProgram();
-
- // Construct IPv4 and IPv6 multicast packets.
- ByteBuffer mcastv4packet = ByteBuffer.wrap(new byte[100]);
- mcastv4packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
- put(mcastv4packet, IPV4_DEST_ADDR_OFFSET, multicastIpv4Addr);
-
- ByteBuffer mcastv6packet = ByteBuffer.wrap(new byte[100]);
- mcastv6packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6);
- mcastv6packet.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_UDP);
- put(mcastv6packet, IPV6_DEST_ADDR_OFFSET, multicastIpv6Addr);
-
- // Construct IPv4 broadcast packet.
- ByteBuffer bcastv4packet1 = ByteBuffer.wrap(new byte[100]);
- bcastv4packet1.put(ETH_BROADCAST_MAC_ADDRESS);
- bcastv4packet1.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
- put(bcastv4packet1, IPV4_DEST_ADDR_OFFSET, multicastIpv4Addr);
-
- ByteBuffer bcastv4packet2 = ByteBuffer.wrap(new byte[100]);
- bcastv4packet2.put(ETH_BROADCAST_MAC_ADDRESS);
- bcastv4packet2.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
- put(bcastv4packet2, IPV4_DEST_ADDR_OFFSET, IPV4_BROADCAST_ADDRESS);
-
- // Construct IPv4 broadcast with L2 unicast address packet (b/30231088).
- ByteBuffer bcastv4unicastl2packet = ByteBuffer.wrap(new byte[100]);
- bcastv4unicastl2packet.put(TestApfFilter.MOCK_MAC_ADDR);
- bcastv4unicastl2packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
- put(bcastv4unicastl2packet, IPV4_DEST_ADDR_OFFSET, broadcastIpv4Addr);
-
- // Verify initially disabled multicast filter is off
- assertPass(program, mcastv4packet.array());
- assertPass(program, mcastv6packet.array());
- assertPass(program, bcastv4packet1.array());
- assertPass(program, bcastv4packet2.array());
- assertPass(program, bcastv4unicastl2packet.array());
-
- // Turn on multicast filter and verify it works
- ipClientCallback.resetApfProgramWait();
- apfFilter.setMulticastFilter(true);
- program = ipClientCallback.getApfProgram();
- assertDrop(program, mcastv4packet.array());
- assertDrop(program, mcastv6packet.array());
- assertDrop(program, bcastv4packet1.array());
- assertDrop(program, bcastv4packet2.array());
- assertDrop(program, bcastv4unicastl2packet.array());
-
- // Turn off multicast filter and verify it's off
- ipClientCallback.resetApfProgramWait();
- apfFilter.setMulticastFilter(false);
- program = ipClientCallback.getApfProgram();
- assertPass(program, mcastv4packet.array());
- assertPass(program, mcastv6packet.array());
- assertPass(program, bcastv4packet1.array());
- assertPass(program, bcastv4packet2.array());
- assertPass(program, bcastv4unicastl2packet.array());
-
- // Verify it can be initialized to on
- ipClientCallback.resetApfProgramWait();
- apfFilter.shutdown();
- config.multicastFilter = DROP_MULTICAST;
- config.ieee802_3Filter = DROP_802_3_FRAMES;
- apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog);
- apfFilter.setLinkProperties(lp);
- program = ipClientCallback.getApfProgram();
- assertDrop(program, mcastv4packet.array());
- assertDrop(program, mcastv6packet.array());
- assertDrop(program, bcastv4packet1.array());
- assertDrop(program, bcastv4unicastl2packet.array());
-
- // Verify that ICMPv6 multicast is not dropped.
- mcastv6packet.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_ICMPV6);
- assertPass(program, mcastv6packet.array());
-
- apfFilter.shutdown();
- }
-
- @Test
- public void testApfFilterMulticastPingWhileDozing() throws Exception {
- MockIpClientCallback ipClientCallback = new MockIpClientCallback();
- ApfFilter apfFilter = setupApfFilter(ipClientCallback, getDefaultConfig());
-
- // Construct a multicast ICMPv6 ECHO request.
- final byte[] multicastIpv6Addr = {(byte)0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,(byte)0xfb};
- ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
- packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6);
- packet.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_ICMPV6);
- packet.put(ICMP6_TYPE_OFFSET, (byte)ICMPV6_ECHO_REQUEST_TYPE);
- put(packet, IPV6_DEST_ADDR_OFFSET, multicastIpv6Addr);
-
- // Normally, we let multicast pings alone...
- assertPass(ipClientCallback.getApfProgram(), packet.array());
-
- // ...and even while dozing...
- apfFilter.setDozeMode(true);
- assertPass(ipClientCallback.getApfProgram(), packet.array());
-
- // ...but when the multicast filter is also enabled, drop the multicast pings to save power.
- apfFilter.setMulticastFilter(true);
- assertDrop(ipClientCallback.getApfProgram(), packet.array());
-
- // However, we should still let through all other ICMPv6 types.
- ByteBuffer raPacket = ByteBuffer.wrap(packet.array().clone());
- raPacket.put(ICMP6_TYPE_OFFSET, (byte)ICMPV6_ROUTER_ADVERTISEMENT);
- assertPass(ipClientCallback.getApfProgram(), raPacket.array());
-
- // Now wake up from doze mode to ensure that we no longer drop the packets.
- // (The multicast filter is still enabled at this point).
- apfFilter.setDozeMode(false);
- assertPass(ipClientCallback.getApfProgram(), packet.array());
-
- apfFilter.shutdown();
- }
-
- @Test
- public void testApfFilter802_3() throws Exception {
- MockIpClientCallback ipClientCallback = new MockIpClientCallback();
- ApfConfiguration config = getDefaultConfig();
- ApfFilter apfFilter = setupApfFilter(ipClientCallback, config);
- byte[] program = ipClientCallback.getApfProgram();
-
- // Verify empty packet of 100 zero bytes is passed
- // Note that eth-type = 0 makes it an IEEE802.3 frame
- ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
- assertPass(program, packet.array());
-
- // Verify empty packet with IPv4 is passed
- packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
- assertPass(program, packet.array());
-
- // Verify empty IPv6 packet is passed
- packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6);
- assertPass(program, packet.array());
-
- // Now turn on the filter
- ipClientCallback.resetApfProgramWait();
- apfFilter.shutdown();
- config.ieee802_3Filter = DROP_802_3_FRAMES;
- apfFilter = setupApfFilter(ipClientCallback, config);
- program = ipClientCallback.getApfProgram();
-
- // Verify that IEEE802.3 frame is dropped
- // In this case ethtype is used for payload length
- packet.putShort(ETH_ETHERTYPE_OFFSET, (short)(100 - 14));
- assertDrop(program, packet.array());
-
- // Verify that IPv4 (as example of Ethernet II) frame will pass
- packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
- assertPass(program, packet.array());
-
- // Verify that IPv6 (as example of Ethernet II) frame will pass
- packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6);
- assertPass(program, packet.array());
-
- apfFilter.shutdown();
- }
-
- @Test
- public void testApfFilterEthTypeBL() throws Exception {
- final int[] emptyBlackList = {};
- final int[] ipv4BlackList = {ETH_P_IP};
- final int[] ipv4Ipv6BlackList = {ETH_P_IP, ETH_P_IPV6};
-
- MockIpClientCallback ipClientCallback = new MockIpClientCallback();
- ApfConfiguration config = getDefaultConfig();
- ApfFilter apfFilter = setupApfFilter(ipClientCallback, config);
- byte[] program = ipClientCallback.getApfProgram();
-
- // Verify empty packet of 100 zero bytes is passed
- // Note that eth-type = 0 makes it an IEEE802.3 frame
- ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
- assertPass(program, packet.array());
-
- // Verify empty packet with IPv4 is passed
- packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
- assertPass(program, packet.array());
-
- // Verify empty IPv6 packet is passed
- packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6);
- assertPass(program, packet.array());
-
- // Now add IPv4 to the black list
- ipClientCallback.resetApfProgramWait();
- apfFilter.shutdown();
- config.ethTypeBlackList = ipv4BlackList;
- apfFilter = setupApfFilter(ipClientCallback, config);
- program = ipClientCallback.getApfProgram();
-
- // Verify that IPv4 frame will be dropped
- packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
- assertDrop(program, packet.array());
-
- // Verify that IPv6 frame will pass
- packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6);
- assertPass(program, packet.array());
-
- // Now let us have both IPv4 and IPv6 in the black list
- ipClientCallback.resetApfProgramWait();
- apfFilter.shutdown();
- config.ethTypeBlackList = ipv4Ipv6BlackList;
- apfFilter = setupApfFilter(ipClientCallback, config);
- program = ipClientCallback.getApfProgram();
-
- // Verify that IPv4 frame will be dropped
- packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
- assertDrop(program, packet.array());
-
- // Verify that IPv6 frame will be dropped
- packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6);
- assertDrop(program, packet.array());
-
- apfFilter.shutdown();
- }
-
- private byte[] getProgram(MockIpClientCallback cb, ApfFilter filter, LinkProperties lp) {
- cb.resetApfProgramWait();
- filter.setLinkProperties(lp);
- return cb.getApfProgram();
- }
-
- private void verifyArpFilter(byte[] program, int filterResult) {
- // Verify ARP request packet
- assertPass(program, arpRequestBroadcast(MOCK_IPV4_ADDR));
- assertVerdict(filterResult, program, arpRequestBroadcast(ANOTHER_IPV4_ADDR));
- assertDrop(program, arpRequestBroadcast(IPV4_ANY_HOST_ADDR));
-
- // Verify ARP reply packets from different source ip
- assertDrop(program, arpReply(IPV4_ANY_HOST_ADDR, IPV4_ANY_HOST_ADDR));
- assertPass(program, arpReply(ANOTHER_IPV4_SOURCE_ADDR, IPV4_ANY_HOST_ADDR));
- assertPass(program, arpReply(BUG_PROBE_SOURCE_ADDR1, IPV4_ANY_HOST_ADDR));
- assertPass(program, arpReply(BUG_PROBE_SOURCE_ADDR2, IPV4_ANY_HOST_ADDR));
-
- // Verify unicast ARP reply packet is always accepted.
- assertPass(program, arpReply(IPV4_SOURCE_ADDR, MOCK_IPV4_ADDR));
- assertPass(program, arpReply(IPV4_SOURCE_ADDR, ANOTHER_IPV4_ADDR));
- assertPass(program, arpReply(IPV4_SOURCE_ADDR, IPV4_ANY_HOST_ADDR));
-
- // Verify GARP reply packets are always filtered
- assertDrop(program, garpReply());
- }
-
- @Test
- public void testApfFilterArp() throws Exception {
- MockIpClientCallback ipClientCallback = new MockIpClientCallback();
- ApfConfiguration config = getDefaultConfig();
- config.multicastFilter = DROP_MULTICAST;
- config.ieee802_3Filter = DROP_802_3_FRAMES;
- TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog);
-
- // Verify initially ARP request filter is off, and GARP filter is on.
- verifyArpFilter(ipClientCallback.getApfProgram(), PASS);
-
- // Inform ApfFilter of our address and verify ARP filtering is on
- LinkAddress linkAddress = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 24);
- LinkProperties lp = new LinkProperties();
- assertTrue(lp.addLinkAddress(linkAddress));
- verifyArpFilter(getProgram(ipClientCallback, apfFilter, lp), DROP);
-
- // Inform ApfFilter of loss of IP and verify ARP filtering is off
- verifyArpFilter(getProgram(ipClientCallback, apfFilter, new LinkProperties()), PASS);
-
- apfFilter.shutdown();
- }
-
- private static byte[] arpReply(byte[] sip, byte[] tip) {
- ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
- packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_ARP);
- put(packet, ARP_HEADER_OFFSET, ARP_IPV4_REPLY_HEADER);
- put(packet, ARP_SOURCE_IP_ADDRESS_OFFSET, sip);
- put(packet, ARP_TARGET_IP_ADDRESS_OFFSET, tip);
- return packet.array();
- }
-
- private static byte[] arpRequestBroadcast(byte[] tip) {
- ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
- packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_ARP);
- put(packet, ETH_DEST_ADDR_OFFSET, ETH_BROADCAST_MAC_ADDRESS);
- put(packet, ARP_HEADER_OFFSET, ARP_IPV4_REQUEST_HEADER);
- put(packet, ARP_TARGET_IP_ADDRESS_OFFSET, tip);
- return packet.array();
- }
-
- private static byte[] garpReply() {
- ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
- packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_ARP);
- put(packet, ETH_DEST_ADDR_OFFSET, ETH_BROADCAST_MAC_ADDRESS);
- put(packet, ARP_HEADER_OFFSET, ARP_IPV4_REPLY_HEADER);
- put(packet, ARP_TARGET_IP_ADDRESS_OFFSET, IPV4_ANY_HOST_ADDR);
- return packet.array();
- }
-
- // Verify that the last program pushed to the IpClient.Callback properly filters the
- // given packet for the given lifetime.
- private void verifyRaLifetime(byte[] program, ByteBuffer packet, int lifetime) {
- final int FRACTION_OF_LIFETIME = 6;
- final int ageLimit = lifetime / FRACTION_OF_LIFETIME;
-
- // Verify new program should drop RA for 1/6th its lifetime and pass afterwards.
- assertDrop(program, packet.array());
- assertDrop(program, packet.array(), ageLimit);
- assertPass(program, packet.array(), ageLimit + 1);
- assertPass(program, packet.array(), lifetime);
- // Verify RA checksum is ignored
- final short originalChecksum = packet.getShort(ICMP6_RA_CHECKSUM_OFFSET);
- packet.putShort(ICMP6_RA_CHECKSUM_OFFSET, (short)12345);
- assertDrop(program, packet.array());
- packet.putShort(ICMP6_RA_CHECKSUM_OFFSET, (short)-12345);
- assertDrop(program, packet.array());
- packet.putShort(ICMP6_RA_CHECKSUM_OFFSET, originalChecksum);
-
- // Verify other changes to RA make it not match filter
- final byte originalFirstByte = packet.get(0);
- packet.put(0, (byte)-1);
- assertPass(program, packet.array());
- packet.put(0, (byte)0);
- assertDrop(program, packet.array());
- packet.put(0, originalFirstByte);
- }
-
- // Test that when ApfFilter is shown the given packet, it generates a program to filter it
- // for the given lifetime.
- private void verifyRaLifetime(TestApfFilter apfFilter, MockIpClientCallback ipClientCallback,
- ByteBuffer packet, int lifetime) throws IOException, ErrnoException {
- // Verify new program generated if ApfFilter witnesses RA
- ipClientCallback.resetApfProgramWait();
- apfFilter.pretendPacketReceived(packet.array());
- byte[] program = ipClientCallback.getApfProgram();
- verifyRaLifetime(program, packet, lifetime);
- }
-
- private void verifyRaEvent(RaEvent expected) {
- ArgumentCaptor<IpConnectivityLog.Event> captor =
- ArgumentCaptor.forClass(IpConnectivityLog.Event.class);
- verify(mLog, atLeastOnce()).log(captor.capture());
- RaEvent got = lastRaEvent(captor.getAllValues());
- if (!raEventEquals(expected, got)) {
- assertEquals(expected, got); // fail for printing an assertion error message.
- }
- }
-
- private RaEvent lastRaEvent(List<IpConnectivityLog.Event> events) {
- RaEvent got = null;
- for (Parcelable ev : events) {
- if (ev instanceof RaEvent) {
- got = (RaEvent) ev;
- }
- }
- return got;
- }
-
- private boolean raEventEquals(RaEvent ev1, RaEvent ev2) {
- return (ev1 != null) && (ev2 != null)
- && (ev1.routerLifetime == ev2.routerLifetime)
- && (ev1.prefixValidLifetime == ev2.prefixValidLifetime)
- && (ev1.prefixPreferredLifetime == ev2.prefixPreferredLifetime)
- && (ev1.routeInfoLifetime == ev2.routeInfoLifetime)
- && (ev1.rdnssLifetime == ev2.rdnssLifetime)
- && (ev1.dnsslLifetime == ev2.dnsslLifetime);
- }
-
- private void assertInvalidRa(TestApfFilter apfFilter, MockIpClientCallback ipClientCallback,
- ByteBuffer packet) throws IOException, ErrnoException {
- ipClientCallback.resetApfProgramWait();
- apfFilter.pretendPacketReceived(packet.array());
- ipClientCallback.assertNoProgramUpdate();
- }
-
- @Test
- public void testApfFilterRa() throws Exception {
- MockIpClientCallback ipClientCallback = new MockIpClientCallback();
- ApfConfiguration config = getDefaultConfig();
- config.multicastFilter = DROP_MULTICAST;
- config.ieee802_3Filter = DROP_802_3_FRAMES;
- TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog);
- byte[] program = ipClientCallback.getApfProgram();
-
- final int ROUTER_LIFETIME = 1000;
- final int PREFIX_VALID_LIFETIME = 200;
- final int PREFIX_PREFERRED_LIFETIME = 100;
- final int RDNSS_LIFETIME = 300;
- final int ROUTE_LIFETIME = 400;
- // Note that lifetime of 2000 will be ignored in favor of shorter route lifetime of 1000.
- final int DNSSL_LIFETIME = 2000;
- final int VERSION_TRAFFIC_CLASS_FLOW_LABEL_OFFSET = ETH_HEADER_LEN;
- // IPv6, traffic class = 0, flow label = 0x12345
- final int VERSION_TRAFFIC_CLASS_FLOW_LABEL = 0x60012345;
-
- // Verify RA is passed the first time
- ByteBuffer basePacket = ByteBuffer.wrap(new byte[ICMP6_RA_OPTION_OFFSET]);
- basePacket.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6);
- basePacket.putInt(VERSION_TRAFFIC_CLASS_FLOW_LABEL_OFFSET,
- VERSION_TRAFFIC_CLASS_FLOW_LABEL);
- basePacket.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_ICMPV6);
- basePacket.put(ICMP6_TYPE_OFFSET, (byte)ICMP6_ROUTER_ADVERTISEMENT);
- basePacket.putShort(ICMP6_RA_ROUTER_LIFETIME_OFFSET, (short)ROUTER_LIFETIME);
- basePacket.position(IPV6_DEST_ADDR_OFFSET);
- basePacket.put(IPV6_ALL_NODES_ADDRESS);
- assertPass(program, basePacket.array());
-
- verifyRaLifetime(apfFilter, ipClientCallback, basePacket, ROUTER_LIFETIME);
- verifyRaEvent(new RaEvent(ROUTER_LIFETIME, -1, -1, -1, -1, -1));
-
- ByteBuffer newFlowLabelPacket = ByteBuffer.wrap(new byte[ICMP6_RA_OPTION_OFFSET]);
- basePacket.clear();
- newFlowLabelPacket.put(basePacket);
- // Check that changes are ignored in every byte of the flow label.
- newFlowLabelPacket.putInt(VERSION_TRAFFIC_CLASS_FLOW_LABEL_OFFSET,
- VERSION_TRAFFIC_CLASS_FLOW_LABEL + 0x11111);
-
- // Ensure zero-length options cause the packet to be silently skipped.
- // Do this before we test other packets. http://b/29586253
- ByteBuffer zeroLengthOptionPacket = ByteBuffer.wrap(
- new byte[ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_OPTION_LEN]);
- basePacket.clear();
- zeroLengthOptionPacket.put(basePacket);
- zeroLengthOptionPacket.put((byte)ICMP6_PREFIX_OPTION_TYPE);
- zeroLengthOptionPacket.put((byte)0);
- assertInvalidRa(apfFilter, ipClientCallback, zeroLengthOptionPacket);
-
- // Generate several RAs with different options and lifetimes, and verify when
- // ApfFilter is shown these packets, it generates programs to filter them for the
- // appropriate lifetime.
- ByteBuffer prefixOptionPacket = ByteBuffer.wrap(
- new byte[ICMP6_RA_OPTION_OFFSET + ICMP6_PREFIX_OPTION_LEN]);
- basePacket.clear();
- prefixOptionPacket.put(basePacket);
- prefixOptionPacket.put((byte)ICMP6_PREFIX_OPTION_TYPE);
- prefixOptionPacket.put((byte)(ICMP6_PREFIX_OPTION_LEN / 8));
- prefixOptionPacket.putInt(
- ICMP6_RA_OPTION_OFFSET + ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET,
- PREFIX_PREFERRED_LIFETIME);
- prefixOptionPacket.putInt(
- ICMP6_RA_OPTION_OFFSET + ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET,
- PREFIX_VALID_LIFETIME);
- verifyRaLifetime(
- apfFilter, ipClientCallback, prefixOptionPacket, PREFIX_PREFERRED_LIFETIME);
- verifyRaEvent(new RaEvent(
- ROUTER_LIFETIME, PREFIX_VALID_LIFETIME, PREFIX_PREFERRED_LIFETIME, -1, -1, -1));
-
- ByteBuffer rdnssOptionPacket = ByteBuffer.wrap(
- new byte[ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_OPTION_LEN]);
- basePacket.clear();
- rdnssOptionPacket.put(basePacket);
- rdnssOptionPacket.put((byte)ICMP6_RDNSS_OPTION_TYPE);
- rdnssOptionPacket.put((byte)(ICMP6_4_BYTE_OPTION_LEN / 8));
- rdnssOptionPacket.putInt(
- ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_LIFETIME_OFFSET, RDNSS_LIFETIME);
- verifyRaLifetime(apfFilter, ipClientCallback, rdnssOptionPacket, RDNSS_LIFETIME);
- verifyRaEvent(new RaEvent(ROUTER_LIFETIME, -1, -1, -1, RDNSS_LIFETIME, -1));
-
- ByteBuffer routeInfoOptionPacket = ByteBuffer.wrap(
- new byte[ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_OPTION_LEN]);
- basePacket.clear();
- routeInfoOptionPacket.put(basePacket);
- routeInfoOptionPacket.put((byte)ICMP6_ROUTE_INFO_OPTION_TYPE);
- routeInfoOptionPacket.put((byte)(ICMP6_4_BYTE_OPTION_LEN / 8));
- routeInfoOptionPacket.putInt(
- ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_LIFETIME_OFFSET, ROUTE_LIFETIME);
- verifyRaLifetime(apfFilter, ipClientCallback, routeInfoOptionPacket, ROUTE_LIFETIME);
- verifyRaEvent(new RaEvent(ROUTER_LIFETIME, -1, -1, ROUTE_LIFETIME, -1, -1));
-
- ByteBuffer dnsslOptionPacket = ByteBuffer.wrap(
- new byte[ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_OPTION_LEN]);
- basePacket.clear();
- dnsslOptionPacket.put(basePacket);
- dnsslOptionPacket.put((byte)ICMP6_DNSSL_OPTION_TYPE);
- dnsslOptionPacket.put((byte)(ICMP6_4_BYTE_OPTION_LEN / 8));
- dnsslOptionPacket.putInt(
- ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_LIFETIME_OFFSET, DNSSL_LIFETIME);
- verifyRaLifetime(apfFilter, ipClientCallback, dnsslOptionPacket, ROUTER_LIFETIME);
- verifyRaEvent(new RaEvent(ROUTER_LIFETIME, -1, -1, -1, -1, DNSSL_LIFETIME));
-
- // Verify that current program filters all five RAs:
- program = ipClientCallback.getApfProgram();
- verifyRaLifetime(program, basePacket, ROUTER_LIFETIME);
- verifyRaLifetime(program, newFlowLabelPacket, ROUTER_LIFETIME);
- verifyRaLifetime(program, prefixOptionPacket, PREFIX_PREFERRED_LIFETIME);
- verifyRaLifetime(program, rdnssOptionPacket, RDNSS_LIFETIME);
- verifyRaLifetime(program, routeInfoOptionPacket, ROUTE_LIFETIME);
- verifyRaLifetime(program, dnsslOptionPacket, ROUTER_LIFETIME);
-
- apfFilter.shutdown();
- }
-
- /**
- * Stage a file for testing, i.e. make it native accessible. Given a resource ID,
- * copy that resource into the app's data directory and return the path to it.
- */
- private String stageFile(int rawId) throws Exception {
- File file = new File(InstrumentationRegistry.getContext().getFilesDir(), "staged_file");
- new File(file.getParent()).mkdirs();
- InputStream in = null;
- OutputStream out = null;
- try {
- in = InstrumentationRegistry.getContext().getResources().openRawResource(rawId);
- out = new FileOutputStream(file);
- Streams.copy(in, out);
- } finally {
- if (in != null) in.close();
- if (out != null) out.close();
- }
- return file.getAbsolutePath();
- }
-
- private static void put(ByteBuffer buffer, int position, byte[] bytes) {
- final int original = buffer.position();
- buffer.position(position);
- buffer.put(bytes);
- buffer.position(original);
- }
-
- @Test
- public void testRaParsing() throws Exception {
- final int maxRandomPacketSize = 512;
- final Random r = new Random();
- MockIpClientCallback cb = new MockIpClientCallback();
- ApfConfiguration config = getDefaultConfig();
- config.multicastFilter = DROP_MULTICAST;
- config.ieee802_3Filter = DROP_802_3_FRAMES;
- TestApfFilter apfFilter = new TestApfFilter(mContext, config, cb, mLog);
- for (int i = 0; i < 1000; i++) {
- byte[] packet = new byte[r.nextInt(maxRandomPacketSize + 1)];
- r.nextBytes(packet);
- try {
- apfFilter.new Ra(packet, packet.length);
- } catch (ApfFilter.InvalidRaException e) {
- } catch (Exception e) {
- throw new Exception("bad packet: " + HexDump.toHexString(packet), e);
- }
- }
- }
-
- @Test
- public void testRaProcessing() throws Exception {
- final int maxRandomPacketSize = 512;
- final Random r = new Random();
- MockIpClientCallback cb = new MockIpClientCallback();
- ApfConfiguration config = getDefaultConfig();
- config.multicastFilter = DROP_MULTICAST;
- config.ieee802_3Filter = DROP_802_3_FRAMES;
- TestApfFilter apfFilter = new TestApfFilter(mContext, config, cb, mLog);
- for (int i = 0; i < 1000; i++) {
- byte[] packet = new byte[r.nextInt(maxRandomPacketSize + 1)];
- r.nextBytes(packet);
- try {
- apfFilter.processRa(packet, packet.length);
- } catch (Exception e) {
- throw new Exception("bad packet: " + HexDump.toHexString(packet), e);
- }
- }
- }
-
- /**
- * Call the APF interpreter to run {@code program} on {@code packet} with persistent memory
- * segment {@data} pretending the filter was installed {@code filter_age} seconds ago.
- */
- private native static int apfSimulate(byte[] program, byte[] packet, byte[] data,
- int filter_age);
-
- /**
- * Compile a tcpdump human-readable filter (e.g. "icmp" or "tcp port 54") into a BPF
- * prorgam and return a human-readable dump of the BPF program identical to "tcpdump -d".
- */
- private native static String compileToBpf(String filter);
-
- /**
- * Open packet capture file {@code pcap_filename} and filter the packets using tcpdump
- * human-readable filter (e.g. "icmp" or "tcp port 54") compiled to a BPF program and
- * at the same time using APF program {@code apf_program}. Return {@code true} if
- * both APF and BPF programs filter out exactly the same packets.
- */
- private native static boolean compareBpfApf(String filter, String pcap_filename,
- byte[] apf_program);
-
-
- /**
- * Open packet capture file {@code pcapFilename} and run it through APF filter. Then
- * checks whether all the packets are dropped and populates data[] {@code data} with
- * the APF counters.
- */
- private native static boolean dropsAllPackets(byte[] program, byte[] data, String pcapFilename);
-
- @Test
- public void testBroadcastAddress() throws Exception {
- assertEqualsIp("255.255.255.255", ApfFilter.ipv4BroadcastAddress(IPV4_ANY_HOST_ADDR, 0));
- assertEqualsIp("0.0.0.0", ApfFilter.ipv4BroadcastAddress(IPV4_ANY_HOST_ADDR, 32));
- assertEqualsIp("0.0.3.255", ApfFilter.ipv4BroadcastAddress(IPV4_ANY_HOST_ADDR, 22));
- assertEqualsIp("0.255.255.255", ApfFilter.ipv4BroadcastAddress(IPV4_ANY_HOST_ADDR, 8));
-
- assertEqualsIp("255.255.255.255", ApfFilter.ipv4BroadcastAddress(MOCK_IPV4_ADDR, 0));
- assertEqualsIp("10.0.0.1", ApfFilter.ipv4BroadcastAddress(MOCK_IPV4_ADDR, 32));
- assertEqualsIp("10.0.0.255", ApfFilter.ipv4BroadcastAddress(MOCK_IPV4_ADDR, 24));
- assertEqualsIp("10.0.255.255", ApfFilter.ipv4BroadcastAddress(MOCK_IPV4_ADDR, 16));
- }
-
- public void assertEqualsIp(String expected, int got) throws Exception {
- int want = bytesToBEInt(InetAddress.getByName(expected).getAddress());
- assertEquals(want, got);
- }
-}
diff --git a/tests/net/java/android/net/apf/Bpf2Apf.java b/tests/net/java/android/net/apf/Bpf2Apf.java
deleted file mode 100644
index 5d57cde22fb1..000000000000
--- a/tests/net/java/android/net/apf/Bpf2Apf.java
+++ /dev/null
@@ -1,327 +0,0 @@
-/*
- * Copyright (C) 2015 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 android.net.apf;
-
-import android.net.apf.ApfGenerator;
-import android.net.apf.ApfGenerator.IllegalInstructionException;
-import android.net.apf.ApfGenerator.Register;
-
-import java.io.BufferedReader;
-import java.io.InputStreamReader;
-
-/**
- * BPF to APF translator.
- *
- * Note: This is for testing purposes only and is not guaranteed to support
- * translation of all BPF programs.
- *
- * Example usage:
- * javac net/java/android/net/apf/ApfGenerator.java \
- * tests/servicestests/src/android/net/apf/Bpf2Apf.java
- * sudo tcpdump -i em1 -d icmp | java -classpath tests/servicestests/src:net/java \
- * android.net.apf.Bpf2Apf
- */
-public class Bpf2Apf {
- private static int parseImm(String line, String arg) {
- if (!arg.startsWith("#0x")) {
- throw new IllegalArgumentException("Unhandled instruction: " + line);
- }
- final long val_long = Long.parseLong(arg.substring(3), 16);
- if (val_long < 0 || val_long > Long.parseLong("ffffffff", 16)) {
- throw new IllegalArgumentException("Unhandled instruction: " + line);
- }
- return new Long((val_long << 32) >> 32).intValue();
- }
-
- /**
- * Convert a single line of "tcpdump -d" (human readable BPF program dump) {@code line} into
- * APF instruction(s) and append them to {@code gen}. Here's an example line:
- * (001) jeq #0x86dd jt 2 jf 7
- */
- private static void convertLine(String line, ApfGenerator gen)
- throws IllegalInstructionException {
- if (line.indexOf("(") != 0 || line.indexOf(")") != 4 || line.indexOf(" ") != 5) {
- throw new IllegalArgumentException("Unhandled instruction: " + line);
- }
- int label = Integer.parseInt(line.substring(1, 4));
- gen.defineLabel(Integer.toString(label));
- String opcode = line.substring(6, 10).trim();
- String arg = line.substring(15, Math.min(32, line.length())).trim();
- switch (opcode) {
- case "ld":
- case "ldh":
- case "ldb":
- case "ldx":
- case "ldxb":
- case "ldxh":
- Register dest = opcode.contains("x") ? Register.R1 : Register.R0;
- if (arg.equals("4*([14]&0xf)")) {
- if (!opcode.equals("ldxb")) {
- throw new IllegalArgumentException("Unhandled instruction: " + line);
- }
- gen.addLoadFromMemory(dest, gen.IPV4_HEADER_SIZE_MEMORY_SLOT);
- break;
- }
- if (arg.equals("#pktlen")) {
- if (!opcode.equals("ld")) {
- throw new IllegalArgumentException("Unhandled instruction: " + line);
- }
- gen.addLoadFromMemory(dest, gen.PACKET_SIZE_MEMORY_SLOT);
- break;
- }
- if (arg.startsWith("#0x")) {
- if (!opcode.equals("ld")) {
- throw new IllegalArgumentException("Unhandled instruction: " + line);
- }
- gen.addLoadImmediate(dest, parseImm(line, arg));
- break;
- }
- if (arg.startsWith("M[")) {
- if (!opcode.startsWith("ld")) {
- throw new IllegalArgumentException("Unhandled instruction: " + line);
- }
- int memory_slot = Integer.parseInt(arg.substring(2, arg.length() - 1));
- if (memory_slot < 0 || memory_slot >= gen.MEMORY_SLOTS ||
- // Disallow use of pre-filled slots as BPF programs might
- // wrongfully assume they're initialized to 0.
- (memory_slot >= gen.FIRST_PREFILLED_MEMORY_SLOT &&
- memory_slot <= gen.LAST_PREFILLED_MEMORY_SLOT)) {
- throw new IllegalArgumentException("Unhandled instruction: " + line);
- }
- gen.addLoadFromMemory(dest, memory_slot);
- break;
- }
- if (arg.startsWith("[x + ")) {
- int offset = Integer.parseInt(arg.substring(5, arg.length() - 1));
- switch (opcode) {
- case "ld":
- case "ldx":
- gen.addLoad32Indexed(dest, offset);
- break;
- case "ldh":
- case "ldxh":
- gen.addLoad16Indexed(dest, offset);
- break;
- case "ldb":
- case "ldxb":
- gen.addLoad8Indexed(dest, offset);
- break;
- }
- } else {
- int offset = Integer.parseInt(arg.substring(1, arg.length() - 1));
- switch (opcode) {
- case "ld":
- case "ldx":
- gen.addLoad32(dest, offset);
- break;
- case "ldh":
- case "ldxh":
- gen.addLoad16(dest, offset);
- break;
- case "ldb":
- case "ldxb":
- gen.addLoad8(dest, offset);
- break;
- }
- }
- break;
- case "st":
- case "stx":
- Register src = opcode.contains("x") ? Register.R1 : Register.R0;
- if (!arg.startsWith("M[")) {
- throw new IllegalArgumentException("Unhandled instruction: " + line);
- }
- int memory_slot = Integer.parseInt(arg.substring(2, arg.length() - 1));
- if (memory_slot < 0 || memory_slot >= gen.MEMORY_SLOTS ||
- // Disallow overwriting pre-filled slots
- (memory_slot >= gen.FIRST_PREFILLED_MEMORY_SLOT &&
- memory_slot <= gen.LAST_PREFILLED_MEMORY_SLOT)) {
- throw new IllegalArgumentException("Unhandled instruction: " + line);
- }
- gen.addStoreToMemory(src, memory_slot);
- break;
- case "add":
- case "and":
- case "or":
- case "sub":
- if (arg.equals("x")) {
- switch(opcode) {
- case "add":
- gen.addAddR1();
- break;
- case "and":
- gen.addAndR1();
- break;
- case "or":
- gen.addOrR1();
- break;
- case "sub":
- gen.addNeg(Register.R1);
- gen.addAddR1();
- gen.addNeg(Register.R1);
- break;
- }
- } else {
- int imm = parseImm(line, arg);
- switch(opcode) {
- case "add":
- gen.addAdd(imm);
- break;
- case "and":
- gen.addAnd(imm);
- break;
- case "or":
- gen.addOr(imm);
- break;
- case "sub":
- gen.addAdd(-imm);
- break;
- }
- }
- break;
- case "jeq":
- case "jset":
- case "jgt":
- case "jge":
- int val = 0;
- boolean reg_compare;
- if (arg.startsWith("x")) {
- reg_compare = true;
- } else {
- reg_compare = false;
- val = parseImm(line, arg);
- }
- int jt_offset = line.indexOf("jt");
- int jf_offset = line.indexOf("jf");
- String true_label = line.substring(jt_offset + 2, jf_offset).trim();
- String false_label = line.substring(jf_offset + 2).trim();
- boolean true_label_is_fallthrough = Integer.parseInt(true_label) == label + 1;
- boolean false_label_is_fallthrough = Integer.parseInt(false_label) == label + 1;
- if (true_label_is_fallthrough && false_label_is_fallthrough)
- break;
- switch (opcode) {
- case "jeq":
- if (!true_label_is_fallthrough) {
- if (reg_compare) {
- gen.addJumpIfR0EqualsR1(true_label);
- } else {
- gen.addJumpIfR0Equals(val, true_label);
- }
- }
- if (!false_label_is_fallthrough) {
- if (!true_label_is_fallthrough) {
- gen.addJump(false_label);
- } else if (reg_compare) {
- gen.addJumpIfR0NotEqualsR1(false_label);
- } else {
- gen.addJumpIfR0NotEquals(val, false_label);
- }
- }
- break;
- case "jset":
- if (reg_compare) {
- gen.addJumpIfR0AnyBitsSetR1(true_label);
- } else {
- gen.addJumpIfR0AnyBitsSet(val, true_label);
- }
- if (!false_label_is_fallthrough) {
- gen.addJump(false_label);
- }
- break;
- case "jgt":
- if (!true_label_is_fallthrough ||
- // We have no less-than-or-equal-to register to register
- // comparison instruction, so in this case we'll jump
- // around an unconditional jump.
- (!false_label_is_fallthrough && reg_compare)) {
- if (reg_compare) {
- gen.addJumpIfR0GreaterThanR1(true_label);
- } else {
- gen.addJumpIfR0GreaterThan(val, true_label);
- }
- }
- if (!false_label_is_fallthrough) {
- if (!true_label_is_fallthrough || reg_compare) {
- gen.addJump(false_label);
- } else {
- gen.addJumpIfR0LessThan(val + 1, false_label);
- }
- }
- break;
- case "jge":
- if (!false_label_is_fallthrough ||
- // We have no greater-than-or-equal-to register to register
- // comparison instruction, so in this case we'll jump
- // around an unconditional jump.
- (!true_label_is_fallthrough && reg_compare)) {
- if (reg_compare) {
- gen.addJumpIfR0LessThanR1(false_label);
- } else {
- gen.addJumpIfR0LessThan(val, false_label);
- }
- }
- if (!true_label_is_fallthrough) {
- if (!false_label_is_fallthrough || reg_compare) {
- gen.addJump(true_label);
- } else {
- gen.addJumpIfR0GreaterThan(val - 1, true_label);
- }
- }
- break;
- }
- break;
- case "ret":
- if (arg.equals("#0")) {
- gen.addJump(gen.DROP_LABEL);
- } else {
- gen.addJump(gen.PASS_LABEL);
- }
- break;
- case "tax":
- gen.addMove(Register.R1);
- break;
- case "txa":
- gen.addMove(Register.R0);
- break;
- default:
- throw new IllegalArgumentException("Unhandled instruction: " + line);
- }
- }
-
- /**
- * Convert the output of "tcpdump -d" (human readable BPF program dump) {@code bpf} into an APF
- * program and return it.
- */
- public static byte[] convert(String bpf) throws IllegalInstructionException {
- ApfGenerator gen = new ApfGenerator(3);
- for (String line : bpf.split("\\n")) convertLine(line, gen);
- return gen.generate();
- }
-
- /**
- * Convert the output of "tcpdump -d" (human readable BPF program dump) piped in stdin into an
- * APF program and output it via stdout.
- */
- public static void main(String[] args) throws Exception {
- BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
- String line = null;
- StringBuilder responseData = new StringBuilder();
- ApfGenerator gen = new ApfGenerator(3);
- while ((line = in.readLine()) != null) convertLine(line, gen);
- System.out.write(gen.generate());
- }
-}
diff --git a/tests/net/java/android/net/dhcp/DhcpPacketTest.java b/tests/net/java/android/net/dhcp/DhcpPacketTest.java
deleted file mode 100644
index a592809618e6..000000000000
--- a/tests/net/java/android/net/dhcp/DhcpPacketTest.java
+++ /dev/null
@@ -1,1075 +0,0 @@
-/*
- * Copyright (C) 2015 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 android.net.dhcp;
-
-import static android.net.NetworkUtils.getBroadcastAddress;
-import static android.net.NetworkUtils.getPrefixMaskAsInet4Address;
-import static android.net.dhcp.DhcpPacket.*;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import android.annotation.Nullable;
-import android.net.DhcpResults;
-import android.net.LinkAddress;
-import android.net.NetworkUtils;
-import android.net.metrics.DhcpErrorEvent;
-import android.support.test.runner.AndroidJUnit4;
-import android.support.test.filters.SmallTest;
-
-import com.android.internal.util.HexDump;
-
-import java.io.ByteArrayOutputStream;
-import java.net.Inet4Address;
-import java.nio.ByteBuffer;
-import java.nio.charset.Charset;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Random;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class DhcpPacketTest {
-
- private static final Inet4Address SERVER_ADDR = v4Address("192.0.2.1");
- private static final Inet4Address CLIENT_ADDR = v4Address("192.0.2.234");
- private static final int PREFIX_LENGTH = 22;
- private static final Inet4Address NETMASK = getPrefixMaskAsInet4Address(PREFIX_LENGTH);
- private static final Inet4Address BROADCAST_ADDR = getBroadcastAddress(
- SERVER_ADDR, PREFIX_LENGTH);
- private static final String HOSTNAME = "testhostname";
- private static final short MTU = 1500;
- // Use our own empty address instead of Inet4Address.ANY or INADDR_ANY to ensure that the code
- // doesn't use == instead of equals when comparing addresses.
- private static final Inet4Address ANY = (Inet4Address) v4Address("0.0.0.0");
-
- private static final byte[] CLIENT_MAC = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 };
-
- private static final Inet4Address v4Address(String addrString) throws IllegalArgumentException {
- return (Inet4Address) NetworkUtils.numericToInetAddress(addrString);
- }
-
- @Before
- public void setUp() {
- DhcpPacket.testOverrideVendorId = "android-dhcp-???";
- DhcpPacket.testOverrideHostname = "android-01234567890abcde";
- }
-
- class TestDhcpPacket extends DhcpPacket {
- private byte mType;
- // TODO: Make this a map of option numbers to bytes instead.
- private byte[] mDomainBytes, mVendorInfoBytes, mLeaseTimeBytes, mNetmaskBytes;
-
- public TestDhcpPacket(byte type, Inet4Address clientIp, Inet4Address yourIp) {
- super(0xdeadbeef, (short) 0, clientIp, yourIp, INADDR_ANY, INADDR_ANY,
- CLIENT_MAC, true);
- mType = type;
- }
-
- public TestDhcpPacket(byte type) {
- this(type, INADDR_ANY, CLIENT_ADDR);
- }
-
- public TestDhcpPacket setDomainBytes(byte[] domainBytes) {
- mDomainBytes = domainBytes;
- return this;
- }
-
- public TestDhcpPacket setVendorInfoBytes(byte[] vendorInfoBytes) {
- mVendorInfoBytes = vendorInfoBytes;
- return this;
- }
-
- public TestDhcpPacket setLeaseTimeBytes(byte[] leaseTimeBytes) {
- mLeaseTimeBytes = leaseTimeBytes;
- return this;
- }
-
- public TestDhcpPacket setNetmaskBytes(byte[] netmaskBytes) {
- mNetmaskBytes = netmaskBytes;
- return this;
- }
-
- public ByteBuffer buildPacket(int encap, short unusedDestUdp, short unusedSrcUdp) {
- ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
- fillInPacket(encap, CLIENT_ADDR, SERVER_ADDR,
- DHCP_CLIENT, DHCP_SERVER, result, DHCP_BOOTREPLY, false);
- return result;
- }
-
- public void finishPacket(ByteBuffer buffer) {
- addTlv(buffer, DHCP_MESSAGE_TYPE, mType);
- if (mDomainBytes != null) {
- addTlv(buffer, DHCP_DOMAIN_NAME, mDomainBytes);
- }
- if (mVendorInfoBytes != null) {
- addTlv(buffer, DHCP_VENDOR_INFO, mVendorInfoBytes);
- }
- if (mLeaseTimeBytes != null) {
- addTlv(buffer, DHCP_LEASE_TIME, mLeaseTimeBytes);
- }
- if (mNetmaskBytes != null) {
- addTlv(buffer, DHCP_SUBNET_MASK, mNetmaskBytes);
- }
- addTlvEnd(buffer);
- }
-
- // Convenience method.
- public ByteBuffer build() {
- // ENCAP_BOOTP packets don't contain ports, so just pass in 0.
- ByteBuffer pkt = buildPacket(ENCAP_BOOTP, (short) 0, (short) 0);
- pkt.flip();
- return pkt;
- }
- }
-
- private void assertDomainAndVendorInfoParses(
- String expectedDomain, byte[] domainBytes,
- String expectedVendorInfo, byte[] vendorInfoBytes) throws Exception {
- ByteBuffer packet = new TestDhcpPacket(DHCP_MESSAGE_TYPE_OFFER)
- .setDomainBytes(domainBytes)
- .setVendorInfoBytes(vendorInfoBytes)
- .build();
- DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP);
- assertEquals(expectedDomain, offerPacket.mDomainName);
- assertEquals(expectedVendorInfo, offerPacket.mVendorInfo);
- }
-
- @Test
- public void testDomainName() throws Exception {
- byte[] nullByte = new byte[] { 0x00 };
- byte[] twoNullBytes = new byte[] { 0x00, 0x00 };
- byte[] nonNullDomain = new byte[] {
- (byte) 'g', (byte) 'o', (byte) 'o', (byte) '.', (byte) 'g', (byte) 'l'
- };
- byte[] trailingNullDomain = new byte[] {
- (byte) 'g', (byte) 'o', (byte) 'o', (byte) '.', (byte) 'g', (byte) 'l', 0x00
- };
- byte[] embeddedNullsDomain = new byte[] {
- (byte) 'g', (byte) 'o', (byte) 'o', 0x00, 0x00, (byte) 'g', (byte) 'l'
- };
- byte[] metered = "ANDROID_METERED".getBytes("US-ASCII");
-
- byte[] meteredEmbeddedNull = metered.clone();
- meteredEmbeddedNull[7] = (char) 0;
-
- byte[] meteredTrailingNull = metered.clone();
- meteredTrailingNull[meteredTrailingNull.length - 1] = (char) 0;
-
- assertDomainAndVendorInfoParses("", nullByte, "\u0000", nullByte);
- assertDomainAndVendorInfoParses("", twoNullBytes, "\u0000\u0000", twoNullBytes);
- assertDomainAndVendorInfoParses("goo.gl", nonNullDomain, "ANDROID_METERED", metered);
- assertDomainAndVendorInfoParses("goo", embeddedNullsDomain,
- "ANDROID\u0000METERED", meteredEmbeddedNull);
- assertDomainAndVendorInfoParses("goo.gl", trailingNullDomain,
- "ANDROID_METERE\u0000", meteredTrailingNull);
- }
-
- private void assertLeaseTimeParses(boolean expectValid, Integer rawLeaseTime,
- long leaseTimeMillis, byte[] leaseTimeBytes) throws Exception {
- TestDhcpPacket testPacket = new TestDhcpPacket(DHCP_MESSAGE_TYPE_OFFER);
- if (leaseTimeBytes != null) {
- testPacket.setLeaseTimeBytes(leaseTimeBytes);
- }
- ByteBuffer packet = testPacket.build();
- DhcpPacket offerPacket = null;
-
- if (!expectValid) {
- try {
- offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP);
- fail("Invalid packet parsed successfully: " + offerPacket);
- } catch (ParseException expected) {
- }
- return;
- }
-
- offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP);
- assertNotNull(offerPacket);
- assertEquals(rawLeaseTime, offerPacket.mLeaseTime);
- DhcpResults dhcpResults = offerPacket.toDhcpResults(); // Just check this doesn't crash.
- assertEquals(leaseTimeMillis, offerPacket.getLeaseTimeMillis());
- }
-
- @Test
- public void testLeaseTime() throws Exception {
- byte[] noLease = null;
- byte[] tooShortLease = new byte[] { 0x00, 0x00 };
- byte[] tooLongLease = new byte[] { 0x00, 0x00, 0x00, 60, 0x01 };
- byte[] zeroLease = new byte[] { 0x00, 0x00, 0x00, 0x00 };
- byte[] tenSecondLease = new byte[] { 0x00, 0x00, 0x00, 10 };
- byte[] oneMinuteLease = new byte[] { 0x00, 0x00, 0x00, 60 };
- byte[] fiveMinuteLease = new byte[] { 0x00, 0x00, 0x01, 0x2c };
- byte[] oneDayLease = new byte[] { 0x00, 0x01, 0x51, (byte) 0x80 };
- byte[] maxIntPlusOneLease = new byte[] { (byte) 0x80, 0x00, 0x00, 0x01 };
- byte[] infiniteLease = new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff };
-
- assertLeaseTimeParses(true, null, 0, noLease);
- assertLeaseTimeParses(false, null, 0, tooShortLease);
- assertLeaseTimeParses(false, null, 0, tooLongLease);
- assertLeaseTimeParses(true, 0, 60 * 1000, zeroLease);
- assertLeaseTimeParses(true, 10, 60 * 1000, tenSecondLease);
- assertLeaseTimeParses(true, 60, 60 * 1000, oneMinuteLease);
- assertLeaseTimeParses(true, 300, 300 * 1000, fiveMinuteLease);
- assertLeaseTimeParses(true, 86400, 86400 * 1000, oneDayLease);
- assertLeaseTimeParses(true, -2147483647, 2147483649L * 1000, maxIntPlusOneLease);
- assertLeaseTimeParses(true, DhcpPacket.INFINITE_LEASE, 0, infiniteLease);
- }
-
- private void checkIpAddress(String expected, Inet4Address clientIp, Inet4Address yourIp,
- byte[] netmaskBytes) throws Exception {
- checkIpAddress(expected, DHCP_MESSAGE_TYPE_OFFER, clientIp, yourIp, netmaskBytes);
- checkIpAddress(expected, DHCP_MESSAGE_TYPE_ACK, clientIp, yourIp, netmaskBytes);
- }
-
- private void checkIpAddress(String expected, byte type,
- Inet4Address clientIp, Inet4Address yourIp,
- byte[] netmaskBytes) throws Exception {
- ByteBuffer packet = new TestDhcpPacket(type, clientIp, yourIp)
- .setNetmaskBytes(netmaskBytes)
- .build();
- DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP);
- DhcpResults results = offerPacket.toDhcpResults();
-
- if (expected != null) {
- LinkAddress expectedAddress = new LinkAddress(expected);
- assertEquals(expectedAddress, results.ipAddress);
- } else {
- assertNull(results);
- }
- }
-
- @Test
- public void testIpAddress() throws Exception {
- byte[] slash11Netmask = new byte[] { (byte) 0xff, (byte) 0xe0, 0x00, 0x00 };
- byte[] slash24Netmask = new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, 0x00 };
- byte[] invalidNetmask = new byte[] { (byte) 0xff, (byte) 0xfb, (byte) 0xff, 0x00 };
- Inet4Address example1 = v4Address("192.0.2.1");
- Inet4Address example2 = v4Address("192.0.2.43");
-
- // A packet without any addresses is not valid.
- checkIpAddress(null, ANY, ANY, slash24Netmask);
-
- // ClientIP is used iff YourIP is not present.
- checkIpAddress("192.0.2.1/24", example2, example1, slash24Netmask);
- checkIpAddress("192.0.2.43/11", example2, ANY, slash11Netmask);
- checkIpAddress("192.0.2.43/11", ANY, example2, slash11Netmask);
-
- // Invalid netmasks are ignored.
- checkIpAddress(null, example2, ANY, invalidNetmask);
-
- // If there is no netmask, implicit netmasks are used.
- checkIpAddress("192.0.2.43/24", ANY, example2, null);
- }
-
- private void assertDhcpResults(String ipAddress, String gateway, String dnsServersString,
- String domains, String serverAddress, String vendorInfo, int leaseDuration,
- boolean hasMeteredHint, int mtu, DhcpResults dhcpResults) throws Exception {
- assertEquals(new LinkAddress(ipAddress), dhcpResults.ipAddress);
- assertEquals(v4Address(gateway), dhcpResults.gateway);
-
- String[] dnsServerStrings = dnsServersString.split(",");
- ArrayList dnsServers = new ArrayList();
- for (String dnsServerString : dnsServerStrings) {
- dnsServers.add(v4Address(dnsServerString));
- }
- assertEquals(dnsServers, dhcpResults.dnsServers);
-
- assertEquals(domains, dhcpResults.domains);
- assertEquals(v4Address(serverAddress), dhcpResults.serverAddress);
- assertEquals(vendorInfo, dhcpResults.vendorInfo);
- assertEquals(leaseDuration, dhcpResults.leaseDuration);
- assertEquals(hasMeteredHint, dhcpResults.hasMeteredHint());
- assertEquals(mtu, dhcpResults.mtu);
- }
-
- @Test
- public void testOffer1() throws Exception {
- // TODO: Turn all of these into golden files. This will probably require using
- // android.support.test.InstrumentationRegistry for obtaining a Context object
- // to read such golden files, along with an appropriate Android.mk.
- final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
- // IP header.
- "451001480000000080118849c0a89003c0a89ff7" +
- // UDP header.
- "004300440134dcfa" +
- // BOOTP header.
- "02010600c997a63b0000000000000000c0a89ff70000000000000000" +
- // MAC address.
- "30766ff2a90c00000000000000000000" +
- // Server name.
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // File.
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // Options
- "638253633501023604c0a89003330400001c200104fffff0000304c0a89ffe06080808080808080404" +
- "3a0400000e103b040000189cff00000000000000000000"));
-
- DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
- assertTrue(offerPacket instanceof DhcpOfferPacket); // Implicitly checks it's non-null.
- DhcpResults dhcpResults = offerPacket.toDhcpResults();
- assertDhcpResults("192.168.159.247/20", "192.168.159.254", "8.8.8.8,8.8.4.4",
- null, "192.168.144.3", null, 7200, false, 0, dhcpResults);
- }
-
- @Test
- public void testOffer2() throws Exception {
- final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
- // IP header.
- "450001518d0600004011144dc0a82b01c0a82bf7" +
- // UDP header.
- "00430044013d9ac7" +
- // BOOTP header.
- "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000" +
- // MAC address.
- "30766ff2a90c00000000000000000000" +
- // Server name.
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // File.
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // Options
- "638253633501023604c0a82b01330400000e103a04000007083b0400000c4e0104ffffff00" +
- "1c04c0a82bff0304c0a82b010604c0a82b012b0f414e44524f49445f4d455445524544ff"));
-
- assertEquals(337, packet.limit());
- DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
- assertTrue(offerPacket instanceof DhcpOfferPacket); // Implicitly checks it's non-null.
- DhcpResults dhcpResults = offerPacket.toDhcpResults();
- assertDhcpResults("192.168.43.247/24", "192.168.43.1", "192.168.43.1",
- null, "192.168.43.1", "ANDROID_METERED", 3600, true, 0, dhcpResults);
- assertTrue(dhcpResults.hasMeteredHint());
- }
-
- @Test
- public void testBadIpPacket() throws Exception {
- final byte[] packet = HexDump.hexStringToByteArray(
- // IP header.
- "450001518d0600004011144dc0a82b01c0a82bf7");
-
- try {
- DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3);
- } catch (DhcpPacket.ParseException expected) {
- assertDhcpErrorCodes(DhcpErrorEvent.L3_TOO_SHORT, expected.errorCode);
- return;
- }
- fail("Dhcp packet parsing should have failed");
- }
-
- @Test
- public void testBadDhcpPacket() throws Exception {
- final byte[] packet = HexDump.hexStringToByteArray(
- // IP header.
- "450001518d0600004011144dc0a82b01c0a82bf7" +
- // UDP header.
- "00430044013d9ac7" +
- // BOOTP header.
- "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000");
-
- try {
- DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3);
- } catch (DhcpPacket.ParseException expected) {
- assertDhcpErrorCodes(DhcpErrorEvent.L3_TOO_SHORT, expected.errorCode);
- return;
- }
- fail("Dhcp packet parsing should have failed");
- }
-
- @Test
- public void testBadTruncatedOffer() throws Exception {
- final byte[] packet = HexDump.hexStringToByteArray(
- // IP header.
- "450001518d0600004011144dc0a82b01c0a82bf7" +
- // UDP header.
- "00430044013d9ac7" +
- // BOOTP header.
- "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000" +
- // MAC address.
- "30766ff2a90c00000000000000000000" +
- // Server name.
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // File, missing one byte
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "00000000000000000000000000000000000000000000000000000000000000");
-
- try {
- DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3);
- } catch (DhcpPacket.ParseException expected) {
- assertDhcpErrorCodes(DhcpErrorEvent.L3_TOO_SHORT, expected.errorCode);
- return;
- }
- fail("Dhcp packet parsing should have failed");
- }
-
- @Test
- public void testBadOfferWithoutACookie() throws Exception {
- final byte[] packet = HexDump.hexStringToByteArray(
- // IP header.
- "450001518d0600004011144dc0a82b01c0a82bf7" +
- // UDP header.
- "00430044013d9ac7" +
- // BOOTP header.
- "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000" +
- // MAC address.
- "30766ff2a90c00000000000000000000" +
- // Server name.
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // File.
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000"
- // No options
- );
-
- try {
- DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3);
- } catch (DhcpPacket.ParseException expected) {
- assertDhcpErrorCodes(DhcpErrorEvent.DHCP_NO_COOKIE, expected.errorCode);
- return;
- }
- fail("Dhcp packet parsing should have failed");
- }
-
- @Test
- public void testOfferWithBadCookie() throws Exception {
- final byte[] packet = HexDump.hexStringToByteArray(
- // IP header.
- "450001518d0600004011144dc0a82b01c0a82bf7" +
- // UDP header.
- "00430044013d9ac7" +
- // BOOTP header.
- "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000" +
- // MAC address.
- "30766ff2a90c00000000000000000000" +
- // Server name.
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // File.
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // Bad cookie
- "DEADBEEF3501023604c0a82b01330400000e103a04000007083b0400000c4e0104ffffff00" +
- "1c04c0a82bff0304c0a82b010604c0a82b012b0f414e44524f49445f4d455445524544ff");
-
- try {
- DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3);
- } catch (DhcpPacket.ParseException expected) {
- assertDhcpErrorCodes(DhcpErrorEvent.DHCP_BAD_MAGIC_COOKIE, expected.errorCode);
- return;
- }
- fail("Dhcp packet parsing should have failed");
- }
-
- private void assertDhcpErrorCodes(int expected, int got) {
- assertEquals(Integer.toHexString(expected), Integer.toHexString(got));
- }
-
- @Test
- public void testTruncatedOfferPackets() throws Exception {
- final byte[] packet = HexDump.hexStringToByteArray(
- // IP header.
- "450001518d0600004011144dc0a82b01c0a82bf7" +
- // UDP header.
- "00430044013d9ac7" +
- // BOOTP header.
- "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000" +
- // MAC address.
- "30766ff2a90c00000000000000000000" +
- // Server name.
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // File.
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // Options
- "638253633501023604c0a82b01330400000e103a04000007083b0400000c4e0104ffffff00" +
- "1c04c0a82bff0304c0a82b010604c0a82b012b0f414e44524f49445f4d455445524544ff");
-
- for (int len = 0; len < packet.length; len++) {
- try {
- DhcpPacket.decodeFullPacket(packet, len, ENCAP_L3);
- } catch (ParseException e) {
- if (e.errorCode == DhcpErrorEvent.PARSING_ERROR) {
- fail(String.format("bad truncated packet of length %d", len));
- }
- }
- }
- }
-
- @Test
- public void testRandomPackets() throws Exception {
- final int maxRandomPacketSize = 512;
- final Random r = new Random();
- for (int i = 0; i < 10000; i++) {
- byte[] packet = new byte[r.nextInt(maxRandomPacketSize + 1)];
- r.nextBytes(packet);
- try {
- DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3);
- } catch (ParseException e) {
- if (e.errorCode == DhcpErrorEvent.PARSING_ERROR) {
- fail("bad packet: " + HexDump.toHexString(packet));
- }
- }
- }
- }
-
- private byte[] mtuBytes(int mtu) {
- // 0x1a02: option 26, length 2. 0xff: no more options.
- if (mtu > Short.MAX_VALUE - Short.MIN_VALUE) {
- throw new IllegalArgumentException(
- String.format("Invalid MTU %d, must be 16-bit unsigned", mtu));
- }
- String hexString = String.format("1a02%04xff", mtu);
- return HexDump.hexStringToByteArray(hexString);
- }
-
- private void checkMtu(ByteBuffer packet, int expectedMtu, byte[] mtuBytes) throws Exception {
- if (mtuBytes != null) {
- packet.position(packet.capacity() - mtuBytes.length);
- packet.put(mtuBytes);
- packet.clear();
- }
- DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
- assertTrue(offerPacket instanceof DhcpOfferPacket); // Implicitly checks it's non-null.
- DhcpResults dhcpResults = offerPacket.toDhcpResults();
- assertDhcpResults("192.168.159.247/20", "192.168.159.254", "8.8.8.8,8.8.4.4",
- null, "192.168.144.3", null, 7200, false, expectedMtu, dhcpResults);
- }
-
- @Test
- public void testMtu() throws Exception {
- final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
- // IP header.
- "451001480000000080118849c0a89003c0a89ff7" +
- // UDP header.
- "004300440134dcfa" +
- // BOOTP header.
- "02010600c997a63b0000000000000000c0a89ff70000000000000000" +
- // MAC address.
- "30766ff2a90c00000000000000000000" +
- // Server name.
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // File.
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // Options
- "638253633501023604c0a89003330400001c200104fffff0000304c0a89ffe06080808080808080404" +
- "3a0400000e103b040000189cff00000000"));
-
- checkMtu(packet, 0, null);
- checkMtu(packet, 0, mtuBytes(1501));
- checkMtu(packet, 1500, mtuBytes(1500));
- checkMtu(packet, 1499, mtuBytes(1499));
- checkMtu(packet, 1280, mtuBytes(1280));
- checkMtu(packet, 0, mtuBytes(1279));
- checkMtu(packet, 0, mtuBytes(576));
- checkMtu(packet, 0, mtuBytes(68));
- checkMtu(packet, 0, mtuBytes(Short.MIN_VALUE));
- checkMtu(packet, 0, mtuBytes(Short.MAX_VALUE + 3));
- checkMtu(packet, 0, mtuBytes(-1));
- }
-
- @Test
- public void testBadHwaddrLength() throws Exception {
- final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
- // IP header.
- "450001518d0600004011144dc0a82b01c0a82bf7" +
- // UDP header.
- "00430044013d9ac7" +
- // BOOTP header.
- "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000" +
- // MAC address.
- "30766ff2a90c00000000000000000000" +
- // Server name.
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // File.
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // Options
- "638253633501023604c0a82b01330400000e103a04000007083b0400000c4e0104ffffff00" +
- "1c04c0a82bff0304c0a82b010604c0a82b012b0f414e44524f49445f4d455445524544ff"));
- String expectedClientMac = "30766FF2A90C";
-
- final int hwAddrLenOffset = 20 + 8 + 2;
- assertEquals(6, packet.get(hwAddrLenOffset));
-
- // Expect the expected.
- DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
- assertNotNull(offerPacket);
- assertEquals(6, offerPacket.getClientMac().length);
- assertEquals(expectedClientMac, HexDump.toHexString(offerPacket.getClientMac()));
-
- // Reduce the hardware address length and verify that it shortens the client MAC.
- packet.flip();
- packet.put(hwAddrLenOffset, (byte) 5);
- offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
- assertNotNull(offerPacket);
- assertEquals(5, offerPacket.getClientMac().length);
- assertEquals(expectedClientMac.substring(0, 10),
- HexDump.toHexString(offerPacket.getClientMac()));
-
- packet.flip();
- packet.put(hwAddrLenOffset, (byte) 3);
- offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
- assertNotNull(offerPacket);
- assertEquals(3, offerPacket.getClientMac().length);
- assertEquals(expectedClientMac.substring(0, 6),
- HexDump.toHexString(offerPacket.getClientMac()));
-
- // Set the the hardware address length to 0xff and verify that we a) don't treat it as -1
- // and crash, and b) hardcode it to 6.
- packet.flip();
- packet.put(hwAddrLenOffset, (byte) -1);
- offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
- assertNotNull(offerPacket);
- assertEquals(6, offerPacket.getClientMac().length);
- assertEquals(expectedClientMac, HexDump.toHexString(offerPacket.getClientMac()));
-
- // Set the the hardware address length to a positive invalid value (> 16) and verify that we
- // hardcode it to 6.
- packet.flip();
- packet.put(hwAddrLenOffset, (byte) 17);
- offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
- assertNotNull(offerPacket);
- assertEquals(6, offerPacket.getClientMac().length);
- assertEquals(expectedClientMac, HexDump.toHexString(offerPacket.getClientMac()));
- }
-
- @Test
- public void testPadAndOverloadedOptionsOffer() throws Exception {
- // A packet observed in the real world that is interesting for two reasons:
- //
- // 1. It uses pad bytes, which we previously didn't support correctly.
- // 2. It uses DHCP option overloading, which we don't currently support (but it doesn't
- // store any information in the overloaded fields).
- //
- // For now, we just check that it parses correctly.
- final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
- // Ethernet header.
- "b4cef6000000e80462236e300800" +
- // IP header.
- "4500014c00000000ff11741701010101ac119876" +
- // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation).
- "004300440138ae5a" +
- // BOOTP header.
- "020106000fa0059f0000000000000000ac1198760000000000000000" +
- // MAC address.
- "b4cef600000000000000000000000000" +
- // Server name.
- "ff00000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // File.
- "ff00000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // Options
- "638253633501023604010101010104ffff000033040000a8c03401030304ac1101010604ac110101" +
- "0000000000000000000000000000000000000000000000ff000000"));
-
- DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
- assertTrue(offerPacket instanceof DhcpOfferPacket);
- DhcpResults dhcpResults = offerPacket.toDhcpResults();
- assertDhcpResults("172.17.152.118/16", "172.17.1.1", "172.17.1.1",
- null, "1.1.1.1", null, 43200, false, 0, dhcpResults);
- }
-
- @Test
- public void testBug2111() throws Exception {
- final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
- // IP header.
- "4500014c00000000ff119beac3eaf3880a3f5d04" +
- // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation).
- "0043004401387464" +
- // BOOTP header.
- "0201060002554812000a0000000000000a3f5d040000000000000000" +
- // MAC address.
- "00904c00000000000000000000000000" +
- // Server name.
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // File.
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // Options.
- "638253633501023604c00002fe33040000bfc60104fffff00003040a3f50010608c0000201c0000202" +
- "0f0f646f6d61696e3132332e636f2e756b0000000000ff00000000"));
-
- DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
- assertTrue(offerPacket instanceof DhcpOfferPacket);
- DhcpResults dhcpResults = offerPacket.toDhcpResults();
- assertDhcpResults("10.63.93.4/20", "10.63.80.1", "192.0.2.1,192.0.2.2",
- "domain123.co.uk", "192.0.2.254", null, 49094, false, 0, dhcpResults);
- }
-
- @Test
- public void testBug2136() throws Exception {
- final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
- // Ethernet header.
- "bcf5ac000000d0c7890000000800" +
- // IP header.
- "4500014c00000000ff119beac3eaf3880a3f5d04" +
- // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation).
- "0043004401387574" +
- // BOOTP header.
- "0201060163339a3000050000000000000a209ecd0000000000000000" +
- // MAC address.
- "bcf5ac00000000000000000000000000" +
- // Server name.
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // File.
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // Options.
- "6382536335010236040a20ff80330400001c200104fffff00003040a20900106089458413494584135" +
- "0f0b6c616e63732e61632e756b000000000000000000ff00000000"));
-
- DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
- assertTrue(offerPacket instanceof DhcpOfferPacket);
- assertEquals("BCF5AC000000", HexDump.toHexString(offerPacket.getClientMac()));
- DhcpResults dhcpResults = offerPacket.toDhcpResults();
- assertDhcpResults("10.32.158.205/20", "10.32.144.1", "148.88.65.52,148.88.65.53",
- "lancs.ac.uk", "10.32.255.128", null, 7200, false, 0, dhcpResults);
- }
-
- @Test
- public void testUdpServerAnySourcePort() throws Exception {
- final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
- // Ethernet header.
- "9cd917000000001c2e0000000800" +
- // IP header.
- "45a00148000040003d115087d18194fb0a0f7af2" +
- // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation).
- // NOTE: The server source port is not the canonical port 67.
- "C29F004401341268" +
- // BOOTP header.
- "02010600d628ba8200000000000000000a0f7af2000000000a0fc818" +
- // MAC address.
- "9cd91700000000000000000000000000" +
- // Server name.
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // File.
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // Options.
- "6382536335010236040a0169fc3304000151800104ffff000003040a0fc817060cd1818003d1819403" +
- "d18180060f0777766d2e6564751c040a0fffffff000000"));
-
- DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
- assertTrue(offerPacket instanceof DhcpOfferPacket);
- assertEquals("9CD917000000", HexDump.toHexString(offerPacket.getClientMac()));
- DhcpResults dhcpResults = offerPacket.toDhcpResults();
- assertDhcpResults("10.15.122.242/16", "10.15.200.23",
- "209.129.128.3,209.129.148.3,209.129.128.6",
- "wvm.edu", "10.1.105.252", null, 86400, false, 0, dhcpResults);
- }
-
- @Test
- public void testUdpInvalidDstPort() throws Exception {
- final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
- // Ethernet header.
- "9cd917000000001c2e0000000800" +
- // IP header.
- "45a00148000040003d115087d18194fb0a0f7af2" +
- // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation).
- // NOTE: The destination port is a non-DHCP port.
- "0043aaaa01341268" +
- // BOOTP header.
- "02010600d628ba8200000000000000000a0f7af2000000000a0fc818" +
- // MAC address.
- "9cd91700000000000000000000000000" +
- // Server name.
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // File.
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // Options.
- "6382536335010236040a0169fc3304000151800104ffff000003040a0fc817060cd1818003d1819403" +
- "d18180060f0777766d2e6564751c040a0fffffff000000"));
-
- try {
- DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
- fail("Packet with invalid dst port did not throw ParseException");
- } catch (ParseException expected) {}
- }
-
- @Test
- public void testMultipleRouters() throws Exception {
- final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
- // Ethernet header.
- "fc3d93000000" + "081735000000" + "0800" +
- // IP header.
- "45000148c2370000ff117ac2c0a8bd02ffffffff" +
- // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation).
- "0043004401343beb" +
- // BOOTP header.
- "0201060027f518e20000800000000000c0a8bd310000000000000000" +
- // MAC address.
- "fc3d9300000000000000000000000000" +
- // Server name.
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // File.
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // Options.
- "638253633501023604c0abbd023304000070803a04000038403b04000062700104ffffff00" +
- "0308c0a8bd01ffffff0006080808080808080404ff000000000000"));
-
- DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
- assertTrue(offerPacket instanceof DhcpOfferPacket);
- assertEquals("FC3D93000000", HexDump.toHexString(offerPacket.getClientMac()));
- DhcpResults dhcpResults = offerPacket.toDhcpResults();
- assertDhcpResults("192.168.189.49/24", "192.168.189.1", "8.8.8.8,8.8.4.4",
- null, "192.171.189.2", null, 28800, false, 0, dhcpResults);
- }
-
- @Test
- public void testDiscoverPacket() throws Exception {
- short secs = 7;
- int transactionId = 0xdeadbeef;
- byte[] hwaddr = {
- (byte) 0xda, (byte) 0x01, (byte) 0x19, (byte) 0x5b, (byte) 0xb1, (byte) 0x7a
- };
-
- ByteBuffer packet = DhcpPacket.buildDiscoverPacket(
- DhcpPacket.ENCAP_L2, transactionId, secs, hwaddr,
- false /* do unicast */, DhcpClient.REQUESTED_PARAMS);
-
- byte[] headers = new byte[] {
- // Ethernet header.
- (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
- (byte) 0xda, (byte) 0x01, (byte) 0x19, (byte) 0x5b, (byte) 0xb1, (byte) 0x7a,
- (byte) 0x08, (byte) 0x00,
- // IP header.
- (byte) 0x45, (byte) 0x10, (byte) 0x01, (byte) 0x56,
- (byte) 0x00, (byte) 0x00, (byte) 0x40, (byte) 0x00,
- (byte) 0x40, (byte) 0x11, (byte) 0x39, (byte) 0x88,
- (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
- (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
- // UDP header.
- (byte) 0x00, (byte) 0x44, (byte) 0x00, (byte) 0x43,
- (byte) 0x01, (byte) 0x42, (byte) 0x6a, (byte) 0x4a,
- // BOOTP.
- (byte) 0x01, (byte) 0x01, (byte) 0x06, (byte) 0x00,
- (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef,
- (byte) 0x00, (byte) 0x07, (byte) 0x00, (byte) 0x00,
- (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
- (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
- (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
- (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
- (byte) 0xda, (byte) 0x01, (byte) 0x19, (byte) 0x5b,
- (byte) 0xb1, (byte) 0x7a
- };
- byte[] options = new byte[] {
- // Magic cookie 0x63825363.
- (byte) 0x63, (byte) 0x82, (byte) 0x53, (byte) 0x63,
- // Message type DISCOVER.
- (byte) 0x35, (byte) 0x01, (byte) 0x01,
- // Client identifier Ethernet, da:01:19:5b:b1:7a.
- (byte) 0x3d, (byte) 0x07,
- (byte) 0x01,
- (byte) 0xda, (byte) 0x01, (byte) 0x19, (byte) 0x5b, (byte) 0xb1, (byte) 0x7a,
- // Max message size 1500.
- (byte) 0x39, (byte) 0x02, (byte) 0x05, (byte) 0xdc,
- // Version "android-dhcp-???".
- (byte) 0x3c, (byte) 0x10,
- 'a', 'n', 'd', 'r', 'o', 'i', 'd', '-', 'd', 'h', 'c', 'p', '-', '?', '?', '?',
- // Hostname "android-01234567890abcde"
- (byte) 0x0c, (byte) 0x18,
- 'a', 'n', 'd', 'r', 'o', 'i', 'd', '-',
- '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'a', 'b', 'c', 'd', 'e',
- // Requested parameter list.
- (byte) 0x37, (byte) 0x0a,
- DHCP_SUBNET_MASK,
- DHCP_ROUTER,
- DHCP_DNS_SERVER,
- DHCP_DOMAIN_NAME,
- DHCP_MTU,
- DHCP_BROADCAST_ADDRESS,
- DHCP_LEASE_TIME,
- DHCP_RENEWAL_TIME,
- DHCP_REBINDING_TIME,
- DHCP_VENDOR_INFO,
- // End options.
- (byte) 0xff,
- // Our packets are always of even length. TODO: find out why and possibly fix it.
- (byte) 0x00
- };
- byte[] expected = new byte[DhcpPacket.MIN_PACKET_LENGTH_L2 + options.length];
- assertTrue((expected.length & 1) == 0);
- System.arraycopy(headers, 0, expected, 0, headers.length);
- System.arraycopy(options, 0, expected, DhcpPacket.MIN_PACKET_LENGTH_L2, options.length);
-
- byte[] actual = new byte[packet.limit()];
- packet.get(actual);
- String msg =
- "Expected:\n " + Arrays.toString(expected) +
- "\nActual:\n " + Arrays.toString(actual);
- assertTrue(msg, Arrays.equals(expected, actual));
- }
-
- public void checkBuildOfferPacket(int leaseTimeSecs, @Nullable String hostname)
- throws Exception {
- final int renewalTime = (int) (Integer.toUnsignedLong(leaseTimeSecs) / 2);
- final int rebindingTime = (int) (Integer.toUnsignedLong(leaseTimeSecs) * 875 / 1000);
- final int transactionId = 0xdeadbeef;
-
- final ByteBuffer packet = DhcpPacket.buildOfferPacket(
- DhcpPacket.ENCAP_BOOTP, transactionId, false /* broadcast */,
- SERVER_ADDR, INADDR_ANY /* relayIp */, CLIENT_ADDR /* yourIp */,
- CLIENT_MAC, leaseTimeSecs, NETMASK /* netMask */,
- BROADCAST_ADDR /* bcAddr */, Collections.singletonList(SERVER_ADDR) /* gateways */,
- Collections.singletonList(SERVER_ADDR) /* dnsServers */,
- SERVER_ADDR /* dhcpServerIdentifier */, null /* domainName */, hostname,
- false /* metered */, MTU);
-
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- // BOOTP headers
- bos.write(new byte[] {
- (byte) 0x02, (byte) 0x01, (byte) 0x06, (byte) 0x00,
- (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef,
- (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
- // ciaddr
- (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
- });
- // yiaddr
- bos.write(CLIENT_ADDR.getAddress());
- // siaddr
- bos.write(SERVER_ADDR.getAddress());
- // giaddr
- bos.write(INADDR_ANY.getAddress());
- // chaddr
- bos.write(CLIENT_MAC);
-
- // Padding
- bos.write(new byte[202]);
-
- // Options
- bos.write(new byte[]{
- // Magic cookie 0x63825363.
- (byte) 0x63, (byte) 0x82, (byte) 0x53, (byte) 0x63,
- // Message type OFFER.
- (byte) 0x35, (byte) 0x01, (byte) 0x02,
- });
- // Server ID
- bos.write(new byte[] { (byte) 0x36, (byte) 0x04 });
- bos.write(SERVER_ADDR.getAddress());
- // Lease time
- bos.write(new byte[] { (byte) 0x33, (byte) 0x04 });
- bos.write(intToByteArray(leaseTimeSecs));
- if (leaseTimeSecs != INFINITE_LEASE) {
- // Renewal time
- bos.write(new byte[]{(byte) 0x3a, (byte) 0x04});
- bos.write(intToByteArray(renewalTime));
- // Rebinding time
- bos.write(new byte[]{(byte) 0x3b, (byte) 0x04});
- bos.write(intToByteArray(rebindingTime));
- }
- // Subnet mask
- bos.write(new byte[] { (byte) 0x01, (byte) 0x04 });
- bos.write(NETMASK.getAddress());
- // Broadcast address
- bos.write(new byte[] { (byte) 0x1c, (byte) 0x04 });
- bos.write(BROADCAST_ADDR.getAddress());
- // Router
- bos.write(new byte[] { (byte) 0x03, (byte) 0x04 });
- bos.write(SERVER_ADDR.getAddress());
- // Nameserver
- bos.write(new byte[] { (byte) 0x06, (byte) 0x04 });
- bos.write(SERVER_ADDR.getAddress());
- // Hostname
- if (hostname != null) {
- bos.write(new byte[]{(byte) 0x0c, (byte) hostname.length()});
- bos.write(hostname.getBytes(Charset.forName("US-ASCII")));
- }
- // MTU
- bos.write(new byte[] { (byte) 0x1a, (byte) 0x02 });
- bos.write(shortToByteArray(MTU));
- // End options.
- bos.write(0xff);
-
- if ((bos.size() & 1) != 0) {
- bos.write(0x00);
- }
-
- final byte[] expected = bos.toByteArray();
- final byte[] actual = new byte[packet.limit()];
- packet.get(actual);
- final String msg = "Expected:\n " + HexDump.dumpHexString(expected) +
- "\nActual:\n " + HexDump.dumpHexString(actual);
- assertTrue(msg, Arrays.equals(expected, actual));
- }
-
- @Test
- public void testOfferPacket() throws Exception {
- checkBuildOfferPacket(3600, HOSTNAME);
- checkBuildOfferPacket(Integer.MAX_VALUE, HOSTNAME);
- checkBuildOfferPacket(0x80000000, HOSTNAME);
- checkBuildOfferPacket(INFINITE_LEASE, HOSTNAME);
- checkBuildOfferPacket(3600, null);
- }
-
- private static byte[] intToByteArray(int val) {
- return ByteBuffer.allocate(4).putInt(val).array();
- }
-
- private static byte[] shortToByteArray(short val) {
- return ByteBuffer.allocate(2).putShort(val).array();
- }
-}
diff --git a/tests/net/java/android/net/ip/IpClientTest.java b/tests/net/java/android/net/ip/IpClientTest.java
deleted file mode 100644
index 5110ce1830b3..000000000000
--- a/tests/net/java/android/net/ip/IpClientTest.java
+++ /dev/null
@@ -1,527 +0,0 @@
-/*
- * Copyright (C) 2017 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 android.net.ip;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyString;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.timeout;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.AlarmManager;
-import android.content.Context;
-import android.content.res.Resources;
-import android.net.ConnectivityManager;
-import android.net.INetd;
-import android.net.IpPrefix;
-import android.net.LinkAddress;
-import android.net.LinkProperties;
-import android.net.MacAddress;
-import android.net.RouteInfo;
-import android.net.shared.InitialConfiguration;
-import android.net.shared.ProvisioningConfiguration;
-import android.net.util.InterfaceParams;
-import android.os.INetworkManagementService;
-import android.provider.Settings;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.test.mock.MockContentResolver;
-
-import com.android.internal.R;
-import com.android.internal.util.test.FakeSettingsProvider;
-import com.android.server.net.BaseNetworkObserver;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.net.InetAddress;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-/**
- * Tests for IpClient.
- */
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class IpClientTest {
- private static final int DEFAULT_AVOIDBADWIFI_CONFIG_VALUE = 1;
-
- private static final String VALID = "VALID";
- private static final String INVALID = "INVALID";
- private static final String TEST_IFNAME = "test_wlan0";
- private static final int TEST_IFINDEX = 1001;
- // See RFC 7042#section-2.1.2 for EUI-48 documentation values.
- private static final MacAddress TEST_MAC = MacAddress.fromString("00:00:5E:00:53:01");
- private static final int TEST_TIMEOUT_MS = 400;
-
- @Mock private Context mContext;
- @Mock private ConnectivityManager mCm;
- @Mock private INetworkManagementService mNMService;
- @Mock private INetd mNetd;
- @Mock private Resources mResources;
- @Mock private IpClientCallbacks mCb;
- @Mock private AlarmManager mAlarm;
- @Mock private IpClient.Dependencies mDependecies;
- private MockContentResolver mContentResolver;
-
- private BaseNetworkObserver mObserver;
- private InterfaceParams mIfParams;
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
-
- when(mContext.getSystemService(eq(Context.ALARM_SERVICE))).thenReturn(mAlarm);
- when(mContext.getSystemServiceName(ConnectivityManager.class))
- .thenReturn(Context.CONNECTIVITY_SERVICE);
- when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(mCm);
- when(mContext.getResources()).thenReturn(mResources);
- when(mResources.getInteger(R.integer.config_networkAvoidBadWifi))
- .thenReturn(DEFAULT_AVOIDBADWIFI_CONFIG_VALUE);
-
- mContentResolver = new MockContentResolver();
- mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
- when(mContext.getContentResolver()).thenReturn(mContentResolver);
-
- mIfParams = null;
-
- when(mDependecies.getNMS()).thenReturn(mNMService);
- when(mDependecies.getNetd()).thenReturn(mNetd);
- }
-
- private void setTestInterfaceParams(String ifname) {
- mIfParams = (ifname != null)
- ? new InterfaceParams(ifname, TEST_IFINDEX, TEST_MAC)
- : null;
- when(mDependecies.getInterfaceParams(anyString())).thenReturn(mIfParams);
- }
-
- private IpClient makeIpClient(String ifname) throws Exception {
- setTestInterfaceParams(ifname);
- final IpClient ipc = new IpClient(mContext, ifname, mCb, mDependecies);
- verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceSetEnableIPv6(ifname, false);
- verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceClearAddrs(ifname);
- ArgumentCaptor<BaseNetworkObserver> arg =
- ArgumentCaptor.forClass(BaseNetworkObserver.class);
- verify(mNMService, times(1)).registerObserver(arg.capture());
- mObserver = arg.getValue();
- reset(mNMService);
- reset(mNetd);
- // Verify IpClient doesn't call onLinkPropertiesChange() when it starts.
- verify(mCb, never()).onLinkPropertiesChange(any());
- reset(mCb);
- return ipc;
- }
-
- private static LinkProperties makeEmptyLinkProperties(String iface) {
- final LinkProperties empty = new LinkProperties();
- empty.setInterfaceName(iface);
- return empty;
- }
-
- @Test
- public void testNullInterfaceNameMostDefinitelyThrows() throws Exception {
- setTestInterfaceParams(null);
- try {
- final IpClient ipc = new IpClient(mContext, null, mCb, mDependecies);
- ipc.shutdown();
- fail();
- } catch (NullPointerException npe) {
- // Phew; null interface names not allowed.
- }
- }
-
- @Test
- public void testNullCallbackMostDefinitelyThrows() throws Exception {
- final String ifname = "lo";
- setTestInterfaceParams(ifname);
- try {
- final IpClient ipc = new IpClient(mContext, ifname, null, mDependecies);
- ipc.shutdown();
- fail();
- } catch (NullPointerException npe) {
- // Phew; null callbacks not allowed.
- }
- }
-
- @Test
- public void testInvalidInterfaceDoesNotThrow() throws Exception {
- setTestInterfaceParams(TEST_IFNAME);
- final IpClient ipc = new IpClient(mContext, TEST_IFNAME, mCb, mDependecies);
- ipc.shutdown();
- }
-
- @Test
- public void testInterfaceNotFoundFailsImmediately() throws Exception {
- setTestInterfaceParams(null);
- final IpClient ipc = new IpClient(mContext, TEST_IFNAME, mCb, mDependecies);
- ipc.startProvisioning(new ProvisioningConfiguration());
- verify(mCb, times(1)).onProvisioningFailure(any());
- ipc.shutdown();
- }
-
- @Test
- public void testDefaultProvisioningConfiguration() throws Exception {
- final String iface = TEST_IFNAME;
- final IpClient ipc = makeIpClient(iface);
-
- ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
- .withoutIPv4()
- // TODO: mock IpReachabilityMonitor's dependencies (NetworkInterface, PowerManager)
- // and enable it in this test
- .withoutIpReachabilityMonitor()
- .build();
-
- ipc.startProvisioning(config);
- verify(mCb, times(1)).setNeighborDiscoveryOffload(true);
- verify(mCb, timeout(TEST_TIMEOUT_MS).times(1)).setFallbackMulticastFilter(false);
- verify(mCb, never()).onProvisioningFailure(any());
-
- ipc.shutdown();
- verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceSetEnableIPv6(iface, false);
- verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceClearAddrs(iface);
- verify(mCb, timeout(TEST_TIMEOUT_MS).times(1))
- .onLinkPropertiesChange(eq(makeEmptyLinkProperties(iface)));
- }
-
- @Test
- public void testProvisioningWithInitialConfiguration() throws Exception {
- final String iface = TEST_IFNAME;
- final IpClient ipc = makeIpClient(iface);
-
- String[] addresses = {
- "fe80::a4be:f92:e1f7:22d1/64",
- "fe80::f04a:8f6:6a32:d756/64",
- "fd2c:4e57:8e3c:0:548d:2db2:4fcf:ef75/64"
- };
- String[] prefixes = { "fe80::/64", "fd2c:4e57:8e3c::/64" };
-
- ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
- .withoutIPv4()
- .withoutIpReachabilityMonitor()
- .withInitialConfiguration(conf(links(addresses), prefixes(prefixes), ips()))
- .build();
-
- ipc.startProvisioning(config);
- verify(mCb, times(1)).setNeighborDiscoveryOffload(true);
- verify(mCb, timeout(TEST_TIMEOUT_MS).times(1)).setFallbackMulticastFilter(false);
- verify(mCb, never()).onProvisioningFailure(any());
-
- for (String addr : addresses) {
- String[] parts = addr.split("/");
- verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1))
- .interfaceAddAddress(iface, parts[0], Integer.parseInt(parts[1]));
- }
-
- final int lastAddr = addresses.length - 1;
-
- // Add N - 1 addresses
- for (int i = 0; i < lastAddr; i++) {
- mObserver.addressUpdated(iface, new LinkAddress(addresses[i]));
- verify(mCb, timeout(TEST_TIMEOUT_MS)).onLinkPropertiesChange(any());
- reset(mCb);
- }
-
- // Add Nth address
- mObserver.addressUpdated(iface, new LinkAddress(addresses[lastAddr]));
- LinkProperties want = linkproperties(links(addresses), routes(prefixes));
- want.setInterfaceName(iface);
- verify(mCb, timeout(TEST_TIMEOUT_MS).times(1)).onProvisioningSuccess(eq(want));
-
- ipc.shutdown();
- verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceSetEnableIPv6(iface, false);
- verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceClearAddrs(iface);
- verify(mCb, timeout(TEST_TIMEOUT_MS).times(1))
- .onLinkPropertiesChange(eq(makeEmptyLinkProperties(iface)));
- }
-
- @Test
- public void testIsProvisioned() throws Exception {
- InitialConfiguration empty = conf(links(), prefixes());
- IsProvisionedTestCase[] testcases = {
- // nothing
- notProvisionedCase(links(), routes(), dns(), null),
- notProvisionedCase(links(), routes(), dns(), empty),
-
- // IPv4
- provisionedCase(links("192.0.2.12/24"), routes(), dns(), empty),
-
- // IPv6
- notProvisionedCase(
- links("fe80::a4be:f92:e1f7:22d1/64", "fd2c:4e57:8e3c:0:548d:2db2:4fcf:ef75/64"),
- routes(), dns(), empty),
- notProvisionedCase(
- links("fe80::a4be:f92:e1f7:22d1/64", "fd2c:4e57:8e3c:0:548d:2db2:4fcf:ef75/64"),
- routes("fe80::/64", "fd2c:4e57:8e3c::/64"), dns("fd00:1234:5678::1000"), empty),
- provisionedCase(
- links("2001:db8:dead:beef:f00::a0/64", "fe80::1/64"),
- routes("::/0"),
- dns("2001:db8:dead:beef:f00::02"), empty),
-
- // Initial configuration
- provisionedCase(
- links("fe80::e1f7:22d1/64", "fd2c:4e57:8e3c:0:548d:2db2:4fcf:ef75/64"),
- routes("fe80::/64", "fd2c:4e57:8e3c::/64"),
- dns(),
- conf(links("fe80::e1f7:22d1/64", "fd2c:4e57:8e3c:0:548d:2db2:4fcf:ef75/64"),
- prefixes( "fe80::/64", "fd2c:4e57:8e3c::/64"), ips()))
- };
-
- for (IsProvisionedTestCase testcase : testcases) {
- if (IpClient.isProvisioned(testcase.lp, testcase.config) != testcase.isProvisioned) {
- fail(testcase.errorMessage());
- }
- }
- }
-
- static class IsProvisionedTestCase {
- boolean isProvisioned;
- LinkProperties lp;
- InitialConfiguration config;
-
- String errorMessage() {
- return String.format("expected %s with config %s to be %s, but was %s",
- lp, config, provisioned(isProvisioned), provisioned(!isProvisioned));
- }
-
- static String provisioned(boolean isProvisioned) {
- return isProvisioned ? "provisioned" : "not provisioned";
- }
- }
-
- static IsProvisionedTestCase provisionedCase(Set<LinkAddress> lpAddrs, Set<RouteInfo> lpRoutes,
- Set<InetAddress> lpDns, InitialConfiguration config) {
- return provisioningTest(true, lpAddrs, lpRoutes, lpDns, config);
- }
-
- static IsProvisionedTestCase notProvisionedCase(Set<LinkAddress> lpAddrs,
- Set<RouteInfo> lpRoutes, Set<InetAddress> lpDns, InitialConfiguration config) {
- return provisioningTest(false, lpAddrs, lpRoutes, lpDns, config);
- }
-
- static IsProvisionedTestCase provisioningTest(boolean isProvisioned, Set<LinkAddress> lpAddrs,
- Set<RouteInfo> lpRoutes, Set<InetAddress> lpDns, InitialConfiguration config) {
- IsProvisionedTestCase testcase = new IsProvisionedTestCase();
- testcase.isProvisioned = isProvisioned;
- testcase.lp = new LinkProperties();
- testcase.lp.setLinkAddresses(lpAddrs);
- for (RouteInfo route : lpRoutes) {
- testcase.lp.addRoute(route);
- }
- for (InetAddress dns : lpDns) {
- testcase.lp.addDnsServer(dns);
- }
- testcase.config = config;
- return testcase;
- }
-
- @Test
- public void testInitialConfigurations() throws Exception {
- InitialConfigurationTestCase[] testcases = {
- validConf("valid IPv4 configuration",
- links("192.0.2.12/24"), prefixes("192.0.2.0/24"), dns("192.0.2.2")),
- validConf("another valid IPv4 configuration",
- links("192.0.2.12/24"), prefixes("192.0.2.0/24"), dns()),
- validConf("valid IPv6 configurations",
- links("2001:db8:dead:beef:f00::a0/64", "fe80::1/64"),
- prefixes("2001:db8:dead:beef::/64", "fe80::/64"),
- dns("2001:db8:dead:beef:f00::02")),
- validConf("valid IPv6 configurations",
- links("fe80::1/64"), prefixes("fe80::/64"), dns()),
- validConf("valid IPv6/v4 configuration",
- links("2001:db8:dead:beef:f00::a0/48", "192.0.2.12/24"),
- prefixes("2001:db8:dead:beef::/64", "192.0.2.0/24"),
- dns("192.0.2.2", "2001:db8:dead:beef:f00::02")),
- validConf("valid IPv6 configuration without any GUA.",
- links("fd00:1234:5678::1/48"),
- prefixes("fd00:1234:5678::/48"),
- dns("fd00:1234:5678::1000")),
-
- invalidConf("empty configuration", links(), prefixes(), dns()),
- invalidConf("v4 addr and dns not in any prefix",
- links("192.0.2.12/24"), prefixes("198.51.100.0/24"), dns("192.0.2.2")),
- invalidConf("v4 addr not in any prefix",
- links("198.51.2.12/24"), prefixes("198.51.100.0/24"), dns("192.0.2.2")),
- invalidConf("v4 dns addr not in any prefix",
- links("192.0.2.12/24"), prefixes("192.0.2.0/24"), dns("198.51.100.2")),
- invalidConf("v6 addr not in any prefix",
- links("2001:db8:dead:beef:f00::a0/64", "fe80::1/64"),
- prefixes("2001:db8:dead:beef::/64"),
- dns("2001:db8:dead:beef:f00::02")),
- invalidConf("v6 dns addr not in any prefix",
- links("fe80::1/64"), prefixes("fe80::/64"), dns("2001:db8:dead:beef:f00::02")),
- invalidConf("default ipv6 route and no GUA",
- links("fd01:1111:2222:3333::a0/128"), prefixes("::/0"), dns()),
- invalidConf("invalid v6 prefix length",
- links("2001:db8:dead:beef:f00::a0/128"), prefixes("2001:db8:dead:beef::/32"),
- dns()),
- invalidConf("another invalid v6 prefix length",
- links("2001:db8:dead:beef:f00::a0/128"), prefixes("2001:db8:dead:beef::/72"),
- dns())
- };
-
- for (InitialConfigurationTestCase testcase : testcases) {
- if (testcase.config.isValid() != testcase.isValid) {
- fail(testcase.errorMessage());
- }
- }
- }
-
- static class InitialConfigurationTestCase {
- String descr;
- boolean isValid;
- InitialConfiguration config;
- public String errorMessage() {
- return String.format("%s: expected configuration %s to be %s, but was %s",
- descr, config, validString(isValid), validString(!isValid));
- }
- static String validString(boolean isValid) {
- return isValid ? VALID : INVALID;
- }
- }
-
- static InitialConfigurationTestCase validConf(String descr, Set<LinkAddress> links,
- Set<IpPrefix> prefixes, Set<InetAddress> dns) {
- return confTestCase(descr, true, conf(links, prefixes, dns));
- }
-
- static InitialConfigurationTestCase invalidConf(String descr, Set<LinkAddress> links,
- Set<IpPrefix> prefixes, Set<InetAddress> dns) {
- return confTestCase(descr, false, conf(links, prefixes, dns));
- }
-
- static InitialConfigurationTestCase confTestCase(
- String descr, boolean isValid, InitialConfiguration config) {
- InitialConfigurationTestCase testcase = new InitialConfigurationTestCase();
- testcase.descr = descr;
- testcase.isValid = isValid;
- testcase.config = config;
- return testcase;
- }
-
- static LinkProperties linkproperties(Set<LinkAddress> addresses, Set<RouteInfo> routes) {
- LinkProperties lp = new LinkProperties();
- lp.setLinkAddresses(addresses);
- for (RouteInfo route : routes) {
- lp.addRoute(route);
- }
- return lp;
- }
-
- static InitialConfiguration conf(Set<LinkAddress> links, Set<IpPrefix> prefixes) {
- return conf(links, prefixes, new HashSet<>());
- }
-
- static InitialConfiguration conf(
- Set<LinkAddress> links, Set<IpPrefix> prefixes, Set<InetAddress> dns) {
- InitialConfiguration conf = new InitialConfiguration();
- conf.ipAddresses.addAll(links);
- conf.directlyConnectedRoutes.addAll(prefixes);
- conf.dnsServers.addAll(dns);
- return conf;
- }
-
- static Set<RouteInfo> routes(String... routes) {
- return mapIntoSet(routes, (r) -> new RouteInfo(new IpPrefix(r)));
- }
-
- static Set<IpPrefix> prefixes(String... prefixes) {
- return mapIntoSet(prefixes, IpPrefix::new);
- }
-
- static Set<LinkAddress> links(String... addresses) {
- return mapIntoSet(addresses, LinkAddress::new);
- }
-
- static Set<InetAddress> ips(String... addresses) {
- return mapIntoSet(addresses, InetAddress::getByName);
- }
-
- static Set<InetAddress> dns(String... addresses) {
- return ips(addresses);
- }
-
- static <A, B> Set<B> mapIntoSet(A[] in, Fn<A, B> fn) {
- Set<B> out = new HashSet<>(in.length);
- for (A item : in) {
- try {
- out.add(fn.call(item));
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
- return out;
- }
-
- interface Fn<A,B> {
- B call(A a) throws Exception;
- }
-
- @Test
- public void testAll() {
- List<String> list1 = Arrays.asList();
- List<String> list2 = Arrays.asList("foo");
- List<String> list3 = Arrays.asList("bar", "baz");
- List<String> list4 = Arrays.asList("foo", "bar", "baz");
-
- assertTrue(IpClient.all(list1, (x) -> false));
- assertFalse(IpClient.all(list2, (x) -> false));
- assertTrue(IpClient.all(list3, (x) -> true));
- assertTrue(IpClient.all(list2, (x) -> x.charAt(0) == 'f'));
- assertFalse(IpClient.all(list4, (x) -> x.charAt(0) == 'f'));
- }
-
- @Test
- public void testAny() {
- List<String> list1 = Arrays.asList();
- List<String> list2 = Arrays.asList("foo");
- List<String> list3 = Arrays.asList("bar", "baz");
- List<String> list4 = Arrays.asList("foo", "bar", "baz");
-
- assertFalse(IpClient.any(list1, (x) -> true));
- assertTrue(IpClient.any(list2, (x) -> true));
- assertTrue(IpClient.any(list2, (x) -> x.charAt(0) == 'f'));
- assertFalse(IpClient.any(list3, (x) -> x.charAt(0) == 'f'));
- assertTrue(IpClient.any(list4, (x) -> x.charAt(0) == 'f'));
- }
-
- @Test
- public void testFindAll() {
- List<String> list1 = Arrays.asList();
- List<String> list2 = Arrays.asList("foo");
- List<String> list3 = Arrays.asList("foo", "bar", "baz");
-
- assertEquals(list1, IpClient.findAll(list1, (x) -> true));
- assertEquals(list1, IpClient.findAll(list3, (x) -> false));
- assertEquals(list3, IpClient.findAll(list3, (x) -> true));
- assertEquals(list2, IpClient.findAll(list3, (x) -> x.charAt(0) == 'f'));
- }
-}
diff --git a/tests/net/java/android/net/ip/IpReachabilityMonitorTest.java b/tests/net/java/android/net/ip/IpReachabilityMonitorTest.java
deleted file mode 100644
index e3b5ddf6f4cf..000000000000
--- a/tests/net/java/android/net/ip/IpReachabilityMonitorTest.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2017 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 android.net.ip;
-
-import static org.mockito.Mockito.anyString;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.net.util.InterfaceParams;
-import android.net.util.SharedLog;
-import android.os.Handler;
-import android.os.Looper;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-
-/**
- * Tests for IpReachabilityMonitor.
- */
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class IpReachabilityMonitorTest {
-
- @Mock IpReachabilityMonitor.Callback mCallback;
- @Mock IpReachabilityMonitor.Dependencies mDependencies;
- @Mock SharedLog mLog;
- @Mock Context mContext;
- Handler mHandler;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- when(mLog.forSubComponent(anyString())).thenReturn(mLog);
- mHandler = new Handler(Looper.getMainLooper());
- }
-
- IpReachabilityMonitor makeMonitor() {
- final InterfaceParams ifParams = new InterfaceParams("fake0", 1, null);
- return new IpReachabilityMonitor(
- mContext, ifParams, mHandler, mLog, mCallback, false, mDependencies);
- }
-
- @Test
- public void testNothing() {
- IpReachabilityMonitor monitor = makeMonitor();
- }
-}
diff --git a/tests/net/java/android/net/util/ConnectivityPacketSummaryTest.java b/tests/net/java/android/net/util/ConnectivityPacketSummaryTest.java
deleted file mode 100644
index dfaf52a953c7..000000000000
--- a/tests/net/java/android/net/util/ConnectivityPacketSummaryTest.java
+++ /dev/null
@@ -1,418 +0,0 @@
-/*
- * Copyright (C) 2016 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 android.net.util;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import android.net.MacAddress;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import libcore.util.HexEncoding;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests for ConnectivityPacketSummary.
- *
- * @hide
- */
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class ConnectivityPacketSummaryTest {
- private static final MacAddress MYHWADDR = MacAddress.fromString("80:7a:bf:6f:48:f3");
-
- private String getSummary(String hexBytes) {
- hexBytes = hexBytes.replaceAll("\\s+", "");
- final byte[] bytes = HexEncoding.decode(hexBytes.toCharArray(), false);
- return ConnectivityPacketSummary.summarize(MYHWADDR, bytes);
- }
-
- @Test
- public void testParseICMPv6DADProbe() {
- final String packet =
- // Ethernet
- "3333FF6F48F3 807ABF6F48F3 86DD" +
- // IPv6
- "600000000018 3A FF" +
- "00000000000000000000000000000000" +
- "FF0200000000000000000001FF6F48F3" +
- // ICMPv6
- "87 00 A8E7" +
- "00000000" +
- "FE80000000000000827ABFFFFE6F48F3";
-
- final String expected =
- "TX 80:7a:bf:6f:48:f3 > 33:33:ff:6f:48:f3 ipv6" +
- " :: > ff02::1:ff6f:48f3 icmp6" +
- " ns fe80::827a:bfff:fe6f:48f3";
-
- assertEquals(expected, getSummary(packet));
- }
-
- @Test
- public void testParseICMPv6RS() {
- final String packet =
- // Ethernet
- "333300000002 807ABF6F48F3 86DD" +
- // IPv6
- "600000000010 3A FF" +
- "FE80000000000000827ABFFFFE6F48F3" +
- "FF020000000000000000000000000002" +
- // ICMPv6 RS
- "85 00 6973" +
- "00000000" +
- "01 01 807ABF6F48F3";
-
- final String expected =
- "TX 80:7a:bf:6f:48:f3 > 33:33:00:00:00:02 ipv6" +
- " fe80::827a:bfff:fe6f:48f3 > ff02::2 icmp6" +
- " rs slla 80:7a:bf:6f:48:f3";
-
- assertEquals(expected, getSummary(packet));
- }
-
- @Test
- public void testParseICMPv6RA() {
- final String packet =
- // Ethernet
- "807ABF6F48F3 100E7E263FC1 86DD" +
- // IPv6
- "600000000068 3A FF" +
- "FE80000000000000FA000004FD000001" +
- "FE80000000000000827ABFFFFE6F48F3" +
- // ICMPv6 RA
- "86 00 8141" +
- "40 00 0E10" +
- "00000000" +
- "00000000" +
- "01 01 00005E000265" +
- "05 01 0000000005DC" +
- "19 05 000000000E10" +
- " 20014860486000000000000000008844" +
- " 20014860486000000000000000008888" +
- "03 04 40 C0" +
- " 00278D00" +
- " 00093A80" +
- " 00000000" +
- " 2401FA000004FD000000000000000000";
-
- final String expected =
- "RX 10:0e:7e:26:3f:c1 > 80:7a:bf:6f:48:f3 ipv6" +
- " fe80::fa00:4:fd00:1 > fe80::827a:bfff:fe6f:48f3 icmp6" +
- " ra slla 00:00:5e:00:02:65 mtu 1500";
-
- assertEquals(expected, getSummary(packet));
- }
-
- @Test
- public void testParseICMPv6NS() {
- final String packet =
- // Ethernet
- "807ABF6F48F3 100E7E263FC1 86DD" +
- // IPv6
- "6C0000000020 3A FF" +
- "FE80000000000000FA000004FD000001" +
- "FF0200000000000000000001FF01C146" +
- // ICMPv6 NS
- "87 00 8AD4" +
- "00000000" +
- "2401FA000004FD0015EA6A5C7B01C146" +
- "01 01 00005E000265";
-
- final String expected =
- "RX 10:0e:7e:26:3f:c1 > 80:7a:bf:6f:48:f3 ipv6" +
- " fe80::fa00:4:fd00:1 > ff02::1:ff01:c146 icmp6" +
- " ns 2401:fa00:4:fd00:15ea:6a5c:7b01:c146 slla 00:00:5e:00:02:65";
-
- assertEquals(expected, getSummary(packet));
- }
-
- @Test
- public void testInvalidICMPv6NDLength() {
- final String packet =
- // Ethernet
- "807ABF6F48F3 100E7E263FC1 86DD" +
- // IPv6
- "600000000068 3A FF" +
- "FE80000000000000FA000004FD000001" +
- "FE80000000000000827ABFFFFE6F48F3" +
- // ICMPv6 RA
- "86 00 8141" +
- "40 00 0E10" +
- "00000000" +
- "00000000" +
- "01 01 00005E000265" +
- "00 00 0102030405D6";
-
- final String expected =
- "RX 10:0e:7e:26:3f:c1 > 80:7a:bf:6f:48:f3 ipv6" +
- " fe80::fa00:4:fd00:1 > fe80::827a:bfff:fe6f:48f3 icmp6" +
- " ra slla 00:00:5e:00:02:65 <malformed>";
-
- assertEquals(expected, getSummary(packet));
- }
-
- @Test
- public void testParseICMPv6NA() {
- final String packet =
- // Ethernet
- "00005E000265 807ABF6F48F3 86DD" +
- "600000000020 3A FF" +
- "2401FA000004FD0015EA6A5C7B01C146" +
- "FE80000000000000FA000004FD000001" +
- "88 00 E8126" +
- "0000000" +
- "2401FA000004FD0015EA6A5C7B01C146" +
- "02 01 807ABF6F48F3";
-
- final String expected =
- "TX 80:7a:bf:6f:48:f3 > 00:00:5e:00:02:65 ipv6" +
- " 2401:fa00:4:fd00:15ea:6a5c:7b01:c146 > fe80::fa00:4:fd00:1 icmp6" +
- " na 2401:fa00:4:fd00:15ea:6a5c:7b01:c146 tlla 80:7a:bf:6f:48:f3";
-
- assertEquals(expected, getSummary(packet));
- }
-
- @Test
- public void testParseARPRequest() {
- final String packet =
- // Ethernet
- "FFFFFFFFFFFF 807ABF6F48F3 0806" +
- // ARP
- "0001 0800 06 04" +
- // Request
- "0001" +
- "807ABF6F48F3 64706ADB" +
- "000000000000 64706FFD";
-
- final String expected =
- "TX 80:7a:bf:6f:48:f3 > ff:ff:ff:ff:ff:ff arp" +
- " who-has 100.112.111.253";
-
- assertEquals(expected, getSummary(packet));
- }
-
- @Test
- public void testParseARPReply() {
- final String packet =
- // Ethernet
- "807ABF6F48F3 288A1CA8DFC1 0806" +
- // ARP
- "0001 0800 06 04" +
- // Reply
- "0002" +
- "288A1CA8DFC1 64706FFD"+
- "807ABF6F48F3 64706ADB" +
- // Ethernet padding to packet min size.
- "0000000000000000000000000000";
-
- final String expected =
- "RX 28:8a:1c:a8:df:c1 > 80:7a:bf:6f:48:f3 arp" +
- " reply 100.112.111.253 28:8a:1c:a8:df:c1";
-
- assertEquals(expected, getSummary(packet));
- }
-
- @Test
- public void testParseDHCPv4Discover() {
- final String packet =
- // Ethernet
- "FFFFFFFFFFFF 807ABF6F48F3 0800" +
- // IPv4
- "451001580000400040113986" +
- "00000000" +
- "FFFFFFFF" +
- // UDP
- "0044 0043" +
- "0144 5559" +
- // DHCPv4
- "01 01 06 00" +
- "79F7ACA4" +
- "0000 0000" +
- "00000000" +
- "00000000" +
- "00000000" +
- "00000000" +
- "807ABF6F48F300000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "63 82 53 63" +
- "35 01 01" +
- "3D 07 01807ABF6F48F3" +
- "39 02 05DC" +
- "3C 12 616E64726F69642D646863702D372E312E32" +
- "0C 18 616E64726F69642D36623030366333313333393835343139" +
- "37 0A 01 03 06 0F 1A 1C 33 3A 3B 2B" +
- "FF" +
- "00";
-
- final String expectedPrefix =
- "TX 80:7a:bf:6f:48:f3 > ff:ff:ff:ff:ff:ff ipv4" +
- " 0.0.0.0 > 255.255.255.255 udp" +
- " 68 > 67 dhcp4" +
- " 80:7a:bf:6f:48:f3 DISCOVER";
-
- assertTrue(getSummary(packet).startsWith(expectedPrefix));
- }
-
- @Test
- public void testParseDHCPv4Offer() {
- final String packet =
- // Ethernet
- "807ABF6F48F3 288A1CA8DFC1 0800" +
- // IPv4
- "4500013D4D2C0000401188CB" +
- "64706FFD" +
- "64706ADB" +
- // UDP
- "0043 0044" +
- "0129 371D" +
- // DHCPv4
- "02 01 06 01" +
- "79F7ACA4" +
- "0000 0000" +
- "00000000" +
- "64706ADB" +
- "00000000" +
- "00000000" +
- "807ABF6F48F300000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "63 82 53 63" +
- "35 01 02" +
- "36 04 AC188A0B" +
- "33 04 00000708" +
- "01 04 FFFFF000" +
- "03 04 64706FFE" +
- "06 08 08080808" +
- " 08080404" +
- "FF0001076165313A363636FF";
-
- final String expectedPrefix =
- "RX 28:8a:1c:a8:df:c1 > 80:7a:bf:6f:48:f3 ipv4" +
- " 100.112.111.253 > 100.112.106.219 udp" +
- " 67 > 68 dhcp4" +
- " 80:7a:bf:6f:48:f3 OFFER";
-
- assertTrue(getSummary(packet).startsWith(expectedPrefix));
- }
-
- @Test
- public void testParseDHCPv4Request() {
- final String packet =
- // Ethernet
- "FFFFFFFFFFFF 807ABF6F48F3 0800" +
- // IPv4
- "45100164000040004011397A" +
- "00000000" +
- "FFFFFFFF" +
- // UDP
- "0044 0043" +
- "0150 E5C7" +
- // DHCPv4
- "01 01 06 00" +
- "79F7ACA4" +
- "0001 0000" +
- "00000000" +
- "00000000" +
- "00000000" +
- "00000000" +
- "807ABF6F48F300000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "63 82 53 63" +
- "35 01 03" +
- "3D 07 01807ABF6F48F3" +
- "32 04 64706ADB" +
- "36 04 AC188A0B" +
- "39 02 05DC" +
- "3C 12 616E64726F69642D646863702D372E312E32" +
- "0C 18 616E64726F69642D36623030366333313333393835343139" +
- "37 0A 01 03 06 0F 1A 1C 33 3A 3B 2B" +
- "FF" +
- "00";
-
- final String expectedPrefix =
- "TX 80:7a:bf:6f:48:f3 > ff:ff:ff:ff:ff:ff ipv4" +
- " 0.0.0.0 > 255.255.255.255 udp" +
- " 68 > 67 dhcp4" +
- " 80:7a:bf:6f:48:f3 REQUEST";
-
- assertTrue(getSummary(packet).startsWith(expectedPrefix));
- }
-
- @Test
- public void testParseDHCPv4Ack() {
- final String packet =
- // Ethernet
- "807ABF6F48F3 288A1CA8DFC1 0800" +
- // IPv4
- "4500013D4D3B0000401188BC" +
- "64706FFD" +
- "64706ADB" +
- // UDP
- "0043 0044" +
- "0129 341C" +
- // DHCPv4
- "02 01 06 01" +
- "79F7ACA4" +
- "0001 0000" +
- "00000000" +
- "64706ADB" +
- "00000000" +
- "00000000" +
- "807ABF6F48F300000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "63 82 53 63" +
- "35 01 05" +
- "36 04 AC188A0B" +
- "33 04 00000708" +
- "01 04 FFFFF000" +
- "03 04 64706FFE" +
- "06 08 08080808" +
- " 08080404" +
- "FF0001076165313A363636FF";
-
- final String expectedPrefix =
- "RX 28:8a:1c:a8:df:c1 > 80:7a:bf:6f:48:f3 ipv4" +
- " 100.112.111.253 > 100.112.106.219 udp" +
- " 67 > 68 dhcp4" +
- " 80:7a:bf:6f:48:f3 ACK";
-
- assertTrue(getSummary(packet).startsWith(expectedPrefix));
- }
-}
diff --git a/tests/net/java/android/net/util/PacketReaderTest.java b/tests/net/java/android/net/util/PacketReaderTest.java
deleted file mode 100644
index dced7435ee74..000000000000
--- a/tests/net/java/android/net/util/PacketReaderTest.java
+++ /dev/null
@@ -1,209 +0,0 @@
-/*
- * Copyright (C) 2016 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 android.net.util;
-
-import static android.net.util.PacketReader.DEFAULT_RECV_BUF_SIZE;
-import static android.system.OsConstants.*;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.system.StructTimeval;
-
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.UncheckedIOException;
-import java.net.DatagramPacket;
-import java.net.DatagramSocket;
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.SocketException;
-import java.util.Arrays;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-import org.junit.runner.RunWith;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import libcore.io.IoBridge;
-
-/**
- * Tests for PacketReader.
- *
- * @hide
- */
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class PacketReaderTest {
- static final InetAddress LOOPBACK6 = Inet6Address.getLoopbackAddress();
- static final StructTimeval TIMEO = StructTimeval.fromMillis(500);
-
- protected CountDownLatch mLatch;
- protected FileDescriptor mLocalSocket;
- protected InetSocketAddress mLocalSockName;
- protected byte[] mLastRecvBuf;
- protected boolean mStopped;
- protected HandlerThread mHandlerThread;
- protected PacketReader mReceiver;
-
- class UdpLoopbackReader extends PacketReader {
- public UdpLoopbackReader(Handler h) {
- super(h);
- }
-
- @Override
- protected FileDescriptor createFd() {
- FileDescriptor s = null;
- try {
- s = Os.socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
- Os.bind(s, LOOPBACK6, 0);
- mLocalSockName = (InetSocketAddress) Os.getsockname(s);
- Os.setsockoptTimeval(s, SOL_SOCKET, SO_SNDTIMEO, TIMEO);
- } catch (ErrnoException|SocketException e) {
- closeFd(s);
- fail();
- return null;
- }
-
- mLocalSocket = s;
- return s;
- }
-
- @Override
- protected void handlePacket(byte[] recvbuf, int length) {
- mLastRecvBuf = Arrays.copyOf(recvbuf, length);
- mLatch.countDown();
- }
-
- @Override
- protected void onStart() {
- mStopped = false;
- mLatch.countDown();
- }
-
- @Override
- protected void onStop() {
- mStopped = true;
- mLatch.countDown();
- }
- };
-
- @Before
- public void setUp() {
- resetLatch();
- mLocalSocket = null;
- mLocalSockName = null;
- mLastRecvBuf = null;
- mStopped = false;
-
- mHandlerThread = new HandlerThread(PacketReaderTest.class.getSimpleName());
- mHandlerThread.start();
- }
-
- @After
- public void tearDown() throws Exception {
- if (mReceiver != null) {
- mHandlerThread.getThreadHandler().post(() -> { mReceiver.stop(); });
- waitForActivity();
- }
- mReceiver = null;
- mHandlerThread.quit();
- mHandlerThread = null;
- }
-
- void resetLatch() { mLatch = new CountDownLatch(1); }
-
- void waitForActivity() throws Exception {
- try {
- mLatch.await(1000, TimeUnit.MILLISECONDS);
- } finally {
- resetLatch();
- }
- }
-
- void sendPacket(byte[] contents) throws Exception {
- final DatagramSocket sender = new DatagramSocket();
- sender.connect(mLocalSockName);
- sender.send(new DatagramPacket(contents, contents.length));
- sender.close();
- }
-
- @Test
- public void testBasicWorking() throws Exception {
- final Handler h = mHandlerThread.getThreadHandler();
- mReceiver = new UdpLoopbackReader(h);
-
- h.post(() -> { mReceiver.start(); });
- waitForActivity();
- assertTrue(mLocalSockName != null);
- assertEquals(LOOPBACK6, mLocalSockName.getAddress());
- assertTrue(0 < mLocalSockName.getPort());
- assertTrue(mLocalSocket != null);
- assertFalse(mStopped);
-
- final byte[] one = "one 1".getBytes("UTF-8");
- sendPacket(one);
- waitForActivity();
- assertEquals(1, mReceiver.numPacketsReceived());
- assertTrue(Arrays.equals(one, mLastRecvBuf));
- assertFalse(mStopped);
-
- final byte[] two = "two 2".getBytes("UTF-8");
- sendPacket(two);
- waitForActivity();
- assertEquals(2, mReceiver.numPacketsReceived());
- assertTrue(Arrays.equals(two, mLastRecvBuf));
- assertFalse(mStopped);
-
- mReceiver.stop();
- waitForActivity();
- assertEquals(2, mReceiver.numPacketsReceived());
- assertTrue(Arrays.equals(two, mLastRecvBuf));
- assertTrue(mStopped);
- mReceiver = null;
- }
-
- class NullPacketReader extends PacketReader {
- public NullPacketReader(Handler h, int recvbufsize) {
- super(h, recvbufsize);
- }
-
- @Override
- public FileDescriptor createFd() { return null; }
- }
-
- @Test
- public void testMinimalRecvBufSize() throws Exception {
- final Handler h = mHandlerThread.getThreadHandler();
-
- for (int i : new int[]{-1, 0, 1, DEFAULT_RECV_BUF_SIZE-1}) {
- final PacketReader b = new NullPacketReader(h, i);
- assertEquals(DEFAULT_RECV_BUF_SIZE, b.recvBufSize());
- }
- }
-}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 1c264184db4c..b5d5f61b527e 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -107,6 +107,8 @@ import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
import android.net.InterfaceConfiguration;
import android.net.IpPrefix;
+import android.net.IpSecManager;
+import android.net.IpSecManager.UdpEncapsulationSocket;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.MatchAllNetworkSpecifier;
@@ -122,6 +124,7 @@ import android.net.NetworkSpecifier;
import android.net.NetworkStack;
import android.net.NetworkUtils;
import android.net.RouteInfo;
+import android.net.SocketKeepalive;
import android.net.UidRange;
import android.net.metrics.IpConnectivityLog;
import android.net.shared.NetworkMonitorUtils;
@@ -186,6 +189,8 @@ import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -402,8 +407,8 @@ public class ConnectivityServiceTest {
private final ConditionVariable mPreventReconnectReceived = new ConditionVariable();
private int mScore;
private NetworkAgent mNetworkAgent;
- private int mStartKeepaliveError = PacketKeepalive.ERROR_HARDWARE_UNSUPPORTED;
- private int mStopKeepaliveError = PacketKeepalive.NO_KEEPALIVE;
+ private int mStartKeepaliveError = SocketKeepalive.ERROR_HARDWARE_UNSUPPORTED;
+ private int mStopKeepaliveError = SocketKeepalive.NO_KEEPALIVE;
private Integer mExpectedKeepaliveSlot = null;
// Contains the redirectUrl from networkStatus(). Before reading, wait for
// mNetworkStatusReceived.
@@ -1508,6 +1513,12 @@ public class ConnectivityServiceTest {
verifyActiveNetwork(TRANSPORT_WIFI);
}
+ @Test
+ public void testRequiresValidation() {
+ assertTrue(NetworkMonitorUtils.isValidationRequired(
+ mCm.getDefaultRequest().networkCapabilities));
+ }
+
enum CallbackState {
NONE,
AVAILABLE,
@@ -3542,6 +3553,80 @@ public class ConnectivityServiceTest {
}
}
+ private static class TestSocketKeepaliveCallback extends SocketKeepalive.Callback {
+
+ public enum CallbackType { ON_STARTED, ON_STOPPED, ON_ERROR };
+
+ private class CallbackValue {
+ public CallbackType callbackType;
+ public int error;
+
+ CallbackValue(CallbackType type) {
+ this.callbackType = type;
+ this.error = SocketKeepalive.SUCCESS;
+ assertTrue("onError callback must have error", type != CallbackType.ON_ERROR);
+ }
+
+ CallbackValue(CallbackType type, int error) {
+ this.callbackType = type;
+ this.error = error;
+ assertEquals("error can only be set for onError", type, CallbackType.ON_ERROR);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof CallbackValue
+ && this.callbackType == ((CallbackValue) o).callbackType
+ && this.error == ((CallbackValue) o).error;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s(%s, %d)", getClass().getSimpleName(), callbackType,
+ error);
+ }
+ }
+
+ private LinkedBlockingQueue<CallbackValue> mCallbacks = new LinkedBlockingQueue<>();
+
+ @Override
+ public void onStarted() {
+ mCallbacks.add(new CallbackValue(CallbackType.ON_STARTED));
+ }
+
+ @Override
+ public void onStopped() {
+ mCallbacks.add(new CallbackValue(CallbackType.ON_STOPPED));
+ }
+
+ @Override
+ public void onError(int error) {
+ mCallbacks.add(new CallbackValue(CallbackType.ON_ERROR, error));
+ }
+
+ private void expectCallback(CallbackValue callbackValue) {
+ try {
+ assertEquals(
+ callbackValue,
+ mCallbacks.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ } catch (InterruptedException e) {
+ fail(callbackValue.callbackType + " callback not seen after " + TIMEOUT_MS + " ms");
+ }
+ }
+
+ public void expectStarted() {
+ expectCallback(new CallbackValue(CallbackType.ON_STARTED));
+ }
+
+ public void expectStopped() {
+ expectCallback(new CallbackValue(CallbackType.ON_STOPPED));
+ }
+
+ public void expectError(int error) {
+ expectCallback(new CallbackValue(CallbackType.ON_ERROR, error));
+ }
+ }
+
private Network connectKeepaliveNetwork(LinkProperties lp) {
// Ensure the network is disconnected before we do anything.
if (mWiFiNetworkAgent != null) {
@@ -3689,6 +3774,145 @@ public class ConnectivityServiceTest {
}
@Test
+ public void testNattSocketKeepalives() throws Exception {
+ // TODO: 1. Move this outside of ConnectivityServiceTest.
+ // 2. Add helper function to test against newSingleThreadExecutor as well as inline
+ // executor.
+ // 3. Make test to verify that Nat-T keepalive socket is created by IpSecService.
+ final int srcPort = 12345;
+ final InetAddress myIPv4 = InetAddress.getByName("192.0.2.129");
+ final InetAddress notMyIPv4 = InetAddress.getByName("192.0.2.35");
+ final InetAddress myIPv6 = InetAddress.getByName("2001:db8::1");
+ final InetAddress dstIPv4 = InetAddress.getByName("8.8.8.8");
+ final InetAddress dstIPv6 = InetAddress.getByName("2001:4860:4860::8888");
+
+ final int validKaInterval = 15;
+ final int invalidKaInterval = 9;
+
+ final IpSecManager mIpSec = (IpSecManager) mContext.getSystemService(Context.IPSEC_SERVICE);
+ final UdpEncapsulationSocket testSocket = mIpSec.openUdpEncapsulationSocket(srcPort);
+
+ final Executor executor = Executors.newSingleThreadExecutor();
+
+ LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName("wlan12");
+ lp.addLinkAddress(new LinkAddress(myIPv6, 64));
+ lp.addLinkAddress(new LinkAddress(myIPv4, 25));
+ lp.addRoute(new RouteInfo(InetAddress.getByName("fe80::1234")));
+ lp.addRoute(new RouteInfo(InetAddress.getByName("192.0.2.254")));
+
+ Network notMyNet = new Network(61234);
+ Network myNet = connectKeepaliveNetwork(lp);
+
+ TestSocketKeepaliveCallback callback = new TestSocketKeepaliveCallback();
+ SocketKeepalive ka;
+
+ // Attempt to start keepalives with invalid parameters and check for errors.
+ // Invalid network.
+ ka = mCm.createSocketKeepalive(notMyNet, testSocket, myIPv4, dstIPv4, executor, callback);
+ ka.start(validKaInterval);
+ callback.expectError(SocketKeepalive.ERROR_INVALID_NETWORK);
+
+ // Invalid interval.
+ ka = mCm.createSocketKeepalive(myNet, testSocket, myIPv4, dstIPv4, executor, callback);
+ ka.start(invalidKaInterval);
+ callback.expectError(SocketKeepalive.ERROR_INVALID_INTERVAL);
+
+ // Invalid destination.
+ ka = mCm.createSocketKeepalive(myNet, testSocket, myIPv4, dstIPv6, executor, callback);
+ ka.start(validKaInterval);
+ callback.expectError(SocketKeepalive.ERROR_INVALID_IP_ADDRESS);
+
+ // Invalid source;
+ ka = mCm.createSocketKeepalive(myNet, testSocket, myIPv6, dstIPv4, executor, callback);
+ ka.start(validKaInterval);
+ callback.expectError(SocketKeepalive.ERROR_INVALID_IP_ADDRESS);
+
+ // NAT-T is only supported for IPv4.
+ ka = mCm.createSocketKeepalive(myNet, testSocket, myIPv6, dstIPv6, executor, callback);
+ ka.start(validKaInterval);
+ callback.expectError(SocketKeepalive.ERROR_INVALID_IP_ADDRESS);
+
+ // Sanity check before testing started keepalive.
+ ka = mCm.createSocketKeepalive(myNet, testSocket, myIPv4, dstIPv4, executor, callback);
+ ka.start(validKaInterval);
+ callback.expectError(SocketKeepalive.ERROR_HARDWARE_UNSUPPORTED);
+
+ // Check that a started keepalive can be stopped.
+ mWiFiNetworkAgent.setStartKeepaliveError(SocketKeepalive.SUCCESS);
+ ka = mCm.createSocketKeepalive(myNet, testSocket, myIPv4, dstIPv4, executor, callback);
+ ka.start(validKaInterval);
+ callback.expectStarted();
+ mWiFiNetworkAgent.setStopKeepaliveError(SocketKeepalive.SUCCESS);
+ ka.stop();
+ callback.expectStopped();
+
+ // Check that deleting the IP address stops the keepalive.
+ LinkProperties bogusLp = new LinkProperties(lp);
+ ka = mCm.createSocketKeepalive(myNet, testSocket, myIPv4, dstIPv4, executor, callback);
+ ka.start(validKaInterval);
+ callback.expectStarted();
+ bogusLp.removeLinkAddress(new LinkAddress(myIPv4, 25));
+ bogusLp.addLinkAddress(new LinkAddress(notMyIPv4, 25));
+ mWiFiNetworkAgent.sendLinkProperties(bogusLp);
+ callback.expectError(SocketKeepalive.ERROR_INVALID_IP_ADDRESS);
+ mWiFiNetworkAgent.sendLinkProperties(lp);
+
+ // Check that a started keepalive is stopped correctly when the network disconnects.
+ ka = mCm.createSocketKeepalive(myNet, testSocket, myIPv4, dstIPv4, executor, callback);
+ ka.start(validKaInterval);
+ callback.expectStarted();
+ mWiFiNetworkAgent.disconnect();
+ waitFor(mWiFiNetworkAgent.getDisconnectedCV());
+ callback.expectError(SocketKeepalive.ERROR_INVALID_NETWORK);
+
+ // ... and that stopping it after that has no adverse effects.
+ waitForIdle();
+ final Network myNetAlias = myNet;
+ assertNull(mCm.getNetworkCapabilities(myNetAlias));
+ ka.stop();
+
+ // Reconnect.
+ myNet = connectKeepaliveNetwork(lp);
+ mWiFiNetworkAgent.setStartKeepaliveError(SocketKeepalive.SUCCESS);
+
+ // Check things work as expected when the keepalive is stopped and the network disconnects.
+ ka = mCm.createSocketKeepalive(myNet, testSocket, myIPv4, dstIPv4, executor, callback);
+ ka.start(validKaInterval);
+ callback.expectStarted();
+ ka.stop();
+ mWiFiNetworkAgent.disconnect();
+ waitFor(mWiFiNetworkAgent.getDisconnectedCV());
+ waitForIdle();
+ callback.expectStopped();
+
+ // Reconnect.
+ myNet = connectKeepaliveNetwork(lp);
+ mWiFiNetworkAgent.setStartKeepaliveError(SocketKeepalive.SUCCESS);
+
+ // Check that keepalive slots start from 1 and increment. The first one gets slot 1.
+ mWiFiNetworkAgent.setExpectedKeepaliveSlot(1);
+ ka = mCm.createSocketKeepalive(myNet, testSocket, myIPv4, dstIPv4, executor, callback);
+ ka.start(validKaInterval);
+ callback.expectStarted();
+
+ // The second one gets slot 2.
+ mWiFiNetworkAgent.setExpectedKeepaliveSlot(2);
+ final UdpEncapsulationSocket testSocket2 = mIpSec.openUdpEncapsulationSocket(6789);
+ TestSocketKeepaliveCallback callback2 = new TestSocketKeepaliveCallback();
+ SocketKeepalive ka2 =
+ mCm.createSocketKeepalive(myNet, testSocket2, myIPv4, dstIPv4, executor, callback2);
+ ka2.start(validKaInterval);
+ callback2.expectStarted();
+
+ ka.stop();
+ callback.expectStopped();
+
+ ka2.stop();
+ callback2.expectStopped();
+ }
+
+ @Test
public void testGetCaptivePortalServerUrl() throws Exception {
String url = mCm.getCaptivePortalServerUrl();
assertEquals("http://connectivitycheck.gstatic.com/generate_204", url);
@@ -4404,8 +4628,7 @@ public class ConnectivityServiceTest {
mMockVpn.setUids(ranges);
// VPN networks do not satisfy the default request and are automatically validated
// by NetworkMonitor
- assertFalse(NetworkMonitorUtils.isValidationRequired(
- mCm.getDefaultRequest().networkCapabilities, vpnNetworkAgent.mNetworkCapabilities));
+ assertFalse(NetworkMonitorUtils.isValidationRequired(vpnNetworkAgent.mNetworkCapabilities));
vpnNetworkAgent.setNetworkValid();
vpnNetworkAgent.connect(false);
diff --git a/tests/net/java/com/android/server/net/ipmemorystore/IpMemoryStoreServiceTest.java b/tests/net/java/com/android/server/net/ipmemorystore/IpMemoryStoreServiceTest.java
index c748d0f5f743..f2ecef95b599 100644
--- a/tests/net/java/com/android/server/net/ipmemorystore/IpMemoryStoreServiceTest.java
+++ b/tests/net/java/com/android/server/net/ipmemorystore/IpMemoryStoreServiceTest.java
@@ -27,6 +27,7 @@ import static org.mockito.Mockito.doReturn;
import android.content.Context;
import android.net.ipmemorystore.Blob;
import android.net.ipmemorystore.IOnBlobRetrievedListener;
+import android.net.ipmemorystore.IOnL2KeyResponseListener;
import android.net.ipmemorystore.IOnNetworkAttributesRetrieved;
import android.net.ipmemorystore.IOnSameNetworkResponseListener;
import android.net.ipmemorystore.IOnStatusListener;
@@ -67,7 +68,14 @@ public class IpMemoryStoreServiceTest {
private static final String TEST_CLIENT_ID = "testClientId";
private static final String TEST_DATA_NAME = "testData";
- private static final String[] FAKE_KEYS = { "fakeKey1", "fakeKey2", "fakeKey3", "fakeKey4" };
+ private static final int FAKE_KEY_COUNT = 20;
+ private static final String[] FAKE_KEYS;
+ static {
+ FAKE_KEYS = new String[FAKE_KEY_COUNT];
+ for (int i = 0; i < FAKE_KEYS.length; ++i) {
+ FAKE_KEYS[i] = "fakeKey" + i;
+ }
+ }
@Mock
private Context mMockContext;
@@ -170,6 +178,25 @@ public class IpMemoryStoreServiceTest {
};
}
+ /** Helper method to make an IOnL2KeyResponseListener */
+ private interface OnL2KeyResponseListener {
+ void onL2KeyResponse(Status status, String key);
+ }
+ private IOnL2KeyResponseListener onL2KeyResponse(final OnL2KeyResponseListener functor) {
+ return new IOnL2KeyResponseListener() {
+ @Override
+ public void onL2KeyResponse(final StatusParcelable status, final String key)
+ throws RemoteException {
+ functor.onL2KeyResponse(new Status(status), key);
+ }
+
+ @Override
+ public IBinder asBinder() {
+ return null;
+ }
+ };
+ }
+
// Helper method to factorize some boilerplate
private void doLatched(final String timeoutMessage, final Consumer<CountDownLatch> functor) {
final CountDownLatch latch = new CountDownLatch(1);
@@ -195,12 +222,9 @@ public class IpMemoryStoreServiceTest {
}
@Test
- public void testNetworkAttributes() {
+ public void testNetworkAttributes() throws UnknownHostException {
final NetworkAttributes.Builder na = new NetworkAttributes.Builder();
- try {
- na.setAssignedV4Address(
- (Inet4Address) Inet4Address.getByAddress(new byte[]{1, 2, 3, 4}));
- } catch (UnknownHostException e) { /* Can't happen */ }
+ na.setAssignedV4Address((Inet4Address) Inet4Address.getByName("1.2.3.4"));
na.setGroupHint("hint1");
na.setMtu(219);
final String l2Key = FAKE_KEYS[0];
@@ -218,10 +242,8 @@ public class IpMemoryStoreServiceTest {
})));
final NetworkAttributes.Builder na2 = new NetworkAttributes.Builder();
- try {
- na.setDnsAddresses(Arrays.asList(
- new InetAddress[] {Inet6Address.getByName("0A1C:2E40:480A::1CA6")}));
- } catch (UnknownHostException e) { /* Still can't happen */ }
+ na.setDnsAddresses(Arrays.asList(
+ new InetAddress[] {Inet6Address.getByName("0A1C:2E40:480A::1CA6")}));
final NetworkAttributes attributes2 = na2.build();
storeAttributes("Did not complete storing attributes 2", l2Key, attributes2);
@@ -333,8 +355,93 @@ public class IpMemoryStoreServiceTest {
}
@Test
- public void testFindL2Key() {
- // TODO : implement this
+ public void testFindL2Key() throws UnknownHostException {
+ final NetworkAttributes.Builder na = new NetworkAttributes.Builder();
+ na.setGroupHint("hint0");
+ storeAttributes(FAKE_KEYS[0], na.build());
+
+ na.setDnsAddresses(Arrays.asList(
+ new InetAddress[] {Inet6Address.getByName("8D56:9AF1::08EE:20F1")}));
+ na.setMtu(219);
+ storeAttributes(FAKE_KEYS[1], na.build());
+ na.setMtu(null);
+ na.setAssignedV4Address((Inet4Address) Inet4Address.getByName("1.2.3.4"));
+ na.setDnsAddresses(Arrays.asList(
+ new InetAddress[] {Inet6Address.getByName("0A1C:2E40:480A::1CA6")}));
+ na.setGroupHint("hint1");
+ storeAttributes(FAKE_KEYS[2], na.build());
+ na.setMtu(219);
+ storeAttributes(FAKE_KEYS[3], na.build());
+ na.setMtu(240);
+ storeAttributes(FAKE_KEYS[4], na.build());
+ na.setAssignedV4Address((Inet4Address) Inet4Address.getByName("5.6.7.8"));
+ storeAttributes(FAKE_KEYS[5], na.build());
+
+ // Matches key 5 exactly
+ doLatched("Did not finish finding L2Key", latch ->
+ mService.findL2Key(na.build().toParcelable(), onL2KeyResponse((status, key) -> {
+ assertTrue("Retrieve network sameness not successful : " + status.resultCode,
+ status.isSuccess());
+ assertEquals(FAKE_KEYS[5], key);
+ })));
+
+ // MTU matches key 4 but v4 address matches key 5. The latter is stronger.
+ na.setMtu(240);
+ doLatched("Did not finish finding L2Key", latch ->
+ mService.findL2Key(na.build().toParcelable(), onL2KeyResponse((status, key) -> {
+ assertTrue("Retrieve network sameness not successful : " + status.resultCode,
+ status.isSuccess());
+ assertEquals(FAKE_KEYS[5], key);
+ })));
+
+ // Closest to key 3 (indeed, identical)
+ na.setAssignedV4Address((Inet4Address) Inet4Address.getByName("1.2.3.4"));
+ na.setMtu(219);
+ doLatched("Did not finish finding L2Key", latch ->
+ mService.findL2Key(na.build().toParcelable(), onL2KeyResponse((status, key) -> {
+ assertTrue("Retrieve network sameness not successful : " + status.resultCode,
+ status.isSuccess());
+ assertEquals(FAKE_KEYS[3], key);
+ })));
+
+ // Group hint alone must not be strong enough to override the rest
+ na.setGroupHint("hint0");
+ doLatched("Did not finish finding L2Key", latch ->
+ mService.findL2Key(na.build().toParcelable(), onL2KeyResponse((status, key) -> {
+ assertTrue("Retrieve network sameness not successful : " + status.resultCode,
+ status.isSuccess());
+ assertEquals(FAKE_KEYS[3], key);
+ })));
+
+ // Still closest to key 3, though confidence is lower
+ na.setGroupHint("hint1");
+ na.setDnsAddresses(null);
+ doLatched("Did not finish finding L2Key", latch ->
+ mService.findL2Key(na.build().toParcelable(), onL2KeyResponse((status, key) -> {
+ assertTrue("Retrieve network sameness not successful : " + status.resultCode,
+ status.isSuccess());
+ assertEquals(FAKE_KEYS[3], key);
+ })));
+
+ // But changing the MTU makes this closer to key 4
+ na.setMtu(240);
+ doLatched("Did not finish finding L2Key", latch ->
+ mService.findL2Key(na.build().toParcelable(), onL2KeyResponse((status, key) -> {
+ assertTrue("Retrieve network sameness not successful : " + status.resultCode,
+ status.isSuccess());
+ assertEquals(FAKE_KEYS[4], key);
+ })));
+
+ // MTU alone not strong enough to make this group-close
+ na.setGroupHint(null);
+ na.setDnsAddresses(null);
+ na.setAssignedV4Address(null);
+ doLatched("Did not finish finding L2Key", latch ->
+ mService.findL2Key(na.build().toParcelable(), onL2KeyResponse((status, key) -> {
+ assertTrue("Retrieve network sameness not successful : " + status.resultCode,
+ status.isSuccess());
+ assertNull(key);
+ })));
}
private void assertNetworksSameness(final String key1, final String key2, final int sameness) {
@@ -349,7 +456,7 @@ public class IpMemoryStoreServiceTest {
@Test
public void testIsSameNetwork() throws UnknownHostException {
final NetworkAttributes.Builder na = new NetworkAttributes.Builder();
- na.setAssignedV4Address((Inet4Address) Inet4Address.getByAddress(new byte[]{1, 2, 3, 4}));
+ na.setAssignedV4Address((Inet4Address) Inet4Address.getByName("1.2.3.4"));
na.setGroupHint("hint1");
na.setMtu(219);
na.setDnsAddresses(Arrays.asList(Inet6Address.getByName("0A1C:2E40:480A::1CA6")));
diff --git a/tests/net/jni/apf_jni.cpp b/tests/net/jni/apf_jni.cpp
deleted file mode 100644
index 4222adf9e06b..000000000000
--- a/tests/net/jni/apf_jni.cpp
+++ /dev/null
@@ -1,248 +0,0 @@
-/*
- * Copyright 2018, 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.
- */
-
-#include <nativehelper/JNIHelp.h>
-#include <nativehelper/ScopedUtfChars.h>
-#include <jni.h>
-#include <pcap.h>
-#include <stdlib.h>
-#include <string>
-#include <utils/Log.h>
-#include <vector>
-
-#include "apf_interpreter.h"
-#include "nativehelper/scoped_primitive_array.h"
-
-#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
-
-// JNI function acting as simply call-through to native APF interpreter.
-static jint com_android_server_ApfTest_apfSimulate(
- JNIEnv* env, jclass, jbyteArray jprogram, jbyteArray jpacket,
- jbyteArray jdata, jint filter_age) {
-
- ScopedByteArrayRO packet(env, jpacket);
- uint32_t packet_len = (uint32_t)packet.size();
- uint32_t program_len = env->GetArrayLength(jprogram);
- uint32_t data_len = jdata ? env->GetArrayLength(jdata) : 0;
- std::vector<uint8_t> buf(program_len + data_len, 0);
-
- env->GetByteArrayRegion(jprogram, 0, program_len, reinterpret_cast<jbyte*>(buf.data()));
- if (jdata) {
- // Merge program and data into a single buffer.
- env->GetByteArrayRegion(jdata, 0, data_len,
- reinterpret_cast<jbyte*>(buf.data() + program_len));
- }
-
- jint result =
- accept_packet(buf.data(), program_len, program_len + data_len,
- reinterpret_cast<const uint8_t*>(packet.get()), packet_len, filter_age);
-
- if (jdata) {
- env->SetByteArrayRegion(jdata, 0, data_len,
- reinterpret_cast<jbyte*>(buf.data() + program_len));
- }
-
- return result;
-}
-
-class ScopedPcap {
- public:
- explicit ScopedPcap(pcap_t* pcap) : pcap_ptr(pcap) {}
- ~ScopedPcap() {
- pcap_close(pcap_ptr);
- }
-
- pcap_t* get() const { return pcap_ptr; };
- private:
- pcap_t* const pcap_ptr;
-};
-
-class ScopedFILE {
- public:
- explicit ScopedFILE(FILE* fp) : file(fp) {}
- ~ScopedFILE() {
- fclose(file);
- }
-
- FILE* get() const { return file; };
- private:
- FILE* const file;
-};
-
-static void throwException(JNIEnv* env, const std::string& error) {
- jclass newExcCls = env->FindClass("java/lang/IllegalStateException");
- if (newExcCls == 0) {
- abort();
- return;
- }
- env->ThrowNew(newExcCls, error.c_str());
-}
-
-static jstring com_android_server_ApfTest_compileToBpf(JNIEnv* env, jclass, jstring jfilter) {
- ScopedUtfChars filter(env, jfilter);
- std::string bpf_string;
- ScopedPcap pcap(pcap_open_dead(DLT_EN10MB, 65535));
- if (pcap.get() == NULL) {
- throwException(env, "pcap_open_dead failed");
- return NULL;
- }
-
- // Compile "filter" to a BPF program
- bpf_program bpf;
- if (pcap_compile(pcap.get(), &bpf, filter.c_str(), 0, PCAP_NETMASK_UNKNOWN)) {
- throwException(env, "pcap_compile failed");
- return NULL;
- }
-
- // Translate BPF program to human-readable format
- const struct bpf_insn* insn = bpf.bf_insns;
- for (uint32_t i = 0; i < bpf.bf_len; i++) {
- bpf_string += bpf_image(insn++, i);
- bpf_string += "\n";
- }
-
- return env->NewStringUTF(bpf_string.c_str());
-}
-
-static jboolean com_android_server_ApfTest_compareBpfApf(JNIEnv* env, jclass, jstring jfilter,
- jstring jpcap_filename, jbyteArray japf_program) {
- ScopedUtfChars filter(env, jfilter);
- ScopedUtfChars pcap_filename(env, jpcap_filename);
- ScopedByteArrayRO apf_program(env, japf_program);
-
- // Open pcap file for BPF filtering
- ScopedFILE bpf_fp(fopen(pcap_filename.c_str(), "rb"));
- char pcap_error[PCAP_ERRBUF_SIZE];
- ScopedPcap bpf_pcap(pcap_fopen_offline(bpf_fp.get(), pcap_error));
- if (bpf_pcap.get() == NULL) {
- throwException(env, "pcap_fopen_offline failed: " + std::string(pcap_error));
- return false;
- }
-
- // Open pcap file for APF filtering
- ScopedFILE apf_fp(fopen(pcap_filename.c_str(), "rb"));
- ScopedPcap apf_pcap(pcap_fopen_offline(apf_fp.get(), pcap_error));
- if (apf_pcap.get() == NULL) {
- throwException(env, "pcap_fopen_offline failed: " + std::string(pcap_error));
- return false;
- }
-
- // Compile "filter" to a BPF program
- bpf_program bpf;
- if (pcap_compile(bpf_pcap.get(), &bpf, filter.c_str(), 0, PCAP_NETMASK_UNKNOWN)) {
- throwException(env, "pcap_compile failed");
- return false;
- }
-
- // Install BPF filter on bpf_pcap
- if (pcap_setfilter(bpf_pcap.get(), &bpf)) {
- throwException(env, "pcap_setfilter failed");
- return false;
- }
-
- while (1) {
- pcap_pkthdr bpf_header, apf_header;
- // Run BPF filter to the next matching packet.
- const uint8_t* bpf_packet = pcap_next(bpf_pcap.get(), &bpf_header);
-
- // Run APF filter to the next matching packet.
- const uint8_t* apf_packet;
- do {
- apf_packet = pcap_next(apf_pcap.get(), &apf_header);
- } while (apf_packet != NULL && !accept_packet(
- reinterpret_cast<uint8_t*>(const_cast<int8_t*>(apf_program.get())),
- apf_program.size(), 0 /* data_len */,
- apf_packet, apf_header.len, 0 /* filter_age */));
-
- // Make sure both filters matched the same packet.
- if (apf_packet == NULL && bpf_packet == NULL)
- break;
- if (apf_packet == NULL || bpf_packet == NULL)
- return false;
- if (apf_header.len != bpf_header.len ||
- apf_header.ts.tv_sec != bpf_header.ts.tv_sec ||
- apf_header.ts.tv_usec != bpf_header.ts.tv_usec ||
- memcmp(apf_packet, bpf_packet, apf_header.len))
- return false;
- }
- return true;
-}
-
-static jboolean com_android_server_ApfTest_dropsAllPackets(JNIEnv* env, jclass, jbyteArray jprogram,
- jbyteArray jdata, jstring jpcap_filename) {
- ScopedUtfChars pcap_filename(env, jpcap_filename);
- ScopedByteArrayRO apf_program(env, jprogram);
- uint32_t apf_program_len = (uint32_t)apf_program.size();
- uint32_t data_len = env->GetArrayLength(jdata);
- pcap_pkthdr apf_header;
- const uint8_t* apf_packet;
- char pcap_error[PCAP_ERRBUF_SIZE];
- std::vector<uint8_t> buf(apf_program_len + data_len, 0);
-
- // Merge program and data into a single buffer.
- env->GetByteArrayRegion(jprogram, 0, apf_program_len, reinterpret_cast<jbyte*>(buf.data()));
- env->GetByteArrayRegion(jdata, 0, data_len,
- reinterpret_cast<jbyte*>(buf.data() + apf_program_len));
-
- // Open pcap file
- ScopedFILE apf_fp(fopen(pcap_filename.c_str(), "rb"));
- ScopedPcap apf_pcap(pcap_fopen_offline(apf_fp.get(), pcap_error));
-
- if (apf_pcap.get() == NULL) {
- throwException(env, "pcap_fopen_offline failed: " + std::string(pcap_error));
- return false;
- }
-
- while ((apf_packet = pcap_next(apf_pcap.get(), &apf_header)) != NULL) {
- int result = accept_packet(buf.data(), apf_program_len,
- apf_program_len + data_len, apf_packet, apf_header.len, 0);
-
- // Return false once packet passes the filter
- if (result) {
- env->SetByteArrayRegion(jdata, 0, data_len,
- reinterpret_cast<jbyte*>(buf.data() + apf_program_len));
- return false;
- }
- }
-
- env->SetByteArrayRegion(jdata, 0, data_len,
- reinterpret_cast<jbyte*>(buf.data() + apf_program_len));
- return true;
-}
-
-extern "C" jint JNI_OnLoad(JavaVM* vm, void*) {
- JNIEnv *env;
- if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
- ALOGE("ERROR: GetEnv failed");
- return -1;
- }
-
- static JNINativeMethod gMethods[] = {
- { "apfSimulate", "([B[B[BI)I",
- (void*)com_android_server_ApfTest_apfSimulate },
- { "compileToBpf", "(Ljava/lang/String;)Ljava/lang/String;",
- (void*)com_android_server_ApfTest_compileToBpf },
- { "compareBpfApf", "(Ljava/lang/String;Ljava/lang/String;[B)Z",
- (void*)com_android_server_ApfTest_compareBpfApf },
- { "dropsAllPackets", "([B[BLjava/lang/String;)Z",
- (void*)com_android_server_ApfTest_dropsAllPackets },
- };
-
- jniRegisterNativeMethods(env, "android/net/apf/ApfTest",
- gMethods, ARRAY_SIZE(gMethods));
-
- return JNI_VERSION_1_6;
-}
diff --git a/tests/net/res/raw/apf.pcap b/tests/net/res/raw/apf.pcap
deleted file mode 100644
index 963165f19f73..000000000000
--- a/tests/net/res/raw/apf.pcap
+++ /dev/null
Binary files differ
diff --git a/tests/net/res/raw/apfPcap.pcap b/tests/net/res/raw/apfPcap.pcap
deleted file mode 100644
index 6f69c4add0f8..000000000000
--- a/tests/net/res/raw/apfPcap.pcap
+++ /dev/null
Binary files differ
diff --git a/tests/utils/testutils/java/com/android/test/filters/SelectTest.java b/tests/utils/testutils/java/com/android/test/filters/SelectTest.java
new file mode 100644
index 000000000000..d0350aff5ef5
--- /dev/null
+++ b/tests/utils/testutils/java/com/android/test/filters/SelectTest.java
@@ -0,0 +1,338 @@
+/*
+ * Copyright (C) 2019 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.test.filters;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import org.junit.runner.Description;
+import org.junit.runner.manipulation.Filter;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringJoiner;
+
+/**
+ * JUnit filter to select tests.
+ *
+ * <p>This filter selects tests specified by package name, class name, and method name. With this
+ * filter, the package and the class options of AndroidJUnitRunner can be superseded. Also the
+ * restriction that prevents using the package and the class options can be mitigated.
+ *
+ * <p><b>Select out tests from Java packages:</b> this option supersedes {@code -e package} option.
+ * <pre>
+ * adb shell am instrument -w \
+ * -e filter com.android.test.filters.SelectTest \
+ * -e selectTest package1.,package2. \
+ * com.tests.pkg/androidx.test.runner.AndroidJUnitRunner
+ * </pre>
+ * Note that the ending {@code .} in package name is mandatory.
+ *
+ * <p><b>Select out test classes:</b> this option supersedes {@code -e class} option.
+ * <pre>
+ * adb shell am instrument -w \
+ * -e filter com.android.test.filters.SelectTest \
+ * -e selectTest package1.ClassA,package2.ClassB \
+ * com.tests.pkg/androidx.test.runner.AndroidJUnitRunner
+ * </pre>
+ *
+ * <p><b>Select out test methods from Java classes:</b>
+ * <pre>
+ * adb shell am instrument -w \
+ * -e filter com.android.test.filters.SelectTest \
+ * -e selectTest package1.ClassA#methodX,package2.ClassB#methodY \
+ * com.tests.pkg/androidx.test.runner.AndroidJUnitRunner
+ * </pre>
+ *
+ * Those options can be used simultaneously. For example
+ * <pre>
+ * adb shell am instrument -w \
+ * -e filter com.android.test.filters.SelectTest \
+ * -e selectTest package1.,package2.classA,package3.ClassB#methodZ \
+ * com.tests.pkg/androidx.test.runner.AndroidJUnitRunner
+ * </pre>
+ * will select out all tests in package1, all tests in classA, and ClassB#methodZ test.
+ *
+ * <p>Note that when this option is specified with either {@code -e package} or {@code -e class}
+ * option, filtering behaves as logically conjunction. Other options, such as {@code -e notPackage},
+ * {@code -e notClass}, {@code -e annotation}, and {@code -e notAnnotation}, should work as expected
+ * with this SelectTest option.
+ *
+ * <p>When specified with {@code -e selectTest_verbose true} option, {@link SelectTest} verbosely
+ * logs to logcat while parsing {@code -e selectTest} option.
+ */
+public class SelectTest extends Filter {
+
+ private static final String TAG = SelectTest.class.getSimpleName();
+
+ @VisibleForTesting
+ static final String OPTION_SELECT_TEST = "selectTest";
+ @VisibleForTesting
+ static final String OPTION_SELECT_TEST_VERBOSE = OPTION_SELECT_TEST + "_verbose";
+
+ private static final String ARGUMENT_ITEM_SEPARATOR = ",";
+ private static final String PACKAGE_NAME_SEPARATOR = ".";
+ private static final String METHOD_SEPARATOR = "#";
+
+ @Nullable
+ private final PackageSet mPackageSet;
+
+ /**
+ * Construct {@link SelectTest} filter from instrumentation arguments in {@link Bundle}.
+ *
+ * @param testArgs instrumentation test arguments.
+ */
+ public SelectTest(@NonNull Bundle testArgs) {
+ mPackageSet = parseSelectTest(testArgs);
+ }
+
+ @Override
+ public boolean shouldRun(Description description) {
+ if (mPackageSet == null) {
+ // Accept all tests because this filter is disabled.
+ return true;
+ }
+ String testClassName = description.getClassName();
+ String testMethodName = description.getMethodName();
+ return mPackageSet.accept(testClassName, testMethodName);
+ }
+
+ @Override
+ public String describe() {
+ return OPTION_SELECT_TEST + "=" + mPackageSet;
+ }
+
+ /**
+ * Create {@link #OPTION_SELECT_TEST} argument and add it to {@code testArgs}.
+ *
+ * <p>This method is intended to be used at constructor of extended {@link Filter} class.
+ *
+ * @param testArgs instrumentation test arguments.
+ * @param selectTests array of class name to be selected to run.
+ * @return modified instrumentation test arguments.
+ */
+ @NonNull
+ protected static Bundle addSelectTest(
+ @NonNull Bundle testArgs, @NonNull String... selectTests) {
+ if (selectTests.length == 0) {
+ return testArgs;
+ }
+ testArgs.putString(OPTION_SELECT_TEST, join(Arrays.asList(selectTests)));
+ return testArgs;
+ }
+
+ /**
+ * Parse {@code -e selectTest} argument.
+ * @param testArgs instrumentation test arguments.
+ * @return {@link PackageSet} that will filter tests. Returns {@code null} when no
+ * {@code -e selectTest} option is specified, thus this filter gets disabled.
+ */
+ @Nullable
+ private static PackageSet parseSelectTest(Bundle testArgs) {
+ final String selectTestArgs = testArgs.getString(OPTION_SELECT_TEST);
+ if (selectTestArgs == null) {
+ Log.w(TAG, "Disabled because no " + OPTION_SELECT_TEST + " option specified");
+ return null;
+ }
+
+ final boolean verbose = new Boolean(testArgs.getString(OPTION_SELECT_TEST_VERBOSE));
+ final PackageSet packageSet = new PackageSet(verbose);
+ for (String selectTestArg : selectTestArgs.split(ARGUMENT_ITEM_SEPARATOR)) {
+ packageSet.add(selectTestArg);
+ }
+ return packageSet;
+ }
+
+ private static String getPackageName(String selectTestArg) {
+ int endPackagePos = selectTestArg.lastIndexOf(PACKAGE_NAME_SEPARATOR);
+ return (endPackagePos < 0) ? "" : selectTestArg.substring(0, endPackagePos);
+ }
+
+ @Nullable
+ private static String getClassName(String selectTestArg) {
+ if (selectTestArg.endsWith(PACKAGE_NAME_SEPARATOR)) {
+ return null;
+ }
+ int methodSepPos = selectTestArg.indexOf(METHOD_SEPARATOR);
+ return (methodSepPos < 0) ? selectTestArg : selectTestArg.substring(0, methodSepPos);
+ }
+
+ @Nullable
+ private static String getMethodName(String selectTestArg) {
+ int methodSepPos = selectTestArg.indexOf(METHOD_SEPARATOR);
+ return (methodSepPos < 0) ? null : selectTestArg.substring(methodSepPos + 1);
+ }
+
+ /** Package level filter */
+ private static class PackageSet {
+ private final boolean mVerbose;
+ /**
+ * Java package name to {@link ClassSet} map. To represent package filtering, a map value
+ * can be {@code null}.
+ */
+ private final Map<String, ClassSet> mClassSetMap = new LinkedHashMap<>();
+
+ PackageSet(boolean verbose) {
+ mVerbose = verbose;
+ }
+
+ void add(final String selectTestArg) {
+ final String packageName = getPackageName(selectTestArg);
+ final String className = getClassName(selectTestArg);
+
+ if (className == null) {
+ ClassSet classSet = mClassSetMap.put(packageName, null); // package filtering.
+ if (mVerbose) {
+ logging("Select package " + selectTestArg, classSet != null,
+ "; supersede " + classSet);
+ }
+ return;
+ }
+
+ ClassSet classSet = mClassSetMap.get(packageName);
+ if (classSet == null) {
+ if (mClassSetMap.containsKey(packageName)) {
+ if (mVerbose) {
+ logging("Select package " + packageName + PACKAGE_NAME_SEPARATOR, true,
+ " ignore " + selectTestArg);
+ }
+ return;
+ }
+ classSet = new ClassSet(mVerbose);
+ mClassSetMap.put(packageName, classSet);
+ }
+ classSet.add(selectTestArg);
+ }
+
+ boolean accept(String className, @Nullable String methodName) {
+ String packageName = getPackageName(className);
+ if (!mClassSetMap.containsKey(packageName)) {
+ return false;
+ }
+ ClassSet classSet = mClassSetMap.get(packageName);
+ return classSet == null || classSet.accept(className, methodName);
+ }
+
+ @Override
+ public String toString() {
+ StringJoiner joiner = new StringJoiner(ARGUMENT_ITEM_SEPARATOR);
+ for (String packageName : mClassSetMap.keySet()) {
+ ClassSet classSet = mClassSetMap.get(packageName);
+ joiner.add(classSet == null
+ ? packageName + PACKAGE_NAME_SEPARATOR : classSet.toString());
+ }
+ return joiner.toString();
+ }
+ }
+
+ /** Class level filter */
+ private static class ClassSet {
+ private final boolean mVerbose;
+ /**
+ * Java class name to set of method names map. To represent class filtering, a map value
+ * can be {@code null}.
+ */
+ private final Map<String, Set<String>> mMethodSetMap = new LinkedHashMap<>();
+
+ ClassSet(boolean verbose) {
+ mVerbose = verbose;
+ }
+
+ void add(String selectTestArg) {
+ final String className = getClassName(selectTestArg);
+ final String methodName = getMethodName(selectTestArg);
+
+ if (methodName == null) {
+ Set<String> methodSet = mMethodSetMap.put(className, null); // class filtering.
+ if (mVerbose) {
+ logging("Select class " + selectTestArg, methodSet != null,
+ "; supersede " + toString(className, methodSet));
+ }
+ return;
+ }
+
+ Set<String> methodSet = mMethodSetMap.get(className);
+ if (methodSet == null) {
+ if (mMethodSetMap.containsKey(className)) {
+ if (mVerbose) {
+ logging("Select class " + className, true, "; ignore " + selectTestArg);
+ }
+ return;
+ }
+ methodSet = new LinkedHashSet<>();
+ mMethodSetMap.put(className, methodSet);
+ }
+
+ methodSet.add(methodName);
+ if (mVerbose) {
+ logging("Select method " + selectTestArg, false, null);
+ }
+ }
+
+ boolean accept(String className, @Nullable String methodName) {
+ if (!mMethodSetMap.containsKey(className)) {
+ return false;
+ }
+ Set<String> methodSet = mMethodSetMap.get(className);
+ return methodName == null || methodSet == null || methodSet.contains(methodName);
+ }
+
+ @Override
+ public String toString() {
+ StringJoiner joiner = new StringJoiner(ARGUMENT_ITEM_SEPARATOR);
+ for (String className : mMethodSetMap.keySet()) {
+ joiner.add(toString(className, mMethodSetMap.get(className)));
+ }
+ return joiner.toString();
+ }
+
+ private static String toString(String className, @Nullable Set<String> methodSet) {
+ if (methodSet == null) {
+ return className;
+ }
+ StringJoiner joiner = new StringJoiner(ARGUMENT_ITEM_SEPARATOR);
+ for (String methodName : methodSet) {
+ joiner.add(className + METHOD_SEPARATOR + methodName);
+ }
+ return joiner.toString();
+ }
+ }
+
+ private static void logging(String infoLog, boolean isWarning, String warningLog) {
+ if (isWarning) {
+ Log.w(TAG, infoLog + warningLog);
+ } else {
+ Log.i(TAG, infoLog);
+ }
+ }
+
+ private static String join(Collection<String> list) {
+ StringJoiner joiner = new StringJoiner(ARGUMENT_ITEM_SEPARATOR);
+ for (String text : list) {
+ joiner.add(text);
+ }
+ return joiner.toString();
+ }
+}
diff --git a/tests/utils/testutils/java/com/android/test/filters/SelectTestTests.java b/tests/utils/testutils/java/com/android/test/filters/SelectTestTests.java
new file mode 100644
index 000000000000..163b00abafcd
--- /dev/null
+++ b/tests/utils/testutils/java/com/android/test/filters/SelectTestTests.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2019 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.test.filters;
+
+import static com.android.test.filters.SelectTest.OPTION_SELECT_TEST;
+import static com.android.test.filters.SelectTest.OPTION_SELECT_TEST_VERBOSE;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Bundle;
+import android.util.ArraySet;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.Description;
+import org.junit.runner.manipulation.Filter;
+
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.Set;
+import java.util.StringJoiner;
+
+public class SelectTestTests {
+
+ private static final String PACKAGE_A = "packageA.";
+ private static final String PACKAGE_B = "packageB.";
+ private static final String PACKAGE_C = "packageC.";
+ private static final String CLASS_A1 = PACKAGE_A + "Class1";
+ private static final String CLASS_A2 = PACKAGE_A + "Class2";
+ private static final String CLASS_B3 = PACKAGE_B + "Class3";
+ private static final String CLASS_B4 = PACKAGE_B + "Class4";
+ private static final String CLASS_C5 = PACKAGE_C + "Class5";
+ private static final String CLASS_C6 = PACKAGE_C + "Class6";
+ private static final String METHOD_A1K = CLASS_A1 + "#methodK";
+ private static final String METHOD_A1L = CLASS_A1 + "#methodL";
+ private static final String METHOD_A2M = CLASS_A2 + "#methodM";
+ private static final String METHOD_A2N = CLASS_A2 + "#methodN";
+ private static final String METHOD_B3P = CLASS_B3 + "#methodP";
+ private static final String METHOD_B3Q = CLASS_B3 + "#methodQ";
+ private static final String METHOD_B4R = CLASS_B4 + "#methodR";
+ private static final String METHOD_B4S = CLASS_B4 + "#methodS";
+ private static final String METHOD_C5W = CLASS_C5 + "#methodW";
+ private static final String METHOD_C5X = CLASS_C5 + "#methodX";
+ private static final String METHOD_C6Y = CLASS_C6 + "#methodY";
+ private static final String METHOD_C6Z = CLASS_C6 + "#methodZ";
+
+ private static final Set<Description> TEST_METHOD_A1K = methodTest(METHOD_A1K);
+ private static final Set<Description> TEST_METHOD_A1L = methodTest(METHOD_A1L);
+ private static final Set<Description> TEST_METHOD_A2M = methodTest(METHOD_A2M);
+ private static final Set<Description> TEST_METHOD_A2N = methodTest(METHOD_A2N);
+ private static final Set<Description> TEST_METHOD_B3P = methodTest(METHOD_B3P);
+ private static final Set<Description> TEST_METHOD_B3Q = methodTest(METHOD_B3Q);
+ private static final Set<Description> TEST_METHOD_B4R = methodTest(METHOD_B4R);
+ private static final Set<Description> TEST_METHOD_B4S = methodTest(METHOD_B4S);
+ private static final Set<Description> TEST_METHOD_C5W = methodTest(METHOD_C5W);
+ private static final Set<Description> TEST_METHOD_C5X = methodTest(METHOD_C5X);
+ private static final Set<Description> TEST_METHOD_C6Y = methodTest(METHOD_C6Y);
+ private static final Set<Description> TEST_METHOD_C6Z = methodTest(METHOD_C6Z);
+ private static final Set<Description> TEST_CLASS_A1 = merge(TEST_METHOD_A1K, TEST_METHOD_A1L);
+ private static final Set<Description> TEST_CLASS_A2 = merge(TEST_METHOD_A2M, TEST_METHOD_A2N);
+ private static final Set<Description> TEST_CLASS_B3 = merge(TEST_METHOD_B3P, TEST_METHOD_B3Q);
+ private static final Set<Description> TEST_CLASS_B4 = merge(TEST_METHOD_B4R, TEST_METHOD_B4S);
+ private static final Set<Description> TEST_CLASS_C5 = merge(TEST_METHOD_C5W, TEST_METHOD_C5X);
+ private static final Set<Description> TEST_CLASS_C6 = merge(TEST_METHOD_C6Y, TEST_METHOD_C6Z);
+ private static final Set<Description> TEST_PACKAGE_A = merge(TEST_CLASS_A1, TEST_CLASS_A2);
+ private static final Set<Description> TEST_PACKAGE_B = merge(TEST_CLASS_B3, TEST_CLASS_B4);
+ private static final Set<Description> TEST_PACKAGE_C = merge(TEST_CLASS_C5, TEST_CLASS_C6);
+ private static final Set<Description> TEST_ALL =
+ merge(TEST_PACKAGE_A, TEST_PACKAGE_B, TEST_PACKAGE_C);
+
+ private SelectTestBuilder mBuilder;
+
+ @Before
+ public void setUp() {
+ mBuilder = new SelectTestBuilder();
+ }
+
+ private static class SelectTestBuilder {
+ private final Bundle mTestArgs = new Bundle();
+
+ Filter build() {
+ mTestArgs.putString(OPTION_SELECT_TEST_VERBOSE, Boolean.TRUE.toString());
+ return new SelectTest(mTestArgs);
+ }
+
+ SelectTestBuilder withSelectTest(String... selectTestArgs) {
+ putTestOption(OPTION_SELECT_TEST, selectTestArgs);
+ return this;
+ }
+
+ private void putTestOption(String option, String... args) {
+ if (args.length > 0) {
+ StringJoiner joiner = new StringJoiner(",");
+ for (String arg : args) {
+ joiner.add(arg);
+ }
+ mTestArgs.putString(option, joiner.toString());
+ }
+ }
+ }
+
+ private static Set<Description> methodTest(String testName) {
+ int methodSep = testName.indexOf("#");
+ String className = testName.substring(0, methodSep);
+ String methodName = testName.substring(methodSep + 1);
+ final Set<Description> tests = new ArraySet<>();
+ tests.add(Description.createSuiteDescription(className));
+ tests.add(Description.createTestDescription(className, methodName));
+ return Collections.unmodifiableSet(tests);
+ }
+
+ @SafeVarargs
+ private static Set<Description> merge(Set<Description>... testSpecs) {
+ final Set<Description> merged = new LinkedHashSet<>();
+ for (Set<Description> testSet : testSpecs) {
+ merged.addAll(testSet);
+ }
+ return Collections.unmodifiableSet(merged);
+ }
+
+ @SafeVarargs
+ private static void acceptTests(Filter filter, Set<Description>... testSpecs) {
+ final Set<Description> accepts = merge(testSpecs);
+ for (Description test : TEST_ALL) {
+ if (accepts.contains(test)) {
+ assertTrue("accept " + test, filter.shouldRun(test));
+ } else {
+ assertFalse("reject " + test, filter.shouldRun(test));
+ }
+ }
+ }
+
+ @Test
+ public void testFilterDisabled() {
+ final Filter filter = mBuilder.build();
+ acceptTests(filter, TEST_ALL);
+ }
+
+ @Test
+ public void testSelectPackage() {
+ final Filter filter = mBuilder.withSelectTest(PACKAGE_A, PACKAGE_B).build();
+ acceptTests(filter, TEST_PACKAGE_A, TEST_PACKAGE_B);
+ }
+
+ @Test
+ public void testSelectClass() {
+ final Filter filter = mBuilder.withSelectTest(CLASS_A1, CLASS_A2, CLASS_B3).build();
+ acceptTests(filter, TEST_CLASS_A1, TEST_CLASS_A2, TEST_CLASS_B3);
+ }
+
+ @Test
+ public void testSelectMethod() {
+ final Filter filter = mBuilder
+ .withSelectTest(METHOD_A1K, METHOD_A2M, METHOD_A2N, METHOD_B3P).build();
+ acceptTests(filter, TEST_METHOD_A1K, TEST_METHOD_A2M, TEST_METHOD_A2N, TEST_METHOD_B3P);
+ }
+
+ @Test
+ public void testSelectClassAndPackage() {
+ final Filter filter = mBuilder.withSelectTest(CLASS_A1, PACKAGE_B, CLASS_C5).build();
+ acceptTests(filter, TEST_CLASS_A1, TEST_PACKAGE_B, TEST_CLASS_C5);
+ }
+
+ @Test
+ public void testSelectMethodAndPackage() {
+ final Filter filter = mBuilder.withSelectTest(METHOD_A1K, PACKAGE_B, METHOD_C5W).build();
+ acceptTests(filter, TEST_METHOD_A1K, TEST_PACKAGE_B, TEST_METHOD_C5W);
+ }
+
+ @Test
+ public void testSelectMethodAndClass() {
+ final Filter filter = mBuilder.withSelectTest(METHOD_A1K, CLASS_C5, METHOD_B3P).build();
+ acceptTests(filter, TEST_METHOD_A1K, TEST_CLASS_C5, TEST_METHOD_B3P);
+ }
+
+ @Test
+ public void testSelectClassAndSamePackage() {
+ final Filter filter = mBuilder.withSelectTest(
+ CLASS_A1, PACKAGE_A, CLASS_B3, PACKAGE_C, CLASS_C5).build();
+ acceptTests(filter, TEST_PACKAGE_A, TEST_CLASS_B3, TEST_PACKAGE_C);
+ }
+
+ @Test
+ public void testSelectMethodAndSameClass() {
+ final Filter filter = mBuilder.withSelectTest(
+ METHOD_A1K, METHOD_A2M, CLASS_A1, CLASS_B3, METHOD_B3P, METHOD_B4R).build();
+ acceptTests(filter, TEST_CLASS_A1, TEST_METHOD_A2M, TEST_CLASS_B3, TEST_METHOD_B4R);
+ }
+
+ @Test
+ public void testSelectMethodAndSamePackage() {
+ final Filter filter = mBuilder.withSelectTest(
+ METHOD_A1K, METHOD_A1L, METHOD_A2M, PACKAGE_A,
+ PACKAGE_C, METHOD_C5W, METHOD_C5X, METHOD_C6Y).build();
+ acceptTests(filter, TEST_PACKAGE_A, TEST_PACKAGE_C);
+ }
+
+ @Test
+ public void testSelectMethodAndClassAndPackage() {
+ final Filter filter = mBuilder.withSelectTest(
+ METHOD_A1K, CLASS_A1, METHOD_A1L, METHOD_A2M, PACKAGE_A,
+ PACKAGE_B, METHOD_B3Q, CLASS_B3, METHOD_B4R, METHOD_B3P).build();
+ acceptTests(filter, TEST_PACKAGE_A, TEST_PACKAGE_B);
+ }
+}