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:
42d38fb7662d0e525eee57062ee8c14f661b1aee2dPaul Jensen    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:
54d38fb7662d0e525eee57062ee8c14f661b1aee2dPaul Jensen    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