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 // Simple program to try running an APF program against a packet.
18
19 #include <errno.h>
20 #include <getopt.h>
21 #include <inttypes.h>
22 #include <libgen.h>
23 #include <limits.h>
24 #include <pcap.h>
25 #include <stdint.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29
30 #include "disassembler.h"
31 #include "v4/apf_interpreter.h"
32 #include "next/apf_interpreter.h"
33 #include "next/test_buf_allocator.h"
34
35 #define __unused __attribute__((unused))
36
37 // The following list must be in sync with
38 // https://cs.android.com/android/platform/superproject/main/+/main:packages/modules/NetworkStack/src/android/net/apf/ApfFilter.java;l=125
39 static const char* counter_name [] = {
40 "RESERVED_OOB",
41 "TOTAL_PACKETS",
42 "PASSED_ARP",
43 "PASSED_DHCP",
44 "PASSED_IPV4",
45 "PASSED_IPV6_NON_ICMP",
46 "PASSED_IPV4_UNICAST",
47 "PASSED_IPV6_ICMP",
48 "PASSED_IPV6_UNICAST_NON_ICMP",
49 "PASSED_ARP_NON_IPV4",
50 "PASSED_ARP_UNKNOWN",
51 "PASSED_ARP_UNICAST_REPLY",
52 "PASSED_NON_IP_UNICAST",
53 "PASSED_MDNS",
54 "DROPPED_ETH_BROADCAST",
55 "DROPPED_RA",
56 "DROPPED_GARP_REPLY",
57 "DROPPED_ARP_OTHER_HOST",
58 "DROPPED_IPV4_L2_BROADCAST",
59 "DROPPED_IPV4_BROADCAST_ADDR",
60 "DROPPED_IPV4_BROADCAST_NET",
61 "DROPPED_IPV4_MULTICAST",
62 "DROPPED_IPV6_ROUTER_SOLICITATION",
63 "DROPPED_IPV6_MULTICAST_NA",
64 "DROPPED_IPV6_MULTICAST",
65 "DROPPED_IPV6_MULTICAST_PING",
66 "DROPPED_IPV6_NON_ICMP_MULTICAST",
67 "DROPPED_802_3_FRAME",
68 "DROPPED_ETHERTYPE_BLACKLISTED",
69 "DROPPED_ARP_REPLY_SPA_NO_HOST",
70 "DROPPED_IPV4_KEEPALIVE_ACK",
71 "DROPPED_IPV6_KEEPALIVE_ACK",
72 "DROPPED_IPV4_NATT_KEEPALIVE",
73 "DROPPED_MDNS"
74 };
75
76 enum {
77 OPT_PROGRAM,
78 OPT_PACKET,
79 OPT_PCAP,
80 OPT_DATA,
81 OPT_AGE,
82 OPT_TRACE,
83 OPT_V6,
84 };
85
86 const struct option long_options[] = {{"program", 1, NULL, OPT_PROGRAM},
87 {"packet", 1, NULL, OPT_PACKET},
88 {"pcap", 1, NULL, OPT_PCAP},
89 {"data", 1, NULL, OPT_DATA},
90 {"age", 1, NULL, OPT_AGE},
91 {"trace", 0, NULL, OPT_TRACE},
92 {"v6", 0, NULL, OPT_V6},
93 {"help", 0, NULL, 'h'},
94 {"cnt", 0, NULL, 'c'},
95 {NULL, 0, NULL, 0}};
96
97 const int COUNTER_SIZE = 4;
98
99 // Parses hex in "input". Allocates and fills "*output" with parsed bytes.
100 // Returns length in bytes of "*output".
parse_hex(const char * input,uint8_t ** output)101 size_t parse_hex(const char* input, uint8_t** output) {
102 int length = strlen(input);
103 if (length & 1) {
104 fprintf(stderr, "Argument not even number of characters: %s\n", input);
105 exit(1);
106 }
107 length >>= 1;
108 *output = malloc(length);
109 if (*output == NULL) {
110 fprintf(stderr, "Out of memory, tried to allocate %d\n", length);
111 exit(1);
112 }
113 for (int i = 0; i < length; i++) {
114 char byte[3] = { input[i*2], input[i*2+1], 0 };
115 char* end_ptr;
116 (*output)[i] = strtol(byte, &end_ptr, 16);
117 if (end_ptr != byte + 2) {
118 fprintf(stderr, "Failed to parse hex %s\n", byte);
119 exit(1);
120 }
121 }
122 return length;
123 }
124
print_hex(const uint8_t * input,int len)125 void print_hex(const uint8_t* input, int len) {
126 for (int i = 0; i < len; ++i) {
127 printf("%02x", input[i]);
128 }
129 }
130
get_counter_value(const uint8_t * data,int data_len,int neg_offset)131 uint32_t get_counter_value(const uint8_t* data, int data_len, int neg_offset) {
132 if (neg_offset > -COUNTER_SIZE || neg_offset + data_len < 0) {
133 return 0;
134 }
135 uint32_t value = 0;
136 for (int i = 0; i < 4; ++i) {
137 value = value << 8 | data[data_len + neg_offset];
138 neg_offset++;
139 }
140 return value;
141 }
142
print_counter(const uint8_t * data,int data_len)143 void print_counter(const uint8_t* data, int data_len) {
144 int counter_len = sizeof(counter_name) / sizeof(counter_name[0]);
145 for (int i = 0; i < counter_len; ++i) {
146 uint32_t value = get_counter_value(data, data_len, -COUNTER_SIZE * i);
147 if (value != 0) {
148 printf("%s : %d \n", counter_name[i], value);
149 }
150 }
151 }
152
153 int tracing_enabled = 0;
154
maybe_print_tracing_header()155 void maybe_print_tracing_header() {
156 if (!tracing_enabled) return;
157
158 printf(" R0 R1 (size) PC Instruction\n");
159 printf("-------------------------------------------------\n");
160
161 }
162
print_all_transmitted_packets()163 void print_all_transmitted_packets() {
164 printf("transmitted packet: \n");
165 packet_buffer* current = head;
166 while (current) {
167 printf("\t");
168 print_hex(current->data, (int) current->len);
169 printf("\n");
170 current = current->next;
171 }
172 }
173
174 // Process packet through APF filter
packet_handler(int use_apf_v6_interpreter,uint8_t * program,uint32_t program_len,uint32_t ram_len,const char * pkt,uint32_t filter_age)175 void packet_handler(int use_apf_v6_interpreter, uint8_t* program,
176 uint32_t program_len, uint32_t ram_len, const char* pkt, uint32_t filter_age) {
177 uint8_t* packet;
178 uint32_t packet_len = parse_hex(pkt, &packet);
179
180 maybe_print_tracing_header();
181
182 int ret;
183 if (use_apf_v6_interpreter) {
184 ret = apf_run(NULL, (uint32_t*)program, program_len, ram_len, packet, packet_len,
185 filter_age);
186 } else {
187 ret = accept_packet(program, program_len, ram_len, packet, packet_len,
188 filter_age);
189 }
190 printf("Packet %sed\n", ret ? "pass" : "dropp");
191
192 free(packet);
193 }
194
195 static int use_apf_v6_interpreter = 0;
196
apf_trace_hook(uint32_t pc,const uint32_t * regs,const uint8_t * program,uint32_t program_len,const uint8_t * packet __unused,uint32_t packet_len __unused,const uint32_t * memory __unused,uint32_t memory_len __unused)197 void apf_trace_hook(uint32_t pc, const uint32_t* regs, const uint8_t* program, uint32_t program_len,
198 const uint8_t* packet __unused, uint32_t packet_len __unused,
199 const uint32_t* memory __unused, uint32_t memory_len __unused) {
200 if (!tracing_enabled) return;
201
202 printf("%8" PRIx32 " %8" PRIx32 " ", regs[0], regs[1]);
203 const disas_ret ret = apf_disassemble(program, program_len, &pc, use_apf_v6_interpreter);
204 printf("%s%s\n", ret.prefix, ret.content);
205 }
206
207 // Process pcap file through APF filter and generate output files
file_handler(int use_apf_v6_interpreter,uint8_t * program,uint32_t program_len,uint32_t ram_len,const char * filename,uint32_t filter_age)208 void file_handler(int use_apf_v6_interpreter, uint8_t* program,
209 uint32_t program_len, uint32_t ram_len, const char* filename,
210 uint32_t filter_age) {
211 char errbuf[PCAP_ERRBUF_SIZE];
212 pcap_t *pcap;
213 struct pcap_pkthdr apf_header;
214 const uint8_t* apf_packet;
215 pcap_dumper_t *passed_dumper, *dropped_dumper;
216 const char passed_file[] = "passed.pcap";
217 const char dropped_file[] = "dropped.pcap";
218 int pass = 0;
219 int drop = 0;
220
221 pcap = pcap_open_offline(filename, errbuf);
222 if (pcap == NULL) {
223 printf("Open pcap file failed.\n");
224 exit(1);
225 }
226
227 passed_dumper = pcap_dump_open(pcap, passed_file);
228 dropped_dumper = pcap_dump_open(pcap, dropped_file);
229
230 if (!passed_dumper || !dropped_dumper) {
231 printf("pcap_dump_open(): open output file failed.\n");
232 pcap_close(pcap);
233 exit(1);
234 }
235
236 while ((apf_packet = pcap_next(pcap, &apf_header)) != NULL) {
237 maybe_print_tracing_header();
238
239 int result;
240 if (use_apf_v6_interpreter) {
241 result = apf_run(NULL, (uint32_t*)program, program_len, ram_len, apf_packet,
242 apf_header.len, filter_age);
243 } else {
244 result = accept_packet(program, program_len, ram_len, apf_packet,
245 apf_header.len, filter_age);
246 }
247
248 if (!result){
249 drop++;
250 pcap_dump((u_char*)dropped_dumper, &apf_header, apf_packet);
251 } else {
252 pass++;
253 pcap_dump((u_char*)passed_dumper, &apf_header, apf_packet);
254 }
255 }
256
257 printf("%d packets dropped\n", drop);
258 printf("%d packets passed\n", pass);
259 pcap_dump_close(passed_dumper);
260 pcap_dump_close(dropped_dumper);
261 pcap_close(pcap);
262 }
263
print_usage(char * cmd)264 void print_usage(char* cmd) {
265 fprintf(stderr,
266 "Usage: %s --program <program> --pcap <file>|--packet <packet> "
267 "[--data <content>] [--age <number>] [--trace]\n"
268 " --program APF program, in hex.\n"
269 " --pcap Pcap file to run through program.\n"
270 " --packet Packet to run through program.\n"
271 " --data Data memory contents, in hex.\n"
272 " --age Age of program in seconds (default: 0).\n"
273 " --trace Enable APF interpreter debug tracing\n"
274 " --v6 Use APF v6\n"
275 " -c, --cnt Print the APF counters\n"
276 " -h, --help Show this message.\n",
277 basename(cmd));
278 }
279
main(int argc,char * argv[])280 int main(int argc, char* argv[]) {
281 uint8_t* program = NULL;
282 uint32_t program_len;
283 const char* filename = NULL;
284 char* packet = NULL;
285 uint8_t* data = NULL;
286 uint32_t data_len = 0;
287 uint32_t filter_age = 0;
288 int print_counter_enabled = 0;
289
290 int opt;
291 char *endptr;
292
293 while ((opt = getopt_long_only(argc, argv, "ch", long_options, NULL)) != -1) {
294 switch (opt) {
295 case OPT_PROGRAM:
296 program_len = parse_hex(optarg, &program);
297 break;
298 case OPT_PACKET:
299 if (!program) {
300 printf("<packet> requires <program> first\n\'%s -h or --help\' "
301 "for more information\n", basename(argv[0]));
302 exit(1);
303 }
304 if (filename) {
305 printf("Cannot use <file> with <packet> \n\'%s -h or --help\' "
306 "for more information\n", basename(argv[0]));
307
308 exit(1);
309 }
310 packet = optarg;
311 break;
312 case OPT_PCAP:
313 if (!program) {
314 printf("<file> requires <program> first\n\'%s -h or --help\' "
315 "for more information\n", basename(argv[0]));
316
317 exit(1);
318 }
319 if (packet) {
320 printf("Cannot use <packet> with <file>\n\'%s -h or --help\' "
321 "for more information\n", basename(argv[0]));
322
323 exit(1);
324 }
325 filename = optarg;
326 break;
327 case OPT_DATA:
328 data_len = parse_hex(optarg, &data);
329 break;
330 case OPT_AGE:
331 errno = 0;
332 filter_age = strtoul(optarg, &endptr, 10);
333 if ((errno == ERANGE && filter_age == UINT32_MAX) ||
334 (errno != 0 && filter_age == 0)) {
335 perror("Error on age option: strtoul");
336 exit(1);
337 }
338 if (endptr == optarg) {
339 printf("No digit found in age.\n");
340 exit(1);
341 }
342 break;
343 case OPT_TRACE:
344 tracing_enabled = 1;
345 break;
346 case OPT_V6:
347 use_apf_v6_interpreter = 1;
348 break;
349 case 'h':
350 print_usage(argv[0]);
351 exit(0);
352 break;
353 case 'c':
354 print_counter_enabled = 1;
355 break;
356 default:
357 print_usage(argv[0]);
358 exit(1);
359 break;
360 }
361 }
362
363 if (!program) {
364 printf("Must have APF program in option.\n");
365 exit(1);
366 }
367
368 if (!filename && !packet) {
369 printf("Missing file or packet after program.\n");
370 exit(1);
371 }
372
373 // Combine the program and data into the unified APF buffer.
374 uint32_t ram_len = program_len + data_len;
375 if (use_apf_v6_interpreter) {
376 ram_len += 3;
377 ram_len &= ~3;
378 if (data_len < 20) ram_len += 20;
379 }
380
381 if (data) {
382 program = realloc(program, ram_len);
383 memcpy(program + ram_len - data_len, data, data_len);
384 free(data);
385 }
386
387 if (filename)
388 file_handler(use_apf_v6_interpreter, program, program_len, ram_len,
389 filename, filter_age);
390 else
391 packet_handler(use_apf_v6_interpreter, program, program_len, ram_len,
392 packet, filter_age);
393
394 if (data_len) {
395 printf("Data: ");
396 print_hex(program + ram_len - data_len, data_len);
397 printf("\n");
398 if (print_counter_enabled) {
399 printf("APF packet counters: \n");
400 print_counter(program + ram_len - data_len, data_len);
401 }
402 }
403
404 if (use_apf_v6_interpreter && head != NULL) {
405 print_all_transmitted_packets();
406 }
407
408 free(program);
409 return 0;
410 }
411