Home | History | Annotate | Download | only in jni
      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