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 <android/log.h>
18 #include <nativehelper/JNIHelp.h>
19 #include <nativehelper/ScopedUtfChars.h>
20 #include <jni.h>
21 #include <pcap.h>
22 #include <stdlib.h>
23 #include <string>
24 #include <vector>
25
26 #include "apf_interpreter.h"
27 #include "disassembler.h"
28 #include "nativehelper/scoped_primitive_array.h"
29 #include "v7/apf_interpreter.h"
30 #include "v7/test_buf_allocator.h"
31
32 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
33 #define LOG_TAG "NetworkStackUtils-JNI"
34
run_apf_interpreter(int apf_version,uint32_t * program,uint32_t program_len,uint32_t ram_len,const uint8_t * packet,uint32_t packet_len,uint32_t filter_age)35 static int run_apf_interpreter(int apf_version, uint32_t* program,
36 uint32_t program_len, uint32_t ram_len,
37 const uint8_t* packet, uint32_t packet_len,
38 uint32_t filter_age) {
39 if (apf_version == 4) {
40 return accept_packet((uint8_t*)program, program_len, ram_len, packet, packet_len,
41 filter_age);
42 } else {
43 return apf_run(nullptr, program, program_len, ram_len, packet, packet_len,
44 filter_age << 14);
45 }
46 }
47
48 // JNI function acting as simply call-through to native APF interpreter.
49 static jint
com_android_server_ApfTest_apfSimulate(JNIEnv * env,jclass,jint apf_version,jbyteArray jprogram,jbyteArray jpacket,jbyteArray jdata,jint filter_age)50 com_android_server_ApfTest_apfSimulate(JNIEnv* env, jclass, jint apf_version,
51 jbyteArray jprogram, jbyteArray jpacket,
52 jbyteArray jdata, jint filter_age) {
53
54 ScopedByteArrayRO packet(env, jpacket);
55 uint32_t packet_len = (uint32_t)packet.size();
56 uint32_t program_len = env->GetArrayLength(jprogram);
57 uint32_t data_len = jdata ? env->GetArrayLength(jdata) : 0;
58 // we need to guarantee room for APFv6's 5 u32 counters (20 bytes)
59 // and we need to make sure ram_len is a multiple of 4 bytes,
60 // so that the counters (which are indexed from the back are aligned.
61 uint32_t ram_len = program_len + data_len;
62 if (apf_version > 4) {
63 ram_len += 3; ram_len &= ~3;
64 if (data_len < 20) ram_len += 20;
65 }
66 std::vector<uint32_t> buf((ram_len + 3) / 4, 0);
67 jbyte* jbuf = reinterpret_cast<jbyte*>(buf.data());
68
69 env->GetByteArrayRegion(jprogram, 0, program_len, jbuf);
70 if (jdata) {
71 // Merge program and data into a single buffer.
72 env->GetByteArrayRegion(jdata, 0, data_len, jbuf + ram_len - data_len);
73 }
74
75 jint result = run_apf_interpreter(
76 apf_version, buf.data(), program_len, ram_len,
77 reinterpret_cast<const uint8_t *>(packet.get()), packet_len,
78 filter_age);
79
80 if (jdata) {
81 env->SetByteArrayRegion(jdata, 0, data_len, jbuf + ram_len - data_len);
82 }
83
84 return result;
85 }
86
87 class ScopedPcap {
88 public:
ScopedPcap(pcap_t * pcap)89 explicit ScopedPcap(pcap_t* pcap) : pcap_ptr(pcap) {}
~ScopedPcap()90 ~ScopedPcap() {
91 pcap_close(pcap_ptr);
92 }
93
get() const94 pcap_t* get() const { return pcap_ptr; };
95 private:
96 pcap_t* const pcap_ptr;
97 };
98
99 class ScopedFILE {
100 public:
ScopedFILE(FILE * fp)101 explicit ScopedFILE(FILE* fp) : file(fp) {}
~ScopedFILE()102 ~ScopedFILE() {
103 fclose(file);
104 }
105
get() const106 FILE* get() const { return file; };
107 private:
108 FILE* const file;
109 };
110
throwException(JNIEnv * env,const std::string & error)111 static void throwException(JNIEnv* env, const std::string& error) {
112 jclass newExcCls = env->FindClass("java/lang/IllegalStateException");
113 if (newExcCls == 0) {
114 abort();
115 return;
116 }
117 env->ThrowNew(newExcCls, error.c_str());
118 }
119
com_android_server_ApfTest_compileToBpf(JNIEnv * env,jclass,jstring jfilter)120 static jstring com_android_server_ApfTest_compileToBpf(JNIEnv* env, jclass, jstring jfilter) {
121 ScopedUtfChars filter(env, jfilter);
122 std::string bpf_string;
123 ScopedPcap pcap(pcap_open_dead(DLT_EN10MB, 65535));
124 if (pcap.get() == NULL) {
125 throwException(env, "pcap_open_dead failed");
126 return NULL;
127 }
128
129 // Compile "filter" to a BPF program
130 bpf_program bpf;
131 if (pcap_compile(pcap.get(), &bpf, filter.c_str(), 0, PCAP_NETMASK_UNKNOWN)) {
132 throwException(env, "pcap_compile failed");
133 return NULL;
134 }
135
136 // Translate BPF program to human-readable format
137 const struct bpf_insn* insn = bpf.bf_insns;
138 for (uint32_t i = 0; i < bpf.bf_len; i++) {
139 bpf_string += bpf_image(insn++, i);
140 bpf_string += "\n";
141 }
142
143 return env->NewStringUTF(bpf_string.c_str());
144 }
145
com_android_server_ApfTest_compareBpfApf(JNIEnv * env,jclass,jint apf_version,jstring jfilter,jstring jpcap_filename,jbyteArray japf_program)146 static jboolean com_android_server_ApfTest_compareBpfApf(
147 JNIEnv* env, jclass, jint apf_version, jstring jfilter,
148 jstring jpcap_filename, jbyteArray japf_program) {
149 ScopedUtfChars filter(env, jfilter);
150 ScopedUtfChars pcap_filename(env, jpcap_filename);
151 uint32_t program_len = env->GetArrayLength(japf_program);
152 uint32_t data_len = (apf_version > 4) ? 20 : 0;
153 uint32_t ram_len = program_len + data_len;
154 if (apf_version > 4) { ram_len += 3; ram_len &= ~3; }
155 std::vector<uint32_t> apf_program((ram_len + 3) / 4, 0);
156 env->GetByteArrayRegion(japf_program, 0, program_len,
157 reinterpret_cast<jbyte*>(apf_program.data()));
158
159 // Open pcap file for BPF filtering
160 ScopedFILE bpf_fp(fopen(pcap_filename.c_str(), "rb"));
161 char pcap_error[PCAP_ERRBUF_SIZE];
162 ScopedPcap bpf_pcap(pcap_fopen_offline(bpf_fp.get(), pcap_error));
163 if (bpf_pcap.get() == NULL) {
164 throwException(env, "pcap_fopen_offline failed: " + std::string(pcap_error));
165 return false;
166 }
167
168 // Open pcap file for APF filtering
169 ScopedFILE apf_fp(fopen(pcap_filename.c_str(), "rb"));
170 ScopedPcap apf_pcap(pcap_fopen_offline(apf_fp.get(), pcap_error));
171 if (apf_pcap.get() == NULL) {
172 throwException(env, "pcap_fopen_offline failed: " + std::string(pcap_error));
173 return false;
174 }
175
176 // Compile "filter" to a BPF program
177 bpf_program bpf;
178 if (pcap_compile(bpf_pcap.get(), &bpf, filter.c_str(), 0, PCAP_NETMASK_UNKNOWN)) {
179 throwException(env, "pcap_compile failed");
180 return false;
181 }
182
183 // Install BPF filter on bpf_pcap
184 if (pcap_setfilter(bpf_pcap.get(), &bpf)) {
185 throwException(env, "pcap_setfilter failed");
186 return false;
187 }
188
189 while (1) {
190 pcap_pkthdr bpf_header, apf_header;
191 // Run BPF filter to the next matching packet.
192 const uint8_t* bpf_packet = pcap_next(bpf_pcap.get(), &bpf_header);
193
194 // Run APF filter to the next matching packet.
195 const uint8_t* apf_packet;
196 do {
197 apf_packet = pcap_next(apf_pcap.get(), &apf_header);
198 } while (apf_packet != NULL && !run_apf_interpreter(apf_version,
199 apf_program.data(), program_len, ram_len,
200 apf_packet, apf_header.len, 0 /* filter_age */));
201
202 // Make sure both filters matched the same packet.
203 if (apf_packet == NULL && bpf_packet == NULL)
204 break;
205 if (apf_packet == NULL || bpf_packet == NULL)
206 return false;
207 if (apf_header.len != bpf_header.len ||
208 apf_header.ts.tv_sec != bpf_header.ts.tv_sec ||
209 apf_header.ts.tv_usec != bpf_header.ts.tv_usec ||
210 memcmp(apf_packet, bpf_packet, apf_header.len))
211 return false;
212 }
213 return true;
214 }
215
com_android_server_ApfTest_dropsAllPackets(JNIEnv * env,jclass,jint apf_version,jbyteArray jprogram,jbyteArray jdata,jstring jpcap_filename)216 static jboolean com_android_server_ApfTest_dropsAllPackets(
217 JNIEnv* env, jclass, jint apf_version, jbyteArray jprogram,
218 jbyteArray jdata, jstring jpcap_filename) {
219 ScopedUtfChars pcap_filename(env, jpcap_filename);
220 ScopedByteArrayRO apf_program(env, jprogram);
221 uint32_t apf_program_len = (uint32_t)apf_program.size();
222 uint32_t data_len = env->GetArrayLength(jdata);
223 uint32_t ram_len = apf_program_len + data_len;
224 if (apf_version > 4) {
225 ram_len += 3; ram_len &= ~3;
226 if (data_len < 20) ram_len += 20;
227 }
228 pcap_pkthdr apf_header;
229 const uint8_t* apf_packet;
230 char pcap_error[PCAP_ERRBUF_SIZE];
231 std::vector<uint32_t> buf((ram_len + 3) / 4, 0);
232 jbyte* jbuf = reinterpret_cast<jbyte*>(buf.data());
233
234 // Merge program and data into a single buffer.
235 env->GetByteArrayRegion(jprogram, 0, apf_program_len, jbuf);
236 env->GetByteArrayRegion(jdata, 0, data_len, jbuf + ram_len - data_len);
237
238 // Open pcap file
239 ScopedFILE apf_fp(fopen(pcap_filename.c_str(), "rb"));
240 ScopedPcap apf_pcap(pcap_fopen_offline(apf_fp.get(), pcap_error));
241
242 if (apf_pcap.get() == NULL) {
243 throwException(env, "pcap_fopen_offline failed: " + std::string(pcap_error));
244 return false;
245 }
246
247 while ((apf_packet = pcap_next(apf_pcap.get(), &apf_header)) != NULL) {
248 int result = run_apf_interpreter(
249 apf_version, buf.data(), apf_program_len, ram_len, apf_packet, apf_header.len, 0);
250
251 // Return false once packet passes the filter
252 if (result) {
253 env->SetByteArrayRegion(jdata, 0, data_len, jbuf + ram_len - data_len);
254 return false;
255 }
256 }
257
258 env->SetByteArrayRegion(jdata, 0, data_len, jbuf + ram_len - data_len);
259 return true;
260 }
261
com_android_server_ApfTest_disassembleApf(JNIEnv * env,jclass,jbyteArray jprogram)262 static jobjectArray com_android_server_ApfTest_disassembleApf(
263 JNIEnv* env, jclass, jbyteArray jprogram) {
264 uint32_t program_len = env->GetArrayLength(jprogram);
265 std::vector<uint8_t> buf(program_len, 0);
266
267 env->GetByteArrayRegion(jprogram, 0, program_len,
268 reinterpret_cast<jbyte*>(buf.data()));
269 std::vector<std::string> disassemble_output;
270 for (uint32_t pc = 0; pc < program_len;) {
271 disassemble_output.emplace_back(apf_disassemble(buf.data(), program_len, &pc));
272 }
273 jclass stringClass = env->FindClass("java/lang/String");
274 jobjectArray disassembleOutput =
275 env->NewObjectArray(disassemble_output.size(), stringClass, nullptr);
276
277 for (jsize i = 0; i < (jsize) disassemble_output.size(); i++) {
278 jstring j_disassemble_output =
279 env->NewStringUTF(disassemble_output[i].c_str());
280 env->SetObjectArrayElement(disassembleOutput, i, j_disassemble_output);
281 env->DeleteLocalRef(j_disassemble_output);
282 }
283
284 return disassembleOutput;
285 }
286
com_android_server_ApfTest_getTransmittedPacket(JNIEnv * env,jclass)287 jbyteArray com_android_server_ApfTest_getTransmittedPacket(JNIEnv* env,
288 jclass) {
289 jbyteArray jdata = env->NewByteArray((jint) apf_test_tx_packet_len);
290 if (jdata == NULL) { return NULL; }
291 if (apf_test_tx_packet_len == 0) { return jdata; }
292
293 env->SetByteArrayRegion(jdata, 0, (jint) apf_test_tx_packet_len,
294 reinterpret_cast<jbyte*>(apf_test_buffer));
295
296 return jdata;
297 }
298
com_android_server_ApfTest_resetTransmittedPacketMemory(JNIEnv,jclass)299 void com_android_server_ApfTest_resetTransmittedPacketMemory(JNIEnv, jclass) {
300 apf_test_tx_packet_len = 0;
301 memset(apf_test_buffer, 0xff, sizeof(apf_test_buffer));
302 }
303
JNI_OnLoad(JavaVM * vm,void *)304 extern "C" jint JNI_OnLoad(JavaVM* vm, void*) {
305 JNIEnv *env;
306 if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
307 __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "ERROR: GetEnv failed");
308 return -1;
309 }
310
311 static JNINativeMethod gMethods[] = {
312 { "apfSimulate", "(I[B[B[BI)I",
313 (void*)com_android_server_ApfTest_apfSimulate },
314 { "compileToBpf", "(Ljava/lang/String;)Ljava/lang/String;",
315 (void*)com_android_server_ApfTest_compileToBpf },
316 { "compareBpfApf", "(ILjava/lang/String;Ljava/lang/String;[B)Z",
317 (void*)com_android_server_ApfTest_compareBpfApf },
318 { "dropsAllPackets", "(I[B[BLjava/lang/String;)Z",
319 (void*)com_android_server_ApfTest_dropsAllPackets },
320 { "disassembleApf", "([B)[Ljava/lang/String;",
321 (void*)com_android_server_ApfTest_disassembleApf },
322 { "getTransmittedPacket", "()[B",
323 (void*)com_android_server_ApfTest_getTransmittedPacket },
324 { "resetTransmittedPacketMemory", "()V",
325 (void*)com_android_server_ApfTest_resetTransmittedPacketMemory },
326 };
327
328 jniRegisterNativeMethods(env, "android/net/apf/ApfJniUtils",
329 gMethods, ARRAY_SIZE(gMethods));
330
331 return JNI_VERSION_1_6;
332 }
333