1 /* 2 * Copyright 2018, 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 <nativehelper/JNIHelp.h> 18 #include <nativehelper/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, 32 jbyteArray data, jint filter_age) { 33 uint8_t* program_raw = (uint8_t*)env->GetByteArrayElements(program, nullptr); 34 uint8_t* packet_raw = (uint8_t*)env->GetByteArrayElements(packet, nullptr); 35 uint8_t* data_raw = (uint8_t*)(data ? env->GetByteArrayElements(data, nullptr) : nullptr); 36 uint32_t program_len = env->GetArrayLength(program); 37 uint32_t packet_len = env->GetArrayLength(packet); 38 uint32_t data_len = data ? env->GetArrayLength(data) : 0; 39 40 // Merge program and data into a single buffer. 41 uint8_t* program_and_data = (uint8_t*)malloc(program_len + data_len); 42 memcpy(program_and_data, program_raw, program_len); 43 memcpy(program_and_data + program_len, data_raw, data_len); 44 45 jint result = 46 accept_packet(program_and_data, program_len, program_len + data_len, 47 packet_raw, packet_len, filter_age); 48 if (data) { 49 memcpy(data_raw, program_and_data + program_len, data_len); 50 env->ReleaseByteArrayElements(data, (jbyte*)data_raw, 0 /* copy back */); 51 } 52 free(program_and_data); 53 env->ReleaseByteArrayElements(packet, (jbyte*)packet_raw, JNI_ABORT); 54 env->ReleaseByteArrayElements(program, (jbyte*)program_raw, JNI_ABORT); 55 return result; 56 } 57 58 class ScopedPcap { 59 public: 60 explicit ScopedPcap(pcap_t* pcap) : pcap_ptr(pcap) {} 61 ~ScopedPcap() { 62 pcap_close(pcap_ptr); 63 } 64 65 pcap_t* get() const { return pcap_ptr; }; 66 private: 67 pcap_t* const pcap_ptr; 68 }; 69 70 class ScopedFILE { 71 public: 72 explicit ScopedFILE(FILE* fp) : file(fp) {} 73 ~ScopedFILE() { 74 fclose(file); 75 } 76 77 FILE* get() const { return file; }; 78 private: 79 FILE* const file; 80 }; 81 82 static void throwException(JNIEnv* env, const std::string& error) { 83 jclass newExcCls = env->FindClass("java/lang/IllegalStateException"); 84 if (newExcCls == 0) { 85 abort(); 86 return; 87 } 88 env->ThrowNew(newExcCls, error.c_str()); 89 } 90 91 static jstring com_android_server_ApfTest_compileToBpf(JNIEnv* env, jclass, jstring jfilter) { 92 ScopedUtfChars filter(env, jfilter); 93 std::string bpf_string; 94 ScopedPcap pcap(pcap_open_dead(DLT_EN10MB, 65535)); 95 if (pcap.get() == NULL) { 96 throwException(env, "pcap_open_dead failed"); 97 return NULL; 98 } 99 100 // Compile "filter" to a BPF program 101 bpf_program bpf; 102 if (pcap_compile(pcap.get(), &bpf, filter.c_str(), 0, PCAP_NETMASK_UNKNOWN)) { 103 throwException(env, "pcap_compile failed"); 104 return NULL; 105 } 106 107 // Translate BPF program to human-readable format 108 const struct bpf_insn* insn = bpf.bf_insns; 109 for (uint32_t i = 0; i < bpf.bf_len; i++) { 110 bpf_string += bpf_image(insn++, i); 111 bpf_string += "\n"; 112 } 113 114 return env->NewStringUTF(bpf_string.c_str()); 115 } 116 117 static jboolean com_android_server_ApfTest_compareBpfApf(JNIEnv* env, jclass, jstring jfilter, 118 jstring jpcap_filename, jbyteArray japf_program) { 119 ScopedUtfChars filter(env, jfilter); 120 ScopedUtfChars pcap_filename(env, jpcap_filename); 121 uint8_t* apf_program = (uint8_t*)env->GetByteArrayElements(japf_program, NULL); 122 uint32_t apf_program_len = env->GetArrayLength(japf_program); 123 124 // Open pcap file for BPF filtering 125 ScopedFILE bpf_fp(fopen(pcap_filename.c_str(), "rb")); 126 char pcap_error[PCAP_ERRBUF_SIZE]; 127 ScopedPcap bpf_pcap(pcap_fopen_offline(bpf_fp.get(), pcap_error)); 128 if (bpf_pcap.get() == NULL) { 129 throwException(env, "pcap_fopen_offline failed: " + std::string(pcap_error)); 130 return false; 131 } 132 133 // Open pcap file for APF filtering 134 ScopedFILE apf_fp(fopen(pcap_filename.c_str(), "rb")); 135 ScopedPcap apf_pcap(pcap_fopen_offline(apf_fp.get(), pcap_error)); 136 if (apf_pcap.get() == NULL) { 137 throwException(env, "pcap_fopen_offline failed: " + std::string(pcap_error)); 138 return false; 139 } 140 141 // Compile "filter" to a BPF program 142 bpf_program bpf; 143 if (pcap_compile(bpf_pcap.get(), &bpf, filter.c_str(), 0, PCAP_NETMASK_UNKNOWN)) { 144 throwException(env, "pcap_compile failed"); 145 return false; 146 } 147 148 // Install BPF filter on bpf_pcap 149 if (pcap_setfilter(bpf_pcap.get(), &bpf)) { 150 throwException(env, "pcap_setfilter failed"); 151 return false; 152 } 153 154 while (1) { 155 pcap_pkthdr bpf_header, apf_header; 156 // Run BPF filter to the next matching packet. 157 const uint8_t* bpf_packet = pcap_next(bpf_pcap.get(), &bpf_header); 158 159 // Run APF filter to the next matching packet. 160 const uint8_t* apf_packet; 161 do { 162 apf_packet = pcap_next(apf_pcap.get(), &apf_header); 163 } while (apf_packet != NULL && !accept_packet( 164 apf_program, apf_program_len, 0 /* data_len */, 165 apf_packet, apf_header.len, 0 /* filter_age */)); 166 167 // Make sure both filters matched the same packet. 168 if (apf_packet == NULL && bpf_packet == NULL) 169 break; 170 if (apf_packet == NULL || bpf_packet == NULL) 171 return false; 172 if (apf_header.len != bpf_header.len || 173 apf_header.ts.tv_sec != bpf_header.ts.tv_sec || 174 apf_header.ts.tv_usec != bpf_header.ts.tv_usec || 175 memcmp(apf_packet, bpf_packet, apf_header.len)) 176 return false; 177 } 178 return true; 179 } 180 181 extern "C" jint JNI_OnLoad(JavaVM* vm, void*) { 182 JNIEnv *env; 183 if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { 184 ALOGE("ERROR: GetEnv failed"); 185 return -1; 186 } 187 188 static JNINativeMethod gMethods[] = { 189 { "apfSimulate", "([B[B[BI)I", 190 (void*)com_android_server_ApfTest_apfSimulate }, 191 { "compileToBpf", "(Ljava/lang/String;)Ljava/lang/String;", 192 (void*)com_android_server_ApfTest_compileToBpf }, 193 { "compareBpfApf", "(Ljava/lang/String;Ljava/lang/String;[B)Z", 194 (void*)com_android_server_ApfTest_compareBpfApf }, 195 }; 196 197 jniRegisterNativeMethods(env, "android/net/apf/ApfTest", 198 gMethods, ARRAY_SIZE(gMethods)); 199 200 return JNI_VERSION_1_6; 201 } 202