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