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