• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
com_android_server_ApfTest_apfSimulate(JNIEnv * env,jclass,jbyteArray program,jbyteArray packet,jint filter_age)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:
ScopedPcap(pcap_t * pcap)42     ScopedPcap(pcap_t* pcap) : pcap_ptr(pcap) {}
~ScopedPcap()43     ~ScopedPcap() {
44         pcap_close(pcap_ptr);
45     }
46 
get() const47     pcap_t* get() const { return pcap_ptr; };
48   private:
49     pcap_t* const pcap_ptr;
50 };
51 
52 class ScopedFILE {
53   public:
ScopedFILE(FILE * fp)54     ScopedFILE(FILE* fp) : file(fp) {}
~ScopedFILE()55     ~ScopedFILE() {
56         fclose(file);
57     }
58 
get() const59     FILE* get() const { return file; };
60   private:
61     FILE* const file;
62 };
63 
throwException(JNIEnv * env,const std::string & error)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 
com_android_server_ApfTest_compileToBpf(JNIEnv * env,jclass,jstring jfilter)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 
com_android_server_ApfTest_compareBpfApf(JNIEnv * env,jclass,jstring jfilter,jstring jpcap_filename,jbyteArray japf_program)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 
JNI_OnLoad(JavaVM * vm,void *)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