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 "apf_interpreter.h"
32
33 #define __unused __attribute__((unused))
34
35 enum {
36 OPT_PROGRAM,
37 OPT_PACKET,
38 OPT_PCAP,
39 OPT_DATA,
40 OPT_AGE,
41 OPT_TRACE,
42 };
43
44 const struct option long_options[] = {{"program", 1, NULL, OPT_PROGRAM},
45 {"packet", 1, NULL, OPT_PACKET},
46 {"pcap", 1, NULL, OPT_PCAP},
47 {"data", 1, NULL, OPT_DATA},
48 {"age", 1, NULL, OPT_AGE},
49 {"trace", 0, NULL, OPT_TRACE},
50 {"help", 0, NULL, 'h'},
51 {NULL, 0, NULL, 0}};
52
53 // Parses hex in "input". Allocates and fills "*output" with parsed bytes.
54 // Returns length in bytes of "*output".
parse_hex(const char * input,uint8_t ** output)55 size_t parse_hex(const char* input, uint8_t** output) {
56 int length = strlen(input);
57 if (length & 1) {
58 fprintf(stderr, "Argument not even number of characters: %s\n", input);
59 exit(1);
60 }
61 length >>= 1;
62 *output = malloc(length);
63 if (*output == NULL) {
64 fprintf(stderr, "Out of memory, tried to allocate %d\n", length);
65 exit(1);
66 }
67 for (int i = 0; i < length; i++) {
68 char byte[3] = { input[i*2], input[i*2+1], 0 };
69 char* end_ptr;
70 (*output)[i] = strtol(byte, &end_ptr, 16);
71 if (end_ptr != byte + 2) {
72 fprintf(stderr, "Failed to parse hex %s\n", byte);
73 exit(1);
74 }
75 }
76 return length;
77 }
78
print_hex(const uint8_t * input,int len)79 void print_hex(const uint8_t* input, int len) {
80 for (int i = 0; i < len; ++i) {
81 printf("%02x", input[i]);
82 }
83 }
84
85 int tracing_enabled = 0;
86
maybe_print_tracing_header()87 void maybe_print_tracing_header() {
88 if (!tracing_enabled) return;
89
90 printf(" R0 R1 PC Instruction\n");
91 printf("-------------------------------------------------\n");
92
93 }
94
95 // Process packet through APF filter
packet_handler(uint8_t * program,uint32_t program_len,uint32_t ram_len,const char * pkt,uint32_t filter_age)96 void packet_handler(uint8_t* program, uint32_t program_len, uint32_t ram_len,
97 const char* pkt, uint32_t filter_age) {
98 uint8_t* packet;
99 uint32_t packet_len = parse_hex(pkt, &packet);
100
101 maybe_print_tracing_header();
102
103 int ret = accept_packet(program, program_len, ram_len, packet, packet_len,
104 filter_age);
105 printf("Packet %sed\n", ret ? "pass" : "dropp");
106
107 free(packet);
108 }
109
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)110 void apf_trace_hook(uint32_t pc, const uint32_t* regs, const uint8_t* program, uint32_t program_len,
111 const uint8_t* packet __unused, uint32_t packet_len __unused,
112 const uint32_t* memory __unused, uint32_t memory_len __unused) {
113 if (!tracing_enabled) return;
114
115 printf("%8" PRIx32 " %8" PRIx32 " ", regs[0], regs[1]);
116 apf_disassemble(program, program_len, pc);
117 }
118
119 // Process pcap file through APF filter and generate output files
file_handler(uint8_t * program,uint32_t program_len,uint32_t ram_len,const char * filename,uint32_t filter_age)120 void file_handler(uint8_t* program, uint32_t program_len, uint32_t ram_len, const char* filename,
121 uint32_t filter_age) {
122 char errbuf[PCAP_ERRBUF_SIZE];
123 pcap_t *pcap;
124 struct pcap_pkthdr apf_header;
125 const uint8_t* apf_packet;
126 pcap_dumper_t *passed_dumper, *dropped_dumper;
127 const char passed_file[] = "passed.pcap";
128 const char dropped_file[] = "dropped.pcap";
129 int pass = 0;
130 int drop = 0;
131
132 pcap = pcap_open_offline(filename, errbuf);
133 if (pcap == NULL) {
134 printf("Open pcap file failed.\n");
135 exit(1);
136 }
137
138 passed_dumper = pcap_dump_open(pcap, passed_file);
139 dropped_dumper = pcap_dump_open(pcap, dropped_file);
140
141 if (!passed_dumper || !dropped_dumper) {
142 printf("pcap_dump_open(): open output file failed.\n");
143 pcap_close(pcap);
144 exit(1);
145 }
146
147 while ((apf_packet = pcap_next(pcap, &apf_header)) != NULL) {
148 maybe_print_tracing_header();
149
150 int result = accept_packet(program, program_len, ram_len, apf_packet,
151 apf_header.len, filter_age);
152
153 if (!result){
154 drop++;
155 pcap_dump((u_char*)dropped_dumper, &apf_header, apf_packet);
156 } else {
157 pass++;
158 pcap_dump((u_char*)passed_dumper, &apf_header, apf_packet);
159 }
160 }
161
162 printf("%d packets dropped\n", drop);
163 printf("%d packets passed\n", pass);
164 pcap_dump_close(passed_dumper);
165 pcap_dump_close(dropped_dumper);
166 pcap_close(pcap);
167 }
168
print_usage(char * cmd)169 void print_usage(char* cmd) {
170 fprintf(stderr,
171 "Usage: %s --program <program> --pcap <file>|--packet <packet> "
172 "[--data <content>] [--age <number>] [--trace]\n"
173 " --program APF program, in hex.\n"
174 " --pcap Pcap file to run through program.\n"
175 " --packet Packet to run through program.\n"
176 " --data Data memory contents, in hex.\n"
177 " --age Age of program in seconds (default: 0).\n"
178 " --trace Enable APF interpreter debug tracing\n"
179 " -h, --help Show this message.\n",
180 basename(cmd));
181 }
182
main(int argc,char * argv[])183 int main(int argc, char* argv[]) {
184 uint8_t* program = NULL;
185 uint32_t program_len;
186 const char* filename = NULL;
187 char* packet = NULL;
188 uint8_t* data = NULL;
189 uint32_t data_len = 0;
190 uint32_t filter_age = 0;
191
192 int opt;
193 char *endptr;
194
195 while ((opt = getopt_long_only(argc, argv, "h", long_options, NULL)) != -1) {
196 switch (opt) {
197 case OPT_PROGRAM:
198 program_len = parse_hex(optarg, &program);
199 break;
200 case OPT_PACKET:
201 if (!program) {
202 printf("<packet> requires <program> first\n\'%s -h or --help\' "
203 "for more information\n", basename(argv[0]));
204 exit(1);
205 }
206 if (filename) {
207 printf("Cannot use <file> with <packet> \n\'%s -h or --help\' "
208 "for more information\n", basename(argv[0]));
209
210 exit(1);
211 }
212 packet = optarg;
213 break;
214 case OPT_PCAP:
215 if (!program) {
216 printf("<file> requires <program> first\n\'%s -h or --help\' "
217 "for more information\n", basename(argv[0]));
218
219 exit(1);
220 }
221 if (packet) {
222 printf("Cannot use <packet> with <file>\n\'%s -h or --help\' "
223 "for more information\n", basename(argv[0]));
224
225 exit(1);
226 }
227 filename = optarg;
228 break;
229 case OPT_DATA:
230 data_len = parse_hex(optarg, &data);
231 break;
232 case OPT_AGE:
233 errno = 0;
234 filter_age = strtoul(optarg, &endptr, 10);
235 if ((errno == ERANGE && filter_age == UINT32_MAX) ||
236 (errno != 0 && filter_age == 0)) {
237 perror("Error on age option: strtoul");
238 exit(1);
239 }
240 if (endptr == optarg) {
241 printf("No digit found in age.\n");
242 exit(1);
243 }
244 break;
245 case OPT_TRACE:
246 tracing_enabled = 1;
247 break;
248 case 'h':
249 print_usage(argv[0]);
250 exit(0);
251 break;
252 default:
253 print_usage(argv[0]);
254 exit(1);
255 break;
256 }
257 }
258
259 if (!program) {
260 printf("Must have APF program in option.\n");
261 exit(1);
262 }
263
264 if (!filename && !packet) {
265 printf("Missing file or packet after program.\n");
266 exit(1);
267 }
268
269 // Combine the program and data into the unified APF buffer.
270 if (data) {
271 program = realloc(program, program_len + data_len);
272 memcpy(program + program_len, data, data_len);
273 free(data);
274 }
275
276 uint32_t ram_len = program_len + data_len;
277
278 if (filename)
279 file_handler(program, program_len, ram_len, filename, filter_age);
280 else
281 packet_handler(program, program_len, ram_len, packet, filter_age);
282
283 if (data_len) {
284 printf("Data: ");
285 print_hex(program + program_len, data_len);
286 printf("\n");
287 }
288
289 free(program);
290 return 0;
291 }
292