blob: 0b36b0ea95699cf7bdbf5d7155ae2dec42c48830 [file] [log] [blame]
Paul Jensenf9491fa2016-07-27 07:48:40 -04001/*
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
Aaron Huang5ef76bd2018-11-19 14:01:09 +080019#include <errno.h>
20#include <getopt.h>
Bernie Innocentid0174a62019-02-08 20:06:57 +090021#include <inttypes.h>
Paul Jensenf9491fa2016-07-27 07:48:40 -040022#include <libgen.h>
Aaron Huang5ef76bd2018-11-19 14:01:09 +080023#include <limits.h>
24#include <pcap.h>
Paul Jensenf9491fa2016-07-27 07:48:40 -040025#include <stdint.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29
Lorenzo Colitti983eb512019-09-26 22:14:16 +090030#include "disassembler.h"
Paul Jensenf9491fa2016-07-27 07:48:40 -040031#include "apf_interpreter.h"
32
Lorenzo Colitti983eb512019-09-26 22:14:16 +090033#define __unused __attribute__((unused))
34
Aaron Huang5ef76bd2018-11-19 14:01:09 +080035enum {
Bernie Innocentid0174a62019-02-08 20:06:57 +090036 OPT_PROGRAM,
37 OPT_PACKET,
38 OPT_PCAP,
39 OPT_DATA,
40 OPT_AGE,
41 OPT_TRACE,
Aaron Huang5ef76bd2018-11-19 14:01:09 +080042};
43
Bernie Innocentid0174a62019-02-08 20:06:57 +090044const 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}};
Aaron Huang5ef76bd2018-11-19 14:01:09 +080052
Paul Jensenf9491fa2016-07-27 07:48:40 -040053// Parses hex in "input". Allocates and fills "*output" with parsed bytes.
54// Returns length in bytes of "*output".
Aaron Huang5ef76bd2018-11-19 14:01:09 +080055size_t parse_hex(const char* input, uint8_t** output) {
Paul Jensenf9491fa2016-07-27 07:48:40 -040056 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
Aaron Huang5ef76bd2018-11-19 14:01:09 +080079void print_hex(const uint8_t* input, int len) {
Bernie Innocenti75410972018-03-19 17:58:35 +090080 for (int i = 0; i < len; ++i) {
81 printf("%02x", input[i]);
82 }
83}
84
Lorenzo Colitti983eb512019-09-26 22:14:16 +090085int tracing_enabled = 0;
86
87void maybe_print_tracing_header() {
88 if (!tracing_enabled) return;
89
90 printf(" R0 R1 PC Instruction\n");
91 printf("-------------------------------------------------\n");
92
93}
94
Aaron Huang5ef76bd2018-11-19 14:01:09 +080095// Process packet through APF filter
96void 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
Lorenzo Colitti983eb512019-09-26 22:14:16 +0900101 maybe_print_tracing_header();
102
Aaron Huang5ef76bd2018-11-19 14:01:09 +0800103 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
Lorenzo Colitti983eb512019-09-26 22:14:16 +0900110void 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) {
Bernie Innocentid0174a62019-02-08 20:06:57 +0900113 if (!tracing_enabled) return;
114
Lorenzo Colitticdb38a52019-10-21 19:09:23 +0900115 printf("%8" PRIx32 " %8" PRIx32 " ", regs[0], regs[1]);
Lorenzo Colitti983eb512019-09-26 22:14:16 +0900116 apf_disassemble(program, program_len, pc);
Bernie Innocentid0174a62019-02-08 20:06:57 +0900117}
118
Aaron Huang5ef76bd2018-11-19 14:01:09 +0800119// Process pcap file through APF filter and generate output files
Bernie Innocentid0174a62019-02-08 20:06:57 +0900120void file_handler(uint8_t* program, uint32_t program_len, uint32_t ram_len, const char* filename,
121 uint32_t filter_age) {
Aaron Huang5ef76bd2018-11-19 14:01:09 +0800122 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");
Paul Jensenf9491fa2016-07-27 07:48:40 -0400135 exit(1);
136 }
Aaron Huang5ef76bd2018-11-19 14:01:09 +0800137
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) {
Lorenzo Colitti983eb512019-09-26 22:14:16 +0900148 maybe_print_tracing_header();
149
Aaron Huang5ef76bd2018-11-19 14:01:09 +0800150 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
169void print_usage(char* cmd) {
170 fprintf(stderr,
171 "Usage: %s --program <program> --pcap <file>|--packet <packet> "
Bernie Innocentid0174a62019-02-08 20:06:57 +0900172 "[--data <content>] [--age <number>] [--trace]\n"
Aaron Huang5ef76bd2018-11-19 14:01:09 +0800173 " --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"
Bernie Innocentid0174a62019-02-08 20:06:57 +0900178 " --trace Enable APF interpreter debug tracing\n"
Aaron Huang5ef76bd2018-11-19 14:01:09 +0800179 " -h, --help Show this message.\n",
180 basename(cmd));
181}
182
183int 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;
Bernie Innocentid0174a62019-02-08 20:06:57 +0900191 const char* filename = NULL;
Aaron Huang5ef76bd2018-11-19 14:01:09 +0800192 char* packet = NULL;
Bernie Innocenti75410972018-03-19 17:58:35 +0900193 uint8_t* data = NULL;
Aaron Huang5ef76bd2018-11-19 14:01:09 +0800194 uint32_t data_len = 0;
Bernie Innocenti90cde572019-10-08 15:41:57 +0900195 uint32_t filter_age = 0;
Aaron Huang5ef76bd2018-11-19 14:01:09 +0800196
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:
Bernie Innocentid0174a62019-02-08 20:06:57 +0900203 program_len = parse_hex(optarg, &program);
204 break;
Aaron Huang5ef76bd2018-11-19 14:01:09 +0800205 case OPT_PACKET:
Bernie Innocentid0174a62019-02-08 20:06:57 +0900206 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]));
Aaron Huang5ef76bd2018-11-19 14:01:09 +0800214
Bernie Innocentid0174a62019-02-08 20:06:57 +0900215 exit(1);
216 }
217 packet = optarg;
218 break;
Aaron Huang5ef76bd2018-11-19 14:01:09 +0800219 case OPT_PCAP:
Bernie Innocentid0174a62019-02-08 20:06:57 +0900220 if (!program) {
221 printf("<file> requires <program> first\n\'%s -h or --help\' "
222 "for more information\n", basename(argv[0]));
Aaron Huang5ef76bd2018-11-19 14:01:09 +0800223
Bernie Innocentid0174a62019-02-08 20:06:57 +0900224 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]));
Aaron Huang5ef76bd2018-11-19 14:01:09 +0800229
Bernie Innocentid0174a62019-02-08 20:06:57 +0900230 exit(1);
231 }
232 filename = optarg;
233 break;
Aaron Huang5ef76bd2018-11-19 14:01:09 +0800234 case OPT_DATA:
Bernie Innocentid0174a62019-02-08 20:06:57 +0900235 data_len = parse_hex(optarg, &data);
236 break;
Aaron Huang5ef76bd2018-11-19 14:01:09 +0800237 case OPT_AGE:
Bernie Innocentid0174a62019-02-08 20:06:57 +0900238 errno = 0;
239 filter_age = strtoul(optarg, &endptr, 10);
Automerger Merge Worker937551c2020-02-24 15:30:45 +0000240 if ((errno == ERANGE && filter_age == UINT32_MAX) ||
Bernie Innocentid0174a62019-02-08 20:06:57 +0900241 (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;
Aaron Huang5ef76bd2018-11-19 14:01:09 +0800253 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) {
Bernie Innocentid0174a62019-02-08 20:06:57 +0900265 printf("Must have APF program in option.\n");
266 exit(1);
Aaron Huang5ef76bd2018-11-19 14:01:09 +0800267 }
268
269 if (!filename && !packet) {
270 printf("Missing file or packet after program.\n");
271 exit(1);
272 }
Bernie Innocenti17c1fbc2018-04-10 22:52:30 +0900273
274 // Combine the program and data into the unified APF buffer.
Bernie Innocenti75410972018-03-19 17:58:35 +0900275 if (data) {
Bernie Innocenti17c1fbc2018-04-10 22:52:30 +0900276 program = realloc(program, program_len + data_len);
277 memcpy(program + program_len, data, data_len);
Bernie Innocenti75410972018-03-19 17:58:35 +0900278 free(data);
279 }
Bernie Innocenti17c1fbc2018-04-10 22:52:30 +0900280
281 uint32_t ram_len = program_len + data_len;
Aaron Huang5ef76bd2018-11-19 14:01:09 +0800282
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
Bernie Innocenti17c1fbc2018-04-10 22:52:30 +0900288 if (data_len) {
289 printf("Data: ");
290 print_hex(program + program_len, data_len);
291 printf("\n");
292 }
Aaron Huang5ef76bd2018-11-19 14:01:09 +0800293
Paul Jensenf9491fa2016-07-27 07:48:40 -0400294 free(program);
Aaron Huang5ef76bd2018-11-19 14:01:09 +0800295 return 0;
Bernie Innocenti75410972018-03-19 17:58:35 +0900296}