diff options
Diffstat (limited to 'tests/src')
15 files changed, 0 insertions, 7997 deletions
diff --git a/tests/src/android/net/apf/ApfTest.java b/tests/src/android/net/apf/ApfTest.java deleted file mode 100644 index 8f2b968..0000000 --- a/tests/src/android/net/apf/ApfTest.java +++ /dev/null @@ -1,2110 +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.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_TCP; -import static android.system.OsConstants.IPPROTO_UDP; -import static android.system.OsConstants.SOCK_STREAM; - -import static com.android.internal.util.BitUtils.bytesToBEInt; -import static com.android.server.util.NetworkStackConstants.ICMPV6_ECHO_REQUEST_TYPE; - -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.mock; -import static org.mockito.Mockito.verify; - -import android.content.Context; -import android.net.LinkAddress; -import android.net.LinkProperties; -import android.net.NattKeepalivePacketDataParcelable; -import android.net.TcpKeepalivePacketDataParcelable; -import android.net.apf.ApfFilter.ApfConfiguration; -import android.net.apf.ApfGenerator.IllegalInstructionException; -import android.net.apf.ApfGenerator.Register; -import android.net.ip.IIpClientCallbacks; -import android.net.ip.IpClient.IpClientCallbacksWrapper; -import android.net.metrics.IpConnectivityLog; -import android.net.metrics.RaEvent; -import android.net.util.InterfaceParams; -import android.net.util.SharedLog; -import android.os.ConditionVariable; -import android.os.Parcelable; -import android.os.SystemClock; -import android.system.ErrnoException; -import android.system.Os; -import android.text.format.DateUtils; -import android.util.Log; - -import androidx.test.InstrumentationRegistry; -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.internal.util.HexDump; -import com.android.server.networkstack.tests.R; -import com.android.server.util.NetworkStackConstants; - -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("networkstacktestsjni"); - } - - 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 IpClientCallbacksWrapper { - private final ConditionVariable mGotApfProgram = new ConditionVariable(); - private byte[] mLastApfProgram; - - MockIpClientCallback() { - super(mock(IIpClientCallbacks.class), mock(SharedLog.class)); - } - - @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, - IpClientCallbacksWrapper 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_HEADER_LEN = 20; - private static final int IPV4_VERSION_IHL_OFFSET = ETH_HEADER_LEN + 0; - private static final int IPV4_TOTAL_LENGTH_OFFSET = ETH_HEADER_LEN + 2; - private static final int IPV4_PROTOCOL_OFFSET = ETH_HEADER_LEN + 9; - private static final int IPV4_SRC_ADDR_OFFSET = ETH_HEADER_LEN + 12; - private static final int IPV4_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 16; - - private static final int IPV4_TCP_HEADER_LEN = 20; - private static final int IPV4_TCP_HEADER_OFFSET = ETH_HEADER_LEN + IPV4_HEADER_LEN; - private static final int IPV4_TCP_SRC_PORT_OFFSET = IPV4_TCP_HEADER_OFFSET + 0; - private static final int IPV4_TCP_DEST_PORT_OFFSET = IPV4_TCP_HEADER_OFFSET + 2; - private static final int IPV4_TCP_SEQ_NUM_OFFSET = IPV4_TCP_HEADER_OFFSET + 4; - private static final int IPV4_TCP_ACK_NUM_OFFSET = IPV4_TCP_HEADER_OFFSET + 8; - private static final int IPV4_TCP_HEADER_LENGTH_OFFSET = IPV4_TCP_HEADER_OFFSET + 12; - private static final int IPV4_TCP_HEADER_FLAG_OFFSET = IPV4_TCP_HEADER_OFFSET + 13; - - private static final int IPV4_UDP_HEADER_OFFSET = ETH_HEADER_LEN + IPV4_HEADER_LEN;; - private static final int IPV4_UDP_SRC_PORT_OFFSET = IPV4_UDP_HEADER_OFFSET + 0; - private static final int IPV4_UDP_DEST_PORT_OFFSET = IPV4_UDP_HEADER_OFFSET + 2; - private static final int IPV4_UDP_LENGTH_OFFSET = IPV4_UDP_HEADER_OFFSET + 4; - private static final int IPV4_UDP_PAYLOAD_OFFSET = IPV4_UDP_HEADER_OFFSET + 8; - private static final byte[] IPV4_BROADCAST_ADDRESS = - {(byte) 255, (byte) 255, (byte) 255, (byte) 255}; - - private static final int IPV6_HEADER_LEN = 40; - private static final int IPV6_NEXT_HEADER_OFFSET = ETH_HEADER_LEN + 6; - private static final int IPV6_SRC_ADDR_OFFSET = ETH_HEADER_LEN + 8; - private static final int IPV6_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 24; - private static final int IPV6_TCP_HEADER_OFFSET = ETH_HEADER_LEN + IPV6_HEADER_LEN; - private static final int IPV6_TCP_SRC_PORT_OFFSET = IPV6_TCP_HEADER_OFFSET + 0; - private static final int IPV6_TCP_DEST_PORT_OFFSET = IPV6_TCP_HEADER_OFFSET + 2; - private static final int IPV6_TCP_SEQ_NUM_OFFSET = IPV6_TCP_HEADER_OFFSET + 4; - private static final int IPV6_TCP_ACK_NUM_OFFSET = IPV6_TCP_HEADER_OFFSET + 8; - // 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( - IpClientCallbacksWrapper 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) NetworkStackConstants.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(); - } - - private static final byte[] IPV4_KEEPALIVE_SRC_ADDR = {10, 0, 0, 5}; - private static final byte[] IPV4_KEEPALIVE_DST_ADDR = {10, 0, 0, 6}; - private static final byte[] IPV4_ANOTHER_ADDR = {10, 0 , 0, 7}; - private static final byte[] IPV6_KEEPALIVE_SRC_ADDR = - {(byte) 0x24, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, (byte) 0xfa, (byte) 0xf1}; - private static final byte[] IPV6_KEEPALIVE_DST_ADDR = - {(byte) 0x24, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, (byte) 0xfa, (byte) 0xf2}; - private static final byte[] IPV6_ANOTHER_ADDR = - {(byte) 0x24, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, (byte) 0xfa, (byte) 0xf5}; - - @Test - public void testApfFilterKeepaliveAck() throws Exception { - final MockIpClientCallback cb = new MockIpClientCallback(); - final ApfConfiguration config = getDefaultConfig(); - config.multicastFilter = DROP_MULTICAST; - config.ieee802_3Filter = DROP_802_3_FRAMES; - final TestApfFilter apfFilter = new TestApfFilter(mContext, config, cb, mLog); - byte[] program; - final int srcPort = 12345; - final int dstPort = 54321; - final int seqNum = 2123456789; - final int ackNum = 1234567890; - final int anotherSrcPort = 23456; - final int anotherDstPort = 65432; - final int anotherSeqNum = 2123456780; - final int anotherAckNum = 1123456789; - final int slot1 = 1; - final int slot2 = 2; - final int window = 14480; - final int windowScale = 4; - - // src: 10.0.0.5, port: 12345 - // dst: 10.0.0.6, port: 54321 - InetAddress srcAddr = InetAddress.getByAddress(IPV4_KEEPALIVE_SRC_ADDR); - InetAddress dstAddr = InetAddress.getByAddress(IPV4_KEEPALIVE_DST_ADDR); - - final TcpKeepalivePacketDataParcelable parcel = new TcpKeepalivePacketDataParcelable(); - parcel.srcAddress = srcAddr.getAddress(); - parcel.srcPort = srcPort; - parcel.dstAddress = dstAddr.getAddress(); - parcel.dstPort = dstPort; - parcel.seq = seqNum; - parcel.ack = ackNum; - - apfFilter.addTcpKeepalivePacketFilter(slot1, parcel); - program = cb.getApfProgram(); - - // Verify IPv4 keepalive ack packet is dropped - // src: 10.0.0.6, port: 54321 - // dst: 10.0.0.5, port: 12345 - assertDrop(program, - ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR, - dstPort, srcPort, ackNum, seqNum + 1, 0 /* dataLength */)); - // Verify IPv4 non-keepalive ack packet from the same source address is passed - assertPass(program, - ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR, - dstPort, srcPort, ackNum + 100, seqNum, 0 /* dataLength */)); - assertPass(program, - ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR, - dstPort, srcPort, ackNum, seqNum + 1, 10 /* dataLength */)); - // Verify IPv4 packet from another address is passed - assertPass(program, - ipv4Packet(IPV4_ANOTHER_ADDR, IPV4_KEEPALIVE_SRC_ADDR, anotherSrcPort, - anotherDstPort, anotherSeqNum, anotherAckNum, 0 /* dataLength */)); - - // Remove IPv4 keepalive filter - apfFilter.removeKeepalivePacketFilter(slot1); - - try { - // src: 2404:0:0:0:0:0:faf1, port: 12345 - // dst: 2404:0:0:0:0:0:faf2, port: 54321 - srcAddr = InetAddress.getByAddress(IPV6_KEEPALIVE_SRC_ADDR); - dstAddr = InetAddress.getByAddress(IPV6_KEEPALIVE_DST_ADDR); - - final TcpKeepalivePacketDataParcelable ipv6Parcel = - new TcpKeepalivePacketDataParcelable(); - ipv6Parcel.srcAddress = srcAddr.getAddress(); - ipv6Parcel.srcPort = srcPort; - ipv6Parcel.dstAddress = dstAddr.getAddress(); - ipv6Parcel.dstPort = dstPort; - ipv6Parcel.seq = seqNum; - ipv6Parcel.ack = ackNum; - - apfFilter.addTcpKeepalivePacketFilter(slot1, ipv6Parcel); - program = cb.getApfProgram(); - - // Verify IPv6 keepalive ack packet is dropped - // src: 2404:0:0:0:0:0:faf2, port: 54321 - // dst: 2404:0:0:0:0:0:faf1, port: 12345 - assertDrop(program, - ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR, - dstPort, srcPort, ackNum, seqNum + 1)); - // Verify IPv6 non-keepalive ack packet from the same source address is passed - assertPass(program, - ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR, - dstPort, srcPort, ackNum + 100, seqNum)); - // Verify IPv6 packet from another address is passed - assertPass(program, - ipv6Packet(IPV6_ANOTHER_ADDR, IPV6_KEEPALIVE_SRC_ADDR, anotherSrcPort, - anotherDstPort, anotherSeqNum, anotherAckNum)); - - // Remove IPv6 keepalive filter - apfFilter.removeKeepalivePacketFilter(slot1); - - // Verify multiple filters - apfFilter.addTcpKeepalivePacketFilter(slot1, parcel); - apfFilter.addTcpKeepalivePacketFilter(slot2, ipv6Parcel); - program = cb.getApfProgram(); - - // Verify IPv4 keepalive ack packet is dropped - // src: 10.0.0.6, port: 54321 - // dst: 10.0.0.5, port: 12345 - assertDrop(program, - ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR, - dstPort, srcPort, ackNum, seqNum + 1, 0 /* dataLength */)); - // Verify IPv4 non-keepalive ack packet from the same source address is passed - assertPass(program, - ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR, - dstPort, srcPort, ackNum + 100, seqNum, 0 /* dataLength */)); - // Verify IPv4 packet from another address is passed - assertPass(program, - ipv4Packet(IPV4_ANOTHER_ADDR, IPV4_KEEPALIVE_SRC_ADDR, anotherSrcPort, - anotherDstPort, anotherSeqNum, anotherAckNum, 0 /* dataLength */)); - - // Verify IPv6 keepalive ack packet is dropped - // src: 2404:0:0:0:0:0:faf2, port: 54321 - // dst: 2404:0:0:0:0:0:faf1, port: 12345 - assertDrop(program, - ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR, - dstPort, srcPort, ackNum, seqNum + 1)); - // Verify IPv6 non-keepalive ack packet from the same source address is passed - assertPass(program, - ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR, - dstPort, srcPort, ackNum + 100, seqNum)); - // Verify IPv6 packet from another address is passed - assertPass(program, - ipv6Packet(IPV6_ANOTHER_ADDR, IPV6_KEEPALIVE_SRC_ADDR, anotherSrcPort, - anotherDstPort, anotherSeqNum, anotherAckNum)); - - // Remove keepalive filters - apfFilter.removeKeepalivePacketFilter(slot1); - apfFilter.removeKeepalivePacketFilter(slot2); - } catch (UnsupportedOperationException e) { - // TODO: support V6 packets - } - - program = cb.getApfProgram(); - - // Verify IPv4, IPv6 packets are passed - assertPass(program, - ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR, - dstPort, srcPort, ackNum, seqNum + 1, 0 /* dataLength */)); - assertPass(program, - ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR, - dstPort, srcPort, ackNum, seqNum + 1)); - assertPass(program, - ipv4Packet(IPV4_ANOTHER_ADDR, IPV4_KEEPALIVE_SRC_ADDR, srcPort, - dstPort, anotherSeqNum, anotherAckNum, 0 /* dataLength */)); - assertPass(program, - ipv6Packet(IPV6_ANOTHER_ADDR, IPV6_KEEPALIVE_SRC_ADDR, srcPort, - dstPort, anotherSeqNum, anotherAckNum)); - - apfFilter.shutdown(); - } - - private static byte[] ipv4Packet(byte[] sip, byte[] dip, int sport, - int dport, int seq, int ack, int dataLength) { - final int totalLength = dataLength + IPV4_HEADER_LEN + IPV4_TCP_HEADER_LEN; - - ByteBuffer packet = ByteBuffer.wrap(new byte[totalLength + ETH_HEADER_LEN]); - - // ether type - packet.putShort(ETH_ETHERTYPE_OFFSET, (short) ETH_P_IP); - - // IPv4 header - packet.put(IPV4_VERSION_IHL_OFFSET, (byte) 0x45); - packet.putShort(IPV4_TOTAL_LENGTH_OFFSET, (short) totalLength); - packet.put(IPV4_PROTOCOL_OFFSET, (byte) IPPROTO_TCP); - put(packet, IPV4_SRC_ADDR_OFFSET, sip); - put(packet, IPV4_DEST_ADDR_OFFSET, dip); - packet.putShort(IPV4_TCP_SRC_PORT_OFFSET, (short) sport); - packet.putShort(IPV4_TCP_DEST_PORT_OFFSET, (short) dport); - packet.putInt(IPV4_TCP_SEQ_NUM_OFFSET, seq); - packet.putInt(IPV4_TCP_ACK_NUM_OFFSET, ack); - - // TCP header length 5(20 bytes), reserved 3 bits, NS=0 - packet.put(IPV4_TCP_HEADER_LENGTH_OFFSET, (byte) 0x50); - // TCP flags: ACK set - packet.put(IPV4_TCP_HEADER_FLAG_OFFSET, (byte) 0x10); - return packet.array(); - } - - private static byte[] ipv6Packet(byte[] sip, byte[] tip, int sport, - int dport, int seq, int ack) { - ByteBuffer packet = ByteBuffer.wrap(new byte[100]); - packet.putShort(ETH_ETHERTYPE_OFFSET, (short) ETH_P_IPV6); - put(packet, IPV6_SRC_ADDR_OFFSET, sip); - put(packet, IPV6_DEST_ADDR_OFFSET, tip); - packet.putShort(IPV6_TCP_SRC_PORT_OFFSET, (short) sport); - packet.putShort(IPV6_TCP_DEST_PORT_OFFSET, (short) dport); - packet.putInt(IPV6_TCP_SEQ_NUM_OFFSET, seq); - packet.putInt(IPV6_TCP_ACK_NUM_OFFSET, ack); - return packet.array(); - } - - @Test - public void testApfFilterNattKeepalivePacket() throws Exception { - final MockIpClientCallback cb = new MockIpClientCallback(); - final ApfConfiguration config = getDefaultConfig(); - config.multicastFilter = DROP_MULTICAST; - config.ieee802_3Filter = DROP_802_3_FRAMES; - final TestApfFilter apfFilter = new TestApfFilter(mContext, config, cb, mLog); - byte[] program; - final int srcPort = 1024; - final int dstPort = 4500; - final int slot1 = 1; - // NAT-T keepalive - final byte[] kaPayload = {(byte) 0xff}; - final byte[] nonKaPayload = {(byte) 0xfe}; - - // src: 10.0.0.5, port: 1024 - // dst: 10.0.0.6, port: 4500 - InetAddress srcAddr = InetAddress.getByAddress(IPV4_KEEPALIVE_SRC_ADDR); - InetAddress dstAddr = InetAddress.getByAddress(IPV4_KEEPALIVE_DST_ADDR); - - final NattKeepalivePacketDataParcelable parcel = new NattKeepalivePacketDataParcelable(); - parcel.srcAddress = srcAddr.getAddress(); - parcel.srcPort = srcPort; - parcel.dstAddress = dstAddr.getAddress(); - parcel.dstPort = dstPort; - - apfFilter.addNattKeepalivePacketFilter(slot1, parcel); - program = cb.getApfProgram(); - - // Verify IPv4 keepalive packet is dropped - // src: 10.0.0.6, port: 4500 - // dst: 10.0.0.5, port: 1024 - byte[] pkt = ipv4UdpPacket(IPV4_KEEPALIVE_DST_ADDR, - IPV4_KEEPALIVE_SRC_ADDR, dstPort, srcPort, 1 /* dataLength */); - System.arraycopy(kaPayload, 0, pkt, IPV4_UDP_PAYLOAD_OFFSET, kaPayload.length); - assertDrop(program, pkt); - - // Verify a packet with payload length 1 byte but it is not 0xff will pass the filter. - System.arraycopy(nonKaPayload, 0, pkt, IPV4_UDP_PAYLOAD_OFFSET, nonKaPayload.length); - assertPass(program, pkt); - - // Verify IPv4 non-keepalive response packet from the same source address is passed - assertPass(program, - ipv4UdpPacket(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR, - dstPort, srcPort, 10 /* dataLength */)); - - // Verify IPv4 non-keepalive response packet from other source address is passed - assertPass(program, - ipv4UdpPacket(IPV4_ANOTHER_ADDR, IPV4_KEEPALIVE_SRC_ADDR, - dstPort, srcPort, 10 /* dataLength */)); - - apfFilter.removeKeepalivePacketFilter(slot1); - apfFilter.shutdown(); - } - - private static byte[] ipv4UdpPacket(byte[] sip, byte[] dip, int sport, - int dport, int dataLength) { - final int totalLength = dataLength + IPV4_HEADER_LEN + UDP_HEADER_LEN; - final int udpLength = UDP_HEADER_LEN + dataLength; - ByteBuffer packet = ByteBuffer.wrap(new byte[totalLength + ETH_HEADER_LEN]); - - // ether type - packet.putShort(ETH_ETHERTYPE_OFFSET, (short) ETH_P_IP); - - // IPv4 header - packet.put(IPV4_VERSION_IHL_OFFSET, (byte) 0x45); - packet.putShort(IPV4_TOTAL_LENGTH_OFFSET, (short) totalLength); - packet.put(IPV4_PROTOCOL_OFFSET, (byte) IPPROTO_UDP); - put(packet, IPV4_SRC_ADDR_OFFSET, sip); - put(packet, IPV4_DEST_ADDR_OFFSET, dip); - packet.putShort(IPV4_UDP_SRC_PORT_OFFSET, (short) sport); - packet.putShort(IPV4_UDP_DEST_PORT_OFFSET, (short) dport); - packet.putShort(IPV4_UDP_LENGTH_OFFSET, (short) udpLength); - - 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/src/android/net/apf/Bpf2Apf.java b/tests/src/android/net/apf/Bpf2Apf.java deleted file mode 100644 index 5d57cde..0000000 --- a/tests/src/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/src/android/net/captiveportal/CaptivePortalProbeSpecTest.java b/tests/src/android/net/captiveportal/CaptivePortalProbeSpecTest.java deleted file mode 100644 index f948086..0000000 --- a/tests/src/android/net/captiveportal/CaptivePortalProbeSpecTest.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (C) 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. - */ - -package android.net.captiveportal; - -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertNull; -import static junit.framework.Assert.assertTrue; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.net.MalformedURLException; -import java.text.ParseException; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class CaptivePortalProbeSpecTest { - - @Test - public void testGetResult_Regex() throws MalformedURLException, ParseException { - // 2xx status or 404, with an empty (match everything) location regex - CaptivePortalProbeSpec statusRegexSpec = CaptivePortalProbeSpec.parseSpec( - "http://www.google.com@@/@@2[0-9]{2}|404@@/@@"); - - // 404, or 301/302 redirect to some HTTPS page under google.com - CaptivePortalProbeSpec redirectSpec = CaptivePortalProbeSpec.parseSpec( - "http://google.com@@/@@404|30[12]@@/@@https://([0-9a-z]+\\.)*google\\.com.*"); - - assertSuccess(statusRegexSpec.getResult(200, null)); - assertSuccess(statusRegexSpec.getResult(299, "qwer")); - assertSuccess(statusRegexSpec.getResult(404, null)); - assertSuccess(statusRegexSpec.getResult(404, "")); - - assertPortal(statusRegexSpec.getResult(300, null)); - assertPortal(statusRegexSpec.getResult(399, "qwer")); - assertPortal(statusRegexSpec.getResult(500, null)); - - assertSuccess(redirectSpec.getResult(404, null)); - assertSuccess(redirectSpec.getResult(404, "")); - assertSuccess(redirectSpec.getResult(301, "https://www.google.com")); - assertSuccess(redirectSpec.getResult(301, "https://www.google.com/test?q=3")); - assertSuccess(redirectSpec.getResult(302, "https://google.com/test?q=3")); - - assertPortal(redirectSpec.getResult(299, "https://google.com/test?q=3")); - assertPortal(redirectSpec.getResult(299, "")); - assertPortal(redirectSpec.getResult(499, null)); - assertPortal(redirectSpec.getResult(301, "http://login.portal.example.com/loginpage")); - assertPortal(redirectSpec.getResult(302, "http://www.google.com/test?q=3")); - } - - @Test(expected = ParseException.class) - public void testParseSpec_Empty() throws MalformedURLException, ParseException { - CaptivePortalProbeSpec.parseSpec(""); - } - - @Test(expected = ParseException.class) - public void testParseSpec_Null() throws MalformedURLException, ParseException { - CaptivePortalProbeSpec.parseSpec(null); - } - - @Test(expected = ParseException.class) - public void testParseSpec_MissingParts() throws MalformedURLException, ParseException { - CaptivePortalProbeSpec.parseSpec("http://google.com/@@/@@123"); - } - - @Test(expected = ParseException.class) - public void testParseSpec_TooManyParts() throws MalformedURLException, ParseException { - CaptivePortalProbeSpec.parseSpec("http://google.com/@@/@@123@@/@@456@@/@@extra"); - } - - @Test(expected = ParseException.class) - public void testParseSpec_InvalidStatusRegex() throws MalformedURLException, ParseException { - CaptivePortalProbeSpec.parseSpec("http://google.com/@@/@@unmatched(parenthesis@@/@@456"); - } - - @Test(expected = ParseException.class) - public void testParseSpec_InvalidLocationRegex() throws MalformedURLException, ParseException { - CaptivePortalProbeSpec.parseSpec("http://google.com/@@/@@123@@/@@unmatched[[]bracket"); - } - - @Test(expected = MalformedURLException.class) - public void testParseSpec_EmptyURL() throws MalformedURLException, ParseException { - CaptivePortalProbeSpec.parseSpec("@@/@@123@@/@@123"); - } - - @Test(expected = ParseException.class) - public void testParseSpec_NoParts() throws MalformedURLException, ParseException { - CaptivePortalProbeSpec.parseSpec("invalid"); - } - - @Test(expected = MalformedURLException.class) - public void testParseSpec_RegexInvalidUrl() throws MalformedURLException, ParseException { - CaptivePortalProbeSpec.parseSpec("notaurl@@/@@123@@/@@123"); - } - - @Test - public void testParseSpecOrNull_UsesSpec() { - final String specUrl = "http://google.com/probe"; - final String redirectUrl = "https://google.com/probe"; - CaptivePortalProbeSpec spec = CaptivePortalProbeSpec.parseSpecOrNull( - specUrl + "@@/@@302@@/@@" + redirectUrl); - assertEquals(specUrl, spec.getUrl().toString()); - - assertPortal(spec.getResult(302, "http://portal.example.com")); - assertSuccess(spec.getResult(302, redirectUrl)); - } - - @Test - public void testParseSpecOrNull_UsesFallback() throws MalformedURLException { - CaptivePortalProbeSpec spec = CaptivePortalProbeSpec.parseSpecOrNull(null); - assertNull(spec); - - spec = CaptivePortalProbeSpec.parseSpecOrNull(""); - assertNull(spec); - - spec = CaptivePortalProbeSpec.parseSpecOrNull("@@/@@ @@/@@ @@/@@"); - assertNull(spec); - - spec = CaptivePortalProbeSpec.parseSpecOrNull("invalid@@/@@123@@/@@456"); - assertNull(spec); - } - - @Test - public void testParseSpecOrUseStatusCodeFallback_EmptySpec() throws MalformedURLException { - CaptivePortalProbeSpec spec = CaptivePortalProbeSpec.parseSpecOrNull(""); - assertNull(spec); - } - - private void assertIsStatusSpec(CaptivePortalProbeSpec spec) { - assertSuccess(spec.getResult(204, null)); - assertSuccess(spec.getResult(204, "1234")); - - assertPortal(spec.getResult(200, null)); - assertPortal(spec.getResult(301, null)); - assertPortal(spec.getResult(302, "1234")); - assertPortal(spec.getResult(399, "")); - - assertFailed(spec.getResult(404, null)); - assertFailed(spec.getResult(500, "1234")); - } - - private void assertPortal(CaptivePortalProbeResult result) { - assertTrue(result.isPortal()); - } - - private void assertSuccess(CaptivePortalProbeResult result) { - assertTrue(result.isSuccessful()); - } - - private void assertFailed(CaptivePortalProbeResult result) { - assertTrue(result.isFailed()); - } -} diff --git a/tests/src/android/net/dhcp/DhcpLeaseRepositoryTest.java b/tests/src/android/net/dhcp/DhcpLeaseRepositoryTest.java deleted file mode 100644 index 27d7255..0000000 --- a/tests/src/android/net/dhcp/DhcpLeaseRepositoryTest.java +++ /dev/null @@ -1,544 +0,0 @@ -/* - * Copyright (C) 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. - */ - -package android.net.dhcp; - -import static android.net.InetAddresses.parseNumericAddress; -import static android.net.dhcp.DhcpLease.HOSTNAME_NONE; -import static android.net.dhcp.DhcpLeaseRepository.CLIENTID_UNSPEC; -import static android.net.dhcp.DhcpLeaseRepository.INETADDR_UNSPEC; - -import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_ANY; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.Mockito.when; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.net.IpPrefix; -import android.net.MacAddress; -import android.net.dhcp.DhcpServer.Clock; -import android.net.util.SharedLog; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import static java.lang.String.format; - -import java.net.Inet4Address; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class DhcpLeaseRepositoryTest { - private static final Inet4Address TEST_DEF_ROUTER = parseAddr4("192.168.42.247"); - private static final Inet4Address TEST_SERVER_ADDR = parseAddr4("192.168.42.241"); - private static final Inet4Address TEST_RESERVED_ADDR = parseAddr4("192.168.42.243"); - private static final MacAddress TEST_MAC_1 = MacAddress.fromBytes( - new byte[] { 5, 4, 3, 2, 1, 0 }); - private static final MacAddress TEST_MAC_2 = MacAddress.fromBytes( - new byte[] { 0, 1, 2, 3, 4, 5 }); - private static final MacAddress TEST_MAC_3 = MacAddress.fromBytes( - new byte[] { 0, 1, 2, 3, 4, 6 }); - private static final Inet4Address TEST_INETADDR_1 = parseAddr4("192.168.42.248"); - private static final Inet4Address TEST_INETADDR_2 = parseAddr4("192.168.42.249"); - private static final String TEST_HOSTNAME_1 = "hostname1"; - private static final String TEST_HOSTNAME_2 = "hostname2"; - private static final IpPrefix TEST_IP_PREFIX = new IpPrefix(TEST_SERVER_ADDR, 22); - private static final long TEST_TIME = 100L; - private static final int TEST_LEASE_TIME_MS = 3_600_000; - private static final Set<Inet4Address> TEST_EXCL_SET = - Collections.unmodifiableSet(new HashSet<>(Arrays.asList( - TEST_SERVER_ADDR, TEST_DEF_ROUTER, TEST_RESERVED_ADDR))); - - @NonNull - private SharedLog mLog; - @NonNull @Mock - private Clock mClock; - @NonNull - private DhcpLeaseRepository mRepo; - - private static Inet4Address parseAddr4(String inet4Addr) { - return (Inet4Address) parseNumericAddress(inet4Addr); - } - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - mLog = new SharedLog("DhcpLeaseRepositoryTest"); - when(mClock.elapsedRealtime()).thenReturn(TEST_TIME); - mRepo = new DhcpLeaseRepository( - TEST_IP_PREFIX, TEST_EXCL_SET, TEST_LEASE_TIME_MS, mLog, mClock); - } - - /** - * Request a number of addresses through offer/request. Useful to test address exhaustion. - * @param nAddr Number of addresses to request. - */ - private void requestAddresses(byte nAddr) throws Exception { - final HashSet<Inet4Address> addrs = new HashSet<>(); - byte[] hwAddrBytes = new byte[] { 8, 4, 3, 2, 1, 0 }; - for (byte i = 0; i < nAddr; i++) { - hwAddrBytes[5] = i; - MacAddress newMac = MacAddress.fromBytes(hwAddrBytes); - final String hostname = "host_" + i; - final DhcpLease lease = mRepo.getOffer(CLIENTID_UNSPEC, newMac, - IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, hostname); - - assertNotNull(lease); - assertEquals(newMac, lease.getHwAddr()); - assertEquals(hostname, lease.getHostname()); - assertTrue(format("Duplicate address allocated: %s in %s", lease.getNetAddr(), addrs), - addrs.add(lease.getNetAddr())); - - requestLeaseSelecting(newMac, lease.getNetAddr(), hostname); - } - } - - @Test - public void testAddressExhaustion() throws Exception { - // Use a /28 to quickly run out of addresses - mRepo.updateParams(new IpPrefix(TEST_SERVER_ADDR, 28), TEST_EXCL_SET, TEST_LEASE_TIME_MS); - - // /28 should have 16 addresses, 14 w/o the first/last, 11 w/o excluded addresses - requestAddresses((byte) 11); - - try { - mRepo.getOffer(null, TEST_MAC_2, - IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE); - fail("Should be out of addresses"); - } catch (DhcpLeaseRepository.OutOfAddressesException e) { - // Expected - } - } - - @Test - public void testUpdateParams_LeaseCleanup() throws Exception { - // Inside /28: - final Inet4Address reqAddrIn28 = parseAddr4("192.168.42.242"); - final Inet4Address declinedAddrIn28 = parseAddr4("192.168.42.245"); - - // Inside /28, but not available there (first address of the range) - final Inet4Address declinedFirstAddrIn28 = parseAddr4("192.168.42.240"); - - final DhcpLease reqAddrIn28Lease = requestLeaseSelecting(TEST_MAC_1, reqAddrIn28); - mRepo.markLeaseDeclined(declinedAddrIn28); - mRepo.markLeaseDeclined(declinedFirstAddrIn28); - - // Inside /22, but outside /28: - final Inet4Address reqAddrIn22 = parseAddr4("192.168.42.3"); - final Inet4Address declinedAddrIn22 = parseAddr4("192.168.42.4"); - - final DhcpLease reqAddrIn22Lease = requestLeaseSelecting(TEST_MAC_3, reqAddrIn22); - mRepo.markLeaseDeclined(declinedAddrIn22); - - // Address that will be reserved in the updateParams call below - final Inet4Address reservedAddr = parseAddr4("192.168.42.244"); - final DhcpLease reservedAddrLease = requestLeaseSelecting(TEST_MAC_2, reservedAddr); - - // Update from /22 to /28 and add another reserved address - Set<Inet4Address> newReserved = new HashSet<>(TEST_EXCL_SET); - newReserved.add(reservedAddr); - mRepo.updateParams(new IpPrefix(TEST_SERVER_ADDR, 28), newReserved, TEST_LEASE_TIME_MS); - - assertHasLease(reqAddrIn28Lease); - assertDeclined(declinedAddrIn28); - - assertNotDeclined(declinedFirstAddrIn28); - - assertNoLease(reqAddrIn22Lease); - assertNotDeclined(declinedAddrIn22); - - assertNoLease(reservedAddrLease); - } - - @Test - public void testGetOffer_StableAddress() throws Exception { - for (final MacAddress macAddr : new MacAddress[] { TEST_MAC_1, TEST_MAC_2, TEST_MAC_3 }) { - final DhcpLease lease = mRepo.getOffer(CLIENTID_UNSPEC, macAddr, - IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE); - - // Same lease is offered twice - final DhcpLease newLease = mRepo.getOffer(CLIENTID_UNSPEC, macAddr, - IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE); - assertEquals(lease, newLease); - } - } - - @Test - public void testUpdateParams_UsesNewPrefix() throws Exception { - final IpPrefix newPrefix = new IpPrefix(parseAddr4("192.168.123.0"), 24); - mRepo.updateParams(newPrefix, TEST_EXCL_SET, TEST_LEASE_TIME_MS); - - DhcpLease lease = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, - IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE); - assertTrue(newPrefix.contains(lease.getNetAddr())); - } - - @Test - public void testGetOffer_ExistingLease() throws Exception { - requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1, TEST_HOSTNAME_1); - - DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, - IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE); - assertEquals(TEST_INETADDR_1, offer.getNetAddr()); - assertEquals(TEST_HOSTNAME_1, offer.getHostname()); - } - - @Test - public void testGetOffer_ClientIdHasExistingLease() throws Exception { - final byte[] clientId = new byte[] { 1, 2 }; - mRepo.requestLease(clientId, TEST_MAC_1, IPV4_ADDR_ANY /* clientAddr */, - IPV4_ADDR_ANY /* relayAddr */, TEST_INETADDR_1 /* reqAddr */, false, - TEST_HOSTNAME_1); - - // Different MAC, but same clientId - DhcpLease offer = mRepo.getOffer(clientId, TEST_MAC_2, - IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE); - assertEquals(TEST_INETADDR_1, offer.getNetAddr()); - assertEquals(TEST_HOSTNAME_1, offer.getHostname()); - } - - @Test - public void testGetOffer_DifferentClientId() throws Exception { - final byte[] clientId1 = new byte[] { 1, 2 }; - final byte[] clientId2 = new byte[] { 3, 4 }; - mRepo.requestLease(clientId1, TEST_MAC_1, IPV4_ADDR_ANY /* clientAddr */, - IPV4_ADDR_ANY /* relayAddr */, TEST_INETADDR_1 /* reqAddr */, false, - TEST_HOSTNAME_1); - - // Same MAC, different client ID - DhcpLease offer = mRepo.getOffer(clientId2, TEST_MAC_1, - IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE); - // Obtains a different address - assertNotEquals(TEST_INETADDR_1, offer.getNetAddr()); - assertEquals(HOSTNAME_NONE, offer.getHostname()); - assertEquals(TEST_MAC_1, offer.getHwAddr()); - } - - @Test - public void testGetOffer_RequestedAddress() throws Exception { - DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, IPV4_ADDR_ANY /* relayAddr */, - TEST_INETADDR_1 /* reqAddr */, TEST_HOSTNAME_1); - assertEquals(TEST_INETADDR_1, offer.getNetAddr()); - assertEquals(TEST_HOSTNAME_1, offer.getHostname()); - } - - @Test - public void testGetOffer_RequestedAddressInUse() throws Exception { - requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1); - DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_2, IPV4_ADDR_ANY /* relayAddr */, - TEST_INETADDR_1 /* reqAddr */, HOSTNAME_NONE); - assertNotEquals(TEST_INETADDR_1, offer.getNetAddr()); - } - - @Test - public void testGetOffer_RequestedAddressReserved() throws Exception { - DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, IPV4_ADDR_ANY /* relayAddr */, - TEST_RESERVED_ADDR /* reqAddr */, HOSTNAME_NONE); - assertNotEquals(TEST_RESERVED_ADDR, offer.getNetAddr()); - } - - @Test - public void testGetOffer_RequestedAddressInvalid() throws Exception { - final Inet4Address invalidAddr = parseAddr4("192.168.42.0"); - DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, IPV4_ADDR_ANY /* relayAddr */, - invalidAddr /* reqAddr */, HOSTNAME_NONE); - assertNotEquals(invalidAddr, offer.getNetAddr()); - } - - @Test - public void testGetOffer_RequestedAddressOutsideSubnet() throws Exception { - final Inet4Address invalidAddr = parseAddr4("192.168.254.2"); - DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, IPV4_ADDR_ANY /* relayAddr */, - invalidAddr /* reqAddr */, HOSTNAME_NONE); - assertNotEquals(invalidAddr, offer.getNetAddr()); - } - - @Test(expected = DhcpLeaseRepository.InvalidSubnetException.class) - public void testGetOffer_RelayInInvalidSubnet() throws Exception { - mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, parseAddr4("192.168.254.2") /* relayAddr */, - INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE); - } - - @Test - public void testRequestLease_SelectingTwice() throws Exception { - final DhcpLease lease1 = requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1, - TEST_HOSTNAME_1); - - // Second request from same client for a different address - final DhcpLease lease2 = requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_2, - TEST_HOSTNAME_2); - - assertEquals(TEST_INETADDR_1, lease1.getNetAddr()); - assertEquals(TEST_HOSTNAME_1, lease1.getHostname()); - - assertEquals(TEST_INETADDR_2, lease2.getNetAddr()); - assertEquals(TEST_HOSTNAME_2, lease2.getHostname()); - - // First address freed when client requested a different one: another client can request it - final DhcpLease lease3 = requestLeaseSelecting(TEST_MAC_2, TEST_INETADDR_1, HOSTNAME_NONE); - assertEquals(TEST_INETADDR_1, lease3.getNetAddr()); - } - - @Test(expected = DhcpLeaseRepository.InvalidAddressException.class) - public void testRequestLease_SelectingInvalid() throws Exception { - requestLeaseSelecting(TEST_MAC_1, parseAddr4("192.168.254.5")); - } - - @Test(expected = DhcpLeaseRepository.InvalidAddressException.class) - public void testRequestLease_SelectingInUse() throws Exception { - requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1); - requestLeaseSelecting(TEST_MAC_2, TEST_INETADDR_1); - } - - @Test(expected = DhcpLeaseRepository.InvalidAddressException.class) - public void testRequestLease_SelectingReserved() throws Exception { - requestLeaseSelecting(TEST_MAC_1, TEST_RESERVED_ADDR); - } - - @Test(expected = DhcpLeaseRepository.InvalidSubnetException.class) - public void testRequestLease_SelectingRelayInInvalidSubnet() throws Exception { - mRepo.requestLease(CLIENTID_UNSPEC, TEST_MAC_1, IPV4_ADDR_ANY /* clientAddr */, - parseAddr4("192.168.128.1") /* relayAddr */, TEST_INETADDR_1 /* reqAddr */, - true /* sidSet */, HOSTNAME_NONE); - } - - @Test - public void testRequestLease_InitReboot() throws Exception { - // Request address once - requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1); - - final long newTime = TEST_TIME + 100; - when(mClock.elapsedRealtime()).thenReturn(newTime); - - // init-reboot (sidSet == false): verify configuration - final DhcpLease lease = requestLeaseInitReboot(TEST_MAC_1, TEST_INETADDR_1); - assertEquals(TEST_INETADDR_1, lease.getNetAddr()); - assertEquals(newTime + TEST_LEASE_TIME_MS, lease.getExpTime()); - } - - @Test(expected = DhcpLeaseRepository.InvalidAddressException.class) - public void testRequestLease_InitRebootWrongAddr() throws Exception { - // Request address once - requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1); - // init-reboot with different requested address - requestLeaseInitReboot(TEST_MAC_1, TEST_INETADDR_2); - } - - @Test - public void testRequestLease_InitRebootUnknownAddr() throws Exception { - // init-reboot with unknown requested address - final DhcpLease lease = requestLeaseInitReboot(TEST_MAC_1, TEST_INETADDR_2); - // RFC2131 says we should not reply to accommodate other servers, but since we are - // authoritative we allow creating the lease to avoid issues with lost lease DB (same as - // dnsmasq behavior) - assertEquals(TEST_INETADDR_2, lease.getNetAddr()); - } - - @Test(expected = DhcpLeaseRepository.InvalidAddressException.class) - public void testRequestLease_InitRebootWrongSubnet() throws Exception { - requestLeaseInitReboot(TEST_MAC_1, parseAddr4("192.168.254.2")); - } - - @Test - public void testRequestLease_Renewing() throws Exception { - requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1); - - final long newTime = TEST_TIME + 100; - when(mClock.elapsedRealtime()).thenReturn(newTime); - - final DhcpLease lease = requestLeaseRenewing(TEST_MAC_1, TEST_INETADDR_1); - - assertEquals(TEST_INETADDR_1, lease.getNetAddr()); - assertEquals(newTime + TEST_LEASE_TIME_MS, lease.getExpTime()); - } - - @Test - public void testRequestLease_RenewingUnknownAddr() throws Exception { - final long newTime = TEST_TIME + 100; - when(mClock.elapsedRealtime()).thenReturn(newTime); - final DhcpLease lease = requestLeaseRenewing(TEST_MAC_1, TEST_INETADDR_1); - // Allows renewing an unknown address if available - assertEquals(TEST_INETADDR_1, lease.getNetAddr()); - assertEquals(newTime + TEST_LEASE_TIME_MS, lease.getExpTime()); - } - - @Test(expected = DhcpLeaseRepository.InvalidAddressException.class) - public void testRequestLease_RenewingAddrInUse() throws Exception { - requestLeaseSelecting(TEST_MAC_2, TEST_INETADDR_1); - requestLeaseRenewing(TEST_MAC_1, TEST_INETADDR_1); - } - - @Test(expected = DhcpLeaseRepository.InvalidAddressException.class) - public void testRequestLease_RenewingInvalidAddr() throws Exception { - requestLeaseRenewing(TEST_MAC_1, parseAddr4("192.168.254.2")); - } - - @Test - public void testReleaseLease() throws Exception { - final DhcpLease lease1 = requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1); - - assertHasLease(lease1); - assertTrue(mRepo.releaseLease(CLIENTID_UNSPEC, TEST_MAC_1, TEST_INETADDR_1)); - assertNoLease(lease1); - - final DhcpLease lease2 = requestLeaseSelecting(TEST_MAC_2, TEST_INETADDR_1); - assertEquals(TEST_INETADDR_1, lease2.getNetAddr()); - } - - @Test - public void testReleaseLease_UnknownLease() { - assertFalse(mRepo.releaseLease(CLIENTID_UNSPEC, TEST_MAC_1, TEST_INETADDR_1)); - } - - @Test - public void testReleaseLease_StableOffer() throws Exception { - for (MacAddress mac : new MacAddress[] { TEST_MAC_1, TEST_MAC_2, TEST_MAC_3 }) { - final DhcpLease lease = mRepo.getOffer(CLIENTID_UNSPEC, mac, - IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE); - - requestLeaseSelecting(mac, lease.getNetAddr()); - mRepo.releaseLease(CLIENTID_UNSPEC, mac, lease.getNetAddr()); - - // Same lease is offered after it was released - final DhcpLease newLease = mRepo.getOffer(CLIENTID_UNSPEC, mac, - IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE); - assertEquals(lease.getNetAddr(), newLease.getNetAddr()); - } - } - - @Test - public void testMarkLeaseDeclined() throws Exception { - final DhcpLease lease = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, - IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE); - - mRepo.markLeaseDeclined(lease.getNetAddr()); - - // Same lease is not offered again - final DhcpLease newLease = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, - IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE); - assertNotEquals(lease.getNetAddr(), newLease.getNetAddr()); - } - - @Test - public void testMarkLeaseDeclined_UsedIfOutOfAddresses() throws Exception { - // Use a /28 to quickly run out of addresses - mRepo.updateParams(new IpPrefix(TEST_SERVER_ADDR, 28), TEST_EXCL_SET, TEST_LEASE_TIME_MS); - - mRepo.markLeaseDeclined(TEST_INETADDR_1); - mRepo.markLeaseDeclined(TEST_INETADDR_2); - - // /28 should have 16 addresses, 14 w/o the first/last, 11 w/o excluded addresses - requestAddresses((byte) 9); - - // Last 2 addresses: addresses marked declined should be used - final DhcpLease firstLease = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, - IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, TEST_HOSTNAME_1); - requestLeaseSelecting(TEST_MAC_1, firstLease.getNetAddr()); - - final DhcpLease secondLease = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_2, - IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, TEST_HOSTNAME_2); - requestLeaseSelecting(TEST_MAC_2, secondLease.getNetAddr()); - - // Now out of addresses - try { - mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_3, IPV4_ADDR_ANY /* relayAddr */, - INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE); - fail("Repository should be out of addresses and throw"); - } catch (DhcpLeaseRepository.OutOfAddressesException e) { /* expected */ } - - assertEquals(TEST_INETADDR_1, firstLease.getNetAddr()); - assertEquals(TEST_HOSTNAME_1, firstLease.getHostname()); - assertEquals(TEST_INETADDR_2, secondLease.getNetAddr()); - assertEquals(TEST_HOSTNAME_2, secondLease.getHostname()); - } - - private DhcpLease requestLease(@NonNull MacAddress macAddr, @NonNull Inet4Address clientAddr, - @Nullable Inet4Address reqAddr, @Nullable String hostname, boolean sidSet) - throws DhcpLeaseRepository.DhcpLeaseException { - return mRepo.requestLease(CLIENTID_UNSPEC, macAddr, clientAddr, - IPV4_ADDR_ANY /* relayAddr */, - reqAddr, sidSet, hostname); - } - - /** - * Request a lease simulating a client in the SELECTING state. - */ - private DhcpLease requestLeaseSelecting(@NonNull MacAddress macAddr, - @NonNull Inet4Address reqAddr, @Nullable String hostname) - throws DhcpLeaseRepository.DhcpLeaseException { - return requestLease(macAddr, IPV4_ADDR_ANY /* clientAddr */, reqAddr, hostname, - true /* sidSet */); - } - - /** - * Request a lease simulating a client in the SELECTING state. - */ - private DhcpLease requestLeaseSelecting(@NonNull MacAddress macAddr, - @NonNull Inet4Address reqAddr) throws DhcpLeaseRepository.DhcpLeaseException { - return requestLeaseSelecting(macAddr, reqAddr, HOSTNAME_NONE); - } - - /** - * Request a lease simulating a client in the INIT-REBOOT state. - */ - private DhcpLease requestLeaseInitReboot(@NonNull MacAddress macAddr, - @NonNull Inet4Address reqAddr) throws DhcpLeaseRepository.DhcpLeaseException { - return requestLease(macAddr, IPV4_ADDR_ANY /* clientAddr */, reqAddr, HOSTNAME_NONE, - false /* sidSet */); - } - - /** - * Request a lease simulating a client in the RENEWING state. - */ - private DhcpLease requestLeaseRenewing(@NonNull MacAddress macAddr, - @NonNull Inet4Address clientAddr) throws DhcpLeaseRepository.DhcpLeaseException { - // Renewing: clientAddr filled in, no reqAddr - return requestLease(macAddr, clientAddr, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE, - true /* sidSet */); - } - - private void assertNoLease(DhcpLease lease) { - assertFalse("Leases contain " + lease, mRepo.getCommittedLeases().contains(lease)); - } - - private void assertHasLease(DhcpLease lease) { - assertTrue("Leases do not contain " + lease, mRepo.getCommittedLeases().contains(lease)); - } - - private void assertNotDeclined(Inet4Address addr) { - assertFalse("Address is declined: " + addr, mRepo.getDeclinedAddresses().contains(addr)); - } - - private void assertDeclined(Inet4Address addr) { - assertTrue("Address is not declined: " + addr, mRepo.getDeclinedAddresses().contains(addr)); - } -} diff --git a/tests/src/android/net/dhcp/DhcpPacketTest.java b/tests/src/android/net/dhcp/DhcpPacketTest.java deleted file mode 100644 index a30d3e4..0000000 --- a/tests/src/android/net/dhcp/DhcpPacketTest.java +++ /dev/null @@ -1,1117 +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.dhcp.DhcpPacket.DHCP_BROADCAST_ADDRESS; -import static android.net.dhcp.DhcpPacket.DHCP_DNS_SERVER; -import static android.net.dhcp.DhcpPacket.DHCP_DOMAIN_NAME; -import static android.net.dhcp.DhcpPacket.DHCP_LEASE_TIME; -import static android.net.dhcp.DhcpPacket.DHCP_MESSAGE_TYPE_ACK; -import static android.net.dhcp.DhcpPacket.DHCP_MESSAGE_TYPE_OFFER; -import static android.net.dhcp.DhcpPacket.DHCP_MTU; -import static android.net.dhcp.DhcpPacket.DHCP_REBINDING_TIME; -import static android.net.dhcp.DhcpPacket.DHCP_RENEWAL_TIME; -import static android.net.dhcp.DhcpPacket.DHCP_ROUTER; -import static android.net.dhcp.DhcpPacket.DHCP_SUBNET_MASK; -import static android.net.dhcp.DhcpPacket.DHCP_VENDOR_INFO; -import static android.net.dhcp.DhcpPacket.ENCAP_BOOTP; -import static android.net.dhcp.DhcpPacket.ENCAP_L2; -import static android.net.dhcp.DhcpPacket.ENCAP_L3; -import static android.net.dhcp.DhcpPacket.INADDR_ANY; -import static android.net.dhcp.DhcpPacket.INFINITE_LEASE; -import static android.net.dhcp.DhcpPacket.ParseException; -import static android.net.shared.Inet4AddressUtils.getBroadcastAddress; -import static android.net.shared.Inet4AddressUtils.getPrefixMaskAsInet4Address; - -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 androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.internal.util.HexDump; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -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; - -@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 IPV4_ADDR_ANY or INADDR_ANY to ensure that the code - // doesn't use == instead of equals when comparing addresses. - private static final Inet4Address ANY = 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 serverHostName, 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(serverHostName, dhcpResults.serverHostName); - 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 - // androidx.test.InstrumentationRegistry for obtaining a Context object - // to read such golden files, along with an appropriate Android.mk. - // CHECKSTYLE:OFF Generated code - 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")); - // CHECKSTYLE:ON Generated code - - 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 { - // CHECKSTYLE:OFF Generated code - final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray( - // IP header. - "450001518d0600004011144dc0a82b01c0a82bf7" + - // UDP header. - "00430044013d9ac7" + - // BOOTP header. - "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000" + - // MAC address. - "30766ff2a90c00000000000000000000" + - // Server name ("dhcp.android.com" plus invalid "AAAA" after null terminator). - "646863702e616e64726f69642e636f6d00000000000000000000000000000000" + - "0000000000004141414100000000000000000000000000000000000000000000" + - // File. - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - // Options - "638253633501023604c0a82b01330400000e103a04000007083b0400000c4e0104ffffff00" + - "1c04c0a82bff0304c0a82b010604c0a82b012b0f414e44524f49445f4d455445524544ff")); - // CHECKSTYLE:ON Generated code - - 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", "dhcp.android.com", "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 { - // CHECKSTYLE:OFF Generated code - 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")); - // CHECKSTYLE:ON Generated code - - 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 { - // CHECKSTYLE:OFF Generated code - 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")); - // CHECKSTYLE:ON Generated code - 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. - // CHECKSTYLE:OFF Generated code - 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")); - // CHECKSTYLE:ON Generated code - - 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 { - // CHECKSTYLE:OFF Generated code - 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")); - // CHECKSTYLE:ON Generated code - - 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 { - // CHECKSTYLE:OFF Generated code - 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")); - // CHECKSTYLE:ON Generated code - - 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 { - // CHECKSTYLE:OFF Generated code - 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")); - // CHECKSTYLE:ON Generated code - - 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 { - // CHECKSTYLE:OFF Generated code - 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")); - // CHECKSTYLE:ON Generated code - - 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 { - // CHECKSTYLE:OFF Generated code - 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")); - // CHECKSTYLE:ON Generated code - - 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/src/android/net/dhcp/DhcpServerTest.java b/tests/src/android/net/dhcp/DhcpServerTest.java deleted file mode 100644 index f0e2f1b..0000000 --- a/tests/src/android/net/dhcp/DhcpServerTest.java +++ /dev/null @@ -1,333 +0,0 @@ -/* - * Copyright (C) 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. - */ - -package android.net.dhcp; - -import static android.net.InetAddresses.parseNumericAddress; -import static android.net.dhcp.DhcpPacket.DHCP_CLIENT; -import static android.net.dhcp.DhcpPacket.DHCP_HOST_NAME; -import static android.net.dhcp.DhcpPacket.ENCAP_BOOTP; -import static android.net.dhcp.DhcpPacket.INADDR_ANY; -import static android.net.dhcp.DhcpPacket.INADDR_BROADCAST; -import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS; - -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertNotNull; -import static junit.framework.Assert.assertTrue; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.isNull; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.net.INetworkStackStatusCallback; -import android.net.LinkAddress; -import android.net.MacAddress; -import android.net.dhcp.DhcpLeaseRepository.InvalidAddressException; -import android.net.dhcp.DhcpLeaseRepository.OutOfAddressesException; -import android.net.dhcp.DhcpServer.Clock; -import android.net.dhcp.DhcpServer.Dependencies; -import android.net.util.SharedLog; -import android.os.HandlerThread; -import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper; -import android.testing.TestableLooper.RunWithLooper; - -import androidx.test.filters.SmallTest; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.net.Inet4Address; -import java.nio.ByteBuffer; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - -@RunWith(AndroidTestingRunner.class) -@SmallTest -@RunWithLooper -public class DhcpServerTest { - private static final String TEST_IFACE = "testiface"; - - private static final Inet4Address TEST_SERVER_ADDR = parseAddr("192.168.0.2"); - private static final LinkAddress TEST_SERVER_LINKADDR = new LinkAddress(TEST_SERVER_ADDR, 20); - private static final Set<Inet4Address> TEST_DEFAULT_ROUTERS = new HashSet<>( - Arrays.asList(parseAddr("192.168.0.123"), parseAddr("192.168.0.124"))); - private static final Set<Inet4Address> TEST_DNS_SERVERS = new HashSet<>( - Arrays.asList(parseAddr("192.168.0.126"), parseAddr("192.168.0.127"))); - private static final Set<Inet4Address> TEST_EXCLUDED_ADDRS = new HashSet<>( - Arrays.asList(parseAddr("192.168.0.200"), parseAddr("192.168.0.201"))); - private static final long TEST_LEASE_TIME_SECS = 3600L; - private static final int TEST_MTU = 1500; - private static final String TEST_HOSTNAME = "testhostname"; - - private static final int TEST_TRANSACTION_ID = 123; - private static final byte[] TEST_CLIENT_MAC_BYTES = new byte [] { 1, 2, 3, 4, 5, 6 }; - private static final MacAddress TEST_CLIENT_MAC = MacAddress.fromBytes(TEST_CLIENT_MAC_BYTES); - private static final Inet4Address TEST_CLIENT_ADDR = parseAddr("192.168.0.42"); - - private static final long TEST_CLOCK_TIME = 1234L; - private static final int TEST_LEASE_EXPTIME_SECS = 3600; - private static final DhcpLease TEST_LEASE = new DhcpLease(null, TEST_CLIENT_MAC, - TEST_CLIENT_ADDR, TEST_LEASE_EXPTIME_SECS * 1000L + TEST_CLOCK_TIME, - null /* hostname */); - private static final DhcpLease TEST_LEASE_WITH_HOSTNAME = new DhcpLease(null, TEST_CLIENT_MAC, - TEST_CLIENT_ADDR, TEST_LEASE_EXPTIME_SECS * 1000L + TEST_CLOCK_TIME, TEST_HOSTNAME); - - @NonNull @Mock - private Dependencies mDeps; - @NonNull @Mock - private DhcpLeaseRepository mRepository; - @NonNull @Mock - private Clock mClock; - @NonNull @Mock - private DhcpPacketListener mPacketListener; - - @NonNull @Captor - private ArgumentCaptor<ByteBuffer> mSentPacketCaptor; - @NonNull @Captor - private ArgumentCaptor<Inet4Address> mResponseDstAddrCaptor; - - @NonNull - private HandlerThread mHandlerThread; - @NonNull - private TestableLooper mLooper; - @NonNull - private DhcpServer mServer; - - @Nullable - private String mPrevShareClassloaderProp; - - private final INetworkStackStatusCallback mAssertSuccessCallback = - new INetworkStackStatusCallback.Stub() { - @Override - public void onStatusAvailable(int statusCode) { - assertEquals(STATUS_SUCCESS, statusCode); - } - - @Override - public int getInterfaceVersion() { - return this.VERSION; - } - }; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - - when(mDeps.makeLeaseRepository(any(), any(), any())).thenReturn(mRepository); - when(mDeps.makeClock()).thenReturn(mClock); - when(mDeps.makePacketListener()).thenReturn(mPacketListener); - doNothing().when(mDeps) - .sendPacket(any(), mSentPacketCaptor.capture(), mResponseDstAddrCaptor.capture()); - when(mClock.elapsedRealtime()).thenReturn(TEST_CLOCK_TIME); - - final DhcpServingParams servingParams = new DhcpServingParams.Builder() - .setDefaultRouters(TEST_DEFAULT_ROUTERS) - .setDhcpLeaseTimeSecs(TEST_LEASE_TIME_SECS) - .setDnsServers(TEST_DNS_SERVERS) - .setServerAddr(TEST_SERVER_LINKADDR) - .setLinkMtu(TEST_MTU) - .setExcludedAddrs(TEST_EXCLUDED_ADDRS) - .build(); - - mLooper = TestableLooper.get(this); - mHandlerThread = spy(new HandlerThread("TestDhcpServer")); - when(mHandlerThread.getLooper()).thenReturn(mLooper.getLooper()); - mServer = new DhcpServer(mHandlerThread, TEST_IFACE, servingParams, - new SharedLog(DhcpServerTest.class.getSimpleName()), mDeps); - - mServer.start(mAssertSuccessCallback); - mLooper.processAllMessages(); - } - - @After - public void tearDown() throws Exception { - mServer.stop(mAssertSuccessCallback); - mLooper.processMessages(1); - verify(mPacketListener, times(1)).stop(); - verify(mHandlerThread, times(1)).quitSafely(); - } - - @Test - public void testStart() throws Exception { - verify(mPacketListener, times(1)).start(); - } - - @Test - public void testDiscover() throws Exception { - // TODO: refactor packet construction to eliminate unnecessary/confusing/duplicate fields - when(mRepository.getOffer(isNull() /* clientId */, eq(TEST_CLIENT_MAC), - eq(INADDR_ANY) /* relayAddr */, isNull() /* reqAddr */, isNull() /* hostname */)) - .thenReturn(TEST_LEASE); - - final DhcpDiscoverPacket discover = new DhcpDiscoverPacket(TEST_TRANSACTION_ID, - (short) 0 /* secs */, INADDR_ANY /* relayIp */, TEST_CLIENT_MAC_BYTES, - false /* broadcast */, INADDR_ANY /* srcIp */); - mServer.processPacket(discover, DHCP_CLIENT); - - assertResponseSentTo(TEST_CLIENT_ADDR); - final DhcpOfferPacket packet = assertOffer(getPacket()); - assertMatchesTestLease(packet); - } - - @Test - public void testDiscover_OutOfAddresses() throws Exception { - when(mRepository.getOffer(isNull() /* clientId */, eq(TEST_CLIENT_MAC), - eq(INADDR_ANY) /* relayAddr */, isNull() /* reqAddr */, isNull() /* hostname */)) - .thenThrow(new OutOfAddressesException("Test exception")); - - final DhcpDiscoverPacket discover = new DhcpDiscoverPacket(TEST_TRANSACTION_ID, - (short) 0 /* secs */, INADDR_ANY /* relayIp */, TEST_CLIENT_MAC_BYTES, - false /* broadcast */, INADDR_ANY /* srcIp */); - mServer.processPacket(discover, DHCP_CLIENT); - - assertResponseSentTo(INADDR_BROADCAST); - final DhcpNakPacket packet = assertNak(getPacket()); - assertMatchesClient(packet); - } - - private DhcpRequestPacket makeRequestSelectingPacket() { - final DhcpRequestPacket request = new DhcpRequestPacket(TEST_TRANSACTION_ID, - (short) 0 /* secs */, INADDR_ANY /* clientIp */, INADDR_ANY /* relayIp */, - TEST_CLIENT_MAC_BYTES, false /* broadcast */); - request.mServerIdentifier = TEST_SERVER_ADDR; - request.mRequestedIp = TEST_CLIENT_ADDR; - return request; - } - - @Test - public void testRequest_Selecting_Ack() throws Exception { - when(mRepository.requestLease(isNull() /* clientId */, eq(TEST_CLIENT_MAC), - eq(INADDR_ANY) /* clientAddr */, eq(INADDR_ANY) /* relayAddr */, - eq(TEST_CLIENT_ADDR) /* reqAddr */, eq(true) /* sidSet */, eq(TEST_HOSTNAME))) - .thenReturn(TEST_LEASE_WITH_HOSTNAME); - - final DhcpRequestPacket request = makeRequestSelectingPacket(); - request.mHostName = TEST_HOSTNAME; - request.mRequestedParams = new byte[] { DHCP_HOST_NAME }; - mServer.processPacket(request, DHCP_CLIENT); - - assertResponseSentTo(TEST_CLIENT_ADDR); - final DhcpAckPacket packet = assertAck(getPacket()); - assertMatchesTestLease(packet, TEST_HOSTNAME); - } - - @Test - public void testRequest_Selecting_Nak() throws Exception { - when(mRepository.requestLease(isNull(), eq(TEST_CLIENT_MAC), - eq(INADDR_ANY) /* clientAddr */, eq(INADDR_ANY) /* relayAddr */, - eq(TEST_CLIENT_ADDR) /* reqAddr */, eq(true) /* sidSet */, isNull() /* hostname */)) - .thenThrow(new InvalidAddressException("Test error")); - - final DhcpRequestPacket request = makeRequestSelectingPacket(); - mServer.processPacket(request, DHCP_CLIENT); - - assertResponseSentTo(INADDR_BROADCAST); - final DhcpNakPacket packet = assertNak(getPacket()); - assertMatchesClient(packet); - } - - @Test - public void testRequest_Selecting_WrongClientPort() throws Exception { - final DhcpRequestPacket request = makeRequestSelectingPacket(); - mServer.processPacket(request, 50000); - - verify(mRepository, never()) - .requestLease(any(), any(), any(), any(), any(), anyBoolean(), any()); - verify(mDeps, never()).sendPacket(any(), any(), any()); - } - - @Test - public void testRelease() throws Exception { - final DhcpReleasePacket release = new DhcpReleasePacket(TEST_TRANSACTION_ID, - TEST_SERVER_ADDR, TEST_CLIENT_ADDR, - INADDR_ANY /* relayIp */, TEST_CLIENT_MAC_BYTES); - mServer.processPacket(release, DHCP_CLIENT); - - verify(mRepository, times(1)) - .releaseLease(isNull(), eq(TEST_CLIENT_MAC), eq(TEST_CLIENT_ADDR)); - } - - /* TODO: add more tests once packet construction is refactored, including: - * - usage of giaddr - * - usage of broadcast bit - * - other request states (init-reboot/renewing/rebinding) - */ - - private void assertMatchesTestLease(@NonNull DhcpPacket packet, @Nullable String hostname) { - assertMatchesClient(packet); - assertFalse(packet.hasExplicitClientId()); - assertEquals(TEST_SERVER_ADDR, packet.mServerIdentifier); - assertEquals(TEST_CLIENT_ADDR, packet.mYourIp); - assertNotNull(packet.mLeaseTime); - assertEquals(TEST_LEASE_EXPTIME_SECS, (int) packet.mLeaseTime); - assertEquals(hostname, packet.mHostName); - } - - private void assertMatchesTestLease(@NonNull DhcpPacket packet) { - assertMatchesTestLease(packet, null); - } - - private void assertMatchesClient(@NonNull DhcpPacket packet) { - assertEquals(TEST_TRANSACTION_ID, packet.mTransId); - assertEquals(TEST_CLIENT_MAC, MacAddress.fromBytes(packet.mClientMac)); - } - - private void assertResponseSentTo(@NonNull Inet4Address addr) { - assertEquals(addr, mResponseDstAddrCaptor.getValue()); - } - - private static DhcpNakPacket assertNak(@Nullable DhcpPacket packet) { - assertTrue(packet instanceof DhcpNakPacket); - return (DhcpNakPacket) packet; - } - - private static DhcpAckPacket assertAck(@Nullable DhcpPacket packet) { - assertTrue(packet instanceof DhcpAckPacket); - return (DhcpAckPacket) packet; - } - - private static DhcpOfferPacket assertOffer(@Nullable DhcpPacket packet) { - assertTrue(packet instanceof DhcpOfferPacket); - return (DhcpOfferPacket) packet; - } - - private DhcpPacket getPacket() throws Exception { - verify(mDeps, times(1)).sendPacket(any(), any(), any()); - return DhcpPacket.decodeFullPacket(mSentPacketCaptor.getValue(), ENCAP_BOOTP); - } - - private static Inet4Address parseAddr(@Nullable String inet4Addr) { - return (Inet4Address) parseNumericAddress(inet4Addr); - } -} diff --git a/tests/src/android/net/dhcp/DhcpServingParamsTest.java b/tests/src/android/net/dhcp/DhcpServingParamsTest.java deleted file mode 100644 index 57a87a4..0000000 --- a/tests/src/android/net/dhcp/DhcpServingParamsTest.java +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright (C) 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. - */ - -package android.net.dhcp; - -import static android.net.InetAddresses.parseNumericAddress; -import static android.net.dhcp.DhcpServingParams.MTU_UNSET; -import static android.net.shared.Inet4AddressUtils.inet4AddressToIntHTH; - -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertTrue; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.net.LinkAddress; -import android.net.dhcp.DhcpServingParams.InvalidParameterException; -import android.net.shared.Inet4AddressUtils; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.lang.reflect.Modifier; -import java.net.Inet4Address; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class DhcpServingParamsTest { - @NonNull - private DhcpServingParams.Builder mBuilder; - - private static final Set<Inet4Address> TEST_DEFAULT_ROUTERS = new HashSet<>( - Arrays.asList(parseAddr("192.168.0.123"), parseAddr("192.168.0.124"))); - private static final long TEST_LEASE_TIME_SECS = 3600L; - private static final Set<Inet4Address> TEST_DNS_SERVERS = new HashSet<>( - Arrays.asList(parseAddr("192.168.0.126"), parseAddr("192.168.0.127"))); - private static final Inet4Address TEST_SERVER_ADDR = parseAddr("192.168.0.2"); - private static final LinkAddress TEST_LINKADDR = new LinkAddress(TEST_SERVER_ADDR, 20); - private static final int TEST_MTU = 1500; - private static final Set<Inet4Address> TEST_EXCLUDED_ADDRS = new HashSet<>( - Arrays.asList(parseAddr("192.168.0.200"), parseAddr("192.168.0.201"))); - private static final boolean TEST_METERED = true; - - @Before - public void setUp() { - mBuilder = new DhcpServingParams.Builder() - .setDefaultRouters(TEST_DEFAULT_ROUTERS) - .setDhcpLeaseTimeSecs(TEST_LEASE_TIME_SECS) - .setDnsServers(TEST_DNS_SERVERS) - .setServerAddr(TEST_LINKADDR) - .setLinkMtu(TEST_MTU) - .setExcludedAddrs(TEST_EXCLUDED_ADDRS) - .setMetered(TEST_METERED); - } - - @Test - public void testBuild_Immutable() throws InvalidParameterException { - final Set<Inet4Address> routers = new HashSet<>(TEST_DEFAULT_ROUTERS); - final Set<Inet4Address> dnsServers = new HashSet<>(TEST_DNS_SERVERS); - final Set<Inet4Address> excludedAddrs = new HashSet<>(TEST_EXCLUDED_ADDRS); - - final DhcpServingParams params = mBuilder - .setDefaultRouters(routers) - .setDnsServers(dnsServers) - .setExcludedAddrs(excludedAddrs) - .build(); - - // Modifications to source objects should not affect builder or final parameters - final Inet4Address addedAddr = parseAddr("192.168.0.223"); - routers.add(addedAddr); - dnsServers.add(addedAddr); - excludedAddrs.add(addedAddr); - - assertEquals(TEST_DEFAULT_ROUTERS, params.defaultRouters); - assertEquals(TEST_LEASE_TIME_SECS, params.dhcpLeaseTimeSecs); - assertEquals(TEST_DNS_SERVERS, params.dnsServers); - assertEquals(TEST_LINKADDR, params.serverAddr); - assertEquals(TEST_MTU, params.linkMtu); - assertEquals(TEST_METERED, params.metered); - - assertContains(params.excludedAddrs, TEST_EXCLUDED_ADDRS); - assertContains(params.excludedAddrs, TEST_DEFAULT_ROUTERS); - assertContains(params.excludedAddrs, TEST_DNS_SERVERS); - assertContains(params.excludedAddrs, TEST_SERVER_ADDR); - - assertFalse("excludedAddrs should not contain " + addedAddr, - params.excludedAddrs.contains(addedAddr)); - } - - @Test(expected = InvalidParameterException.class) - public void testBuild_NegativeLeaseTime() throws InvalidParameterException { - mBuilder.setDhcpLeaseTimeSecs(-1).build(); - } - - @Test(expected = InvalidParameterException.class) - public void testBuild_LeaseTimeTooLarge() throws InvalidParameterException { - // Set lease time larger than max value for uint32 - mBuilder.setDhcpLeaseTimeSecs(1L << 32).build(); - } - - @Test - public void testBuild_InfiniteLeaseTime() throws InvalidParameterException { - final long infiniteLeaseTime = 0xffffffffL; - final DhcpServingParams params = mBuilder - .setDhcpLeaseTimeSecs(infiniteLeaseTime).build(); - assertEquals(infiniteLeaseTime, params.dhcpLeaseTimeSecs); - assertTrue(params.dhcpLeaseTimeSecs > 0L); - } - - @Test - public void testBuild_UnsetMtu() throws InvalidParameterException { - final DhcpServingParams params = mBuilder.setLinkMtu(MTU_UNSET).build(); - assertEquals(MTU_UNSET, params.linkMtu); - } - - @Test(expected = InvalidParameterException.class) - public void testBuild_MtuTooSmall() throws InvalidParameterException { - mBuilder.setLinkMtu(20).build(); - } - - @Test(expected = InvalidParameterException.class) - public void testBuild_MtuTooLarge() throws InvalidParameterException { - mBuilder.setLinkMtu(65_536).build(); - } - - @Test(expected = InvalidParameterException.class) - public void testBuild_IPv6Addr() throws InvalidParameterException { - mBuilder.setServerAddr(new LinkAddress(parseNumericAddress("fe80::1111"), 120)).build(); - } - - @Test(expected = InvalidParameterException.class) - public void testBuild_PrefixTooLarge() throws InvalidParameterException { - mBuilder.setServerAddr(new LinkAddress(TEST_SERVER_ADDR, 15)).build(); - } - - @Test(expected = InvalidParameterException.class) - public void testBuild_PrefixTooSmall() throws InvalidParameterException { - mBuilder.setDefaultRouters(parseAddr("192.168.0.254")) - .setServerAddr(new LinkAddress(TEST_SERVER_ADDR, 31)) - .build(); - } - - @Test(expected = InvalidParameterException.class) - public void testBuild_RouterNotInPrefix() throws InvalidParameterException { - mBuilder.setDefaultRouters(parseAddr("192.168.254.254")).build(); - } - - @Test - public void testFromParcelableObject() throws InvalidParameterException { - final DhcpServingParams params = mBuilder.build(); - final DhcpServingParamsParcel parcel = new DhcpServingParamsParcel(); - parcel.defaultRouters = toIntArray(TEST_DEFAULT_ROUTERS); - parcel.dhcpLeaseTimeSecs = TEST_LEASE_TIME_SECS; - parcel.dnsServers = toIntArray(TEST_DNS_SERVERS); - parcel.serverAddr = inet4AddressToIntHTH(TEST_SERVER_ADDR); - parcel.serverAddrPrefixLength = TEST_LINKADDR.getPrefixLength(); - parcel.linkMtu = TEST_MTU; - parcel.excludedAddrs = toIntArray(TEST_EXCLUDED_ADDRS); - parcel.metered = TEST_METERED; - final DhcpServingParams parceled = DhcpServingParams.fromParcelableObject(parcel); - - assertEquals(params.defaultRouters, parceled.defaultRouters); - assertEquals(params.dhcpLeaseTimeSecs, parceled.dhcpLeaseTimeSecs); - assertEquals(params.dnsServers, parceled.dnsServers); - assertEquals(params.serverAddr, parceled.serverAddr); - assertEquals(params.linkMtu, parceled.linkMtu); - assertEquals(params.excludedAddrs, parceled.excludedAddrs); - assertEquals(params.metered, parceled.metered); - - // Ensure that we do not miss any field if added in the future - final long numFields = Arrays.stream(DhcpServingParams.class.getDeclaredFields()) - .filter(f -> !Modifier.isStatic(f.getModifiers())) - .count(); - assertEquals(7, numFields); - } - - @Test(expected = InvalidParameterException.class) - public void testFromParcelableObject_NullArgument() throws InvalidParameterException { - DhcpServingParams.fromParcelableObject(null); - } - - private static int[] toIntArray(Collection<Inet4Address> addrs) { - return addrs.stream().mapToInt(Inet4AddressUtils::inet4AddressToIntHTH).toArray(); - } - - private static <T> void assertContains(@NonNull Set<T> set, @NonNull Set<T> subset) { - for (final T elem : subset) { - assertContains(set, elem); - } - } - - private static <T> void assertContains(@NonNull Set<T> set, @Nullable T elem) { - assertTrue("Set does not contain " + elem, set.contains(elem)); - } - - @NonNull - private static Inet4Address parseAddr(@NonNull String inet4Addr) { - return (Inet4Address) parseNumericAddress(inet4Addr); - } -} diff --git a/tests/src/android/net/ip/IpClientTest.java b/tests/src/android/net/ip/IpClientTest.java deleted file mode 100644 index 5f80006..0000000 --- a/tests/src/android/net/ip/IpClientTest.java +++ /dev/null @@ -1,547 +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.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import android.app.AlarmManager; -import android.content.ContentResolver; -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.NetworkStackIpMemoryStore; -import android.net.RouteInfo; -import android.net.ipmemorystore.NetworkAttributes; -import android.net.shared.InitialConfiguration; -import android.net.shared.ProvisioningConfiguration; -import android.net.util.InterfaceParams; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.internal.R; -import com.android.server.NetworkObserver; -import com.android.server.NetworkObserverRegistry; -import com.android.server.NetworkStackService; - -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; - private static final String TEST_L2KEY = "some l2key"; - private static final String TEST_GROUPHINT = "some grouphint"; - - @Mock private Context mContext; - @Mock private ConnectivityManager mCm; - @Mock private NetworkObserverRegistry mObserverRegistry; - @Mock private INetd mNetd; - @Mock private Resources mResources; - @Mock private IIpClientCallbacks mCb; - @Mock private AlarmManager mAlarm; - @Mock private IpClient.Dependencies mDependencies; - @Mock private ContentResolver mContentResolver; - @Mock private NetworkStackService.NetworkStackServiceManager mNetworkStackServiceManager; - @Mock private NetworkStackIpMemoryStore mIpMemoryStore; - - private NetworkObserver mObserver; - private InterfaceParams mIfParams; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - - when(mContext.getSystemService(eq(Context.ALARM_SERVICE))).thenReturn(mAlarm); - when(mContext.getSystemService(eq(ConnectivityManager.class))).thenReturn(mCm); - when(mContext.getResources()).thenReturn(mResources); - when(mDependencies.getNetd(any())).thenReturn(mNetd); - when(mResources.getInteger(R.integer.config_networkAvoidBadWifi)) - .thenReturn(DEFAULT_AVOIDBADWIFI_CONFIG_VALUE); - when(mContext.getContentResolver()).thenReturn(mContentResolver); - - mIfParams = null; - } - - private void setTestInterfaceParams(String ifname) { - mIfParams = (ifname != null) - ? new InterfaceParams(ifname, TEST_IFINDEX, TEST_MAC) - : null; - when(mDependencies.getInterfaceParams(anyString())).thenReturn(mIfParams); - } - - private IpClient makeIpClient(String ifname) throws Exception { - setTestInterfaceParams(ifname); - final IpClient ipc = new IpClient(mContext, ifname, mCb, mObserverRegistry, - mNetworkStackServiceManager, mDependencies); - verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceSetEnableIPv6(ifname, false); - verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceClearAddrs(ifname); - ArgumentCaptor<NetworkObserver> arg = ArgumentCaptor.forClass(NetworkObserver.class); - verify(mObserverRegistry, times(1)).registerObserverForNonblockingCallback(arg.capture()); - mObserver = arg.getValue(); - reset(mObserverRegistry); - 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; - } - - private void verifyNetworkAttributesStored(final String l2Key, - final NetworkAttributes attributes) { - // TODO : when storing is implemented, turn this on - // verify(mIpMemoryStore).storeNetworkAttributes(eq(l2Key), eq(attributes), any()); - } - - @Test - public void testNullInterfaceNameMostDefinitelyThrows() throws Exception { - setTestInterfaceParams(null); - try { - final IpClient ipc = new IpClient(mContext, null, mCb, mObserverRegistry, - mNetworkStackServiceManager, mDependencies); - 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, mObserverRegistry, - mNetworkStackServiceManager, mDependencies); - 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, mObserverRegistry, - mNetworkStackServiceManager, mDependencies); - verifyNoMoreInteractions(mIpMemoryStore); - ipc.shutdown(); - } - - @Test - public void testInterfaceNotFoundFailsImmediately() throws Exception { - setTestInterfaceParams(null); - final IpClient ipc = new IpClient(mContext, TEST_IFNAME, mCb, mObserverRegistry, - mNetworkStackServiceManager, mDependencies); - ipc.startProvisioning(new ProvisioningConfiguration()); - verify(mCb, times(1)).onProvisioningFailure(any()); - verify(mIpMemoryStore, never()).storeNetworkAttributes(any(), any(), 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()); - verify(mIpMemoryStore, never()).storeNetworkAttributes(any(), any(), 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(makeEmptyLinkProperties(iface)); - } - - @Test - public void testProvisioningWithInitialConfiguration() throws Exception { - final String iface = TEST_IFNAME; - final IpClient ipc = makeIpClient(iface); - final String l2Key = TEST_L2KEY; - final String groupHint = TEST_GROUPHINT; - - 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()); - ipc.setL2KeyAndGroupHint(l2Key, groupHint); - - 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.onInterfaceAddressUpdated(new LinkAddress(addresses[i]), iface); - verify(mCb, timeout(TEST_TIMEOUT_MS)).onLinkPropertiesChange(any()); - reset(mCb); - } - - // Add Nth address - mObserver.onInterfaceAddressUpdated(new LinkAddress(addresses[lastAddr]), iface); - LinkProperties want = linkproperties(links(addresses), routes(prefixes)); - want.setInterfaceName(iface); - verify(mCb, timeout(TEST_TIMEOUT_MS).times(1)).onProvisioningSuccess(want); - verifyNetworkAttributesStored(l2Key, new NetworkAttributes.Builder() - .setGroupHint(groupHint) - .build()); - - 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(makeEmptyLinkProperties(iface)); - verifyNoMoreInteractions(mIpMemoryStore); - } - - @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(InitialConfiguration.all(list1, (x) -> false)); - assertFalse(InitialConfiguration.all(list2, (x) -> false)); - assertTrue(InitialConfiguration.all(list3, (x) -> true)); - assertTrue(InitialConfiguration.all(list2, (x) -> x.charAt(0) == 'f')); - assertFalse(InitialConfiguration.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(InitialConfiguration.any(list1, (x) -> true)); - assertTrue(InitialConfiguration.any(list2, (x) -> true)); - assertTrue(InitialConfiguration.any(list2, (x) -> x.charAt(0) == 'f')); - assertFalse(InitialConfiguration.any(list3, (x) -> x.charAt(0) == 'f')); - assertTrue(InitialConfiguration.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/src/android/net/ip/IpReachabilityMonitorTest.java b/tests/src/android/net/ip/IpReachabilityMonitorTest.java deleted file mode 100644 index 64b168a..0000000 --- a/tests/src/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 androidx.test.filters.SmallTest; -import androidx.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/src/android/net/util/ConnectivityPacketSummaryTest.java b/tests/src/android/net/util/ConnectivityPacketSummaryTest.java deleted file mode 100644 index 71be8b3..0000000 --- a/tests/src/android/net/util/ConnectivityPacketSummaryTest.java +++ /dev/null @@ -1,419 +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 androidx.test.filters.SmallTest; -import androidx.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/src/android/net/util/PacketReaderTest.java b/tests/src/android/net/util/PacketReaderTest.java deleted file mode 100644 index 289dcad..0000000 --- a/tests/src/android/net/util/PacketReaderTest.java +++ /dev/null @@ -1,211 +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.AF_INET6; -import static android.system.OsConstants.IPPROTO_UDP; -import static android.system.OsConstants.SOCK_DGRAM; -import static android.system.OsConstants.SOCK_NONBLOCK; -import static android.system.OsConstants.SOL_SOCKET; -import static android.system.OsConstants.SO_SNDTIMEO; - -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.system.ErrnoException; -import android.system.Os; -import android.system.StructTimeval; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.io.FileDescriptor; -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; - -/** - * 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 | SOCK_NONBLOCK, 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/src/com/android/server/connectivity/NetworkMonitorTest.java b/tests/src/com/android/server/connectivity/NetworkMonitorTest.java deleted file mode 100644 index 832b712..0000000 --- a/tests/src/com/android/server/connectivity/NetworkMonitorTest.java +++ /dev/null @@ -1,1022 +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 com.android.server.connectivity; - -import static android.net.CaptivePortal.APP_RETURN_DISMISSED; -import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_INVALID; -import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY; -import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_VALID; -import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; -import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD; -import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_EVALUATION_TYPE; -import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_MIN_EVALUATE_INTERVAL; -import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_VALID_DNS_TIME_THRESHOLD; -import static android.net.util.DataStallUtils.DATA_STALL_EVALUATION_TYPE_DNS; -import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS; -import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS; -import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_USE_HTTPS; - -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertFalse; - -import static org.junit.Assert.assertArrayEquals; -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 static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.doThrow; -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.annotation.NonNull; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.res.Resources; -import android.net.ConnectivityManager; -import android.net.DnsResolver; -import android.net.INetworkMonitorCallbacks; -import android.net.InetAddresses; -import android.net.LinkProperties; -import android.net.Network; -import android.net.NetworkCapabilities; -import android.net.NetworkInfo; -import android.net.captiveportal.CaptivePortalProbeResult; -import android.net.metrics.IpConnectivityLog; -import android.net.shared.PrivateDnsConfig; -import android.net.util.SharedLog; -import android.net.wifi.WifiInfo; -import android.net.wifi.WifiManager; -import android.os.Bundle; -import android.os.ConditionVariable; -import android.os.Handler; -import android.os.Looper; -import android.os.Process; -import android.os.RemoteException; -import android.os.SystemClock; -import android.provider.Settings; -import android.telephony.CellSignalStrength; -import android.telephony.TelephonyManager; -import android.util.ArrayMap; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.networkstack.R; -import com.android.networkstack.metrics.DataStallDetectionStats; -import com.android.networkstack.metrics.DataStallStatsUtils; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.mockito.Spy; - -import java.io.IOException; -import java.net.HttpURLConnection; -import java.net.InetAddress; -import java.net.URL; -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Random; -import java.util.concurrent.Executor; - -import javax.net.ssl.SSLHandshakeException; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class NetworkMonitorTest { - private static final String LOCATION_HEADER = "location"; - - private @Mock Context mContext; - private @Mock Resources mResources; - private @Mock IpConnectivityLog mLogger; - private @Mock SharedLog mValidationLogger; - private @Mock NetworkInfo mNetworkInfo; - private @Mock DnsResolver mDnsResolver; - private @Mock ConnectivityManager mCm; - private @Mock TelephonyManager mTelephony; - private @Mock WifiManager mWifi; - private @Mock HttpURLConnection mHttpConnection; - private @Mock HttpURLConnection mHttpsConnection; - private @Mock HttpURLConnection mFallbackConnection; - private @Mock HttpURLConnection mOtherFallbackConnection; - private @Mock Random mRandom; - private @Mock NetworkMonitor.Dependencies mDependencies; - private @Mock INetworkMonitorCallbacks mCallbacks; - private @Spy Network mCleartextDnsNetwork = new Network(TEST_NETID); - private @Mock Network mNetwork; - private @Mock DataStallStatsUtils mDataStallStatsUtils; - private @Mock WifiInfo mWifiInfo; - private @Captor ArgumentCaptor<String> mNetworkTestedRedirectUrlCaptor; - - private HashSet<WrappedNetworkMonitor> mCreatedNetworkMonitors; - private HashSet<BroadcastReceiver> mRegisteredReceivers; - - private static final int TEST_NETID = 4242; - private static final String TEST_HTTP_URL = "http://www.google.com/gen_204"; - private static final String TEST_HTTPS_URL = "https://www.google.com/gen_204"; - private static final String TEST_FALLBACK_URL = "http://fallback.google.com/gen_204"; - private static final String TEST_OTHER_FALLBACK_URL = "http://otherfallback.google.com/gen_204"; - private static final String TEST_MCCMNC = "123456"; - - private static final int RETURN_CODE_DNS_SUCCESS = 0; - private static final int RETURN_CODE_DNS_TIMEOUT = 255; - private static final int DEFAULT_DNS_TIMEOUT_THRESHOLD = 5; - - private static final int HANDLER_TIMEOUT_MS = 1000; - - private static final LinkProperties TEST_LINK_PROPERTIES = new LinkProperties(); - - private static final NetworkCapabilities METERED_CAPABILITIES = new NetworkCapabilities() - .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) - .addCapability(NET_CAPABILITY_INTERNET); - - private static final NetworkCapabilities NOT_METERED_CAPABILITIES = new NetworkCapabilities() - .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) - .addCapability(NET_CAPABILITY_INTERNET) - .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED); - - private static final NetworkCapabilities NO_INTERNET_CAPABILITIES = new NetworkCapabilities() - .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); - - /** - * Fakes DNS responses. - * - * Allows test methods to configure the IP addresses that will be resolved by - * Network#getAllByName and by DnsResolver#query. - */ - class FakeDns { - private final ArrayMap<String, List<InetAddress>> mAnswers = new ArrayMap<>(); - private boolean mNonBypassPrivateDnsWorking = true; - - /** Whether DNS queries on mNonBypassPrivateDnsWorking should succeed. */ - private void setNonBypassPrivateDnsWorking(boolean working) { - mNonBypassPrivateDnsWorking = working; - } - - /** Clears all DNS entries. */ - private synchronized void clearAll() { - mAnswers.clear(); - } - - /** Returns the answer for a given name on the given mock network. */ - private synchronized List<InetAddress> getAnswer(Object mock, String hostname) { - if (mock == mNetwork && !mNonBypassPrivateDnsWorking) { - return null; - } - if (mAnswers.containsKey(hostname)) { - return mAnswers.get(hostname); - } - return mAnswers.get("*"); - } - - /** Sets the answer for a given name. */ - private synchronized void setAnswer(String hostname, String[] answer) - throws UnknownHostException { - if (answer == null) { - mAnswers.remove(hostname); - } else { - List<InetAddress> answerList = new ArrayList<>(); - for (String addr : answer) { - answerList.add(InetAddresses.parseNumericAddress(addr)); - } - mAnswers.put(hostname, answerList); - } - } - - /** Simulates a getAllByName call for the specified name on the specified mock network. */ - private InetAddress[] getAllByName(Object mock, String hostname) - throws UnknownHostException { - List<InetAddress> answer = getAnswer(mock, hostname); - if (answer == null || answer.size() == 0) { - throw new UnknownHostException(hostname); - } - return answer.toArray(new InetAddress[0]); - } - - /** Starts mocking DNS queries. */ - private void startMocking() throws UnknownHostException { - // Queries on mNetwork using getAllByName. - doAnswer(invocation -> { - return getAllByName(invocation.getMock(), invocation.getArgument(0)); - }).when(mNetwork).getAllByName(any()); - - // Queries on mCleartextDnsNetwork using DnsResolver#query. - doAnswer(invocation -> { - String hostname = (String) invocation.getArgument(1); - Executor executor = (Executor) invocation.getArgument(3); - DnsResolver.Callback<List<InetAddress>> callback = invocation.getArgument(5); - - List<InetAddress> answer = getAnswer(invocation.getMock(), hostname); - if (answer != null && answer.size() > 0) { - new Handler(Looper.getMainLooper()).post(() -> { - executor.execute(() -> callback.onAnswer(answer, 0)); - }); - } - // If no answers, do nothing. sendDnsProbeWithTimeout will time out and throw UHE. - return null; - }).when(mDnsResolver).query(any(), any(), anyInt(), any(), any(), any()); - - // Queries on mCleartextDnsNetwork using using DnsResolver#query with QueryType. - doAnswer(invocation -> { - String hostname = (String) invocation.getArgument(1); - Executor executor = (Executor) invocation.getArgument(4); - DnsResolver.Callback<List<InetAddress>> callback = invocation.getArgument(6); - - List<InetAddress> answer = getAnswer(invocation.getMock(), hostname); - if (answer != null && answer.size() > 0) { - new Handler(Looper.getMainLooper()).post(() -> { - executor.execute(() -> callback.onAnswer(answer, 0)); - }); - } - // If no answers, do nothing. sendDnsProbeWithTimeout will time out and throw UHE. - return null; - }).when(mDnsResolver).query(any(), any(), anyInt(), anyInt(), any(), any(), any()); - } - } - - private FakeDns mFakeDns; - - @Before - public void setUp() throws IOException { - MockitoAnnotations.initMocks(this); - when(mDependencies.getPrivateDnsBypassNetwork(any())).thenReturn(mCleartextDnsNetwork); - when(mDependencies.getDnsResolver()).thenReturn(mDnsResolver); - when(mDependencies.getRandom()).thenReturn(mRandom); - when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_MODE), anyInt())) - .thenReturn(Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT); - when(mDependencies.getDeviceConfigPropertyInt(any(), eq(CAPTIVE_PORTAL_USE_HTTPS), - anyInt())).thenReturn(1); - when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_HTTP_URL), any())) - .thenReturn(TEST_HTTP_URL); - when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_HTTPS_URL), any())) - .thenReturn(TEST_HTTPS_URL); - - doReturn(mCleartextDnsNetwork).when(mNetwork).getPrivateDnsBypassingCopy(); - - when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(mCm); - when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephony); - when(mContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mWifi); - when(mContext.getResources()).thenReturn(mResources); - - when(mResources.getString(anyInt())).thenReturn(""); - when(mResources.getStringArray(anyInt())).thenReturn(new String[0]); - - when(mNetworkInfo.getType()).thenReturn(ConnectivityManager.TYPE_WIFI); - setFallbackUrl(TEST_FALLBACK_URL); - setOtherFallbackUrls(TEST_OTHER_FALLBACK_URL); - setFallbackSpecs(null); // Test with no fallback spec by default - when(mRandom.nextInt()).thenReturn(0); - - // DNS probe timeout should not be defined more than half of HANDLER_TIMEOUT_MS. Otherwise, - // it will fail the test because of timeout expired for querying AAAA and A sequentially. - when(mResources.getInteger(eq(R.integer.config_captive_portal_dns_probe_timeout))) - .thenReturn(200); - - doAnswer((invocation) -> { - URL url = invocation.getArgument(0); - switch(url.toString()) { - case TEST_HTTP_URL: - return mHttpConnection; - case TEST_HTTPS_URL: - return mHttpsConnection; - case TEST_FALLBACK_URL: - return mFallbackConnection; - case TEST_OTHER_FALLBACK_URL: - return mOtherFallbackConnection; - default: - fail("URL not mocked: " + url.toString()); - return null; - } - }).when(mCleartextDnsNetwork).openConnection(any()); - when(mHttpConnection.getRequestProperties()).thenReturn(new ArrayMap<>()); - when(mHttpsConnection.getRequestProperties()).thenReturn(new ArrayMap<>()); - - mFakeDns = new FakeDns(); - mFakeDns.startMocking(); - mFakeDns.setAnswer("*", new String[]{"2001:db8::1", "192.0.2.2"}); - - when(mContext.registerReceiver(any(BroadcastReceiver.class), any())).then((invocation) -> { - mRegisteredReceivers.add(invocation.getArgument(0)); - return new Intent(); - }); - - doAnswer((invocation) -> { - mRegisteredReceivers.remove(invocation.getArgument(0)); - return null; - }).when(mContext).unregisterReceiver(any()); - - setMinDataStallEvaluateInterval(500); - setDataStallEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS); - setValidDataStallDnsTimeThreshold(500); - setConsecutiveDnsTimeoutThreshold(5); - - mCreatedNetworkMonitors = new HashSet<>(); - mRegisteredReceivers = new HashSet<>(); - } - - @After - public void tearDown() { - mFakeDns.clearAll(); - assertTrue(mCreatedNetworkMonitors.size() > 0); - // Make a local copy of mCreatedNetworkMonitors because during the iteration below, - // WrappedNetworkMonitor#onQuitting will delete elements from it on the handler threads. - WrappedNetworkMonitor[] networkMonitors = mCreatedNetworkMonitors.toArray( - new WrappedNetworkMonitor[0]); - for (WrappedNetworkMonitor nm : networkMonitors) { - nm.notifyNetworkDisconnected(); - nm.awaitQuit(); - } - assertEquals("NetworkMonitor still running after disconnect", - 0, mCreatedNetworkMonitors.size()); - assertEquals("BroadcastReceiver still registered after disconnect", - 0, mRegisteredReceivers.size()); - } - - private class WrappedNetworkMonitor extends NetworkMonitor { - private long mProbeTime = 0; - private final ConditionVariable mQuitCv = new ConditionVariable(false); - - WrappedNetworkMonitor() { - super(mContext, mCallbacks, mNetwork, mLogger, mValidationLogger, - mDependencies, mDataStallStatsUtils); - } - - @Override - protected long getLastProbeTime() { - return mProbeTime; - } - - protected void setLastProbeTime(long time) { - mProbeTime = time; - } - - @Override - protected void addDnsEvents(@NonNull final DataStallDetectionStats.Builder stats) { - generateTimeoutDnsEvent(stats, DEFAULT_DNS_TIMEOUT_THRESHOLD); - } - - @Override - protected void onQuitting() { - assertTrue(mCreatedNetworkMonitors.remove(this)); - mQuitCv.open(); - } - - protected void awaitQuit() { - assertTrue("NetworkMonitor did not quit after " + HANDLER_TIMEOUT_MS + "ms", - mQuitCv.block(HANDLER_TIMEOUT_MS)); - } - } - - private WrappedNetworkMonitor makeMonitor(NetworkCapabilities nc) { - final WrappedNetworkMonitor nm = new WrappedNetworkMonitor(); - nm.start(); - setNetworkCapabilities(nm, nc); - waitForIdle(nm.getHandler()); - mCreatedNetworkMonitors.add(nm); - return nm; - } - - private WrappedNetworkMonitor makeMeteredNetworkMonitor() { - final WrappedNetworkMonitor nm = makeMonitor(METERED_CAPABILITIES); - return nm; - } - - private WrappedNetworkMonitor makeNotMeteredNetworkMonitor() { - final WrappedNetworkMonitor nm = makeMonitor(NOT_METERED_CAPABILITIES); - return nm; - } - - private void setNetworkCapabilities(NetworkMonitor nm, NetworkCapabilities nc) { - nm.notifyNetworkCapabilitiesChanged(nc); - waitForIdle(nm.getHandler()); - } - - private void waitForIdle(Handler handler) { - final ConditionVariable cv = new ConditionVariable(false); - handler.post(cv::open); - if (!cv.block(HANDLER_TIMEOUT_MS)) { - fail("Timed out waiting for handler"); - } - } - - @Test - public void testGetIntSetting() throws Exception { - WrappedNetworkMonitor wnm = makeNotMeteredNetworkMonitor(); - - // No config resource, no device config. Expect to get default resource. - doThrow(new Resources.NotFoundException()) - .when(mResources).getInteger(eq(R.integer.config_captive_portal_dns_probe_timeout)); - doAnswer(invocation -> { - int defaultValue = invocation.getArgument(2); - return defaultValue; - }).when(mDependencies).getDeviceConfigPropertyInt(any(), - eq(NetworkMonitor.CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT), - anyInt()); - when(mResources.getInteger(eq(R.integer.default_captive_portal_dns_probe_timeout))) - .thenReturn(42); - assertEquals(42, wnm.getIntSetting(mContext, - R.integer.config_captive_portal_dns_probe_timeout, - NetworkMonitor.CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT, - R.integer.default_captive_portal_dns_probe_timeout)); - - // Set device config. Expect to get device config. - when(mDependencies.getDeviceConfigPropertyInt(any(), - eq(NetworkMonitor.CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT), anyInt())) - .thenReturn(1234); - assertEquals(1234, wnm.getIntSetting(mContext, - R.integer.config_captive_portal_dns_probe_timeout, - NetworkMonitor.CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT, - R.integer.default_captive_portal_dns_probe_timeout)); - - // Set config resource. Expect to get config resource. - when(mResources.getInteger(eq(R.integer.config_captive_portal_dns_probe_timeout))) - .thenReturn(5678); - assertEquals(5678, wnm.getIntSetting(mContext, - R.integer.config_captive_portal_dns_probe_timeout, - NetworkMonitor.CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT, - R.integer.default_captive_portal_dns_probe_timeout)); - } - - @Test - public void testIsCaptivePortal_HttpProbeIsPortal() throws IOException { - setSslException(mHttpsConnection); - setPortal302(mHttpConnection); - - runPortalNetworkTest(); - } - - @Test - public void testIsCaptivePortal_HttpsProbeIsNotPortal() throws IOException { - setStatus(mHttpsConnection, 204); - setStatus(mHttpConnection, 500); - - runNotPortalNetworkTest(); - } - - @Test - public void testIsCaptivePortal_FallbackProbeIsPortal() throws IOException { - setSslException(mHttpsConnection); - setStatus(mHttpConnection, 500); - setPortal302(mFallbackConnection); - - runPortalNetworkTest(); - } - - @Test - public void testIsCaptivePortal_FallbackProbeIsNotPortal() throws IOException { - setSslException(mHttpsConnection); - setStatus(mHttpConnection, 500); - setStatus(mFallbackConnection, 500); - - // Fallback probe did not see portal, HTTPS failed -> inconclusive - runFailedNetworkTest(); - } - - @Test - public void testIsCaptivePortal_OtherFallbackProbeIsPortal() throws IOException { - // Set all fallback probes but one to invalid URLs to verify they are being skipped - setFallbackUrl(TEST_FALLBACK_URL); - setOtherFallbackUrls(TEST_FALLBACK_URL + "," + TEST_OTHER_FALLBACK_URL); - - setSslException(mHttpsConnection); - setStatus(mHttpConnection, 500); - setStatus(mFallbackConnection, 500); - setPortal302(mOtherFallbackConnection); - - // TEST_OTHER_FALLBACK_URL is third - when(mRandom.nextInt()).thenReturn(2); - - // First check always uses the first fallback URL: inconclusive - final NetworkMonitor monitor = runNetworkTest(NETWORK_TEST_RESULT_INVALID); - assertNull(mNetworkTestedRedirectUrlCaptor.getValue()); - verify(mFallbackConnection, times(1)).getResponseCode(); - verify(mOtherFallbackConnection, never()).getResponseCode(); - - // Second check uses the URL chosen by Random - final CaptivePortalProbeResult result = monitor.isCaptivePortal(); - assertTrue(result.isPortal()); - verify(mOtherFallbackConnection, times(1)).getResponseCode(); - } - - @Test - public void testIsCaptivePortal_AllProbesFailed() throws IOException { - setSslException(mHttpsConnection); - setStatus(mHttpConnection, 500); - setStatus(mFallbackConnection, 404); - - runFailedNetworkTest(); - verify(mFallbackConnection, times(1)).getResponseCode(); - verify(mOtherFallbackConnection, never()).getResponseCode(); - } - - @Test - public void testIsCaptivePortal_InvalidUrlSkipped() throws IOException { - setFallbackUrl("invalid"); - setOtherFallbackUrls("otherinvalid," + TEST_OTHER_FALLBACK_URL + ",yetanotherinvalid"); - - setSslException(mHttpsConnection); - setStatus(mHttpConnection, 500); - setPortal302(mOtherFallbackConnection); - - runPortalNetworkTest(); - verify(mOtherFallbackConnection, times(1)).getResponseCode(); - verify(mFallbackConnection, never()).getResponseCode(); - } - - private void setupFallbackSpec() throws IOException { - setFallbackSpecs("http://example.com@@/@@204@@/@@" - + "@@,@@" - + TEST_OTHER_FALLBACK_URL + "@@/@@30[12]@@/@@https://(www\\.)?google.com/?.*"); - - setSslException(mHttpsConnection); - setStatus(mHttpConnection, 500); - - // Use the 2nd fallback spec - when(mRandom.nextInt()).thenReturn(1); - } - - @Test - public void testIsCaptivePortal_FallbackSpecIsPartial() throws IOException { - setupFallbackSpec(); - set302(mOtherFallbackConnection, "https://www.google.com/test?q=3"); - - // HTTPS failed, fallback spec went through -> partial connectivity - runPartialConnectivityNetworkTest(); - verify(mOtherFallbackConnection, times(1)).getResponseCode(); - verify(mFallbackConnection, never()).getResponseCode(); - } - - @Test - public void testIsCaptivePortal_FallbackSpecIsPortal() throws IOException { - setupFallbackSpec(); - set302(mOtherFallbackConnection, "http://login.portal.example.com"); - - runPortalNetworkTest(); - } - - @Test - public void testIsCaptivePortal_IgnorePortals() throws IOException { - setCaptivePortalMode(Settings.Global.CAPTIVE_PORTAL_MODE_IGNORE); - setSslException(mHttpsConnection); - setPortal302(mHttpConnection); - - runNotPortalNetworkTest(); - } - - @Test - public void testIsDataStall_EvaluationDisabled() { - setDataStallEvaluationType(0); - WrappedNetworkMonitor wrappedMonitor = makeMeteredNetworkMonitor(); - wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100); - assertFalse(wrappedMonitor.isDataStall()); - } - - @Test - public void testIsDataStall_EvaluationDnsOnNotMeteredNetwork() { - WrappedNetworkMonitor wrappedMonitor = makeNotMeteredNetworkMonitor(); - wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100); - makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD); - assertTrue(wrappedMonitor.isDataStall()); - } - - @Test - public void testIsDataStall_EvaluationDnsOnMeteredNetwork() { - WrappedNetworkMonitor wrappedMonitor = makeMeteredNetworkMonitor(); - wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100); - assertFalse(wrappedMonitor.isDataStall()); - - wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000); - makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD); - assertTrue(wrappedMonitor.isDataStall()); - } - - @Test - public void testIsDataStall_EvaluationDnsWithDnsTimeoutCount() { - WrappedNetworkMonitor wrappedMonitor = makeMeteredNetworkMonitor(); - wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000); - makeDnsTimeoutEvent(wrappedMonitor, 3); - assertFalse(wrappedMonitor.isDataStall()); - // Reset consecutive timeout counts. - makeDnsSuccessEvent(wrappedMonitor, 1); - makeDnsTimeoutEvent(wrappedMonitor, 2); - assertFalse(wrappedMonitor.isDataStall()); - - makeDnsTimeoutEvent(wrappedMonitor, 3); - assertTrue(wrappedMonitor.isDataStall()); - - // Set the value to larger than the default dns log size. - setConsecutiveDnsTimeoutThreshold(51); - wrappedMonitor = makeMeteredNetworkMonitor(); - wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000); - makeDnsTimeoutEvent(wrappedMonitor, 50); - assertFalse(wrappedMonitor.isDataStall()); - - makeDnsTimeoutEvent(wrappedMonitor, 1); - assertTrue(wrappedMonitor.isDataStall()); - } - - @Test - public void testIsDataStall_EvaluationDnsWithDnsTimeThreshold() { - // Test dns events happened in valid dns time threshold. - WrappedNetworkMonitor wrappedMonitor = makeMeteredNetworkMonitor(); - wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100); - makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD); - assertFalse(wrappedMonitor.isDataStall()); - wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000); - assertTrue(wrappedMonitor.isDataStall()); - - // Test dns events happened before valid dns time threshold. - setValidDataStallDnsTimeThreshold(0); - wrappedMonitor = makeMeteredNetworkMonitor(); - wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100); - makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD); - assertFalse(wrappedMonitor.isDataStall()); - wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000); - assertFalse(wrappedMonitor.isDataStall()); - } - - @Test - public void testBrokenNetworkNotValidated() throws Exception { - setSslException(mHttpsConnection); - setStatus(mHttpConnection, 500); - setStatus(mFallbackConnection, 404); - - runFailedNetworkTest(); - } - - @Test - public void testNoInternetCapabilityValidated() throws Exception { - runNetworkTest(NO_INTERNET_CAPABILITIES, NETWORK_TEST_RESULT_VALID); - verify(mCleartextDnsNetwork, never()).openConnection(any()); - } - - @Test - public void testLaunchCaptivePortalApp() throws Exception { - setSslException(mHttpsConnection); - setPortal302(mHttpConnection); - - final NetworkMonitor nm = makeMonitor(METERED_CAPABILITIES); - nm.notifyNetworkConnected(TEST_LINK_PROPERTIES, METERED_CAPABILITIES); - - verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)) - .showProvisioningNotification(any(), any()); - - assertEquals(1, mRegisteredReceivers.size()); - - // Check that startCaptivePortalApp sends the expected intent. - nm.launchCaptivePortalApp(); - - final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); - final ArgumentCaptor<Network> networkCaptor = ArgumentCaptor.forClass(Network.class); - verify(mCm, timeout(HANDLER_TIMEOUT_MS).times(1)) - .startCaptivePortalApp(networkCaptor.capture(), bundleCaptor.capture()); - final Bundle bundle = bundleCaptor.getValue(); - final Network bundleNetwork = bundle.getParcelable(ConnectivityManager.EXTRA_NETWORK); - assertEquals(TEST_NETID, bundleNetwork.netId); - // network is passed both in bundle and as parameter, as the bundle is opaque to the - // framework and only intended for the captive portal app, but the framework needs - // the network to identify the right NetworkMonitor. - assertEquals(TEST_NETID, networkCaptor.getValue().netId); - - // Have the app report that the captive portal is dismissed, and check that we revalidate. - setStatus(mHttpsConnection, 204); - setStatus(mHttpConnection, 204); - - nm.notifyCaptivePortalAppFinished(APP_RETURN_DISMISSED); - verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)) - .notifyNetworkTested(NETWORK_TEST_RESULT_VALID, null); - - assertEquals(0, mRegisteredReceivers.size()); - } - - @Test - public void testPrivateDnsSuccess() throws Exception { - setStatus(mHttpsConnection, 204); - setStatus(mHttpConnection, 204); - mFakeDns.setAnswer("dns.google", new String[]{"2001:db8::53"}); - - WrappedNetworkMonitor wnm = makeNotMeteredNetworkMonitor(); - wnm.notifyPrivateDnsSettingsChanged(new PrivateDnsConfig("dns.google", new InetAddress[0])); - wnm.notifyNetworkConnected(TEST_LINK_PROPERTIES, NOT_METERED_CAPABILITIES); - verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)) - .notifyNetworkTested(eq(NETWORK_TEST_RESULT_VALID), eq(null)); - } - - @Test - public void testPrivateDnsResolutionRetryUpdate() throws Exception { - // Set a private DNS hostname that doesn't resolve and expect validation to fail. - mFakeDns.setAnswer("dns.google", new String[0]); - setStatus(mHttpsConnection, 204); - setStatus(mHttpConnection, 204); - - WrappedNetworkMonitor wnm = makeNotMeteredNetworkMonitor(); - wnm.notifyPrivateDnsSettingsChanged(new PrivateDnsConfig("dns.google", new InetAddress[0])); - wnm.notifyNetworkConnected(TEST_LINK_PROPERTIES, NOT_METERED_CAPABILITIES); - verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)) - .notifyNetworkTested(eq(NETWORK_TEST_RESULT_INVALID), eq(null)); - - // Fix DNS and retry, expect validation to succeed. - reset(mCallbacks); - mFakeDns.setAnswer("dns.google", new String[]{"2001:db8::1"}); - - wnm.forceReevaluation(Process.myUid()); - verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)) - .notifyNetworkTested(eq(NETWORK_TEST_RESULT_VALID), eq(null)); - - // Change configuration to an invalid DNS name, expect validation to fail. - reset(mCallbacks); - mFakeDns.setAnswer("dns.bad", new String[0]); - wnm.notifyPrivateDnsSettingsChanged(new PrivateDnsConfig("dns.bad", new InetAddress[0])); - verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)) - .notifyNetworkTested(eq(NETWORK_TEST_RESULT_INVALID), eq(null)); - - // Change configuration back to working again, but make private DNS not work. - // Expect validation to fail. - reset(mCallbacks); - mFakeDns.setNonBypassPrivateDnsWorking(false); - wnm.notifyPrivateDnsSettingsChanged(new PrivateDnsConfig("dns.google", new InetAddress[0])); - verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)) - .notifyNetworkTested(eq(NETWORK_TEST_RESULT_INVALID), eq(null)); - - // Make private DNS work again. Expect validation to succeed. - reset(mCallbacks); - mFakeDns.setNonBypassPrivateDnsWorking(true); - wnm.forceReevaluation(Process.myUid()); - verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)) - .notifyNetworkTested(eq(NETWORK_TEST_RESULT_VALID), eq(null)); - } - - @Test - public void testDataStall_StallSuspectedAndSendMetrics() throws IOException { - WrappedNetworkMonitor wrappedMonitor = makeNotMeteredNetworkMonitor(); - wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000); - makeDnsTimeoutEvent(wrappedMonitor, 5); - assertTrue(wrappedMonitor.isDataStall()); - verify(mDataStallStatsUtils, times(1)).write(makeEmptyDataStallDetectionStats(), any()); - } - - @Test - public void testDataStall_NoStallSuspectedAndSendMetrics() throws IOException { - WrappedNetworkMonitor wrappedMonitor = makeNotMeteredNetworkMonitor(); - wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000); - makeDnsTimeoutEvent(wrappedMonitor, 3); - assertFalse(wrappedMonitor.isDataStall()); - verify(mDataStallStatsUtils, never()).write(makeEmptyDataStallDetectionStats(), any()); - } - - @Test - public void testCollectDataStallMetrics() { - WrappedNetworkMonitor wrappedMonitor = makeNotMeteredNetworkMonitor(); - - when(mTelephony.getDataNetworkType()).thenReturn(TelephonyManager.NETWORK_TYPE_LTE); - when(mTelephony.getNetworkOperator()).thenReturn(TEST_MCCMNC); - when(mTelephony.getSimOperator()).thenReturn(TEST_MCCMNC); - - DataStallDetectionStats.Builder stats = - new DataStallDetectionStats.Builder() - .setEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS) - .setNetworkType(NetworkCapabilities.TRANSPORT_CELLULAR) - .setCellData(TelephonyManager.NETWORK_TYPE_LTE /* radioType */, - true /* roaming */, - TEST_MCCMNC /* networkMccmnc */, - TEST_MCCMNC /* simMccmnc */, - CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN /* signalStrength */); - generateTimeoutDnsEvent(stats, DEFAULT_DNS_TIMEOUT_THRESHOLD); - - assertEquals(wrappedMonitor.buildDataStallDetectionStats( - NetworkCapabilities.TRANSPORT_CELLULAR), stats.build()); - - when(mWifi.getConnectionInfo()).thenReturn(mWifiInfo); - - stats = new DataStallDetectionStats.Builder() - .setEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS) - .setNetworkType(NetworkCapabilities.TRANSPORT_WIFI) - .setWiFiData(mWifiInfo); - generateTimeoutDnsEvent(stats, DEFAULT_DNS_TIMEOUT_THRESHOLD); - - assertEquals( - wrappedMonitor.buildDataStallDetectionStats(NetworkCapabilities.TRANSPORT_WIFI), - stats.build()); - } - - @Test - public void testIgnoreHttpsProbe() throws Exception { - setSslException(mHttpsConnection); - setStatus(mHttpConnection, 204); - - final NetworkMonitor nm = runNetworkTest(NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY); - - nm.setAcceptPartialConnectivity(); - verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)) - .notifyNetworkTested(eq(NETWORK_TEST_RESULT_VALID), any()); - } - - @Test - public void testIsPartialConnectivity() throws IOException { - setStatus(mHttpsConnection, 500); - setStatus(mHttpConnection, 204); - setStatus(mFallbackConnection, 500); - runPartialConnectivityNetworkTest(); - - setStatus(mHttpsConnection, 500); - setStatus(mHttpConnection, 500); - setStatus(mFallbackConnection, 204); - runPartialConnectivityNetworkTest(); - } - - private void assertIpAddressArrayEquals(String[] expected, InetAddress[] actual) { - String[] actualStrings = new String[actual.length]; - for (int i = 0; i < actual.length; i++) { - actualStrings[i] = actual[i].getHostAddress(); - } - assertArrayEquals("Array of IP addresses differs", expected, actualStrings); - } - - @Test - public void testSendDnsProbeWithTimeout() throws Exception { - WrappedNetworkMonitor wnm = makeNotMeteredNetworkMonitor(); - final int shortTimeoutMs = 200; - - // Clear the wildcard DNS response created in setUp. - mFakeDns.setAnswer("*", null); - - String[] expected = new String[]{"2001:db8::"}; - mFakeDns.setAnswer("www.google.com", expected); - InetAddress[] actual = wnm.sendDnsProbeWithTimeout("www.google.com", shortTimeoutMs); - assertIpAddressArrayEquals(expected, actual); - - expected = new String[]{"2001:db8::", "192.0.2.1"}; - mFakeDns.setAnswer("www.googleapis.com", expected); - actual = wnm.sendDnsProbeWithTimeout("www.googleapis.com", shortTimeoutMs); - assertIpAddressArrayEquals(expected, actual); - - mFakeDns.setAnswer("www.google.com", new String[0]); - try { - wnm.sendDnsProbeWithTimeout("www.google.com", shortTimeoutMs); - fail("No DNS results, expected UnknownHostException"); - } catch (UnknownHostException e) { - } - - mFakeDns.setAnswer("www.google.com", null); - try { - wnm.sendDnsProbeWithTimeout("www.google.com", shortTimeoutMs); - fail("DNS query timed out, expected UnknownHostException"); - } catch (UnknownHostException e) { - } - } - - private void makeDnsTimeoutEvent(WrappedNetworkMonitor wrappedMonitor, int count) { - for (int i = 0; i < count; i++) { - wrappedMonitor.getDnsStallDetector().accumulateConsecutiveDnsTimeoutCount( - RETURN_CODE_DNS_TIMEOUT); - } - } - - private void makeDnsSuccessEvent(WrappedNetworkMonitor wrappedMonitor, int count) { - for (int i = 0; i < count; i++) { - wrappedMonitor.getDnsStallDetector().accumulateConsecutiveDnsTimeoutCount( - RETURN_CODE_DNS_SUCCESS); - } - } - - private DataStallDetectionStats makeEmptyDataStallDetectionStats() { - return new DataStallDetectionStats.Builder().build(); - } - - private void setDataStallEvaluationType(int type) { - when(mDependencies.getDeviceConfigPropertyInt(any(), - eq(CONFIG_DATA_STALL_EVALUATION_TYPE), anyInt())).thenReturn(type); - } - - private void setMinDataStallEvaluateInterval(int time) { - when(mDependencies.getDeviceConfigPropertyInt(any(), - eq(CONFIG_DATA_STALL_MIN_EVALUATE_INTERVAL), anyInt())).thenReturn(time); - } - - private void setValidDataStallDnsTimeThreshold(int time) { - when(mDependencies.getDeviceConfigPropertyInt(any(), - eq(CONFIG_DATA_STALL_VALID_DNS_TIME_THRESHOLD), anyInt())).thenReturn(time); - } - - private void setConsecutiveDnsTimeoutThreshold(int num) { - when(mDependencies.getDeviceConfigPropertyInt(any(), - eq(CONFIG_DATA_STALL_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD), anyInt())).thenReturn(num); - } - - private void setFallbackUrl(String url) { - when(mDependencies.getSetting(any(), - eq(Settings.Global.CAPTIVE_PORTAL_FALLBACK_URL), any())).thenReturn(url); - } - - private void setOtherFallbackUrls(String urls) { - when(mDependencies.getDeviceConfigProperty(any(), - eq(CAPTIVE_PORTAL_OTHER_FALLBACK_URLS), any())).thenReturn(urls); - } - - private void setFallbackSpecs(String specs) { - when(mDependencies.getDeviceConfigProperty(any(), - eq(CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS), any())).thenReturn(specs); - } - - private void setCaptivePortalMode(int mode) { - when(mDependencies.getSetting(any(), - eq(Settings.Global.CAPTIVE_PORTAL_MODE), anyInt())).thenReturn(mode); - } - - private void runPortalNetworkTest() { - runNetworkTest(NETWORK_TEST_RESULT_INVALID); - assertEquals(1, mRegisteredReceivers.size()); - assertNotNull(mNetworkTestedRedirectUrlCaptor.getValue()); - } - - private void runNotPortalNetworkTest() { - runNetworkTest(NETWORK_TEST_RESULT_VALID); - assertEquals(0, mRegisteredReceivers.size()); - assertNull(mNetworkTestedRedirectUrlCaptor.getValue()); - } - - private void runFailedNetworkTest() { - runNetworkTest(NETWORK_TEST_RESULT_INVALID); - assertEquals(0, mRegisteredReceivers.size()); - assertNull(mNetworkTestedRedirectUrlCaptor.getValue()); - } - - private void runPartialConnectivityNetworkTest() { - runNetworkTest(NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY); - assertEquals(0, mRegisteredReceivers.size()); - assertNull(mNetworkTestedRedirectUrlCaptor.getValue()); - } - - private NetworkMonitor runNetworkTest(int testResult) { - return runNetworkTest(METERED_CAPABILITIES, testResult); - } - - private NetworkMonitor runNetworkTest(NetworkCapabilities nc, int testResult) { - final NetworkMonitor monitor = makeMonitor(nc); - monitor.notifyNetworkConnected(TEST_LINK_PROPERTIES, nc); - try { - verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)) - .notifyNetworkTested(eq(testResult), mNetworkTestedRedirectUrlCaptor.capture()); - } catch (RemoteException e) { - fail("Unexpected exception: " + e); - } - waitForIdle(monitor.getHandler()); - - return monitor; - } - - private void setSslException(HttpURLConnection connection) throws IOException { - doThrow(new SSLHandshakeException("Invalid cert")).when(connection).getResponseCode(); - } - - private void set302(HttpURLConnection connection, String location) throws IOException { - setStatus(connection, 302); - doReturn(location).when(connection).getHeaderField(LOCATION_HEADER); - } - - private void setPortal302(HttpURLConnection connection) throws IOException { - set302(connection, "http://login.example.com"); - } - - private void setStatus(HttpURLConnection connection, int status) throws IOException { - doReturn(status).when(connection).getResponseCode(); - } - - private void generateTimeoutDnsEvent(DataStallDetectionStats.Builder stats, int num) { - for (int i = 0; i < num; i++) { - stats.addDnsEvent(RETURN_CODE_DNS_TIMEOUT, 123456789 /* timeMs */); - } - } -} - diff --git a/tests/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java b/tests/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java deleted file mode 100644 index 87346e5..0000000 --- a/tests/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java +++ /dev/null @@ -1,663 +0,0 @@ -/* - * Copyright (C) 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. - */ - -package com.android.server.connectivity.ipmemorystore; - -import static com.android.server.connectivity.ipmemorystore.RegularMaintenanceJobService.InterruptMaintenance; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.doReturn; - -import android.app.job.JobScheduler; -import android.content.Context; -import android.net.ipmemorystore.Blob; -import android.net.ipmemorystore.IOnBlobRetrievedListener; -import android.net.ipmemorystore.IOnL2KeyResponseListener; -import android.net.ipmemorystore.IOnNetworkAttributesRetrievedListener; -import android.net.ipmemorystore.IOnSameL3NetworkResponseListener; -import android.net.ipmemorystore.IOnStatusListener; -import android.net.ipmemorystore.NetworkAttributes; -import android.net.ipmemorystore.NetworkAttributesParcelable; -import android.net.ipmemorystore.SameL3NetworkResponse; -import android.net.ipmemorystore.SameL3NetworkResponseParcelable; -import android.net.ipmemorystore.Status; -import android.net.ipmemorystore.StatusParcelable; -import android.os.ConditionVariable; -import android.os.IBinder; -import android.os.RemoteException; - -import androidx.test.InstrumentationRegistry; -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.io.File; -import java.lang.reflect.Modifier; -import java.net.Inet4Address; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.Arrays; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.function.Consumer; - -/** Unit tests for {@link IpMemoryStoreService}. */ -@SmallTest -@RunWith(AndroidJUnit4.class) -public class IpMemoryStoreServiceTest { - private static final String TEST_CLIENT_ID = "testClientId"; - private static final String TEST_DATA_NAME = "testData"; - - private static final int TEST_DATABASE_SIZE_THRESHOLD = 100 * 1024; //100KB - private static final int DEFAULT_TIMEOUT_MS = 5000; - private static final int LONG_TIMEOUT_MS = 30000; - 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; - @Mock - private JobScheduler mMockJobScheduler; - private File mDbFile; - - private IpMemoryStoreService mService; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - final Context context = InstrumentationRegistry.getContext(); - final File dir = context.getFilesDir(); - mDbFile = new File(dir, "test.db"); - doReturn(mDbFile).when(mMockContext).getDatabasePath(anyString()); - doReturn(mMockJobScheduler).when(mMockContext) - .getSystemService(Context.JOB_SCHEDULER_SERVICE); - mService = new IpMemoryStoreService(mMockContext) { - @Override - protected int getDbSizeThreshold() { - return TEST_DATABASE_SIZE_THRESHOLD; - } - - @Override - boolean isDbSizeOverThreshold() { - // Add a 100ms delay here for pausing maintenance job a while. Interrupted flag can - // be set at this time. - waitForMs(100); - return super.isDbSizeOverThreshold(); - } - }; - } - - @After - public void tearDown() { - mService.shutdown(); - mDbFile.delete(); - } - - /** Helper method to make a vanilla IOnStatusListener */ - private IOnStatusListener onStatus(Consumer<Status> functor) { - return new IOnStatusListener() { - @Override - public void onComplete(final StatusParcelable statusParcelable) throws RemoteException { - functor.accept(new Status(statusParcelable)); - } - - @Override - public IBinder asBinder() { - return null; - } - - @Override - public int getInterfaceVersion() { - return this.VERSION; - } - }; - } - - /** Helper method to make an IOnBlobRetrievedListener */ - private interface OnBlobRetrievedListener { - void onBlobRetrieved(Status status, String l2Key, String name, byte[] data); - } - private IOnBlobRetrievedListener onBlobRetrieved(final OnBlobRetrievedListener functor) { - return new IOnBlobRetrievedListener() { - @Override - public void onBlobRetrieved(final StatusParcelable statusParcelable, - final String l2Key, final String name, final Blob blob) throws RemoteException { - functor.onBlobRetrieved(new Status(statusParcelable), l2Key, name, - null == blob ? null : blob.data); - } - - @Override - public IBinder asBinder() { - return null; - } - - @Override - public int getInterfaceVersion() { - return this.VERSION; - } - }; - } - - /** Helper method to make an IOnNetworkAttributesRetrievedListener */ - private interface OnNetworkAttributesRetrievedListener { - void onNetworkAttributesRetrieved(Status status, String l2Key, NetworkAttributes attr); - } - private IOnNetworkAttributesRetrievedListener onNetworkAttributesRetrieved( - final OnNetworkAttributesRetrievedListener functor) { - return new IOnNetworkAttributesRetrievedListener() { - @Override - public void onNetworkAttributesRetrieved(final StatusParcelable status, - final String l2Key, final NetworkAttributesParcelable attributes) - throws RemoteException { - functor.onNetworkAttributesRetrieved(new Status(status), l2Key, - null == attributes ? null : new NetworkAttributes(attributes)); - } - - @Override - public IBinder asBinder() { - return null; - } - - @Override - public int getInterfaceVersion() { - return this.VERSION; - } - }; - } - - /** Helper method to make an IOnSameNetworkResponseListener */ - private interface OnSameL3NetworkResponseListener { - void onSameL3NetworkResponse(Status status, SameL3NetworkResponse answer); - } - private IOnSameL3NetworkResponseListener onSameResponse( - final OnSameL3NetworkResponseListener functor) { - return new IOnSameL3NetworkResponseListener() { - @Override - public void onSameL3NetworkResponse(final StatusParcelable status, - final SameL3NetworkResponseParcelable sameL3Network) - throws RemoteException { - functor.onSameL3NetworkResponse(new Status(status), - null == sameL3Network ? null : new SameL3NetworkResponse(sameL3Network)); - } - - @Override - public IBinder asBinder() { - return null; - } - - @Override - public int getInterfaceVersion() { - return this.VERSION; - } - }; - } - - /** 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; - } - - @Override - public int getInterfaceVersion() { - return this.VERSION; - } - }; - } - - // Helper method to factorize some boilerplate - private void doLatched(final String timeoutMessage, final Consumer<CountDownLatch> functor) { - doLatched(timeoutMessage, functor, DEFAULT_TIMEOUT_MS); - } - - private void doLatched(final String timeoutMessage, final Consumer<CountDownLatch> functor, - final int timeout) { - final CountDownLatch latch = new CountDownLatch(1); - functor.accept(latch); - try { - if (!latch.await(timeout, TimeUnit.MILLISECONDS)) { - fail(timeoutMessage); - } - } catch (InterruptedException e) { - fail("Thread was interrupted"); - } - } - - // Helper methods to factorize more boilerplate - private void storeAttributes(final String l2Key, final NetworkAttributes na) { - storeAttributes("Did not complete storing attributes", l2Key, na); - } - private void storeAttributes(final String timeoutMessage, final String l2Key, - final NetworkAttributes na) { - doLatched(timeoutMessage, latch -> mService.storeNetworkAttributes(l2Key, na.toParcelable(), - onStatus(status -> { - assertTrue("Store not successful : " + status.resultCode, status.isSuccess()); - latch.countDown(); - }))); - } - - /** Insert large data that db size will be over threshold for maintenance test usage. */ - private void insertFakeDataAndOverThreshold() { - try { - final NetworkAttributes.Builder na = new NetworkAttributes.Builder(); - 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"))); - final byte[] data = new byte[]{-3, 6, 8, -9, 12, -128, 0, 89, 112, 91, -34}; - final long time = System.currentTimeMillis() - 1; - for (int i = 0; i < 1000; i++) { - int errorCode = IpMemoryStoreDatabase.storeNetworkAttributes( - mService.mDb, - "fakeKey" + i, - // Let first 100 records get expiry. - i < 100 ? time : time + TimeUnit.HOURS.toMillis(i), - na.build()); - assertEquals(errorCode, Status.SUCCESS); - - errorCode = IpMemoryStoreDatabase.storeBlob( - mService.mDb, "fakeKey" + i, TEST_CLIENT_ID, TEST_DATA_NAME, data); - assertEquals(errorCode, Status.SUCCESS); - } - - // After added 5000 records, db size is larger than fake threshold(100KB). - assertTrue(mService.isDbSizeOverThreshold()); - } catch (final UnknownHostException e) { - fail("Insert fake data fail"); - } - } - - /** Wait for assigned time. */ - private void waitForMs(long ms) { - try { - Thread.sleep(ms); - } catch (final InterruptedException e) { - fail("Thread was interrupted"); - } - } - - @Test - public void testNetworkAttributes() throws UnknownHostException { - final NetworkAttributes.Builder na = new NetworkAttributes.Builder(); - na.setAssignedV4Address((Inet4Address) Inet4Address.getByName("1.2.3.4")); - na.setAssignedV4AddressExpiry(System.currentTimeMillis() + 7_200_000); - na.setGroupHint("hint1"); - na.setMtu(219); - final String l2Key = FAKE_KEYS[0]; - NetworkAttributes attributes = na.build(); - storeAttributes(l2Key, attributes); - - doLatched("Did not complete retrieving attributes", latch -> - mService.retrieveNetworkAttributes(l2Key, onNetworkAttributesRetrieved( - (status, key, attr) -> { - assertTrue("Retrieve network attributes not successful : " - + status.resultCode, status.isSuccess()); - assertEquals(l2Key, key); - assertEquals(attributes, attr); - latch.countDown(); - }))); - - final NetworkAttributes.Builder na2 = new NetworkAttributes.Builder(); - 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); - - doLatched("Did not complete retrieving attributes 2", latch -> - mService.retrieveNetworkAttributes(l2Key, onNetworkAttributesRetrieved( - (status, key, attr) -> { - assertTrue("Retrieve network attributes not successful : " - + status.resultCode, status.isSuccess()); - assertEquals(l2Key, key); - assertEquals(attributes.assignedV4Address, attr.assignedV4Address); - assertEquals(attributes.assignedV4AddressExpiry, - attr.assignedV4AddressExpiry); - assertEquals(attributes.groupHint, attr.groupHint); - assertEquals(attributes.mtu, attr.mtu); - assertEquals(attributes2.dnsAddresses, attr.dnsAddresses); - latch.countDown(); - }))); - - doLatched("Did not complete retrieving attributes 3", latch -> - mService.retrieveNetworkAttributes(l2Key + "nonexistent", - onNetworkAttributesRetrieved( - (status, key, attr) -> { - assertTrue("Retrieve network attributes not successful : " - + status.resultCode, status.isSuccess()); - assertEquals(l2Key + "nonexistent", key); - assertNull("Retrieved data not stored", attr); - latch.countDown(); - } - ))); - - // Verify that this test does not miss any new field added later. - // If any field is added to NetworkAttributes it must be tested here for storing - // and retrieving. - assertEquals(5, Arrays.stream(NetworkAttributes.class.getDeclaredFields()) - .filter(f -> !Modifier.isStatic(f.getModifiers())).count()); - } - - @Test - public void testInvalidAttributes() { - doLatched("Did not complete storing bad attributes", latch -> - mService.storeNetworkAttributes("key", null, onStatus(status -> { - assertFalse("Success storing on a null key", - status.isSuccess()); - assertEquals(Status.ERROR_ILLEGAL_ARGUMENT, status.resultCode); - latch.countDown(); - }))); - - final NetworkAttributes na = new NetworkAttributes.Builder().setMtu(2).build(); - doLatched("Did not complete storing bad attributes", latch -> - mService.storeNetworkAttributes(null, na.toParcelable(), onStatus(status -> { - assertFalse("Success storing null attributes on a null key", - status.isSuccess()); - assertEquals(Status.ERROR_ILLEGAL_ARGUMENT, status.resultCode); - latch.countDown(); - }))); - - doLatched("Did not complete storing bad attributes", latch -> - mService.storeNetworkAttributes(null, null, onStatus(status -> { - assertFalse("Success storing null attributes on a null key", - status.isSuccess()); - assertEquals(Status.ERROR_ILLEGAL_ARGUMENT, status.resultCode); - latch.countDown(); - }))); - - doLatched("Did not complete retrieving bad attributes", latch -> - mService.retrieveNetworkAttributes(null, onNetworkAttributesRetrieved( - (status, key, attr) -> { - assertFalse("Success retrieving attributes for a null key", - status.isSuccess()); - assertEquals(Status.ERROR_ILLEGAL_ARGUMENT, status.resultCode); - assertNull(key); - assertNull(attr); - latch.countDown(); - }))); - } - - @Test - public void testPrivateData() { - final Blob b = new Blob(); - b.data = new byte[] { -3, 6, 8, -9, 12, -128, 0, 89, 112, 91, -34 }; - final String l2Key = FAKE_KEYS[0]; - doLatched("Did not complete storing private data", latch -> - mService.storeBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME, b, - onStatus(status -> { - assertTrue("Store status not successful : " + status.resultCode, - status.isSuccess()); - latch.countDown(); - }))); - - doLatched("Did not complete retrieving private data", latch -> - mService.retrieveBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME, onBlobRetrieved( - (status, key, name, data) -> { - assertTrue("Retrieve blob status not successful : " + status.resultCode, - status.isSuccess()); - assertEquals(l2Key, key); - assertEquals(name, TEST_DATA_NAME); - assertTrue(Arrays.equals(b.data, data)); - latch.countDown(); - }))); - - // Most puzzling error message ever - doLatched("Did not complete retrieving nothing", latch -> - mService.retrieveBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME + "2", onBlobRetrieved( - (status, key, name, data) -> { - assertTrue("Retrieve blob status not successful : " + status.resultCode, - status.isSuccess()); - assertEquals(l2Key, key); - assertEquals(name, TEST_DATA_NAME + "2"); - assertNull(data); - latch.countDown(); - }))); - } - - @Test - 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); - latch.countDown(); - }))); - - // 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); - latch.countDown(); - }))); - - // 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); - latch.countDown(); - }))); - - // 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); - latch.countDown(); - }))); - - // 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); - latch.countDown(); - }))); - - // 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); - latch.countDown(); - }))); - - // 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); - latch.countDown(); - }))); - } - - private void assertNetworksSameness(final String key1, final String key2, final int sameness) { - doLatched("Did not finish evaluating sameness", latch -> - mService.isSameNetwork(key1, key2, onSameResponse((status, answer) -> { - assertTrue("Retrieve network sameness not successful : " + status.resultCode, - status.isSuccess()); - assertEquals(sameness, answer.getNetworkSameness()); - latch.countDown(); - }))); - } - - @Test - public void testIsSameNetwork() throws UnknownHostException { - final NetworkAttributes.Builder na = new NetworkAttributes.Builder(); - 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"))); - - storeAttributes(FAKE_KEYS[0], na.build()); - // 0 and 1 have identical attributes - storeAttributes(FAKE_KEYS[1], na.build()); - - // Hopefully only the MTU being different still means it's the same network - na.setMtu(200); - storeAttributes(FAKE_KEYS[2], na.build()); - - // Hopefully different MTU, assigned V4 address and grouphint make a different network, - // even with identical DNS addresses - na.setAssignedV4Address(null); - na.setGroupHint("hint2"); - storeAttributes(FAKE_KEYS[3], na.build()); - - assertNetworksSameness(FAKE_KEYS[0], FAKE_KEYS[1], SameL3NetworkResponse.NETWORK_SAME); - assertNetworksSameness(FAKE_KEYS[0], FAKE_KEYS[2], SameL3NetworkResponse.NETWORK_SAME); - assertNetworksSameness(FAKE_KEYS[1], FAKE_KEYS[2], SameL3NetworkResponse.NETWORK_SAME); - assertNetworksSameness(FAKE_KEYS[0], FAKE_KEYS[3], SameL3NetworkResponse.NETWORK_DIFFERENT); - assertNetworksSameness(FAKE_KEYS[0], "neverInsertedKey", - SameL3NetworkResponse.NETWORK_NEVER_CONNECTED); - - doLatched("Did not finish evaluating sameness", latch -> - mService.isSameNetwork(null, null, onSameResponse((status, answer) -> { - assertFalse("Retrieve network sameness suspiciously successful : " - + status.resultCode, status.isSuccess()); - assertEquals(Status.ERROR_ILLEGAL_ARGUMENT, status.resultCode); - assertNull(answer); - latch.countDown(); - }))); - } - - - @Test - public void testFullMaintenance() { - insertFakeDataAndOverThreshold(); - - final InterruptMaintenance im = new InterruptMaintenance(0/* Fake JobId */); - // Do full maintenance and then db size should go down and meet the threshold. - doLatched("Maintenance unexpectedly completed successfully", latch -> - mService.fullMaintenance(onStatus((status) -> { - assertTrue("Execute full maintenance failed: " - + status.resultCode, status.isSuccess()); - latch.countDown(); - }), im), LONG_TIMEOUT_MS); - - // Assume that maintenance is successful, db size shall meet the threshold. - assertFalse(mService.isDbSizeOverThreshold()); - } - - @Test - public void testInterruptMaintenance() { - insertFakeDataAndOverThreshold(); - - final InterruptMaintenance im = new InterruptMaintenance(0/* Fake JobId */); - - // Test interruption immediately. - im.setInterrupted(true); - // Do full maintenance and the expectation is not completed by interruption. - doLatched("Maintenance unexpectedly completed successfully", latch -> - mService.fullMaintenance(onStatus((status) -> { - assertFalse(status.isSuccess()); - latch.countDown(); - }), im), LONG_TIMEOUT_MS); - - // Assume that no data are removed, db size shall be over the threshold. - assertTrue(mService.isDbSizeOverThreshold()); - - // Reset the flag and test interruption during maintenance. - im.setInterrupted(false); - - final ConditionVariable latch = new ConditionVariable(); - // Do full maintenance and the expectation is not completed by interruption. - mService.fullMaintenance(onStatus((status) -> { - assertFalse(status.isSuccess()); - latch.open(); - }), im); - - // Give a little bit of time for maintenance to start up for realism - waitForMs(50); - // Interrupt maintenance job. - im.setInterrupted(true); - - if (!latch.block(LONG_TIMEOUT_MS)) { - fail("Maintenance unexpectedly completed successfully"); - } - - // Assume that only do dropAllExpiredRecords method in previous maintenance, db size shall - // still be over the threshold. - assertTrue(mService.isDbSizeOverThreshold()); - } -} diff --git a/tests/src/com/android/server/connectivity/ipmemorystore/RelevanceUtilsTests.java b/tests/src/com/android/server/connectivity/ipmemorystore/RelevanceUtilsTests.java deleted file mode 100644 index 3d3aabc..0000000 --- a/tests/src/com/android/server/connectivity/ipmemorystore/RelevanceUtilsTests.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (C) 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. - */ - -package com.android.server.connectivity.ipmemorystore; - -import static com.android.server.connectivity.ipmemorystore.RelevanceUtils.CAPPED_RELEVANCE; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -/** Unit tests for {@link RelevanceUtils}. */ -@SmallTest -@RunWith(AndroidJUnit4.class) -public class RelevanceUtilsTests { - @Test - public void testComputeRelevanceForTargetDate() { - final long dayInMillis = 24L * 60 * 60 * 1000; - final long base = 1_000_000L; // any given point in time - // Relevance when the network expires in 1000 years must be capped - assertEquals(CAPPED_RELEVANCE, RelevanceUtils.computeRelevanceForTargetDate( - base + 1000L * dayInMillis, base)); - // Relevance when expiry is before the date must be 0 - assertEquals(0, RelevanceUtils.computeRelevanceForTargetDate(base - 1, base)); - // Make sure the relevance for a given target date is higher if the expiry is further - // in the future - assertTrue(RelevanceUtils.computeRelevanceForTargetDate(base + 100 * dayInMillis, base) - < RelevanceUtils.computeRelevanceForTargetDate(base + 150 * dayInMillis, base)); - - // Make sure the relevance falls slower as the expiry is closing in. This is to ensure - // the decay is indeed logarithmic. - final int relevanceAtExpiry = RelevanceUtils.computeRelevanceForTargetDate(base, base); - final int relevance50DaysBeforeExpiry = - RelevanceUtils.computeRelevanceForTargetDate(base + 50 * dayInMillis, base); - final int relevance100DaysBeforeExpiry = - RelevanceUtils.computeRelevanceForTargetDate(base + 100 * dayInMillis, base); - final int relevance150DaysBeforeExpiry = - RelevanceUtils.computeRelevanceForTargetDate(base + 150 * dayInMillis, base); - assertEquals(0, relevanceAtExpiry); - assertTrue(relevance50DaysBeforeExpiry - relevanceAtExpiry - < relevance100DaysBeforeExpiry - relevance50DaysBeforeExpiry); - assertTrue(relevance100DaysBeforeExpiry - relevance50DaysBeforeExpiry - < relevance150DaysBeforeExpiry - relevance100DaysBeforeExpiry); - } - - @Test - public void testIncreaseRelevance() { - long expiry = System.currentTimeMillis(); - - final long firstBump = RelevanceUtils.bumpExpiryDate(expiry); - // Though a few milliseconds might have elapsed, the first bump should push the duration - // to days in the future, so unless this test takes literal days between these two lines, - // this should always pass. - assertTrue(firstBump > expiry); - - expiry = 0; - long lastDifference = Long.MAX_VALUE; - // The relevance should be capped in at most this many steps. Otherwise, fail. - final int steps = 1000; - for (int i = 0; i < steps; ++i) { - final long newExpiry = RelevanceUtils.bumpExpiryDuration(expiry); - if (newExpiry == expiry) { - // The relevance should be capped. Make sure it is, then exit without failure. - assertEquals(newExpiry, RelevanceUtils.CAPPED_RELEVANCE_LIFETIME_MS); - return; - } - // Make sure the new expiry is further in the future than last time. - assertTrue(newExpiry > expiry); - // Also check that it was not bumped as much as the last bump, because the - // decay must be exponential. - assertTrue(newExpiry - expiry < lastDifference); - lastDifference = newExpiry - expiry; - expiry = newExpiry; - } - fail("Relevance failed to go to the maximum value after " + steps + " bumps"); - } - - @Test - public void testContinuity() { - final long expiry = System.currentTimeMillis(); - - // Relevance at expiry and after expiry should be the cap. - final int relevanceBeforeMaxLifetime = RelevanceUtils.computeRelevanceForTargetDate(expiry, - expiry - (RelevanceUtils.CAPPED_RELEVANCE_LIFETIME_MS + 1_000_000)); - assertEquals(relevanceBeforeMaxLifetime, CAPPED_RELEVANCE); - final int relevanceForMaxLifetime = RelevanceUtils.computeRelevanceForTargetDate(expiry, - expiry - RelevanceUtils.CAPPED_RELEVANCE_LIFETIME_MS); - assertEquals(relevanceForMaxLifetime, CAPPED_RELEVANCE); - - // If the max relevance is reached at the cap lifetime, one millisecond less than this - // should be very close. Strictly speaking this is a bit brittle, but it should be - // good enough for the purposes of the memory store. - final int relevanceForOneMillisecLessThanCap = RelevanceUtils.computeRelevanceForTargetDate( - expiry, expiry - RelevanceUtils.CAPPED_RELEVANCE_LIFETIME_MS + 1); - assertTrue(relevanceForOneMillisecLessThanCap <= CAPPED_RELEVANCE); - assertTrue(relevanceForOneMillisecLessThanCap >= CAPPED_RELEVANCE - 10); - - // Likewise the relevance one millisecond before expiry should be very close to 0. It's - // fine if it rounds down to 0. - final int relevanceOneMillisecBeforeExpiry = RelevanceUtils.computeRelevanceForTargetDate( - expiry, expiry - 1); - assertTrue(relevanceOneMillisecBeforeExpiry <= 10); - assertTrue(relevanceOneMillisecBeforeExpiry >= 0); - - final int relevanceAtExpiry = RelevanceUtils.computeRelevanceForTargetDate(expiry, expiry); - assertEquals(relevanceAtExpiry, 0); - final int relevanceAfterExpiry = RelevanceUtils.computeRelevanceForTargetDate(expiry, - expiry + 1_000_000); - assertEquals(relevanceAfterExpiry, 0); - } - - // testIncreaseRelevance makes sure bumping the expiry continuously always yields a - // monotonically increasing date as a side effect, but this tests that the relevance (as - // opposed to the expiry date) increases monotonically with increasing periods. - @Test - public void testMonotonicity() { - // Hopefully the relevance is granular enough to give a different value for every one - // of this number of steps. - final int steps = 40; - final long expiry = System.currentTimeMillis(); - - int lastRelevance = -1; - for (int i = 0; i < steps; ++i) { - final long date = expiry - i * (RelevanceUtils.CAPPED_RELEVANCE_LIFETIME_MS / steps); - final int relevance = RelevanceUtils.computeRelevanceForTargetDate(expiry, date); - assertTrue(relevance > lastRelevance); - lastRelevance = relevance; - } - } -} diff --git a/tests/src/com/android/server/util/SharedLogTest.java b/tests/src/com/android/server/util/SharedLogTest.java deleted file mode 100644 index b1db051..0000000 --- a/tests/src/com/android/server/util/SharedLogTest.java +++ /dev/null @@ -1,97 +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 com.android.server.util; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import android.net.util.SharedLog; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.io.ByteArrayOutputStream; -import java.io.PrintWriter; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class SharedLogTest { - private static final String TIMESTAMP_PATTERN = "\\d{2}:\\d{2}:\\d{2}"; - private static final String TIMESTAMP = "HH:MM:SS"; - - @Test - public void testBasicOperation() { - final SharedLog logTop = new SharedLog("top"); - logTop.mark("first post!"); - - final SharedLog logLevel2a = logTop.forSubComponent("twoA"); - final SharedLog logLevel2b = logTop.forSubComponent("twoB"); - logLevel2b.e("2b or not 2b"); - logLevel2b.e("No exception", null); - logLevel2b.e("Wait, here's one", new Exception("Test")); - logLevel2a.w("second post?"); - - final SharedLog logLevel3 = logLevel2a.forSubComponent("three"); - logTop.log("still logging"); - logLevel3.log("3 >> 2"); - logLevel2a.mark("ok: last post"); - - final String[] expected = { - " - MARK first post!", - " - [twoB] ERROR 2b or not 2b", - " - [twoB] ERROR No exception", - // No stacktrace in shared log, only in logcat - " - [twoB] ERROR Wait, here's one: Test", - " - [twoA] WARN second post?", - " - still logging", - " - [twoA.three] 3 >> 2", - " - [twoA] MARK ok: last post", - }; - // Verify the logs are all there and in the correct order. - verifyLogLines(expected, logTop); - - // In fact, because they all share the same underlying LocalLog, - // every subcomponent SharedLog's dump() is identical. - verifyLogLines(expected, logLevel2a); - verifyLogLines(expected, logLevel2b); - verifyLogLines(expected, logLevel3); - } - - private static void verifyLogLines(String[] expected, SharedLog log) { - final ByteArrayOutputStream ostream = new ByteArrayOutputStream(); - final PrintWriter pw = new PrintWriter(ostream, true); - log.dump(null, pw, null); - - final String dumpOutput = ostream.toString(); - assertTrue(dumpOutput != null); - assertTrue(!"".equals(dumpOutput)); - - final String[] lines = dumpOutput.split("\n"); - assertEquals(expected.length, lines.length); - - for (int i = 0; i < expected.length; i++) { - String got = lines[i]; - String want = expected[i]; - assertTrue(String.format("'%s' did not contain '%s'", got, want), got.endsWith(want)); - assertTrue(String.format("'%s' did not contain a %s timestamp", got, TIMESTAMP), - got.replaceFirst(TIMESTAMP_PATTERN, TIMESTAMP).contains(TIMESTAMP)); - } - } -} |