blob: 41c3fab8f0d046430340e59111cec2768ab277d3 [file] [log] [blame]
/*
* 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[] payload = {(byte) 0xff};
// 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
final byte[] nattKaPkt = ipv4UdpPacket(IPV4_KEEPALIVE_DST_ADDR,
IPV4_KEEPALIVE_SRC_ADDR, dstPort, srcPort, 1 /* dataLength */);
System.arraycopy(payload, 0, nattKaPkt, IPV4_UDP_PAYLOAD_OFFSET, payload.length);
assertDrop(program, nattKaPkt);
// Verify IPv4 non-keepalive 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 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);
}
}