• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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     if (argc > 9) {
185         print_usage(argv[0]);
186         exit(1);
187     }
188 
189     uint8_t* program = NULL;
190     uint32_t program_len;
191     const char* filename = NULL;
192     char* packet = NULL;
193     uint8_t* data = NULL;
194     uint32_t data_len = 0;
195     uint32_t filter_age = 0;
196 
197     int opt;
198     char *endptr;
199 
200     while ((opt = getopt_long_only(argc, argv, "h", long_options, NULL)) != -1) {
201         switch (opt) {
202             case OPT_PROGRAM:
203                 program_len = parse_hex(optarg, &program);
204                 break;
205             case OPT_PACKET:
206                 if (!program) {
207                     printf("<packet> requires <program> first\n\'%s -h or --help\' "
208                            "for more information\n", basename(argv[0]));
209                     exit(1);
210                 }
211                 if (filename) {
212                     printf("Cannot use <file> with <packet> \n\'%s -h or --help\' "
213                            "for more information\n", basename(argv[0]));
214 
215                     exit(1);
216                 }
217                 packet = optarg;
218                 break;
219             case OPT_PCAP:
220                 if (!program) {
221                     printf("<file> requires <program> first\n\'%s -h or --help\' "
222                            "for more information\n", basename(argv[0]));
223 
224                     exit(1);
225                 }
226                 if (packet) {
227                     printf("Cannot use <packet> with <file>\n\'%s -h or --help\' "
228                            "for more information\n", basename(argv[0]));
229 
230                     exit(1);
231                 }
232                 filename = optarg;
233                 break;
234             case OPT_DATA:
235                 data_len = parse_hex(optarg, &data);
236                 break;
237             case OPT_AGE:
238                 errno = 0;
239                 filter_age = strtoul(optarg, &endptr, 10);
240                 if ((errno == ERANGE && filter_age == UINT32_MAX) ||
241                     (errno != 0 && filter_age == 0)) {
242                     perror("Error on age option: strtoul");
243                     exit(1);
244                 }
245                 if (endptr == optarg) {
246                     printf("No digit found in age.\n");
247                     exit(1);
248                 }
249                 break;
250             case OPT_TRACE:
251                 tracing_enabled = 1;
252                 break;
253             case 'h':
254                 print_usage(argv[0]);
255                 exit(0);
256                 break;
257             default:
258                 print_usage(argv[0]);
259                 exit(1);
260                 break;
261         }
262     }
263 
264     if (!program) {
265         printf("Must have APF program in option.\n");
266         exit(1);
267     }
268 
269     if (!filename && !packet) {
270         printf("Missing file or packet after program.\n");
271         exit(1);
272     }
273 
274     // Combine the program and data into the unified APF buffer.
275     if (data) {
276         program = realloc(program, program_len + data_len);
277         memcpy(program + program_len, data, data_len);
278         free(data);
279     }
280 
281     uint32_t ram_len = program_len + data_len;
282 
283     if (filename)
284         file_handler(program, program_len, ram_len, filename, filter_age);
285     else
286         packet_handler(program, program_len, ram_len, packet, filter_age);
287 
288     if (data_len) {
289         printf("Data: ");
290         print_hex(program + program_len, data_len);
291         printf("\n");
292     }
293 
294     free(program);
295     return 0;
296 }
297