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