• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
com_android_server_ApfTest_apfSimulate(JNIEnv * env,jclass,jbyteArray program,jbyteArray packet,jbyteArray data,jint filter_age)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:
ScopedPcap(pcap_t * pcap)60     explicit ScopedPcap(pcap_t* pcap) : pcap_ptr(pcap) {}
~ScopedPcap()61     ~ScopedPcap() {
62         pcap_close(pcap_ptr);
63     }
64 
get() const65     pcap_t* get() const { return pcap_ptr; };
66   private:
67     pcap_t* const pcap_ptr;
68 };
69 
70 class ScopedFILE {
71   public:
ScopedFILE(FILE * fp)72     explicit ScopedFILE(FILE* fp) : file(fp) {}
~ScopedFILE()73     ~ScopedFILE() {
74         fclose(file);
75     }
76 
get() const77     FILE* get() const { return file; };
78   private:
79     FILE* const file;
80 };
81 
throwException(JNIEnv * env,const std::string & error)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 
com_android_server_ApfTest_compileToBpf(JNIEnv * env,jclass,jstring jfilter)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 
com_android_server_ApfTest_compareBpfApf(JNIEnv * env,jclass,jstring jfilter,jstring jpcap_filename,jbyteArray japf_program)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 
JNI_OnLoad(JavaVM * vm,void *)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