1 /* 2 * Copyright 2016, The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include <JNIHelp.h> 18 #include <ScopedUtfChars.h> 19 #include <jni.h> 20 #include <pcap.h> 21 #include <stdlib.h> 22 #include <string> 23 #include <utils/Log.h> 24 25 #include "apf_interpreter.h" 26 27 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) 28 29 // JNI function acting as simply call-through to native APF interpreter. 30 static jint com_android_server_ApfTest_apfSimulate( 31 JNIEnv* env, jclass, jbyteArray program, jbyteArray packet, jint filter_age) { 32 return accept_packet( 33 (uint8_t*)env->GetByteArrayElements(program, NULL), 34 env->GetArrayLength(program), 35 (uint8_t*)env->GetByteArrayElements(packet, NULL), 36 env->GetArrayLength(packet), 37 filter_age); 38 } 39 40 class ScopedPcap { 41 public: 42 ScopedPcap(pcap_t* pcap) : pcap_ptr(pcap) {} 43 ~ScopedPcap() { 44 pcap_close(pcap_ptr); 45 } 46 47 pcap_t* get() const { return pcap_ptr; }; 48 private: 49 pcap_t* const pcap_ptr; 50 }; 51 52 class ScopedFILE { 53 public: 54 ScopedFILE(FILE* fp) : file(fp) {} 55 ~ScopedFILE() { 56 fclose(file); 57 } 58 59 FILE* get() const { return file; }; 60 private: 61 FILE* const file; 62 }; 63 64 static void throwException(JNIEnv* env, const std::string& error) { 65 jclass newExcCls = env->FindClass("java/lang/IllegalStateException"); 66 if (newExcCls == 0) { 67 abort(); 68 return; 69 } 70 env->ThrowNew(newExcCls, error.c_str()); 71 } 72 73 static jstring com_android_server_ApfTest_compileToBpf(JNIEnv* env, jclass, jstring jfilter) { 74 ScopedUtfChars filter(env, jfilter); 75 std::string bpf_string; 76 ScopedPcap pcap(pcap_open_dead(DLT_EN10MB, 65535)); 77 if (pcap.get() == NULL) { 78 throwException(env, "pcap_open_dead failed"); 79 return NULL; 80 } 81 82 // Compile "filter" to a BPF program 83 bpf_program bpf; 84 if (pcap_compile(pcap.get(), &bpf, filter.c_str(), 0, PCAP_NETMASK_UNKNOWN)) { 85 throwException(env, "pcap_compile failed"); 86 return NULL; 87 } 88 89 // Translate BPF program to human-readable format 90 const struct bpf_insn* insn = bpf.bf_insns; 91 for (uint32_t i = 0; i < bpf.bf_len; i++) { 92 bpf_string += bpf_image(insn++, i); 93 bpf_string += "\n"; 94 } 95 96 return env->NewStringUTF(bpf_string.c_str()); 97 } 98 99 static jboolean com_android_server_ApfTest_compareBpfApf(JNIEnv* env, jclass, jstring jfilter, 100 jstring jpcap_filename, jbyteArray japf_program) { 101 ScopedUtfChars filter(env, jfilter); 102 ScopedUtfChars pcap_filename(env, jpcap_filename); 103 const uint8_t* apf_program = (uint8_t*)env->GetByteArrayElements(japf_program, NULL); 104 const uint32_t apf_program_len = env->GetArrayLength(japf_program); 105 106 // Open pcap file for BPF filtering 107 ScopedFILE bpf_fp(fopen(pcap_filename.c_str(), "rb")); 108 char pcap_error[PCAP_ERRBUF_SIZE]; 109 ScopedPcap bpf_pcap(pcap_fopen_offline(bpf_fp.get(), pcap_error)); 110 if (bpf_pcap.get() == NULL) { 111 throwException(env, "pcap_fopen_offline failed: " + std::string(pcap_error)); 112 return false; 113 } 114 115 // Open pcap file for APF filtering 116 ScopedFILE apf_fp(fopen(pcap_filename.c_str(), "rb")); 117 ScopedPcap apf_pcap(pcap_fopen_offline(apf_fp.get(), pcap_error)); 118 if (apf_pcap.get() == NULL) { 119 throwException(env, "pcap_fopen_offline failed: " + std::string(pcap_error)); 120 return false; 121 } 122 123 // Compile "filter" to a BPF program 124 bpf_program bpf; 125 if (pcap_compile(bpf_pcap.get(), &bpf, filter.c_str(), 0, PCAP_NETMASK_UNKNOWN)) { 126 throwException(env, "pcap_compile failed"); 127 return false; 128 } 129 130 // Install BPF filter on bpf_pcap 131 if (pcap_setfilter(bpf_pcap.get(), &bpf)) { 132 throwException(env, "pcap_setfilter failed"); 133 return false; 134 } 135 136 while (1) { 137 pcap_pkthdr bpf_header, apf_header; 138 // Run BPF filter to the next matching packet. 139 const uint8_t* bpf_packet = pcap_next(bpf_pcap.get(), &bpf_header); 140 141 // Run APF filter to the next matching packet. 142 const uint8_t* apf_packet; 143 do { 144 apf_packet = pcap_next(apf_pcap.get(), &apf_header); 145 } while (apf_packet != NULL && !accept_packet( 146 apf_program, apf_program_len, apf_packet, apf_header.len, 0)); 147 148 // Make sure both filters matched the same packet. 149 if (apf_packet == NULL && bpf_packet == NULL) 150 break; 151 if (apf_packet == NULL || bpf_packet == NULL) 152 return false; 153 if (apf_header.len != bpf_header.len || 154 apf_header.ts.tv_sec != bpf_header.ts.tv_sec || 155 apf_header.ts.tv_usec != bpf_header.ts.tv_usec || 156 memcmp(apf_packet, bpf_packet, apf_header.len)) 157 return false; 158 } 159 return true; 160 } 161 162 extern "C" jint JNI_OnLoad(JavaVM* vm, void*) { 163 JNIEnv *env; 164 if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { 165 ALOGE("ERROR: GetEnv failed"); 166 return -1; 167 } 168 169 static JNINativeMethod gMethods[] = { 170 { "apfSimulate", "([B[BI)I", 171 (void*)com_android_server_ApfTest_apfSimulate }, 172 { "compileToBpf", "(Ljava/lang/String;)Ljava/lang/String;", 173 (void*)com_android_server_ApfTest_compileToBpf }, 174 { "compareBpfApf", "(Ljava/lang/String;Ljava/lang/String;[B)Z", 175 (void*)com_android_server_ApfTest_compareBpfApf }, 176 }; 177 178 jniRegisterNativeMethods(env, "android/net/apf/ApfTest", 179 gMethods, ARRAY_SIZE(gMethods)); 180 181 return JNI_VERSION_1_6; 182 } 183