diff options
author | rgrimes <rgrimes@FreeBSD.org> | 1993-06-12 14:49:13 +0000 |
---|---|---|
committer | rgrimes <rgrimes@FreeBSD.org> | 1993-06-12 14:49:13 +0000 |
commit | 869ee22f8da97214d87ba34f8c7900dbf7f1288c (patch) | |
tree | 4d8b6067ee93228ea53f75f025d7a28f463c24dd /usr.sbin/tcpdump | |
download | FreeBSD-src-869ee22f8da97214d87ba34f8c7900dbf7f1288c.zip FreeBSD-src-869ee22f8da97214d87ba34f8c7900dbf7f1288c.tar.gz |
Initial import, 0.1 + pk 0.2.4-B1
Diffstat (limited to 'usr.sbin/tcpdump')
-rw-r--r-- | usr.sbin/tcpdump/tcpdump/bpf_image.c | 282 | ||||
-rw-r--r-- | usr.sbin/tcpdump/tcpdump/etherent.c | 144 | ||||
-rw-r--r-- | usr.sbin/tcpdump/tcpdump/etherent.h | 34 | ||||
-rw-r--r-- | usr.sbin/tcpdump/tcpdump/etherproto.h | 70 | ||||
-rw-r--r-- | usr.sbin/tcpdump/tcpdump/gencode.c | 1384 | ||||
-rw-r--r-- | usr.sbin/tcpdump/tcpdump/gencode.h | 156 | ||||
-rw-r--r-- | usr.sbin/tcpdump/tcpdump/inet.c | 172 | ||||
-rw-r--r-- | usr.sbin/tcpdump/tcpdump/md.c | 35 | ||||
-rw-r--r-- | usr.sbin/tcpdump/tcpdump/nametoaddr.c | 258 | ||||
-rw-r--r-- | usr.sbin/tcpdump/tcpdump/nametoaddr.h | 43 | ||||
-rw-r--r-- | usr.sbin/tcpdump/tcpdump/optimize.c | 1871 | ||||
-rw-r--r-- | usr.sbin/tcpdump/tcpdump/os.c | 26 | ||||
-rw-r--r-- | usr.sbin/tcpdump/tcpdump/pcap.c | 209 | ||||
-rw-r--r-- | usr.sbin/tcpdump/tcpdump/savefile.c | 303 | ||||
-rw-r--r-- | usr.sbin/tcpdump/tcpdump/savefile.h | 75 | ||||
-rw-r--r-- | usr.sbin/tcpdump/tcpdump/tcpdump.1 | 1067 | ||||
-rw-r--r-- | usr.sbin/tcpdump/tcpdump/tcpgram.y | 232 | ||||
-rw-r--r-- | usr.sbin/tcpdump/tcpdump/tcplex.l | 146 |
18 files changed, 6507 insertions, 0 deletions
diff --git a/usr.sbin/tcpdump/tcpdump/bpf_image.c b/usr.sbin/tcpdump/tcpdump/bpf_image.c new file mode 100644 index 0000000..d36eab2 --- /dev/null +++ b/usr.sbin/tcpdump/tcpdump/bpf_image.c @@ -0,0 +1,282 @@ +/* + * Copyright (c) 1988-1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the University of California, + * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of + * the University nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +static char rcsid[] = + "@(#) $Header: bpf_image.c,v 1.10 92/01/26 21:01:16 mccanne Exp $ (LBL)"; +#endif + +#include <stdio.h> +#include <string.h> + +#include <sys/types.h> +#include <sys/time.h> +#include <net/bpf.h> + +char * +bpf_image(p, n) + struct bpf_insn *p; + int n; +{ + int v; + char *fmt, *op; + static char image[256]; + char operand[64]; + + v = p->k; + switch (p->code) { + + default: + op = "unimp"; + fmt = "0x%x"; + v = p->code; + break; + + case BPF_RET|BPF_K: + op = "ret"; + fmt = "#%d"; + break; + + case BPF_RET|BPF_A: + op = "ret"; + fmt = ""; + break; + + case BPF_LD|BPF_W|BPF_ABS: + op = "ld"; + fmt = "[%d]"; + break; + + case BPF_LD|BPF_H|BPF_ABS: + op = "ldh"; + fmt = "[%d]"; + break; + + case BPF_LD|BPF_B|BPF_ABS: + op = "ldb"; + fmt = "[%d]"; + break; + + case BPF_LD|BPF_W|BPF_LEN: + op = "ld"; + fmt = "#pktlen"; + break; + + case BPF_LD|BPF_W|BPF_IND: + op = "ld"; + fmt = "[x + %d]"; + break; + + case BPF_LD|BPF_H|BPF_IND: + op = "ldh"; + fmt = "[x + %d]"; + break; + + case BPF_LD|BPF_B|BPF_IND: + op = "ldb"; + fmt = "[x + %d]"; + break; + + case BPF_LD|BPF_IMM: + op = "ld"; + fmt = "#0x%x"; + break; + + case BPF_LDX|BPF_IMM: + op = "ldx"; + fmt = "#0x%x"; + break; + + case BPF_LDX|BPF_MSH|BPF_B: + op = "ldxb"; + fmt = "4*([%d]&0xf)"; + break; + + case BPF_LD|BPF_MEM: + op = "ld"; + fmt = "M[%d]"; + break; + + case BPF_LDX|BPF_MEM: + op = "ldx"; + fmt = "M[%d]"; + break; + + case BPF_ST: + op = "st"; + fmt = "M[%d]"; + break; + + case BPF_STX: + op = "stx"; + fmt = "M[%d]"; + break; + + case BPF_JMP|BPF_JA: + op = "ja"; + fmt = "%d"; + v = n + p->k; + break; + + case BPF_JMP|BPF_JGT|BPF_K: + op = "jgt"; + fmt = "#0x%x"; + break; + + case BPF_JMP|BPF_JGE|BPF_K: + op = "jge"; + fmt = "#0x%x"; + break; + + case BPF_JMP|BPF_JEQ|BPF_K: + op = "jeq"; + fmt = "#0x%x"; + break; + + case BPF_JMP|BPF_JSET|BPF_K: + op = "jset"; + fmt = "#0x%x"; + break; + + case BPF_JMP|BPF_JGT|BPF_X: + op = "jgt"; + fmt = "x"; + break; + + case BPF_JMP|BPF_JGE|BPF_X: + op = "jge"; + fmt = "x"; + break; + + case BPF_JMP|BPF_JEQ|BPF_X: + op = "jeq"; + fmt = "x"; + break; + + case BPF_JMP|BPF_JSET|BPF_X: + op = "jset"; + fmt = "x"; + break; + + case BPF_ALU|BPF_ADD|BPF_X: + op = "add"; + fmt = "x"; + break; + + case BPF_ALU|BPF_SUB|BPF_X: + op = "sub"; + fmt = "x"; + break; + + case BPF_ALU|BPF_MUL|BPF_X: + op = "mul"; + fmt = "x"; + break; + + case BPF_ALU|BPF_DIV|BPF_X: + op = "div"; + fmt = "x"; + break; + + case BPF_ALU|BPF_AND|BPF_X: + op = "and"; + fmt = "x"; + break; + + case BPF_ALU|BPF_OR|BPF_X: + op = "or"; + fmt = "x"; + break; + + case BPF_ALU|BPF_LSH|BPF_X: + op = "lsh"; + fmt = "x"; + break; + + case BPF_ALU|BPF_RSH|BPF_X: + op = "rsh"; + fmt = "x"; + break; + + case BPF_ALU|BPF_ADD|BPF_K: + op = "add"; + fmt = "#%d"; + break; + + case BPF_ALU|BPF_SUB|BPF_K: + op = "sub"; + fmt = "#%d"; + break; + + case BPF_ALU|BPF_MUL|BPF_K: + op = "mul"; + fmt = "#%d"; + break; + + case BPF_ALU|BPF_DIV|BPF_K: + op = "div"; + fmt = "#%d"; + break; + + case BPF_ALU|BPF_AND|BPF_K: + op = "and"; + fmt = "#%d"; + break; + + case BPF_ALU|BPF_OR|BPF_K: + op = "or"; + fmt = "#%d"; + break; + + case BPF_ALU|BPF_LSH|BPF_K: + op = "lsh"; + fmt = "#%d"; + break; + + case BPF_ALU|BPF_RSH|BPF_K: + op = "rsh"; + fmt = "#%d"; + break; + + case BPF_ALU|BPF_NEG: + op = "neg"; + fmt = ""; + break; + + case BPF_MISC|BPF_TAX: + op = "tax"; + fmt = ""; + break; + + case BPF_MISC|BPF_TXA: + op = "txa"; + fmt = ""; + break; + } + (void)sprintf(operand, fmt, v); + (void)sprintf(image, + (BPF_CLASS(p->code) == BPF_JMP && + BPF_OP(p->code) != BPF_JA) ? + "(%03d) %-8s %-16s jt %d\tjf %d" + : "(%03d) %-8s %s", + n, op, operand, n + 1 + p->jt, n + 1 + p->jf); + return image; +} diff --git a/usr.sbin/tcpdump/tcpdump/etherent.c b/usr.sbin/tcpdump/tcpdump/etherent.c new file mode 100644 index 0000000..9d7ee80 --- /dev/null +++ b/usr.sbin/tcpdump/tcpdump/etherent.c @@ -0,0 +1,144 @@ +/* + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the University of California, + * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of + * the University nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ +#ifndef lint +static char rcsid[] = + "@(#) $Header: etherent.c,v 1.2 90/09/20 23:16:06 mccanne Exp $ (LBL)"; +#endif + +#include <stdio.h> +#include <ctype.h> +#include <sys/types.h> +#include "interface.h" + +#ifndef ETHER_SERVICE + +#include "etherent.h" + +/* Hex digit to integer. */ +static inline int +xdtoi(c) +{ + if (isdigit(c)) + return c - '0'; + else if (islower(c)) + return c - 'a' + 10; + else + return c - 'A' + 10; +} + +static inline int +skip_space(f) + FILE *f; +{ + int c; + + do { + c = getc(f); + } while (isspace(c) && c != '\n'); + + return c; +} + +static inline int +skip_line(f) + FILE *f; +{ + int c; + + do + c = getc(f); + while (c != '\n' && c != EOF); + + return c; +} + +struct etherent * +next_etherent(fp) + FILE *fp; +{ + register int c, d, i; + char *bp; + static struct etherent e; + static int nline = 1; + top: + while (nline) { + /* Find addr */ + c = skip_space(fp); + if (c == '\n') + continue; + /* If this is a comment, or first thing on line + cannot be etehrnet address, skip the line. */ + else if (!isxdigit(c)) + c = skip_line(fp); + else { + /* must be the start of an address */ + for (i = 0; i < 6; i += 1) { + d = xdtoi(c); + c = getc(fp); + if (c != ':') { + d <<= 4; + d |= xdtoi(c); + c = getc(fp); + } + e.addr[i] = d; + if (c != ':') + break; + c = getc(fp); + } + nline = 0; + } + if (c == EOF) + return 0; + } + + /* If we started a new line, 'c' holds the char past the ether addr, + which we assume is white space. If we are continuning a line, + 'c' is garbage. In either case, we can throw it away. */ + + c = skip_space(fp); + if (c == '\n') { + nline = 1; + goto top; + } + else if (c == '#') { + (void)skip_line(fp); + nline = 1; + goto top; + } + else if (c == EOF) + return 0; + + /* Must be a name. */ + bp = e.name; + /* Use 'd' to prevent buffer overflow. */ + d = sizeof(e.name) - 1; + do { + *bp++ = c; + c = getc(fp); + } while (!isspace(c) && c != EOF && --d > 0); + *bp = '\0'; + if (c == '\n') + nline = 1; + + return &e; +} + +#endif diff --git a/usr.sbin/tcpdump/tcpdump/etherent.h b/usr.sbin/tcpdump/tcpdump/etherent.h new file mode 100644 index 0000000..83ebaab --- /dev/null +++ b/usr.sbin/tcpdump/tcpdump/etherent.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the University of California, + * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of + * the University nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * @(#) $Header: etherent.h,v 1.2 90/09/20 23:16:17 mccanne Exp $ (LBL) + */ + +/* File name of ethernet address data base. */ + +#define ETHERS_FILE "/etc/ethers" + +struct etherent { + u_char addr[6]; + char name[122]; +}; + +struct etherent *next_etherent(); + diff --git a/usr.sbin/tcpdump/tcpdump/etherproto.h b/usr.sbin/tcpdump/tcpdump/etherproto.h new file mode 100644 index 0000000..5c0e245 --- /dev/null +++ b/usr.sbin/tcpdump/tcpdump/etherproto.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 1988-1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the University of California, + * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of + * the University nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * @(#) $Header: etherproto.h,v 1.7 90/10/10 15:04:04 mccanne Exp $ (LBL) + */ + +/* Map between Ethernet protocol types and names */ + +/* Add other Ethernet packet types here */ +#ifndef ETHERTYPE_SPRITE +#define ETHERTYPE_SPRITE 0x0500 +#endif +#ifndef ETHERTYPE_MOPDL +#define ETHERTYPE_MOPDL 0x6001 +#endif +#ifndef ETHERTYPE_MOPRC +#define ETHERTYPE_MOPRC 0x6002 +#endif +#ifndef ETHERTYPE_DN +#define ETHERTYPE_DN 0x6003 +#endif +#ifndef ETHERTYPE_LAT +#define ETHERTYPE_LAT 0x6004 +#endif +#ifndef ETHERTYPE_LANBRIDGE +#define ETHERTYPE_LANBRIDGE 0x8038 +#endif +#ifndef ETHERTYPE_VEXP +#define ETHERTYPE_VEXP 0x805b +#endif +#ifndef ETHERTYPE_VPROD +#define ETHERTYPE_VPROD 0x805c +#endif +#ifndef ETHERTYPE_LOOPBACK +#define ETHERTYPE_LOOPBACK 0x9000 +#endif + +#ifndef ETHERTYPE_ATALK +#define ETHERTYPE_ATALK 0x809b /* XXX */ +#endif +#ifndef ETHERTYPE_AARP +#define ETHERTYPE_AARP 0x80f3 +#endif +#ifndef ETHERTYPE_NS +#define ETHERTYPE_NS 0x0600 +#endif + +struct eproto { + char *s; + u_short p; +}; + +extern struct eproto eproto_db[]; diff --git a/usr.sbin/tcpdump/tcpdump/gencode.c b/usr.sbin/tcpdump/tcpdump/gencode.c new file mode 100644 index 0000000..8cb48ea --- /dev/null +++ b/usr.sbin/tcpdump/tcpdump/gencode.c @@ -0,0 +1,1384 @@ +/* + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the University of California, + * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of + * the University nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ +#ifndef lint +static char rcsid[] = + "@(#) $Header: gencode.c,v 1.33 92/05/22 16:38:39 mccanne Exp $ (LBL)"; +#endif + +#ifdef __STDC__ +#include <stdlib.h> +#endif +#include <sys/types.h> +#include <sys/socket.h> +#include <net/if.h> +#include <netinet/in.h> +#include <netinet/if_ether.h> + +#include <sys/time.h> +#include <net/bpf.h> + +#include "interface.h" +#include "gencode.h" +#include "nametoaddr.h" +#include "extract.h" + +#define JMP(c) ((c)|BPF_JMP|BPF_K) + +extern struct bpf_insn *icode_to_fcode(); +extern u_long net_mask(); +static void init_linktype(); + +static int alloc_reg(); +static void free_reg(); + +static struct block *root; + +/* + * We divy out chunks of memory rather than call malloc each time so + * we don't have to worry about leaking memory. It's probably + * not a big deal if all this memory was wasted but it this ever + * goes into a library that would probably not be a good idea. + */ +#define NCHUNKS 16 +#define CHUNK0SIZE 1024 +struct chunk { + u_int n_left; + void *m; +}; + +static struct chunk chunks[NCHUNKS]; +static int cur_chunk; + +static void * +newchunk(n) + u_int n; +{ + struct chunk *cp; + int k, size; + + /* XXX Round up to nearest long. */ + n = (n + sizeof(long) - 1) & ~(sizeof(long) - 1); + + cp = &chunks[cur_chunk]; + if (n > cp->n_left) { + ++cp, k = ++cur_chunk; + if (k >= NCHUNKS) + error("out of memory"); + size = CHUNK0SIZE << k; + cp->m = (void *)malloc(size); + bzero((char *)cp->m, size); + cp->n_left = size; + if (n > size) + error("out of memory"); + } + cp->n_left -= n; + return (void *)((char *)cp->m + cp->n_left); +} + +static void +freechunks() +{ + int i; + + for (i = 0; i < NCHUNKS; ++i) + if (chunks[i].m) + free(chunks[i].m); +} + +static inline struct block * +new_block(code) + int code; +{ + struct block *p; + + p = (struct block *)newchunk(sizeof(*p)); + p->s.code = code; + p->head = p; + + return p; +} + +static inline struct slist * +new_stmt(code) + int code; +{ + struct slist *p; + + p = (struct slist *)newchunk(sizeof(*p)); + p->s.code = code; + + return p; +} + +static struct block * +gen_retblk(v) + int v; +{ + struct block *b = new_block(BPF_RET|BPF_K); + + b->s.k = v; + return b; +} + +static inline void +syntax() +{ + error("syntax error in filter expression"); +} + +static u_long netmask; + +struct bpf_program * +parse(buf, Oflag, linktype, mask) + char *buf; + int Oflag; + int linktype; + u_long mask; +{ + extern int n_errors; + static struct bpf_program F; + struct bpf_insn *p; + int len; + + netmask = mask; + + F.bf_insns = 0; + F.bf_len = 0; + + lex_init(buf ? buf : ""); + init_linktype(linktype); + yyparse(); + + if (n_errors) + syntax(); + + if (root == 0) + root = gen_retblk(snaplen); + + if (Oflag) { + optimize(&root); + if (root == 0 || + (root->s.code == (BPF_RET|BPF_K) && root->s.k == 0)) + error("expression rejects all packets"); + } + p = icode_to_fcode(root, &len); + F.bf_insns = p; + F.bf_len = len; + + freechunks(); + return &F; +} + +/* + * Backpatch the blocks in 'list' to 'target'. The 'sense' field indicates + * which of the jt and jf fields has been resolved and which is a pointer + * back to another unresolved block (or nil). At least one of the fields + * in each block is already resolved. + */ +static void +backpatch(list, target) + struct block *list, *target; +{ + struct block *next; + + while (list) { + if (!list->sense) { + next = JT(list); + JT(list) = target; + } else { + next = JF(list); + JF(list) = target; + } + list = next; + } +} + +/* + * Merge the lists in b0 and b1, using the 'sense' field to indicate + * which of jt and jf is the link. + */ +static void +merge(b0, b1) + struct block *b0, *b1; +{ + register struct block **p = &b0; + + /* Find end of list. */ + while (*p) + p = !((*p)->sense) ? &JT(*p) : &JF(*p); + + /* Concatenate the lists. */ + *p = b1; +} + +void +finish_parse(p) + struct block *p; +{ + backpatch(p, gen_retblk(snaplen)); + p->sense = !p->sense; + backpatch(p, gen_retblk(0)); + root = p->head; +} + +void +gen_and(b0, b1) + struct block *b0, *b1; +{ + backpatch(b0, b1->head); + b0->sense = !b0->sense; + b1->sense = !b1->sense; + merge(b1, b0); + b1->sense = !b1->sense; + b1->head = b0->head; +} + +void +gen_or(b0, b1) + struct block *b0, *b1; +{ + b0->sense = !b0->sense; + backpatch(b0, b1->head); + b0->sense = !b0->sense; + merge(b1, b0); + b1->head = b0->head; +} + +void +gen_not(b) + struct block *b; +{ + b->sense = !b->sense; +} + +static struct block * +gen_cmp(offset, size, v) + u_int offset, size; + long v; +{ + struct slist *s; + struct block *b; + + s = new_stmt(BPF_LD|BPF_ABS|size); + s->s.k = offset; + + b = new_block(JMP(BPF_JEQ)); + b->stmts = s; + b->s.k = v; + + return b; +} + +struct block * +gen_mcmp(offset, size, v, mask) + u_int offset, size; + long v; + u_long mask; +{ + struct block *b = gen_cmp(offset, size, v); + struct slist *s; + + if (mask != 0xffffffff) { + s = new_stmt(BPF_ALU|BPF_AND|BPF_K); + s->s.k = mask; + b->stmts->next = s; + } + return b; +} + +struct block * +gen_bcmp(offset, size, v) + u_int offset; + u_int size; + u_char *v; +{ + struct block *b, *tmp; + int k; + + b = 0; + while (size >= 4) { + k = size - 4; + tmp = gen_cmp(offset + k, BPF_W, EXTRACT_LONG(&v[k])); + if (b != 0) + gen_and(b, tmp); + b = tmp; + size -= 4; + } + while (size >= 2) { + k = size - 2; + tmp = gen_cmp(offset + k, BPF_H, (long)EXTRACT_SHORT(&v[k])); + if (b != 0) + gen_and(b, tmp); + b = tmp; + size -= 2; + } + if (size > 0) { + tmp = gen_cmp(offset, BPF_B, (long)v[0]); + if (b != 0) + gen_and(b, tmp); + b = tmp; + } + return b; +} + +/* + * Various code contructs need to know the layout of the data link + * layer. These variables give the necessary offsets. off_linktype + * is set to -1 for no encapsulation, in which case, IP is assumed. + */ +static u_int off_linktype; +static u_int off_nl; +static int linktype; + +static void +init_linktype(type) + int type; +{ + linktype = type; + + switch (type) { + + case DLT_EN10MB: + off_linktype = 12; + off_nl = 14; + return; + + case DLT_SLIP: + /* + * SLIP doesn't have a link level type. The 16 byte + * header is hacked into our SLIP driver. + */ + off_linktype = -1; + off_nl = 16; + return; + + case DLT_NULL: + off_linktype = -1; + off_nl = 0; + return; + + case DLT_PPP: + off_linktype = 2; + off_nl = 4; + return; + + case DLT_FDDI: + off_linktype = 19; + off_nl = 21; + return; + + case DLT_IEEE802: + off_linktype = 20; + off_nl = 22; + return; + } + error("unknown data link type 0x%x", linktype); + /* NOTREACHED */ +} + +static struct block * +gen_uncond(rsense) + int rsense; +{ + struct block *b; + struct slist *s; + + s = new_stmt(BPF_LD|BPF_IMM); + s->s.k = !rsense; + b = new_block(JMP(BPF_JEQ)); + b->stmts = s; + + return b; +} + +static inline struct block * +gen_true() +{ + return gen_uncond(1); +} + +static inline struct block * +gen_false() +{ + return gen_uncond(0); +} + +struct block * +gen_linktype(proto) + int proto; +{ + switch (linktype) { + case DLT_SLIP: + if (proto == ETHERTYPE_IP) + return gen_true(); + else + return gen_false(); + + case DLT_PPP: + if (proto == ETHERTYPE_IP) + proto = 0x0021; /* XXX - need ppp.h defs */ + break; + } + return gen_cmp(off_linktype, BPF_H, (long)proto); +} + +static struct block * +gen_hostop(addr, mask, dir, proto, src_off, dst_off) + u_long addr; + u_long mask; + int dir, proto; + u_int src_off, dst_off; +{ + struct block *b0, *b1; + u_int offset; + + switch (dir) { + + case Q_SRC: + offset = src_off; + break; + + case Q_DST: + offset = dst_off; + break; + + case Q_AND: + b0 = gen_hostop(addr, mask, Q_SRC, proto, src_off, dst_off); + b1 = gen_hostop(addr, mask, Q_DST, proto, src_off, dst_off); + gen_and(b0, b1); + return b1; + + case Q_OR: + case Q_DEFAULT: + b0 = gen_hostop(addr, mask, Q_SRC, proto, src_off, dst_off); + b1 = gen_hostop(addr, mask, Q_DST, proto, src_off, dst_off); + gen_or(b0, b1); + return b1; + + default: + abort(); + } + b0 = gen_linktype(proto); + b1 = gen_mcmp(offset, BPF_W, (long)addr, mask); + gen_and(b0, b1); + return b1; +} + +static struct block * +gen_ehostop(eaddr, dir) + u_char *eaddr; + int dir; +{ + struct block *b0, *b1; + + switch (dir) { + case Q_SRC: + return gen_bcmp(6, 6, eaddr); + + case Q_DST: + return gen_bcmp(0, 6, eaddr); + + case Q_AND: + b0 = gen_ehostop(eaddr, Q_SRC); + b1 = gen_ehostop(eaddr, Q_DST); + gen_and(b0, b1); + return b1; + + case Q_DEFAULT: + case Q_OR: + b0 = gen_ehostop(eaddr, Q_SRC); + b1 = gen_ehostop(eaddr, Q_DST); + gen_or(b0, b1); + return b1; + } + abort(); + /* NOTREACHED */ +} + +static struct block * +gen_host(addr, mask, proto, dir) + u_long addr; + u_long mask; + int proto; + int dir; +{ + struct block *b0, *b1; + + switch (proto) { + + case Q_DEFAULT: + b0 = gen_host(addr, mask, Q_IP, dir); + b1 = gen_host(addr, mask, Q_ARP, dir); + gen_or(b0, b1); + b0 = gen_host(addr, mask, Q_RARP, dir); + gen_or(b1, b0); + return b0; + + case Q_IP: + return gen_hostop(addr, mask, dir, ETHERTYPE_IP, + off_nl + 12, off_nl + 16); + + case Q_RARP: + return gen_hostop(addr, mask, dir, ETHERTYPE_REVARP, + off_nl + 14, off_nl + 24); + + case Q_ARP: + return gen_hostop(addr, mask, dir, ETHERTYPE_ARP, + off_nl + 14, off_nl + 24); + + case Q_TCP: + error("'tcp' modifier applied to host"); + + case Q_UDP: + error("'udp' modifier applied to host"); + + case Q_ICMP: + error("'icmp' modifier applied to host"); + } + abort(); + /* NOTREACHED */ +} + +static struct block * +gen_gateway(eaddr, alist, proto, dir) + u_char *eaddr; + u_long **alist; + int proto; + int dir; +{ + struct block *b0, *b1, *tmp; + + if (dir != 0) + error("direction applied to 'gateway'"); + + switch (proto) { + case Q_DEFAULT: + case Q_IP: + case Q_ARP: + case Q_RARP: + b0 = gen_ehostop(eaddr, Q_OR); + b1 = gen_host(**alist++, 0xffffffffL, proto, Q_OR); + while (*alist) { + tmp = gen_host(**alist++, 0xffffffffL, proto, Q_OR); + gen_or(b1, tmp); + b1 = tmp; + } + gen_not(b1); + gen_and(b0, b1); + return b1; + } + error("illegal modifier of 'gateway'"); + /* NOTREACHED */ +} + +struct block * +gen_proto_abbrev(proto) + int proto; +{ + struct block *b0, *b1; + + switch (proto) { + + case Q_TCP: + b0 = gen_linktype(ETHERTYPE_IP); + b1 = gen_cmp(off_nl + 9, BPF_B, (long)IPPROTO_TCP); + gen_and(b0, b1); + break; + + case Q_UDP: + b0 = gen_linktype(ETHERTYPE_IP); + b1 = gen_cmp(off_nl + 9, BPF_B, (long)IPPROTO_UDP); + gen_and(b0, b1); + break; + + case Q_ICMP: + b0 = gen_linktype(ETHERTYPE_IP); + b1 = gen_cmp(off_nl + 9, BPF_B, (long)IPPROTO_ICMP); + gen_and(b0, b1); + break; + + case Q_IP: + b1 = gen_linktype(ETHERTYPE_IP); + break; + + case Q_ARP: + b1 = gen_linktype(ETHERTYPE_ARP); + break; + + case Q_RARP: + b1 = gen_linktype(ETHERTYPE_REVARP); + break; + + case Q_LINK: + error("link layer applied in wrong context"); + + default: + abort(); + } + return b1; +} + +static struct block * +gen_ipfrag() +{ + struct slist *s; + struct block *b; + + /* not ip frag */ + s = new_stmt(BPF_LD|BPF_H|BPF_ABS); + s->s.k = off_nl + 6; + b = new_block(JMP(BPF_JSET)); + b->s.k = 0x1fff; + b->stmts = s; + gen_not(b); + + return b; +} + +static struct block * +gen_portatom(off, v) + int off; + long v; +{ + struct slist *s; + struct block *b; + + s = new_stmt(BPF_LDX|BPF_MSH|BPF_B); + s->s.k = off_nl; + + s->next = new_stmt(BPF_LD|BPF_IND|BPF_H); + s->next->s.k = off_nl + off; + + b = new_block(JMP(BPF_JEQ)); + b->stmts = s; + b->s.k = v; + + return b; +} + +struct block * +gen_portop(port, proto, dir) + int port; + int proto; + int dir; +{ + struct block *b0, *b1, *tmp; + + /* ip proto 'proto' */ + tmp = gen_cmp(off_nl + 9, BPF_B, (long)proto); + b0 = gen_ipfrag(); + gen_and(tmp, b0); + + switch (dir) { + case Q_SRC: + b1 = gen_portatom(0, (long)port); + break; + + case Q_DST: + b1 = gen_portatom(2, (long)port); + break; + + case Q_OR: + case Q_DEFAULT: + tmp = gen_portatom(0, (long)port); + b1 = gen_portatom(2, (long)port); + gen_or(tmp, b1); + break; + + case Q_AND: + tmp = gen_portatom(0, (long)port); + b1 = gen_portatom(2, (long)port); + gen_and(tmp, b1); + break; + + default: + abort(); + } + gen_and(b0, b1); + + return b1; +} + +static struct block * +gen_port(port, ip_proto, dir) + int port; + int ip_proto; + int dir; +{ + struct block *b0, *b1, *tmp; + + /* ether proto ip */ + b0 = gen_linktype(ETHERTYPE_IP); + + switch (ip_proto) { + case IPPROTO_UDP: + case IPPROTO_TCP: + b1 = gen_portop(port, ip_proto, dir); + break; + + case PROTO_UNDEF: + tmp = gen_portop(port, IPPROTO_TCP, dir); + b1 = gen_portop(port, IPPROTO_UDP, dir); + gen_or(tmp, b1); + break; + + default: + abort(); + } + gen_and(b0, b1); + return b1; +} + +int +lookup_proto(name, proto) + char *name; + int proto; +{ + int v; + + switch (proto) { + case Q_DEFAULT: + case Q_IP: + v = s_nametoproto(name); + if (v == PROTO_UNDEF) + error("unknown ip proto '%s'", name); + break; + + case Q_LINK: + /* XXX should look up h/w protocol type based on linktype */ + v = s_nametoeproto(name); + if (v == PROTO_UNDEF) + error("unknown ether proto '%s'", name); + break; + + default: + v = PROTO_UNDEF; + break; + } + return v; +} + +struct block * +gen_proto(v, proto, dir) + int v; + int proto; + int dir; +{ + struct block *b0, *b1; + + if (dir != Q_DEFAULT) + error("direction applied to 'proto'"); + + switch (proto) { + case Q_DEFAULT: + case Q_IP: + b0 = gen_linktype(ETHERTYPE_IP); + b1 = gen_cmp(off_nl + 9, BPF_B, (long)v); + gen_and(b0, b1); + return b1; + + case Q_ARP: + error("arp does not encapsulate another protocol"); + /* NOTREACHED */ + + case Q_RARP: + error("rarp does not encapsulate another protocol"); + /* NOTREACHED */ + + case Q_LINK: + return gen_linktype(v); + + case Q_UDP: + error("'udp proto' is bogus"); + + case Q_TCP: + error("'tcp proto' is bogus"); + + case Q_ICMP: + error("'icmp proto' is bogus"); + } + abort(); + /* NOTREACHED */ +} + +struct block * +gen_scode(name, q) + char *name; + struct qual q; +{ + int proto = q.proto; + int dir = q.dir; + u_char *eaddr; + u_long mask, addr, **alist; + struct block *b, *tmp; + int port, real_proto; + + switch (q.addr) { + + case Q_NET: + addr = s_nametonetaddr(name); + if (addr == 0) + error("unknown network '%s'", name); + mask = net_mask(&addr); + return gen_host(addr, mask, proto, dir); + + case Q_DEFAULT: + case Q_HOST: + if (proto == Q_LINK) { + /* XXX Should lookup hw addr based on link layer */ + eaddr = ETHER_hostton(name); + if (eaddr == 0) + error("unknown ether host '%s'", name); + return gen_ehostop(eaddr, dir); + + } else { + alist = s_nametoaddr(name); + if (alist == 0 || *alist == 0) + error("uknown host '%s'", name); + b = gen_host(**alist++, 0xffffffffL, proto, dir); + while (*alist) { + tmp = gen_host(**alist++, 0xffffffffL, + proto, dir); + gen_or(b, tmp); + b = tmp; + } + return b; + } + + case Q_PORT: + if (proto != Q_DEFAULT && proto != Q_UDP && proto != Q_TCP) + error("illegal qualifier of 'port'"); + if (s_nametoport(name, &port, &real_proto) == 0) + error("unknown port '%s'", name); + if (proto == Q_UDP) { + if (real_proto == IPPROTO_TCP) + error("port '%s' is tcp", name); + else + /* override PROTO_UNDEF */ + real_proto = IPPROTO_UDP; + } + if (proto == Q_TCP) { + if (real_proto == IPPROTO_UDP) + error("port '%s' is udp", name); + else + /* override PROTO_UNDEF */ + real_proto = IPPROTO_TCP; + } + return gen_port(port, real_proto, dir); + + case Q_GATEWAY: + eaddr = ETHER_hostton(name); + if (eaddr == 0) + error("unknown ether host: %s", name); + + alist = s_nametoaddr(name); + if (alist == 0 || *alist == 0) + error("uknown host '%s'", name); + return gen_gateway(eaddr, alist, proto, dir); + + case Q_PROTO: + real_proto = lookup_proto(name, proto); + if (real_proto >= 0) + return gen_proto(real_proto, proto, dir); + else + error("unknown protocol: %s", name); + + case Q_UNDEF: + syntax(); + /* NOTREACHED */ + } + abort(); + /* NOTREACHED */ +} + +struct block * +gen_ncode(v, q) + u_long v; + struct qual q; +{ + u_long mask; + int proto = q.proto; + int dir = q.dir; + + switch (q.addr) { + + case Q_DEFAULT: + case Q_HOST: + case Q_NET: + mask = net_mask(&v); + return gen_host(v, mask, proto, dir); + + case Q_PORT: + if (proto == Q_UDP) + proto = IPPROTO_UDP; + else if (proto == Q_TCP) + proto = IPPROTO_TCP; + else if (proto == Q_DEFAULT) + proto = PROTO_UNDEF; + else + error("illegal qualifier of 'port'"); + + return gen_port((int)v, proto, dir); + + case Q_GATEWAY: + error("'gateway' requires a name"); + /* NOTREACHED */ + + case Q_PROTO: + return gen_proto((int)v, proto, dir); + + case Q_UNDEF: + syntax(); + /* NOTREACHED */ + } + abort(); + /* NOTREACHED */ +} + +struct block * +gen_ecode(eaddr, q) + u_char *eaddr; + struct qual q; +{ + if ((q.addr == Q_HOST || q.addr == Q_DEFAULT) && q.proto == Q_LINK) + return gen_ehostop(eaddr, (int)q.dir); + else + error("ethernet address used in non-ether expression"); + /* NOTREACHED */ +} + +void +sappend(s0, s1) + struct slist *s0, *s1; +{ + /* + * This is definitely not the best way to do this, but the + * lists will rarely get long. + */ + while (s0->next) + s0 = s0->next; + s0->next = s1; +} + +struct slist * +xfer_to_x(a) + struct arth *a; +{ + struct slist *s; + + s = new_stmt(BPF_LDX|BPF_MEM); + s->s.k = a->regno; + return s; +} + +struct slist * +xfer_to_a(a) + struct arth *a; +{ + struct slist *s; + + s = new_stmt(BPF_LD|BPF_MEM); + s->s.k = a->regno; + return s; +} + +struct arth * +gen_load(proto, index, size) + int proto; + struct arth *index; + int size; +{ + struct slist *s, *tmp; + struct block *b; + int regno = alloc_reg(); + + free_reg(index->regno); + switch (size) { + + default: + error("data size must be 1, 2, or 4"); + + case 1: + size = BPF_B; + break; + + case 2: + size = BPF_H; + break; + + case 4: + size = BPF_W; + break; + } + switch (proto) { + default: + error("unsupported index operation"); + + case Q_LINK: + s = xfer_to_x(index); + tmp = new_stmt(BPF_LD|BPF_IND|size); + sappend(s, tmp); + sappend(index->s, s); + break; + + case Q_IP: + case Q_ARP: + case Q_RARP: + /* XXX Note that we assume a fixed link link header here. */ + s = xfer_to_x(index); + tmp = new_stmt(BPF_LD|BPF_IND|size); + tmp->s.k = off_nl; + sappend(s, tmp); + sappend(index->s, s); + + b = gen_proto_abbrev(proto); + if (index->b) + gen_and(index->b, b); + index->b = b; + break; + + case Q_TCP: + case Q_UDP: + case Q_ICMP: + s = new_stmt(BPF_LDX|BPF_MSH|BPF_B); + s->s.k = off_nl; + sappend(s, xfer_to_a(index)); + sappend(s, new_stmt(BPF_ALU|BPF_ADD|BPF_X)); + sappend(s, new_stmt(BPF_MISC|BPF_TAX)); + sappend(s, tmp = new_stmt(BPF_LD|BPF_IND|size)); + tmp->s.k = off_nl; + sappend(index->s, s); + + gen_and(gen_proto_abbrev(proto), b = gen_ipfrag()); + if (index->b) + gen_and(index->b, b); + index->b = b; + break; + } + index->regno = regno; + s = new_stmt(BPF_ST); + s->s.k = regno; + sappend(index->s, s); + + return index; +} + +struct block * +gen_relation(code, a0, a1, reversed) + int code; + struct arth *a0, *a1; + int reversed; +{ + struct slist *s0, *s1, *s2; + struct block *b, *tmp; + + s0 = xfer_to_x(a1); + s1 = xfer_to_a(a0); + s2 = new_stmt(BPF_ALU|BPF_SUB|BPF_X); + b = new_block(JMP(code)); + if (reversed) + gen_not(b); + + sappend(s1, s2); + sappend(s0, s1); + sappend(a1->s, s0); + sappend(a0->s, a1->s); + + b->stmts = a0->s; + + free_reg(a0->regno); + free_reg(a1->regno); + + /* 'and' together protocol checks */ + if (a0->b) { + if (a1->b) { + gen_and(a0->b, tmp = a1->b); + } + else + tmp = a0->b; + } else + tmp = a1->b; + + if (tmp) + gen_and(tmp, b); + + return b; +} + +struct arth * +gen_loadlen() +{ + int regno = alloc_reg(); + struct arth *a = (struct arth *)newchunk(sizeof(*a)); + struct slist *s; + + s = new_stmt(BPF_LD|BPF_LEN); + s->next = new_stmt(BPF_ST); + s->next->s.k = regno; + a->s = s; + a->regno = regno; + + return a; +} + +struct arth * +gen_loadi(val) + int val; +{ + struct arth *a; + struct slist *s; + int reg; + + a = (struct arth *)newchunk(sizeof(*a)); + + reg = alloc_reg(); + + s = new_stmt(BPF_LD|BPF_IMM); + s->s.k = val; + s->next = new_stmt(BPF_ST); + s->next->s.k = reg; + a->s = s; + a->regno = reg; + + return a; +} + +struct arth * +gen_neg(a) + struct arth *a; +{ + struct slist *s; + + s = xfer_to_a(a); + sappend(a->s, s); + s = new_stmt(BPF_ALU|BPF_NEG); + s->s.k = 0; + sappend(a->s, s); + s = new_stmt(BPF_ST); + s->s.k = a->regno; + sappend(a->s, s); + + return a; +} + +struct arth * +gen_arth(code, a0, a1) + int code; + struct arth *a0, *a1; +{ + struct slist *s0, *s1, *s2; + + s0 = xfer_to_x(a1); + s1 = xfer_to_a(a0); + s2 = new_stmt(BPF_ALU|BPF_X|code); + + sappend(s1, s2); + sappend(s0, s1); + sappend(a1->s, s0); + sappend(a0->s, a1->s); + + free_reg(a1->regno); + + s0 = new_stmt(BPF_ST); + a0->regno = s0->s.k = alloc_reg(); + sappend(a0->s, s0); + + return a0; +} + +/* + * Here we handle simple allocation of the scratch registers. + * If too many registers are alloc'd, the allocator punts. + */ +static int regused[BPF_MEMWORDS]; +static int curreg; + +/* + * Return the next free register. + */ +static int +alloc_reg() +{ + int n = BPF_MEMWORDS; + + while (--n >= 0) { + if (regused[curreg]) + curreg = (curreg + 1) % BPF_MEMWORDS; + else { + regused[curreg] = 1; + return curreg; + } + } + error("too many registers needed to evaluate expression"); + /* NOTREACHED */ +} + +/* + * Return a register to the table so it can + * be used later. + */ +static void +free_reg(n) + int n; +{ + regused[n] = 0; +} + +static struct block * +gen_len(jmp, n) + int jmp; + int n; +{ + struct slist *s; + struct block *b; + + s = new_stmt(BPF_LD|BPF_LEN); + s->next = new_stmt(BPF_SUB|BPF_IMM); + s->next->s.k = n; + b = new_block(JMP(jmp)); + b->stmts = s; + + return b; +} + +struct block * +gen_greater(n) + int n; +{ + return gen_len(BPF_JGE, n); +} + +struct block * +gen_less(n) + int n; +{ + struct block *b; + + b = gen_len(BPF_JGT, n); + gen_not(b); + + return b; +} + +struct block * +gen_byteop(op, idx, val) + int op; + int idx; + int val; +{ + struct block *b; + struct slist *s; + + switch (op) { + default: + abort(); + + case '=': + return gen_cmp((u_int)idx, BPF_B, (long)val); + + case '<': + b = gen_cmp((u_int)idx, BPF_B, (long)val); + b->s.code = JMP(BPF_JGE); + gen_not(b); + return b; + + case '>': + b = gen_cmp((u_int)idx, BPF_B, (long)val); + b->s.code = JMP(BPF_JGT); + return b; + + case '|': + s = new_stmt(BPF_ALU|BPF_AND|BPF_K); + break; + + case '&': + s = new_stmt(BPF_ALU|BPF_AND|BPF_K); + break; + } + s->s.k = val; + b = new_block(JMP(BPF_JEQ)); + b->stmts = s; + gen_not(b); + + return b; +} + +struct block * +gen_broadcast(proto) + int proto; +{ + u_long hostmask; + struct block *b0, *b1, *b2; + static u_char ebroadcast[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + switch (proto) { + + case Q_DEFAULT: + case Q_LINK: + if (linktype == DLT_EN10MB) + return gen_ehostop(ebroadcast, Q_DST); + error("not a broadcast link"); + break; + + case Q_IP: + b0 = gen_linktype(ETHERTYPE_IP); + hostmask = ~netmask; + b1 = gen_mcmp(off_nl + 16, BPF_W, (long)0, hostmask); + b2 = gen_mcmp(off_nl + 16, BPF_W, + (long)(~0 & hostmask), hostmask); + gen_or(b1, b2); + gen_and(b0, b2); + return b2; + } + error("only ether/ip broadcast filters supported"); +} + +struct block * +gen_multicast(proto) + int proto; +{ + register struct block *b0, *b1, *b2; + register struct slist *s; + + switch (proto) { + + case Q_DEFAULT: + case Q_LINK: + if (linktype != DLT_EN10MB) + break; + + /* ether[0] & 1 != 0 */ + s = new_stmt(BPF_LD|BPF_B|BPF_ABS); + s->s.k = 0; + b0 = new_block(JMP(BPF_JSET)); + b0->s.k = 1; + b0->stmts = s; + return b0; + + case Q_IP: + b0 = gen_linktype(ETHERTYPE_IP); + b1 = gen_cmp(off_nl + 16, BPF_B, (long)224); + b1->s.code = JMP(BPF_JGE); + gen_and(b0, b1); + return b1; + } + error("only ether/ip multicast filters supported"); +} diff --git a/usr.sbin/tcpdump/tcpdump/gencode.h b/usr.sbin/tcpdump/tcpdump/gencode.h new file mode 100644 index 0000000..b8f342d --- /dev/null +++ b/usr.sbin/tcpdump/tcpdump/gencode.h @@ -0,0 +1,156 @@ +/* + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the University of California, + * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of + * the University nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * @(#) $Header: gencode.h,v 1.14 92/02/14 15:18:55 mccanne Exp $ (LBL) + */ + +/* + * filter.h must be included before this file. + */ + +/* Address qualifers. */ + +#define Q_HOST 1 +#define Q_NET 2 +#define Q_PORT 3 +#define Q_GATEWAY 4 +#define Q_PROTO 5 + +/* Protocol qualifiers. */ + +#define Q_LINK 1 +#define Q_IP 2 +#define Q_ARP 3 +#define Q_RARP 4 +#define Q_TCP 5 +#define Q_UDP 6 +#define Q_ICMP 7 + +/* Directional qualifers. */ + +#define Q_SRC 1 +#define Q_DST 2 +#define Q_OR 3 +#define Q_AND 4 + +#define Q_DEFAULT 0 +#define Q_UNDEF 255 + +struct stmt { + int code; + long k; +}; + +struct slist { + struct stmt s; + struct slist *next; +}; + +/* + * A bit vector to represent definition sets. We assume TOT_REGISTERS + * is smaller than 8*sizeof(atomset). + */ +typedef u_long atomset; +#define ATOMMASK(n) (1 << (n)) +#define ATOMELEM(d, n) (d & ATOMMASK(n)) + +/* + * An unbounded set. + */ +typedef u_long *uset; + +/* + * Total number of atomic entities, including accumulator (A) and index (X). + * We treat all these guys similarly during flow analysis. + */ +#define N_ATOMS (BPF_MEMWORDS+2) + +struct edge { + int id; + int code; + uset edom; + struct block *succ; + struct block *pred; + struct edge *next; /* link list of incoming edges for a node */ +}; + +struct block { + int id; + struct slist *stmts; /* side effect stmts */ + struct stmt s; /* branch stmt */ + int mark; + int level; + int offset; + int sense; + struct edge et; + struct edge ef; + struct block *head; + struct block *link; /* link field used by optimizer */ + uset dom; + uset closure; + struct edge *in_edges; + atomset def, kill; + atomset in_use; + atomset out_use; + long oval; + long val[N_ATOMS]; +}; + +struct arth { + struct block *b; /* protocol checks */ + struct slist *s; /* stmt list */ + int regno; /* virtual register number of result */ +}; + +extern struct arth *gen_loadi(); +extern struct arth *gen_load(); +extern struct arth *gen_loadlen(); +extern struct arth *gen_neg(); +extern struct arth *gen_arth(); + +extern void gen_and(); +extern void gen_or(); +extern void gen_not(); + +extern struct block *gen_scode(); +extern struct block *gen_ecode(); +extern struct block *gen_ncode(); +extern struct block *gen_proto_abbrev(); +extern struct block *gen_relation(); +extern struct block *gen_less(); +extern struct block *gen_greater(); +extern struct block *gen_byteop(); +extern struct block *gen_broadcast(); +extern struct block *gen_multicast(); + +extern void optimize(); + +extern void finish_parse(); + +struct qual { + unsigned char addr; + unsigned char proto; + unsigned char dir; + unsigned char pad; +}; + +/* XXX */ +#define JT(b) ((b)->et.succ) +#define JF(b) ((b)->ef.succ) diff --git a/usr.sbin/tcpdump/tcpdump/inet.c b/usr.sbin/tcpdump/tcpdump/inet.c new file mode 100644 index 0000000..550129e --- /dev/null +++ b/usr.sbin/tcpdump/tcpdump/inet.c @@ -0,0 +1,172 @@ +/* + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the University of California, + * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of + * the University nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +static char rcsid[] = + "@(#)$Header: inet.c,v 1.12 92/01/29 12:46:18 mccanne Exp $ (LBL)"; +#endif + +#include <stdio.h> +#include <ctype.h> +#include <strings.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/file.h> +#include <sys/ioctl.h> + +#include <net/if.h> +#include <netinet/in.h> + +#include "interface.h" + +/* Not all systems have IFF_LOOPBACK */ +#ifdef IFF_LOOPBACK +#define ISLOOPBACK(p) ((p)->ifr_flags & IFF_LOOPBACK) +#else +#define ISLOOPBACK(p) (strcmp((p)->ifr_name, "lo0") == 0) +#endif + +/* + * Return the name of a network interface attached to the system, or 0 + * if none can be found. The interface must be configured up; the + * lowest unit number is preferred; loopback is ignored. + */ +char * +lookup_device() +{ + struct ifreq ibuf[16], *ifrp, *ifend, *mp; + struct ifconf ifc; + int fd; + int minunit, n; + char *cp; + static char device[sizeof(ifrp->ifr_name)]; + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + perror("tcpdump: socket"); + exit(1); + } + ifc.ifc_len = sizeof ibuf; + ifc.ifc_buf = (caddr_t)ibuf; + + if (ioctl(fd, SIOCGIFCONF, (char *)&ifc) < 0 || + ifc.ifc_len < sizeof(struct ifreq)) { + perror("tcpdump: SIOCGIFCONF: "); + exit(1); + } + ifrp = ibuf; + ifend = (struct ifreq *)((char *)ibuf + ifc.ifc_len); + + mp = 0; + minunit = 666; + while (ifrp < ifend) { + struct ifreq ifr; + /* + * Need a template to preserve address info that is + * used below to locate the next entry. (Otherwise, + * SIOCGIFFLAGS stomps over it because the requests + * are returned in a union.) + */ + bcopy(ifrp->ifr_name, ifr.ifr_name, sizeof(ifr.ifr_name)); + if (ioctl(fd, SIOCGIFFLAGS, (char *)&ifr) < 0) { + fprintf(stderr, "tcpdump: SIOCGIFFLAGS: "); + perror(ifrp->ifr_name); + exit(1); + } + if ((ifr.ifr_flags & IFF_UP) && !ISLOOPBACK(&ifr)) { + for (cp = ifrp->ifr_name; !isdigit(*cp); ++cp) + ; + n = atoi(cp); + if (n < minunit) { + minunit = n; + mp = ifrp; + } + } +#if BSD >= 199006 + n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name); + if (n < sizeof(*ifrp)) + ++ifrp; + else + ifrp = (struct ifreq *)((char *)ifrp + n); +#else + ++ifrp; +#endif + } + close(fd); + if (mp == 0) + return (0); + + (void)strcpy(device, mp->ifr_name); + return (device); +} + +/* + * Get the netmask of an IP address. This routine is used if + * SIOCGIFNETMASK doesn't work. + */ +static u_long +ipaddrtonetmask(addr) + u_long addr; +{ + if (IN_CLASSA(addr)) + return (IN_CLASSA_NET); + if (IN_CLASSB(addr)) + return (IN_CLASSB_NET); + if (IN_CLASSC(addr)) + return (IN_CLASSC_NET); + error("unknown IP address class: %08X", addr); + /* NOTREACHED */ +} + +void +lookup_net(device, netp, maskp) + char *device; + u_long *netp; + u_long *maskp; +{ + int fd; + struct ifreq ifr; + struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr; + + /* Use data gram socket to get IP address. */ + if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + perror("tcpdump: socket"); + exit(1); + } + (void)strncpy(ifr.ifr_name, device, sizeof ifr.ifr_name); + if (ioctl(fd, SIOCGIFADDR, (char *)&ifr) < 0) { + /* + * This will fail if an IP address hasn't been assigned. + */ + *netp = 0; + *maskp = 0; + return; + } + *netp = sin->sin_addr.s_addr; + if (ioctl(fd, SIOCGIFNETMASK, (char *)&ifr) < 0) + *maskp = 0; + else + *maskp = sin->sin_addr.s_addr; + if (*maskp == 0) + *maskp = ipaddrtonetmask(*netp); + *netp &= *maskp; + (void)close(fd); +} diff --git a/usr.sbin/tcpdump/tcpdump/md.c b/usr.sbin/tcpdump/tcpdump/md.c new file mode 100644 index 0000000..6bb04b7 --- /dev/null +++ b/usr.sbin/tcpdump/tcpdump/md.c @@ -0,0 +1,35 @@ +/* + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the University of California, + * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of + * the University nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +static char rcsid[] = + "@(#) $Header: md-vax.c,v 1.3 90/10/03 14:14:33 mccanne Locked $ (LBL)"; +#endif + +/* Vaxen appear to have clocks accurate to 1 us, + but packetfilter is timestamping to 10 ms. */ + +int +clock_sigfigs() +{ + return 2; +} + diff --git a/usr.sbin/tcpdump/tcpdump/nametoaddr.c b/usr.sbin/tcpdump/tcpdump/nametoaddr.c new file mode 100644 index 0000000..6f2330f --- /dev/null +++ b/usr.sbin/tcpdump/tcpdump/nametoaddr.c @@ -0,0 +1,258 @@ +/* + * Copyright (c) 1988-1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the University of California, + * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of + * the University nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Name to id translation routines used by the scanner. + * These functions are not time critical. + */ + +#ifndef lint +static char rcsid[] = + "@(#) $Header: nametoaddr.c,v 1.9 91/02/04 16:56:46 mccanne Exp $ (LBL)"; +#endif + +#include <stdio.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <net/if.h> +#include <netdb.h> +#include <netinet/in.h> +#include <netinet/if_ether.h> +#include <errno.h> +#include <arpa/inet.h> + +#ifdef ultrix +#include <sys/time.h> +#include <rpc/types.h> +#include <nfs/nfs.h> +#endif + +#include "interface.h" +#include "etherent.h" +#include "nametoaddr.h" + +/* + * Convert host name to internet address. + * Return 0 upon failure. + */ +u_long ** +s_nametoaddr(name) + char *name; +{ +#ifndef h_addr + static u_long *hlist[2]; +#endif + u_long **p; + struct hostent *hp; + + if (hp = gethostbyname(name)) { +#ifndef h_addr + hlist[0] = (u_long *)hp->h_addr; + NTOHL(hp->h_addr); + return hlist; +#else + for (p = (u_long **)hp->h_addr_list; *p; ++p) + NTOHL(**p); + return (u_long **)hp->h_addr_list; +#endif + } + else + return 0; +} + +/* + * Convert net name to internet address. + * Return 0 upon failure. + */ +u_long +s_nametonetaddr(name) + char *name; +{ + struct netent *np; + + if (np = getnetbyname(name)) + return np->n_net; + else + return 0; +} + +/* + * Convert a port name to its port and protocol numbers. + * We assume only TCP or UDP. + * Return 0 upon failure. + */ +s_nametoport(name, port, proto) + char *name; + int *port; + int *proto; +{ + struct servent *sp; + char *other; + + sp = getservbyname(name, (char *)0); + if (sp != 0) { + NTOHS(sp->s_port); + *port = sp->s_port; + *proto = s_nametoproto(sp->s_proto); + /* + * We need to check /etc/services for ambiguous entries. + * If we find the ambiguous entry, and it has the + * same port number, change the proto to PROTO_UNDEF + * so both TCP and UDP will be checked. + */ + if (*proto == IPPROTO_TCP) + other = "udp"; + else + other = "tcp"; + + sp = getservbyname(name, other); + if (sp != 0) { + NTOHS(sp->s_port); + if (*port != sp->s_port) + /* Can't handle ambigous names that refer + to different port numbers. */ + warning("ambiguous port %s in /etc/services", + name); + *proto = PROTO_UNDEF; + } + return 1; + } +#ifdef ultrix + /* Special hack in case NFS isn't in /etc/services */ + if (strcmp(name, "nfs") == 0) { + *port = NFS_PORT; + *proto = PROTO_UNDEF; + return 1; + } +#endif + return 0; +} + +int +s_nametoproto(str) + char *str; +{ + struct protoent *p; + + p = getprotobyname(str); + if (p != 0) + return p->p_proto; + else + return PROTO_UNDEF; +} + +#include "etherproto.h" + +int +s_nametoeproto(s) + char *s; +{ + struct eproto *p = eproto_db; + + while (p->s != 0) { + if (strcmp(p->s, s) == 0) + return p->p; + p += 1; + } + return PROTO_UNDEF; +} + +/* Hex digit to integer. */ +static inline int +xdtoi(c) +{ + if (isdigit(c)) + return c - '0'; + else if (islower(c)) + return c - 'a' + 10; + else + return c - 'A' + 10; +} + +u_long +atoin(s) + char *s; +{ + u_long addr = 0; + u_int n; + + while (1) { + n = 0; + while (*s && *s != '.') + n = n * 10 + *s++ - '0'; + addr <<= 8; + addr |= n & 0xff; + if (*s == '\0') + return addr; + ++s; + } + /* NOTREACHED */ +} + + +/* + * Convert 's' which has the form "xx:xx:xx:xx:xx:xx" into a new + * ethernet address. Assumes 's' is well formed. + */ +u_char * +ETHER_aton(s) + char *s; +{ + register u_char *ep, *e; + register u_int d; + + e = ep = (u_char *)malloc(6); + + while (*s) { + if (*s == ':') + s += 1; + d = xdtoi(*s++); + if (isxdigit(*s)) { + d <<= 4; + d |= xdtoi(*s++); + } + *ep++ = d; + } + + return e; +} + +#ifndef ETHER_SERVICE +u_char * +ETHER_hostton(name) + char *name; +{ + struct etherent *ep; + FILE *fp; + u_char *ap; + + fp = fopen(ETHERS_FILE, "r"); + if (fp != 0) { + while (ep = next_etherent(fp)) { + if (strcmp(ep->name, name) == 0) { + ap = (u_char *)malloc(6); + bcopy(ep->addr, ap, 6); + return ap; + } + } + } + return (u_char *)0; +} +#endif diff --git a/usr.sbin/tcpdump/tcpdump/nametoaddr.h b/usr.sbin/tcpdump/tcpdump/nametoaddr.h new file mode 100644 index 0000000..23da3f2 --- /dev/null +++ b/usr.sbin/tcpdump/tcpdump/nametoaddr.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 1988-1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the University of California, + * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of + * the University nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * @(#) $Header: nametoaddr.h,v 1.6 90/09/24 12:50:41 mccanne Exp $ (LBL) + * + * Address to name translation routines. + */ + +extern u_long **s_nametoaddr(); +extern u_long s_nametonetaddr(); + +extern int s_nametoport(); +extern int s_nametoproto(); +extern int s_nametoeproto(); + +extern u_char *ETHER_hostton(); +extern u_char *ETHER_aton(); + +/* + * If a protocol is unknown, PROTO_UNDEF is returned. + * Also, s_nametoport() returns the protocol along with the port number. + * If there are ambiguous entried in /etc/services (i.e. domain + * can be either tcp or udp) PROTO_UNDEF is returned. + */ +#define PROTO_UNDEF -1 + diff --git a/usr.sbin/tcpdump/tcpdump/optimize.c b/usr.sbin/tcpdump/tcpdump/optimize.c new file mode 100644 index 0000000..5064011 --- /dev/null +++ b/usr.sbin/tcpdump/tcpdump/optimize.c @@ -0,0 +1,1871 @@ +/* + * Copyright (c) 1988-1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the University of California, + * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of + * the University nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Optimization module for tcpdump intermediate representation. + */ +#ifndef lint +static char rcsid[] = + "@(#) $Header: optimize.c,v 1.35 91/07/18 09:27:55 mccanne Exp $ (LBL)"; +#endif + +#include <stdio.h> +#include <sys/types.h> + +#include <sys/time.h> +#include <net/bpf.h> + +#include "interface.h" +#include "gencode.h" + +#define A_ATOM BPF_MEMWORDS +#define X_ATOM (BPF_MEMWORDS+1) + +#define NOP -1 + +/* + * This define is used to represent *both* the accumulator and + * x register in use-def computations. + * Currently, the use-def code assumes only one definition per instruction. + */ +#define AX_ATOM N_ATOMS + +/* + * A flag to indicate that further optimization is needed. + * Iterative passes are continued until a given pass yields no + * branch movement. + */ +static int done; + +/* + * A block is marked if only if its mark equals the current mark. + * Rather than traverse the code array, marking each item, 'cur_mark' is + * incremented. This automatically makes each element unmarked. + */ +static int cur_mark; +#define isMarked(p) ((p)->mark == cur_mark) +#define unMarkAll() cur_mark += 1 +#define Mark(p) ((p)->mark = cur_mark) + +static void opt_init(); +static void opt_cleanup(); + +static void make_marks(); +static void mark_code(); + +static void intern_blocks(); + +static int eq_slist(); + +static int n_blocks; +struct block **blocks; +static int n_edges; +struct edge **edges; + +/* + * A bit vector set representation of the dominators. + * We round up the set size to the next power of two. + */ +static int nodewords; +static int edgewords; +struct block **levels; +u_long *space; +#define BITS_PER_WORD (8*sizeof(u_long)) +/* + * True if a is in uset {p} + */ +#define SET_MEMBER(p, a) \ +((p)[(unsigned)(a) / BITS_PER_WORD] & (1 << ((unsigned)(a) % BITS_PER_WORD))) + +/* + * Add 'a' to uset p. + */ +#define SET_INSERT(p, a) \ +(p)[(unsigned)(a) / BITS_PER_WORD] |= (1 << ((unsigned)(a) % BITS_PER_WORD)) + +/* + * Delete 'a' from uset p. + */ +#define SET_DELETE(p, a) \ +(p)[(unsigned)(a) / BITS_PER_WORD] &= ~(1 << ((unsigned)(a) % BITS_PER_WORD)) + +/* + * a := a intersect b + */ +#define SET_INTERSECT(a, b, n)\ +{\ + register u_long *_x = a, *_y = b;\ + register int _n = n;\ + while (--_n >= 0) *_x++ &= *_y++;\ +} + +/* + * a := a - b + */ +#define SET_SUBTRACT(a, b, n)\ +{\ + register u_long *_x = a, *_y = b;\ + register int _n = n;\ + while (--_n >= 0) *_x++ &=~ *_y++;\ +} + +/* + * a := a union b + */ +#define SET_UNION(a, b, n)\ +{\ + register u_long *_x = a, *_y = b;\ + register int _n = n;\ + while (--_n >= 0) *_x++ |= *_y++;\ +} + +static uset all_dom_sets; +static uset all_closure_sets; +static uset all_edge_sets; + +#ifndef MAX +#define MAX(a,b) ((a)>(b)?(a):(b)) +#endif + +static void +find_levels_r(b) + struct block *b; +{ + int level; + + if (isMarked(b)) + return; + + Mark(b); + b->link = 0; + + if (JT(b)) { + find_levels_r(JT(b)); + find_levels_r(JF(b)); + level = MAX(JT(b)->level, JF(b)->level) + 1; + } else + level = 0; + b->level = level; + b->link = levels[level]; + levels[level] = b; +} + +/* + * Level graph. The levels go from 0 at the leaves to + * N_LEVELS at the root. The levels[] array points to the + * first node of the level list, whose elements are linked + * with the 'link' field of the struct block. + */ +static void +find_levels(root) + struct block *root; +{ + bzero((char *)levels, n_blocks * sizeof(*levels)); + unMarkAll(); + find_levels_r(root); +} + +/* + * Find dominator relationships. + * Assumes graph has been leveled. + */ +static void +find_dom(root) + struct block *root; +{ + int i; + struct block *b; + u_long *x; + + /* + * Initialize sets to contain all nodes. + */ + x = all_dom_sets; + i = n_blocks * nodewords; + while (--i >= 0) + *x++ = ~0; + /* Root starts off empty. */ + for (i = nodewords; --i >= 0;) + root->dom[i] = 0; + + /* root->level is the highest level no found. */ + for (i = root->level; i >= 0; --i) { + for (b = levels[i]; b; b = b->link) { + SET_INSERT(b->dom, b->id); + if (JT(b) == 0) + continue; + SET_INTERSECT(JT(b)->dom, b->dom, nodewords); + SET_INTERSECT(JF(b)->dom, b->dom, nodewords); + } + } +} + +static void +propedom(ep) + struct edge *ep; +{ + SET_INSERT(ep->edom, ep->id); + if (ep->succ) { + SET_INTERSECT(ep->succ->et.edom, ep->edom, edgewords); + SET_INTERSECT(ep->succ->ef.edom, ep->edom, edgewords); + } +} + +/* + * Compute edge dominators. + * Assumes graph has been leveled and predecessors estabished. + */ +static void +find_edom(root) + struct block *root; +{ + int i; + uset x; + struct block *b; + + x = all_edge_sets; + for (i = n_edges * edgewords; --i >= 0; ) + x[i] = ~0; + + /* root->level is the highest level no found. */ + bzero(root->et.edom, edgewords * sizeof(*(uset)0)); + bzero(root->ef.edom, edgewords * sizeof(*(uset)0)); + for (i = root->level; i >= 0; --i) { + for (b = levels[i]; b != 0; b = b->link) { + propedom(&b->et); + propedom(&b->ef); + } + } +} + +/* + * Find the backwards transitive closure of the flow graph. These sets + * are backwards in the sense that we find the set of nodes that reach + * a given node, not the set of nodes that can be reached by a node. + * + * Assumes graph has been leveled. + */ +static void +find_closure(root) + struct block *root; +{ + int i; + struct block *b; + + /* + * Initialize sets to contain no nodes. + */ + bzero((char *)all_closure_sets, + n_blocks * nodewords * sizeof(*all_closure_sets)); + + /* root->level is the highest level no found. */ + for (i = root->level; i >= 0; --i) { + for (b = levels[i]; b; b = b->link) { + SET_INSERT(b->closure, b->id); + if (JT(b) == 0) + continue; + SET_UNION(JT(b)->closure, b->closure, nodewords); + SET_UNION(JF(b)->closure, b->closure, nodewords); + } + } +} + +/* + * Return the register number that is used by s. If A and X are both + * used, return AX_ATOM. If no register is used, return -1. + * + * The implementation should probably change to an array access. + */ +static int +atomuse(s) + struct stmt *s; +{ + register int c = s->code; + + if (c == NOP) + return -1; + + switch (BPF_CLASS(c)) { + + case BPF_RET: + return (BPF_RVAL(c) == BPF_A) ? A_ATOM : + (BPF_RVAL(c) == BPF_X) ? X_ATOM : -1; + + case BPF_LD: + case BPF_LDX: + return (BPF_MODE(c) == BPF_IND) ? X_ATOM : + (BPF_MODE(c) == BPF_MEM) ? s->k : -1; + + case BPF_ST: + return A_ATOM; + + case BPF_STX: + return X_ATOM; + + case BPF_JMP: + case BPF_ALU: + if (BPF_SRC(c) == BPF_X) + return AX_ATOM; + return A_ATOM; + + case BPF_MISC: + return BPF_MISCOP(c) == BPF_TXA ? X_ATOM : A_ATOM; + } + abort(); + /* NOTREACHED */ +} + +/* + * Return the register number that is defined by 's'. We assume that + * a single stmt cannot define more than one register. If no register + * is defined, return -1. + * + * The implementation should probably change to an array access. + */ +static int +atomdef(s) + struct stmt *s; +{ + if (s->code == NOP) + return -1; + + switch (BPF_CLASS(s->code)) { + + case BPF_LD: + case BPF_ALU: + return A_ATOM; + + case BPF_LDX: + return X_ATOM; + + case BPF_ST: + case BPF_STX: + return s->k; + + case BPF_MISC: + return BPF_MISCOP(s->code) == BPF_TAX ? X_ATOM : A_ATOM; + } + return -1; +} + +static void +compute_local_ud(b) + struct block *b; +{ + struct slist *s; + atomset def = 0, use = 0, kill = 0; + int atom; + + for (s = b->stmts; s; s = s->next) { + if (s->s.code == NOP) + continue; + atom = atomuse(&s->s); + if (atom >= 0) { + if (atom == AX_ATOM) { + if (!ATOMELEM(def, X_ATOM)) + use |= ATOMMASK(X_ATOM); + if (!ATOMELEM(def, A_ATOM)) + use |= ATOMMASK(A_ATOM); + } + else if (atom < N_ATOMS) { + if (!ATOMELEM(def, atom)) + use |= ATOMMASK(atom); + } + else + abort(); + } + atom = atomdef(&s->s); + if (atom >= 0) { + if (!ATOMELEM(atom, use)) + kill |= ATOMMASK(atom); + def |= ATOMMASK(atom); + } + } + if (!ATOMELEM(def, A_ATOM) && BPF_CLASS(b->s.code) == BPF_JMP) + use |= ATOMMASK(A_ATOM); + + b->def = def; + b->kill = kill; + b->in_use = use; +} + +/* + * Assume graph is already leveled. + */ +static void +find_ud(root) + struct block *root; +{ + int i, maxlevel; + struct block *p; + + /* + * root->level is the highest level no found; + * count down from there. + */ + maxlevel = root->level; + for (i = maxlevel; i >= 0; --i) + for (p = levels[i]; p; p = p->link) { + compute_local_ud(p); + p->out_use = 0; + } + + for (i = 1; i <= maxlevel; ++i) { + for (p = levels[i]; p; p = p->link) { + p->out_use |= JT(p)->in_use | JF(p)->in_use; + p->in_use |= p->out_use &~ p->kill; + } + } +} + +/* + * These data structures are used in a Cocke and Shwarz style + * value numbering scheme. Since the flowgraph is acyclic, + * exit values can be propagated from a node's predecessors + * provided it is uniquely defined. + */ +struct valnode { + int code; + long v0, v1; + long val; + struct valnode *next; +}; + +#define MODULUS 213 +static struct valnode *hashtbl[MODULUS]; +static int curval; +static int maxval; + +/* Integer constants mapped with the load immediate opcode. */ +#define K(i) F(BPF_LD|BPF_IMM|BPF_W, i, 0L) + +struct vmapinfo { + int is_const; + long const_val; +}; + +struct vmapinfo *vmap; +struct valnode *vnode_base; +struct valnode *next_vnode; + +static void +init_val() +{ + curval = 0; + next_vnode = vnode_base; + bzero((char *)vmap, maxval * sizeof(*vmap)); + bzero((char *)hashtbl, sizeof hashtbl); +} + +/* Because we really don't have an IR, this stuff is a little messy. */ +static long +F(code, v0, v1) + int code; + long v0, v1; +{ + u_int hash; + int val; + struct valnode *p; + + hash = (u_int)code ^ (v0 << 4) ^ (v1 << 8); + hash %= MODULUS; + + for (p = hashtbl[hash]; p; p = p->next) + if (p->code == code && p->v0 == v0 && p->v1 == v1) + return p->val; + + val = ++curval; + if (BPF_MODE(code) == BPF_IMM && + (BPF_CLASS(code) == BPF_LD || BPF_CLASS(code) == BPF_LDX)) { + vmap[val].const_val = v0; + vmap[val].is_const = 1; + } + p = next_vnode++; + p->val = val; + p->code = code; + p->v0 = v0; + p->v1 = v1; + p->next = hashtbl[hash]; + hashtbl[hash] = p; + + return val; +} + +static inline void +vstore(s, valp, newval, alter) + struct stmt *s; + long *valp; + long newval; + int alter; +{ + if (alter && *valp == newval) + s->code = NOP; + else + *valp = newval; +} + +static void +fold_op(s, v0, v1) + struct stmt *s; + long v0, v1; +{ + long a, b; + + a = vmap[v0].const_val; + b = vmap[v1].const_val; + + switch (BPF_OP(s->code)) { + case BPF_ADD: + a += b; + break; + + case BPF_SUB: + a -= b; + break; + + case BPF_MUL: + a *= b; + break; + + case BPF_DIV: + if (b == 0) + error("division by zero"); + a /= b; + break; + + case BPF_AND: + a &= b; + break; + + case BPF_OR: + a |= b; + break; + + case BPF_LSH: + a <<= b; + break; + + case BPF_RSH: + a >>= b; + break; + + case BPF_NEG: + a = -a; + break; + + default: + abort(); + } + s->k = a; + s->code = BPF_LD|BPF_IMM; + done = 0; +} + +static inline struct slist * +this_op(s) + struct slist *s; +{ + while (s != 0 && s->s.code == NOP) + s = s->next; + return s; +} + +static void +opt_not(b) + struct block *b; +{ + struct block *tmp = JT(b); + + JT(b) = JF(b); + JF(b) = tmp; +} + +static void +opt_peep(b) + struct block *b; +{ + struct slist *s; + struct slist *next, *last; + int val; + long v; + + s = b->stmts; + if (s == 0) + return; + + last = s; + while (1) { + s = this_op(s); + if (s == 0) + break; + next = this_op(s->next); + if (next == 0) + break; + last = next; + + /* + * st M[k] --> st M[k] + * ldx M[k] tax + */ + if (s->s.code == BPF_ST && + next->s.code == (BPF_LDX|BPF_MEM) && + s->s.k == next->s.k) { + done = 0; + next->s.code = BPF_MISC|BPF_TAX; + } + /* + * ld #k --> ldx #k + * tax txa + */ + if (s->s.code == (BPF_LD|BPF_IMM) && + next->s.code == (BPF_MISC|BPF_TAX)) { + s->s.code = BPF_LDX|BPF_IMM; + next->s.code = BPF_MISC|BPF_TXA; + done = 0; + } + /* + * This is an ugly special case, but it happens + * when you say tcp[k] or udp[k] where k is a constant. + */ + if (s->s.code == (BPF_LD|BPF_IMM)) { + struct slist *add, *tax, *ild; + + /* + * Check that X isn't used on exit from this + * block (which the optimizer might cause). + * We know the code generator won't generate + * any local dependencies. + */ + if (ATOMELEM(b->out_use, X_ATOM)) + break; + + if (next->s.code != (BPF_LDX|BPF_MSH|BPF_B)) + add = next; + else + add = this_op(next->next); + if (add == 0 || add->s.code != (BPF_ALU|BPF_ADD|BPF_X)) + break; + + tax = this_op(add->next); + if (tax == 0 || tax->s.code != (BPF_MISC|BPF_TAX)) + break; + + ild = this_op(tax->next); + if (ild == 0 || BPF_CLASS(ild->s.code) != BPF_LD || + BPF_MODE(ild->s.code) != BPF_IND) + break; + /* + * XXX We need to check that X is not + * subsequently used. We know we can eliminate the + * accumulator modifications since it is defined + * by the last stmt of this sequence. + * + * We want to turn this sequence: + * + * (004) ldi #0x2 {s} + * (005) ldxms [14] {next} -- optional + * (006) addx {add} + * (007) tax {tax} + * (008) ild [x+0] {ild} + * + * into this sequence: + * + * (004) nop + * (005) ldxms [14] + * (006) nop + * (007) nop + * (008) ild [x+2] + * + */ + ild->s.k += s->s.k; + s->s.code = NOP; + add->s.code = NOP; + tax->s.code = NOP; + done = 0; + } + s = next; + } + /* + * If we have a subtract to do a comparsion, and the X register + * is a known constant, we can merge this value into the + * comparison. + */ + if (last->s.code == (BPF_ALU|BPF_SUB|BPF_X) && + !ATOMELEM(b->out_use, A_ATOM)) { + val = b->val[X_ATOM]; + if (vmap[val].is_const) { + b->s.k += vmap[val].const_val; + last->s.code = NOP; + done = 0; + } else if (b->s.k == 0) { + /* + * sub x -> nop + * j #0 j x + */ + last->s.code = NOP; + b->s.code = BPF_CLASS(b->s.code) | BPF_OP(b->s.code) | + BPF_X; + done = 0; + } + } + /* + * Likewise, a constant subtract can be simplified. + */ + else if (last->s.code == (BPF_ALU|BPF_SUB|BPF_K) && + !ATOMELEM(b->out_use, A_ATOM)) { + b->s.k += last->s.k; + last->s.code = NOP; + done = 0; + } + /* + * and #k nop + * jeq #0 -> jset #k + */ + if (last->s.code == (BPF_ALU|BPF_AND|BPF_K) && + !ATOMELEM(b->out_use, A_ATOM) && b->s.k == 0) { + b->s.k = last->s.k; + b->s.code = BPF_JMP|BPF_K|BPF_JSET; + last->s.code = NOP; + done = 0; + opt_not(b); + } + /* + * If the accumulator is a known constant, we can compute the + * comparison result. + */ + val = b->val[A_ATOM]; + if (vmap[val].is_const && BPF_SRC(b->s.code) == BPF_K) { + v = vmap[val].const_val; + switch (BPF_OP(b->s.code)) { + + case BPF_JEQ: + v = v == b->s.k; + break; + + case BPF_JGT: + v = v > b->s.k; + break; + + case BPF_JGE: + v = v >= b->s.k; + break; + + case BPF_JSET: + v &= b->s.k; + break; + + default: + abort(); + } + if (JF(b) != JT(b)) + done = 0; + if (v) + JF(b) = JT(b); + else + JT(b) = JF(b); + } +} + +/* + * Compute the symbolic value of expression of 's', and update + * anything it defines in the value table 'val'. If 'alter' is true, + * do various optimizations. This code would be cleaner if symblic + * evaluation and code transformations weren't folded together. + */ +static void +opt_stmt(s, val, alter) + struct stmt *s; + long val[]; + int alter; +{ + int op; + long v; + + switch (s->code) { + + case BPF_LD|BPF_ABS|BPF_W: + case BPF_LD|BPF_ABS|BPF_H: + case BPF_LD|BPF_ABS|BPF_B: + v = F(s->code, s->k, 0L); + vstore(s, &val[A_ATOM], v, alter); + break; + + case BPF_LD|BPF_IND|BPF_W: + case BPF_LD|BPF_IND|BPF_H: + case BPF_LD|BPF_IND|BPF_B: + v = val[X_ATOM]; + if (alter && vmap[v].is_const) { + s->code = BPF_LD|BPF_ABS|BPF_SIZE(s->code); + s->k += vmap[v].const_val; + v = F(s->code, s->k, 0L); + done = 0; + } + else + v = F(s->code, s->k, v); + vstore(s, &val[A_ATOM], v, alter); + break; + + case BPF_LD|BPF_LEN: + v = F(s->code, 0L, 0L); + vstore(s, &val[A_ATOM], v, alter); + break; + + case BPF_LD|BPF_IMM: + v = K(s->k); + vstore(s, &val[A_ATOM], v, alter); + break; + + case BPF_LDX|BPF_IMM: + v = K(s->k); + vstore(s, &val[X_ATOM], v, alter); + break; + + case BPF_LDX|BPF_MSH|BPF_B: + v = F(s->code, s->k, 0L); + vstore(s, &val[X_ATOM], v, alter); + break; + + case BPF_ALU|BPF_NEG: + if (alter && vmap[val[A_ATOM]].is_const) { + s->code = BPF_LD|BPF_IMM; + s->k = -vmap[val[A_ATOM]].const_val; + val[A_ATOM] = K(s->k); + } + else + val[A_ATOM] = F(s->code, val[A_ATOM], 0L); + break; + + case BPF_ALU|BPF_ADD|BPF_K: + case BPF_ALU|BPF_SUB|BPF_K: + case BPF_ALU|BPF_MUL|BPF_K: + case BPF_ALU|BPF_DIV|BPF_K: + case BPF_ALU|BPF_AND|BPF_K: + case BPF_ALU|BPF_OR|BPF_K: + case BPF_ALU|BPF_LSH|BPF_K: + case BPF_ALU|BPF_RSH|BPF_K: + op = BPF_OP(s->code); + if (alter) { + if (s->k == 0) { + if (op == BPF_ADD || op == BPF_SUB || + op == BPF_LSH || op == BPF_RSH || + op == BPF_OR) { + s->code = NOP; + break; + } + if (op == BPF_MUL || op == BPF_AND) { + s->code = BPF_LD|BPF_IMM; + val[A_ATOM] = K(s->k); + break; + } + } + if (vmap[val[A_ATOM]].is_const) { + fold_op(s, val[A_ATOM], K(s->k)); + val[A_ATOM] = K(s->k); + break; + } + } + val[A_ATOM] = F(s->code, val[A_ATOM], K(s->k)); + break; + + case BPF_ALU|BPF_ADD|BPF_X: + case BPF_ALU|BPF_SUB|BPF_X: + case BPF_ALU|BPF_MUL|BPF_X: + case BPF_ALU|BPF_DIV|BPF_X: + case BPF_ALU|BPF_AND|BPF_X: + case BPF_ALU|BPF_OR|BPF_X: + case BPF_ALU|BPF_LSH|BPF_X: + case BPF_ALU|BPF_RSH|BPF_X: + op = BPF_OP(s->code); + if (alter && vmap[val[X_ATOM]].is_const) { + if (vmap[val[A_ATOM]].is_const) { + fold_op(s, val[A_ATOM], val[X_ATOM]); + val[A_ATOM] = K(s->k); + } + else { + s->code = BPF_ALU|BPF_K|op; + s->k = vmap[val[X_ATOM]].const_val; + done = 0; + val[A_ATOM] = + F(s->code, val[A_ATOM], K(s->k)); + } + break; + } + /* + * Check if we're doing something to an accumulator + * that is 0, and simplify. This may not seem like + * much of a simplification but it could open up further + * optimizations. + * XXX We could also check for mul by 1, and -1, etc. + */ + if (alter && vmap[val[A_ATOM]].is_const + && vmap[val[A_ATOM]].const_val == 0) { + if (op == BPF_ADD || op == BPF_OR || + op == BPF_LSH || op == BPF_RSH || op == BPF_SUB) { + s->code = BPF_MISC|BPF_TXA; + vstore(s, &val[A_ATOM], val[X_ATOM], alter); + break; + } + else if (op == BPF_MUL || op == BPF_DIV || + op == BPF_AND) { + s->code = BPF_LD|BPF_IMM; + s->k = 0; + vstore(s, &val[A_ATOM], K(s->k), alter); + break; + } + else if (op == BPF_NEG) { + s->code = NOP; + break; + } + } + val[A_ATOM] = F(s->code, val[A_ATOM], val[X_ATOM]); + break; + + case BPF_MISC|BPF_TXA: + vstore(s, &val[A_ATOM], val[X_ATOM], alter); + break; + + case BPF_LD|BPF_MEM: + v = val[s->k]; + if (alter && vmap[v].is_const) { + s->code = BPF_LD|BPF_IMM; + s->k = vmap[v].const_val; + done = 0; + } + vstore(s, &val[A_ATOM], v, alter); + break; + + case BPF_MISC|BPF_TAX: + vstore(s, &val[X_ATOM], val[A_ATOM], alter); + break; + + case BPF_LDX|BPF_MEM: + v = val[s->k]; + if (alter && vmap[v].is_const) { + s->code = BPF_LDX|BPF_IMM; + s->k = vmap[v].const_val; + done = 0; + } + vstore(s, &val[X_ATOM], v, alter); + break; + + case BPF_ST: + vstore(s, &val[s->k], val[A_ATOM], alter); + break; + + case BPF_STX: + vstore(s, &val[s->k], val[X_ATOM], alter); + break; + } +} + +static void +deadstmt(s, last) + register struct stmt *s; + register struct stmt *last[]; +{ + register int atom; + + atom = atomuse(s); + if (atom >= 0) { + if (atom == AX_ATOM) { + last[X_ATOM] = 0; + last[A_ATOM] = 0; + } + else + last[atom] = 0; + } + atom = atomdef(s); + if (atom >= 0) { + if (last[atom]) { + done = 0; + last[atom]->code = NOP; + } + last[atom] = s; + } +} + +static void +opt_deadstores(b) + register struct block *b; +{ + register struct slist *s; + register int atom; + struct stmt *last[N_ATOMS]; + + bzero((char *)last, sizeof last); + + for (s = b->stmts; s != 0; s = s->next) + deadstmt(&s->s, last); + deadstmt(&b->s, last); + + for (atom = 0; atom < N_ATOMS; ++atom) + if (last[atom] && !ATOMELEM(b->out_use, atom)) { + last[atom]->code = NOP; + done = 0; + } +} + +static void +opt_blk(b, do_stmts) + struct block *b; +{ + struct slist *s; + struct edge *p; + int i; + long aval; + + /* + * Initialize the atom values. + * If we have no predecessors, everything is undefined. + * Otherwise, we inherent our values from our predecessors. + * If any register has an ambiguous value (i.e. control paths are + * merging) give it the undefined value of 0. + */ + p = b->in_edges; + if (p == 0) + bzero((char *)b->val, sizeof(b->val)); + else { + bcopy((char *)p->pred->val, (char *)b->val, sizeof(b->val)); + while (p = p->next) { + for (i = 0; i < N_ATOMS; ++i) + if (b->val[i] != p->pred->val[i]) + b->val[i] = 0; + } + } + aval = b->val[A_ATOM]; + for (s = b->stmts; s; s = s->next) + opt_stmt(&s->s, b->val, do_stmts); + + /* + * This is a special case: if we don't use anything from this + * block, and we load the accumulator with value that is + * already there, eliminate all the statements. + */ + if (do_stmts && b->out_use == 0 && aval != 0 && + b->val[A_ATOM] == aval) + b->stmts = 0; + else { + opt_peep(b); + opt_deadstores(b); + } + /* + * Set up values for branch optimizer. + */ + if (BPF_SRC(b->s.code) == BPF_K) + b->oval = K(b->s.k); + else + b->oval = b->val[X_ATOM]; + b->et.code = b->s.code; + b->ef.code = -b->s.code; +} + +/* + * Return true if any register that is used on exit from 'succ', has + * an exit value that is different from the corresponding exit value + * from 'b'. + */ +static int +use_conflict(b, succ) + struct block *b, *succ; +{ + int atom; + atomset use = succ->out_use; + + if (use == 0) + return 0; + + for (atom = 0; atom < N_ATOMS; ++atom) + if (ATOMELEM(use, atom)) + if (b->val[atom] != succ->val[atom]) + return 1; + return 0; +} + +struct block * +fold_edge(child, ep) + struct block *child; + struct edge *ep; +{ + int sense; + int aval0, aval1, oval0, oval1; + int code = ep->code; + + if (code < 0) { + code = -code; + sense = 0; + } else + sense = 1; + + if (child->s.code != code) + return 0; + + aval0 = child->val[A_ATOM]; + oval0 = child->oval; + aval1 = ep->pred->val[A_ATOM]; + oval1 = ep->pred->oval; + + if (aval0 != aval1) + return 0; + + if (oval0 == oval1) + /* + * The operands are identical, so the + * result is true if a true branch was + * taken to get here, otherwise false. + */ + return sense ? JT(child) : JF(child); + + if (sense && code == (BPF_JMP|BPF_JEQ|BPF_K)) + /* + * At this point, we only know the comparison if we + * came down the true branch, and it was an equility + * comparison with a constant. We rely on the fact that + * distinct constants have distinct value numbers. + */ + return JF(child); + + return 0; +} + +static void +opt_j(ep) + struct edge *ep; +{ + register int i, k; + register struct block *target; + + if (JT(ep->succ) == 0) + return; + + if (JT(ep->succ) == JF(ep->succ)) { + /* + * Common branch targets can be eliminated, provided + * there is no data dependency. + */ + if (!use_conflict(ep->pred, ep->succ->et.succ)) { + done = 0; + ep->succ = JT(ep->succ); + } + } + /* + * For each edge dominator that matches the successor of this + * edge, promote the edge succesor to the its grandchild. + * + * XXX We violate the set abstraction here in favor a reasonbly + * efficient loop. + */ + top: + for (i = 0; i < edgewords; ++i) { + register u_long x = ep->edom[i]; + + while (x != 0) { + k = ffs(x) - 1; + x &=~ 1 << k; + k += i * BITS_PER_WORD; + + target = fold_edge(ep->succ, edges[k]); + /* + * Check that there is no data dependency between + * nodes that will be violated if we move the edge. + */ + if (target != 0 && !use_conflict(ep->pred, target)) { + done = 0; + ep->succ = target; + if (JT(target) != 0) + /* + * Start over unless we hit a leaf. + */ + goto top; + return; + } + } + } +} + + +static void +or_pullup(b) + struct block *b; +{ + int val, at_top; + struct block *pull; + struct block **diffp, **samep; + struct edge *ep; + + ep = b->in_edges; + if (ep == 0) + return; + + /* + * Make sure each predecessor loads the same value. + * XXX why? + */ + val = ep->pred->val[A_ATOM]; + for (ep = ep->next; ep != 0; ep = ep->next) + if (val != ep->pred->val[A_ATOM]) + return; + + if (JT(b->in_edges->pred) == b) + diffp = &JT(b->in_edges->pred); + else + diffp = &JF(b->in_edges->pred); + + at_top = 1; + while (1) { + if (*diffp == 0) + return; + + if (JT(*diffp) != JT(b)) + return; + + if (!SET_MEMBER((*diffp)->dom, b->id)) + return; + + if ((*diffp)->val[A_ATOM] != val) + break; + + diffp = &JF(*diffp); + at_top = 0; + } + samep = &JF(*diffp); + while (1) { + if (*samep == 0) + return; + + if (JT(*samep) != JT(b)) + return; + + if (!SET_MEMBER((*samep)->dom, b->id)) + return; + + if ((*samep)->val[A_ATOM] == val) + break; + + /* XXX Need to check that there are no data dependencies + between dp0 and dp1. Currently, the code generator + will not produce such dependencies. */ + samep = &JF(*samep); + } +#ifdef notdef + /* XXX This doesn't cover everything. */ + for (i = 0; i < N_ATOMS; ++i) + if ((*samep)->val[i] != pred->val[i]) + return; +#endif + /* Pull up the node. */ + pull = *samep; + *samep = JF(pull); + JF(pull) = *diffp; + + /* + * At the top of the chain, each predecessor needs to point at the + * pulled up node. Inside the chain, there is only one predecessor + * to worry about. + */ + if (at_top) { + for (ep = b->in_edges; ep != 0; ep = ep->next) { + if (JT(ep->pred) == b) + JT(ep->pred) = pull; + else + JF(ep->pred) = pull; + } + } + else + *diffp = pull; + + done = 0; +} + +static void +and_pullup(b) + struct block *b; +{ + int val, at_top; + struct block *pull; + struct block **diffp, **samep; + struct edge *ep; + + ep = b->in_edges; + if (ep == 0) + return; + + /* + * Make sure each predecessor loads the same value. + */ + val = ep->pred->val[A_ATOM]; + for (ep = ep->next; ep != 0; ep = ep->next) + if (val != ep->pred->val[A_ATOM]) + return; + + if (JT(b->in_edges->pred) == b) + diffp = &JT(b->in_edges->pred); + else + diffp = &JF(b->in_edges->pred); + + at_top = 1; + while (1) { + if (*diffp == 0) + return; + + if (JF(*diffp) != JF(b)) + return; + + if (!SET_MEMBER((*diffp)->dom, b->id)) + return; + + if ((*diffp)->val[A_ATOM] != val) + break; + + diffp = &JT(*diffp); + at_top = 0; + } + samep = &JT(*diffp); + while (1) { + if (*samep == 0) + return; + + if (JF(*samep) != JF(b)) + return; + + if (!SET_MEMBER((*samep)->dom, b->id)) + return; + + if ((*samep)->val[A_ATOM] == val) + break; + + /* XXX Need to check that there are no data dependencies + between diffp and samep. Currently, the code generator + will not produce such dependencies. */ + samep = &JT(*samep); + } +#ifdef notdef + /* XXX This doesn't cover everything. */ + for (i = 0; i < N_ATOMS; ++i) + if ((*samep)->val[i] != pred->val[i]) + return; +#endif + /* Pull up the node. */ + pull = *samep; + *samep = JT(pull); + JT(pull) = *diffp; + + /* + * At the top of the chain, each predecessor needs to point at the + * pulled up node. Inside the chain, there is only one predecessor + * to worry about. + */ + if (at_top) { + for (ep = b->in_edges; ep != 0; ep = ep->next) { + if (JT(ep->pred) == b) + JT(ep->pred) = pull; + else + JF(ep->pred) = pull; + } + } + else + *diffp = pull; + + done = 0; +} + +static void +opt_blks(root, do_stmts) + struct block *root; +{ + int i, maxlevel; + struct block *p; + + init_val(); + maxlevel = root->level; + for (i = maxlevel; i >= 0; --i) + for (p = levels[i]; p; p = p->link) + opt_blk(p, do_stmts); + + if (do_stmts) + /* + * No point trying to move branches; it can't possibly + * make a difference at this point. + */ + return; + + for (i = 1; i <= maxlevel; ++i) { + for (p = levels[i]; p; p = p->link) { + opt_j(&p->et); + opt_j(&p->ef); + } + } + for (i = 1; i <= maxlevel; ++i) { + for (p = levels[i]; p; p = p->link) { + or_pullup(p); + and_pullup(p); + } + } +} + +static inline void +link_inedge(parent, child) + struct edge *parent; + struct block *child; +{ + parent->next = child->in_edges; + child->in_edges = parent; +} + +static void +find_inedges(root) + struct block *root; +{ + int i; + struct block *b; + struct edge *ep; + + for (i = 0; i < n_blocks; ++i) + blocks[i]->in_edges = 0; + + /* + * Traverse the graph, adding each edge to the predecessor + * list of its sucessors. Skip the leaves (i.e. level 0). + */ + for (i = root->level; i > 0; --i) { + for (b = levels[i]; b != 0; b = b->link) { + link_inedge(&b->et, JT(b)); + link_inedge(&b->ef, JF(b)); + } + } +} + +static void +opt_root(b) + struct block **b; +{ + struct slist *tmp, *s; + + s = (*b)->stmts; + (*b)->stmts = 0; + while (BPF_CLASS((*b)->s.code) == BPF_JMP && JT(*b) == JF(*b)) + *b = JT(*b); + + tmp = (*b)->stmts; + if (tmp != 0) + sappend(s, tmp); + (*b)->stmts = s; +} + +static void +opt_loop(root, do_stmts) + struct block *root; + int do_stmts; +{ + int passes = 0; +#ifdef BDEBUG + if (dflag > 1) + opt_dump(root); +#endif + do { + done = 1; + find_levels(root); + find_dom(root); + find_closure(root); + find_inedges(root); + find_ud(root); + find_edom(root); + opt_blks(root, do_stmts); +#ifdef BDEBUG + if (dflag > 1) + opt_dump(root); +#endif + } while (!done); +} + +/* + * Optimize the filter code in its dag representation. + */ +void +optimize(rootp) + struct block **rootp; +{ + struct block *root; + + root = *rootp; + + opt_init(root); + opt_loop(root, 0); + opt_loop(root, 1); + intern_blocks(root); + opt_root(rootp); + opt_cleanup(); +} + +static void +make_marks(p) + struct block *p; +{ + if (!isMarked(p)) { + Mark(p); + if (BPF_CLASS(p->s.code) != BPF_RET) { + make_marks(JT(p)); + make_marks(JF(p)); + } + } +} + +/* + * Mark code array such that isMarked(i) is true + * only for nodes that are alive. + */ +static void +mark_code(p) + struct block *p; +{ + cur_mark += 1; + make_marks(p); +} + +/* + * True iff the two stmt lists load the same value from the packet into + * the accumulator. + */ +static int +eq_slist(x, y) + struct slist *x, *y; +{ + while (1) { + while (x && x->s.code == NOP) + x = x->next; + while (y && y->s.code == NOP) + y = y->next; + if (x == 0) + return y == 0; + if (y == 0) + return x == 0; + if (x->s.code != y->s.code || x->s.k != y->s.k) + return 0; + x = x->next; + y = y->next; + } +} + +static inline int +eq_blk(b0, b1) + struct block *b0, *b1; +{ + if (b0->s.code == b1->s.code && + b0->s.k == b1->s.k && + b0->et.succ == b1->et.succ && + b0->ef.succ == b1->ef.succ) + return eq_slist(b0->stmts, b1->stmts); + return 0; +} + +static void +intern_blocks(root) + struct block *root; +{ + struct block *p; + int i, j; + int done; + top: + done = 1; + for (i = 0; i < n_blocks; ++i) + blocks[i]->link = 0; + + mark_code(root); + + for (i = n_blocks - 1; --i >= 0; ) { + if (!isMarked(blocks[i])) + continue; + for (j = i + 1; j < n_blocks; ++j) { + if (!isMarked(blocks[j])) + continue; + if (eq_blk(blocks[i], blocks[j])) { + blocks[i]->link = blocks[j]->link ? + blocks[j]->link : blocks[j]; + break; + } + } + } + for (i = 0; i < n_blocks; ++i) { + p = blocks[i]; + if (JT(p) == 0) + continue; + if (JT(p)->link) { + done = 0; + JT(p) = JT(p)->link; + } + if (JF(p)->link) { + done = 0; + JF(p) = JF(p)->link; + } + } + if (!done) + goto top; +} + +static void +opt_cleanup() +{ + free((void *)vnode_base); + free((void *)vmap); + free((void *)edges); + free((void *)space); + free((void *)levels); + free((void *)blocks); +} + +/* + * Return the number of stmts in 's'. + */ +static int +slength(s) + struct slist *s; +{ + int n = 0; + + for (; s; s = s->next) + if (s->s.code != NOP) + ++n; + return n; +} + +/* + * Return the number of nodes reachable by 'p'. + * All nodes should be initially unmarked. + */ +static int +count_blocks(p) + struct block *p; +{ + if (p == 0 || isMarked(p)) + return 0; + Mark(p); + return count_blocks(JT(p)) + count_blocks(JF(p)) + 1; +} + +/* + * Do a depth first search on the flow graph, numbering the + * the basic blocks, and entering them into the 'blocks' array.` + */ +static void +number_blks_r(p) + struct block *p; +{ + int n; + + if (p == 0 || isMarked(p)) + return; + + Mark(p); + n = n_blocks++; + p->id = n; + blocks[n] = p; + + number_blks_r(JT(p)); + number_blks_r(JF(p)); +} + +/* + * Return the number of stmts in the flowgraph reachable by 'p'. + * The nodes should be unmarked before calling. + */ +static int +count_stmts(p) + struct block *p; +{ + int n; + + if (p == 0 || isMarked(p)) + return 0; + Mark(p); + n = count_stmts(JT(p)) + count_stmts(JF(p)); + return slength(p->stmts) + n + 1; +} + +/* + * Allocate memory. All allocation is done before optimization + * is begun. A linear bound on the size of all data structures is computed + * from the total number of blocks and/or statements. + */ +static void +opt_init(root) + struct block *root; +{ + u_long *p; + int i, n, max_stmts; + + /* + * First, count the blocks, so we can malloc an array to map + * block number to block. Then, put the blocks into the array. + */ + unMarkAll(); + n = count_blocks(root); + blocks = (struct block **)malloc(n * sizeof(*blocks)); + unMarkAll(); + n_blocks = 0; + number_blks_r(root); + + n_edges = 2 * n_blocks; + edges = (struct edge **)malloc(n_edges * sizeof(*edges)); + + /* + * The number of levels is bounded by the number of nodes. + */ + levels = (struct block **)malloc(n_blocks * sizeof(*levels)); + + edgewords = n_edges / (8 * sizeof(u_long)) + 1; + nodewords = n_blocks / (8 * sizeof(u_long)) + 1; + + /* XXX */ + space = (u_long *)malloc(2 * n_blocks * nodewords * sizeof(*space) + + n_edges * edgewords * sizeof(*space)); + p = space; + all_dom_sets = p; + for (i = 0; i < n; ++i) { + blocks[i]->dom = p; + p += nodewords; + } + all_closure_sets = p; + for (i = 0; i < n; ++i) { + blocks[i]->closure = p; + p += nodewords; + } + all_edge_sets = p; + for (i = 0; i < n; ++i) { + register struct block *b = blocks[i]; + + b->et.edom = p; + p += edgewords; + b->ef.edom = p; + p += edgewords; + b->et.id = i; + edges[i] = &b->et; + b->ef.id = n_blocks + i; + edges[n_blocks + i] = &b->ef; + b->et.pred = b; + b->ef.pred = b; + } + max_stmts = 0; + for (i = 0; i < n; ++i) + max_stmts += slength(blocks[i]->stmts) + 1; + /* + * We allocate at most 3 value numbers per statement, + * so this is an upper bound on the number of valnodes + * we'll need. + */ + maxval = 3 * max_stmts; + vmap = (struct vmapinfo *)malloc(maxval * sizeof(*vmap)); + vnode_base = (struct valnode *)malloc(maxval * sizeof(*vmap)); +} + +/* + * Some pointers used to convert the basic block form of the code, + * into the array form that BPF requires. 'fstart' will point to + * the malloc'd array while 'ftail' is used during the recursive traversal. + */ +static struct bpf_insn *fstart; +static struct bpf_insn *ftail; + +#ifdef BDEBUG +int bids[1000]; +#endif + +static void +convert_code_r(p) + struct block *p; +{ + struct bpf_insn *dst; + struct slist *src; + int slen; + u_int off; + + if (p == 0 || isMarked(p)) + return; + Mark(p); + + convert_code_r(JF(p)); + convert_code_r(JT(p)); + + slen = slength(p->stmts); + dst = ftail -= slen + 1; + + p->offset = dst - fstart; + + for (src = p->stmts; src; src = src->next) { + if (src->s.code == NOP) + continue; + dst->code = (u_short)src->s.code; + dst->k = src->s.k; + ++dst; + } +#ifdef BDEBUG + bids[dst - fstart] = p->id + 1; +#endif + dst->code = (u_short)p->s.code; + dst->k = p->s.k; + if (JT(p)) { + off = JT(p)->offset - (p->offset + slen) - 1; + if (off >= 256) + error("long jumps not supported"); + dst->jt = off; + off = JF(p)->offset - (p->offset + slen) - 1; + if (off >= 256) + error("long jumps not supported"); + dst->jf = off; + } +} + + +/* + * Convert flowgraph intermediate representation to the + * BPF array representation. Set *lenp to the number of instructions. + */ +struct bpf_insn * +icode_to_fcode(root, lenp) + struct block *root; + int *lenp; +{ + int n; + struct bpf_insn *fp; + + unMarkAll(); + n = *lenp = count_stmts(root); + + fp = (struct bpf_insn *)malloc(sizeof(*fp) * n); + bzero((char *)fp, sizeof(*fp) * n); + fstart = fp; + ftail = fp + n; + + unMarkAll(); + convert_code_r(root); + + return fp; +} + +#ifdef BDEBUG +opt_dump(root) + struct block *root; +{ + struct bpf_program f; + + bzero(bids, sizeof bids); + f.bf_insns = icode_to_fcode(root, &f.bf_len); + bpf_dump(&f, 1); + putchar('\n'); + free((char *)f.bf_insns); +} +#endif diff --git a/usr.sbin/tcpdump/tcpdump/os.c b/usr.sbin/tcpdump/tcpdump/os.c new file mode 100644 index 0000000..856fb5a --- /dev/null +++ b/usr.sbin/tcpdump/tcpdump/os.c @@ -0,0 +1,26 @@ +/* + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the University of California, + * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of + * the University nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +static char rcsid[] = + "@(#) $Header: os-bsd.c,v 1.2 90/09/21 02:12:17 mccanne Exp $ (LBL)"; +#endif + diff --git a/usr.sbin/tcpdump/tcpdump/pcap.c b/usr.sbin/tcpdump/tcpdump/pcap.c new file mode 100644 index 0000000..04c9695 --- /dev/null +++ b/usr.sbin/tcpdump/tcpdump/pcap.c @@ -0,0 +1,209 @@ +/* + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the University of California, + * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of + * the University nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ +#ifndef lint +static char rcsid[] = + "@(#)$Header: pcap-bpf.c,v 1.29 92/06/02 17:57:29 mccanne Exp $ (LBL)"; +#endif + +#include <stdio.h> +#include <netdb.h> +#include <ctype.h> +#include <signal.h> +#include <errno.h> +#include <sys/param.h> /* optionally get BSD define */ +#include <sys/time.h> +#include <sys/timeb.h> +#include <sys/socket.h> +#include <sys/file.h> +#include <sys/ioctl.h> +#include <net/bpf.h> +#include <net/if.h> +#include <string.h> + +#include "interface.h" + +extern int errno; + +static void +bpf_stats(fd) + int fd; +{ + struct bpf_stat s; + + if (ioctl(fd, BIOCGSTATS, &s) < 0) + return; + + (void)fflush(stdout); + (void)fprintf(stderr, "%d packets received by filter\n", s.bs_recv); + (void)fprintf(stderr, "%d packets dropped by kernel\n", s.bs_drop); +} + +void +readloop(cnt, if_fd, fp, printit) + int cnt; + int if_fd; + struct bpf_program *fp; + void (*printit)(); +{ + u_char *buf; + u_int bufsize; + int cc; + + if (ioctl(if_fd, BIOCGBLEN, (caddr_t)&bufsize) < 0) { + perror("tcpdump: BIOCGBLEN"); + exit(1); + } + buf = (u_char *)malloc(bufsize); + + if (ioctl(if_fd, BIOCSETF, (caddr_t)fp) < 0) { + perror("tcpdump: BIOCSETF"); + exit(1); + } + while (1) { + register u_char *bp, *ep; + + if ((cc = read(if_fd, (char *)buf, (int)bufsize)) < 0) { + /* Don't choke when we get ptraced */ + if (errno == EINTR) + continue; +#if defined(sun) && !defined(BSD) + /* + * Due to a SunOS bug, after 2^31 bytes, the kernel + * file offset overflows and read fails with EINVAL. + * The lseek() to 0 will fix things. + */ + if (errno == EINVAL && + (long)(tell(if_fd) + bufsize) < 0) { + (void)lseek(if_fd, 0, 0); + continue; + } +#endif + perror("tcpdump: read"); + exit(1); + } + /* + * Loop through each packet. + */ +#define bhp ((struct bpf_hdr *)bp) + bp = buf; + ep = bp + cc; + while (bp < ep) { + register int caplen, hdrlen; + if (cnt >= 0 && --cnt < 0) + goto out; + (*printit)(bp + (hdrlen = bhp->bh_hdrlen), + &bhp->bh_tstamp, bhp->bh_datalen, + caplen = bhp->bh_caplen); + bp += BPF_WORDALIGN(caplen + hdrlen); + } +#undef bhp + } + out: + wrapup(if_fd); +} + +wrapup(fd) + int fd; +{ + bpf_stats(fd); + close(fd); +} + +static inline int +bpf_open() +{ + int fd; + int n = 0; + char device[sizeof "/dev/bpf000"]; + + /* + * Go through all the minors and find one that isn't in use. + */ + do { + (void)sprintf(device, "/dev/bpf%d", n++); + fd = open(device, O_RDONLY); + } while (fd < 0 && errno == EBUSY); + + if (fd < 0) { + (void) fprintf(stderr, "tcpdump: "); + perror(device); + exit(-1); + } + return fd; +} + +int +initdevice(device, pflag, linktype) + char *device; + int pflag; + int *linktype; +{ + struct timeval timeout; + int if_fd; + struct ifreq ifr; + struct bpf_version bv; + + if_fd = bpf_open(); + + if (ioctl(if_fd, BIOCVERSION, (caddr_t)&bv) < 0) + warning("kernel bpf interpreter may be out of date"); + else if (bv.bv_major != BPF_MAJOR_VERSION || + bv.bv_minor < BPF_MINOR_VERSION) + error("requires bpf language %d.%d or higher; kernel is %d.%d", + BPF_MAJOR_VERSION, BPF_MINOR_VERSION, + bv.bv_major, bv.bv_minor); + + (void)strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name)); + if (ioctl(if_fd, BIOCSETIF, (caddr_t)&ifr) < 0) { + (void) fprintf(stderr, "tcpdump: BIOCSETIF: "); + perror(device); + exit(-1); + } + /* Get the data link layer type. */ + if (ioctl(if_fd, BIOCGDLT, (caddr_t)linktype) < 0) { + perror("tcpdump: BIOCGDLT"); + exit(-1); + } + /* set timeout */ + timeout.tv_sec = 1; + timeout.tv_usec = 0; + if (ioctl(if_fd, BIOCSRTIMEOUT, (caddr_t)&timeout) < 0) { + perror("tcpdump: BIOCSRTIMEOUT"); + exit(-1); + } + /* set promiscuous mode if requested, but only for broadcast nets */ + if (pflag == 0) { + switch (*linktype) { + + case DLT_SLIP: + case DLT_PPP: + case DLT_NULL: + break; + + default: + if (ioctl(if_fd, BIOCPROMISC, (void *)0) < 0) { + perror("tcpdump: BIOCPROMISC"); + exit(-1); + } + } + } + return(if_fd); +} diff --git a/usr.sbin/tcpdump/tcpdump/savefile.c b/usr.sbin/tcpdump/tcpdump/savefile.c new file mode 100644 index 0000000..79bb9e8 --- /dev/null +++ b/usr.sbin/tcpdump/tcpdump/savefile.c @@ -0,0 +1,303 @@ +/* + * Copyright (c) 1990,1991 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the University of California, + * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of + * the University nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ +#ifndef lint +static char rcsid[] = + "@(#)$Header: savefile.c,v 1.27 92/01/26 21:29:26 mccanne Exp $ (LBL)"; +#endif + +/* + * savefile.c - supports offline use of tcpdump + * Extraction/creation by Jeffrey Mogul, DECWRL + * Modified by Steve McCanne, LBL. + * + * Used to save the received packet headers, after filtering, to + * a file, and then read them later. + * The first record in the file contains saved values for the machine + * dependent values so we can print the dump file on any architecture. + */ + +#include <stdio.h> +#include <sys/types.h> +#include <sys/time.h> +#include <net/bpf.h> + +#include "version.h" +#include "savefile.h" + +#define TCPDUMP_MAGIC 0xa1b2c3d4 + +/* + * The first record in the file contains saved values for some + * of the flags used in the printout phases of tcpdump. + * Many fields here are longs so compilers won't insert unwanted + * padding; these files need to be interchangeable across architectures. + */ +struct file_header { + u_long magic; + u_short version_major; + u_short version_minor; + long thiszone; /* gmt to local correction */ + u_long sigfigs; /* accuracy of timestamps */ + u_long snaplen; /* max length saved portion of each pkt */ + u_long linktype; +}; + +int sf_swapped; + +FILE *sf_readfile; +FILE *sf_writefile; + +static int +sf_write_header(fp, linktype, thiszone, snaplen, precision) + FILE *fp; + int linktype; + int thiszone; + int snaplen; + int precision; +{ + struct file_header hdr; + + hdr.magic = TCPDUMP_MAGIC; + hdr.version_major = VERSION_MAJOR; + hdr.version_minor = VERSION_MINOR; + + hdr.thiszone = thiszone; + hdr.snaplen = snaplen; + hdr.sigfigs = precision; + hdr.linktype = linktype; + + if (fwrite((char *)&hdr, sizeof(hdr), 1, fp) != 1) + return -1; + + return 0; +} + +static void +swap_hdr(hp) + struct file_header *hp; +{ + hp->version_major = SWAPSHORT(hp->version_major); + hp->version_minor = SWAPSHORT(hp->version_minor); + hp->thiszone = SWAPLONG(hp->thiszone); + hp->sigfigs = SWAPLONG(hp->sigfigs); + hp->snaplen = SWAPLONG(hp->snaplen); + hp->linktype = SWAPLONG(hp->linktype); +} + +int +sf_read_init(fname, linktypep, thiszonep, snaplenp, precision) + char *fname; + int *linktypep, *thiszonep, *snaplenp, *precision; +{ + register FILE *fp; + struct file_header hdr; + + if (fname[0] == '-' && fname[1] == '\0') + fp = stdin; + else { + fp = fopen(fname, "r"); + if (fp == 0) { + (void) fprintf(stderr, "tcpdump: fopen: "); + perror(fname); + exit(1); + } + } + if (fread((char *)&hdr, sizeof(hdr), 1, fp) != 1) { + (void) fprintf(stderr, "tcpdump: fread: "); + perror(fname); + exit(1); + } + if (hdr.magic != TCPDUMP_MAGIC) { + if (SWAPLONG(hdr.magic) != TCPDUMP_MAGIC) + return SFERR_BADF; + sf_swapped = 1; + swap_hdr(&hdr); + } + if (hdr.version_major < VERSION_MAJOR) + return SFERR_BADVERSION; + + *thiszonep = hdr.thiszone; + *snaplenp = hdr.snaplen; + *linktypep = hdr.linktype; + *precision = hdr.sigfigs; + + sf_readfile = fp; + + return 0; +} + +/* + * Print out packets stored in the file initilized by sf_read_init(). + * If cnt >= 0, return after 'cnt' packets, otherwise continue until eof. + */ +int +sf_read(filtp, cnt, snaplen, printit) + struct bpf_program *filtp; + int cnt, snaplen; + void (*printit)(); +{ + struct packet_header h; + u_char *buf; + struct bpf_insn *fcode = filtp->bf_insns; + int status = 0; + + buf = (u_char *)malloc(snaplen); + + while (status == 0) { + status = sf_next_packet(&h, buf, snaplen); + + if (status) + break; + /* + * XXX It's possible (and likely) for us to screw up the + * network layer alignment when we pass down packets from + * this point (ip_print deals by copying the ip header + * to an aligned buffer). There doesn't seem to be a + * clean way to fix this. We could compute an offset + * from the link type (which would have to be passed in), + * but that only works for fixed size headers. + */ + if (bpf_filter(fcode, buf, h.len, h.caplen)) { + if (cnt >= 0 && --cnt < 0) + break; + (*printit)(buf, &h.ts, h.len, h.caplen); + } + } + + if (status == SFERR_EOF) + /* treat EOF's as okay status */ + status = 0; + + free((char *)buf); + return status; +} + +/* + * Read sf_readfile and return the next packet. Return the header in hdr + * and the contents in buf. Return 0 on success, SFERR_EOF if there were + * no more packets, and SFERR_TRUNC if a partial packet was encountered. + */ +int +sf_next_packet(hdr, buf, buflen) + struct packet_header *hdr; + u_char *buf; + int buflen; +{ + FILE *fp = sf_readfile; + + /* read the stamp */ + if (fread((char *)hdr, sizeof(struct packet_header), 1, fp) != 1) { + /* probably an EOF, though could be a truncated packet */ + return SFERR_EOF; + } + + if (sf_swapped) { + /* these were written in opposite byte order */ + hdr->caplen = SWAPLONG(hdr->caplen); + hdr->len = SWAPLONG(hdr->len); + hdr->ts.tv_sec = SWAPLONG(hdr->ts.tv_sec); + hdr->ts.tv_usec = SWAPLONG(hdr->ts.tv_usec); + } + + if (hdr->caplen > buflen) + return SFERR_BADF; + + /* read the packet itself */ + + if (fread((char *)buf, hdr->caplen, 1, fp) != 1) + return SFERR_TRUNC; + + return 0; +} + +/* + * Initialize so that sf_write() will output to the file named 'fname'. + */ +void +sf_write_init(fname, linktype, thiszone, snaplen, precision) + char *fname; + int linktype; + int thiszone; + int snaplen; + int precision; +{ + if (fname[0] == '-' && fname[1] == '\0') + sf_writefile = stdout; + else { + sf_writefile = fopen(fname, "w"); + if (sf_writefile == 0) { + (void) fprintf(stderr, "tcpdump: fopen: "); + perror(fname); + exit(1); + } + } + (void)sf_write_header(sf_writefile, + linktype, thiszone, snaplen, precision); +} + +/* + * Output a packet to the intialized dump file. + */ +void +sf_write(sp, tvp, length, caplen) + u_char *sp; + struct timeval *tvp; + int length; + int caplen; +{ + struct packet_header h; + + h.ts.tv_sec = tvp->tv_sec; + h.ts.tv_usec = tvp->tv_usec; + h.len = length; + h.caplen = caplen; + + (void)fwrite((char *)&h, sizeof h, 1, sf_writefile); + (void)fwrite((char *)sp, caplen, 1, sf_writefile); +} + +void +sf_err(code) + int code; +{ + switch (code) { + case SFERR_BADVERSION: + error("archaic file format"); + /* NOTREACHED */ + + case SFERR_BADF: + error("bad dump file format"); + /* NOTREACHED */ + + case SFERR_TRUNC: + error("truncated dump file"); + /* NOTREACHED */ + + case SFERR_EOF: + error("EOF reading dump file"); + /* NOTREACHED */ + + default: + error("unknown dump file error code in sf_err()"); + /* NOTREACHED */ + } + abort(); +} diff --git a/usr.sbin/tcpdump/tcpdump/savefile.h b/usr.sbin/tcpdump/tcpdump/savefile.h new file mode 100644 index 0000000..a5082f9 --- /dev/null +++ b/usr.sbin/tcpdump/tcpdump/savefile.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the University of California, + * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of + * the University nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Header: savefile.h,v 1.10 90/12/17 13:48:58 mccanne Exp $ + * + * Header for offline storage info. + * Extraction/creation by Jeffrey Mogul, DECWRL. + * + * Used to save the received packet headers, after filtering, to + * a file, and then read them later. + */ + +/* + * Each packet in the dump file is prepended with this generic header. + * This gets around the problem of different headers for different + * packet interfaces. + */ +struct packet_header { + struct timeval ts; /* time stamp */ + u_long len; /* length this packet (off wire) */ + u_long caplen; /* length of portion present */ +}; + +/* true if the contents of the savefile being read are byte-swapped */ +extern int sf_swapped; + +/* macros for when sf_swapped is true: */ +/* + * We use the "receiver-makes-right" approach to byte order, + * because time is at a premium when we are writing the file. + * In other words, the file_header and packet_header records + * are written in host byte order. + * Note that the packets are always written in network byte order. + * + * ntoh[ls] aren't sufficient because we might need to swap on a big-endian + * machine (if the file was written in little-end order). + */ +#define SWAPLONG(y) \ +((((y)&0xff)<<24) | (((y)&0xff00)<<8) | (((y)&0xff0000)>>8) | (((y)>>24)&0xff)) +#define SWAPSHORT(y) \ + ( (((y)&0xff)<<8) | (((y)&0xff00)>>8) ) + + +extern FILE *sf_readfile; /* dump file being read from */ +extern FILE *sf_writefile; /* dump file being written to */ + +int sf_read_init(); +int sf_read(); +int sf_next_packet(); +void sf_write_init(); +void sf_write(); +void sf_err(); + +#define SFERR_TRUNC 1 +#define SFERR_BADVERSION 2 +#define SFERR_BADF 3 +#define SFERR_EOF 4 /* not really an error, just a status */ + diff --git a/usr.sbin/tcpdump/tcpdump/tcpdump.1 b/usr.sbin/tcpdump/tcpdump/tcpdump.1 new file mode 100644 index 0000000..2406319 --- /dev/null +++ b/usr.sbin/tcpdump/tcpdump/tcpdump.1 @@ -0,0 +1,1067 @@ +.\" @(#) $Header: tcpdump.1,v 1.40 92/01/29 16:56:02 mccanne Exp $ (LBL) +.\" +.\" Copyright (c) 1988, 1989, 1990, 1991, 1992 +.\" The Regents of the University of California. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that: (1) source code distributions +.\" retain the above copyright notice and this paragraph in its entirety, (2) +.\" distributions including binary code include the above copyright notice and +.\" this paragraph in its entirety in the documentation or other materials +.\" provided with the distribution, and (3) all advertising materials mentioning +.\" features or use of this software display the following acknowledgement: +.\" ``This product includes software developed by the University of California, +.\" Lawrence Berkeley Laboratory and its contributors.'' Neither the name of +.\" the University nor the names of its contributors may be used to endorse +.\" or promote products derived from this software without specific prior +.\" written permission. +.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED +.\" WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +.\" +.TH TCPDUMP 1 "4 Jan 1992" +.SH NAME +tcpdump \- dump traffic on a network +.SH SYNOPSIS +.na +.B tcpdump +[ +.B \-deflnNOpqStvx +] [ +.B \-c +.I count +] [ +.B \-F +.I file +] +.br +.ti +8 +[ +.B \-i +.I interface +] [ +.B \-r +.I file +] +[ +.B \-s +.I snaplen +] +.br +.ti +8 +[ +.B \-w +.I file +] +.I expression +.br +.ad +.SH DESCRIPTION +.LP +\fITcpdump\fP prints out the headers of packets on a network interface +that match the boolean \fIexpression\fP. +.B Under SunOS: +You must be root to invoke \fItcpdump\fP or it must be installed +setuid to root. +.B Under Ultrix: +Any user can invoke \fItcpdump\fP once the super-user has enabled +promiscuous-mode operation using +.IR pfconfig (8). +.B Under BSD: +Access is controlled by the permissions on +.I /dev/bpf0, +etc. +.SH OPTIONS +.TP +.B \-c +Exit after receiving \fIcount\fP packets. +.TP +.B \-d +Dump the compiled packet-matching code to standard output and stop. +.TP +.B \-e +Print the link-level header on each dump line. +.TP +.B \-f +Print `foreign' internet addresses numerically rather than symbolically +(this option is intended to get around serious brain damage in +Sun's yp server \(em usually it hangs forever translating non-local +internet numbers). +.TP +.B \-F +Use \fIfile\fP as input for the filter expression. +An additional expression given on the command line is ignored. +.TP +.B \-i +Listen on \fIinterface\fP. +If unspecified, \fItcpdump\fP searches the system interface list for the +lowest numbered, configured up interface (excluding loopback). +Ties are broken by choosing the earliest match. +.TP +.B \-l +Make stdout line buffered. Useful if you want to see the data +while capturing it. E.g., +.br +``tcpdump\ \ \-l\ \ |\ \ tee dat'' or +``tcpdump\ \ \-l \ \ > dat\ \ &\ \ tail\ \ \-f\ \ dat''. +.TP +.B \-n +Don't convert addresses (i.e., host addresses, port numbers, etc.) to names. +.TP +.B \-N +Don't print domain name qualification of host names. E.g., +if you give this flag then \fItcpdump\fP will print ``nic'' +instead of ``nic.ddn.mil''. +.TP +.B \-O +Do not run the packet-matching code optimizer. This is useful only +if you suspect a bug in the optimizer. +.TP +.B \-p +\fIDon't\fP put the interface +into promiscuous mode. Note that the interface might be in promiscuous +for some other reason; hence, `-p' cannot be used as an abbreviation for +`ether host {localhost} or broadcast'. +.TP +.B \-q +Quick (quiet?) output. Print less protocol information so output +lines are shorter. +.TP +.B \-r +Read packets from \fIfile\fR (which was created with the -w option). +Standard input is used if \fIfile\fR is ``-''. +.TP +.B \-s +Snarf \fIsnaplen\fP bytes of data from each packet rather than the +default of 68 (with NIT, the minimum is actually 96). +68 bytes is adequate for IP, ICMP, TCP +and UDP but may truncate protocol information from name server and NFS +packets (see below). Packets truncated because of a limited snapshot +are indicated in the output with ``[|\fIproto\fP]'', where \fIproto\fP +is the name of the protocol level at which the truncation has occured. +Note that taking larger snapshots both increases +the amount of time it takes to process packets and, effectively, +decreases the amount of packet buffering. This may cause packets to be +lost. You should limit \fIsnaplen\fP to the smallest number that will +capture the protocol information you're interested in. +.TP +.B \-S +Print absolute, rather than relative, TCP sequence numbers. +.TP +.B \-t +\fIDon't\fP print a timestamp on each dump line. +.TP +.B \-tt +Print an unformatted timestamp on each dump line. +.TP +.B \-v +(Slightly more) verbose output. For example, the time to live +and type of service information in an IP packet is printed. +.TP +.B \-w +Write the raw packets to \fIfile\fR rather than parsing and printing +them out. They can later be printed with the \-r option. +Standard output is used if \fIfile\fR is ``-''. +.TP +.B \-x +Print each packet (minus its link level header) in hex. +The smaller of the entire packet or +.I snaplen +bytes will be printed. +.IP "\fI expression\fP" +.RS +selects which packets will be dumped. If no \fIexpression\fP +is given, all packets on the net will be dumped. Otherwise, +only packets for which \fIexpression\fP is `true' will be dumped. +.LP +The \fIexpression\fP consists of one or more +.I primitives. +Primitives usually consist of an +.I id +(name or number) preceded by one or more qualifiers. There are three +different kinds of qualifier: +.IP \fItype\fP +qualifiers say what kind of thing the id name or number refers to. +Possible types are +.BR host , +.B net +and +.BR port . +E.g., `host foo', `net 128.3', `port 20'. If there is no type +qualifier, +.B host +is assumed. +.IP \fIdir\fP +qualifiers specify a particular tranfer direction to and/or from +.I id. +Possible directions are +.BR src , +.BR dst , +.B "src or dst" +and +.BR "src and dst" . +E.g., `src foo', `dst net 128.3', `src or dst port ftp-data'. If +there is no dir qualifier, +.B "src or dst" +is assumed. +.IP \fIproto\fP +qualifiers restrict the match to a particular protocol. Possible +protos are: +.BR ether , +.BR ip , +.BR arp , +.BR rarp , +.B tcp +and +.BR udp . +E.g., `ether src foo', `arp net 128.3', `tcp port 21'. If there is +no proto qualifier, all protocols consistent with the type are +assumed. E.g., `src foo' means `(ip or arp or rarp) src foo' +(except the latter is not legal syntax), `net bar' means `(ip or +arp or rarp) net bar' and `port 53' means `(tcp or udp) port 53'. +.LP +In addition to the above, there are some special `primitive' keywords +that don't follow the pattern: +.BR gateway , +.BR broadcast , +.BR less , +.B greater +and arithmetic expressions. All of these are described below. +.LP +More complex filter expressions are built up by using the words +.BR and , +.B or +and +.B not +to combine primitives. E.g., `host foo and not port ftp and not port ftp-data'. +To save typing, identical qualifier lists can be omitted. E.g., +`tcp dst port ftp or ftp-data or domain' is exactly the same as +`tcp dst port ftp or tcp dst port ftp-data or tcp dst port domain'. +.LP +Allowable primitives are: +.IP "\fBdst host \fIhost\fR" +True if the IP destination field of the packet is \fIhost\fP, +which may be either an address or a name. +.IP "\fBsrc host \fIhost\fR" +True if the IP source field of the packet is \fIhost\fP. +.IP "\fBhost \fIhost\fP +True if either the IP source or destination of the packet is \fIhost\fP. +Any of the above host expressions can be prepended with the keywords, +\fBip\fP, \fBarp\fP, or \fBrarp\fP as in: +.in +.5i +.nf +\fBip host \fIhost\fR +.fi +.in -.5i +which is equivalent to: +.in +.5i +.nf +\fBether proto \fI\\ip\fB and host \fIhost\fR +.fi +.in -.5i +If \fIhost\fR is a name with multiple IP addresses, each address will +be checked for a match. +.IP "\fBether dst \fIehost\fP +True if the ethernet destination address is \fIehost\fP. \fIEhost\fP +may be either a name from /etc/ethers or a number (see +.IR ethers (3N) +for numeric format). +.IP "\fBether src \fIehost\fP +True if the ethernet source address is \fIehost\fP. +.IP "\fBether host \fIehost\fP +True if either the ethernet source or destination address is \fIehost\fP. +.IP "\fBgateway\fP \fIhost\fP +True if the packet used \fIhost\fP as a gateway. I.e., the ethernet +source or destination address was \fIhost\fP but neither the IP source +nor the IP destination was \fIhost\fP. \fIHost\fP must be a name and +must be found in both /etc/hosts and /etc/ethers. (An equivalent +expression is +.in +.5i +.nf +\fBether host \fIehost \fBand not host \fIhost\fR +.fi +.in -.5i +which can be used with either names or numbers for \fIhost / ehost\fP.) +.IP "\fBdst net \fInet\fR" +True if the IP destination address of the packet has a network +number of \fInet\fP, which may be either an address or a name. +.IP "\fBsrc net \fInet\fR" +True if the IP source address of the packet has a network +number of \fInet\fP. +.IP "\fBnet \fInet\fR" +True if either the IP source or destination address of the packet has a network +number of \fInet\fP. +.IP "\fBdst port \fIport\fR" +True if the packet is ip/tcp or ip/udp and has a +destination port value of \fIport\fP. +The \fIport\fP can be a number or a name used in /etc/services (see +.IR tcp (4P) +and +.IR udp (4P)). +If a name is used, both the port +number and protocol are checked. If a number or ambiguous name is used, +only the port number is checked (e.g., \fBdst port 513\fR will print both +tcp/login traffic and udp/who traffic, and \fBport domain\fR will print +both tcp/domain and udp/domain traffic). +.IP "\fBsrc port \fIport\fR" +True if the packet has a source port value of \fIport\fP. +.IP "\fBport \fIport\fR" +True if either the source or destination port of the packet is \fIport\fP. +Any of the above port expressions can be prepended with the keywords, +\fBtcp\fP or \fBudp\fP, as in: +.in +.5i +.nf +\fBtcp src port \fIport\fR +.fi +.in -.5i +which matches only tcp packets. +.IP "\fBless \fIlength\fR" +True if the packet has a length less than or equal to \fIlength\fP. +This is equivalent to: +.in +.5i +.nf +\fBlen <= \fIlength\fP. +.fi +.in -.5i +.IP "\fBgreater \fIlength\fR" +True if the packet has a length greater than or equal to \fIlength\fP. +This is equivalent to: +.in +.5i +.nf +\fBlen >= \fIlength\fP. +.fi +.in -.5i +.IP "\fBip proto \fIprotocol\fR" +True if the packet is an ip packet (see +.IR ip (4P)) +of protocol type \fIprotocol\fP. +\fIProtocol\fP can be a number or one of the names +\fIicmp\fP, \fIudp\fP, \fInd\fP, or \fItcp\fP. +Note that the identifiers \fItcp\fP, \fIudp\fP, and \fIicmp\fP are also +keywords and must be escaped via backslash (\\), which is \\\\ in the C-shell. +.IP "\fBether broadcast\fR" +True if the packet is an ethernet broadcast packet. The \fIether\fP +keyword is optional. +.IP "\fBip broadcast\fR" +True if the packet is an IP broadcast packet. It checks for both +the all-zeroes and all-ones broadcast conventions, and looks up +the local subnet mask. +.IP "\fBether multicast\fR" +True if the packet is an ethernet multicast packet. The \fIether\fP +keyword is optional. +This is shorthand for `\fBether[0] & 1 != 0\fP'. +.IP "\fBip multicast\fR" +True if the packet is an IP multicast packet. +.IP "\fBether proto \fIprotocol\fR" +True if the packet is of ether type \fIprotocol\fR. +\fIProtocol\fP can be a number or a name like +\fIip\fP, \fIarp\fP, or \fIrarp\fP. +Note these identifiers are also keywords +and must be escaped via backslash (\\). +.IP "\fBip\fR, \fBarp\fR, \fBrarp\fR" +Abbreviations for: +.in +.5i +.nf +\fBether proto \fIp\fR +.fi +.in -.5i +where \fIp\fR is one of the above protocols. +.IP "\fBtcp\fR, \fBudp\fR, \fBicmp\fR" +Abbreviations for: +.in +.5i +.nf +\fBip proto \fIp\fR +.fi +.in -.5i +where \fIp\fR is one of the above protocols. +.IP "\fIexpr relop expr\fR" +True if the relation holds, where \fIrelop\fR is one of >, <, >=, <=, =, !=, +and \fIexpr\fR is an arithmetic expression composed of integer constants +(expressed in standard C syntax), the normal binary operators +[+, -, *, /, &, |], a length operator, and special packet data accessors. +To access +data inside the packet, use the following syntax: +.in +.5i +.nf +\fIproto\fB [ \fIexpr\fB : \fIsize\fB ]\fR +.fi +.in -.5i +\fIProto\fR is one of \fBether, ip, arp, rarp, tcp, udp, \fRor \fBicmp\fR, and +indicates the protocol layer for the index operation. +The byte offset, relative to the indicated protocol layer, is +given by \fIexpr\fR. +\fISize\fR is optional and indicates the number of bytes in the +field of interest; it can be either one, two, or four, and defaults to one. +The length operator, indicated by the keyword \fBlen\fP, gives the +length of the packet. + +For example, `\fBether[0] & 1 != 0\fP' catches all multicast traffic. +The expression `\fBip[0] & 0xf != 5\fP' +catches all IP packets with options. The expression +`\fBip[2:2] & 0x1fff = 0\fP' +catches only unfragmented datagrams and frag zero of fragmented datagrams. +This check is implicitly applied to the \fBtcp\fP and \fBudp\fP +index opertations. +For instance, \fBtcp[0]\fP always means the first +byte of the TCP \fIheader\fP, and never means the first byte of an +intervening fragment. +.LP +Primitives may be combined using: +.IP +A parenthesized group of primitives and operators +(parentheses are special to the Shell and must be escaped). +.IP +Negation (`\fB!\fP' or `\fBnot\fP'). +.IP +Concatenation (`\fBand\fP'). +.IP +Alternation (`\fBor\fP'). +.LP +Negation has highest precedence. +Alternation and concatenation have equal precedence and associate +left to right. Note that explicit \fBand\fR tokens, not juxtaposition, +are now required for concatenation. +.LP +If an identifier is given without a keyword, the most recent keyword +is assumed. +For example, +.in +.5i +.nf +\fBnot host vs and ace\fR +.fi +.in -.5i +is short for +.in +.5i +.nf +\fBnot host vs and host ace\fR +.fi +.in -.5i +which should not be confused with +.in +.5i +.nf +\fBnot ( host vs or ace )\fR +.fi +.in -.5i +.LP +Expression arguments can be passed to tcpdump as either a single argument +or as multiple arguments, whichever is more convenient. +Generally, if the expression contains Shell metacharacters, it is +easier to pass it as a single, quoted argument. +Multiple arguments are concatenated with spaces before being parsed. +.SH EXAMPLES +.LP +To print all packets arriving at or departing from \fIsundown\fP: +.RS +.nf +\fBtcpdump host sundown\fP +.fi +.RE +.LP +To print traffic between \fIhelios\fR and either \fIhot\fR or \fIace\fR: +.RS +.nf +\fBtcpdump host helios and \\( hot or ace \\)\fP +.fi +.RE +.LP +To print all IP packets between \fIace\fR and any host except \fIhelios\fR: +.RS +.nf +\fBtcpdump ip host ace and not helios\fP +.fi +.RE +.LP +To print all traffic between local hosts and hosts at Berkeley: +.RS +.nf +.B +tcpdump net ucb-ether +.fi +.RE +.LP +To print all ftp traffic through internet gateway \fIsnup\fP: +(note that the expression is quoted to prevent the shell from +(mis-)interpreting the parentheses): +.RS +.nf +.B +tcpdump 'gateway snup and (port ftp or ftp-data)' +.fi +.RE +.LP +To print traffic neither sourced from nor destined for local hosts +(if you gateway to one other net, this stuff should never make it +onto your local net). +.RS +.nf +.B +tcpdump ip and not net \fIlocalnet\fP +.fi +.RE +.LP +To print the start and end packets (the SYN and FIN packets) of each +TCP conversation that involves a non-local host. +.RS +.nf +.B +tcpdump 'tcp[13] & 3 != 0 and not src and dst net \fIlocalnet\fP' +.fi +.RE +.LP +To print IP packets longer than 576 bytes sent through gateway \fIsnup\fP: +.RS +.nf +.B +tcpdump 'gateway snup and ip[2:2] > 576' +.fi +.RE +.LP +To print IP broadcast or multicast packets that were +.I not +sent via ethernet broadcast or multicast: +.RS +.nf +.B +tcpdump 'ether[0] & 1 = 0 and ip[16] >= 224' +.fi +.RE +.LP +To print all ICMP packets that are not echo requests/replies (i.e., not +ping packets): +.RS +.nf +.B +tcpdump 'icmp[0] != 8 and icmp[0] != 0" +.fi +.RE +.SH OUTPUT FORMAT +.LP +The output of \fItcpdump\fP is protocol dependent. The following +gives a brief description and examples of most of the formats. +.de HD +.sp 1.5 +.B +.. +.HD +Link Level Headers +.LP +If the '-e' option is given, the link level header is printed out. +On ethernets, the source and destination addresses, protocol, +and packet length are printed. +.LP +\fI(N.B.: The following description assumes familiarity with +the SLIP compression algorithm described in RFC-1144.)\fP +.LP +On SLIP links, a direction indicator (``I'' for inbound, ``O'' for outbound), +packet type, and compression information are printed out. +The packet type is printed first. +The three types are \fIip\fP, \fIutcp\fP, and \fIctcp\fP. +No further link information is printed for \fIip\fR packets. +For TCP packets, the connection identifier is printed following the type. +If the packet is compressed, its encoded header is printed out. +The special cases are printed out as +\fB*S+\fIn\fR and \fB*SA+\fIn\fR, where \fIn\fR is the amount by which +the sequence number (or sequence number and ack) has changed. +If it is not a special case, +zero or more changes are printed. +A change is indicated by U (urgent pointer), W (window), A (ack), +S (sequence number), and I (packet ID), followed by a delta (+n or -n), +or a new value (=n). +Finally, the amount of data in the packet and compressed header length +are printed. +.LP +For example, the following line shows an outbound compressed TCP packet, +with an implicit connection identifier; the ack has changed by 6, +the sequence number by 49, and the packet ID by 6; there are 3 bytes of +data and 6 bytes of compressed header: +.RS +.nf +\fBO ctcp * A+6 S+49 I+6 3 (6)\fP +.fi +.RE +.HD +ARP/RARP Packets +.LP +Arp/rarp output shows the type of request and its arguments. The +format is intended to be self explanatory. +Here is a short sample taken from the start of an `rlogin' from +host \fIrtsg\fP to host \fIcsam\fP: +.RS +.nf +.sp .5 +\f(CWarp who-has csam tell rtsg +arp reply csam is-at CSAM\fP +.sp .5 +.fi +.RE +The first line says that rtsg sent an arp packet asking +for the ethernet address of internet host csam. Csam +replies with its ethernet address (in this example, ethernet addresses +are in caps and internet addresses in lower case). +.LP +This would look less redundant if we had done \fBtcpdump \-n\fP: +.RS +.nf +.sp .5 +\f(CWarp who-has 128.3.254.6 tell 128.3.254.68 +arp reply 128.3.254.6 is-at 02:07:01:00:01:c4\fP +.fi +.RE +.LP +If we had done \fBtcpdump \-e\fP, the fact that the first packet is +broadcast and the second is point-to-point would be visible: +.RS +.nf +.sp .5 +\f(CWRTSG Broadcast 0806 64: arp who-has csam tell rtsg +CSAM RTSG 0806 64: arp reply csam is-at CSAM\fP +.sp .5 +.fi +.RE +For the first packet this says the ethernet source address is RTSG, the +destination is the broadcast address, the type field +contained hex 0806 (type ETHER_ARP) and the total length was 64 bytes. +.HD +TCP Packets +.LP +\fI(N.B.:The following description assumes familiarity with +the TCP protocol described in RFC-793. If you are not familiar +with the protocol, neither this description nor tcpdump will +be of much use to you.)\fP +.LP +The general format of a tcp protocol line is: +.RS +.nf +.sp .5 +\fIsrc > dst: flags data-seqno ack window urgent options\fP +.sp .5 +.fi +.RE +\fISrc\fP and \fIdst\fP are the source and destination IP +addresses and ports. \fIFlags\fP are some combination of S (SYN), +F (FIN), P (PUSH) or R (RST) or a single `.' (no flags). +\fIData-seqno\fP describes the portion of sequence space covered +by the data in this packet (see example below). +\fIAck\fP is sequence number of the next data expected the other +direction on this connection. +\fIWindow\fP is the number of bytes of receive buffer space available +the other direction on this connection. +\fIUrg\fP indicates there is `urgent' data in the packet. +\fIOptions\fP are tcp options enclosed in angle brackets (e.g., <mss 1024>). +.LP +\fISrc, dst\fP and \fIflags\fP are always present. The other fields +depend on the contents of the packet's tcp protocol header and +are output only if appropriate. +.LP +Here is the opening portion of an rlogin from host \fIrtsg\fP to +host \fIcsam\fP. +.RS +.nf +.sp .5 +\s-2\f(CWrtsg.1023 > csam.login: S 768512:768512(0) win 4096 <mss 1024> +csam.login > rtsg.1023: S 947648:947648(0) ack 768513 win 4096 <mss 1024> +rtsg.1023 > csam.login: . ack 1 win 4096 +rtsg.1023 > csam.login: P 1:2(1) ack 1 win 4096 +csam.login > rtsg.1023: . ack 2 win 4096 +rtsg.1023 > csam.login: P 2:21(19) ack 1 win 4096 +csam.login > rtsg.1023: P 1:2(1) ack 21 win 4077 +csam.login > rtsg.1023: P 2:3(1) ack 21 win 4077 urg 1 +csam.login > rtsg.1023: P 3:4(1) ack 21 win 4077 urg 1\fP\s+2 +.sp .5 +.fi +.RE +The first line says that tcp port 1023 on rtsg sent a packet +to port \fIlogin\fP +on csam. The \fBS\fP indicates that the \fISYN\fP flag was set. +The packet sequence number was 768512 and it contained no data. +(The notation is `first:last(nbytes)' which means `sequence +numbers \fIfirst\fP +up to but not including \fIlast\fP which is \fInbytes\fP bytes of user data'.) +There was no piggy-backed ack, the available receive window was 4096 +bytes and there was a max-segment-size option requesting an mss of +1024 bytes. +.LP +Csam replies with a similar packet except it includes a piggy-backed +ack for rtsg's SYN. Rtsg then acks csam's SYN. The `.' means no +flags were set. +The packet contained no data so there is no data sequence number. +Note that the ack sequence +number is a small integer (1). The first time \fBtcpdump\fP sees a +tcp `conversation', it prints the sequence number from the packet. +On subsequent packets of the conversation, the difference between +the current packet's sequence number and this initial sequence number +is printed. This means that sequence numbers after the +first can be interpreted +as relative byte positions in the conversation's data stream (with the +first data byte each direction being `1'). `-S' will override this +feature, causing the original sequence numbers to be output. +.LP +On the 6th line, rtsg sends csam 19 bytes of data (bytes 2 through 20 +in the rtsg \(-> csam side of the conversation). +The PUSH flag is set in the packet. +On the 7th line, csam says it's received data sent by rtsg up to +but not including byte 21. Most of this data is apparently sitting in the +socket buffer since csam's receive window has gotten 19 bytes smaller. +Csam also sends one byte of data to rtsg in this packet. +On the 8th and 9th lines, +csam sends two bytes of urgent, pushed data to rtsg. +.HD +.B +UDP Packets +.LP +UDP format is illustrated by this rwho packet: +.RS +.nf +.sp .5 +\f(CWactinide.who > broadcast.who: udp 84\fP +.sp .5 +.fi +.RE +This says that port \fIwho\fP on host \fIactinide\fP sent a udp +datagram to port \fIwho\fP on host \fIbroadcast\fP, the Internet +broadcast address. The packet contained 84 bytes of user data. +.LP +Some UDP services are recognized (from the source or destination +port number) and the higher level protocol information printed. +In particular, Domain Name service requests (RFC-1034/1035) and Sun +RPC calls (RFC-1050) to NFS. +.HD +UDP Name Server Requests +.LP +\fI(N.B.:The following description assumes familiarity with +the Domain Service protocol described in RFC-1035. If you are not familiar +with the protocol, the following description will appear to be written +in greek.)\fP +.LP +Name server requests are formatted as +.RS +.nf +.sp .5 +\fIsrc > dst: id op? flags qtype qclass name (len)\fP +.sp .5 +\f(CWh2opolo.1538 > helios.domain: 3+ A? ucbvax.berkeley.edu. (37)\fP +.sp .5 +.fi +.RE +Host \fIh2opolo\fP asked the domain server on \fIhelios\fP for an +address record (qtype=A) associated with the name \fIucbvax.berkeley.edu.\fP +The query id was `3'. The `+' indicates the \fIrecursion desired\fP flag +was set. The query length was 37 bytes, not including the UDP and +IP protocol headers. The query operation was the normal one, \fIQuery\fP, +so the op field was omitted. If the op had been anything else, it would +have been printed between the `3' and the `+'. +Similarly, the qclass was the normal one, +\fIC_IN\fP, and omitted. Any other qclass would have been printed +immediately after the `A'. +.LP +A few anomalies are checked and may result in extra fields enclosed in +square brackets: If a query contains an answer, name server or +authority section, +.IR ancount , +.IR nscount , +or +.I arcount +are printed as `[\fIn\fPa]', `[\fIn\fPn]' or `[\fIn\fPau]' where \fIn\fP +is the appropriate count. +If any of the response bits are set (AA, RA or rcode) or any of the +`must be zero' bits are set in bytes two and three, `[b2&3=\fIx\fP]' +is printed, where \fIx\fP is the hex value of header bytes two and three. +.HD +UDP Name Server Responses +.LP +Name server responses are formatted as +.RS +.nf +.sp .5 +\fIsrc > dst: id op rcode flags a/n/au type class data (len)\fP +.sp .5 +\f(CWhelios.domain > h2opolo.1538: 3 3/3/7 A 128.32.137.3 (273) +helios.domain > h2opolo.1537: 2 NXDomain* 0/1/0 (97)\fP +.sp .5 +.fi +.RE +In the first example, \fIhelios\fP responds to query id 3 from \fIh2opolo\fP +with 3 answer records, 3 name server records and 7 authority records. +The first answer record is type A (address) and its data is internet +address 128.32.137.3. The total size of the response was 273 bytes, +excluding UDP and IP headers. The op (Query) and response code +(NoError) were omitted, as was the class (C_IN) of the A record. +.LP +In the second example, \fIhelios\fP responds to query 2 with a +response code of non-existent domain (NXDomain) with no answers, +one name server and no authority records. The `*' indicates that +the \fIauthoritative answer\fP bit was set. Since there were no +answers, no type, class or data were printed. +.LP +Other flag characters that might appear are `\-' (recursion available, +RA, \fInot\fP set) and `|' (truncated message, TC, set). If the +`question' section doesn't contain exactly one entry, `[\fIn\fPq]' +is printed. +.LP +Note that name server requests and responses tend to be large and the +default \fIsnaplen\fP of 96 bytes may not capture enough of the packet +to print. Use the \fB\-s\fP flag to increase the snaplen if you +need to seriously investigate name server traffic. `\fB\-s 128\fP' +has worked well for me. + +.HD +NFS Requests +.LP +Sun NFS (Network File System) requests and replies are printed as: +.RS +.nf +.sp .5 +\fIsrc.xid > dst.nfs: len op args\fP +\fIsrc.nfs > dst.xid: reply stat len\fP +.sp .5 +\f(CWvs.e2766 > helios.nfs: 136 readdir fh 6.5197 8192 bytes @ 0 +helios.nfs > vs.e2766: reply ok 384 +vs.e2767 > helios.nfs: 136 lookup fh 6.5197 `RCS'\fP +.sp .5 +.fi +.RE +In the first line, host \fIvs\fP sends a transaction with id \fIe2766\fP +to \fIhelios\fP (note that the number following the src host is a +transaction id, \fInot\fP the source port). The request was 136 bytes, +excluding the UDP and IP headers. The operation was a \fIreaddir\fP +(read directory) on file handle (\fIfh\fP) 6.5197. 8192 bytes are +read, starting at offset 0. \fIHelios\fP replies `ok' with 384 +bytes of data. (The design of Sun's RPC protocol makes it difficult to +interpret replies. I don't bother.) +.LP +In the third line, \fIvs\fP asks \fIhelios\fP to lookup the name +`\fIRCS\fP' in directory file 6.5197. Note that the data printed +depends on the operation type. The format is intended to be self +explanatory (at least, to me) if read in conjunction with +an NFS protocol spec. +.LP +Note that NFS requests are very large and the above won't be printed +unless \fIsnaplen\fP is increased. I use `\fB\-s 192\fP' to watch +NFS traffic. + +.HD +KIP Appletalk (DDP in UDP) +.LP +Appletalk DDP packets encapsulated in UDP datagrams are de-encapsulated +and dumped as DDP packets (i.e., all the UDP header information is +discarded). The file +.I /etc/atalk.names +is used to translate appletalk net and node numbers to names. +Lines in this file have the form +.RS +.nf +.sp .5 +\fInumber name\fP + +\f(CW1.254 ether +16.1 icsd-net +1.254.110 ace\fP +.sp .5 +.fi +.RE +The first two lines give the names of appletalk networks. The third +line gives the name of a particular host (a host is distinguished +from a net by the 3rd octet in the number \- +a net number \fImust\fP have two octets and a host number \fImust\fP +have three octets.) The number and name should be separated by +whitespace (blanks or tabs). +The +.I /etc/atalk.names +file may contain blank lines or comment lines (lines starting with +a `#'). +.LP +Appletalk addresses are printed in the form +.RS +.nf +.sp .5 +\fInet.host.port\fP + +\f(CW144.1.209.2 > icsd-net.112.220 +office.2 > icsd-net.112.220 +jssmag.149.235 > icsd-net.2\fP +.sp .5 +.fi +.RE +(If the +.I /etc/atalk.names +doesn't exist or doesn't contain an entry for some appletalk +host/net number, addresses are printed in numeric form.) +In the first example, NBP (DDP port 2) on net 144.1 node 209 +is sending to whatever is listening on port 220 of net icsd node 112. +The second line is the same except the full name of the source node +is known (`office'). The third line is a send from port 235 on +net jssmag node 149 to broadcast on the icsd-net NBP port (note that +the broadcast address (255) is indicated by a net name with no host +number \- for this reason it's a good idea to keep node names and +net names distinct in /etc/atalk.names). +.LP +NBP (name binding protocol) and ATP (Appletalk transaction protocol) +packets have their contents interpreted. Other protocols just dump +the protocol name (or number if no name is registered for the +protocol) and packet size. + +\fBNBP packets\fP are formatted like the following examples: +.RS +.nf +.sp .5 +\s-2\f(CWicsd-net.112.220 > jssmag.2: nbp-lkup 190: "=:LaserWriter@*" +jssmag.209.2 > icsd-net.112.220: nbp-reply 190: "RM1140:LaserWriter@*" 250 +techpit.2 > icsd-net.112.220: nbp-reply 190: "techpit:LaserWriter@*" 186\fP\s+2 +.sp .5 +.fi +.RE +The first line is a name lookup request for laserwriters sent by net icsd host +112 and broadcast on net jssmag. The nbp id for the lookup is 190. +The second line shows a reply for this request (note that it has the +same id) from host jssmag.209 saying that it has a laserwriter +resource named "RM1140" registered on port 250. The third line is +another reply to the same request saying host techpit has laserwriter +"techpit" registered on port 186. + +\fBATP packet\fP formatting is demonstrated by the following example: +.RS +.nf +.sp .5 +\s-2\f(CWjssmag.209.165 > helios.132: atp-req 12266<0-7> 0xae030001 +helios.132 > jssmag.209.165: atp-resp 12266:0 (512) 0xae040000 +helios.132 > jssmag.209.165: atp-resp 12266:1 (512) 0xae040000 +helios.132 > jssmag.209.165: atp-resp 12266:2 (512) 0xae040000 +helios.132 > jssmag.209.165: atp-resp 12266:3 (512) 0xae040000 +helios.132 > jssmag.209.165: atp-resp 12266:4 (512) 0xae040000 +helios.132 > jssmag.209.165: atp-resp 12266:5 (512) 0xae040000 +helios.132 > jssmag.209.165: atp-resp 12266:6 (512) 0xae040000 +helios.132 > jssmag.209.165: atp-resp*12266:7 (512) 0xae040000 +jssmag.209.165 > helios.132: atp-req 12266<3,5> 0xae030001 +helios.132 > jssmag.209.165: atp-resp 12266:3 (512) 0xae040000 +helios.132 > jssmag.209.165: atp-resp 12266:5 (512) 0xae040000 +jssmag.209.165 > helios.132: atp-rel 12266<0-7> 0xae030001 +jssmag.209.133 > helios.132: atp-req* 12267<0-7> 0xae030002\fP\s+2 +.sp .5 +.fi +.RE +Jssmag.209 initiates transaction id 12266 with host helios by requesting +up to 8 packets (the `<0-7>'). The hex number at the end of the line +is the value of the `userdata' field in the request. +.LP +Helios responds with 8 512-byte packets. The `:digit' following the +transaction id gives the packet sequence number in the transaction +and the number in parens is the amount of data in the packet, +excluding the atp header. The `*' on packet 7 indicates that the +EOM bit was set. +.LP +Jssmag.209 then requests that packets 3 & 5 be retransmitted. Helios +resends them then jssmag.209 releases the transaction. Finally, +jssmag.209 initiates the next request. The `*' on the request +indicates that XO (`exactly once') was \fInot\fP set. + +.HD +IP Fragmentation +.LP +Fragmented Internet datagrams are printed as +.RS +.nf +.sp .5 +\fB(frag \fIid\fB:\fIsize\fB@\fIoffset\fB+)\fR +\fB(frag \fIid\fB:\fIsize\fB@\fIoffset\fB)\fR +.sp .5 +.fi +.RE +(The first form indicates there are more fragments. The second +indicates this is the last fragment.) +.LP +\fIId\fP is the fragment id. \fISize\fP is the fragment +size (in bytes) excluding the IP header. \fIOffset\fP is this +fragment's offset (in bytes) in the original datagram. +.LP +The fragment information is output for each fragment. The first +fragment contains the higher level protocol header and the frag +info is printed after the protocol info. Fragments +after the first contain no higher level protocol header and the +frag info is printed after the source and destination addresses. +For example, here is part of an ftp from arizona.edu to lbl-rtsg.arpa +over a CSNET connection that doesn't appear to handle 576 byte datagrams: +.RS +.nf +.sp .5 +\s-2\f(CWarizona.ftp-data > rtsg.1170: . 1024:1332(308) ack 1 win 4096 (frag 595a:328@0+) +arizona > rtsg: (frag 595a:204@328) +rtsg.1170 > arizona.ftp-data: . ack 1536 win 2560\fP\s+2 +.sp .5 +.fi +.RE +There are a couple of things to note here: First, addresses in the +2nd line don't include port numbers. This is because the TCP +protocol information is all in the first fragment and we have no idea +what the port or sequence numbers are when we print the later fragments. +Second, the tcp sequence information in the first line is printed as if there +were 308 bytes of user data when, in fact, there are 512 bytes (308 in +the first frag and 204 in the second). If you are looking for holes +in the sequence space or trying to match up acks +with packets, this can fool you. +.LP +A packet with the IP \fIdon't fragment\fP flag is marked with a +trailing \fB(DF)\fP. +.HD +Timestamps +.LP +By default, all output lines are preceded by a timestamp. The timestamp +is the current clock time in the form +.RS +.nf +\fIhh:mm:ss.frac\fP +.fi +.RE +and is as accurate as the kernel's clock (e.g., \(+-10ms on a Sun-3). +The timestamp reflects the time the kernel first saw the packet. No attempt +is made to account for the time lag between when the +ethernet interface removed the packet from the wire and when the kernel +serviced the `new packet' interrupt (of course, +with Sun's lousy clock resolution this time lag is negligible.) +.SH "SEE ALSO" +traffic(1C), nit(4P), bpf(4) +.SH AUTHORS +Van Jacobson (van@helios.ee.lbl.gov), +Craig Leres (leres@helios.ee.lbl.gov) and +Steven McCanne (mccanne@helios.ee.lbl.gov), all of +Lawrence Berkeley Laboratory, University of California, Berkeley, CA. +.SH BUGS +The clock resolution on most Suns is pathetic (20ms). +If you want to use the timestamp to generate some of the important +performance distributions (like packet interarrival time) it's best +to watch something that generates packets slowly (like an Arpanet +gateway or a MicroVax running VMS). +.LP +NIT doesn't let you watch your own outbound traffic, BPF will. +We recommend that you use the latter. +.LP +\fItcpdump\fP for Ultrix requires Ultrix version 4.0 or later; the kernel +has to have been built with the \fIpacketfilter\fP pseudo-device driver +(see +.IR packetfilter (4)). +As of this writing, Ultrix does not let you +watch either your own outbound or inbound traffic. +.LP +Under SunOS 4.1, the packet capture code (or Streams NIT) is not what +you'd call efficient. Don't plan on doing much with your Sun while +you're monitoring a busy network. +.LP +On Sun systems prior to release 3.2, NIT is very buggy. +If run on an old system, tcpdump may crash the machine. +.LP +Some attempt should be made to reassemble IP fragments or, at least +to compute the right length for the higher level protocol. +.LP +Name server inverse queries are not dumped correctly: The (empty) +question section is printed rather than real query in the answer +section. Some believe that inverse queries are themselves a bug and +prefer to fix the program generating them rather than tcpdump. +.LP +Apple Ethertalk DDP packets could be dumped as easily as KIP DDP +packets but aren't. +Even if we were inclined to do anything to promote the use of +Ethertalk (we aren't), LBL doesn't allow Ethertalk on any of its +networks so we'd would have no way of testing this code. +.LP +A packet trace that crosses a daylight savings time change will give +skewed time stamps (the time change is ignored). diff --git a/usr.sbin/tcpdump/tcpdump/tcpgram.y b/usr.sbin/tcpdump/tcpdump/tcpgram.y new file mode 100644 index 0000000..da235d0 --- /dev/null +++ b/usr.sbin/tcpdump/tcpdump/tcpgram.y @@ -0,0 +1,232 @@ +%{ +/* + * Copyright (c) 1988-1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the University of California, + * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of + * the University nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Grammar for tcpdump. + */ +#ifndef lint +static char rcsid[] = + "@(#) $Header: tcpgram.y,v 1.29 92/03/17 13:45:08 mccanne Exp $ (LBL)"; +#endif + +#include <stdio.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <net/if.h> +#include <netinet/in.h> +#include <netinet/if_ether.h> + +#include "interface.h" + +#include <sys/time.h> +#include <net/bpf.h> + +#include "gencode.h" + +#define QSET(q, p, d, a) (q).proto = (p),\ + (q).dir = (d),\ + (q).addr = (a) + +int n_errors = 0; + +static struct qual qerr = { Q_UNDEF, Q_UNDEF, Q_UNDEF, Q_UNDEF }; + +static void +yyerror() +{ + ++n_errors; +} + +%} + +%union { + int i; + u_long h; + u_char *e; + char *s; + struct stmt *stmt; + struct arth *a; + struct { + struct qual q; + struct block *b; + } blk; + struct block *rblk; +} + +%type <blk> expr id nid pid term rterm qid +%type <blk> head +%type <i> pqual dqual aqual ndaqual +%type <a> arth narth +%type <i> byteop pname pnum relop irelop +%type <blk> and or paren not null prog +%type <rblk> other + +%token DST SRC HOST GATEWAY +%token NET PORT LESS GREATER PROTO BYTE +%token ARP RARP IP TCP UDP ICMP +%token TK_BROADCAST TK_MULTICAST +%token NUM +%token LINK +%token GEQ LEQ NEQ +%token ID EID HID +%token LSH RSH +%token LEN + +%type <s> ID +%type <e> EID +%type <h> HID +%type <i> NUM + +%left OR AND +%nonassoc '!' +%left '|' +%left '&' +%left LSH RSH +%left '+' '-' +%left '*' '/' +%nonassoc UMINUS +%% +prog: null expr +{ + finish_parse($2.b); +} + | null + ; +null: /* null */ { $$.q = qerr; } + ; +expr: term + | expr and term { gen_and($1.b, $3.b); $$ = $3; } + | expr and id { gen_and($1.b, $3.b); $$ = $3; } + | expr or term { gen_or($1.b, $3.b); $$ = $3; } + | expr or id { gen_or($1.b, $3.b); $$ = $3; } + ; +and: AND { $$ = $<blk>0; } + ; +or: OR { $$ = $<blk>0; } + ; +id: nid + | pnum { $$.b = gen_ncode((u_long)$1, + $$.q = $<blk>0.q); } + | paren pid ')' { $$ = $2; } + ; +nid: ID { $$.b = gen_scode($1, $$.q = $<blk>0.q); } + | HID { $$.b = gen_ncode($1, $$.q = $<blk>0.q); } + | EID { $$.b = gen_ecode($1, $$.q = $<blk>0.q); } + | not id { gen_not($2.b); $$ = $2; } + ; +not: '!' { $$ = $<blk>0; } + ; +paren: '(' { $$ = $<blk>0; } + ; +pid: nid + | qid and id { gen_and($1.b, $3.b); $$ = $3; } + | qid or id { gen_or($1.b, $3.b); $$ = $3; } + ; +qid: pnum { $$.b = gen_ncode((u_long)$1, + $$.q = $<blk>0.q); } + | pid + ; +term: rterm + | not term { gen_not($2.b); $$ = $2; } + ; +head: pqual dqual aqual { QSET($$.q, $1, $2, $3); } + | pqual dqual { QSET($$.q, $1, $2, Q_DEFAULT); } + | pqual aqual { QSET($$.q, $1, Q_DEFAULT, $2); } + | pqual PROTO { QSET($$.q, $1, Q_DEFAULT, Q_PROTO); } + | pqual ndaqual { QSET($$.q, $1, Q_DEFAULT, $2); } + ; +rterm: head id { $$ = $2; } + | paren expr ')' { $$.b = $2.b; $$.q = $1.q; } + | pname { $$.b = gen_proto_abbrev($1); $$.q = qerr; } + | arth relop arth { $$.b = gen_relation($2, $1, $3, 0); + $$.q = qerr; } + | arth irelop arth { $$.b = gen_relation($2, $1, $3, 1); + $$.q = qerr; } + | other { $$.b = $1; $$.q = qerr; } + ; +/* protocol level qualifiers */ +pqual: pname + | { $$ = Q_DEFAULT; } + ; +/* 'direction' qualifiers */ +dqual: SRC { $$ = Q_SRC; } + | DST { $$ = Q_DST; } + | SRC OR DST { $$ = Q_OR; } + | DST OR SRC { $$ = Q_OR; } + | SRC AND DST { $$ = Q_AND; } + | DST AND SRC { $$ = Q_AND; } + ; +/* address type qualifiers */ +aqual: HOST { $$ = Q_HOST; } + | NET { $$ = Q_NET; } + | PORT { $$ = Q_PORT; } + ; +/* non-directional address type qualifiers */ +ndaqual: GATEWAY { $$ = Q_GATEWAY; } + ; +pname: LINK { $$ = Q_LINK; } + | IP { $$ = Q_IP; } + | ARP { $$ = Q_ARP; } + | RARP { $$ = Q_RARP; } + | TCP { $$ = Q_TCP; } + | UDP { $$ = Q_UDP; } + | ICMP { $$ = Q_ICMP; } + ; +other: pqual TK_BROADCAST { $$ = gen_broadcast($1); } + | pqual TK_MULTICAST { $$ = gen_multicast($1); } + | LESS NUM { $$ = gen_less($2); } + | GREATER NUM { $$ = gen_greater($2); } + | BYTE NUM byteop NUM { $$ = gen_byteop($3, $2, $4); } + ; +relop: '>' { $$ = BPF_JGT; } + | GEQ { $$ = BPF_JGE; } + | '=' { $$ = BPF_JEQ; } + ; +irelop: LEQ { $$ = BPF_JGT; } + | '<' { $$ = BPF_JGE; } + | NEQ { $$ = BPF_JEQ; } + ; +arth: pnum { $$ = gen_loadi($1); } + | narth + ; +narth: pname '[' arth ']' { $$ = gen_load($1, $3, 1); } + | pname '[' arth ':' NUM ']' { $$ = gen_load($1, $3, $5); } + | arth '+' arth { $$ = gen_arth(BPF_ADD, $1, $3); } + | arth '-' arth { $$ = gen_arth(BPF_SUB, $1, $3); } + | arth '*' arth { $$ = gen_arth(BPF_MUL, $1, $3); } + | arth '/' arth { $$ = gen_arth(BPF_DIV, $1, $3); } + | arth '&' arth { $$ = gen_arth(BPF_AND, $1, $3); } + | arth '|' arth { $$ = gen_arth(BPF_OR, $1, $3); } + | arth LSH arth { $$ = gen_arth(BPF_LSH, $1, $3); } + | arth RSH arth { $$ = gen_arth(BPF_RSH, $1, $3); } + | '-' arth %prec UMINUS { $$ = gen_neg($2); } + | paren narth ')' { $$ = $2; } + | LEN { $$ = gen_loadlen(); } + ; +byteop: '&' { $$ = '&'; } + | '|' { $$ = '|'; } + | '<' { $$ = '<'; } + | '>' { $$ = '>'; } + | '=' { $$ = '='; } + ; +pnum: NUM + | paren pnum ')' { $$ = $2; } + ; +%% diff --git a/usr.sbin/tcpdump/tcpdump/tcplex.l b/usr.sbin/tcpdump/tcpdump/tcplex.l new file mode 100644 index 0000000..840590a --- /dev/null +++ b/usr.sbin/tcpdump/tcpdump/tcplex.l @@ -0,0 +1,146 @@ +%{ +/* + * Copyright (c) 1988-1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the University of California, + * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of + * the University nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +static char rcsid[] = + "@(#) $Header: tcplex.l,v 1.26 92/02/14 15:16:35 mccanne Exp $ (LBL)"; +#endif + +/* + * Compiling with gcc under SunOS will cause problems unless we have this + * cruft here. The flex skeleton includes stddef.h which defines these types + * (under gcc). They will conflict with Sun's definitions in sys/types.h. + */ +#define size_t xxxsize_t +#define ptrdiff_t xxxptrdiff_t +#define wchar_t xxxwchar_t +#include <sys/types.h> +#undef size_t +#undef ptrdiff_t +#undef wchar_t + +#include "nametoaddr.h" + +/* + * We need bpf since enum bpf_code is in YYSTYPE. + */ +#include <sys/time.h> +#include <net/bpf.h> + +#include "gencode.h" +#include "y.tab.h" /* "tokdefs.h" */ + +#ifdef FLEX_SCANNER +#undef YY_INPUT +#define YY_INPUT(buf, result, max)\ + {\ + char *src = in_buffer;\ + int i;\ +\ + if (*src == 0)\ + result = YY_NULL;\ + else {\ + for (i = 0; *src && i < max; ++i)\ + buf[i] = *src++;\ + in_buffer += i;\ + result = i;\ + }\ + } +#else +#undef getc +#define getc(fp) (*in_buffer == 0 ? EOF : *in_buffer++) +#endif + +extern YYSTYPE yylval; +static char *in_buffer; + +%} + +N ([0-9]+|(0X|0x)[0-9A-Fa-f]+) +B ([0-9A-Fa-f][0-9A-Fa-f]?) + +%a 3000 + +%% +dst return DST; +src return SRC; + +link|ether|ppp|slip return LINK; +arp return ARP; +rarp return RARP; +ip return IP; +tcp return TCP; +udp return UDP; +icmp return ICMP; + +host return HOST; +net return NET; +port return PORT; +proto return PROTO; + +gateway return GATEWAY; + +less return LESS; +greater return GREATER; +byte return BYTE; +broadcast return TK_BROADCAST; +multicast return TK_MULTICAST; + +and return AND; +or return OR; +not return '!'; + +len return LEN; + +[ \n\t] ; +[+\-*/:\[\]!<>()&|=] return yytext[0]; +">=" return GEQ; +"<=" return LEQ; +"!=" return NEQ; +"==" return '='; +"<<" return LSH; +">>" return RSH; +{N} { yylval.i = stoi(yytext); return NUM; } +({N}\.{N})|({N}\.{N}\.{N})|({N}\.{N}\.{N}\.{N}) { + yylval.h = atoin(yytext); return HID; +} +{B}:{B}:{B}:{B}:{B}:{B} { yylval.e = ETHER_aton(yytext); return EID; } +{B}:+({B}:+)+ { error("bogus ethernet address %s", yytext); } +[A-Za-z][-_.A-Za-z0-9]* { yylval.s = yytext; return ID; } +"\\"[^ !()\n\t]+ { yylval.s = yytext + 1; return ID; } +[^ \[\]\t\n\-_.A-Za-z0-9!<>()&|=]+ { error("illegal token: %s\n", yytext); } +. { error("illegal char '%c'", *yytext); } +%% +void +lex_init(buf) + char *buf; +{ + in_buffer = buf; +} +#ifndef FLEX_SCANNER +int +yywrap() +/* so we don't need -ll */ +{ + return 1; +} +#endif |