diff options
author | jkh <jkh@FreeBSD.org> | 1995-01-20 04:13:07 +0000 |
---|---|---|
committer | jkh <jkh@FreeBSD.org> | 1995-01-20 04:13:07 +0000 |
commit | 92a97f198be3fb322c5ee6f5bc5443a05b713b57 (patch) | |
tree | e20b1b84001d0bfa407f612304872ad7460e3f9f /lib/libpcap | |
download | FreeBSD-src-92a97f198be3fb322c5ee6f5bc5443a05b713b57.zip FreeBSD-src-92a97f198be3fb322c5ee6f5bc5443a05b713b57.tar.gz |
Add the Packet Capture Library from Michael Reifenberger.
Submitted by: mr
Diffstat (limited to 'lib/libpcap')
-rw-r--r-- | lib/libpcap/bpf_filter.c | 550 | ||||
-rw-r--r-- | lib/libpcap/bpf_image.c | 284 | ||||
-rw-r--r-- | lib/libpcap/etherent.c | 148 | ||||
-rw-r--r-- | lib/libpcap/ethertype.h | 74 | ||||
-rw-r--r-- | lib/libpcap/gencode.c | 1755 | ||||
-rw-r--r-- | lib/libpcap/gencode.h | 173 | ||||
-rw-r--r-- | lib/libpcap/grammar.y | 264 | ||||
-rw-r--r-- | lib/libpcap/inet.c | 196 | ||||
-rw-r--r-- | lib/libpcap/nametoaddr.c | 357 | ||||
-rw-r--r-- | lib/libpcap/optimize.c | 1923 | ||||
-rw-r--r-- | lib/libpcap/pcap-bpf.c | 233 | ||||
-rw-r--r-- | lib/libpcap/pcap-dlpi.c | 506 | ||||
-rw-r--r-- | lib/libpcap/pcap-enet.c | 227 | ||||
-rw-r--r-- | lib/libpcap/pcap-int.h | 98 | ||||
-rw-r--r-- | lib/libpcap/pcap-namedb.h | 76 | ||||
-rw-r--r-- | lib/libpcap/pcap-nit.c | 240 | ||||
-rw-r--r-- | lib/libpcap/pcap-nit.h | 19 | ||||
-rw-r--r-- | lib/libpcap/pcap-pf.c | 310 | ||||
-rw-r--r-- | lib/libpcap/pcap-pf.h | 19 | ||||
-rw-r--r-- | lib/libpcap/pcap-snit.c | 293 | ||||
-rw-r--r-- | lib/libpcap/pcap-snoop.c | 195 | ||||
-rw-r--r-- | lib/libpcap/pcap.3 | 323 | ||||
-rw-r--r-- | lib/libpcap/pcap.c | 180 | ||||
-rw-r--r-- | lib/libpcap/pcap.h | 137 | ||||
-rw-r--r-- | lib/libpcap/savefile.c | 285 | ||||
-rw-r--r-- | lib/libpcap/scanner.l | 193 |
26 files changed, 9058 insertions, 0 deletions
diff --git a/lib/libpcap/bpf_filter.c b/lib/libpcap/bpf_filter.c new file mode 100644 index 0000000..5baa552 --- /dev/null +++ b/lib/libpcap/bpf_filter.c @@ -0,0 +1,550 @@ +/*- + * Copyright (c) 1990, 1991, 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from the Stanford/CMU enet packet filter, + * (net/enet.c) distributed as part of 4.3BSD, and code contributed + * to Berkeley by Steven McCanne and Van Jacobson both of Lawrence + * Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)bpf.c 7.5 (Berkeley) 7/15/91 + * + * static char rcsid[] = + * "$Header: bpf_filter.c,v 1.22 94/06/05 20:12:39 leres Exp $"; + */ +#if !(defined(lint) || defined(KERNEL)) +static char rcsid[] = + "@(#) $Header: bpf_filter.c,v 1.22 94/06/05 20:12:39 leres Exp $ (LBL)"; +#endif + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/time.h> +#include <net/bpf.h> + +#if defined(__alpha) +typedef int int32; +typedef u_int u_int32; +#else +typedef long int32; +typedef u_long u_int32; +#endif + +#ifdef sun +#include <netinet/in.h> +#endif + +#if defined(sparc) || defined(mips) || defined(ibm032) || defined(__alpha) +#define BPF_ALIGN +#endif + +#ifndef BPF_ALIGN +#define EXTRACT_SHORT(p) ((u_short)ntohs(*(u_short *)p)) +#define EXTRACT_LONG(p) (ntohl(*(u_int32 *)p)) +#else +#define EXTRACT_SHORT(p)\ + ((u_short)\ + ((u_short)*((u_char *)p+0)<<8|\ + (u_short)*((u_char *)p+1)<<0)) +#define EXTRACT_LONG(p)\ + ((u_int32)*((u_char *)p+0)<<24|\ + (u_int32)*((u_char *)p+1)<<16|\ + (u_int32)*((u_char *)p+2)<<8|\ + (u_int32)*((u_char *)p+3)<<0) +#endif + +#ifdef KERNEL +#include <sys/mbuf.h> +#define MINDEX(m, k) \ +{ \ + register int len = m->m_len; \ + \ + while (k >= len) { \ + k -= len; \ + m = m->m_next; \ + if (m == 0) \ + return 0; \ + len = m->m_len; \ + } \ +} + +static int +m_xword(m, k, err) + register struct mbuf *m; + register int k, *err; +{ + register int len; + register u_char *cp, *np; + register struct mbuf *m0; + + len = m->m_len; + while (k >= len) { + k -= len; + m = m->m_next; + if (m == 0) + goto bad; + len = m->m_len; + } + cp = mtod(m, u_char *) + k; + if (len - k >= 4) { + *err = 0; + return EXTRACT_LONG(cp); + } + m0 = m->m_next; + if (m0 == 0 || m0->m_len + len - k < 4) + goto bad; + *err = 0; + np = mtod(m0, u_char *); + switch (len - k) { + + case 1: + return (cp[k] << 24) | (np[0] << 16) | (np[1] << 8) | np[2]; + + case 2: + return (cp[k] << 24) | (cp[k + 1] << 16) | (np[0] << 8) | + np[1]; + + default: + return (cp[k] << 24) | (cp[k + 1] << 16) | (cp[k + 2] << 8) | + np[0]; + } + bad: + *err = 1; + return 0; +} + +static int +m_xhalf(m, k, err) + register struct mbuf *m; + register int k, *err; +{ + register int len; + register u_char *cp; + register struct mbuf *m0; + + len = m->m_len; + while (k >= len) { + k -= len; + m = m->m_next; + if (m == 0) + goto bad; + len = m->m_len; + } + cp = mtod(m, u_char *) + k; + if (len - k >= 2) { + *err = 0; + return EXTRACT_SHORT(cp); + } + m0 = m->m_next; + if (m0 == 0) + goto bad; + *err = 0; + return (cp[k] << 8) | mtod(m0, u_char *)[0]; + bad: + *err = 1; + return 0; +} +#endif + +/* + * Execute the filter program starting at pc on the packet p + * wirelen is the length of the original packet + * buflen is the amount of data present + */ +u_int +bpf_filter(pc, p, wirelen, buflen) + register struct bpf_insn *pc; + register u_char *p; + u_int wirelen; + register u_int buflen; +{ + register u_int32 A, X; + register int k; + int32 mem[BPF_MEMWORDS]; + + if (pc == 0) + /* + * No filter means accept all. + */ + return (u_int)-1; +#ifdef lint + A = 0; + X = 0; +#endif + --pc; + while (1) { + ++pc; + switch (pc->code) { + + default: +#ifdef KERNEL + return 0; +#else + abort(); +#endif + case BPF_RET|BPF_K: + return (u_int)pc->k; + + case BPF_RET|BPF_A: + return (u_int)A; + + case BPF_LD|BPF_W|BPF_ABS: + k = pc->k; + if (k + sizeof(int32) > buflen) { +#ifdef KERNEL + int merr; + + if (buflen != 0) + return 0; + A = m_xword((struct mbuf *)p, k, &merr); + if (merr != 0) + return 0; + continue; +#else + return 0; +#endif + } + A = EXTRACT_LONG(&p[k]); + continue; + + case BPF_LD|BPF_H|BPF_ABS: + k = pc->k; + if (k + sizeof(short) > buflen) { +#ifdef KERNEL + int merr; + + if (buflen != 0) + return 0; + A = m_xhalf((struct mbuf *)p, k, &merr); + continue; +#else + return 0; +#endif + } + A = EXTRACT_SHORT(&p[k]); + continue; + + case BPF_LD|BPF_B|BPF_ABS: + k = pc->k; + if (k >= buflen) { +#ifdef KERNEL + register struct mbuf *m; + + if (buflen != 0) + return 0; + m = (struct mbuf *)p; + MINDEX(m, k); + A = mtod(m, u_char *)[k]; + continue; +#else + return 0; +#endif + } + A = p[k]; + continue; + + case BPF_LD|BPF_W|BPF_LEN: + A = wirelen; + continue; + + case BPF_LDX|BPF_W|BPF_LEN: + X = wirelen; + continue; + + case BPF_LD|BPF_W|BPF_IND: + k = X + pc->k; + if (k + sizeof(int32) > buflen) { +#ifdef KERNEL + int merr; + + if (buflen != 0) + return 0; + A = m_xword((struct mbuf *)p, k, &merr); + if (merr != 0) + return 0; + continue; +#else + return 0; +#endif + } + A = EXTRACT_LONG(&p[k]); + continue; + + case BPF_LD|BPF_H|BPF_IND: + k = X + pc->k; + if (k + sizeof(short) > buflen) { +#ifdef KERNEL + int merr; + + if (buflen != 0) + return 0; + A = m_xhalf((struct mbuf *)p, k, &merr); + if (merr != 0) + return 0; + continue; +#else + return 0; +#endif + } + A = EXTRACT_SHORT(&p[k]); + continue; + + case BPF_LD|BPF_B|BPF_IND: + k = X + pc->k; + if (k >= buflen) { +#ifdef KERNEL + register struct mbuf *m; + + if (buflen != 0) + return 0; + m = (struct mbuf *)p; + MINDEX(m, k); + A = mtod(m, char *)[k]; + continue; +#else + return 0; +#endif + } + A = p[k]; + continue; + + case BPF_LDX|BPF_MSH|BPF_B: + k = pc->k; + if (k >= buflen) { +#ifdef KERNEL + register struct mbuf *m; + + if (buflen != 0) + return 0; + m = (struct mbuf *)p; + MINDEX(m, k); + X = (mtod(m, char *)[k] & 0xf) << 2; + continue; +#else + return 0; +#endif + } + X = (p[pc->k] & 0xf) << 2; + continue; + + case BPF_LD|BPF_IMM: + A = pc->k; + continue; + + case BPF_LDX|BPF_IMM: + X = pc->k; + continue; + + case BPF_LD|BPF_MEM: + A = mem[pc->k]; + continue; + + case BPF_LDX|BPF_MEM: + X = mem[pc->k]; + continue; + + case BPF_ST: + mem[pc->k] = A; + continue; + + case BPF_STX: + mem[pc->k] = X; + continue; + + case BPF_JMP|BPF_JA: + pc += pc->k; + continue; + + case BPF_JMP|BPF_JGT|BPF_K: + pc += (A > pc->k) ? pc->jt : pc->jf; + continue; + + case BPF_JMP|BPF_JGE|BPF_K: + pc += (A >= pc->k) ? pc->jt : pc->jf; + continue; + + case BPF_JMP|BPF_JEQ|BPF_K: + pc += (A == pc->k) ? pc->jt : pc->jf; + continue; + + case BPF_JMP|BPF_JSET|BPF_K: + pc += (A & pc->k) ? pc->jt : pc->jf; + continue; + + case BPF_JMP|BPF_JGT|BPF_X: + pc += (A > X) ? pc->jt : pc->jf; + continue; + + case BPF_JMP|BPF_JGE|BPF_X: + pc += (A >= X) ? pc->jt : pc->jf; + continue; + + case BPF_JMP|BPF_JEQ|BPF_X: + pc += (A == X) ? pc->jt : pc->jf; + continue; + + case BPF_JMP|BPF_JSET|BPF_X: + pc += (A & X) ? pc->jt : pc->jf; + continue; + + case BPF_ALU|BPF_ADD|BPF_X: + A += X; + continue; + + case BPF_ALU|BPF_SUB|BPF_X: + A -= X; + continue; + + case BPF_ALU|BPF_MUL|BPF_X: + A *= X; + continue; + + case BPF_ALU|BPF_DIV|BPF_X: + if (X == 0) + return 0; + A /= X; + continue; + + case BPF_ALU|BPF_AND|BPF_X: + A &= X; + continue; + + case BPF_ALU|BPF_OR|BPF_X: + A |= X; + continue; + + case BPF_ALU|BPF_LSH|BPF_X: + A <<= X; + continue; + + case BPF_ALU|BPF_RSH|BPF_X: + A >>= X; + continue; + + case BPF_ALU|BPF_ADD|BPF_K: + A += pc->k; + continue; + + case BPF_ALU|BPF_SUB|BPF_K: + A -= pc->k; + continue; + + case BPF_ALU|BPF_MUL|BPF_K: + A *= pc->k; + continue; + + case BPF_ALU|BPF_DIV|BPF_K: + A /= pc->k; + continue; + + case BPF_ALU|BPF_AND|BPF_K: + A &= pc->k; + continue; + + case BPF_ALU|BPF_OR|BPF_K: + A |= pc->k; + continue; + + case BPF_ALU|BPF_LSH|BPF_K: + A <<= pc->k; + continue; + + case BPF_ALU|BPF_RSH|BPF_K: + A >>= pc->k; + continue; + + case BPF_ALU|BPF_NEG: + A = -A; + continue; + + case BPF_MISC|BPF_TAX: + X = A; + continue; + + case BPF_MISC|BPF_TXA: + A = X; + continue; + } + } +} + +#ifdef KERNEL +/* + * Return true if the 'fcode' is a valid filter program. + * The constraints are that each jump be forward and to a valid + * code. The code must terminate with either an accept or reject. + * 'valid' is an array for use by the routine (it must be at least + * 'len' bytes long). + * + * The kernel needs to be able to verify an application's filter code. + * Otherwise, a bogus program could easily crash the system. + */ +int +bpf_validate(f, len) + struct bpf_insn *f; + int len; +{ + register int i; + register struct bpf_insn *p; + + for (i = 0; i < len; ++i) { + /* + * Check that that jumps are forward, and within + * the code block. + */ + p = &f[i]; + if (BPF_CLASS(p->code) == BPF_JMP) { + register int from = i + 1; + + if (BPF_OP(p->code) == BPF_JA) { + if (from + p->k >= len) + return 0; + } + else if (from + p->jt >= len || from + p->jf >= len) + return 0; + } + /* + * Check that memory operations use valid addresses. + */ + if ((BPF_CLASS(p->code) == BPF_ST || + (BPF_CLASS(p->code) == BPF_LD && + (p->code & 0xe0) == BPF_MEM)) && + (p->k >= BPF_MEMWORDS || p->k < 0)) + return 0; + /* + * Check for constant division by 0. + */ + if (p->code == (BPF_ALU|BPF_DIV|BPF_K) && p->k == 0) + return 0; + } + return BPF_CLASS(f[len - 1].code) == BPF_RET; +} +#endif diff --git a/lib/libpcap/bpf_image.c b/lib/libpcap/bpf_image.c new file mode 100644 index 0000000..e86fa85 --- /dev/null +++ b/lib/libpcap/bpf_image.c @@ -0,0 +1,284 @@ +/* + * Copyright (c) 1990, 1991, 1992, 1994 + * 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.12 94/01/31 03:22:34 leres Exp $ (LBL)"; +#endif + +#include <sys/types.h> +#include <sys/time.h> + +#include <net/bpf.h> + +#include <pcap.h> +#include <stdio.h> +#include <string.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/lib/libpcap/etherent.c b/lib/libpcap/etherent.c new file mode 100644 index 0000000..7045f3d --- /dev/null +++ b/lib/libpcap/etherent.c @@ -0,0 +1,148 @@ +/* + * Copyright (c) 1990, 1993, 1994 + * 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.8 94/06/20 19:07:50 leres Exp $ (LBL)"; +#endif + +#include <sys/types.h> + +#include <ctype.h> +#include <pcap.h> +#include <pcap-namedb.h> +#include <stdio.h> + +#ifndef __GNUC__ +#define inline +#endif + +static inline int xdtoi(int); +static inline int skip_space(FILE *); +static inline int skip_line(FILE *); + +/* Hex digit to integer. */ +static inline int +xdtoi(c) + register int 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 pcap_etherent * +pcap_next_etherent(FILE *fp) +{ + register int c, d, i; + char *bp; + static struct pcap_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 ethernet 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 continuing 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; +} diff --git a/lib/libpcap/ethertype.h b/lib/libpcap/ethertype.h new file mode 100644 index 0000000..37f6920 --- /dev/null +++ b/lib/libpcap/ethertype.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 1993, 1994 + * 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: ethertype.h,v 1.2 94/06/14 20:03:27 leres 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_DECDNS +#define ETHERTYPE_DECDNS 0x803c +#endif +#ifndef ETHERTYPE_DECDTS +#define ETHERTYPE_DECDTS 0x803e +#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 + +#ifndef ETHERTYPE_REVARP +#define ETHERTYPE_REVARP 0x8035 +#endif + diff --git a/lib/libpcap/gencode.c b/lib/libpcap/gencode.c new file mode 100644 index 0000000..0c7c049 --- /dev/null +++ b/lib/libpcap/gencode.c @@ -0,0 +1,1755 @@ +/* + * Copyright (c) 1990, 1991, 1992, 1993, 1994 + * 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.55 94/06/20 19:07:53 leres Exp $ (LBL)"; +#endif + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/time.h> + +#include <net/if.h> +#include <net/bpf.h> + +#include <netinet/in.h> +#include <netinet/if_ether.h> + +#include <memory.h> +#include <pcap.h> +#include <pcap-namedb.h> +#include <setjmp.h> +#if __STDC__ +#include <stdarg.h> +#include <stdlib.h> +#else +#include <varargs.h> +#endif + +#include "gencode.h" + +#ifndef __GNUC__ +#define inline +#endif + +#ifndef ETHERTYPE_REVARP +#define ETHERTYPE_REVARP 0x8035 +#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 + +#define JMP(c) ((c)|BPF_JMP|BPF_K) + +static jmp_buf top_ctx; +static pcap_t *bpf_pcap; + +/* VARARGS */ +volatile void +#if __STDC__ || defined(SOLARIS) +bpf_error(char *fmt, ...) +#else +bpf_error(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; + +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + if (bpf_pcap != NULL) + (void)vsprintf(pcap_geterr(bpf_pcap), fmt, ap); + va_end(ap); + longjmp(top_ctx, 1); + /* NOTREACHED */ +} + +static void init_linktype(int); + +static int alloc_reg(void); +static void free_reg(int); + +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(u_int); +static void freechunks(void); +static inline struct block *new_block(int); +static inline struct slist *new_stmt(int); +static struct block *gen_retblk(int); +static inline void syntax(void); + +static void backpatch(struct block *, struct block *); +static void merge(struct block *, struct block *); +static struct block *gen_cmp(u_int, u_int, long); +static struct block *gen_mcmp(u_int, u_int, long, u_long); +static struct block *gen_bcmp(u_int, u_int, u_char *); +static struct block *gen_uncond(int); +static inline struct block *gen_true(void); +static inline struct block *gen_false(void); +static struct block *gen_linktype(int); +static struct block *gen_hostop(u_long, u_long, int, int, u_int, u_int); +static struct block *gen_ehostop(u_char *, int); +#ifdef FDDI +static struct block *gen_fhostop(u_char *, int); +#endif +static struct block *gen_dnhostop(u_long, int, u_int); +static struct block *gen_host(u_long, u_long, int, int); +static struct block *gen_gateway(u_char *, u_long **, int, int); +static struct block *gen_ipfrag(void); +static struct block *gen_portatom(int, long); +struct block *gen_portop(int, int, int); +static struct block *gen_port(int, int, int); +static int lookup_proto(char *, int); +static struct block *gen_proto(int, int, int); +static u_long net_mask(u_long *); +static u_long net_mask(u_long *); +static struct slist *xfer_to_x(struct arth *); +static struct slist *xfer_to_a(struct arth *); +static struct block *gen_len(int, int); + +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) + bpf_error("out of memory"); + size = CHUNK0SIZE << k; + cp->m = (void *)malloc(size); + memset((char *)cp->m, 0, size); + cp->n_left = size; + if (n > size) + bpf_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); +} + +/* + * A strdup whose allocations are freed after code generation is over. + */ +char * +sdup(s) + char *s; +{ + int n = strlen(s) + 1; + char *cp = newchunk(n); + strcpy(cp, s); + return (cp); +} + +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() +{ + bpf_error("syntax error in filter expression"); +} + +static u_long netmask; +static int snaplen; + +int +pcap_compile(pcap_t *p, struct bpf_program *program, + char *buf, int optimize, u_long mask) +{ + extern int n_errors; + int len; + + bpf_pcap = p; + if (setjmp(top_ctx)) + return (-1); + + netmask = mask; + snaplen = pcap_snapshot(p); + + lex_init(buf ? buf : ""); + init_linktype(pcap_datalink(p)); + pcap_parse(); + + if (n_errors) + syntax(); + + if (root == NULL) + root = gen_retblk(snaplen); + + if (optimize) { + bpf_optimize(&root); + if (root == NULL || + (root->s.code == (BPF_RET|BPF_K) && root->s.k == 0)) + bpf_error("expression rejects all packets"); + } + program->bf_insns = icode_to_fcode(root, &len); + program->bf_len = len; + + freechunks(); + return (0); +} + +/* + * 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; +} + +static 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; +} + +static struct block * +gen_bcmp(offset, size, v) + u_int offset, size; + u_char *v; +{ + struct block *b, *tmp; + + b = NULL; + while (size >= 4) { + u_char *p = &v[size - 4]; + long w = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; + tmp = gen_cmp(offset + size - 4, BPF_W, w); + if (b != NULL) + gen_and(b, tmp); + b = tmp; + size -= 4; + } + while (size >= 2) { + u_char *p = &v[size - 2]; + long w = (p[0] << 8) | p[1]; + tmp = gen_cmp(offset + size - 2, BPF_H, w); + if (b != NULL) + gen_and(b, tmp); + b = tmp; + size -= 2; + } + if (size > 0) { + tmp = gen_cmp(offset, BPF_B, (long)v[0]); + if (b != NULL) + gen_and(b, tmp); + b = tmp; + } + return b; +} + +/* + * Various code constructs 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; +#ifdef FDDI +extern int fddipad; +#endif + +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; + +#ifdef FDDI + case DLT_FDDI: + /* + * FDDI doesn't really have a link-level type field. + * We assume that SSAP = SNAP is being used and pick + * out the encapsulated Ethernet type. + */ + off_linktype = 19 + fddipad; + off_nl = 21 + fddipad; + return; +#endif + + case DLT_IEEE802: + off_linktype = 20; + off_nl = 22; + return; + } + bpf_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); +} + +static 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 */ +} + +#ifdef FDDI +/* + * Like gen_ehostop, but for DLT_FDDI + */ +static struct block * +gen_fhostop(eaddr, dir) + u_char *eaddr; + int dir; +{ + struct block *b0, *b1; + + switch (dir) { + case Q_SRC: + return gen_bcmp(6 + 1 + fddipad, 6, eaddr); + + case Q_DST: + return gen_bcmp(0 + 1 + fddipad, 6, eaddr); + + case Q_AND: + b0 = gen_fhostop(eaddr, Q_SRC); + b1 = gen_fhostop(eaddr, Q_DST); + gen_and(b0, b1); + return b1; + + case Q_DEFAULT: + case Q_OR: + b0 = gen_fhostop(eaddr, Q_SRC); + b1 = gen_fhostop(eaddr, Q_DST); + gen_or(b0, b1); + return b1; + } + abort(); + /* NOTREACHED */ +} +#endif + +/* + * This is quite tricky because there may be pad bytes in front of the + * DECNET header, and then there are two possible data packet formats that + * carry both src and dst addresses, plus 5 packet types in a format that + * carries only the src node, plus 2 types that use a different format and + * also carry just the src node. + * + * Yuck. + * + * Instead of doing those all right, we just look for data packets with + * 0 or 1 bytes of padding. If you want to look at other packets, that + * will require a lot more hacking. + * + * To add support for filtering on DECNET "areas" (network numbers) + * one would want to add a "mask" argument to this routine. That would + * make the filter even more inefficient, although one could be clever + * and not generate masking instructions if the mask is 0xFFFF. + */ +static struct block * +gen_dnhostop(addr, dir, base_off) + u_long addr; + int dir; + u_int base_off; +{ + struct block *b0, *b1, *b2, *tmp; + u_int offset_lh; /* offset if long header is received */ + u_int offset_sh; /* offset if short header is received */ + + switch (dir) { + + case Q_DST: + offset_sh = 1; /* follows flags */ + offset_lh = 7; /* flgs,darea,dsubarea,HIORD */ + break; + + case Q_SRC: + offset_sh = 3; /* follows flags, dstnode */ + offset_lh = 15; /* flgs,darea,dsubarea,did,sarea,ssub,HIORD */ + break; + + case Q_AND: + /* Inefficient because we do our Calvinball dance twice */ + b0 = gen_dnhostop(addr, Q_SRC, base_off); + b1 = gen_dnhostop(addr, Q_DST, base_off); + gen_and(b0, b1); + return b1; + + case Q_OR: + case Q_DEFAULT: + /* Inefficient because we do our Calvinball dance twice */ + b0 = gen_dnhostop(addr, Q_SRC, base_off); + b1 = gen_dnhostop(addr, Q_DST, base_off); + gen_or(b0, b1); + return b1; + + default: + abort(); + } + b0 = gen_linktype(ETHERTYPE_DN); + /* Check for pad = 1, long header case */ + tmp = gen_mcmp(base_off + 2, BPF_H, + (long)ntohs(0x0681), (long)ntohs(0x07FF)); + b1 = gen_cmp(base_off + 2 + 1 + offset_lh, BPF_H, (long)ntohs(addr)); + gen_and(tmp, b1); + /* Check for pad = 0, long header case */ + tmp = gen_mcmp(base_off + 2, BPF_B, (long)0x06, (long)0x7); + b2 = gen_cmp(base_off + 2 + offset_lh, BPF_H, (long)ntohs(addr)); + gen_and(tmp, b2); + gen_or(b2, b1); + /* Check for pad = 1, short header case */ + tmp = gen_mcmp(base_off + 2, BPF_H, + (long)ntohs(0x0281), (long)ntohs(0x07FF)); + b2 = gen_cmp(base_off + 2 + 1 + offset_sh, BPF_H, (long)ntohs(addr)); + gen_and(tmp, b2); + gen_or(b2, b1); + /* Check for pad = 0, short header case */ + tmp = gen_mcmp(base_off + 2, BPF_B, (long)0x02, (long)0x7); + b2 = gen_cmp(base_off + 2 + offset_sh, BPF_H, (long)ntohs(addr)); + gen_and(tmp, b2); + gen_or(b2, b1); + + /* Combine with test for linktype */ + gen_and(b0, b1); + return b1; +} + +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: + bpf_error("'tcp' modifier applied to host"); + + case Q_UDP: + bpf_error("'udp' modifier applied to host"); + + case Q_ICMP: + bpf_error("'icmp' modifier applied to host"); + + case Q_DECNET: + return gen_dnhostop(addr, dir, off_nl); + + case Q_LAT: + bpf_error("LAT host filtering not implemented"); + + case Q_MOPDL: + bpf_error("MOPDL host filtering not implemented"); + + case Q_MOPRC: + bpf_error("MOPRC host filtering not implemented"); + + default: + 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) + bpf_error("direction applied to 'gateway'"); + + switch (proto) { + case Q_DEFAULT: + case Q_IP: + case Q_ARP: + case Q_RARP: + if (linktype == DLT_EN10MB) + b0 = gen_ehostop(eaddr, Q_OR); +#ifdef FDDI + else if (linktype == DLT_FDDI) + b0 = gen_fhostop(eaddr, Q_OR); +#endif + else + bpf_error("'gateway' supported only on ethernet or FDDI"); + + 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; + } + bpf_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: + bpf_error("link layer applied in wrong context"); + + case Q_DECNET: + b1 = gen_linktype(ETHERTYPE_DN); + break; + + case Q_LAT: + b1 = gen_linktype(ETHERTYPE_LAT); + break; + + case Q_MOPDL: + b1 = gen_linktype(ETHERTYPE_MOPDL); + break; + + case Q_MOPRC: + b1 = gen_linktype(ETHERTYPE_MOPRC); + break; + + 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, proto, 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; +} + +static int +lookup_proto(name, proto) + char *name; + int proto; +{ + int v; + + switch (proto) { + case Q_DEFAULT: + case Q_IP: + v = pcap_nametoproto(name); + if (v == PROTO_UNDEF) + bpf_error("unknown ip proto '%s'", name); + break; + + case Q_LINK: + /* XXX should look up h/w protocol type based on linktype */ + v = pcap_nametoeproto(name); + if (v == PROTO_UNDEF) + bpf_error("unknown ether proto '%s'", name); + break; + + default: + v = PROTO_UNDEF; + break; + } + return v; +} + +static struct block * +gen_proto(v, proto, dir) + int v; + int proto; + int dir; +{ + struct block *b0, *b1; + + if (dir != Q_DEFAULT) + bpf_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: + bpf_error("arp does not encapsulate another protocol"); + /* NOTREACHED */ + + case Q_RARP: + bpf_error("rarp does not encapsulate another protocol"); + /* NOTREACHED */ + + case Q_DECNET: + bpf_error("decnet encapsulation is not specifiable"); + /* NOTREACHED */ + + case Q_LAT: + bpf_error("lat does not encapsulate another protocol"); + /* NOTREACHED */ + + case Q_MOPRC: + bpf_error("moprc does not encapsulate another protocol"); + /* NOTREACHED */ + + case Q_MOPDL: + bpf_error("mopdl does not encapsulate another protocol"); + /* NOTREACHED */ + + case Q_LINK: + return gen_linktype(v); + + case Q_UDP: + bpf_error("'udp proto' is bogus"); + /* NOTREACHED */ + + case Q_TCP: + bpf_error("'tcp proto' is bogus"); + /* NOTREACHED */ + + case Q_ICMP: + bpf_error("'icmp proto' is bogus"); + /* NOTREACHED */ + + default: + abort(); + /* NOTREACHED */ + } + /* NOTREACHED */ +} + +/* + * Left justify 'addr' and return its resulting network mask. + */ +static u_long +net_mask(addr) + u_long *addr; +{ + register u_long m = 0xffffffff; + + if (*addr) + while ((*addr & 0xff000000) == 0) + *addr <<= 8, m <<= 8; + + return m; +} + +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 = pcap_nametonetaddr(name); + if (addr == 0) + bpf_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) { + switch (linktype) { + case DLT_EN10MB: + eaddr = pcap_ether_hostton(name); + if (eaddr == NULL) + bpf_error("unknown ether host '%s'", name); + return gen_ehostop(eaddr, dir); + +#ifdef FDDI + case DLT_FDDI: + eaddr = pcap_ether_hostton(name); + if (eaddr == NULL) + bpf_error("unknown FDDI host '%s'", name); + return gen_fhostop(eaddr, dir); +#endif + default: + bpf_error("only ethernet/FDDI supports link-level host name"); + break; + } + } else if (proto == Q_DECNET) { + unsigned short dn_addr = __pcap_nametodnaddr(name); + /* + * I don't think DECNET hosts can be multihomed, so + * there is no need to build up a list of addresses + */ + return (gen_host(dn_addr, 0, proto, dir)); + } else { + alist = pcap_nametoaddr(name); + if (alist == NULL || *alist == NULL) + bpf_error("unknown 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) + bpf_error("illegal qualifier of 'port'"); + if (pcap_nametoport(name, &port, &real_proto) == 0) + bpf_error("unknown port '%s'", name); + if (proto == Q_UDP) { + if (real_proto == IPPROTO_TCP) + bpf_error("port '%s' is tcp", name); + else + /* override PROTO_UNDEF */ + real_proto = IPPROTO_UDP; + } + if (proto == Q_TCP) { + if (real_proto == IPPROTO_UDP) + bpf_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 = pcap_ether_hostton(name); + if (eaddr == NULL) + bpf_error("unknown ether host: %s", name); + + alist = pcap_nametoaddr(name); + if (alist == NULL || *alist == NULL) + bpf_error("unknown 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 + bpf_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: + if (proto == Q_DECNET) + return gen_host(v, 0, proto, dir); + else if (proto == Q_LINK) { + bpf_error("illegal link layer address"); + } else { + 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 + bpf_error("illegal qualifier of 'port'"); + + return gen_port((int)v, proto, dir); + + case Q_GATEWAY: + bpf_error("'gateway' requires a name"); + /* NOTREACHED */ + + case Q_PROTO: + return gen_proto((int)v, proto, dir); + + case Q_UNDEF: + syntax(); + /* NOTREACHED */ + + default: + abort(); + /* NOTREACHED */ + } + /* 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) { + if (linktype == DLT_EN10MB) + return gen_ehostop(eaddr, (int)q.dir); +#ifdef FDDI + if (linktype == DLT_FDDI) + return gen_fhostop(eaddr, (int)q.dir); +#endif + } + bpf_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; +} + +static 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; +} + +static 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: + bpf_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: + bpf_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: + case Q_DECNET: + case Q_LAT: + case Q_MOPRC: + case Q_MOPDL: + /* 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; + } + } + bpf_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, n; +{ + struct slist *s; + struct block *b; + + s = new_stmt(BPF_LD|BPF_LEN); + s->next = new_stmt(BPF_ALU|BPF_SUB|BPF_K); + 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, idx, 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_OR|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); +#ifdef FDDI + if (linktype == DLT_FDDI) + return gen_fhostop(ebroadcast, Q_DST); +#endif + bpf_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; + } + bpf_error("only ether/ip broadcast filters supported"); +} + +struct block * +gen_multicast(proto) + int proto; +{ + register struct block *b0, *b1; + register struct slist *s; + + switch (proto) { + + case Q_DEFAULT: + case Q_LINK: + if (linktype == DLT_EN10MB) { + /* 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; + } + + if (linktype == DLT_FDDI) { + /* XXX TEST THIS: MIGHT NOT PORT PROPERLY XXX */ + /* fddi[1] & 1 != 0 */ + s = new_stmt(BPF_LD|BPF_B|BPF_ABS); + s->s.k = 1; + b0 = new_block(JMP(BPF_JSET)); + b0->s.k = 1; + b0->stmts = s; + return b0; + } + /* Link not known to support multicasts */ + break; + + 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; + } + bpf_error("only IP multicast filters supported on ethernet/FDDI"); +} + +/* + * generate command for inbound/outbound. It's here so we can + * make it link-type specific. 'dir' = 0 implies "inbound", + * = 1 implies "outbound". + */ +struct block * +gen_inbound(dir) + int dir; +{ + register struct block *b0; + + b0 = gen_relation(BPF_JEQ, + gen_load(Q_LINK, gen_loadi(0), 1), + gen_loadi(0), + dir); + return (b0); +} diff --git a/lib/libpcap/gencode.h b/lib/libpcap/gencode.h new file mode 100644 index 0000000..b202acf --- /dev/null +++ b/lib/libpcap/gencode.h @@ -0,0 +1,173 @@ +/* + * Copyright (c) 1990, 1991, 1992, 1993, 1994 + * 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.20 94/06/12 14:29:30 leres 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 + +#define Q_DECNET 8 +#define Q_LAT 9 +#define Q_MOPRC 10 +#define Q_MOPDL 11 + +/* 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 */ +}; + +struct qual { + unsigned char addr; + unsigned char proto; + unsigned char dir; + unsigned char pad; +}; + +#ifndef __GNUC__ +#define volatile +#endif + +struct arth *gen_loadi(int); +struct arth *gen_load(int, struct arth *, int); +struct arth *gen_loadlen(void); +struct arth *gen_neg(struct arth *); +struct arth *gen_arth(int, struct arth *, struct arth *); + +void gen_and(struct block *, struct block *); +void gen_or(struct block *, struct block *); +void gen_not(struct block *); + +struct block *gen_scode(char *, struct qual); +struct block *gen_ecode(u_char *, struct qual); +struct block *gen_ncode(u_long, struct qual); +struct block *gen_proto_abbrev(int); +struct block *gen_relation(int, struct arth *, struct arth *, int); +struct block *gen_less(int); +struct block *gen_greater(int); +struct block *gen_byteop(int, int, int); +struct block *gen_broadcast(int); +struct block *gen_multicast(int); +struct block *gen_inbound(int); + +void bpf_optimize(struct block **); +volatile void bpf_error(char *, ...); + +void finish_parse(struct block *); +char *sdup(char *); + +struct bpf_insn *icode_to_fcode(struct block *, int *); +int pcap_parse(void); +void lex_init(char *); +void sappend(struct slist *, struct slist *); + +/* XXX */ +#define JT(b) ((b)->et.succ) +#define JF(b) ((b)->ef.succ) diff --git a/lib/libpcap/grammar.y b/lib/libpcap/grammar.y new file mode 100644 index 0000000..55fa198 --- /dev/null +++ b/lib/libpcap/grammar.y @@ -0,0 +1,264 @@ +%{ +/* + * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994 + * 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: grammar.y,v 1.39 94/06/14 20:09:25 leres Exp $ (LBL)"; +#endif + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/socket.h> + +#include <net/if.h> +#include <net/bpf.h> + +#include <netinet/in.h> +#include <netinet/if_ether.h> + +#include <stdio.h> +#include <pcap.h> +#include <pcap-namedb.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(char *msg) +{ + ++n_errors; + bpf_error(msg); + /* NOTREACHED */ +} + +#ifndef YYBISON +pcap_parse() +{ + return (yyparse()); +} +#endif + +%} + +%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 DECNET LAT MOPRC MOPDL +%token TK_BROADCAST TK_MULTICAST +%token NUM INBOUND OUTBOUND +%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 { + /* Decide how to parse HID based on proto */ + $$.q = $<blk>0.q; + switch ($$.q.proto) { + case Q_DECNET: + $$.b = + gen_ncode(__pcap_atodn((char *)$1), + $$.q); + break; + default: + $$.b = + gen_ncode(__pcap_atoin((char *)$1), + $$.q); + break; + } + } + | 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; } + | DECNET { $$ = Q_DECNET; } + | LAT { $$ = Q_LAT; } + | MOPDL { $$ = Q_MOPDL; } + | MOPRC { $$ = Q_MOPRC; } + ; +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); } + | INBOUND { $$ = gen_inbound(0); } + | OUTBOUND { $$ = gen_inbound(1); } + ; +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/lib/libpcap/inet.c b/lib/libpcap/inet.c new file mode 100644 index 0000000..004e275 --- /dev/null +++ b/lib/libpcap/inet.c @@ -0,0 +1,196 @@ +/* + * Copyright (c) 1994 + * 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 the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the Computer Systems + * Engineering Group at Lawrence Berkeley Laboratory. + * 4. Neither the name of the University nor of the Laboratory may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char rcsid[] = + "@(#) $Header: inet.c,v 1.4 94/06/07 01:16:50 leres Exp $ (LBL)"; +#endif + +#include <sys/param.h> +#include <sys/file.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#ifdef SOLARIS +#include <sys/sockio.h> +#endif + +#include <net/if.h> +#include <netinet/in.h> + +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <pcap.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 NULL + * if none can be found. The interface must be configured up; the + * lowest unit number is preferred; loopback is ignored. + */ +char * +pcap_lookupdev(errbuf) + register char *errbuf; +{ + register int fd, minunit, n; + register char *cp; + register struct ifreq *ifrp, *ifend, *ifnext, *mp; + struct ifconf ifc; + struct ifreq ibuf[16], ifr; + static char device[sizeof(ifrp->ifr_name) + 1]; + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + (void)sprintf(errbuf, "socket: %s", pcap_strerror(errno)); + return (NULL); + } + ifc.ifc_len = sizeof ibuf; + ifc.ifc_buf = (caddr_t)ibuf; + + if (ioctl(fd, SIOCGIFCONF, (char *)&ifc) < 0 || + ifc.ifc_len < sizeof(struct ifreq)) { + (void)sprintf(errbuf, "SIOCGIFCONF: %s", pcap_strerror(errno)); + (void)close(fd); + return (NULL); + } + ifrp = ibuf; + ifend = (struct ifreq *)((char *)ibuf + ifc.ifc_len); + + mp = NULL; + minunit = 666; + for (; ifrp < ifend; ifrp = ifnext) { +#if BSD - 0 >= 199006 + n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name); + if (n < sizeof(*ifrp)) + ifnext = ifrp + 1; + else + ifnext = (struct ifreq *)((char *)ifrp + n); + if (ifrp->ifr_addr.sa_family != AF_INET) + continue; +#else + ifnext = ifrp + 1; +#endif + /* + * 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.) + */ + strncpy(ifr.ifr_name, ifrp->ifr_name, sizeof(ifr.ifr_name)); + if (ioctl(fd, SIOCGIFFLAGS, (char *)&ifr) < 0) { + (void)sprintf(errbuf, "SIOCGIFFLAGS: %s", + pcap_strerror(errno)); + (void)close(fd); + return (NULL); + } + + /* Must be up and not the loopback */ + if ((ifr.ifr_flags & IFF_UP) == 0 || ISLOOPBACK(&ifr)) + continue; + + for (cp = ifrp->ifr_name; !isdigit(*cp); ++cp) + continue; + n = atoi(cp); + if (n < minunit) { + minunit = n; + mp = ifrp; + } + } + (void)close(fd); + if (mp == NULL) { + (void)strcpy(errbuf, "no suitable device found"); + return (NULL); + } + + (void)strncpy(device, mp->ifr_name, sizeof(device) - 1); + device[sizeof(device) - 1] = '\0'; + return (device); +} + +int +pcap_lookupnet(device, netp, maskp, errbuf) + register char *device; + register u_long *netp, *maskp; + register char *errbuf; +{ + register int fd; + register struct sockaddr_in *sin; + struct ifreq ifr; + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + (void)sprintf(errbuf, "socket: %s", pcap_strerror(errno)); + return (-1); + } + (void)strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name)); + if (ioctl(fd, SIOCGIFADDR, (char *)&ifr) < 0) { + (void)sprintf(errbuf, "SIOCGIFADDR: %s: %s", + device, pcap_strerror(errno)); + (void)close(fd); + return (-1); + } + sin = (struct sockaddr_in *)&ifr.ifr_addr; + *netp = sin->sin_addr.s_addr; + if (ioctl(fd, SIOCGIFNETMASK, (char *)&ifr) < 0) { + (void)sprintf(errbuf, "SIOCGIFNETMASK: %s: %s", + device, pcap_strerror(errno)); + (void)close(fd); + return (-1); + } + (void)close(fd); + *maskp = sin->sin_addr.s_addr; + if (*maskp == 0) { + if (IN_CLASSA(*netp)) + *maskp = IN_CLASSA_NET; + else if (IN_CLASSB(*netp)) + *maskp = IN_CLASSB_NET; + else if (IN_CLASSC(*netp)) + *maskp = IN_CLASSC_NET; + else { + (void)sprintf(errbuf, "inet class for 0x%x unknown", + *netp); + return (-1); + } + } + *netp &= *maskp; + return (0); +} diff --git a/lib/libpcap/nametoaddr.c b/lib/libpcap/nametoaddr.c new file mode 100644 index 0000000..0d8bbf7 --- /dev/null +++ b/lib/libpcap/nametoaddr.c @@ -0,0 +1,357 @@ +/* + * Copyright (c) 1990, 1991, 1992, 1993, 1994 + * 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.21 94/06/20 19:07:54 leres Exp $ (LBL)"; +#endif + +#include <sys/param.h> +#include <sys/socket.h> +#include <net/if.h> +#include <netinet/in.h> +#include <netinet/if_ether.h> +#include <arpa/inet.h> + +#include <ctype.h> +#include <errno.h> +#include <netdb.h> +#include <pcap.h> +#include <pcap-namedb.h> +#include <stdio.h> + +#include "gencode.h" + +#ifndef __GNUC__ +#define inline +#endif + +#ifndef NTOHL +#define NTOHL(x) (x) = ntohl(x) +#define NTOHS(x) (x) = ntohs(x) +#endif + +static inline int xdtoi(int); + +/* + * Convert host name to internet address. + * Return 0 upon failure. + */ +u_long ** +pcap_nametoaddr(const char *name) +{ +#ifndef h_addr + static u_long *hlist[2]; +#endif + u_long **p; + struct hostent *hp; + + if ((hp = gethostbyname(name)) != NULL) { +#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 +pcap_nametonetaddr(const char *name) +{ + struct netent *np; + + if ((np = getnetbyname(name)) != NULL) + 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. + */ +int +pcap_nametoport(const char *name, int *port, int *proto) +{ + struct servent *sp; + char *other; + + sp = getservbyname(name, (char *)0); + if (sp != NULL) { + NTOHS(sp->s_port); + *port = sp->s_port; + *proto = pcap_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 ambiguous names that refer + to different port numbers. */ +#ifdef notdef + warning("ambiguous port %s in /etc/services", + name); +#else + ; +#endif + *proto = PROTO_UNDEF; + } + return 1; + } +#if defined(ultrix) || defined(__osf__) + /* Special hack in case NFS isn't in /etc/services */ + if (strcmp(name, "nfs") == 0) { + *port = 2049; + *proto = PROTO_UNDEF; + return 1; + } +#endif + return 0; +} + +int +pcap_nametoproto(const char *str) +{ + struct protoent *p; + + p = getprotobyname(str); + if (p != 0) + return p->p_proto; + else + return PROTO_UNDEF; +} + +#include "ethertype.h" + +struct eproto { + char *s; + u_short p; +}; + +/* Static data base of ether protocol types. */ +struct eproto eproto_db[] = { + { "pup", ETHERTYPE_PUP }, + { "xns", ETHERTYPE_NS }, + { "ip", ETHERTYPE_IP }, + { "arp", ETHERTYPE_ARP }, + { "rarp", ETHERTYPE_REVARP }, + { "sprite", ETHERTYPE_SPRITE }, + { "mopdl", ETHERTYPE_MOPDL }, + { "moprc", ETHERTYPE_MOPRC }, + { "decnet", ETHERTYPE_DN }, + { "lat", ETHERTYPE_LAT }, + { "lanbridge", ETHERTYPE_LANBRIDGE }, + { "vexp", ETHERTYPE_VEXP }, + { "vprod", ETHERTYPE_VPROD }, + { "atalk", ETHERTYPE_ATALK }, + { "atalkarp", ETHERTYPE_AARP }, + { "loopback", ETHERTYPE_LOOPBACK }, + { "decdts", ETHERTYPE_DECDTS }, + { "decdns", ETHERTYPE_DECDNS }, + { (char *)0, 0 } +}; + +int +pcap_nametoeproto(const 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) + register int c; +{ + if (isdigit(c)) + return c - '0'; + else if (islower(c)) + return c - 'a' + 10; + else + return c - 'A' + 10; +} + +u_long +__pcap_atoin(const 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 */ +} + +u_long +__pcap_atodn(const char *s) +{ +#define AREASHIFT 10 +#define AREAMASK 0176000 +#define NODEMASK 01777 + + u_long addr = 0; + u_int node, area; + + if (sscanf((char *)s, "%d.%d", &area, &node) != 2) + bpf_error("malformed decnet address '%s'", s); + + addr = (area << AREASHIFT) & AREAMASK; + addr |= (node & NODEMASK); + + return(addr); +} + +/* + * Convert 's' which has the form "xx:xx:xx:xx:xx:xx" into a new + * ethernet address. Assumes 's' is well formed. + */ +u_char * +pcap_ether_aton(const 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 +/* Roll our own */ +u_char * +pcap_ether_hostton(const char *name) +{ + register struct pcap_etherent *ep; + register u_char *ap; + static FILE *fp = NULL; + static init = 0; + + if (!init) { + fp = fopen(PCAP_ETHERS_FILE, "r"); + ++init; + if (fp == NULL) + return (NULL); + } else if (fp == NULL) + return (NULL); + else + rewind(fp); + + while ((ep = pcap_next_etherent(fp)) != NULL) { + if (strcmp(ep->name, name) == 0) { + ap = (u_char *)malloc(6); + if (ap != NULL) { + memcpy(ap, ep->addr, 6); + return (ap); + } + break; + } + } + return (NULL); +} +#else +/* Use the os supplied routines */ +u_char * +pcap_ether_hostton(const char *name) +{ + register u_char *ap; + u_char a[6]; +#ifndef sgi + extern int ether_hostton(char *, struct ether_addr *); +#endif + + ap = NULL; + if (ether_hostton((char*)name, (struct ether_addr *)a) == 0) { + ap = (u_char *)malloc(6); + if (ap != NULL) + memcpy(ap, a, 6); + } + return (ap); +} +#endif + +u_short +__pcap_nametodnaddr(const char *name) +{ +#ifdef DECNETLIB + struct nodeent *getnodebyname(); + struct nodeent *nep; + unsigned short res; + + nep = getnodebyname(name); + if (nep == ((struct nodeent *)0)) + bpf_error("unknown decnet host name '%s'\n", name); + + memcpy((char *)&res, (char *)nep->n_addr, sizeof(unsigned short)); + return(res); +#else + bpf_error("decnet name support not included, '%s' cannot be translated\n", + name); +#endif +} diff --git a/lib/libpcap/optimize.c b/lib/libpcap/optimize.c new file mode 100644 index 0000000..50eb88e --- /dev/null +++ b/lib/libpcap/optimize.c @@ -0,0 +1,1923 @@ +/* + * Copyright (c) 1988, 1989, 1990, 1991, 1993, 1994 + * 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.45 94/06/20 19:07:55 leres Exp $ (LBL)"; +#endif + +#include <sys/types.h> +#include <sys/time.h> + +#include <net/bpf.h> + +#include <stdio.h> +#ifdef __osf__ +#include <stdlib.h> +#include <malloc.h> +#endif +#include <memory.h> + +#include "gencode.h" + +#ifndef __GNUC__ +#define inline +#endif + +#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(struct block *); +static void opt_cleanup(void); + +static void make_marks(struct block *); +static void mark_code(struct block *); + +static void intern_blocks(struct block *); + +static int eq_slist(struct slist *, struct slist *); + +static void find_levels_r(struct block *); + +static void find_levels(struct block *); +static void find_dom(struct block *); +static void propedom(struct edge *); +static void find_edom(struct block *); +static void find_closure(struct block *); +static int atomuse(struct stmt *); +static int atomdef(struct stmt *); +static void compute_local_ud(struct block *); +static void find_ud(struct block *); +static void init_val(void); +static long F(int, long, long); +static inline void vstore(struct stmt *, long *, long, int); +static void opt_blk(struct block *, int); +static int use_conflict(struct block *, struct block *); +static void opt_j(struct edge *); +static void or_pullup(struct block *); +static void and_pullup(struct block *); +static void opt_blks(struct block *, int); +static inline void link_inedge(struct edge *, struct block *); +static void find_inedges(struct block *); +static void opt_root(struct block **); +static void opt_loop(struct block *, int); +static void fold_op(struct stmt *, long, long); +static inline struct slist *this_op(struct slist *); +static void opt_not(struct block *); +static void opt_peep(struct block *); +static void opt_stmt(struct stmt *, long[], int); +static void deadstmt(struct stmt *, struct stmt *[]); +static void opt_deadstores(struct block *); +static void opt_blk(struct block *, int); +static int use_conflict(struct block *, struct block *); +static void opt_j(struct edge *); +static struct block *fold_edge(struct block *, struct edge *); +static inline int eq_blk(struct block *, struct block *); +static int slength(struct slist *); +static int count_blocks(struct block *); +static void number_blks_r(struct block *); +static int count_stmts(struct block *); +static void convert_code_r(struct block *); + +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; +{ + memset((char *)levels, 0, 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 established. + */ +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. */ + memset(root->et.edom, 0, edgewords * sizeof(*(uset)0)); + memset(root->ef.edom, 0, 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. + */ + memset((char *)all_closure_sets, 0, + 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(use, atom)) + 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; + memset((char *)vmap, 0, maxval * sizeof(*vmap)); + memset((char *)hashtbl, 0, 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) + bpf_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 comparison, 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 symbolic + * 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]; + + memset((char *)last, 0, 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; + int do_stmts; +{ + 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) + memset((char *)b->val, 0, sizeof(b->val)); + else { + memcpy((char *)b->val, (char *)p->pred->val, sizeof(b->val)); + while ((p = p->next) != NULL) { + 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; +} + +static 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 equality + * 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 successor to the its grandchild. + * + * XXX We violate the set abstraction here in favor a reasonably + * 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 do_stmts; +{ + 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; + + for (i = 0; i < n_blocks; ++i) + blocks[i]->in_edges = 0; + + /* + * Traverse the graph, adding each edge to the predecessor + * list of its successors. 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; +{ + +#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 +bpf_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) + bpf_error("long jumps not supported"); + dst->jt = off; + off = JF(p)->offset - (p->offset + slen) - 1; + if (off >= 256) + bpf_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); + memset((char *)fp, 0, 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; + + memset(bids, 0, 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/lib/libpcap/pcap-bpf.c b/lib/libpcap/pcap-bpf.c new file mode 100644 index 0000000..39e6197 --- /dev/null +++ b/lib/libpcap/pcap-bpf.c @@ -0,0 +1,233 @@ +/* + * Copyright (c) 1993, 1994 + * 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.14 94/06/03 19:58:49 leres 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 "pcap-int.h" + +int +pcap_stats(pcap_t *p, struct pcap_stat *ps) +{ + struct bpf_stat s; + + if (ioctl(p->fd, BIOCGSTATS, &s) < 0) { + sprintf(p->errbuf, "BIOCGSTATS: %s", pcap_strerror(errno)); + return (-1); + } + + ps->ps_recv = s.bs_recv; + ps->ps_drop = s.bs_drop; + return (0); +} + +int +pcap_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) +{ + int cc; + int n = 0; + register u_char *bp, *ep; + + again: + cc = p->cc; + if (p->cc == 0) { + cc = read(p->fd, (char *)p->buffer, p->bufsize); + if (cc < 0) { + /* Don't choke when we get ptraced */ + switch (errno) { + + case EINTR: + goto again; + + case EWOULDBLOCK: + return (0); +#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. + */ + case EINVAL: + if ((long)(tell(p->fd) + p->bufsize) < 0) { + (void)lseek(p->fd, 0, 0); + goto again; + } + /* fall through */ +#endif + } + sprintf(p->errbuf, "read: %s", pcap_strerror(errno)); + return (-1); + } + bp = p->buffer; + } else + bp = p->bp; + + /* + * Loop through each packet. + */ +#define bhp ((struct bpf_hdr *)bp) + ep = bp + cc; + while (bp < ep) { + register int caplen, hdrlen; + caplen = bhp->bh_caplen; + hdrlen = bhp->bh_hdrlen; + /* + * XXX A bpf_hdr matches a pcap_pkthdr. + */ + (*callback)(user, (struct pcap_pkthdr*)bp, bp + hdrlen); + bp += BPF_WORDALIGN(caplen + hdrlen); + if (++n >= cnt && cnt > 0) { + p->bp = bp; + p->cc = ep - bp; + return (n); + } + } +#undef bhp + p->cc = 0; + return (n); +} + +static inline int +bpf_open(pcap_t *p, char *errbuf) +{ + 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); + + /* + * XXX better message for all minors used + */ + if (fd < 0) + sprintf(errbuf, "%s: %s", device, pcap_strerror(errno)); + + return (fd); +} + +pcap_t * +pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf) +{ + int fd; + struct ifreq ifr; + struct bpf_version bv; + u_int v; + pcap_t *p; + + p = (pcap_t *)malloc(sizeof(*p)); + if (p == NULL) { + sprintf(ebuf, "malloc: %s", pcap_strerror(errno)); + return (NULL); + } + bzero(p, sizeof(*p)); + fd = bpf_open(p, ebuf); + if (fd < 0) + goto bad; + + p->fd = fd; + p->snapshot = snaplen; + + if (ioctl(fd, BIOCVERSION, (caddr_t)&bv) < 0) { + sprintf(ebuf, "BIOCVERSION: %s", pcap_strerror(errno)); + goto bad; + } + if (bv.bv_major != BPF_MAJOR_VERSION || + bv.bv_minor < BPF_MINOR_VERSION) { + sprintf(ebuf, "kernel bpf filter out of date"); + goto bad; + } + (void)strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name)); + if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) < 0) { + sprintf(ebuf, "%s: %s", device, pcap_strerror(errno)); + goto bad; + } + /* Get the data link layer type. */ + if (ioctl(fd, BIOCGDLT, (caddr_t)&v) < 0) { + sprintf(ebuf, "BIOCGDLT: %s", pcap_strerror(errno)); + goto bad; + } + p->linktype = v; + + /* set timeout */ + if (to_ms != 0) { + struct timeval to; + to.tv_sec = to_ms / 1000; + to.tv_usec = (to_ms * 1000) % 1000000; + if (ioctl(p->fd, BIOCSRTIMEOUT, (caddr_t)&to) < 0) { + sprintf(ebuf, "BIOCSRTIMEOUT: %s", + pcap_strerror(errno)); + goto bad; + } + } + if (promisc) + /* set promiscuous mode, okay if it fails */ + (void)ioctl(p->fd, BIOCPROMISC, NULL); + + if (ioctl(fd, BIOCGBLEN, (caddr_t)&v) < 0) { + sprintf(ebuf, "BIOCGBLEN: %s", pcap_strerror(errno)); + goto bad; + } + p->bufsize = v; + p->buffer = (u_char*)malloc(p->bufsize); + if (p->buffer == NULL) { + sprintf(ebuf, "malloc: %s", pcap_strerror(errno)); + goto bad; + } + + return (p); + bad: + free(p); + return (NULL); +} + +int +pcap_setfilter(pcap_t *p, struct bpf_program *fp) +{ + if (p->sf.rfile != NULL) + p->fcode = *fp; + else if (ioctl(p->fd, BIOCSETF, (caddr_t)fp) < 0) { + sprintf(p->errbuf, "BIOCSETF: %s", pcap_strerror(errno)); + return (-1); + } + return (0); +} diff --git a/lib/libpcap/pcap-dlpi.c b/lib/libpcap/pcap-dlpi.c new file mode 100644 index 0000000..adcffcd --- /dev/null +++ b/lib/libpcap/pcap-dlpi.c @@ -0,0 +1,506 @@ +/* + * Copyright (c) 1993, 1994 + * 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. + * + * This code contributed by Atanu Ghosh (atanu@cs.ucl.ac.uk), + * University College London. + */ +#ifndef lint +static char rcsid[] = + "@(#)$Header: pcap-dlpi.c,v 1.22x 94/10/12 20:08:15 leres Exp $ (LBL)"; +#endif + +/* + * Packet capture routine for dlpi under SunOS 5 + * + * Notes: + * + * - Apparently the DLIOCRAW ioctl() is specific to SunOS. + * + * - There is a bug in bufmod(7) such that setting the snapshot + * length results in data being left of the front of the packet. + * + * - It might be desirable to use pfmod(7) to filter packets in the + * kernel. + */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/bufmod.h> +#include <sys/dlpi.h> +#include <sys/stream.h> +#include <sys/systeminfo.h> + +#include <net/bpf.h> + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <memory.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stropts.h> +#include <unistd.h> + +#include "pcap-int.h" + +#define MAXDLBUF 8192 + +/* Forwards */ +static int send_request(int, char *, int, char *, char *); +static int dlattachreq(int, u_long, char *); +static int dlinfoack(int, char *, char *); +static int dlinforeq(int, char *); +static int dlpromisconreq(int, u_long, char *); +static int dlokack(int, char *, char *); +static int strioctl(int, int, int, char *); +#ifdef SOLARIS +static char *getrelease(long *, long *, long *); +#endif + +int +pcap_stats(pcap_t *p, struct pcap_stat *ps) +{ + + *ps = p->md.stat; + return (0); +} + +int +pcap_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) +{ + register int cc, n; + register u_char *bp, *ep, *pk; + register struct bpf_insn *fcode; + register struct sb_hdr *sbp; + int flags; + struct strbuf data; + struct pcap_pkthdr pkthdr; + + flags = 0; + cc = p->cc; + if (cc == 0) { + data.buf = (char *)p->buffer; + data.maxlen = MAXDLBUF; + data.len = 0; + do { + if (getmsg(p->fd, NULL, &data, &flags) < 0) { + /* Don't choke when we get ptraced */ + if (errno == EINTR) { + cc = 0; + continue; + } + strcpy(p->errbuf, pcap_strerror(errno)); + return (-1); + } + cc = data.len; + } while (cc == 0); + bp = p->buffer; + } else + bp = p->bp; + + /* Loop through packets */ + fcode = p->fcode.bf_insns; + ep = bp + cc; + n = 0; + while (bp < ep) { + sbp = (struct sb_hdr *)bp; + p->md.stat.ps_drop += sbp->sbh_drops; + ++p->md.stat.ps_recv; + pk = bp + sizeof(*sbp); + bp += sbp->sbh_totlen; + if (bpf_filter(fcode, pk, sbp->sbh_origlen, sbp->sbh_msglen)) { + pkthdr.ts = sbp->sbh_timestamp; + pkthdr.len = sbp->sbh_origlen; + pkthdr.caplen = sbp->sbh_msglen; + (*callback)(user, &pkthdr, pk); + if (++n >= cnt && cnt >= 0) { + p->cc = ep - bp; + p->bp = bp; + return (n); + } + } + } + p->cc = 0; + return (n); +} + +pcap_t * +pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf) +{ + register pcap_t *p; + long buf[MAXDLBUF]; + int ppa; + int cppa; + register dl_info_ack_t *infop; + u_long ss, flag; +#ifdef SOLARIS + char *release; + long osmajor, osminor, osmicro; +#endif + char dname[100]; + + p = (pcap_t *)malloc(sizeof(*p)); + if (p == NULL) { + strcpy(ebuf, pcap_strerror(errno)); + return (NULL); + } + memset(p, 0, sizeof(*p)); + + /* + ** 1) In order to get the ppa take the last character of the device + ** name if it is a number then fail the open. + ** + ** 2) If the name starts with a '/' then this is an absolute pathname, + ** otherwise prepend '/dev/'. + ** + ** 3) Remove the trailing digit and try and open the device + ** not staggeringly intuitive but it should work. + ** + ** If there are more than 9 devices this code will fail. + */ + + cppa = device[strlen(device) - 1]; + if (!isdigit(cppa)) { + sprintf(ebuf, "%c is not a digit, therefore not a valid ppa", + cppa); + goto bad; + } + + dname[0] = '\0'; + if (device[0] != '/') + strcpy(dname, "/dev/"); + + strcat(dname, device); + dname[strlen(dname) - 1] = '\0'; + + if ((p->fd = open(dname, O_RDWR)) < 0) { + sprintf(ebuf, "%s: %s", dname, pcap_strerror(errno)); + goto bad; + } + p->snapshot = snaplen; + + ppa = cppa - '0'; + /* + ** Attach. + */ + if (dlattachreq(p->fd, ppa, ebuf) < 0 || + dlokack(p->fd, (char *)buf, ebuf) < 0) + goto bad; + + if (promisc) { + /* + ** enable promiscuous. + */ + if (dlpromisconreq(p->fd, DL_PROMISC_PHYS, ebuf) < 0 || + dlokack(p->fd, (char *)buf, ebuf) < 0) + goto bad; + if (dlpromisconreq(p->fd, DL_PROMISC_SAP, ebuf) < 0 || + dlokack(p->fd, (char *)buf, ebuf) < 0) + goto bad; + + /* + ** enable multicast, you would have thought promiscuous + ** would be sufficient. + */ + if (dlpromisconreq(p->fd, DL_PROMISC_MULTI, ebuf) < 0 || + dlokack(p->fd, (char *)buf, ebuf) < 0) + goto bad; + } + + /* + ** Determine link type + */ + if (dlinforeq(p->fd, ebuf) < 0 || + dlinfoack(p->fd, (char *)buf, ebuf) < 0) + goto bad; + + infop = &((union DL_primitives *)buf)->info_ack; + switch (infop->dl_mac_type) { + + case DL_ETHER: + p->linktype = DLT_EN10MB; + break; + + case DL_FDDI: + p->linktype = DLT_FDDI; + break; + + default: + sprintf(ebuf, "unknown mac type 0x%lu", infop->dl_mac_type); + goto bad; + } + +#ifdef DLIOCRAW + /* + ** This is a non standard SunOS hack to get the ethernet header. + */ + if (strioctl(p->fd, DLIOCRAW, 0, NULL) < 0) { + sprintf(ebuf, "DLIOCRAW: %s", pcap_strerror(errno)); + goto bad; + } +#endif + + /* + ** Another non standard call to get the data nicely buffered + */ + if (ioctl(p->fd, I_PUSH, "bufmod") != 0) { + sprintf(ebuf, "I_PUSH bufmod: %s", pcap_strerror(errno)); + goto bad; + } + + /* + ** Now that the bufmod is pushed lets configure it. + ** + ** There is a bug in bufmod(7). When dealing with messages of + ** less than snaplen size it strips data from the beginning not + ** the end. + ** + ** This bug is supposed to be fixed in 5.3.2. Also, there is a + ** patch available. Ask for bugid 1149065. + */ + ss = snaplen; +#ifdef SOLARIS + release = getrelease(&osmajor, &osminor, &osmicro); + if (osmajor == 5 && (osminor <= 2 || (osminor == 3 && osmicro < 2)) && + getenv("BUFMOD_FIXED") == NULL) { + fprintf(stderr, + "WARNING: bufmod is broken in SunOS %s; ignoring snaplen.\n", + release); + ss = 0; + } +#endif + if (ss > 0 && + strioctl(p->fd, SBIOCSSNAP, sizeof(u_long), (char *)&ss) != 0) { + sprintf(ebuf, "SBIOCSSNAP: %s", pcap_strerror(errno)); + goto bad; + } + + /* + ** Set up the bufmod flags + */ + if (strioctl(p->fd, SBIOCGFLAGS, sizeof(u_long), (char *)&flag) < 0) { + sprintf(ebuf, "SBIOCGFLAGS: %s", pcap_strerror(errno)); + goto bad; + } + flag |= SB_NO_DROPS; + if (strioctl(p->fd, SBIOCSFLAGS, sizeof(u_long), (char *)&flag) != 0) { + sprintf(ebuf, "SBIOCSFLAGS: %s", pcap_strerror(errno)); + goto bad; + } + /* + ** Set up the bufmod timeout + */ + if (to_ms != 0) { + struct timeval to; + + to.tv_sec = to_ms / 1000; + to.tv_usec = (to_ms * 1000) % 1000000; + if (strioctl(p->fd, SBIOCSTIME, sizeof(to), (char *)&to) != 0) { + sprintf(ebuf, "SBIOCSTIME: %s", pcap_strerror(errno)); + goto bad; + } + } + + /* + ** As the last operation flush the read side. + */ + if (ioctl(p->fd, I_FLUSH, FLUSHR) != 0) { + sprintf(ebuf, "FLUSHR: %s", pcap_strerror(errno)); + goto bad; + } + /* Allocate data buffer */ + p->bufsize = MAXDLBUF * sizeof(long); + p->buffer = (u_char *)malloc(p->bufsize); + return (p); +bad: + free(p); + return (NULL); +} + +int +pcap_setfilter(pcap_t *p, struct bpf_program *fp) +{ + + p->fcode = *fp; + return (0); +} + +static int +send_request(int fd, char *ptr, int len, char *what, char *ebuf) +{ + struct strbuf ctl; + int flags; + + ctl.maxlen = 0; + ctl.len = len; + ctl.buf = ptr; + + flags = 0; + if (putmsg(fd, &ctl, (struct strbuf *) NULL, flags) < 0) { + sprintf(ebuf, "putmsg \"%s\"failed: %s", + what, pcap_strerror(errno)); + return (-1); + } + return (0); +} + +static int +dlattachreq(int fd, u_long ppa, char *ebuf) +{ + dl_attach_req_t req; + + req.dl_primitive = DL_ATTACH_REQ; + req.dl_ppa = ppa; + + return (send_request(fd, (char *)&req, sizeof(req), "attach", ebuf)); +} + +static int +dlpromisconreq(int fd, u_long level, char *ebuf) +{ + dl_promiscon_req_t req; + + req.dl_primitive = DL_PROMISCON_REQ; + req.dl_level = level; + + return (send_request(fd, (char *)&req, sizeof(req), "promiscon", ebuf)); +} + +static int +dlokack(int fd, char *bufp, char *ebuf) +{ + union DL_primitives *dlp; + struct strbuf ctl; + int flags; + + ctl.maxlen = MAXDLBUF; + ctl.len = 0; + ctl.buf = bufp; + + flags = 0; + if (getmsg(fd, &ctl, (struct strbuf*)NULL, &flags) < 0) { + sprintf(ebuf, "getmsg: %s", pcap_strerror(errno)); + return (-1); + } + + dlp = (union DL_primitives *) ctl.buf; + + if (dlp->dl_primitive != DL_OK_ACK) { + sprintf(ebuf, "dlokack unexpected primitive %d", + dlp->dl_primitive); + return (-1); + } + + if (ctl.len != sizeof(dl_ok_ack_t)) { + sprintf(ebuf, "dlokack incorrect size returned"); + return (-1); + } + return (0); +} + + +static int +dlinforeq(int fd, char *ebuf) +{ + dl_info_req_t req; + + req.dl_primitive = DL_INFO_REQ; + + return (send_request(fd, (char *)&req, sizeof(req), "info", ebuf)); +} + +static int +dlinfoack(int fd, char *bufp, char *ebuf) +{ + union DL_primitives *dlp; + struct strbuf ctl; + int flags; + + ctl.maxlen = MAXDLBUF; + ctl.len = 0; + ctl.buf = bufp; + + flags = 0; + if (getmsg(fd, &ctl, (struct strbuf *)NULL, &flags) < 0) { + sprintf(ebuf, "dlinfoack: getmsg: %s", pcap_strerror(errno)); + return (-1); + } + + dlp = (union DL_primitives *) ctl.buf; + + if (dlp->dl_primitive != DL_INFO_ACK) { + sprintf(ebuf, "dlinfoack: unexpected primitive %ld", + dlp->dl_primitive); + return (-1); + } + + /* Extra stuff like the broadcast address can be returned */ + if (ctl.len < DL_INFO_ACK_SIZE) { + sprintf(ebuf, "dlinfoack: incorrect size returned"); + return (-1); + } + return (0); +} + +static int +strioctl(int fd, int cmd, int len, char *dp) +{ + struct strioctl str; + int rc; + + str.ic_cmd = cmd; + str.ic_timout = -1; + str.ic_len = len; + str.ic_dp = dp; + rc = ioctl(fd, I_STR, &str); + + if (rc < 0) + return (rc); + else + return (str.ic_len); +} + +#ifdef SOLARIS +static char * +getrelease(long *majorp, long *minorp, long *microp) +{ + char *cp; + static char buf[32]; + + *majorp = 0; + *minorp = 0; + *microp = 0; + if (sysinfo(SI_RELEASE, buf, sizeof(buf)) < 0) + return ("?"); + cp = buf; + if (!isdigit(*cp)) + return (buf); + *majorp = strtol(cp, &cp, 10); + if (*cp++ != '.') + return (buf); + *minorp = strtol(cp, &cp, 10); + if (*cp++ != '.') + return (buf); + *microp = strtol(cp, &cp, 10); + return (buf); +} +#endif diff --git a/lib/libpcap/pcap-enet.c b/lib/libpcap/pcap-enet.c new file mode 100644 index 0000000..3f4189b --- /dev/null +++ b/lib/libpcap/pcap-enet.c @@ -0,0 +1,227 @@ +/* + * Stanford Enetfilter subroutines for tcpdump + * + * Based on the MERIT NNstat etherifrt.c and the Ultrix pcap-pf.c + * subroutines. + * + * Rayan Zachariassen, CA*Net + */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/file.h> +#include <sys/ioctl.h> +#include <sys/socket.h> + +#include <net/if.h> +#include <net/bpf.h> +#include <net/enet.h> + +#include <netinet/in.h> +#include <netinet/if_ether.h> + +#include <stdio.h> +#include <errno.h> + +#include "interface.h" + +struct packet_header { +#ifdef IBMRTPC + struct LengthWords length; + struct tap_header tap; +#endif /* IBMRTPC */ + u_char packet[8] +}; + +extern int errno; + +#define BUFSPACE (4*1024) + +/* Forwards */ +static void efReadError(int, char *); + +void +readloop(int cnt, int if_fd, struct bpf_program *fp, printfunc printit) +{ +#ifdef IBMRTPC + register struct packet_header *ph; + register u_char *bp; + register int inc; +#else /* !IBMRTPC */ + static struct timeval tv = { 0 }; +#endif /* IBMRTPC */ + register int cc, caplen; + register struct bpf_insn *fcode = fp->bf_insns; + union { + struct packet_header hdr; + u_char p[BUFSPACE]; + u_short s; + } buf; + + while (1) { + if ((cc = read(if_fd, (char *)buf.p, sizeof(buf))) < 0) + efReadError(if_fd, "reader"); + +#ifdef IBMRTPC + /* + * Loop through each packet. + */ + bp = buf.p; + while (cc > 0) { + ph = (struct packet_header *)bp; + caplen = ph->tap.th_wirelen > snaplen ? snaplen : ph->tap +.th_wirelen ; + if (bpf_filter(fcode, (char *)ph->packet, + ph->tap.th_wirelen, caplen)) { + if (cnt >= 0 && --cnt < 0) + goto out; + (*printit)((char *)ph->packet, + (struct timeval *)ph->tap.th_timestamp, + ph->tap.th_wirelen, caplen); + } + inc = ph->length.PacketOffset; + cc -= inc; + bp += inc; + } +#else /* !IBMRTPC */ + caplen = cc > snaplen ? snaplen : cc ; + if (bpf_filter(fcode, buf.hdr.packet, cc, caplen)) { + if (cnt >= 0 && --cnt < 0) + goto out; + (*printit)(buf.hdr.packet, &tv, cc, caplen); + } +#endif /* IBMRTPC */ + } + out: + wrapup(if_fd); +} + +/* Call ONLY if read() has returned an error on packet filter */ +static void +efReadError(int fid, char *msg) +{ + if (errno == EINVAL) { /* read MAXINT bytes already! */ + if (lseek(fid, 0, 0) < 0) { + perror("tcpdump: efReadError/lseek"); + exit(-1); + } + else + return; + } + else { + (void) fprintf(stderr, "tcpdump: "); + perror(msg); + exit(-1); + } +} + +void +wrapup(int fd) +{ +#ifdef IBMRTPC + struct enstats es; + + if (ioctl(fd, EIOSTATS, &es) == -1) { + perror("tcpdump: enet ioctl EIOSTATS error"); + exit(-1); + } + + fprintf(stderr, "%d packets queued", es.enStat_Rcnt); + if (es.enStat_Rdrops > 0) + fprintf(stderr, ", %d dropped", es.enStat_Rdrops); + if (es.enStat_Reads > 0) + fprintf(stderr, ", %d tcpdump %s", es.enStat_Reads, + es.enStat_Reads > 1 ? "reads" : "read"); + if (es.enStat_MaxRead > 1) + fprintf(stderr, ", %d packets in largest read", + es.enStat_MaxRead); + putc('\n', stderr); +#endif /* IBMRTPC */ + close(fd); +} + +int +initdevice(char *device, int pflag, int *linktype) +{ + struct eniocb ctl; + struct enfilter filter; + u_int maxwaiting; + int if_fd; + +#ifdef IBMRTPC + GETENETDEVICE(0, O_RDONLY, &if_fd); +#else /* !IBMRTPC */ + if_fd = open("/dev/enet", O_RDONLY, 0); +#endif /* IBMRTPC */ + + if (if_fd == -1) { + perror("tcpdump: enet open error"); + error( +"your system may not be properly configured; see \"man enet(4)\""); + exit(-1); + } + + /* Get operating parameters. */ + + if (ioctl(if_fd, EIOCGETP, (char *)&ctl) == -1) { + perror("tcpdump: enet ioctl EIOCGETP error"); + exit(-1); + } + + /* Set operating parameters. */ + +#ifdef IBMRTPC + ctl.en_rtout = 1 * ctl.en_hz; + ctl.en_tr_etherhead = 1; + ctl.en_tap_network = 1; + ctl.en_multi_packet = 1; + ctl.en_maxlen = BUFSPACE; +#else /* !IBMRTPC */ + ctl.en_rtout = 64; /* randomly picked value for HZ */ +#endif /* IBMRTPC */ + if (ioctl(if_fd, EIOCSETP, &ctl) == -1) { + perror("tcpdump: enet ioctl EIOCSETP error"); + exit(-1); + } + + /* Flush the receive queue, since we've changed + the operating parameters and we otherwise might + receive data without headers. */ + + if (ioctl(if_fd, EIOCFLUSH) == -1) { + perror("tcpdump: enet ioctl EIOCFLUSH error"); + exit(-1); + } + + /* Set the receive queue depth to its maximum. */ + + maxwaiting = ctl.en_maxwaiting; + if (ioctl(if_fd, EIOCSETW, &maxwaiting) == -1) { + perror("tcpdump: enet ioctl EIOCSETW error"); + exit(-1); + } + +#ifdef IBMRTPC + /* Clear statistics. */ + + if (ioctl(if_fd, EIOCLRSTAT, 0) == -1) { + perror("tcpdump: enet ioctl EIOCLRSTAT error"); + exit(-1); + } +#endif /* IBMRTPC */ + + /* Set the filter (accept all packets). */ + + filter.enf_Priority = 3; + filter.enf_FilterLen = 0; + if (ioctl(if_fd, EIOCSETF, &filter) == -1) { + perror("tcpdump: enet ioctl EIOCSETF error"); + exit(-1); + } + /* + * "enetfilter" supports only ethernets. + */ + *linktype = DLT_EN10MB; + + return(if_fd); +} diff --git a/lib/libpcap/pcap-int.h b/lib/libpcap/pcap-int.h new file mode 100644 index 0000000..1f5da22 --- /dev/null +++ b/lib/libpcap/pcap-int.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 1994 + * 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 the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the Computer Systems + * Engineering Group at Lawrence Berkeley Laboratory. + * 4. Neither the name of the University nor of the Laboratory may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#) $Header: pcap-int.h,v 1.7 94/06/14 20:03:33 leres Exp $ (LBL) + */ + +#ifndef pcap_int_h +#define pcap_int_h + +#include <pcap.h> + +/* + * Savefile + */ +struct pcap_sf { + FILE *rfile; + int swapped; + int version_major; + int version_minor; + u_char *base; +}; + +struct pcap_md { + struct pcap_stat stat; +#ifdef PCAP_PF + int use_bpf; + u_long TotPkts; /* can't oflow for 79 hrs on ether */ + u_long TotAccepted; /* count accepted by filter */ + u_long TotDrops; /* count of dropped packets */ + long TotMissed; /* missed by i/f during this run */ + long OrigMissed; /* missed by i/f before this run */ +#endif +}; + +struct pcap { + int fd; + int snapshot; + int linktype; + int tzoff; /* timezone offset */ + + struct pcap_sf sf; + struct pcap_md md; + + /* + * Read buffer. + */ + int bufsize; + u_char *buffer; + u_char *bp; + int cc; + + /* + * Place holder for pcap_next(). + */ + u_char *pkt; + + + /* + * Placeholder for filter code if bpf not in kernel. + */ + struct bpf_program fcode; + + char errbuf[PCAP_ERRBUF_SIZE]; +}; + +/* XXX should these be in pcap.h? */ +int pcap_offline_read(pcap_t *, int, pcap_handler, u_char *); +int pcap_read(pcap_t *, int cnt, pcap_handler, u_char *); +#endif diff --git a/lib/libpcap/pcap-namedb.h b/lib/libpcap/pcap-namedb.h new file mode 100644 index 0000000..3471227 --- /dev/null +++ b/lib/libpcap/pcap-namedb.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 1994 + * 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 the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the Computer Systems + * Engineering Group at Lawrence Berkeley Laboratory. + * 4. Neither the name of the University nor of the Laboratory may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#) $Header: pcap-namedb.h,v 1.2 94/06/14 20:03:34 leres Exp $ (LBL) + */ + +#ifndef lib_pcap_ethers_h +#define lib_pcap_ethers_h + +/* + * As returned by the pcap_next_etherent() + * XXX this stuff doesn't belong in this inteface, but this + * library already must do name to address translation, so + * on systems that don't have support for /etc/ethers, we + * export these hooks since they'll + */ +struct pcap_etherent { + u_char addr[6]; + char name[122]; +}; +#ifndef PCAP_ETHERS_FILE +#define PCAP_ETHERS_FILE "/etc/ethers" +#endif +struct pcap_etherent *pcap_next_etherent(FILE *); +u_char *pcap_ether_hostton(const char*); +u_char *pcap_ether_aton(const char *); + +u_long **pcap_nametoaddr(const char *); +u_long pcap_nametonetaddr(const char *); + +int pcap_nametoport(const char *, int *, int *); +int pcap_nametoproto(const char *); +int pcap_nametoeproto(const char *); +/* + * If a protocol is unknown, PROTO_UNDEF is returned. + * Also, pcap_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 + +/* XXX move these to pcap-int.h? */ +u_long __pcap_atodn(const char *); +u_long __pcap_atoin(const char *); +u_short __pcap_nametodnaddr(const char *); + +#endif diff --git a/lib/libpcap/pcap-nit.c b/lib/libpcap/pcap-nit.c new file mode 100644 index 0000000..125e14f --- /dev/null +++ b/lib/libpcap/pcap-nit.c @@ -0,0 +1,240 @@ +/* + * Copyright (c) 1990, 1991, 1992, 1993, 1994 + * 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-nit.c,v 1.24 94/02/10 23:02:37 leres Exp $ (LBL)"; +#endif + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/timeb.h> +#include <sys/file.h> +#include <sys/ioctl.h> +#include <sys/socket.h> + +#include <net/bpf.h> +#include <net/if.h> +#include <net/nit.h> + +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/if_ether.h> +#include <netinet/ip_var.h> +#include <netinet/udp.h> +#include <netinet/udp_var.h> +#include <netinet/tcp.h> +#include <netinet/tcpip.h> + +#include <ctype.h> +#include <errno.h> +#include <stdio.h> + +#include "pcap-int.h" + +/* + * The chunk size for NIT. This is the amount of buffering + * done for read calls. + */ +#define CHUNKSIZE (2*1024) + +/* + * The total buffer space used by NIT. + */ +#define BUFSPACE (4*CHUNKSIZE) + +/* Forwards */ +static int nit_setflags(int, int, int, char *); + +int +pcap_stats(pcap_t *p, struct pcap_stat *ps) +{ + + *ps = p->md.stat; + return (0); +} + +int +pcap_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) +{ + register int cc, n; + register struct bpf_insn *fcode = p->fcode.bf_insns; + register u_char *bp, *cp, *ep; + register struct nit_hdr *nh; + register int caplen; + + cc = p->cc; + if (cc == 0) { + cc = read(p->fd, (char *)p->buffer, p->bufsize); + if (cc < 0) { + if (errno == EWOULDBLOCK) + return (0); + sprintf(p->errbuf, "pcap_read: %s", + pcap_strerror(errno)); + return (-1); + } + bp = p->buffer; + } else + bp = p->bp; + + /* + * Loop through each packet. The increment expression + * rounds up to the next int boundary past the end of + * the previous packet. + */ + n = 0; + ep = bp + cc; + while (bp < ep) { + nh = (struct nit_hdr *)bp; + cp = bp + sizeof(*nh); + + switch (nh->nh_state) { + + case NIT_CATCH: + break; + + case NIT_NOMBUF: + case NIT_NOCLUSTER: + case NIT_NOSPACE: + p->md.stat.ps_drop = nh->nh_dropped; + continue; + + case NIT_SEQNO: + continue; + + default: + sprintf(p->errbuf, "bad nit state %d", nh->nh_state); + return (-1); + } + ++p->md.stat.ps_recv; + bp += ((sizeof(struct nit_hdr) + nh->nh_datalen + + sizeof(int) - 1) & ~(sizeof(int) - 1)); + + caplen = nh->nh_wirelen; + if (caplen > p->snapshot) + caplen = p->snapshot; + if (bpf_filter(fcode, cp, nh->nh_wirelen, caplen)) { + struct pcap_pkthdr h; + h.ts = nh->nh_timestamp; + h.len = nh->nh_wirelen; + h.caplen = caplen; + (*callback)(user, &h, cp); + if (++n >= cnt && cnt >= 0) { + p->cc = ep - bp; + p->bp = bp; + return (n); + } + } + } + p->cc = 0; + return (n); +} + +static int +nit_setflags(int fd, int promisc, int to_ms, char *ebuf) +{ + struct nit_ioc nioc; + + bzero((char *)&nioc, sizeof(nioc)); + nioc.nioc_bufspace = BUFSPACE; + nioc.nioc_chunksize = CHUNKSIZE; + nioc.nioc_typetomatch = NT_ALLTYPES; + nioc.nioc_snaplen = p->snapshot; + nioc.nioc_bufalign = sizeof(int); + nioc.nioc_bufoffset = 0; + + if (to_ms != 0) { + nioc.nioc_flags |= NF_TIMEOUT; + nioc.nioc_timeout.tv_sec = to_ms / 1000; + nioc.nioc_timeout.tv_usec = (to_ms * 1000) % 1000000; + } + if (promisc) + nioc.nioc_flags |= NF_PROMISC; + + if (ioctl(fd, SIOCSNIT, &nioc) < 0) { + sprintf(ebuf, "SIOCSNIT: %s", pcap_strerror(errno)); + return (-1); + } + return (0); +} + +pcap_t * +pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf) +{ + int fd; + struct sockaddr_nit snit; + register pcap_t *p; + + p = (pcap_t *)malloc(sizeof(*p)); + if (p == NULL) { + strcpy(ebuf, pcap_strerror(errno)); + return (NULL); + } + + if (snaplen < 96) + /* + * NIT requires a snapshot length of at least 96. + */ + snaplen = 96; + + bzero(p, sizeof(*p)); + p->fd = fd = socket(AF_NIT, SOCK_RAW, NITPROTO_RAW); + if (fd < 0) { + sprintf(ebuf, "socket: %s", pcap_strerror(errno)); + goto bad; + } + snit.snit_family = AF_NIT; + (void)strncpy(snit.snit_ifname, device, NITIFSIZ); + + if (bind(fd, (struct sockaddr *)&snit, sizeof(snit))) { + sprintf(ebuf, "bind: %s: %s", snit.snit_ifname, + pcap_strerror(errno)); + goto bad; + } + p->snapshot = snaplen; + nit_setflags(p->fd, promisc, to_ms, ebuf); + + /* + * NIT supports only ethernets. + */ + p->linktype = DLT_EN10MB; + + p->bufsize = BUFSPACE; + p->buffer = (u_char *)malloc(p->bufsize); + if (p->buffer == NULL) { + strcpy(ebuf, pcap_strerror(errno)); + goto bad; + } + return (p); + bad: + if (fd >= 0) + close(fd); + free(p); + return (NULL); +} + +int +pcap_setfilter(pcap_t *p, struct bpf_program *fp) +{ + + p->fcode = *fp; + return (0); +} diff --git a/lib/libpcap/pcap-nit.h b/lib/libpcap/pcap-nit.h new file mode 100644 index 0000000..dc1c9e8 --- /dev/null +++ b/lib/libpcap/pcap-nit.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 1990, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Lawrence Berkeley Laboratory, + * Berkeley, CA. The name of the University may not 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 MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * @(#) $Header: pcap-nit.h,v 1.2 94/06/14 20:06:03 leres Exp $ (LBL) + */ diff --git a/lib/libpcap/pcap-pf.c b/lib/libpcap/pcap-pf.c new file mode 100644 index 0000000..483dc97 --- /dev/null +++ b/lib/libpcap/pcap-pf.c @@ -0,0 +1,310 @@ +/* + * Copyright (c) 1990, 1991, 1992, 1993, 1994 + * 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-pf.c,v 1.32 94/06/10 17:41:01 mccanne Exp $ (LBL)"; +#endif + +/* + * packet filter subroutines for tcpdump + * Extraction/creation by Jeffrey Mogul, DECWRL + * + * Extracted from tcpdump.c. + */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/timeb.h> +#include <sys/socket.h> +#include <sys/file.h> +#include <sys/ioctl.h> +#include <net/pfilt.h> + +#include <net/if.h> +#include <net/bpf.h> + +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/if_ether.h> +#include <netinet/ip_var.h> +#include <netinet/udp.h> +#include <netinet/udp_var.h> +#include <netinet/tcp.h> +#include <netinet/tcpip.h> + +#include <ctype.h> +#include <errno.h> +#include <netdb.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "pcap-int.h" + +/* + * BUFSPACE is the size in bytes of the packet read buffer. Most tcpdump + * applications aren't going to need more than 200 bytes of packet header + * and the read shouldn't return more packets than packetfilter's internal + * queue limit (bounded at 256). + */ +#define BUFSPACE (200*256) + +int +pcap_read(pcap_t *pc, int cnt, pcap_handler callback, u_char *user) +{ + u_char *p; + struct bpf_insn *fcode; + int cc; + register u_char *bp; + int buflen, inc; + struct enstamp stamp; + int n; + fcode = pc->md.use_bpf ? 0 : pc->fcode.bf_insns; + again: + cc = pc->cc; + if (cc == 0) { + cc = read(pc->fd, (char *)pc->buffer, pc->bufsize); + if (cc < 0) { + if (errno == EWOULDBLOCK) + return (0); + if (errno == EINVAL && + (long)(tell(pc->fd) + pc->bufsize) < 0) { + /* + * Due to a kernel bug, after 2^31 bytes, + * the kernel file offset overflows and + * read fails with EINVAL. The lseek() + * to 0 will fix things. + */ + (void)lseek(pc->fd, 0L, 0); + goto again; + } + sprintf(pc->errbuf, "pf read: %s", + pcap_strerror(errno)); + return (-1); + } + bp = pc->buffer; + } else + bp = pc->bp; + /* + * Loop through each packet. + */ + n = 0; + while (cc > 0) { + /* avoid alignment issues here */ + bcopy((char *)bp, (char *)&stamp, sizeof(stamp)); + if (stamp.ens_stamplen != sizeof(stamp)) + /* buffer is garbage, treat it as poison */ + break; + + p = bp + stamp.ens_stamplen; + + buflen = stamp.ens_count; + if (buflen > pc->snapshot) + buflen = pc->snapshot; + + /* + * Short-circuit evaluation: if using BPF filter + * in kernel, no need to do it now. + */ + if (fcode == 0 || + bpf_filter(fcode, p, stamp.ens_count, buflen)) { + struct pcap_pkthdr h; + pc->md.TotAccepted++; + h.ts = stamp.ens_tstamp; + h.len = stamp.ens_count; + h.caplen = buflen; + (*callback)(user, &h, p); + if (++n >= cnt && cnt > 0) { + inc = ENALIGN(buflen + stamp.ens_stamplen); + cc -= inc; + bp += inc; + pc->cc = cc; + pc->bp = bp; + return (n); + } + } + pc->md.TotPkts++; + pc->md.TotDrops += stamp.ens_dropped; + pc->md.TotMissed = stamp.ens_ifoverflows; + if (pc->md.OrigMissed < 0) + pc->md.OrigMissed = pc->md.TotMissed; + + inc = ENALIGN(buflen + stamp.ens_stamplen); + cc -= inc; + bp += inc; + } + pc->cc = 0; + return (n); +} + +int +pcap_stats(pcap_t *p, struct pcap_stat *ps) +{ + ps->ps_recv = p->md.TotAccepted; + ps->ps_drop = p->md.TotDrops; + ps->ps_ifdrop = p->md.TotMissed - p->md.OrigMissed; + return (0); +} + +pcap_t * +pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf) +{ + pcap_t *p; + short enmode; + int backlog = -1; /* request the most */ + struct enfilter Filter; + struct endevp devparams; + + p = (pcap_t *)malloc(sizeof(*p)); + if (p == 0) { + strcpy(ebuf, "no swap"); + return (0); + } + bzero(p, sizeof(*p)); + p->fd = pfopen(device, 0); + if (p->fd < 0) { + sprintf(ebuf, "pf open: %s: %s\n\ +your system may not be properly configured; see \"man packetfilter(4)\"\n", + device, pcap_strerror(errno)); + goto bad; + } + p->md.OrigMissed = -1; + enmode = ENTSTAMP|ENBATCH|ENNONEXCL; + if (promisc) + enmode |= ENPROMISC; + if (ioctl(p->fd, EIOCMBIS, (caddr_t)&enmode) < 0) { + sprintf(ebuf, "EIOCMBIS: %s", pcap_strerror(errno)); + goto bad; + } +#ifdef ENCOPYALL + /* Try to set COPYALL mode so that we see packets to ourself */ + enmode = ENCOPYALL; + (void)ioctl(p->fd, EIOCMBIS, (caddr_t)&enmode);/* OK if this fails */ +#endif + /* set the backlog */ + if (ioctl(p->fd, EIOCSETW, (caddr_t)&backlog) < 0) { + sprintf(ebuf, "EIOCSETW: %s", pcap_strerror(errno)); + goto bad; + } + /* set truncation */ + if (ioctl(p->fd, EIOCTRUNCATE, (caddr_t)&snaplen) < 0) { + sprintf(ebuf, "EIOCTRUNCATE: %s", pcap_strerror(errno)); + goto bad; + } + p->snapshot = snaplen; + /* accept all packets */ + Filter.enf_Priority = 37; /* anything > 2 */ + Filter.enf_FilterLen = 0; /* means "always true" */ + if (ioctl(p->fd, EIOCSETF, (caddr_t)&Filter) < 0) { + sprintf(ebuf, "EIOCSETF: %s", pcap_strerror(errno)); + goto bad; + } + /* discover interface type */ + if (ioctl(p->fd, EIOCDEVP, (caddr_t)&devparams) < 0) { + sprintf(ebuf, "EIOCDEVP: %s", pcap_strerror(errno)); + goto bad; + } + /* HACK: to compile prior to Ultrix 4.2 */ +#ifndef ENDT_FDDI +#define ENDT_FDDI 4 +#endif + switch (devparams.end_dev_type) { + case ENDT_10MB: + p->linktype = DLT_EN10MB; + break; + + case ENDT_FDDI: + p->linktype = DLT_FDDI; + break; + + default: + /* + * XXX + * Currently, the Ultrix packet filter supports only + * Ethernet and FDDI. Eventually, support for SLIP and PPP + * (and possibly others: T1?) should be added. + */ +#ifdef notdef + warning( + "Packet filter data-link type %d unknown, assuming Ethernet", + devparams.end_dev_type); +#endif + p->linktype = DLT_EN10MB; + break; + } + + if (to_ms != 0) { + struct timeval timeout; + timeout.tv_sec = to_ms / 1000; + timeout.tv_usec = (to_ms * 1000) % 1000000; + if (ioctl(p->fd, EIOCSRTIMEOUT, (caddr_t)&timeout) < 0) { + sprintf(ebuf, "EIOCSRTIMEOUT: %s", + pcap_strerror(errno)); + goto bad; + } + } + p->bufsize = BUFSPACE; + p->buffer = (u_char*)malloc(p->bufsize); + + return (p); + bad: + free(p); + return (0); +} + +int +pcap_setfilter(pcap_t *p, struct bpf_program *fp) +{ + /* + * See if BIOCSETF works. If it does, the kernel supports + * BPF-style filters, and we do not need to do post-filtering. + */ + p->md.use_bpf = (ioctl(p->fd, BIOCSETF, (caddr_t)fp) >= 0); + if (p->md.use_bpf) { + struct bpf_version bv; + + if (ioctl(p->fd, BIOCVERSION, (caddr_t)&bv) < 0) { + sprintf(p->errbuf, "BIOCVERSION: %s", + pcap_strerror(errno)); + return (-1); + } + else if (bv.bv_major != BPF_MAJOR_VERSION || + bv.bv_minor < BPF_MINOR_VERSION) { + fprintf(stderr, + "requires bpf language %d.%d or higher; kernel is %d.%d", + BPF_MAJOR_VERSION, BPF_MINOR_VERSION, + bv.bv_major, bv.bv_minor); + /* don't give up, just be inefficient */ + p->md.use_bpf = 0; + } + } else + p->fcode = *fp; + + /*XXX this goes in tcpdump*/ + if (p->md.use_bpf) + fprintf(stderr, "tcpdump: Using kernel BPF filter\n"); + else + fprintf(stderr, "tcpdump: Filtering in user process\n"); + return (0); +} diff --git a/lib/libpcap/pcap-pf.h b/lib/libpcap/pcap-pf.h new file mode 100644 index 0000000..65ca067 --- /dev/null +++ b/lib/libpcap/pcap-pf.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 1990, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Lawrence Berkeley Laboratory, + * Berkeley, CA. The name of the University may not 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 MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * @(#) $Header: pcap-pf.h,v 1.2 94/06/14 20:06:33 leres Exp $ (LBL) + */ diff --git a/lib/libpcap/pcap-snit.c b/lib/libpcap/pcap-snit.c new file mode 100644 index 0000000..562b965 --- /dev/null +++ b/lib/libpcap/pcap-snit.c @@ -0,0 +1,293 @@ +/* + * Copyright (c) 1990, 1991, 1992, 1993, 1994 + * 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-snit.c,v 1.33 94/06/23 13:51:17 leres Exp $ (LBL)"; +#endif + +/* + * Modifications made to accomodate the new SunOS4.0 NIT facility by + * Micky Liu, micky@cunixc.cc.columbia.edu, Columbia University in May, 1989. + * This module now handles the STREAMS based NIT. + */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/timeb.h> +#include <sys/dir.h> +#include <sys/fcntlcom.h> +#include <sys/file.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/stropts.h> + +#include <net/bpf.h> +#include <net/if.h> +#include <net/nit.h> +#include <net/nit_if.h> +#include <net/nit_pf.h> +#include <net/nit_buf.h> + +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/if_ether.h> +#include <netinet/ip_var.h> +#include <netinet/udp.h> +#include <netinet/udp_var.h> +#include <netinet/tcp.h> +#include <netinet/tcpip.h> + +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> + +#include "pcap-int.h" + +/* + * The chunk size for NIT. This is the amount of buffering + * done for read calls. + */ +#define CHUNKSIZE (2*1024) + +/* + * The total buffer space used by NIT. + */ +#define BUFSPACE (4*CHUNKSIZE) + +/* Forwards */ +static int nit_setflags(int, int, int, char *); + +int +pcap_stats(pcap_t *p, struct pcap_stat *ps) +{ + + *ps = p->md.stat; + return (0); +} + +int +pcap_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) +{ + register int cc, n; + register struct bpf_insn *fcode = p->fcode.bf_insns; + register u_char *bp, *cp, *ep; + register struct nit_bufhdr *hdrp; + register struct nit_iftime *ntp; + register struct nit_iflen *nlp; + register struct nit_ifdrops *ndp; + register int caplen; + + cc = p->cc; + if (cc == 0) { + cc = read(p->fd, (char *)p->buffer, p->bufsize); + if (cc < 0) { + if (errno == EWOULDBLOCK) + return (0); + sprintf(p->errbuf, "pcap_read: %s", + pcap_strerror(errno)); + return (-1); + } + bp = p->buffer; + } else + bp = p->bp; + + /* + * loop through each snapshot in the chunk + */ + n = 0; + ep = bp + cc; + while (bp < ep) { + ++p->md.stat.ps_recv; + cp = bp; + + /* get past NIT buffer */ + hdrp = (struct nit_bufhdr *)cp; + cp += sizeof(*hdrp); + + /* get past NIT timer */ + ntp = (struct nit_iftime *)cp; + cp += sizeof(*ntp); + + ndp = (struct nit_ifdrops *)cp; + p->md.stat.ps_drop = ndp->nh_drops; + cp += sizeof *ndp; + + /* get past packet len */ + nlp = (struct nit_iflen *)cp; + cp += sizeof(*nlp); + + /* next snapshot */ + bp += hdrp->nhb_totlen; + + caplen = nlp->nh_pktlen; + if (caplen > p->snapshot) + caplen = p->snapshot; + + if (bpf_filter(fcode, cp, nlp->nh_pktlen, caplen)) { + struct pcap_pkthdr h; + h.ts = ntp->nh_timestamp; + h.len = nlp->nh_pktlen; + h.caplen = caplen; + (*callback)(user, &h, cp); + if (++n >= cnt && cnt >= 0) { + p->cc = ep - bp; + p->bp = bp; + return (n); + } + } + } + p->cc = 0; + return (n); +} + +static int +nit_setflags(int fd, int promisc, int to_ms, char *ebuf) +{ + u_long flags; + struct strioctl si; + struct timeval timeout; + + si.ic_timout = INFTIM; + if (to_ms != 0) { + timeout.tv_sec = to_ms / 1000; + timeout.tv_usec = (to_ms * 1000) % 1000000; + si.ic_cmd = NIOCSTIME; + si.ic_len = sizeof(timeout); + si.ic_dp = (char *)&timeout; + if (ioctl(fd, I_STR, (char *)&si) < 0) { + sprintf(ebuf, "NIOCSTIME: %s", pcap_strerror(errno)); + return (-1); + } + } + flags = NI_TIMESTAMP | NI_LEN | NI_DROPS; + if (promisc) + flags |= NI_PROMISC; + si.ic_cmd = NIOCSFLAGS; + si.ic_len = sizeof(flags); + si.ic_dp = (char *)&flags; + if (ioctl(fd, I_STR, (char *)&si) < 0) { + sprintf(ebuf, "NIOCSFLAGS: %s", pcap_strerror(errno)); + return (-1); + } + return (0); +} + +pcap_t * +pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf) +{ + struct strioctl si; /* struct for ioctl() */ + struct ifreq ifr; /* interface request struct */ + int chunksize = CHUNKSIZE; + int fd; + static char dev[] = "/dev/nit"; + register pcap_t *p; + + p = (pcap_t *)malloc(sizeof(*p)); + if (p == NULL) { + strcpy(ebuf, pcap_strerror(errno)); + return (NULL); + } + + if (snaplen < 96) + /* + * NIT requires a snapshot length of at least 96. + */ + snaplen = 96; + + bzero(p, sizeof(*p)); + p->fd = fd = open(dev, O_RDONLY); + if (fd < 0) { + sprintf(ebuf, "%s: %s", dev, pcap_strerror(errno)); + goto bad; + } + + /* arrange to get discrete messages from the STREAM and use NIT_BUF */ + if (ioctl(fd, I_SRDOPT, (char *)RMSGD) < 0) { + sprintf(ebuf, "I_SRDOPT: %s", pcap_strerror(errno)); + goto bad; + } + if (ioctl(fd, I_PUSH, "nbuf") < 0) { + sprintf(ebuf, "push nbuf: %s", pcap_strerror(errno)); + goto bad; + } + /* set the chunksize */ + si.ic_cmd = NIOCSCHUNK; + si.ic_timout = INFTIM; + si.ic_len = sizeof(chunksize); + si.ic_dp = (char *)&chunksize; + if (ioctl(fd, I_STR, (char *)&si) < 0) { + sprintf(ebuf, "NIOCSCHUNK: %s", pcap_strerror(errno)); + goto bad; + } + + /* request the interface */ + strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name)); + ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = ' '; + si.ic_cmd = NIOCBIND; + si.ic_len = sizeof(ifr); + si.ic_dp = (char *)𝔦 + if (ioctl(fd, I_STR, (char *)&si) < 0) { + sprintf(ebuf, "NIOCBIND: %s: %s", + ifr.ifr_name, pcap_strerror(errno)); + goto bad; + } + + /* set the snapshot length */ + si.ic_cmd = NIOCSSNAP; + si.ic_len = sizeof(snaplen); + si.ic_dp = (char *)&snaplen; + if (ioctl(fd, I_STR, (char *)&si) < 0) { + sprintf(ebuf, "NIOCSSNAP: %s", pcap_strerror(errno)); + goto bad; + } + p->snapshot = snaplen; + if (nit_setflags(p->fd, promisc, to_ms, ebuf) < 0) + goto bad; + + (void)ioctl(fd, I_FLUSH, (char *)FLUSHR); + /* + * NIT supports only ethernets. + */ + p->linktype = DLT_EN10MB; + + p->bufsize = BUFSPACE; + p->buffer = (u_char *)malloc(p->bufsize); + if (p->buffer == NULL) { + strcpy(ebuf, pcap_strerror(errno)); + goto bad; + } + return (p); + bad: + if (fd >= 0) + close(fd); + free(p); + return (NULL); +} + +int +pcap_setfilter(pcap_t *p, struct bpf_program *fp) +{ + + p->fcode = *fp; + return (0); +} diff --git a/lib/libpcap/pcap-snoop.c b/lib/libpcap/pcap-snoop.c new file mode 100644 index 0000000..50f9785 --- /dev/null +++ b/lib/libpcap/pcap-snoop.c @@ -0,0 +1,195 @@ +/* + * Copyright (c) 1993, 1994 + * 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-snoop.c,v 1.6 94/01/31 05:26:09 leres Exp $ (LBL)"; +#endif + +#include <sys/param.h> +#include <stdio.h> +#include <netdb.h> +#include <ctype.h> +#include <signal.h> +#include <errno.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <sys/file.h> +#include <sys/ioctl.h> + +#include <net/raw.h> + +#include <net/if.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/if_ether.h> +#include <netinet/ip_var.h> +#include <netinet/udp.h> +#include <netinet/udp_var.h> +#include <netinet/tcp.h> +#include <netinet/tcpip.h> +#include <net/bpf.h> + +#include "pcap-int.h" + +static int hdrpad; /* XXX */ + +int +pcap_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) +{ + int cc; + register struct snoopheader *sh; + register int datalen; + register int caplen; + register u_char *cp; + again: + cc = read(p->fd, (char *)p->buffer, p->bufsize); + if (cc < 0) { + switch (errno) { + case EWOULDBLOCK: + return (0); /* XXX */ + } + sprintf(p->errbuf, "read: %s", pcap_strerror(errno)); + return (-1); + } + sh = (struct snoopheader *)p->buffer; + datalen = sh->snoop_packetlen; + caplen = (datalen < p->snapshot) ? datalen : p->snapshot; + cp = (u_char *)(sh + 1) + hdrpad; /* XXX */ + + if (p->fcode.bf_insns == NULL || + bpf_filter(p->fcode.bf_insns, cp, datalen, caplen)) { + struct pcap_pkthdr h; + ++p->md.stat.ps_recv; + h.ts = sh->snoop_timestamp; + h.len = datalen; + h.caplen = caplen; + (*callback)(user, &h, cp); + return (1); + } + return (0); +} + +int +pcap_stats(pcap_t *p, struct pcap_stat *ps) +{ + register struct rawstats *rs; + struct rawstats rawstats; + + rs = &rawstats; + bzero((char *)rs, sizeof(*rs)); + if (ioctl(p->fd, SIOCRAWSTATS, (char *)rs) < 0) { + sprintf(p->errbuf, "SIOCRAWSTATS: %s", pcap_strerror(errno)); + return (-1); + } + + p->md.stat.ps_drop = + rs->rs_snoop.ss_ifdrops + rs->rs_snoop.ss_sbdrops + + rs->rs_drain.ds_ifdrops + rs->rs_drain.ds_sbdrops; + + *ps = p->md.stat; + return (0); +} + +/* XXX can't disable promiscuous */ +pcap_t * +pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf) +{ + pcap_t *p; + struct sockaddr_raw sr; + int fd; + int v; + struct snoopfilter sf; + + p = (pcap_t *)malloc(sizeof(*p)); + if (p == NULL) { + strcpy(ebuf, "no swap"); + return (0); + } + bzero(p, sizeof(*p)); + p->fd = -1; + p->bufsize = 4096; /* XXX */ + p->buffer = (u_char *)malloc(p->bufsize); + if (p->buffer == NULL) { + strcpy(ebuf, "no swap"); + goto bad; + } + fd = p->fd = socket(PF_RAW, SOCK_RAW, RAWPROTO_SNOOP); + if (fd < 0) { + sprintf(ebuf, "snoop socket: %s", pcap_strerror(errno)); + goto bad; + } + sr.sr_family = AF_RAW; + sr.sr_port = 0; + (void)strncpy(sr.sr_ifname, device, sizeof(sr.sr_ifname)); + if (bind(fd, (struct sockaddr *)&sr, sizeof(sr))) { + sprintf(ebuf, "snoop bind: %s", pcap_strerror(errno)); + goto bad; + } + bzero((char *)&sf, sizeof(sf)); + if (ioctl(fd, SIOCADDSNOOP, &sf) < 0) { + sprintf(ebuf, "SIOCADDSNOOP: %s", pcap_strerror(errno)); + goto bad; + } + v = 64 * 1024; + (void)setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *)&v, sizeof(v)); + if (ioctl(fd, SIOCSNOOPLEN, &snaplen) < 0) { + sprintf(ebuf, "SIOCSNOOPLEN: %s", pcap_strerror(errno)); + goto bad; + } + p->snapshot = snaplen; + v = 1; + if (ioctl(fd, SIOCSNOOPING, &v) < 0) { + sprintf(ebuf, "SIOCSNOOPING: %s", pcap_strerror(errno)); + goto bad; + } + /* + * XXX hack - map device name to link later type + */ + if (strncmp("et", device, 2) == 0 || + strncmp("ec", device, 2) == 0) { + p->linktype = DLT_EN10MB; + hdrpad = RAW_HDRPAD(sizeof(struct ether_header)); + } else if (strncmp("ipg", device, 3) == 0 || + strncmp("xpi", device, 3) == 0) { + p->linktype = DLT_FDDI; + hdrpad = 3; /* XXX yeah? */ + } else { + sprintf(ebuf, "snoop: unknown physical layer type"); + goto bad; + } + return (p); + bad: + if (fd >= 0) + close(fd); + if (p->buffer != NULL) + free(p->buffer); + free(p); + return (0); +} + +int +pcap_setfilter(pcap_t *p, struct bpf_program *fp) +{ + + p->fcode = *fp; + return (0); +} diff --git a/lib/libpcap/pcap.3 b/lib/libpcap/pcap.3 new file mode 100644 index 0000000..2159c4d --- /dev/null +++ b/lib/libpcap/pcap.3 @@ -0,0 +1,323 @@ +.\" Copyright (c) 1994 +.\" 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 PCAP 3 "14 Jun 1994" +.SH NAME +pcap \- Packet Capture library +.SH SYNOPSIS +.nf +.ft B +#include <pcap.h> +.ft +.LP +.ft B +pcap_t *pcap_open_live(char *device, int snaplen, +.ti +8 +int promisc, int to_ms, char *ebuf) +pcap_t *pcap_open_offline(char *fname, char *ebuf) +pcap_dumper_t *pcap_dump_open(pcap_t *p, char *fname) +.ft +.LP +.ft B +char errbuf[PCAP_ERRBUF_SIZE]; +char *pcap_lookupdev(char *errbuf) +int pcap_lookupnet(char *device, u_long *netp, +.ti +8 +u_long *maskp, char *errbuf) +.ft +.LP +.ft B +int pcap_dispatch(pcap_t *p, int cnt, +.ti +8 +pcap_handler callback, u_char *user) +int pcap_loop(pcap_t *p, int cnt, +.ti +8 +pcap_handler callback, u_char *user) +void pcap_dump(u_char *user, struct pcap_pkthdr *h, +.ti +8 +u_char *sp) +.ft +.LP +.ft B +int pcap_immediate(pcap_t *p) +.ft +.LP +.ft B +int pcap_compile(pcap_t *p, struct bpf_program *fp, +.ti +8 +char *str, int optimize, u_long netmask) +int pcap_setfilter(pcap_t *p, struct bpf_program *fp) +.ft +.LP +.ft B +u_char *pcap_next(pcap_t *p, struct pcap_pkthdr *h) +.ft +.LP +.ft B +int pcap_datalink(pcap_t *p) +int pcap_snapshot(pcap_t *p) +int pcap_is_swapped(pcap_t *p) +int pcap_major_version(pcap_t *p) +int pcap_minor_version(pcap_t *p) +int pcap_stats(pcap_t *p, struct pcap_stat *ps) +FILE *pcap_file(pcap_t *p) +int pcap_fileno(pcap_t *p) +void pcap_perror(pcap_t *p, char *prefix) +char *pcap_geterr(pcap_t *p) +char *pcap_strerror(int error) +.ft +.LP +.ft B +void pcap_close(pcap_t *p) +void pcap_dump_close(pcap_dumper_t *p) +.ft +.fi +.SH DESCRIPTION +The Packet Capture library +provides a high level interface to packet capture systems. All packets +on the network, even those destined for other hosts, are accessible +through this mechanism. +.PP +.SH ROUTINES +.B pcap_open_live() +is used to obtain a packet capture descriptor to look +at packets on the network. +.I device +is a string that specifies the network device to open. +.I snaplen +specifies the maximum number of bytes to capture. +.I to_ms +specifies the read timeout in milliseconds. +.I ebuf +is used to return error text and is only set when +.B pcap_open_live() +fails and returns +.BR NULL . +.PP +.B pcap_open_offline() +is called to open a ``savefile'' for reading. +.I fname +specifies the name of the file to open. The file has +the same format as those used by +.B tcpdump(1) +and +.BR tcpslice(1) . +The name "-" in a synonym for +.BR stdin . +.I ebuf +is used to return error text and is only set when +.B pcap_open_offline() +fails and returns +.BR NULL . +.PP +.B pcap_dump_open() +is called to open a ``savefile'' for writing. The name "-" in a synonym +for +.BR stdin . +.B NULL +is returned on failure. +.I p +is a +.I pcap +struct as returned by +.B pcap_open_offline() +or +.BR pcap_open_live() . +.I fname +specifies the name of the file to open. +If +.B NULL +is returned, +.B pcap_geterr() +can be used to get the error text. +.PP +.B pcap_lookupdev() +returns a pointer to a network device suitable for use with +.B pcap_open_live() +and +.BR pcap_lookupnet() . +If there is an error, +.B NULL +is returned and +.I errbuf +is filled in with with an appropriate error message. +.PP +.B pcap_lookupnet() +is used to determine the network number and mask +associated with the network device +.BR device . +Both +.I netp +and +.I maskp +are +.I u_long +pointers. +A return of -1 indicates an error in which case +.I errbuf +is filled in with with an appropriate error message. +.PP +.B pcap_dispatch() +is used to collect and process packets. +.I cnt +specifies the maximum number of packets to process before returning. A +.I cnt +of -1 processes all the packets received in one buffer. A +.I cnt +of 0 processes all packets until an error occurs (or +.B EOF +is reached). +.I callback +specifies a routine to be called with three arguments: +a +.I u_char +pointer which is passed in from +.BR pcap_dispatch() , +a pointer to the +.I pcap_pkthdr +struct (which precede the actual network headers and data), +and a length. The number of packets read is returned. +Zero is returned when +.B EOF +is reached in a ``savefile.'' A return of -1 indicates +an error in which case +.B pcap_perror() +or +.BR pcap_geterr() +may be used to display the error text. +.PP +.B pcap_dump() +outputs a packet to the ``savefile'' opened with +.BR pcap_dump_open() . +Note that its calling arguments are suitable for use with +.BR pcap_dispatch() . +.ft B +(??? this guy is kind of weird.) +.ft +.PP +.B pcap_immediate() +sets ``immediate'' mode. +If this isn't supported by the under lying packet capture, -1 is +returned and the error text can be obtained with +.B pcap_perror() +or +.BR pcap_geterr() . +.PP +.B pcap_compile() +is used to compile the string +.I str +into a filter program. +.I program +is a pointer to a +.I bpf_program +struct and is filled in by +.BR pcap_compile() . +.I optimize +controls whether optimization on the resulting code is performed. +.I netmask +specifies the netmask of the local net. +.PP +.B pcap_setfilter() +is used to specify the a filter program. +.I fp +is a pointer to an array of +.I bpf_program +struct, usually the result of a call to +.BR pcap_compile() . +.PP +.B pcap_loop() +is similar to +.B pcap_dispatch() +except it keeps reading packets until +.I cnt +packets are processed or an error occurs. +A negative +.I cnt +causes +.B pcap_loop() +to loop forever (or at least until an error occurs). +.PP +.B pcap_next() +returns a +.I u_char +pointer to the next packet. +.PP +.B pcap_datalink() +returns the link layer type, e.g. +.BR DLT_EN10MB . +.PP +.B pcap_snapshot() +returns the snapshot length specified when +.B pcap_open_live +was called. +.PP +.B pcap_is_swapped() +returns true if the current ``savefile'' uses a different byte order +than the current system. +.PP +.B pcap_major_version() +returns the major number of the version of the pcap used to write the +savefile. +.PP +.B pcap_minor_version() +returns the major number of the version of the pcap used to write the +savefile. +.PP +.B pcap_file() +returns the name of the ``savefile.'' +.PP +.B int pcap_stats() +returns 0 and fills in a +.B pcap_stat +struct with packet statistics. If there is an error or the under lying +packet capture doesn't support packet statistics, -1 is returned and +the error text can be obtained with +.B pcap_perror() +or +.BR pcap_geterr() . +.PP +.B pcap_fileno() +returns the file descriptor number of the ``savefile.'' +.PP +.B pcap_perror() +prints the text of the last pcap library error on +.BR stderr , +prefixed by +.IR prefix . +.PP +.B pcap_geterr() +returns the error text pertaining to the last pcap library error. +.PP +.B pcap_strerror() +is provided in case +.BR strerror (1) +isn't available. +.PP +.B pcap_close() +closes the files associated with +.I p +and deallocates resources. +.PP +.B pcap_dump_close() +closes the ``savefile.'' +.PP +.SH SEE ALSO +tcpdump(1), tcpslice(1) +.SH BUGS +.SH HISTORY diff --git a/lib/libpcap/pcap.c b/lib/libpcap/pcap.c new file mode 100644 index 0000000..82070d2 --- /dev/null +++ b/lib/libpcap/pcap.c @@ -0,0 +1,180 @@ +/* + * Copyright (c) 1993, 1994 + * 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 the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the Computer Systems + * Engineering Group at Lawrence Berkeley Laboratory. + * 4. Neither the name of the University nor of the Laboratory may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char rcsid[] = + "@(#) $Header: pcap.c,v 1.12 94/06/12 14:32:23 leres Exp $ (LBL)"; +#endif + +#include <sys/types.h> + +#include <unistd.h> + +#include "pcap-int.h" + +int +pcap_dispatch(pcap_t *p, int cnt, pcap_handler callback, u_char *user) +{ + if (p->sf.rfile != NULL) + return (pcap_offline_read(p, cnt, callback, user)); + else + return (pcap_read(p, cnt, callback, user)); +} + +int +pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user) +{ + for (;;) { + int n = pcap_dispatch(p, cnt, callback, user); + if (n < 0) + return (n); + if (cnt > 0) { + cnt -= n; + if (cnt <= 0) + return (0); + } + } +} + +struct singleton { + struct pcap_pkthdr *hdr; + const u_char *pkt; +}; + + +static void +pcap_oneshot(u_char *userData, const struct pcap_pkthdr *h, const u_char *pkt) +{ + struct singleton *sp = (struct singleton *)userData; + *sp->hdr = *h; + sp->pkt = pkt; +} + +const u_char * +pcap_next(pcap_t *p, struct pcap_pkthdr *h) +{ + struct singleton s; + + s.hdr = h; + if (pcap_dispatch(p, 1, pcap_oneshot, (u_char*)&s) < 0) + return (0); + return (s.pkt); +} + +int +pcap_datalink(pcap_t *p) +{ + return (p->linktype); +} + +int +pcap_snapshot(pcap_t *p) +{ + return (p->snapshot); +} + +int +pcap_is_swapped(pcap_t *p) +{ + return (p->sf.swapped); +} + +int +pcap_major_version(pcap_t *p) +{ + return (p->sf.version_major); +} + +int +pcap_minor_version(pcap_t *p) +{ + return (p->sf.version_minor); +} + +FILE * +pcap_file(pcap_t *p) +{ + return (p->sf.rfile); +} + +int +pcap_fileno(pcap_t *p) +{ + return (p->fd); +} + +void +pcap_perror(pcap_t *p, char *prefix) +{ + fprintf(stderr, "%s: %s\n", prefix, p->errbuf); +} + +char * +pcap_geterr(pcap_t *p) +{ + return (p->errbuf); +} + +/* + * Not all systems have strerror(). + */ +char * +pcap_strerror(int errnum) +{ + extern int sys_nerr; +/* + extern char *sys_errlist[]; +*/ + static char ebuf[20]; + + if ((unsigned int)errnum < sys_nerr) + return (sys_errlist[errnum]); + (void)sprintf(ebuf, "Unknown error: %d", errnum); + return(ebuf); +} + +void +pcap_close(pcap_t *p) +{ + /*XXX*/ + if (p->fd >= 0) + close(p->fd); + if (p->sf.rfile != NULL) { + fclose(p->sf.rfile); + if (p->sf.base != NULL) + free(p->sf.base); + } else if (p->buffer != NULL) + free(p->buffer); + + free(p); +} diff --git a/lib/libpcap/pcap.h b/lib/libpcap/pcap.h new file mode 100644 index 0000000..506b15b --- /dev/null +++ b/lib/libpcap/pcap.h @@ -0,0 +1,137 @@ +/* + * Copyright (c) 1993, 1994 + * 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 the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the Computer Systems + * Engineering Group at Lawrence Berkeley Laboratory. + * 4. Neither the name of the University nor of the Laboratory may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#) $Header: pcap.h,v 1.15 94/06/14 20:03:34 leres Exp $ (LBL) + */ + +#ifndef lib_pcap_h +#define lib_pcap_h + +#include <sys/types.h> +#include <sys/time.h> + +#include <net/bpf.h> + +#include <stdio.h> + +#define PCAP_VERSION_MAJOR 2 +#define PCAP_VERSION_MINOR 4 + +#define PCAP_ERRBUF_SIZE 256 + +/* + * Compatibility for systems that have a bpf.h that + * predates the bpf typedefs for 64-bit support. + */ +#if BPF_RELEASE - 0 < 199406 +typedef long bpf_int32; +typedef u_long bpf_u_int32; +#endif + +typedef struct pcap pcap_t; +typedef struct pcap_dumper pcap_dumper_t; + +/* + * 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 pcap_file_header { + bpf_u_int32 magic; + u_short version_major; + u_short version_minor; + bpf_int32 thiszone; /* gmt to local correction */ + bpf_u_int32 sigfigs; /* accuracy of timestamps */ + bpf_u_int32 snaplen; /* max length saved portion of each pkt */ + bpf_u_int32 linktype; /* data link type (DLT_*) */ +}; + +/* + * 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 pcap_pkthdr { + struct timeval ts; /* time stamp */ + bpf_u_int32 caplen; /* length of portion present */ + bpf_u_int32 len; /* length this packet (off wire) */ +}; + +/* + * As returned by the pcap_stats() + */ +struct pcap_stat { + u_int ps_recv; /* number of packets received */ + u_int ps_drop; /* number of packets dropped */ + u_int ps_ifdrop; /* drops by interface XXX not yet supported */ +}; + +typedef void (*pcap_handler)(u_char *, const struct pcap_pkthdr *, + const u_char *); + +char *pcap_lookupdev(char *); +int pcap_lookupnet(char *, u_long *, u_long *, char *); +pcap_t *pcap_open_live(char *, int, int, int, char *); +pcap_t *pcap_open_offline(char *, char *); +void pcap_close(pcap_t *); +int pcap_loop(pcap_t *, int, pcap_handler, u_char *); +int pcap_dispatch(pcap_t *, int, pcap_handler, u_char *); +const u_char* + pcap_next(pcap_t *, struct pcap_pkthdr *); +int pcap_stats(pcap_t *, struct pcap_stat *); +int pcap_setfilter(pcap_t *, struct bpf_program *); +void pcap_perror(pcap_t *, char *); +char *pcap_strerror(int); +char *pcap_geterr(pcap_t *); +int pcap_compile(pcap_t *, struct bpf_program *, char *, int, u_long); +/* XXX */ +int pcap_freecode(pcap_t *, struct bpf_program *); +int pcap_datalink(pcap_t *); +int pcap_snapshot(pcap_t *); +int pcap_is_swapped(pcap_t *); +int pcap_major_version(pcap_t *); +int pcap_minor_version(pcap_t *); + +/* XXX */ +FILE *pcap_file(pcap_t *); +int pcap_fileno(pcap_t *); + +pcap_dumper_t *pcap_dump_open(pcap_t *, char *); +void pcap_dump_close(pcap_dumper_t *); +void pcap_dump(u_char *, const struct pcap_pkthdr *, const u_char *); + +/* XXX this guy lives in the bpf tree */ +u_int bpf_filter(struct bpf_insn *, u_char *, u_int, u_int); +char *bpf_image(struct bpf_insn *, int); + +#endif diff --git a/lib/libpcap/savefile.c b/lib/libpcap/savefile.c new file mode 100644 index 0000000..90ee723 --- /dev/null +++ b/lib/libpcap/savefile.c @@ -0,0 +1,285 @@ +/* + * Copyright (c) 1993, 1994 + * 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.16 94/06/20 19:07:56 leres 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 <sys/types.h> +#include <sys/time.h> + +#include <net/bpf.h> + +#include <errno.h> +#include <memory.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "pcap-int.h" + +#define TCPDUMP_MAGIC 0xa1b2c3d4 + +/* + * 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 pcap_file_header and pcap_pkthdr, + * 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) ) + +#define SFERR_TRUNC 1 +#define SFERR_BADVERSION 2 +#define SFERR_BADF 3 +#define SFERR_EOF 4 /* not really an error, just a status */ + +static int +sf_write_header(FILE *fp, int linktype, int thiszone, int snaplen) +{ + struct pcap_file_header hdr; + + hdr.magic = TCPDUMP_MAGIC; + hdr.version_major = PCAP_VERSION_MAJOR; + hdr.version_minor = PCAP_VERSION_MINOR; + + hdr.thiszone = thiszone; + hdr.snaplen = snaplen; + hdr.sigfigs = 0; + hdr.linktype = linktype; + + if (fwrite((char *)&hdr, sizeof(hdr), 1, fp) != 1) + return (-1); + + return (0); +} + +static void +swap_hdr(struct pcap_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); +} + +pcap_t * +pcap_open_offline(char *fname, char *errbuf) +{ + register pcap_t *p; + register FILE *fp; + struct pcap_file_header hdr; + int linklen; + + p = (pcap_t *)malloc(sizeof(*p)); + if (p == NULL) { + strcpy(errbuf, "out of swap"); + return (NULL); + } + +#ifdef notdef + bzero(p, sizeof(*p)); +#else + memset(p, 0, sizeof(*p)); +#endif + /* + * Set this field so we don't close stdin in pcap_close! + */ + p->fd = -1; + + if (fname[0] == '-' && fname[1] == '\0') + fp = stdin; + else { + fp = fopen(fname, "r"); + if (fp == NULL) { + sprintf(errbuf, "%s: %s", fname, pcap_strerror(errno)); + goto bad; + } + } + if (fread((char *)&hdr, sizeof(hdr), 1, fp) != 1) { + sprintf(errbuf, "fread: %s", pcap_strerror(errno)); + goto bad; + } + if (hdr.magic != TCPDUMP_MAGIC) { + if (SWAPLONG(hdr.magic) != TCPDUMP_MAGIC) { + sprintf(errbuf, "bad dump file format"); + goto bad; + } + p->sf.swapped = 1; + swap_hdr(&hdr); + } + if (hdr.version_major < PCAP_VERSION_MAJOR) { + sprintf(errbuf, "archaic file format"); + goto bad; + } + p->tzoff = hdr.thiszone; + p->snapshot = hdr.snaplen; + p->linktype = hdr.linktype; + p->sf.rfile = fp; + p->bufsize = hdr.snaplen; + /* Align link header as required for proper data alignment */ + linklen = 14; /* XXX */ + p->sf.base = (u_char *)malloc(p->bufsize + BPF_ALIGNMENT); + p->buffer = p->sf.base + BPF_ALIGNMENT - (linklen % BPF_ALIGNMENT); + p->sf.version_major = hdr.version_major; + p->sf.version_minor = hdr.version_minor; + + return (p); + bad: + free(p); + return (NULL); +} + +/* + * 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. + */ +static int +sf_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char *buf, int buflen) +{ + FILE *fp = p->sf.rfile; + + /* read the stamp */ + if (fread((char *)hdr, sizeof(struct pcap_pkthdr), 1, fp) != 1) { + /* probably an EOF, though could be a truncated packet */ + return (1); + } + + if (p->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); + } + /* + * We interchanged the caplen and len fields at version 2.3, + * in order to match the bpf header layout. But unfortunately + * some files were written with version 2.3 in their headers + * but without the interchanged fields. + */ + if (p->sf.version_minor < 3 || + (p->sf.version_minor == 3 && hdr->caplen > hdr->len)) { + int t = hdr->caplen; + hdr->caplen = hdr->len; + hdr->len = t; + } + + if (hdr->caplen > buflen) { + sprintf(p->errbuf, "bad dump file format"); + return (-1); + } + + /* read the packet itself */ + + if (fread((char *)buf, hdr->caplen, 1, fp) != 1) { + sprintf(p->errbuf, "truncated dump file"); + return (-1); + } + return (0); +} + +/* + * Print out packets stored in the file initialized by sf_read_init(). + * If cnt > 0, return after 'cnt' packets, otherwise continue until eof. + */ +int +pcap_offline_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) +{ + struct bpf_insn *fcode = p->fcode.bf_insns; + int status = 0; + int n = 0; + + while (status == 0) { + struct pcap_pkthdr h; + + status = sf_next_packet(p, &h, p->buffer, p->bufsize); + if (status) + return (-1); + + if (fcode == NULL || + bpf_filter(fcode, p->buffer, h.len, h.caplen)) { + (*callback)(user, &h, p->buffer); + if (++n >= cnt && cnt > 0) + break; + } + } + /*XXX this breaks semantics tcpslice expects */ + return (n); +} + +/* + * Output a packet to the initialized dump file. + */ +void +pcap_dump(u_char *user, const struct pcap_pkthdr *h, const u_char *sp) +{ + FILE * f = (FILE *)user; + (void)fwrite((char *)h, sizeof(*h), 1, f); + (void)fwrite((char *)sp, h->caplen, 1, f); +} + +/* + * Initialize so that sf_write() will output to the file named 'fname'. + */ +pcap_dumper_t * +pcap_dump_open(pcap_t *p, char *fname) +{ + FILE *f; + if (fname[0] == '-' && fname[1] == '\0') + f = stdout; + else { + f = fopen(fname, "w"); + if (f == NULL) { + sprintf(p->errbuf, "%s: %s", + fname, pcap_strerror(errno)); + return (NULL); + } + } + (void)sf_write_header(f, p->linktype, p->tzoff, p->snapshot); + return ((pcap_dumper_t *)f); +} + +void +pcap_dump_close(pcap_dumper_t *p) +{ + fclose((FILE *)p); +} diff --git a/lib/libpcap/scanner.l b/lib/libpcap/scanner.l new file mode 100644 index 0000000..c2ff10d --- /dev/null +++ b/lib/libpcap/scanner.l @@ -0,0 +1,193 @@ +%{ +/* + * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994 + * 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: scanner.l,v 1.40 94/06/10 17:21:44 mccanne Exp $ (LBL)"; +#endif + +#include <sys/types.h> +#include <sys/time.h> + +#include <ctype.h> +#include <pcap.h> +#include <pcap-namedb.h> + +#include "gencode.h" +#include "tokdefs.h" + +#ifndef __GNUC__ +#define inline +#endif + +static int stoi(char *); +static inline int xdtoi(int); + +#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 + +#define yylval pcap_lval +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; +fddi return LINK; +arp return ARP; +rarp return RARP; +ip return IP; +tcp return TCP; +udp return UDP; +icmp return ICMP; + +decnet return DECNET; +lat return LAT; +moprc return MOPRC; +mopdl return MOPDL; + +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|length return LEN; +inbound return INBOUND; +outbound return OUTBOUND; + +[ \n\t] ; +[+\-*/:\[\]!<>()&|=] return yytext[0]; +">=" return GEQ; +"<=" return LEQ; +"!=" return NEQ; +"==" return '='; +"<<" return LSH; +">>" return RSH; +{N} { yylval.i = stoi((char *)yytext); return NUM; } +({N}\.{N})|({N}\.{N}\.{N})|({N}\.{N}\.{N}\.{N}) { + yylval.s = sdup((char *)yytext); return HID; +} +{B}:{B}:{B}:{B}:{B}:{B} { yylval.e = pcap_ether_aton((char *)yytext); + return EID; } +{B}:+({B}:+)+ { bpf_error("bogus ethernet address %s", yytext); } +[A-Za-z][-_.A-Za-z0-9]* { yylval.s = sdup((char *)yytext); return ID; } +"\\"[^ !()\n\t]+ { yylval.s = sdup((char *)yytext + 1); return ID; } +[^ \[\]\t\n\-_.A-Za-z0-9!<>()&|=]+ { bpf_error("illegal token: %s\n", yytext); } +. { bpf_error("illegal char '%c'", *yytext); } +%% +void +lex_init(buf) + char *buf; +{ + in_buffer = buf; +} + +/* + * Also define a yywrap. Note that if we're using flex, it will + * define a macro to map this identifier to pcap_wrap. + */ +int +yywrap() +{ + return 1; +} + +/* Hex digit to integer. */ +static inline int +xdtoi(c) + register int c; +{ + if (isdigit(c)) + return c - '0'; + else if (islower(c)) + return c - 'a' + 10; + else + return c - 'A' + 10; +} + +/* + * Convert string to integer. Just like atoi(), but checks for + * preceding 0x or 0 and uses hex or octal instead of decimal. + */ +static int +stoi(s) + char *s; +{ + int base = 10; + int n = 0; + + if (*s == '0') { + if (s[1] == 'x' || s[1] == 'X') { + s += 2; + base = 16; + } + else { + base = 8; + s += 1; + } + } + while (*s) + n = n * base + xdtoi(*s++); + + return n; +} + |