Merge "Teach ApfGenerator about the new opcodes for accessing data memory"

This commit is contained in:
Bernie Innocenti
2018-04-16 05:24:29 +00:00
committed by Gerrit Code Review
5 changed files with 304 additions and 108 deletions

View File

@@ -522,7 +522,7 @@ public class ApfFilter {
ICMP6_4_BYTE_LIFETIME_OFFSET, ICMP6_4_BYTE_LIFETIME_LEN);
}
// Note that this parses RA and may throw IllegalArgumentException (from
// Note that this parses RA and may throw InvalidRaException (from
// Buffer.position(int) or due to an invalid-length option) or IndexOutOfBoundsException
// (from ByteBuffer.get(int) ) if parsing encounters something non-compliant with
// specifications.
@@ -986,9 +986,8 @@ public class ApfFilter {
*/
@GuardedBy("this")
private ApfGenerator beginProgramLocked() throws IllegalInstructionException {
ApfGenerator gen = new ApfGenerator();
// This is guaranteed to return true because of the check in maybeCreate.
gen.setApfVersion(mApfCapabilities.apfVersionSupported);
// This is guaranteed to succeed because of the check in maybeCreate.
ApfGenerator gen = new ApfGenerator(mApfCapabilities.apfVersionSupported);
// Here's a basic summary of what the initial program does:
//
@@ -1215,7 +1214,7 @@ public class ApfFilter {
// 1. the program generator will need its offsets adjusted.
// 2. the packet filter attached to our packet socket will need its offset adjusted.
if (apfCapabilities.apfPacketFormat != ARPHRD_ETHER) return null;
if (!new ApfGenerator().setApfVersion(apfCapabilities.apfVersionSupported)) {
if (!ApfGenerator.supportsVersion(apfCapabilities.apfVersionSupported)) {
Log.e(TAG, "Unsupported APF version: " + apfCapabilities.apfVersionSupported);
return null;
}

View File

@@ -58,7 +58,9 @@ public class ApfGenerator {
JLT(18), // Compare less than and branch, e.g. "jlt R0,5,label"
JSET(19), // Compare any bits set and branch, e.g. "jset R0,5,label"
JNEBS(20), // Compare not equal byte sequence, e.g. "jnebs R0,5,label,0x1122334455"
EXT(21); // Followed by immediate indicating ExtendedOpcodes.
EXT(21), // Followed by immediate indicating ExtendedOpcodes.
LDDW(22), // Load 4 bytes from data memory address (register + immediate): "lddw R0, [5]R1"
STDW(23); // Store 4 bytes to data memory address (register + immediate): "stdw R0, [5]R1"
final int value;
@@ -355,19 +357,38 @@ public class ApfGenerator {
*/
public static final int LAST_PREFILLED_MEMORY_SLOT = FILTER_AGE_MEMORY_SLOT;
// This version number syncs up with APF_VERSION in hardware/google/apf/apf_interpreter.h
private static final int MIN_APF_VERSION = 2;
private final ArrayList<Instruction> mInstructions = new ArrayList<Instruction>();
private final HashMap<String, Instruction> mLabels = new HashMap<String, Instruction>();
private final Instruction mDropLabel = new Instruction(Opcodes.LABEL);
private final Instruction mPassLabel = new Instruction(Opcodes.LABEL);
private final int mVersion;
private boolean mGenerated;
/**
* Set version of APF instruction set to generate instructions for. Returns {@code true}
* if generating for this version is supported, {@code false} otherwise.
* Creates an ApfGenerator instance which is able to emit instructions for the specified
* {@code version} of the APF interpreter. Throws {@code IllegalInstructionException} if
* the requested version is unsupported.
*/
public boolean setApfVersion(int version) {
// This version number syncs up with APF_VERSION in hardware/google/apf/apf_interpreter.h
return version >= 2;
ApfGenerator(int version) throws IllegalInstructionException {
mVersion = version;
requireApfVersion(MIN_APF_VERSION);
}
/**
* Returns true if the specified {@code version} is supported by the ApfGenerator, otherwise
* false.
*/
public static boolean supportsVersion(int version) {
return version >= MIN_APF_VERSION;
}
private void requireApfVersion(int minimumVersion) throws IllegalInstructionException {
if (mVersion < minimumVersion) {
throw new IllegalInstructionException("Requires APF >= " + minimumVersion);
}
}
private void addInstruction(Instruction instruction) {
@@ -818,6 +839,36 @@ public class ApfGenerator {
return this;
}
/**
* Add an instruction to the end of the program to load 32 bits from the data memory into
* {@code register}. The source address is computed by adding @{code offset} to the other
* register.
* Requires APF v3 or greater.
*/
public ApfGenerator addLoadData(Register destinationRegister, int offset)
throws IllegalInstructionException {
requireApfVersion(3);
Instruction instruction = new Instruction(Opcodes.LDDW, destinationRegister);
instruction.setUnsignedImm(offset);
addInstruction(instruction);
return this;
}
/**
* Add an instruction to the end of the program to store 32 bits from {@code register} into the
* data memory. The destination address is computed by adding @{code offset} to the other
* register.
* Requires APF v3 or greater.
*/
public ApfGenerator addStoreData(Register sourceRegister, int offset)
throws IllegalInstructionException {
requireApfVersion(3);
Instruction instruction = new Instruction(Opcodes.STDW, sourceRegister);
instruction.setUnsignedImm(offset);
addInstruction(instruction);
return this;
}
/**
* Updates instruction offset fields using latest instruction sizes.
* @return current program length in bytes.

View File

@@ -30,7 +30,6 @@ import static org.mockito.Mockito.verify;
import android.content.Context;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.NetworkUtils;
import android.net.apf.ApfFilter.ApfConfiguration;
import android.net.apf.ApfGenerator.IllegalInstructionException;
import android.net.apf.ApfGenerator.Register;
@@ -42,22 +41,13 @@ import android.os.ConditionVariable;
import android.os.Parcelable;
import android.os.SystemClock;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.system.ErrnoException;
import android.system.Os;
import android.text.format.DateUtils;
import com.android.frameworks.tests.net.R;
import com.android.internal.util.HexDump;
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;
@@ -68,9 +58,14 @@ import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.Random;
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;
/**
* Tests for APF program generator and interpreter.
@@ -82,6 +77,7 @@ import libcore.io.Streams;
@SmallTest
public class ApfTest {
private static final int TIMEOUT_MS = 500;
private final static int MIN_APF_VERSION = 2;
@Mock IpConnectivityLog mLog;
@Mock Context mContext;
@@ -131,11 +127,11 @@ public class ApfTest {
}
private void assertVerdict(int expected, byte[] program, byte[] packet, int filterAge) {
assertReturnCodesEqual(expected, apfSimulate(program, packet, filterAge));
assertReturnCodesEqual(expected, apfSimulate(program, packet, null, filterAge));
}
private void assertVerdict(int expected, byte[] program, byte[] packet) {
assertReturnCodesEqual(expected, apfSimulate(program, packet, 0));
assertReturnCodesEqual(expected, apfSimulate(program, packet, null, 0));
}
private void assertPass(byte[] program, byte[] packet, int filterAge) {
@@ -154,9 +150,24 @@ public class ApfTest {
assertVerdict(DROP, program, packet);
}
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(
"program: " + 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, filterAge));
assertReturnCodesEqual(expected, apfSimulate(gen.generate(), packet, null,
filterAge));
}
private void assertPass(ApfGenerator gen, byte[] packet, int filterAge)
@@ -189,11 +200,11 @@ public class ApfTest {
// 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();
ApfGenerator gen = new ApfGenerator(MIN_APF_VERSION);
assertPass(gen);
// Test jumping to pass label.
gen = new ApfGenerator();
gen = new ApfGenerator(MIN_APF_VERSION);
gen.addJump(gen.PASS_LABEL);
byte[] program = gen.generate();
assertEquals(1, program.length);
@@ -201,7 +212,7 @@ public class ApfTest {
assertPass(program, new byte[MIN_PKT_SIZE], 0);
// Test jumping to drop label.
gen = new ApfGenerator();
gen = new ApfGenerator(MIN_APF_VERSION);
gen.addJump(gen.DROP_LABEL);
program = gen.generate();
assertEquals(2, program.length);
@@ -210,121 +221,121 @@ public class ApfTest {
assertDrop(program, new byte[15], 15);
// Test jumping if equal to 0.
gen = new ApfGenerator();
gen = new ApfGenerator(MIN_APF_VERSION);
gen.addJumpIfR0Equals(0, gen.DROP_LABEL);
assertDrop(gen);
// Test jumping if not equal to 0.
gen = new ApfGenerator();
gen = new ApfGenerator(MIN_APF_VERSION);
gen.addJumpIfR0NotEquals(0, gen.DROP_LABEL);
assertPass(gen);
gen = new ApfGenerator();
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();
gen = new ApfGenerator(MIN_APF_VERSION);
gen.addJumpIfR0EqualsR1(gen.DROP_LABEL);
assertDrop(gen);
// Test jumping if registers not equal.
gen = new ApfGenerator();
gen = new ApfGenerator(MIN_APF_VERSION);
gen.addJumpIfR0NotEqualsR1(gen.DROP_LABEL);
assertPass(gen);
gen = new ApfGenerator();
gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R0, 1);
gen.addJumpIfR0NotEqualsR1(gen.DROP_LABEL);
assertDrop(gen);
// Test load immediate.
gen = new ApfGenerator();
gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R0, 1234567890);
gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
assertDrop(gen);
// Test add.
gen = new ApfGenerator();
gen = new ApfGenerator(MIN_APF_VERSION);
gen.addAdd(1234567890);
gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
assertDrop(gen);
// Test subtract.
gen = new ApfGenerator();
gen = new ApfGenerator(MIN_APF_VERSION);
gen.addAdd(-1234567890);
gen.addJumpIfR0Equals(-1234567890, gen.DROP_LABEL);
assertDrop(gen);
// Test or.
gen = new ApfGenerator();
gen = new ApfGenerator(MIN_APF_VERSION);
gen.addOr(1234567890);
gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
assertDrop(gen);
// Test and.
gen = new ApfGenerator();
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();
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();
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();
gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R0, 1234567890);
gen.addMul(2);
gen.addJumpIfR0Equals(1234567890 * 2, gen.DROP_LABEL);
assertDrop(gen);
// Test divide.
gen = new ApfGenerator();
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();
gen = new ApfGenerator(MIN_APF_VERSION);
gen.addDiv(0);
gen.addJump(gen.DROP_LABEL);
assertPass(gen);
// Test add.
gen = new ApfGenerator();
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();
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();
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();
gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R0, 1234567890);
gen.addLoadImmediate(Register.R1, 123456789);
gen.addAndR1();
@@ -332,7 +343,7 @@ public class ApfTest {
assertDrop(gen);
// Test left shift.
gen = new ApfGenerator();
gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R0, 1234567890);
gen.addLoadImmediate(Register.R1, 1);
gen.addLeftShiftR1();
@@ -340,7 +351,7 @@ public class ApfTest {
assertDrop(gen);
// Test right shift.
gen = new ApfGenerator();
gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R0, 1234567890);
gen.addLoadImmediate(Register.R1, -1);
gen.addLeftShiftR1();
@@ -348,7 +359,7 @@ public class ApfTest {
assertDrop(gen);
// Test multiply.
gen = new ApfGenerator();
gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R0, 1234567890);
gen.addLoadImmediate(Register.R1, 2);
gen.addMulR1();
@@ -356,7 +367,7 @@ public class ApfTest {
assertDrop(gen);
// Test divide.
gen = new ApfGenerator();
gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R0, 1234567890);
gen.addLoadImmediate(Register.R1, 2);
gen.addDivR1();
@@ -364,136 +375,136 @@ public class ApfTest {
assertDrop(gen);
// Test divide by zero.
gen = new ApfGenerator();
gen = new ApfGenerator(MIN_APF_VERSION);
gen.addDivR1();
gen.addJump(gen.DROP_LABEL);
assertPass(gen);
// Test byte load.
gen = new ApfGenerator();
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();
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();
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();
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();
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();
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();
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();
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();
gen = new ApfGenerator(MIN_APF_VERSION);
gen.addJumpIfR0GreaterThan(0, gen.DROP_LABEL);
assertPass(gen);
gen = new ApfGenerator();
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();
gen = new ApfGenerator(MIN_APF_VERSION);
gen.addJumpIfR0LessThan(0, gen.DROP_LABEL);
assertPass(gen);
gen = new ApfGenerator();
gen = new ApfGenerator(MIN_APF_VERSION);
gen.addJumpIfR0LessThan(1, gen.DROP_LABEL);
assertDrop(gen);
// Test jumping if any bits set.
gen = new ApfGenerator();
gen = new ApfGenerator(MIN_APF_VERSION);
gen.addJumpIfR0AnyBitsSet(3, gen.DROP_LABEL);
assertPass(gen);
gen = new ApfGenerator();
gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R0, 1);
gen.addJumpIfR0AnyBitsSet(3, gen.DROP_LABEL);
assertDrop(gen);
gen = new ApfGenerator();
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();
gen = new ApfGenerator(MIN_APF_VERSION);
gen.addJumpIfR0GreaterThanR1(gen.DROP_LABEL);
assertPass(gen);
gen = new ApfGenerator();
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();
gen = new ApfGenerator(MIN_APF_VERSION);
gen.addJumpIfR0LessThanR1(gen.DROP_LABEL);
assertPass(gen);
gen = new ApfGenerator();
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();
gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R1, 3);
gen.addJumpIfR0AnyBitsSetR1(gen.DROP_LABEL);
assertPass(gen);
gen = new ApfGenerator();
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();
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();
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();
gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R1, 1234567890);
gen.addStoreToMemory(Register.R1, 12);
gen.addLoadFromMemory(Register.R0, 12);
@@ -501,63 +512,63 @@ public class ApfTest {
assertDrop(gen);
// Test filter age pre-filled memory.
gen = new ApfGenerator();
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();
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();
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();
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();
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();
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();
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();
gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R1, 1234567890);
gen.addSwap();
gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
assertDrop(gen);
gen = new ApfGenerator();
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();
gen = new ApfGenerator(MIN_APF_VERSION);
gen.addLoadImmediate(Register.R0, 1);
gen.addJumpIfBytesNotEqual(Register.R0, new byte[]{123}, gen.DROP_LABEL);
program = gen.generate();
@@ -569,25 +580,152 @@ public class ApfTest {
assertEquals(1, program[4]);
assertEquals(123, program[5]);
assertDrop(program, new byte[MIN_PKT_SIZE], 0);
gen = new ApfGenerator();
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();
gen = new ApfGenerator(MIN_APF_VERSION);
gen.addJumpIfBytesNotEqual(Register.R0, new byte[]{123}, gen.DROP_LABEL);
assertDrop(gen, packet123, 0);
gen = new ApfGenerator();
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();
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
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,16};
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 3 + 2, in big-endian order.
gen = new ApfGenerator(3);
gen.addLoadImmediate(Register.R0, 0x87654321);
gen.addLoadImmediate(Register.R1, 2);
gen.addStoreData(Register.R0, 3);
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
public void testApfDataRead() throws IllegalInstructionException, Exception {
// Program that DROPs if address 11 (7 + 3) contains 0x87654321.
ApfGenerator gen = new ApfGenerator(3);
gen.addLoadImmediate(Register.R1, 3);
gen.addLoadData(Register.R0, 7);
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[32];
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
public void testApfDataReadModifyWrite() throws IllegalInstructionException, Exception {
ApfGenerator gen = new ApfGenerator(3);
gen.addLoadImmediate(Register.R1, 3);
gen.addLoadData(Register.R0, 7); // Load from address 7 + 3 = 10
gen.addAdd(0x78453412); // 87654321 + 78453412 = FFAA7733
gen.addStoreData(Register.R0, 11); // Write back to address 11 + 3 = 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);
// ...but underflowing isn't allowed.
gen = new ApfGenerator(3);
gen.addLoadImmediate(Register.R0, 20);
gen.addLoadData(Register.R1, -30);
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.
@@ -1422,10 +1560,11 @@ public class ApfTest {
}
/**
* Call the APF interpreter the run {@code program} on {@code packet} pretending the
* filter was installed {@code filter_age} seconds ago.
* 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, int filter_age);
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

View File

@@ -307,7 +307,7 @@ public class Bpf2Apf {
* program and return it.
*/
public static byte[] convert(String bpf) throws IllegalInstructionException {
ApfGenerator gen = new ApfGenerator();
ApfGenerator gen = new ApfGenerator(3);
for (String line : bpf.split("\\n")) convertLine(line, gen);
return gen.generate();
}
@@ -320,7 +320,7 @@ public class Bpf2Apf {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String line = null;
StringBuilder responseData = new StringBuilder();
ApfGenerator gen = new ApfGenerator();
ApfGenerator gen = new ApfGenerator(3);
while ((line = in.readLine()) != null) convertLine(line, gen);
System.out.write(gen.generate());
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016, The Android Open Source Project
* Copyright 2018, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,15 +28,22 @@
// JNI function acting as simply call-through to native APF interpreter.
static jint com_android_server_ApfTest_apfSimulate(
JNIEnv* env, jclass, jbyteArray program, jbyteArray packet, jint filter_age) {
return accept_packet(
(uint8_t*)env->GetByteArrayElements(program, NULL),
env->GetArrayLength(program),
(uint8_t*)env->GetByteArrayElements(packet, NULL),
env->GetArrayLength(packet),
nullptr,
0,
filter_age);
JNIEnv* env, jclass, jbyteArray program, jbyteArray packet,
jbyteArray data, jint filter_age) {
uint8_t* program_raw = (uint8_t*)env->GetByteArrayElements(program, nullptr);
uint8_t* packet_raw = (uint8_t*)env->GetByteArrayElements(packet, nullptr);
uint8_t* data_raw = (uint8_t*)(data ? env->GetByteArrayElements(data, nullptr) : nullptr);
uint32_t program_len = env->GetArrayLength(program);
uint32_t packet_len = env->GetArrayLength(packet);
uint32_t data_len = data ? env->GetArrayLength(data) : 0;
jint result = accept_packet(program_raw, program_len, packet_raw,
packet_len, data_raw, data_len, filter_age);
if (data) {
env->ReleaseByteArrayElements(data, (jbyte*)data_raw, 0 /* copy back */);
}
env->ReleaseByteArrayElements(packet, (jbyte*)packet_raw, JNI_ABORT);
env->ReleaseByteArrayElements(program, (jbyte*)program_raw, JNI_ABORT);
return result;
}
class ScopedPcap {
@@ -170,7 +177,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void*) {
}
static JNINativeMethod gMethods[] = {
{ "apfSimulate", "([B[BI)I",
{ "apfSimulate", "([B[B[BI)I",
(void*)com_android_server_ApfTest_apfSimulate },
{ "compileToBpf", "(Ljava/lang/String;)Ljava/lang/String;",
(void*)com_android_server_ApfTest_compileToBpf },