diff options
| -rw-r--r-- | services/net/java/android/net/apf/ApfFilter.java | 2 | ||||
| -rw-r--r-- | tests/net/java/android/net/apf/ApfTest.java | 41 | ||||
| -rw-r--r-- | tests/net/jni/apf_jni.cpp | 101 | ||||
| -rw-r--r-- | tests/net/res/raw/apfPcap.pcap | bin | 0 -> 101547 bytes |
4 files changed, 116 insertions, 28 deletions
diff --git a/services/net/java/android/net/apf/ApfFilter.java b/services/net/java/android/net/apf/ApfFilter.java index a7209a076461..b9cc372c5138 100644 --- a/services/net/java/android/net/apf/ApfFilter.java +++ b/services/net/java/android/net/apf/ApfFilter.java @@ -111,7 +111,7 @@ public class ApfFilter { * the last writable 32bit word. */ @VisibleForTesting - private static enum Counter { + public static enum Counter { RESERVED_OOB, // Points to offset 0 from the end of the buffer (out-of-bounds) TOTAL_PACKETS, PASSED_ARP, diff --git a/tests/net/java/android/net/apf/ApfTest.java b/tests/net/java/android/net/apf/ApfTest.java index 983802035bfb..436dd859beca 100644 --- a/tests/net/java/android/net/apf/ApfTest.java +++ b/tests/net/java/android/net/apf/ApfTest.java @@ -46,6 +46,7 @@ import android.support.test.runner.AndroidJUnit4; import android.system.ErrnoException; import android.system.Os; import android.text.format.DateUtils; +import android.util.Log; import com.android.frameworks.tests.net.R; import com.android.internal.util.HexDump; import java.io.File; @@ -89,6 +90,7 @@ public class ApfTest { System.loadLibrary("frameworksnettestsjni"); } + private static final String TAG = "ApfTest"; // Expected return codes from APF interpreter. private static final int PASS = 1; private static final int DROP = 0; @@ -869,6 +871,37 @@ public class ApfTest { } } + /** + * 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 IpClient.Callback { private final ConditionVariable mGotApfProgram = new ConditionVariable(); private byte[] mLastApfProgram; @@ -1706,6 +1739,14 @@ public class ApfTest { 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)); diff --git a/tests/net/jni/apf_jni.cpp b/tests/net/jni/apf_jni.cpp index 1ea9e274ab9e..4222adf9e06b 100644 --- a/tests/net/jni/apf_jni.cpp +++ b/tests/net/jni/apf_jni.cpp @@ -21,37 +21,40 @@ #include <stdlib.h> #include <string> #include <utils/Log.h> +#include <vector> #include "apf_interpreter.h" +#include "nativehelper/scoped_primitive_array.h" #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) // JNI function acting as simply call-through to native APF interpreter. static jint com_android_server_ApfTest_apfSimulate( - JNIEnv* env, jclass, jbyteArray 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; - - // Merge program and data into a single buffer. - uint8_t* program_and_data = (uint8_t*)malloc(program_len + data_len); - memcpy(program_and_data, program_raw, program_len); - memcpy(program_and_data + program_len, data_raw, data_len); + JNIEnv* env, jclass, jbyteArray jprogram, jbyteArray jpacket, + jbyteArray jdata, jint filter_age) { + + ScopedByteArrayRO packet(env, jpacket); + uint32_t packet_len = (uint32_t)packet.size(); + uint32_t program_len = env->GetArrayLength(jprogram); + uint32_t data_len = jdata ? env->GetArrayLength(jdata) : 0; + std::vector<uint8_t> buf(program_len + data_len, 0); + + env->GetByteArrayRegion(jprogram, 0, program_len, reinterpret_cast<jbyte*>(buf.data())); + if (jdata) { + // Merge program and data into a single buffer. + env->GetByteArrayRegion(jdata, 0, data_len, + reinterpret_cast<jbyte*>(buf.data() + program_len)); + } jint result = - accept_packet(program_and_data, program_len, program_len + data_len, - packet_raw, packet_len, filter_age); - if (data) { - memcpy(data_raw, program_and_data + program_len, data_len); - env->ReleaseByteArrayElements(data, (jbyte*)data_raw, 0 /* copy back */); - } - free(program_and_data); - env->ReleaseByteArrayElements(packet, (jbyte*)packet_raw, JNI_ABORT); - env->ReleaseByteArrayElements(program, (jbyte*)program_raw, JNI_ABORT); + accept_packet(buf.data(), program_len, program_len + data_len, + reinterpret_cast<const uint8_t*>(packet.get()), packet_len, filter_age); + + if (jdata) { + env->SetByteArrayRegion(jdata, 0, data_len, + reinterpret_cast<jbyte*>(buf.data() + program_len)); + } + return result; } @@ -118,8 +121,7 @@ static jboolean com_android_server_ApfTest_compareBpfApf(JNIEnv* env, jclass, js jstring jpcap_filename, jbyteArray japf_program) { ScopedUtfChars filter(env, jfilter); ScopedUtfChars pcap_filename(env, jpcap_filename); - uint8_t* apf_program = (uint8_t*)env->GetByteArrayElements(japf_program, NULL); - uint32_t apf_program_len = env->GetArrayLength(japf_program); + ScopedByteArrayRO apf_program(env, japf_program); // Open pcap file for BPF filtering ScopedFILE bpf_fp(fopen(pcap_filename.c_str(), "rb")); @@ -161,14 +163,15 @@ static jboolean com_android_server_ApfTest_compareBpfApf(JNIEnv* env, jclass, js do { apf_packet = pcap_next(apf_pcap.get(), &apf_header); } while (apf_packet != NULL && !accept_packet( - apf_program, apf_program_len, 0 /* data_len */, + reinterpret_cast<uint8_t*>(const_cast<int8_t*>(apf_program.get())), + apf_program.size(), 0 /* data_len */, apf_packet, apf_header.len, 0 /* filter_age */)); // Make sure both filters matched the same packet. if (apf_packet == NULL && bpf_packet == NULL) - break; + break; if (apf_packet == NULL || bpf_packet == NULL) - return false; + return false; if (apf_header.len != bpf_header.len || apf_header.ts.tv_sec != bpf_header.ts.tv_sec || apf_header.ts.tv_usec != bpf_header.ts.tv_usec || @@ -178,6 +181,48 @@ static jboolean com_android_server_ApfTest_compareBpfApf(JNIEnv* env, jclass, js return true; } +static jboolean com_android_server_ApfTest_dropsAllPackets(JNIEnv* env, jclass, jbyteArray jprogram, + jbyteArray jdata, jstring jpcap_filename) { + ScopedUtfChars pcap_filename(env, jpcap_filename); + ScopedByteArrayRO apf_program(env, jprogram); + uint32_t apf_program_len = (uint32_t)apf_program.size(); + uint32_t data_len = env->GetArrayLength(jdata); + pcap_pkthdr apf_header; + const uint8_t* apf_packet; + char pcap_error[PCAP_ERRBUF_SIZE]; + std::vector<uint8_t> buf(apf_program_len + data_len, 0); + + // Merge program and data into a single buffer. + env->GetByteArrayRegion(jprogram, 0, apf_program_len, reinterpret_cast<jbyte*>(buf.data())); + env->GetByteArrayRegion(jdata, 0, data_len, + reinterpret_cast<jbyte*>(buf.data() + apf_program_len)); + + // Open pcap file + ScopedFILE apf_fp(fopen(pcap_filename.c_str(), "rb")); + ScopedPcap apf_pcap(pcap_fopen_offline(apf_fp.get(), pcap_error)); + + if (apf_pcap.get() == NULL) { + throwException(env, "pcap_fopen_offline failed: " + std::string(pcap_error)); + return false; + } + + while ((apf_packet = pcap_next(apf_pcap.get(), &apf_header)) != NULL) { + int result = accept_packet(buf.data(), apf_program_len, + apf_program_len + data_len, apf_packet, apf_header.len, 0); + + // Return false once packet passes the filter + if (result) { + env->SetByteArrayRegion(jdata, 0, data_len, + reinterpret_cast<jbyte*>(buf.data() + apf_program_len)); + return false; + } + } + + env->SetByteArrayRegion(jdata, 0, data_len, + reinterpret_cast<jbyte*>(buf.data() + apf_program_len)); + return true; +} + extern "C" jint JNI_OnLoad(JavaVM* vm, void*) { JNIEnv *env; if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { @@ -192,6 +237,8 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void*) { (void*)com_android_server_ApfTest_compileToBpf }, { "compareBpfApf", "(Ljava/lang/String;Ljava/lang/String;[B)Z", (void*)com_android_server_ApfTest_compareBpfApf }, + { "dropsAllPackets", "([B[BLjava/lang/String;)Z", + (void*)com_android_server_ApfTest_dropsAllPackets }, }; jniRegisterNativeMethods(env, "android/net/apf/ApfTest", diff --git a/tests/net/res/raw/apfPcap.pcap b/tests/net/res/raw/apfPcap.pcap Binary files differnew file mode 100644 index 000000000000..6f69c4add0f8 --- /dev/null +++ b/tests/net/res/raw/apfPcap.pcap |