1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Minimal BPF debugger |
4 | * |
5 | * Minimal BPF debugger that mimics the kernel's engine (w/o extensions) |
6 | * and allows for single stepping through selected packets from a pcap |
7 | * with a provided user filter in order to facilitate verification of a |
8 | * BPF program. Besides others, this is useful to verify BPF programs |
9 | * before attaching to a live system, and can be used in socket filters, |
10 | * cls_bpf, xt_bpf, team driver and e.g. PTP code; in particular when a |
11 | * single more complex BPF program is being used. Reasons for a more |
12 | * complex BPF program are likely primarily to optimize execution time |
13 | * for making a verdict when multiple simple BPF programs are combined |
14 | * into one in order to prevent parsing same headers multiple times. |
15 | * |
16 | * More on how to debug BPF opcodes see Documentation/networking/filter.rst |
17 | * which is the main document on BPF. Mini howto for getting started: |
18 | * |
19 | * 1) `./bpf_dbg` to enter the shell (shell cmds denoted with '>'): |
20 | * 2) > load bpf 6,40 0 0 12,21 0 3 20... (output from `bpf_asm` or |
21 | * `tcpdump -iem1 -ddd port 22 | tr '\n' ','` to load as filter) |
22 | * 3) > load pcap foo.pcap |
23 | * 4) > run <n>/disassemble/dump/quit (self-explanatory) |
24 | * 5) > breakpoint 2 (sets bp at loaded BPF insns 2, do `run` then; |
25 | * multiple bps can be set, of course, a call to `breakpoint` |
26 | * w/o args shows currently loaded bps, `breakpoint reset` for |
27 | * resetting all breakpoints) |
28 | * 6) > select 3 (`run` etc will start from the 3rd packet in the pcap) |
29 | * 7) > step [-<n>, +<n>] (performs single stepping through the BPF) |
30 | * |
31 | * Copyright 2013 Daniel Borkmann <borkmann@redhat.com> |
32 | */ |
33 | |
34 | #include <stdio.h> |
35 | #include <unistd.h> |
36 | #include <stdlib.h> |
37 | #include <ctype.h> |
38 | #include <stdbool.h> |
39 | #include <stdarg.h> |
40 | #include <setjmp.h> |
41 | #include <linux/filter.h> |
42 | #include <linux/if_packet.h> |
43 | #include <readline/readline.h> |
44 | #include <readline/history.h> |
45 | #include <sys/types.h> |
46 | #include <sys/socket.h> |
47 | #include <sys/stat.h> |
48 | #include <sys/mman.h> |
49 | #include <fcntl.h> |
50 | #include <errno.h> |
51 | #include <signal.h> |
52 | #include <arpa/inet.h> |
53 | #include <net/ethernet.h> |
54 | |
55 | #define TCPDUMP_MAGIC 0xa1b2c3d4 |
56 | |
57 | #define BPF_LDX_B (BPF_LDX | BPF_B) |
58 | #define BPF_LDX_W (BPF_LDX | BPF_W) |
59 | #define BPF_JMP_JA (BPF_JMP | BPF_JA) |
60 | #define BPF_JMP_JEQ (BPF_JMP | BPF_JEQ) |
61 | #define BPF_JMP_JGT (BPF_JMP | BPF_JGT) |
62 | #define BPF_JMP_JGE (BPF_JMP | BPF_JGE) |
63 | #define BPF_JMP_JSET (BPF_JMP | BPF_JSET) |
64 | #define BPF_ALU_ADD (BPF_ALU | BPF_ADD) |
65 | #define BPF_ALU_SUB (BPF_ALU | BPF_SUB) |
66 | #define BPF_ALU_MUL (BPF_ALU | BPF_MUL) |
67 | #define BPF_ALU_DIV (BPF_ALU | BPF_DIV) |
68 | #define BPF_ALU_MOD (BPF_ALU | BPF_MOD) |
69 | #define BPF_ALU_NEG (BPF_ALU | BPF_NEG) |
70 | #define BPF_ALU_AND (BPF_ALU | BPF_AND) |
71 | #define BPF_ALU_OR (BPF_ALU | BPF_OR) |
72 | #define BPF_ALU_XOR (BPF_ALU | BPF_XOR) |
73 | #define BPF_ALU_LSH (BPF_ALU | BPF_LSH) |
74 | #define BPF_ALU_RSH (BPF_ALU | BPF_RSH) |
75 | #define BPF_MISC_TAX (BPF_MISC | BPF_TAX) |
76 | #define BPF_MISC_TXA (BPF_MISC | BPF_TXA) |
77 | #define BPF_LD_B (BPF_LD | BPF_B) |
78 | #define BPF_LD_H (BPF_LD | BPF_H) |
79 | #define BPF_LD_W (BPF_LD | BPF_W) |
80 | |
81 | #ifndef array_size |
82 | # define array_size(x) (sizeof(x) / sizeof((x)[0])) |
83 | #endif |
84 | |
85 | #ifndef __check_format_printf |
86 | # define __check_format_printf(pos_fmtstr, pos_fmtargs) \ |
87 | __attribute__ ((format (printf, (pos_fmtstr), (pos_fmtargs)))) |
88 | #endif |
89 | |
90 | enum { |
91 | CMD_OK, |
92 | CMD_ERR, |
93 | CMD_EX, |
94 | }; |
95 | |
96 | struct shell_cmd { |
97 | const char *name; |
98 | int (*func)(char *args); |
99 | }; |
100 | |
101 | struct pcap_filehdr { |
102 | uint32_t magic; |
103 | uint16_t version_major; |
104 | uint16_t version_minor; |
105 | int32_t thiszone; |
106 | uint32_t sigfigs; |
107 | uint32_t snaplen; |
108 | uint32_t linktype; |
109 | }; |
110 | |
111 | struct pcap_timeval { |
112 | int32_t tv_sec; |
113 | int32_t tv_usec; |
114 | }; |
115 | |
116 | struct pcap_pkthdr { |
117 | struct pcap_timeval ts; |
118 | uint32_t caplen; |
119 | uint32_t len; |
120 | }; |
121 | |
122 | struct bpf_regs { |
123 | uint32_t A; |
124 | uint32_t X; |
125 | uint32_t M[BPF_MEMWORDS]; |
126 | uint32_t R; |
127 | bool Rs; |
128 | uint16_t Pc; |
129 | }; |
130 | |
131 | static struct sock_filter bpf_image[BPF_MAXINSNS + 1]; |
132 | static unsigned int bpf_prog_len; |
133 | |
134 | static int bpf_breakpoints[64]; |
135 | static struct bpf_regs bpf_regs[BPF_MAXINSNS + 1]; |
136 | static struct bpf_regs bpf_curr; |
137 | static unsigned int bpf_regs_len; |
138 | |
139 | static int pcap_fd = -1; |
140 | static unsigned int pcap_packet; |
141 | static size_t pcap_map_size; |
142 | static char *pcap_ptr_va_start, *pcap_ptr_va_curr; |
143 | |
144 | static const char * const op_table[] = { |
145 | [BPF_ST] = "st" , |
146 | [BPF_STX] = "stx" , |
147 | [BPF_LD_B] = "ldb" , |
148 | [BPF_LD_H] = "ldh" , |
149 | [BPF_LD_W] = "ld" , |
150 | [BPF_LDX] = "ldx" , |
151 | [BPF_LDX_B] = "ldxb" , |
152 | [BPF_JMP_JA] = "ja" , |
153 | [BPF_JMP_JEQ] = "jeq" , |
154 | [BPF_JMP_JGT] = "jgt" , |
155 | [BPF_JMP_JGE] = "jge" , |
156 | [BPF_JMP_JSET] = "jset" , |
157 | [BPF_ALU_ADD] = "add" , |
158 | [BPF_ALU_SUB] = "sub" , |
159 | [BPF_ALU_MUL] = "mul" , |
160 | [BPF_ALU_DIV] = "div" , |
161 | [BPF_ALU_MOD] = "mod" , |
162 | [BPF_ALU_NEG] = "neg" , |
163 | [BPF_ALU_AND] = "and" , |
164 | [BPF_ALU_OR] = "or" , |
165 | [BPF_ALU_XOR] = "xor" , |
166 | [BPF_ALU_LSH] = "lsh" , |
167 | [BPF_ALU_RSH] = "rsh" , |
168 | [BPF_MISC_TAX] = "tax" , |
169 | [BPF_MISC_TXA] = "txa" , |
170 | [BPF_RET] = "ret" , |
171 | }; |
172 | |
173 | static __check_format_printf(1, 2) int rl_printf(const char *fmt, ...) |
174 | { |
175 | int ret; |
176 | va_list vl; |
177 | |
178 | va_start(vl, fmt); |
179 | ret = vfprintf(rl_outstream, fmt, vl); |
180 | va_end(vl); |
181 | |
182 | return ret; |
183 | } |
184 | |
185 | static int matches(const char *cmd, const char *pattern) |
186 | { |
187 | int len = strlen(cmd); |
188 | |
189 | if (len > strlen(pattern)) |
190 | return -1; |
191 | |
192 | return memcmp(p: pattern, q: cmd, size: len); |
193 | } |
194 | |
195 | static void hex_dump(const uint8_t *buf, size_t len) |
196 | { |
197 | int i; |
198 | |
199 | rl_printf(fmt: "%3u: " , 0); |
200 | for (i = 0; i < len; i++) { |
201 | if (i && !(i % 16)) |
202 | rl_printf(fmt: "\n%3u: " , i); |
203 | rl_printf(fmt: "%02x " , buf[i]); |
204 | } |
205 | rl_printf(fmt: "\n" ); |
206 | } |
207 | |
208 | static bool bpf_prog_loaded(void) |
209 | { |
210 | if (bpf_prog_len == 0) |
211 | rl_printf(fmt: "no bpf program loaded!\n" ); |
212 | |
213 | return bpf_prog_len > 0; |
214 | } |
215 | |
216 | static void bpf_disasm(const struct sock_filter f, unsigned int i) |
217 | { |
218 | const char *op, *fmt; |
219 | int val = f.k; |
220 | char buf[256]; |
221 | |
222 | switch (f.code) { |
223 | case BPF_RET | BPF_K: |
224 | op = op_table[BPF_RET]; |
225 | fmt = "#%#x" ; |
226 | break; |
227 | case BPF_RET | BPF_A: |
228 | op = op_table[BPF_RET]; |
229 | fmt = "a" ; |
230 | break; |
231 | case BPF_RET | BPF_X: |
232 | op = op_table[BPF_RET]; |
233 | fmt = "x" ; |
234 | break; |
235 | case BPF_MISC_TAX: |
236 | op = op_table[BPF_MISC_TAX]; |
237 | fmt = "" ; |
238 | break; |
239 | case BPF_MISC_TXA: |
240 | op = op_table[BPF_MISC_TXA]; |
241 | fmt = "" ; |
242 | break; |
243 | case BPF_ST: |
244 | op = op_table[BPF_ST]; |
245 | fmt = "M[%d]" ; |
246 | break; |
247 | case BPF_STX: |
248 | op = op_table[BPF_STX]; |
249 | fmt = "M[%d]" ; |
250 | break; |
251 | case BPF_LD_W | BPF_ABS: |
252 | op = op_table[BPF_LD_W]; |
253 | fmt = "[%d]" ; |
254 | break; |
255 | case BPF_LD_H | BPF_ABS: |
256 | op = op_table[BPF_LD_H]; |
257 | fmt = "[%d]" ; |
258 | break; |
259 | case BPF_LD_B | BPF_ABS: |
260 | op = op_table[BPF_LD_B]; |
261 | fmt = "[%d]" ; |
262 | break; |
263 | case BPF_LD_W | BPF_LEN: |
264 | op = op_table[BPF_LD_W]; |
265 | fmt = "#len" ; |
266 | break; |
267 | case BPF_LD_W | BPF_IND: |
268 | op = op_table[BPF_LD_W]; |
269 | fmt = "[x+%d]" ; |
270 | break; |
271 | case BPF_LD_H | BPF_IND: |
272 | op = op_table[BPF_LD_H]; |
273 | fmt = "[x+%d]" ; |
274 | break; |
275 | case BPF_LD_B | BPF_IND: |
276 | op = op_table[BPF_LD_B]; |
277 | fmt = "[x+%d]" ; |
278 | break; |
279 | case BPF_LD | BPF_IMM: |
280 | op = op_table[BPF_LD_W]; |
281 | fmt = "#%#x" ; |
282 | break; |
283 | case BPF_LDX | BPF_IMM: |
284 | op = op_table[BPF_LDX]; |
285 | fmt = "#%#x" ; |
286 | break; |
287 | case BPF_LDX_B | BPF_MSH: |
288 | op = op_table[BPF_LDX_B]; |
289 | fmt = "4*([%d]&0xf)" ; |
290 | break; |
291 | case BPF_LD | BPF_MEM: |
292 | op = op_table[BPF_LD_W]; |
293 | fmt = "M[%d]" ; |
294 | break; |
295 | case BPF_LDX | BPF_MEM: |
296 | op = op_table[BPF_LDX]; |
297 | fmt = "M[%d]" ; |
298 | break; |
299 | case BPF_JMP_JA: |
300 | op = op_table[BPF_JMP_JA]; |
301 | fmt = "%d" ; |
302 | val = i + 1 + f.k; |
303 | break; |
304 | case BPF_JMP_JGT | BPF_X: |
305 | op = op_table[BPF_JMP_JGT]; |
306 | fmt = "x" ; |
307 | break; |
308 | case BPF_JMP_JGT | BPF_K: |
309 | op = op_table[BPF_JMP_JGT]; |
310 | fmt = "#%#x" ; |
311 | break; |
312 | case BPF_JMP_JGE | BPF_X: |
313 | op = op_table[BPF_JMP_JGE]; |
314 | fmt = "x" ; |
315 | break; |
316 | case BPF_JMP_JGE | BPF_K: |
317 | op = op_table[BPF_JMP_JGE]; |
318 | fmt = "#%#x" ; |
319 | break; |
320 | case BPF_JMP_JEQ | BPF_X: |
321 | op = op_table[BPF_JMP_JEQ]; |
322 | fmt = "x" ; |
323 | break; |
324 | case BPF_JMP_JEQ | BPF_K: |
325 | op = op_table[BPF_JMP_JEQ]; |
326 | fmt = "#%#x" ; |
327 | break; |
328 | case BPF_JMP_JSET | BPF_X: |
329 | op = op_table[BPF_JMP_JSET]; |
330 | fmt = "x" ; |
331 | break; |
332 | case BPF_JMP_JSET | BPF_K: |
333 | op = op_table[BPF_JMP_JSET]; |
334 | fmt = "#%#x" ; |
335 | break; |
336 | case BPF_ALU_NEG: |
337 | op = op_table[BPF_ALU_NEG]; |
338 | fmt = "" ; |
339 | break; |
340 | case BPF_ALU_LSH | BPF_X: |
341 | op = op_table[BPF_ALU_LSH]; |
342 | fmt = "x" ; |
343 | break; |
344 | case BPF_ALU_LSH | BPF_K: |
345 | op = op_table[BPF_ALU_LSH]; |
346 | fmt = "#%d" ; |
347 | break; |
348 | case BPF_ALU_RSH | BPF_X: |
349 | op = op_table[BPF_ALU_RSH]; |
350 | fmt = "x" ; |
351 | break; |
352 | case BPF_ALU_RSH | BPF_K: |
353 | op = op_table[BPF_ALU_RSH]; |
354 | fmt = "#%d" ; |
355 | break; |
356 | case BPF_ALU_ADD | BPF_X: |
357 | op = op_table[BPF_ALU_ADD]; |
358 | fmt = "x" ; |
359 | break; |
360 | case BPF_ALU_ADD | BPF_K: |
361 | op = op_table[BPF_ALU_ADD]; |
362 | fmt = "#%d" ; |
363 | break; |
364 | case BPF_ALU_SUB | BPF_X: |
365 | op = op_table[BPF_ALU_SUB]; |
366 | fmt = "x" ; |
367 | break; |
368 | case BPF_ALU_SUB | BPF_K: |
369 | op = op_table[BPF_ALU_SUB]; |
370 | fmt = "#%d" ; |
371 | break; |
372 | case BPF_ALU_MUL | BPF_X: |
373 | op = op_table[BPF_ALU_MUL]; |
374 | fmt = "x" ; |
375 | break; |
376 | case BPF_ALU_MUL | BPF_K: |
377 | op = op_table[BPF_ALU_MUL]; |
378 | fmt = "#%d" ; |
379 | break; |
380 | case BPF_ALU_DIV | BPF_X: |
381 | op = op_table[BPF_ALU_DIV]; |
382 | fmt = "x" ; |
383 | break; |
384 | case BPF_ALU_DIV | BPF_K: |
385 | op = op_table[BPF_ALU_DIV]; |
386 | fmt = "#%d" ; |
387 | break; |
388 | case BPF_ALU_MOD | BPF_X: |
389 | op = op_table[BPF_ALU_MOD]; |
390 | fmt = "x" ; |
391 | break; |
392 | case BPF_ALU_MOD | BPF_K: |
393 | op = op_table[BPF_ALU_MOD]; |
394 | fmt = "#%d" ; |
395 | break; |
396 | case BPF_ALU_AND | BPF_X: |
397 | op = op_table[BPF_ALU_AND]; |
398 | fmt = "x" ; |
399 | break; |
400 | case BPF_ALU_AND | BPF_K: |
401 | op = op_table[BPF_ALU_AND]; |
402 | fmt = "#%#x" ; |
403 | break; |
404 | case BPF_ALU_OR | BPF_X: |
405 | op = op_table[BPF_ALU_OR]; |
406 | fmt = "x" ; |
407 | break; |
408 | case BPF_ALU_OR | BPF_K: |
409 | op = op_table[BPF_ALU_OR]; |
410 | fmt = "#%#x" ; |
411 | break; |
412 | case BPF_ALU_XOR | BPF_X: |
413 | op = op_table[BPF_ALU_XOR]; |
414 | fmt = "x" ; |
415 | break; |
416 | case BPF_ALU_XOR | BPF_K: |
417 | op = op_table[BPF_ALU_XOR]; |
418 | fmt = "#%#x" ; |
419 | break; |
420 | default: |
421 | op = "nosup" ; |
422 | fmt = "%#x" ; |
423 | val = f.code; |
424 | break; |
425 | } |
426 | |
427 | memset(buf, 0, sizeof(buf)); |
428 | snprintf(buf, size: sizeof(buf), fmt, val); |
429 | buf[sizeof(buf) - 1] = 0; |
430 | |
431 | if ((BPF_CLASS(f.code) == BPF_JMP && BPF_OP(f.code) != BPF_JA)) |
432 | rl_printf(fmt: "l%d:\t%s %s, l%d, l%d\n" , i, op, buf, |
433 | i + 1 + f.jt, i + 1 + f.jf); |
434 | else |
435 | rl_printf(fmt: "l%d:\t%s %s\n" , i, op, buf); |
436 | } |
437 | |
438 | static void bpf_dump_curr(struct bpf_regs *r, struct sock_filter *f) |
439 | { |
440 | int i, m = 0; |
441 | |
442 | rl_printf(fmt: "pc: [%u]\n" , r->Pc); |
443 | rl_printf(fmt: "code: [%u] jt[%u] jf[%u] k[%u]\n" , |
444 | f->code, f->jt, f->jf, f->k); |
445 | rl_printf(fmt: "curr: " ); |
446 | bpf_disasm(f: *f, i: r->Pc); |
447 | |
448 | if (f->jt || f->jf) { |
449 | rl_printf(fmt: "jt: " ); |
450 | bpf_disasm(f: *(f + f->jt + 1), i: r->Pc + f->jt + 1); |
451 | rl_printf(fmt: "jf: " ); |
452 | bpf_disasm(f: *(f + f->jf + 1), i: r->Pc + f->jf + 1); |
453 | } |
454 | |
455 | rl_printf(fmt: "A: [%#08x][%u]\n" , r->A, r->A); |
456 | rl_printf(fmt: "X: [%#08x][%u]\n" , r->X, r->X); |
457 | if (r->Rs) |
458 | rl_printf(fmt: "ret: [%#08x][%u]!\n" , r->R, r->R); |
459 | |
460 | for (i = 0; i < BPF_MEMWORDS; i++) { |
461 | if (r->M[i]) { |
462 | m++; |
463 | rl_printf(fmt: "M[%d]: [%#08x][%u]\n" , i, r->M[i], r->M[i]); |
464 | } |
465 | } |
466 | if (m == 0) |
467 | rl_printf(fmt: "M[0,%d]: [%#08x][%u]\n" , BPF_MEMWORDS - 1, 0, 0); |
468 | } |
469 | |
470 | static void bpf_dump_pkt(uint8_t *pkt, uint32_t pkt_caplen, uint32_t pkt_len) |
471 | { |
472 | if (pkt_caplen != pkt_len) |
473 | rl_printf(fmt: "cap: %u, len: %u\n" , pkt_caplen, pkt_len); |
474 | else |
475 | rl_printf(fmt: "len: %u\n" , pkt_len); |
476 | |
477 | hex_dump(buf: pkt, len: pkt_caplen); |
478 | } |
479 | |
480 | static void bpf_disasm_all(const struct sock_filter *f, unsigned int len) |
481 | { |
482 | unsigned int i; |
483 | |
484 | for (i = 0; i < len; i++) |
485 | bpf_disasm(f: f[i], i); |
486 | } |
487 | |
488 | static void bpf_dump_all(const struct sock_filter *f, unsigned int len) |
489 | { |
490 | unsigned int i; |
491 | |
492 | rl_printf(fmt: "/* { op, jt, jf, k }, */\n" ); |
493 | for (i = 0; i < len; i++) |
494 | rl_printf(fmt: "{ %#04x, %2u, %2u, %#010x },\n" , |
495 | f[i].code, f[i].jt, f[i].jf, f[i].k); |
496 | } |
497 | |
498 | static bool bpf_runnable(struct sock_filter *f, unsigned int len) |
499 | { |
500 | int sock, ret, i; |
501 | struct sock_fprog bpf = { |
502 | .filter = f, |
503 | .len = len, |
504 | }; |
505 | |
506 | sock = socket(AF_INET, SOCK_DGRAM, 0); |
507 | if (sock < 0) { |
508 | rl_printf(fmt: "cannot open socket!\n" ); |
509 | return false; |
510 | } |
511 | ret = setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof(bpf)); |
512 | close(sock); |
513 | if (ret < 0) { |
514 | rl_printf(fmt: "program not allowed to run by kernel!\n" ); |
515 | return false; |
516 | } |
517 | for (i = 0; i < len; i++) { |
518 | if (BPF_CLASS(f[i].code) == BPF_LD && |
519 | f[i].k > SKF_AD_OFF) { |
520 | rl_printf(fmt: "extensions currently not supported!\n" ); |
521 | return false; |
522 | } |
523 | } |
524 | |
525 | return true; |
526 | } |
527 | |
528 | static void bpf_reset_breakpoints(void) |
529 | { |
530 | int i; |
531 | |
532 | for (i = 0; i < array_size(bpf_breakpoints); i++) |
533 | bpf_breakpoints[i] = -1; |
534 | } |
535 | |
536 | static void bpf_set_breakpoints(unsigned int where) |
537 | { |
538 | int i; |
539 | bool set = false; |
540 | |
541 | for (i = 0; i < array_size(bpf_breakpoints); i++) { |
542 | if (bpf_breakpoints[i] == (int) where) { |
543 | rl_printf(fmt: "breakpoint already set!\n" ); |
544 | set = true; |
545 | break; |
546 | } |
547 | |
548 | if (bpf_breakpoints[i] == -1 && set == false) { |
549 | bpf_breakpoints[i] = where; |
550 | set = true; |
551 | } |
552 | } |
553 | |
554 | if (!set) |
555 | rl_printf(fmt: "too many breakpoints set, reset first!\n" ); |
556 | } |
557 | |
558 | static void bpf_dump_breakpoints(void) |
559 | { |
560 | int i; |
561 | |
562 | rl_printf(fmt: "breakpoints: " ); |
563 | |
564 | for (i = 0; i < array_size(bpf_breakpoints); i++) { |
565 | if (bpf_breakpoints[i] < 0) |
566 | continue; |
567 | rl_printf(fmt: "%d " , bpf_breakpoints[i]); |
568 | } |
569 | |
570 | rl_printf(fmt: "\n" ); |
571 | } |
572 | |
573 | static void bpf_reset(void) |
574 | { |
575 | bpf_regs_len = 0; |
576 | |
577 | memset(bpf_regs, 0, sizeof(bpf_regs)); |
578 | memset(&bpf_curr, 0, sizeof(bpf_curr)); |
579 | } |
580 | |
581 | static void bpf_safe_regs(void) |
582 | { |
583 | memcpy(&bpf_regs[bpf_regs_len++], &bpf_curr, sizeof(bpf_curr)); |
584 | } |
585 | |
586 | static bool bpf_restore_regs(int off) |
587 | { |
588 | unsigned int index = bpf_regs_len - 1 + off; |
589 | |
590 | if (index == 0) { |
591 | bpf_reset(); |
592 | return true; |
593 | } else if (index < bpf_regs_len) { |
594 | memcpy(&bpf_curr, &bpf_regs[index], sizeof(bpf_curr)); |
595 | bpf_regs_len = index; |
596 | return true; |
597 | } else { |
598 | rl_printf(fmt: "reached bottom of register history stack!\n" ); |
599 | return false; |
600 | } |
601 | } |
602 | |
603 | static uint32_t (uint8_t *pkt, uint32_t off) |
604 | { |
605 | uint32_t r; |
606 | |
607 | memcpy(&r, &pkt[off], sizeof(r)); |
608 | |
609 | return ntohl(r); |
610 | } |
611 | |
612 | static uint16_t (uint8_t *pkt, uint32_t off) |
613 | { |
614 | uint16_t r; |
615 | |
616 | memcpy(&r, &pkt[off], sizeof(r)); |
617 | |
618 | return ntohs(r); |
619 | } |
620 | |
621 | static uint8_t (uint8_t *pkt, uint32_t off) |
622 | { |
623 | return pkt[off]; |
624 | } |
625 | |
626 | static void set_return(struct bpf_regs *r) |
627 | { |
628 | r->R = 0; |
629 | r->Rs = true; |
630 | } |
631 | |
632 | static void bpf_single_step(struct bpf_regs *r, struct sock_filter *f, |
633 | uint8_t *pkt, uint32_t pkt_caplen, |
634 | uint32_t pkt_len) |
635 | { |
636 | uint32_t K = f->k; |
637 | int d; |
638 | |
639 | switch (f->code) { |
640 | case BPF_RET | BPF_K: |
641 | r->R = K; |
642 | r->Rs = true; |
643 | break; |
644 | case BPF_RET | BPF_A: |
645 | r->R = r->A; |
646 | r->Rs = true; |
647 | break; |
648 | case BPF_RET | BPF_X: |
649 | r->R = r->X; |
650 | r->Rs = true; |
651 | break; |
652 | case BPF_MISC_TAX: |
653 | r->X = r->A; |
654 | break; |
655 | case BPF_MISC_TXA: |
656 | r->A = r->X; |
657 | break; |
658 | case BPF_ST: |
659 | r->M[K] = r->A; |
660 | break; |
661 | case BPF_STX: |
662 | r->M[K] = r->X; |
663 | break; |
664 | case BPF_LD_W | BPF_ABS: |
665 | d = pkt_caplen - K; |
666 | if (d >= sizeof(uint32_t)) |
667 | r->A = extract_u32(pkt, off: K); |
668 | else |
669 | set_return(r); |
670 | break; |
671 | case BPF_LD_H | BPF_ABS: |
672 | d = pkt_caplen - K; |
673 | if (d >= sizeof(uint16_t)) |
674 | r->A = extract_u16(pkt, off: K); |
675 | else |
676 | set_return(r); |
677 | break; |
678 | case BPF_LD_B | BPF_ABS: |
679 | d = pkt_caplen - K; |
680 | if (d >= sizeof(uint8_t)) |
681 | r->A = extract_u8(pkt, off: K); |
682 | else |
683 | set_return(r); |
684 | break; |
685 | case BPF_LD_W | BPF_IND: |
686 | d = pkt_caplen - (r->X + K); |
687 | if (d >= sizeof(uint32_t)) |
688 | r->A = extract_u32(pkt, off: r->X + K); |
689 | break; |
690 | case BPF_LD_H | BPF_IND: |
691 | d = pkt_caplen - (r->X + K); |
692 | if (d >= sizeof(uint16_t)) |
693 | r->A = extract_u16(pkt, off: r->X + K); |
694 | else |
695 | set_return(r); |
696 | break; |
697 | case BPF_LD_B | BPF_IND: |
698 | d = pkt_caplen - (r->X + K); |
699 | if (d >= sizeof(uint8_t)) |
700 | r->A = extract_u8(pkt, off: r->X + K); |
701 | else |
702 | set_return(r); |
703 | break; |
704 | case BPF_LDX_B | BPF_MSH: |
705 | d = pkt_caplen - K; |
706 | if (d >= sizeof(uint8_t)) { |
707 | r->X = extract_u8(pkt, off: K); |
708 | r->X = (r->X & 0xf) << 2; |
709 | } else |
710 | set_return(r); |
711 | break; |
712 | case BPF_LD_W | BPF_LEN: |
713 | r->A = pkt_len; |
714 | break; |
715 | case BPF_LDX_W | BPF_LEN: |
716 | r->A = pkt_len; |
717 | break; |
718 | case BPF_LD | BPF_IMM: |
719 | r->A = K; |
720 | break; |
721 | case BPF_LDX | BPF_IMM: |
722 | r->X = K; |
723 | break; |
724 | case BPF_LD | BPF_MEM: |
725 | r->A = r->M[K]; |
726 | break; |
727 | case BPF_LDX | BPF_MEM: |
728 | r->X = r->M[K]; |
729 | break; |
730 | case BPF_JMP_JA: |
731 | r->Pc += K; |
732 | break; |
733 | case BPF_JMP_JGT | BPF_X: |
734 | r->Pc += r->A > r->X ? f->jt : f->jf; |
735 | break; |
736 | case BPF_JMP_JGT | BPF_K: |
737 | r->Pc += r->A > K ? f->jt : f->jf; |
738 | break; |
739 | case BPF_JMP_JGE | BPF_X: |
740 | r->Pc += r->A >= r->X ? f->jt : f->jf; |
741 | break; |
742 | case BPF_JMP_JGE | BPF_K: |
743 | r->Pc += r->A >= K ? f->jt : f->jf; |
744 | break; |
745 | case BPF_JMP_JEQ | BPF_X: |
746 | r->Pc += r->A == r->X ? f->jt : f->jf; |
747 | break; |
748 | case BPF_JMP_JEQ | BPF_K: |
749 | r->Pc += r->A == K ? f->jt : f->jf; |
750 | break; |
751 | case BPF_JMP_JSET | BPF_X: |
752 | r->Pc += r->A & r->X ? f->jt : f->jf; |
753 | break; |
754 | case BPF_JMP_JSET | BPF_K: |
755 | r->Pc += r->A & K ? f->jt : f->jf; |
756 | break; |
757 | case BPF_ALU_NEG: |
758 | r->A = -r->A; |
759 | break; |
760 | case BPF_ALU_LSH | BPF_X: |
761 | r->A <<= r->X; |
762 | break; |
763 | case BPF_ALU_LSH | BPF_K: |
764 | r->A <<= K; |
765 | break; |
766 | case BPF_ALU_RSH | BPF_X: |
767 | r->A >>= r->X; |
768 | break; |
769 | case BPF_ALU_RSH | BPF_K: |
770 | r->A >>= K; |
771 | break; |
772 | case BPF_ALU_ADD | BPF_X: |
773 | r->A += r->X; |
774 | break; |
775 | case BPF_ALU_ADD | BPF_K: |
776 | r->A += K; |
777 | break; |
778 | case BPF_ALU_SUB | BPF_X: |
779 | r->A -= r->X; |
780 | break; |
781 | case BPF_ALU_SUB | BPF_K: |
782 | r->A -= K; |
783 | break; |
784 | case BPF_ALU_MUL | BPF_X: |
785 | r->A *= r->X; |
786 | break; |
787 | case BPF_ALU_MUL | BPF_K: |
788 | r->A *= K; |
789 | break; |
790 | case BPF_ALU_DIV | BPF_X: |
791 | case BPF_ALU_MOD | BPF_X: |
792 | if (r->X == 0) { |
793 | set_return(r); |
794 | break; |
795 | } |
796 | goto do_div; |
797 | case BPF_ALU_DIV | BPF_K: |
798 | case BPF_ALU_MOD | BPF_K: |
799 | if (K == 0) { |
800 | set_return(r); |
801 | break; |
802 | } |
803 | do_div: |
804 | switch (f->code) { |
805 | case BPF_ALU_DIV | BPF_X: |
806 | r->A /= r->X; |
807 | break; |
808 | case BPF_ALU_DIV | BPF_K: |
809 | r->A /= K; |
810 | break; |
811 | case BPF_ALU_MOD | BPF_X: |
812 | r->A %= r->X; |
813 | break; |
814 | case BPF_ALU_MOD | BPF_K: |
815 | r->A %= K; |
816 | break; |
817 | } |
818 | break; |
819 | case BPF_ALU_AND | BPF_X: |
820 | r->A &= r->X; |
821 | break; |
822 | case BPF_ALU_AND | BPF_K: |
823 | r->A &= K; |
824 | break; |
825 | case BPF_ALU_OR | BPF_X: |
826 | r->A |= r->X; |
827 | break; |
828 | case BPF_ALU_OR | BPF_K: |
829 | r->A |= K; |
830 | break; |
831 | case BPF_ALU_XOR | BPF_X: |
832 | r->A ^= r->X; |
833 | break; |
834 | case BPF_ALU_XOR | BPF_K: |
835 | r->A ^= K; |
836 | break; |
837 | } |
838 | } |
839 | |
840 | static bool bpf_pc_has_breakpoint(uint16_t pc) |
841 | { |
842 | int i; |
843 | |
844 | for (i = 0; i < array_size(bpf_breakpoints); i++) { |
845 | if (bpf_breakpoints[i] < 0) |
846 | continue; |
847 | if (bpf_breakpoints[i] == pc) |
848 | return true; |
849 | } |
850 | |
851 | return false; |
852 | } |
853 | |
854 | static bool bpf_handle_breakpoint(struct bpf_regs *r, struct sock_filter *f, |
855 | uint8_t *pkt, uint32_t pkt_caplen, |
856 | uint32_t pkt_len) |
857 | { |
858 | rl_printf(fmt: "-- register dump --\n" ); |
859 | bpf_dump_curr(r, f: &f[r->Pc]); |
860 | rl_printf(fmt: "-- packet dump --\n" ); |
861 | bpf_dump_pkt(pkt, pkt_caplen, pkt_len); |
862 | rl_printf(fmt: "(breakpoint)\n" ); |
863 | return true; |
864 | } |
865 | |
866 | static int bpf_run_all(struct sock_filter *f, uint16_t bpf_len, uint8_t *pkt, |
867 | uint32_t pkt_caplen, uint32_t pkt_len) |
868 | { |
869 | bool stop = false; |
870 | |
871 | while (bpf_curr.Rs == false && stop == false) { |
872 | bpf_safe_regs(); |
873 | |
874 | if (bpf_pc_has_breakpoint(pc: bpf_curr.Pc)) |
875 | stop = bpf_handle_breakpoint(r: &bpf_curr, f, pkt, |
876 | pkt_caplen, pkt_len); |
877 | |
878 | bpf_single_step(r: &bpf_curr, f: &f[bpf_curr.Pc], pkt, pkt_caplen, |
879 | pkt_len); |
880 | bpf_curr.Pc++; |
881 | } |
882 | |
883 | return stop ? -1 : bpf_curr.R; |
884 | } |
885 | |
886 | static int bpf_run_stepping(struct sock_filter *f, uint16_t bpf_len, |
887 | uint8_t *pkt, uint32_t pkt_caplen, |
888 | uint32_t pkt_len, int next) |
889 | { |
890 | bool stop = false; |
891 | int i = 1; |
892 | |
893 | while (!bpf_curr.Rs && !stop) { |
894 | bpf_safe_regs(); |
895 | |
896 | if (i++ == next) |
897 | stop = bpf_handle_breakpoint(r: &bpf_curr, f, pkt, |
898 | pkt_caplen, pkt_len); |
899 | |
900 | bpf_single_step(r: &bpf_curr, f: &f[bpf_curr.Pc], pkt, pkt_caplen, |
901 | pkt_len); |
902 | bpf_curr.Pc++; |
903 | } |
904 | |
905 | return stop ? -1 : bpf_curr.R; |
906 | } |
907 | |
908 | static bool pcap_loaded(void) |
909 | { |
910 | if (pcap_fd < 0) |
911 | rl_printf(fmt: "no pcap file loaded!\n" ); |
912 | |
913 | return pcap_fd >= 0; |
914 | } |
915 | |
916 | static struct pcap_pkthdr *pcap_curr_pkt(void) |
917 | { |
918 | return (void *) pcap_ptr_va_curr; |
919 | } |
920 | |
921 | static bool pcap_next_pkt(void) |
922 | { |
923 | struct pcap_pkthdr *hdr = pcap_curr_pkt(); |
924 | |
925 | if (pcap_ptr_va_curr + sizeof(*hdr) - |
926 | pcap_ptr_va_start >= pcap_map_size) |
927 | return false; |
928 | if (hdr->caplen == 0 || hdr->len == 0 || hdr->caplen > hdr->len) |
929 | return false; |
930 | if (pcap_ptr_va_curr + sizeof(*hdr) + hdr->caplen - |
931 | pcap_ptr_va_start >= pcap_map_size) |
932 | return false; |
933 | |
934 | pcap_ptr_va_curr += (sizeof(*hdr) + hdr->caplen); |
935 | return true; |
936 | } |
937 | |
938 | static void pcap_reset_pkt(void) |
939 | { |
940 | pcap_ptr_va_curr = pcap_ptr_va_start + sizeof(struct pcap_filehdr); |
941 | } |
942 | |
943 | static int try_load_pcap(const char *file) |
944 | { |
945 | struct pcap_filehdr *hdr; |
946 | struct stat sb; |
947 | int ret; |
948 | |
949 | pcap_fd = open(file, O_RDONLY); |
950 | if (pcap_fd < 0) { |
951 | rl_printf(fmt: "cannot open pcap [%s]!\n" , strerror(errno)); |
952 | return CMD_ERR; |
953 | } |
954 | |
955 | ret = fstat(pcap_fd, &sb); |
956 | if (ret < 0) { |
957 | rl_printf(fmt: "cannot fstat pcap file!\n" ); |
958 | return CMD_ERR; |
959 | } |
960 | |
961 | if (!S_ISREG(sb.st_mode)) { |
962 | rl_printf(fmt: "not a regular pcap file, duh!\n" ); |
963 | return CMD_ERR; |
964 | } |
965 | |
966 | pcap_map_size = sb.st_size; |
967 | if (pcap_map_size <= sizeof(struct pcap_filehdr)) { |
968 | rl_printf(fmt: "pcap file too small!\n" ); |
969 | return CMD_ERR; |
970 | } |
971 | |
972 | pcap_ptr_va_start = mmap(NULL, pcap_map_size, PROT_READ, |
973 | MAP_SHARED | MAP_LOCKED, pcap_fd, 0); |
974 | if (pcap_ptr_va_start == MAP_FAILED) { |
975 | rl_printf(fmt: "mmap of file failed!" ); |
976 | return CMD_ERR; |
977 | } |
978 | |
979 | hdr = (void *) pcap_ptr_va_start; |
980 | if (hdr->magic != TCPDUMP_MAGIC) { |
981 | rl_printf(fmt: "wrong pcap magic!\n" ); |
982 | return CMD_ERR; |
983 | } |
984 | |
985 | pcap_reset_pkt(); |
986 | |
987 | return CMD_OK; |
988 | |
989 | } |
990 | |
991 | static void try_close_pcap(void) |
992 | { |
993 | if (pcap_fd >= 0) { |
994 | munmap(pcap_ptr_va_start, pcap_map_size); |
995 | close(pcap_fd); |
996 | |
997 | pcap_ptr_va_start = pcap_ptr_va_curr = NULL; |
998 | pcap_map_size = 0; |
999 | pcap_packet = 0; |
1000 | pcap_fd = -1; |
1001 | } |
1002 | } |
1003 | |
1004 | static int cmd_load_bpf(char *bpf_string) |
1005 | { |
1006 | char sp, *token, separator = ','; |
1007 | unsigned short bpf_len, i = 0; |
1008 | struct sock_filter tmp; |
1009 | |
1010 | bpf_prog_len = 0; |
1011 | memset(bpf_image, 0, sizeof(bpf_image)); |
1012 | |
1013 | if (sscanf(bpf_string, "%hu%c" , &bpf_len, &sp) != 2 || |
1014 | sp != separator || bpf_len > BPF_MAXINSNS || bpf_len == 0) { |
1015 | rl_printf(fmt: "syntax error in head length encoding!\n" ); |
1016 | return CMD_ERR; |
1017 | } |
1018 | |
1019 | token = bpf_string; |
1020 | while ((token = strchr(token, separator)) && (++token)[0]) { |
1021 | if (i >= bpf_len) { |
1022 | rl_printf(fmt: "program exceeds encoded length!\n" ); |
1023 | return CMD_ERR; |
1024 | } |
1025 | |
1026 | if (sscanf(token, "%hu %hhu %hhu %u," , |
1027 | &tmp.code, &tmp.jt, &tmp.jf, &tmp.k) != 4) { |
1028 | rl_printf(fmt: "syntax error at instruction %d!\n" , i); |
1029 | return CMD_ERR; |
1030 | } |
1031 | |
1032 | bpf_image[i].code = tmp.code; |
1033 | bpf_image[i].jt = tmp.jt; |
1034 | bpf_image[i].jf = tmp.jf; |
1035 | bpf_image[i].k = tmp.k; |
1036 | |
1037 | i++; |
1038 | } |
1039 | |
1040 | if (i != bpf_len) { |
1041 | rl_printf(fmt: "syntax error exceeding encoded length!\n" ); |
1042 | return CMD_ERR; |
1043 | } else |
1044 | bpf_prog_len = bpf_len; |
1045 | if (!bpf_runnable(f: bpf_image, len: bpf_prog_len)) |
1046 | bpf_prog_len = 0; |
1047 | |
1048 | return CMD_OK; |
1049 | } |
1050 | |
1051 | static int cmd_load_pcap(char *file) |
1052 | { |
1053 | char *file_trim, *tmp; |
1054 | |
1055 | file_trim = strtok_r(file, " " , &tmp); |
1056 | if (file_trim == NULL) |
1057 | return CMD_ERR; |
1058 | |
1059 | try_close_pcap(); |
1060 | |
1061 | return try_load_pcap(file: file_trim); |
1062 | } |
1063 | |
1064 | static int cmd_load(char *arg) |
1065 | { |
1066 | char *subcmd, *cont = NULL, *tmp = strdup(arg); |
1067 | int ret = CMD_OK; |
1068 | |
1069 | subcmd = strtok_r(tmp, " " , &cont); |
1070 | if (subcmd == NULL) |
1071 | goto out; |
1072 | if (matches(cmd: subcmd, pattern: "bpf" ) == 0) { |
1073 | bpf_reset(); |
1074 | bpf_reset_breakpoints(); |
1075 | |
1076 | if (!cont) |
1077 | ret = CMD_ERR; |
1078 | else |
1079 | ret = cmd_load_bpf(bpf_string: cont); |
1080 | } else if (matches(cmd: subcmd, pattern: "pcap" ) == 0) { |
1081 | ret = cmd_load_pcap(file: cont); |
1082 | } else { |
1083 | out: |
1084 | rl_printf(fmt: "bpf <code>: load bpf code\n" ); |
1085 | rl_printf(fmt: "pcap <file>: load pcap file\n" ); |
1086 | ret = CMD_ERR; |
1087 | } |
1088 | |
1089 | free(tmp); |
1090 | return ret; |
1091 | } |
1092 | |
1093 | static int cmd_step(char *num) |
1094 | { |
1095 | struct pcap_pkthdr *hdr; |
1096 | int steps, ret; |
1097 | |
1098 | if (!bpf_prog_loaded() || !pcap_loaded()) |
1099 | return CMD_ERR; |
1100 | |
1101 | steps = strtol(num, NULL, 10); |
1102 | if (steps == 0 || strlen(num) == 0) |
1103 | steps = 1; |
1104 | if (steps < 0) { |
1105 | if (!bpf_restore_regs(off: steps)) |
1106 | return CMD_ERR; |
1107 | steps = 1; |
1108 | } |
1109 | |
1110 | hdr = pcap_curr_pkt(); |
1111 | ret = bpf_run_stepping(f: bpf_image, bpf_len: bpf_prog_len, |
1112 | pkt: (uint8_t *) hdr + sizeof(*hdr), |
1113 | pkt_caplen: hdr->caplen, pkt_len: hdr->len, next: steps); |
1114 | if (ret >= 0 || bpf_curr.Rs) { |
1115 | bpf_reset(); |
1116 | if (!pcap_next_pkt()) { |
1117 | rl_printf(fmt: "(going back to first packet)\n" ); |
1118 | pcap_reset_pkt(); |
1119 | } else { |
1120 | rl_printf(fmt: "(next packet)\n" ); |
1121 | } |
1122 | } |
1123 | |
1124 | return CMD_OK; |
1125 | } |
1126 | |
1127 | static int cmd_select(char *num) |
1128 | { |
1129 | unsigned int which, i; |
1130 | bool have_next = true; |
1131 | |
1132 | if (!pcap_loaded() || strlen(num) == 0) |
1133 | return CMD_ERR; |
1134 | |
1135 | which = strtoul(num, NULL, 10); |
1136 | if (which == 0) { |
1137 | rl_printf(fmt: "packet count starts with 1, clamping!\n" ); |
1138 | which = 1; |
1139 | } |
1140 | |
1141 | pcap_reset_pkt(); |
1142 | bpf_reset(); |
1143 | |
1144 | for (i = 0; i < which && (have_next = pcap_next_pkt()); i++) |
1145 | /* noop */; |
1146 | if (!have_next || pcap_curr_pkt() == NULL) { |
1147 | rl_printf(fmt: "no packet #%u available!\n" , which); |
1148 | pcap_reset_pkt(); |
1149 | return CMD_ERR; |
1150 | } |
1151 | |
1152 | return CMD_OK; |
1153 | } |
1154 | |
1155 | static int cmd_breakpoint(char *subcmd) |
1156 | { |
1157 | if (!bpf_prog_loaded()) |
1158 | return CMD_ERR; |
1159 | if (strlen(subcmd) == 0) |
1160 | bpf_dump_breakpoints(); |
1161 | else if (matches(cmd: subcmd, pattern: "reset" ) == 0) |
1162 | bpf_reset_breakpoints(); |
1163 | else { |
1164 | unsigned int where = strtoul(subcmd, NULL, 10); |
1165 | |
1166 | if (where < bpf_prog_len) { |
1167 | bpf_set_breakpoints(where); |
1168 | rl_printf(fmt: "breakpoint at: " ); |
1169 | bpf_disasm(f: bpf_image[where], i: where); |
1170 | } |
1171 | } |
1172 | |
1173 | return CMD_OK; |
1174 | } |
1175 | |
1176 | static int cmd_run(char *num) |
1177 | { |
1178 | static uint32_t pass, fail; |
1179 | bool has_limit = true; |
1180 | int pkts = 0, i = 0; |
1181 | |
1182 | if (!bpf_prog_loaded() || !pcap_loaded()) |
1183 | return CMD_ERR; |
1184 | |
1185 | pkts = strtol(num, NULL, 10); |
1186 | if (pkts == 0 || strlen(num) == 0) |
1187 | has_limit = false; |
1188 | |
1189 | do { |
1190 | struct pcap_pkthdr *hdr = pcap_curr_pkt(); |
1191 | int ret = bpf_run_all(f: bpf_image, bpf_len: bpf_prog_len, |
1192 | pkt: (uint8_t *) hdr + sizeof(*hdr), |
1193 | pkt_caplen: hdr->caplen, pkt_len: hdr->len); |
1194 | if (ret > 0) |
1195 | pass++; |
1196 | else if (ret == 0) |
1197 | fail++; |
1198 | else |
1199 | return CMD_OK; |
1200 | bpf_reset(); |
1201 | } while (pcap_next_pkt() && (!has_limit || (++i < pkts))); |
1202 | |
1203 | rl_printf(fmt: "bpf passes:%u fails:%u\n" , pass, fail); |
1204 | |
1205 | pcap_reset_pkt(); |
1206 | bpf_reset(); |
1207 | |
1208 | pass = fail = 0; |
1209 | return CMD_OK; |
1210 | } |
1211 | |
1212 | static int cmd_disassemble(char *line_string) |
1213 | { |
1214 | bool single_line = false; |
1215 | unsigned long line; |
1216 | |
1217 | if (!bpf_prog_loaded()) |
1218 | return CMD_ERR; |
1219 | if (strlen(line_string) > 0 && |
1220 | (line = strtoul(line_string, NULL, 10)) < bpf_prog_len) |
1221 | single_line = true; |
1222 | if (single_line) |
1223 | bpf_disasm(f: bpf_image[line], i: line); |
1224 | else |
1225 | bpf_disasm_all(f: bpf_image, len: bpf_prog_len); |
1226 | |
1227 | return CMD_OK; |
1228 | } |
1229 | |
1230 | static int cmd_dump(char *dontcare) |
1231 | { |
1232 | if (!bpf_prog_loaded()) |
1233 | return CMD_ERR; |
1234 | |
1235 | bpf_dump_all(f: bpf_image, len: bpf_prog_len); |
1236 | |
1237 | return CMD_OK; |
1238 | } |
1239 | |
1240 | static int cmd_quit(char *dontcare) |
1241 | { |
1242 | return CMD_EX; |
1243 | } |
1244 | |
1245 | static const struct shell_cmd cmds[] = { |
1246 | { .name = "load" , .func = cmd_load }, |
1247 | { .name = "select" , .func = cmd_select }, |
1248 | { .name = "step" , .func = cmd_step }, |
1249 | { .name = "run" , .func = cmd_run }, |
1250 | { .name = "breakpoint" , .func = cmd_breakpoint }, |
1251 | { .name = "disassemble" , .func = cmd_disassemble }, |
1252 | { .name = "dump" , .func = cmd_dump }, |
1253 | { .name = "quit" , .func = cmd_quit }, |
1254 | }; |
1255 | |
1256 | static int execf(char *arg) |
1257 | { |
1258 | char *cmd, *cont, *tmp = strdup(arg); |
1259 | int i, ret = 0, len; |
1260 | |
1261 | cmd = strtok_r(tmp, " " , &cont); |
1262 | if (cmd == NULL) |
1263 | goto out; |
1264 | len = strlen(cmd); |
1265 | for (i = 0; i < array_size(cmds); i++) { |
1266 | if (len != strlen(cmds[i].name)) |
1267 | continue; |
1268 | if (strncmp(cmds[i].name, cmd, len) == 0) { |
1269 | ret = cmds[i].func(cont); |
1270 | break; |
1271 | } |
1272 | } |
1273 | out: |
1274 | free(tmp); |
1275 | return ret; |
1276 | } |
1277 | |
1278 | static char *shell_comp_gen(const char *buf, int state) |
1279 | { |
1280 | static int list_index, len; |
1281 | |
1282 | if (!state) { |
1283 | list_index = 0; |
1284 | len = strlen(buf); |
1285 | } |
1286 | |
1287 | for (; list_index < array_size(cmds); ) { |
1288 | const char *name = cmds[list_index].name; |
1289 | |
1290 | list_index++; |
1291 | if (strncmp(name, buf, len) == 0) |
1292 | return strdup(name); |
1293 | } |
1294 | |
1295 | return NULL; |
1296 | } |
1297 | |
1298 | static char **shell_completion(const char *buf, int start, int end) |
1299 | { |
1300 | char **matches = NULL; |
1301 | |
1302 | if (start == 0) |
1303 | matches = rl_completion_matches(buf, shell_comp_gen); |
1304 | |
1305 | return matches; |
1306 | } |
1307 | |
1308 | static void intr_shell(int sig) |
1309 | { |
1310 | if (rl_end) |
1311 | rl_kill_line(-1, 0); |
1312 | |
1313 | rl_crlf(); |
1314 | rl_refresh_line(0, 0); |
1315 | rl_free_line_state(); |
1316 | } |
1317 | |
1318 | static void init_shell(FILE *fin, FILE *fout) |
1319 | { |
1320 | char file[128]; |
1321 | |
1322 | snprintf(buf: file, size: sizeof(file), fmt: "%s/.bpf_dbg_history" , getenv("HOME" )); |
1323 | read_history(file); |
1324 | |
1325 | rl_instream = fin; |
1326 | rl_outstream = fout; |
1327 | |
1328 | rl_readline_name = "bpf_dbg" ; |
1329 | rl_terminal_name = getenv("TERM" ); |
1330 | |
1331 | rl_catch_signals = 0; |
1332 | rl_catch_sigwinch = 1; |
1333 | |
1334 | rl_attempted_completion_function = shell_completion; |
1335 | |
1336 | rl_bind_key('\t', rl_complete); |
1337 | |
1338 | rl_bind_key_in_map('\t', rl_complete, emacs_meta_keymap); |
1339 | rl_bind_key_in_map('\033', rl_complete, emacs_meta_keymap); |
1340 | |
1341 | snprintf(buf: file, size: sizeof(file), fmt: "%s/.bpf_dbg_init" , getenv("HOME" )); |
1342 | rl_read_init_file(file); |
1343 | |
1344 | rl_prep_terminal(0); |
1345 | rl_set_signals(); |
1346 | |
1347 | signal(SIGINT, intr_shell); |
1348 | } |
1349 | |
1350 | static void exit_shell(FILE *fin, FILE *fout) |
1351 | { |
1352 | char file[128]; |
1353 | |
1354 | snprintf(buf: file, size: sizeof(file), fmt: "%s/.bpf_dbg_history" , getenv("HOME" )); |
1355 | write_history(file); |
1356 | |
1357 | clear_history(); |
1358 | rl_deprep_terminal(); |
1359 | |
1360 | try_close_pcap(); |
1361 | |
1362 | if (fin != stdin) |
1363 | fclose(fin); |
1364 | if (fout != stdout) |
1365 | fclose(fout); |
1366 | } |
1367 | |
1368 | static int run_shell_loop(FILE *fin, FILE *fout) |
1369 | { |
1370 | char *buf; |
1371 | |
1372 | init_shell(fin, fout); |
1373 | |
1374 | while ((buf = readline("> " )) != NULL) { |
1375 | int ret = execf(arg: buf); |
1376 | if (ret == CMD_EX) |
1377 | break; |
1378 | if (ret == CMD_OK && strlen(buf) > 0) |
1379 | add_history(buf); |
1380 | |
1381 | free(buf); |
1382 | } |
1383 | |
1384 | exit_shell(fin, fout); |
1385 | return 0; |
1386 | } |
1387 | |
1388 | int main(int argc, char **argv) |
1389 | { |
1390 | FILE *fin = NULL, *fout = NULL; |
1391 | |
1392 | if (argc >= 2) |
1393 | fin = fopen(argv[1], "r" ); |
1394 | if (argc >= 3) |
1395 | fout = fopen(argv[2], "w" ); |
1396 | |
1397 | return run_shell_loop(fin ? : stdin, fout ? : stdout); |
1398 | } |
1399 | |