Paul Jensen | f9491fa | 2016-07-27 07:48:40 -0400 | [diff] [blame] | 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 | |
Aaron Huang | 5ef76bd | 2018-11-19 14:01:09 +0800 | [diff] [blame] | 19 | #include <errno.h> |
| 20 | #include <getopt.h> |
Bernie Innocenti | d0174a6 | 2019-02-08 20:06:57 +0900 | [diff] [blame] | 21 | #include <inttypes.h> |
Paul Jensen | f9491fa | 2016-07-27 07:48:40 -0400 | [diff] [blame] | 22 | #include <libgen.h> |
Aaron Huang | 5ef76bd | 2018-11-19 14:01:09 +0800 | [diff] [blame] | 23 | #include <limits.h> |
| 24 | #include <pcap.h> |
Paul Jensen | f9491fa | 2016-07-27 07:48:40 -0400 | [diff] [blame] | 25 | #include <stdint.h> |
| 26 | #include <stdio.h> |
| 27 | #include <stdlib.h> |
| 28 | #include <string.h> |
| 29 | |
Lorenzo Colitti | 983eb51 | 2019-09-26 22:14:16 +0900 | [diff] [blame] | 30 | #include "disassembler.h" |
Paul Jensen | f9491fa | 2016-07-27 07:48:40 -0400 | [diff] [blame] | 31 | #include "apf_interpreter.h" |
| 32 | |
Lorenzo Colitti | 983eb51 | 2019-09-26 22:14:16 +0900 | [diff] [blame] | 33 | #define __unused __attribute__((unused)) |
| 34 | |
Aaron Huang | 5ef76bd | 2018-11-19 14:01:09 +0800 | [diff] [blame] | 35 | enum { |
Bernie Innocenti | d0174a6 | 2019-02-08 20:06:57 +0900 | [diff] [blame] | 36 | OPT_PROGRAM, |
| 37 | OPT_PACKET, |
| 38 | OPT_PCAP, |
| 39 | OPT_DATA, |
| 40 | OPT_AGE, |
| 41 | OPT_TRACE, |
Aaron Huang | 5ef76bd | 2018-11-19 14:01:09 +0800 | [diff] [blame] | 42 | }; |
| 43 | |
Bernie Innocenti | d0174a6 | 2019-02-08 20:06:57 +0900 | [diff] [blame] | 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}}; |
Aaron Huang | 5ef76bd | 2018-11-19 14:01:09 +0800 | [diff] [blame] | 52 | |
Paul Jensen | f9491fa | 2016-07-27 07:48:40 -0400 | [diff] [blame] | 53 | // Parses hex in "input". Allocates and fills "*output" with parsed bytes. |
| 54 | // Returns length in bytes of "*output". |
Aaron Huang | 5ef76bd | 2018-11-19 14:01:09 +0800 | [diff] [blame] | 55 | size_t parse_hex(const char* input, uint8_t** output) { |
Paul Jensen | f9491fa | 2016-07-27 07:48:40 -0400 | [diff] [blame] | 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 | |
Aaron Huang | 5ef76bd | 2018-11-19 14:01:09 +0800 | [diff] [blame] | 79 | void print_hex(const uint8_t* input, int len) { |
Bernie Innocenti | 7541097 | 2018-03-19 17:58:35 +0900 | [diff] [blame] | 80 | for (int i = 0; i < len; ++i) { |
| 81 | printf("%02x", input[i]); |
| 82 | } |
| 83 | } |
| 84 | |
Lorenzo Colitti | 983eb51 | 2019-09-26 22:14:16 +0900 | [diff] [blame] | 85 | int tracing_enabled = 0; |
| 86 | |
| 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 | |
Aaron Huang | 5ef76bd | 2018-11-19 14:01:09 +0800 | [diff] [blame] | 95 | // Process packet through APF filter |
| 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 | |
Lorenzo Colitti | 983eb51 | 2019-09-26 22:14:16 +0900 | [diff] [blame] | 101 | maybe_print_tracing_header(); |
| 102 | |
Aaron Huang | 5ef76bd | 2018-11-19 14:01:09 +0800 | [diff] [blame] | 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 | |
Lorenzo Colitti | 983eb51 | 2019-09-26 22:14:16 +0900 | [diff] [blame] | 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) { |
Bernie Innocenti | d0174a6 | 2019-02-08 20:06:57 +0900 | [diff] [blame] | 113 | if (!tracing_enabled) return; |
| 114 | |
Lorenzo Colitti | cdb38a5 | 2019-10-21 19:09:23 +0900 | [diff] [blame] | 115 | printf("%8" PRIx32 " %8" PRIx32 " ", regs[0], regs[1]); |
Lorenzo Colitti | 983eb51 | 2019-09-26 22:14:16 +0900 | [diff] [blame] | 116 | apf_disassemble(program, program_len, pc); |
Bernie Innocenti | d0174a6 | 2019-02-08 20:06:57 +0900 | [diff] [blame] | 117 | } |
| 118 | |
Aaron Huang | 5ef76bd | 2018-11-19 14:01:09 +0800 | [diff] [blame] | 119 | // Process pcap file through APF filter and generate output files |
Bernie Innocenti | d0174a6 | 2019-02-08 20:06:57 +0900 | [diff] [blame] | 120 | void file_handler(uint8_t* program, uint32_t program_len, uint32_t ram_len, const char* filename, |
| 121 | uint32_t filter_age) { |
Aaron Huang | 5ef76bd | 2018-11-19 14:01:09 +0800 | [diff] [blame] | 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"); |
Paul Jensen | f9491fa | 2016-07-27 07:48:40 -0400 | [diff] [blame] | 135 | exit(1); |
| 136 | } |
Aaron Huang | 5ef76bd | 2018-11-19 14:01:09 +0800 | [diff] [blame] | 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) { |
Lorenzo Colitti | 983eb51 | 2019-09-26 22:14:16 +0900 | [diff] [blame] | 148 | maybe_print_tracing_header(); |
| 149 | |
Aaron Huang | 5ef76bd | 2018-11-19 14:01:09 +0800 | [diff] [blame] | 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 | |
| 169 | void print_usage(char* cmd) { |
| 170 | fprintf(stderr, |
| 171 | "Usage: %s --program <program> --pcap <file>|--packet <packet> " |
Bernie Innocenti | d0174a6 | 2019-02-08 20:06:57 +0900 | [diff] [blame] | 172 | "[--data <content>] [--age <number>] [--trace]\n" |
Aaron Huang | 5ef76bd | 2018-11-19 14:01:09 +0800 | [diff] [blame] | 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" |
Bernie Innocenti | d0174a6 | 2019-02-08 20:06:57 +0900 | [diff] [blame] | 178 | " --trace Enable APF interpreter debug tracing\n" |
Aaron Huang | 5ef76bd | 2018-11-19 14:01:09 +0800 | [diff] [blame] | 179 | " -h, --help Show this message.\n", |
| 180 | basename(cmd)); |
| 181 | } |
| 182 | |
| 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; |
Bernie Innocenti | d0174a6 | 2019-02-08 20:06:57 +0900 | [diff] [blame] | 191 | const char* filename = NULL; |
Aaron Huang | 5ef76bd | 2018-11-19 14:01:09 +0800 | [diff] [blame] | 192 | char* packet = NULL; |
Bernie Innocenti | 7541097 | 2018-03-19 17:58:35 +0900 | [diff] [blame] | 193 | uint8_t* data = NULL; |
Aaron Huang | 5ef76bd | 2018-11-19 14:01:09 +0800 | [diff] [blame] | 194 | uint32_t data_len = 0; |
Bernie Innocenti | 90cde57 | 2019-10-08 15:41:57 +0900 | [diff] [blame] | 195 | uint32_t filter_age = 0; |
Aaron Huang | 5ef76bd | 2018-11-19 14:01:09 +0800 | [diff] [blame] | 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: |
Bernie Innocenti | d0174a6 | 2019-02-08 20:06:57 +0900 | [diff] [blame] | 203 | program_len = parse_hex(optarg, &program); |
| 204 | break; |
Aaron Huang | 5ef76bd | 2018-11-19 14:01:09 +0800 | [diff] [blame] | 205 | case OPT_PACKET: |
Bernie Innocenti | d0174a6 | 2019-02-08 20:06:57 +0900 | [diff] [blame] | 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])); |
Aaron Huang | 5ef76bd | 2018-11-19 14:01:09 +0800 | [diff] [blame] | 214 | |
Bernie Innocenti | d0174a6 | 2019-02-08 20:06:57 +0900 | [diff] [blame] | 215 | exit(1); |
| 216 | } |
| 217 | packet = optarg; |
| 218 | break; |
Aaron Huang | 5ef76bd | 2018-11-19 14:01:09 +0800 | [diff] [blame] | 219 | case OPT_PCAP: |
Bernie Innocenti | d0174a6 | 2019-02-08 20:06:57 +0900 | [diff] [blame] | 220 | if (!program) { |
| 221 | printf("<file> requires <program> first\n\'%s -h or --help\' " |
| 222 | "for more information\n", basename(argv[0])); |
Aaron Huang | 5ef76bd | 2018-11-19 14:01:09 +0800 | [diff] [blame] | 223 | |
Bernie Innocenti | d0174a6 | 2019-02-08 20:06:57 +0900 | [diff] [blame] | 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])); |
Aaron Huang | 5ef76bd | 2018-11-19 14:01:09 +0800 | [diff] [blame] | 229 | |
Bernie Innocenti | d0174a6 | 2019-02-08 20:06:57 +0900 | [diff] [blame] | 230 | exit(1); |
| 231 | } |
| 232 | filename = optarg; |
| 233 | break; |
Aaron Huang | 5ef76bd | 2018-11-19 14:01:09 +0800 | [diff] [blame] | 234 | case OPT_DATA: |
Bernie Innocenti | d0174a6 | 2019-02-08 20:06:57 +0900 | [diff] [blame] | 235 | data_len = parse_hex(optarg, &data); |
| 236 | break; |
Aaron Huang | 5ef76bd | 2018-11-19 14:01:09 +0800 | [diff] [blame] | 237 | case OPT_AGE: |
Bernie Innocenti | d0174a6 | 2019-02-08 20:06:57 +0900 | [diff] [blame] | 238 | errno = 0; |
| 239 | filter_age = strtoul(optarg, &endptr, 10); |
Automerger Merge Worker | 937551c | 2020-02-24 15:30:45 +0000 | [diff] [blame] | 240 | if ((errno == ERANGE && filter_age == UINT32_MAX) || |
Bernie Innocenti | d0174a6 | 2019-02-08 20:06:57 +0900 | [diff] [blame] | 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; |
Aaron Huang | 5ef76bd | 2018-11-19 14:01:09 +0800 | [diff] [blame] | 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) { |
Bernie Innocenti | d0174a6 | 2019-02-08 20:06:57 +0900 | [diff] [blame] | 265 | printf("Must have APF program in option.\n"); |
| 266 | exit(1); |
Aaron Huang | 5ef76bd | 2018-11-19 14:01:09 +0800 | [diff] [blame] | 267 | } |
| 268 | |
| 269 | if (!filename && !packet) { |
| 270 | printf("Missing file or packet after program.\n"); |
| 271 | exit(1); |
| 272 | } |
Bernie Innocenti | 17c1fbc | 2018-04-10 22:52:30 +0900 | [diff] [blame] | 273 | |
| 274 | // Combine the program and data into the unified APF buffer. |
Bernie Innocenti | 7541097 | 2018-03-19 17:58:35 +0900 | [diff] [blame] | 275 | if (data) { |
Bernie Innocenti | 17c1fbc | 2018-04-10 22:52:30 +0900 | [diff] [blame] | 276 | program = realloc(program, program_len + data_len); |
| 277 | memcpy(program + program_len, data, data_len); |
Bernie Innocenti | 7541097 | 2018-03-19 17:58:35 +0900 | [diff] [blame] | 278 | free(data); |
| 279 | } |
Bernie Innocenti | 17c1fbc | 2018-04-10 22:52:30 +0900 | [diff] [blame] | 280 | |
| 281 | uint32_t ram_len = program_len + data_len; |
Aaron Huang | 5ef76bd | 2018-11-19 14:01:09 +0800 | [diff] [blame] | 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 | |
Bernie Innocenti | 17c1fbc | 2018-04-10 22:52:30 +0900 | [diff] [blame] | 288 | if (data_len) { |
| 289 | printf("Data: "); |
| 290 | print_hex(program + program_len, data_len); |
| 291 | printf("\n"); |
| 292 | } |
Aaron Huang | 5ef76bd | 2018-11-19 14:01:09 +0800 | [diff] [blame] | 293 | |
Paul Jensen | f9491fa | 2016-07-27 07:48:40 -0400 | [diff] [blame] | 294 | free(program); |
Aaron Huang | 5ef76bd | 2018-11-19 14:01:09 +0800 | [diff] [blame] | 295 | return 0; |
Bernie Innocenti | 7541097 | 2018-03-19 17:58:35 +0900 | [diff] [blame] | 296 | } |