X-Git-Url: https://git.tcpdump.org/libpcap/blobdiff_plain/3c03d42af250048e73a9a0bca7e2fee63d6aaa7d..refs/heads/libpcap-0.7:/gencode.c diff --git a/gencode.c b/gencode.c index 8b8be784..f18bad5c 100644 --- a/gencode.c +++ b/gencode.c @@ -21,7 +21,11 @@ */ #ifndef lint static const char rcsid[] = - "@(#) $Header: /tcpdump/master/libpcap/gencode.c,v 1.96 1999-10-30 04:30:14 itojun Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/gencode.c,v 1.160.2.4 2002-06-01 23:23:20 guy Exp $ (LBL)"; +#endif + +#ifdef HAVE_CONFIG_H +#include "config.h" #endif #include @@ -31,37 +35,38 @@ static const char rcsid[] = #include #endif -#if __STDC__ -struct mbuf; -struct rtentry; -#endif - +struct mbuf; /* Squelch compiler warnings on some platforms for */ +struct rtentry; /* declarations in */ #include #include -#include #include +#include #include #include -#if __STDC__ #include -#else -#include -#endif #include "pcap-int.h" #include "ethertype.h" +#include "nlpid.h" +#include "llc.h" #include "gencode.h" #include "ppp.h" -#include +#include "sll.h" +#include "arcnet.h" #ifdef INET6 -#include -#include +#include /* for "struct addrinfo" */ #endif /*INET6*/ +#include + +#define ETHERMTU 1500 + +#ifndef IPPROTO_SCTP +#define IPPROTO_SCTP 132 +#endif -#include "gnuc.h" #ifdef HAVE_OS_PROTO_H #include "os-proto.h" #endif @@ -72,6 +77,9 @@ struct rtentry; static jmp_buf top_ctx; static pcap_t *bpf_pcap; +/* Hack for updating VLAN offsets. */ +static u_int orig_linktype = -1, orig_nl = -1, orig_nl_nosnap = -1; + /* XXX */ #ifdef PCAP_FDDIPAD int pcap_fddipad = PCAP_FDDIPAD; @@ -80,24 +88,16 @@ int pcap_fddipad; #endif /* VARARGS */ -__dead void -#if __STDC__ +void bpf_error(const char *fmt, ...) -#else -bpf_error(fmt, va_alist) - const 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); + (void)vsnprintf(pcap_geterr(bpf_pcap), PCAP_ERRBUF_SIZE, + fmt, ap); va_end(ap); longjmp(top_ctx, 1); /* NOTREACHED */ @@ -136,24 +136,30 @@ 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, bpf_int32); +static struct block *gen_cmp_gt(u_int, u_int, bpf_int32); static struct block *gen_mcmp(u_int, u_int, bpf_int32, bpf_u_int32); static struct block *gen_bcmp(u_int, u_int, const 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_snap(bpf_u_int32, bpf_u_int32, u_int); static struct block *gen_hostop(bpf_u_int32, bpf_u_int32, int, int, u_int, u_int); #ifdef INET6 static struct block *gen_hostop6(struct in6_addr *, struct in6_addr *, int, int, u_int, u_int); #endif +static struct block *gen_ahostop(const u_char *, int); static struct block *gen_ehostop(const u_char *, int); static struct block *gen_fhostop(const u_char *, int); +static struct block *gen_thostop(const u_char *, int); static struct block *gen_dnhostop(bpf_u_int32, int, u_int); static struct block *gen_host(bpf_u_int32, bpf_u_int32, int, int); #ifdef INET6 static struct block *gen_host6(struct in6_addr *, struct in6_addr *, int, int); #endif +#ifndef INET6 static struct block *gen_gateway(const u_char *, bpf_u_int32 **, int, int); +#endif static struct block *gen_ipfrag(void); static struct block *gen_portatom(int, bpf_int32); #ifdef INET6 @@ -166,6 +172,7 @@ struct block *gen_portop6(int, int, int); static struct block *gen_port6(int, int, int); #endif static int lookup_proto(const char *, int); +static struct block *gen_protochain(int, int, int); static struct block *gen_proto(int, int, int); static struct slist *xfer_to_x(struct arth *); static struct slist *xfer_to_a(struct arth *); @@ -225,7 +232,7 @@ sdup(s) int n = strlen(s) + 1; char *cp = newchunk(n); - strcpy(cp, s); + strlcpy(cp, s, n); return (cp); } @@ -286,12 +293,19 @@ pcap_compile(pcap_t *p, struct bpf_program *program, root = NULL; bpf_pcap = p; if (setjmp(top_ctx)) { + lex_cleanup(); freechunks(); return (-1); } netmask = mask; + snaplen = pcap_snapshot(p); + if (snaplen == 0) { + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "snaplen of 0 rejects all packets"); + return -1; + } lex_init(buf ? buf : ""); init_linktype(pcap_datalink(p)); @@ -312,10 +326,45 @@ pcap_compile(pcap_t *p, struct bpf_program *program, program->bf_insns = icode_to_fcode(root, &len); program->bf_len = len; + lex_cleanup(); freechunks(); return (0); } +/* + * entry point for using the compiler with no pcap open + * pass in all the stuff that is needed explicitly instead. + */ +int +pcap_compile_nopcap(int snaplen_arg, int linktype_arg, + struct bpf_program *program, + char *buf, int optimize, bpf_u_int32 mask) +{ + pcap_t *p; + int ret; + + p = pcap_open_dead(linktype_arg, snaplen_arg); + if (p == NULL) + return (-1); + ret = pcap_compile(p, program, buf, optimize, mask); + pcap_close(p); + return (ret); +} + +/* + * Clean up a "struct bpf_program" by freeing all the memory allocated + * in it. + */ +void +pcap_freecode(struct bpf_program *program) +{ + program->bf_len = 0; + if (program->bf_insns != NULL) { + free((char *)program->bf_insns); + program->bf_insns = NULL; + } +} + /* * 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 @@ -416,6 +465,24 @@ gen_cmp(offset, size, v) return b; } +static struct block * +gen_cmp_gt(offset, size, v) + u_int offset, size; + bpf_int32 v; +{ + struct slist *s; + struct block *b; + + s = new_stmt(BPF_LD|BPF_ABS|size); + s->s.k = offset; + + b = new_block(JMP(BPF_JGT)); + b->stmts = s; + b->s.k = v; + + return b; +} + static struct block * gen_mcmp(offset, size, v, mask) u_int offset, size; @@ -473,11 +540,55 @@ gen_bcmp(offset, size, v) /* * 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. + * layer. These variables give the necessary offsets. + */ + +/* + * "off_linktype" is the offset to information in the link-layer header + * giving the packet type. + * + * For Ethernet, it's the offset of the Ethernet type field. + * + * For link-layer types that always use 802.2 headers, it's the + * offset of the LLC header. + * + * For PPP, it's the offset of the PPP type field. + * + * For Cisco HDLC, it's the offset of the CHDLC type field. + * + * For BSD loopback, it's the offset of the AF_ value. + * + * For Linux cooked sockets, it's the offset of the type field. + * + * It's set to -1 for no encapsulation, in which case, IP is assumed. */ static u_int off_linktype; + +/* + * These are offsets to the beginning of the network-layer header. + * + * If the link layer never uses 802.2 LLC: + * + * "off_nl" and "off_nl_nosnap" are the same. + * + * If the link layer always uses 802.2 LLC: + * + * "off_nl" is the offset if there's a SNAP header following + * the 802.2 header; + * + * "off_nl_nosnap" is the offset if there's no SNAP header. + * + * If the link layer is Ethernet: + * + * "off_nl" is the offset if the packet is an Ethernet II packet + * (we assume no 802.3+802.2+SNAP); + * + * "off_nl_nosnap" is the offset if the packet is an 802.3 packet + * with an 802.2 header following it. + */ static u_int off_nl; +static u_int off_nl_nosnap; + static int linktype; static void @@ -486,11 +597,22 @@ init_linktype(type) { linktype = type; + orig_linktype = -1; + orig_nl = -1; + orig_nl_nosnap = -1; + switch (type) { + case DLT_ARCNET: + off_linktype = 2; + off_nl = 6; /* XXX in reality, variable! */ + off_nl_nosnap = 6; /* no 802.2 LLC */ + return; + case DLT_EN10MB: off_linktype = 12; - off_nl = 14; + off_nl = 14; /* Ethernet II */ + off_nl_nosnap = 17; /* 802.3+802.2 */ return; case DLT_SLIP: @@ -500,6 +622,7 @@ init_linktype(type) */ off_linktype = -1; off_nl = 16; + off_nl_nosnap = 16; /* no 802.2 LLC */ return; case DLT_SLIP_BSDOS: @@ -507,60 +630,159 @@ init_linktype(type) off_linktype = -1; /* XXX end */ off_nl = 24; + off_nl_nosnap = 24; /* no 802.2 LLC */ return; case DLT_NULL: + case DLT_LOOP: off_linktype = 0; off_nl = 4; + off_nl_nosnap = 4; /* no 802.2 LLC */ return; case DLT_PPP: - case DLT_CHDLC: + case DLT_C_HDLC: /* BSD/OS Cisco HDLC */ + case DLT_PPP_SERIAL: /* NetBSD sync/async serial PPP */ off_linktype = 2; off_nl = 4; + off_nl_nosnap = 4; /* no 802.2 LLC */ + return; + + case DLT_PPP_ETHER: + /* + * This does no include the Ethernet header, and + * only covers session state. + */ + off_linktype = 6; + off_nl = 8; + off_nl_nosnap = 8; /* no 802.2 LLC */ return; case DLT_PPP_BSDOS: off_linktype = 5; off_nl = 24; + off_nl_nosnap = 24; /* no 802.2 LLC */ return; 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. + * We set "off_linktype" to the offset of the LLC header. + * + * To check for Ethernet types, we assume that SSAP = SNAP + * is being used and pick out the encapsulated Ethernet type. + * XXX - should we generate code to check for SNAP? */ - off_linktype = 19; + off_linktype = 13; #ifdef PCAP_FDDIPAD off_linktype += pcap_fddipad; #endif - off_nl = 21; + off_nl = 21; /* FDDI+802.2+SNAP */ + off_nl_nosnap = 16; /* FDDI+802.2 */ #ifdef PCAP_FDDIPAD off_nl += pcap_fddipad; + off_nl_nosnap += pcap_fddipad; #endif return; case DLT_IEEE802: - off_linktype = 20; - off_nl = 22; + /* + * Token Ring doesn't really have a link-level type field. + * We set "off_linktype" to the offset of the LLC header. + * + * To check for Ethernet types, we assume that SSAP = SNAP + * is being used and pick out the encapsulated Ethernet type. + * XXX - should we generate code to check for SNAP? + * + * XXX - the header is actually variable-length. + * Some various Linux patched versions gave 38 + * as "off_linktype" and 40 as "off_nl"; however, + * if a token ring packet has *no* routing + * information, i.e. is not source-routed, the correct + * values are 20 and 22, as they are in the vanilla code. + * + * A packet is source-routed iff the uppermost bit + * of the first byte of the source address, at an + * offset of 8, has the uppermost bit set. If the + * packet is source-routed, the total number of bytes + * of routing information is 2 plus bits 0x1F00 of + * the 16-bit value at an offset of 14 (shifted right + * 8 - figure out which byte that is). + */ + off_linktype = 14; + off_nl = 22; /* Token Ring+802.2+SNAP */ + off_nl_nosnap = 17; /* Token Ring+802.2 */ + return; + + case DLT_IEEE802_11: + /* + * 802.11 doesn't really have a link-level type field. + * We set "off_linktype" to the offset of the LLC header. + * + * To check for Ethernet types, we assume that SSAP = SNAP + * is being used and pick out the encapsulated Ethernet type. + * XXX - should we generate code to check for SNAP? + * + * XXX - the header is actually variable-length. We + * assume a 24-byte link-layer header, as appears in + * data frames in networks with no bridges. + */ + off_linktype = 24; + off_nl = 32; /* 802.11+802.2+SNAP */ + off_nl_nosnap = 27; /* 802.22+802.2 */ + return; + + case DLT_PRISM_HEADER: + /* + * Same as 802.11, but with an additional header before + * the 802.11 header, containing a bunch of additional + * information including radio-level information. + * + * The header is 144 bytes long. + * + * XXX - same variable-length header problem; at least + * the Prism header is fixed-length. + */ + off_linktype = 144+24; + off_nl = 144+32; /* Prism+802.11+802.2+SNAP */ + off_nl_nosnap = 144+27; /* Prism+802.11+802.2 */ return; case DLT_ATM_RFC1483: + case DLT_ATM_CLIP: /* Linux ATM defines this */ /* * assume routed, non-ISO PDUs * (i.e., LLC = 0xAA-AA-03, OUT = 0x00-00-00) */ - off_linktype = 6; - off_nl = 8; + off_linktype = 0; + off_nl = 8; /* 802.2+SNAP */ + off_nl_nosnap = 3; /* 802.2 */ return; case DLT_RAW: off_linktype = -1; off_nl = 0; + off_nl_nosnap = 0; /* no 802.2 LLC */ + return; + + case DLT_LINUX_SLL: /* fake header for Linux cooked socket */ + off_linktype = 14; + off_nl = 16; + off_nl_nosnap = 16; /* no 802.2 LLC */ + return; + + case DLT_LTALK: + /* + * LocalTalk does have a 1-byte type field in the LLAP header, + * but really it just indicates whether there is a "short" or + * "long" DDP packet following. + */ + off_linktype = -1; + off_nl = 0; + off_nl_nosnap = 0; /* no 802.2 LLC */ return; } - bpf_error("unknown data link type 0x%x", linktype); + bpf_error("unknown data link type %d", linktype); /* NOTREACHED */ } @@ -591,31 +813,512 @@ gen_false() return gen_uncond(0); } +/* + * Byte-swap a 32-bit number. + * ("htonl()" or "ntohl()" won't work - we want to byte-swap even on + * big-endian platforms.) + */ +#define SWAPLONG(y) \ +((((y)&0xff)<<24) | (((y)&0xff00)<<8) | (((y)&0xff0000)>>8) | (((y)>>24)&0xff)) + static struct block * gen_linktype(proto) register int proto; { struct block *b0, *b1; - /* If we're not using encapsulation and checking for IP, we're done */ - if (off_linktype == -1 && proto == ETHERTYPE_IP) - return gen_true(); - switch (linktype) { + case DLT_EN10MB: + switch (proto) { + + case LLCSAP_ISONS: + /* + * OSI protocols always use 802.2 encapsulation. + * XXX - should we check both the DSAP and the + * SSAP, like this, or should we check just the + * DSAP? + */ + b0 = gen_cmp_gt(off_linktype, BPF_H, ETHERMTU); + gen_not(b0); + b1 = gen_cmp(off_linktype + 2, BPF_H, (bpf_int32) + ((LLCSAP_ISONS << 8) | LLCSAP_ISONS)); + gen_and(b0, b1); + return b1; + + case LLCSAP_NETBEUI: + /* + * NetBEUI always uses 802.2 encapsulation. + * XXX - should we check both the DSAP and the + * SSAP, like this, or should we check just the + * DSAP? + */ + b0 = gen_cmp_gt(off_linktype, BPF_H, ETHERMTU); + gen_not(b0); + b1 = gen_cmp(off_linktype + 2, BPF_H, (bpf_int32) + ((LLCSAP_NETBEUI << 8) | LLCSAP_NETBEUI)); + gen_and(b0, b1); + return b1; + + case LLCSAP_IPX: + /* + * Check for; + * + * Ethernet_II frames, which are Ethernet + * frames with a frame type of ETHERTYPE_IPX; + * + * Ethernet_802.3 frames, which are 802.3 + * frames (i.e., the type/length field is + * a length field, <= ETHERMTU, rather than + * a type field) with the first two bytes + * after the Ethernet/802.3 header being + * 0xFFFF; + * + * Ethernet_802.2 frames, which are 802.3 + * frames with an 802.2 LLC header and + * with the IPX LSAP as the DSAP in the LLC + * header; + * + * Ethernet_SNAP frames, which are 802.3 + * frames with an LLC header and a SNAP + * header and with an OUI of 0x000000 + * (encapsulated Ethernet) and a protocol + * ID of ETHERTYPE_IPX in the SNAP header. + * + * XXX - should we generate the same code both + * for tests for LLCSAP_IPX and for ETHERTYPE_IPX? + */ + + /* + * This generates code to check both for the + * IPX LSAP (Ethernet_802.2) and for Ethernet_802.3. + */ + b0 = gen_cmp(off_linktype + 2, BPF_B, + (bpf_int32)LLCSAP_IPX); + b1 = gen_cmp(off_linktype + 2, BPF_H, + (bpf_int32)0xFFFF); + gen_or(b0, b1); + + /* + * Now we add code to check for SNAP frames with + * ETHERTYPE_IPX, i.e. Ethernet_SNAP. + */ + b0 = gen_snap(0x000000, ETHERTYPE_IPX, 14); + gen_or(b0, b1); + + /* + * Now we generate code to check for 802.3 + * frames in general. + */ + b0 = gen_cmp_gt(off_linktype, BPF_H, ETHERMTU); + gen_not(b0); + + /* + * Now add the check for 802.3 frames before the + * check for Ethernet_802.2 and Ethernet_802.3, + * as those checks should only be done on 802.3 + * frames, not on Ethernet frames. + */ + gen_and(b0, b1); + + /* + * Now add the check for Ethernet_II frames, and + * do that before checking for the other frame + * types. + */ + b0 = gen_cmp(off_linktype, BPF_H, + (bpf_int32)ETHERTYPE_IPX); + gen_or(b0, b1); + return b1; + + case ETHERTYPE_ATALK: + case ETHERTYPE_AARP: + /* + * EtherTalk (AppleTalk protocols on Ethernet link + * layer) may use 802.2 encapsulation. + */ + + /* + * Check for 802.2 encapsulation (EtherTalk phase 2?); + * we check for an Ethernet type field less than + * 1500, which means it's an 802.3 length field. + */ + b0 = gen_cmp_gt(off_linktype, BPF_H, ETHERMTU); + gen_not(b0); + + /* + * 802.2-encapsulated ETHERTYPE_ATALK packets are + * SNAP packets with an organization code of + * 0x080007 (Apple, for Appletalk) and a protocol + * type of ETHERTYPE_ATALK (Appletalk). + * + * 802.2-encapsulated ETHERTYPE_AARP packets are + * SNAP packets with an organization code of + * 0x000000 (encapsulated Ethernet) and a protocol + * type of ETHERTYPE_AARP (Appletalk ARP). + */ + if (proto == ETHERTYPE_ATALK) + b1 = gen_snap(0x080007, ETHERTYPE_ATALK, 14); + else /* proto == ETHERTYPE_AARP */ + b1 = gen_snap(0x000000, ETHERTYPE_AARP, 14); + gen_and(b0, b1); + + /* + * Check for Ethernet encapsulation (Ethertalk + * phase 1?); we just check for the Ethernet + * protocol type. + */ + b0 = gen_cmp(off_linktype, BPF_H, (bpf_int32)proto); + + gen_or(b0, b1); + return b1; + + default: + if (proto <= ETHERMTU) { + /* + * This is an LLC SAP value, so the frames + * that match would be 802.2 frames. + * Check that the frame is an 802.2 frame + * (i.e., that the length/type field is + * a length field, <= ETHERMTU) and + * then check the DSAP. + */ + b0 = gen_cmp_gt(off_linktype, BPF_H, ETHERMTU); + gen_not(b0); + b1 = gen_cmp(off_linktype + 2, BPF_B, + (bpf_int32)proto); + gen_and(b0, b1); + return b1; + } else { + /* + * This is an Ethernet type, so compare + * the length/type field with it (if + * the frame is an 802.2 frame, the length + * field will be <= ETHERMTU, and, as + * "proto" is > ETHERMTU, this test + * will fail and the frame won't match, + * which is what we want). + */ + return gen_cmp(off_linktype, BPF_H, + (bpf_int32)proto); + } + } + break; + + case DLT_IEEE802_11: + case DLT_PRISM_HEADER: + case DLT_FDDI: + case DLT_IEEE802: + case DLT_ATM_RFC1483: + case DLT_ATM_CLIP: + /* + * XXX - handle token-ring variable-length header. + */ + switch (proto) { + + case LLCSAP_ISONS: + return gen_cmp(off_linktype, BPF_H, (long) + ((LLCSAP_ISONS << 8) | LLCSAP_ISONS)); + + case LLCSAP_NETBEUI: + return gen_cmp(off_linktype, BPF_H, (long) + ((LLCSAP_NETBEUI << 8) | LLCSAP_NETBEUI)); + + case LLCSAP_IPX: + /* + * XXX - are there ever SNAP frames for IPX on + * non-Ethernet 802.x networks? + */ + return gen_cmp(off_linktype, BPF_B, + (bpf_int32)LLCSAP_IPX); + + case ETHERTYPE_ATALK: + /* + * 802.2-encapsulated ETHERTYPE_ATALK packets are + * SNAP packets with an organization code of + * 0x080007 (Apple, for Appletalk) and a protocol + * type of ETHERTYPE_ATALK (Appletalk). + * + * XXX - check for an organization code of + * encapsulated Ethernet as well? + */ + return gen_snap(0x080007, ETHERTYPE_ATALK, + off_linktype); + break; + + default: + /* + * XXX - we don't have to check for IPX 802.3 + * here, but should we check for the IPX Ethertype? + */ + if (proto <= ETHERMTU) { + /* + * This is an LLC SAP value, so check + * the DSAP. + */ + return gen_cmp(off_linktype, BPF_B, + (bpf_int32)proto); + } else { + /* + * This is an Ethernet type; we assume + * that it's unlikely that it'll + * appear in the right place at random, + * and therefore check only the + * location that would hold the Ethernet + * type in a SNAP frame with an organization + * code of 0x000000 (encapsulated Ethernet). + * + * XXX - if we were to check for the SNAP DSAP + * and LSAP, as per XXX, and were also to check + * for an organization code of 0x000000 + * (encapsulated Ethernet), we'd do + * + * return gen_snap(0x000000, proto, + * off_linktype); + * + * here; for now, we don't, as per the above. + * I don't know whether it's worth the + * extra CPU time to do the right check + * or not. + */ + return gen_cmp(off_linktype+6, BPF_H, + (bpf_int32)proto); + } + } + break; + + case DLT_LINUX_SLL: + switch (proto) { + + case LLCSAP_ISONS: + /* + * OSI protocols always use 802.2 encapsulation. + * XXX - should we check both the DSAP and the + * LSAP, like this, or should we check just the + * DSAP? + */ + b0 = gen_cmp(off_linktype, BPF_H, LINUX_SLL_P_802_2); + b1 = gen_cmp(off_linktype + 2, BPF_H, (bpf_int32) + ((LLCSAP_ISONS << 8) | LLCSAP_ISONS)); + gen_and(b0, b1); + return b1; + + case LLCSAP_NETBEUI: + /* + * NetBEUI always uses 802.2 encapsulation. + * XXX - should we check both the DSAP and the + * LSAP, like this, or should we check just the + * DSAP? + */ + b0 = gen_cmp(off_linktype, BPF_H, LINUX_SLL_P_802_2); + b1 = gen_cmp(off_linktype + 2, BPF_H, (bpf_int32) + ((LLCSAP_NETBEUI << 8) | LLCSAP_NETBEUI)); + gen_and(b0, b1); + return b1; + + case LLCSAP_IPX: + /* + * Ethernet_II frames, which are Ethernet + * frames with a frame type of ETHERTYPE_IPX; + * + * Ethernet_802.3 frames, which have a frame + * type of LINUX_SLL_P_802_3; + * + * Ethernet_802.2 frames, which are 802.3 + * frames with an 802.2 LLC header (i.e, have + * a frame type of LINUX_SLL_P_802_2) and + * with the IPX LSAP as the DSAP in the LLC + * header; + * + * Ethernet_SNAP frames, which are 802.3 + * frames with an LLC header and a SNAP + * header and with an OUI of 0x000000 + * (encapsulated Ethernet) and a protocol + * ID of ETHERTYPE_IPX in the SNAP header. + * + * First, do the checks on LINUX_SLL_P_802_2 + * frames; generate the check for either + * Ethernet_802.2 or Ethernet_SNAP frames, and + * then put a check for LINUX_SLL_P_802_2 frames + * before it. + */ + b0 = gen_cmp(off_linktype + 2, BPF_B, + (bpf_int32)LLCSAP_IPX); + b1 = gen_snap(0x000000, ETHERTYPE_IPX, + off_linktype + 2); + gen_or(b0, b1); + b0 = gen_cmp(off_linktype, BPF_H, LINUX_SLL_P_802_2); + gen_and(b0, b1); + + /* + * Now check for 802.3 frames and OR that with + * the previous test. + */ + b0 = gen_cmp(off_linktype, BPF_H, LINUX_SLL_P_802_3); + gen_or(b0, b1); + + /* + * Now add the check for Ethernet_II frames, and + * do that before checking for the other frame + * types. + */ + b0 = gen_cmp(off_linktype, BPF_H, + (bpf_int32)ETHERTYPE_IPX); + gen_or(b0, b1); + return b1; + + case ETHERTYPE_ATALK: + case ETHERTYPE_AARP: + /* + * EtherTalk (AppleTalk protocols on Ethernet link + * layer) may use 802.2 encapsulation. + */ + + /* + * Check for 802.2 encapsulation (EtherTalk phase 2?); + * we check for the 802.2 protocol type in the + * "Ethernet type" field. + */ + b0 = gen_cmp(off_linktype, BPF_H, LINUX_SLL_P_802_2); + + /* + * 802.2-encapsulated ETHERTYPE_ATALK packets are + * SNAP packets with an organization code of + * 0x080007 (Apple, for Appletalk) and a protocol + * type of ETHERTYPE_ATALK (Appletalk). + * + * 802.2-encapsulated ETHERTYPE_AARP packets are + * SNAP packets with an organization code of + * 0x000000 (encapsulated Ethernet) and a protocol + * type of ETHERTYPE_AARP (Appletalk ARP). + */ + if (proto == ETHERTYPE_ATALK) + b1 = gen_snap(0x080007, ETHERTYPE_ATALK, + off_linktype + 2); + else /* proto == ETHERTYPE_AARP */ + b1 = gen_snap(0x000000, ETHERTYPE_AARP, + off_linktype + 2); + gen_and(b0, b1); + + /* + * Check for Ethernet encapsulation (Ethertalk + * phase 1?); we just check for the Ethernet + * protocol type. + */ + b0 = gen_cmp(off_linktype, BPF_H, (bpf_int32)proto); + + gen_or(b0, b1); + return b1; + + default: + if (proto <= ETHERMTU) { + /* + * This is an LLC SAP value, so the frames + * that match would be 802.2 frames. + * Check for the 802.2 protocol type + * in the "Ethernet type" field, and + * then check the DSAP. + */ + b0 = gen_cmp(off_linktype, BPF_H, + LINUX_SLL_P_802_2); + b1 = gen_cmp(off_linktype + 2, BPF_B, + (bpf_int32)proto); + gen_and(b0, b1); + return b1; + } else { + /* + * This is an Ethernet type, so compare + * the length/type field with it (if + * the frame is an 802.2 frame, the length + * field will be <= ETHERMTU, and, as + * "proto" is > ETHERMTU, this test + * will fail and the frame won't match, + * which is what we want). + */ + return gen_cmp(off_linktype, BPF_H, + (bpf_int32)proto); + } + } + break; + case DLT_SLIP: - return gen_false(); + case DLT_SLIP_BSDOS: + case DLT_RAW: + /* + * These types don't provide any type field; packets + * are always IP. + * + * XXX - for IPv4, check for a version number of 4, and, + * for IPv6, check for a version number of 6? + */ + switch (proto) { + + case ETHERTYPE_IP: +#ifdef INET6 + case ETHERTYPE_IPV6: +#endif + return gen_true(); /* always true */ + + default: + return gen_false(); /* always false */ + } + break; case DLT_PPP: - if (proto == ETHERTYPE_IP) + case DLT_PPP_SERIAL: + case DLT_PPP_ETHER: + /* + * We use Ethernet protocol types inside libpcap; + * map them to the corresponding PPP protocol types. + */ + switch (proto) { + + case ETHERTYPE_IP: proto = PPP_IP; /* XXX was 0x21 */ + break; + #ifdef INET6 - else if (proto == ETHERTYPE_IPV6) + case ETHERTYPE_IPV6: proto = PPP_IPV6; + break; #endif + + case ETHERTYPE_DN: + proto = PPP_DECNET; + break; + + case ETHERTYPE_ATALK: + proto = PPP_APPLE; + break; + + case ETHERTYPE_NS: + proto = PPP_NS; + break; + + case LLCSAP_ISONS: + proto = PPP_OSI; + break; + + case LLCSAP_8021D: + /* + * I'm assuming the "Bridging PDU"s that go + * over PPP are Spanning Tree Protocol + * Bridging PDUs. + */ + proto = PPP_BRPDU; + break; + + case LLCSAP_IPX: + proto = PPP_IPX; + break; + } break; case DLT_PPP_BSDOS: + /* + * We use Ethernet protocol types inside libpcap; + * map them to the corresponding PPP protocol types. + */ switch (proto) { case ETHERTYPE_IP: @@ -644,23 +1347,183 @@ gen_linktype(proto) case ETHERTYPE_NS: proto = PPP_NS; break; + + case LLCSAP_ISONS: + proto = PPP_OSI; + break; + + case LLCSAP_8021D: + /* + * I'm assuming the "Bridging PDU"s that go + * over PPP are Spanning Tree Protocol + * Bridging PDUs. + */ + proto = PPP_BRPDU; + break; + + case LLCSAP_IPX: + proto = PPP_IPX; + break; + } + break; + + case DLT_NULL: + case DLT_LOOP: + /* + * For DLT_NULL, the link-layer header is a 32-bit + * word containing an AF_ value in *host* byte order. + * + * In addition, if we're reading a saved capture file, + * the host byte order in the capture may not be the + * same as the host byte order on this machine. + * + * For DLT_LOOP, the link-layer header is a 32-bit + * word containing an AF_ value in *network* byte order. + * + * XXX - AF_ values may, unfortunately, be platform- + * dependent; for example, FreeBSD's AF_INET6 is 24 + * whilst NetBSD's and OpenBSD's is 26. + * + * This means that, when reading a capture file, just + * checking for our AF_INET6 value won't work if the + * capture file came from another OS. + */ + switch (proto) { + + case ETHERTYPE_IP: + proto = AF_INET; + break; + +#ifdef INET6 + case ETHERTYPE_IPV6: + proto = AF_INET6; + break; +#endif + + default: + /* + * Not a type on which we support filtering. + * XXX - support those that have AF_ values + * #defined on this platform, at least? + */ + return gen_false(); + } + + if (linktype == DLT_NULL) { + /* + * The AF_ value is in host byte order, but + * the BPF interpreter will convert it to + * network byte order. + * + * If this is a save file, and it's from a + * machine with the opposite byte order to + * ours, we byte-swap the AF_ value. + * + * Then we run it through "htonl()", and + * generate code to compare against the result. + */ + if (bpf_pcap->sf.rfile != NULL && + bpf_pcap->sf.swapped) + proto = SWAPLONG(proto); + proto = htonl(proto); + } + return (gen_cmp(0, BPF_W, (bpf_int32)proto)); + + case DLT_ARCNET: + /* + * XXX should we check for first fragment if the protocol + * uses PHDS? + */ + switch(proto) { + default: + return gen_false(); +#ifdef INET6 + case ETHERTYPE_IPV6: + return(gen_cmp(2, BPF_B, + (bpf_int32)htonl(ARCTYPE_INET6))); +#endif /* INET6 */ + case ETHERTYPE_IP: + b0 = gen_cmp(2, BPF_B, (bpf_int32)htonl(ARCTYPE_IP)); + b1 = gen_cmp(2, BPF_B, + (bpf_int32)htonl(ARCTYPE_IP_OLD)); + gen_or(b0, b1); + return(b1); + case ETHERTYPE_ARP: + b0 = gen_cmp(2, BPF_B, (bpf_int32)htonl(ARCTYPE_ARP)); + b1 = gen_cmp(2, BPF_B, + (bpf_int32)htonl(ARCTYPE_ARP_OLD)); + gen_or(b0, b1); + return(b1); + case ETHERTYPE_REVARP: + return(gen_cmp(2, BPF_B, + (bpf_int32)htonl(ARCTYPE_REVARP))); + case ETHERTYPE_ATALK: + return(gen_cmp(2, BPF_B, + (bpf_int32)htonl(ARCTYPE_ATALK))); } break; - case DLT_NULL: - /* XXX */ - if (proto == ETHERTYPE_IP) - return (gen_cmp(0, BPF_W, (bpf_int32)htonl(AF_INET))); -#ifdef INET6 - else if (proto == ETHERTYPE_IPV6) - return (gen_cmp(0, BPF_W, (bpf_int32)htonl(AF_INET6))); -#endif - else + case DLT_LTALK: + switch (proto) { + case ETHERTYPE_ATALK: + return gen_true(); + default: return gen_false(); + } + break; } + + /* + * All the types that have no encapsulation should either be + * handled as DLT_SLIP, DLT_SLIP_BSDOS, and DLT_RAW are, if + * all packets are IP packets, or should be handled in some + * special case, if none of them are (if some are and some + * aren't, the lack of encapsulation is a problem, as we'd + * have to find some other way of determining the packet type). + * + * Therefore, if "off_linktype" is -1, there's an error. + */ + if (off_linktype == -1) + abort(); + + /* + * Any type not handled above should always have an Ethernet + * type at an offset of "off_linktype". (PPP is partially + * handled above - the protocol type is mapped from the + * Ethernet and LLC types we use internally to the corresponding + * PPP type - but the PPP type is always specified by a value + * at "off_linktype", so we don't have to do the code generation + * above.) + */ return gen_cmp(off_linktype, BPF_H, (bpf_int32)proto); } +/* + * Check for an LLC SNAP packet with a given organization code and + * protocol type; we check the entire contents of the 802.2 LLC and + * snap headers, checking for DSAP and SSAP of SNAP and a control + * field of 0x03 in the LLC header, and for the specified organization + * code and protocol type in the SNAP header. + */ +static struct block * +gen_snap(orgcode, ptype, offset) + bpf_u_int32 orgcode; + bpf_u_int32 ptype; + u_int offset; +{ + u_char snapblock[8]; + + snapblock[0] = LLCSAP_SNAP; /* DSAP = SNAP */ + snapblock[1] = LLCSAP_SNAP; /* SSAP = SNAP */ + snapblock[2] = 0x03; /* control = UI */ + snapblock[3] = (orgcode >> 16); /* upper 8 bits of organization code */ + snapblock[4] = (orgcode >> 8); /* middle 8 bits of organization code */ + snapblock[5] = (orgcode >> 0); /* lower 8 bits of organization code */ + snapblock[6] = (ptype >> 8); /* upper 8 bits of protocol type */ + snapblock[7] = (ptype >> 0); /* lower 8 bits of protocol type */ + return gen_bcmp(offset, 8, snapblock); +} + static struct block * gen_hostop(addr, mask, dir, proto, src_off, dst_off) bpf_u_int32 addr; @@ -830,6 +1693,40 @@ gen_fhostop(eaddr, dir) /* NOTREACHED */ } +/* + * Like gen_ehostop, but for DLT_IEEE802 (Token Ring) + */ +static struct block * +gen_thostop(eaddr, dir) + register const u_char *eaddr; + register int dir; +{ + register struct block *b0, *b1; + + switch (dir) { + case Q_SRC: + return gen_bcmp(8, 6, eaddr); + + case Q_DST: + return gen_bcmp(2, 6, eaddr); + + case Q_AND: + b0 = gen_thostop(eaddr, Q_SRC); + b1 = gen_thostop(eaddr, Q_DST); + gen_and(b0, b1); + return b1; + + case Q_DEFAULT: + case Q_OR: + b0 = gen_thostop(eaddr, Q_SRC); + b1 = gen_thostop(eaddr, Q_DST); + gen_or(b0, b1); + return b1; + } + abort(); + /* NOTREACHED */ +} + /* * 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 @@ -885,6 +1782,9 @@ gen_dnhostop(addr, dir, base_off) gen_or(b0, b1); return b1; + case Q_ISO: + bpf_error("ISO host filtering not implemented"); + default: abort(); } @@ -931,10 +1831,12 @@ gen_host(addr, mask, proto, dir) 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); + if (off_linktype != -1) { + 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: @@ -952,6 +1854,9 @@ gen_host(addr, mask, proto, dir) case Q_TCP: bpf_error("'tcp' modifier applied to host"); + case Q_SCTP: + bpf_error("'sctp' modifier applied to host"); + case Q_UDP: bpf_error("'udp' modifier applied to host"); @@ -967,9 +1872,15 @@ gen_host(addr, mask, proto, dir) case Q_PIM: bpf_error("'pim' modifier applied to host"); + case Q_VRRP: + bpf_error("'vrrp' modifier applied to host"); + case Q_ATALK: bpf_error("ATALK host filtering not implemented"); + case Q_AARP: + bpf_error("AARP host filtering not implemented"); + case Q_DECNET: return gen_dnhostop(addr, dir, off_nl); @@ -999,6 +1910,27 @@ gen_host(addr, mask, proto, dir) case Q_ESP: bpf_error("'esp' modifier applied to host"); + case Q_ISO: + bpf_error("ISO host filtering not implemented"); + + case Q_ESIS: + bpf_error("'esis' modifier applied to host"); + + case Q_ISIS: + bpf_error("'isis' modifier applied to host"); + + case Q_CLNP: + bpf_error("'clnp' modifier applied to host"); + + case Q_STP: + bpf_error("'stp' modifier applied to host"); + + case Q_IPX: + bpf_error("IPX host filtering not implemented"); + + case Q_NETBEUI: + bpf_error("'netbeui' modifier applied to host"); + default: abort(); } @@ -1013,8 +1945,6 @@ gen_host6(addr, mask, proto, dir) int proto; int dir; { - struct block *b0, *b1; - switch (proto) { case Q_DEFAULT: @@ -1029,6 +1959,9 @@ gen_host6(addr, mask, proto, dir) case Q_ARP: bpf_error("'arp' modifier applied to ip6 host"); + case Q_SCTP: + bpf_error("'sctp' modifier applied to host"); + case Q_TCP: bpf_error("'tcp' modifier applied to host"); @@ -1047,9 +1980,15 @@ gen_host6(addr, mask, proto, dir) case Q_PIM: bpf_error("'pim' modifier applied to host"); + case Q_VRRP: + bpf_error("'vrrp' modifier applied to host"); + case Q_ATALK: bpf_error("ATALK host filtering not implemented"); + case Q_AARP: + bpf_error("AARP host filtering not implemented"); + case Q_DECNET: bpf_error("'decnet' modifier applied to ip6 host"); @@ -1078,6 +2017,27 @@ gen_host6(addr, mask, proto, dir) case Q_ESP: bpf_error("'esp' modifier applied to host"); + case Q_ISO: + bpf_error("ISO host filtering not implemented"); + + case Q_ESIS: + bpf_error("'esis' modifier applied to host"); + + case Q_ISIS: + bpf_error("'isis' modifier applied to host"); + + case Q_CLNP: + bpf_error("'clnp' modifier applied to host"); + + case Q_STP: + bpf_error("'stp' modifier applied to host"); + + case Q_IPX: + bpf_error("IPX host filtering not implemented"); + + case Q_NETBEUI: + bpf_error("'netbeui' modifier applied to host"); + default: abort(); } @@ -1085,6 +2045,7 @@ gen_host6(addr, mask, proto, dir) } #endif /*INET6*/ +#ifndef INET6 static struct block * gen_gateway(eaddr, alist, proto, dir) const u_char *eaddr; @@ -1106,9 +2067,11 @@ gen_gateway(eaddr, alist, proto, dir) b0 = gen_ehostop(eaddr, Q_OR); else if (linktype == DLT_FDDI) b0 = gen_fhostop(eaddr, Q_OR); + else if (linktype == DLT_IEEE802) + b0 = gen_thostop(eaddr, Q_OR); else bpf_error( - "'gateway' supported only on ethernet or FDDI"); + "'gateway' supported only on ethernet, FDDI or token ring"); b1 = gen_host(**alist++, 0xffffffff, proto, Q_OR); while (*alist) { @@ -1123,15 +2086,27 @@ gen_gateway(eaddr, alist, proto, dir) bpf_error("illegal modifier of 'gateway'"); /* NOTREACHED */ } +#endif struct block * gen_proto_abbrev(proto) int proto; { - struct block *b0, *b1; +#ifdef INET6 + struct block *b0; +#endif + struct block *b1; switch (proto) { + case Q_SCTP: + b1 = gen_proto(IPPROTO_SCTP, Q_IP, Q_DEFAULT); +#ifdef INET6 + b0 = gen_proto(IPPROTO_SCTP, Q_IPV6, Q_DEFAULT); + gen_or(b0, b1); +#endif + break; + case Q_TCP: b1 = gen_proto(IPPROTO_TCP, Q_IP, Q_DEFAULT); #ifdef INET6 @@ -1152,8 +2127,12 @@ gen_proto_abbrev(proto) b1 = gen_proto(IPPROTO_ICMP, Q_IP, Q_DEFAULT); break; +#ifndef IPPROTO_IGMP +#define IPPROTO_IGMP 2 +#endif + case Q_IGMP: - b1 = gen_proto(2, Q_IP, Q_DEFAULT); + b1 = gen_proto(IPPROTO_IGMP, Q_IP, Q_DEFAULT); break; #ifndef IPPROTO_IGRP @@ -1161,7 +2140,6 @@ gen_proto_abbrev(proto) #endif case Q_IGRP: b1 = gen_proto(IPPROTO_IGRP, Q_IP, Q_DEFAULT); - gen_and(b0, b1); break; #ifndef IPPROTO_PIM @@ -1176,6 +2154,14 @@ gen_proto_abbrev(proto) #endif break; +#ifndef IPPROTO_VRRP +#define IPPROTO_VRRP 112 +#endif + + case Q_VRRP: + b1 = gen_proto(IPPROTO_VRRP, Q_IP, Q_DEFAULT); + break; + case Q_IP: b1 = gen_linktype(ETHERTYPE_IP); break; @@ -1195,6 +2181,10 @@ gen_proto_abbrev(proto) b1 = gen_linktype(ETHERTYPE_ATALK); break; + case Q_AARP: + b1 = gen_linktype(ETHERTYPE_AARP); + break; + case Q_DECNET: b1 = gen_linktype(ETHERTYPE_DN); break; @@ -1250,6 +2240,34 @@ gen_proto_abbrev(proto) #endif break; + case Q_ISO: + b1 = gen_linktype(LLCSAP_ISONS); + break; + + case Q_ESIS: + b1 = gen_proto(ISO9542_ESIS, Q_ISO, Q_DEFAULT); + break; + + case Q_ISIS: + b1 = gen_proto(ISO10589_ISIS, Q_ISO, Q_DEFAULT); + break; + + case Q_CLNP: + b1 = gen_proto(ISO8473_CLNP, Q_ISO, Q_DEFAULT); + break; + + case Q_STP: + b1 = gen_linktype(LLCSAP_8021D); + break; + + case Q_IPX: + b1 = gen_linktype(LLCSAP_IPX); + break; + + case Q_NETBEUI: + b1 = gen_linktype(LLCSAP_NETBEUI); + break; + default: abort(); } @@ -1359,6 +2377,7 @@ gen_port(port, ip_proto, dir) switch (ip_proto) { case IPPROTO_UDP: case IPPROTO_TCP: + case IPPROTO_SCTP: b1 = gen_portop(port, ip_proto, dir); break; @@ -1366,6 +2385,8 @@ gen_port(port, ip_proto, dir) tmp = gen_portop(port, IPPROTO_TCP, dir); b1 = gen_portop(port, IPPROTO_UDP, dir); gen_or(tmp, b1); + tmp = gen_portop(port, IPPROTO_SCTP, dir); + gen_or(tmp, b1); break; default: @@ -1429,6 +2450,7 @@ gen_port6(port, ip_proto, dir) switch (ip_proto) { case IPPROTO_UDP: case IPPROTO_TCP: + case IPPROTO_SCTP: b1 = gen_portop6(port, ip_proto, dir); break; @@ -1436,6 +2458,8 @@ gen_port6(port, ip_proto, dir) tmp = gen_portop6(port, IPPROTO_TCP, dir); b1 = gen_portop6(port, IPPROTO_UDP, dir); gen_or(tmp, b1); + tmp = gen_portop6(port, IPPROTO_SCTP, dir); + gen_or(tmp, b1); break; default: @@ -1457,6 +2481,7 @@ lookup_proto(name, proto) case Q_DEFAULT: case Q_IP: + case Q_IPV6: v = pcap_nametoproto(name); if (v == PROTO_UNDEF) bpf_error("unknown ip proto '%s'", name); @@ -1469,6 +2494,17 @@ lookup_proto(name, proto) bpf_error("unknown ether proto '%s'", name); break; + case Q_ISO: + if (strcmp(name, "esis") == 0) + v = ISO9542_ESIS; + else if (strcmp(name, "isis") == 0) + v = ISO10589_ISIS; + else if (strcmp(name, "clnp") == 0) + v = ISO8473_CLNP; + else + bpf_error("unknown osi proto '%s'", name); + break; + default: v = PROTO_UNDEF; break; @@ -1476,25 +2512,30 @@ lookup_proto(name, proto) return v; } +#if 0 struct stmt * gen_joinsp(s, n) struct stmt **s; int n; { + return NULL; } +#endif -struct block * +static struct block * gen_protochain(v, proto, dir) int v; int proto; int dir; { +#ifdef NO_PROTOCHAIN + return gen_proto(v, proto, dir); +#else struct block *b0, *b; - struct slist *s[100], *sp; + struct slist *s[100]; int fix2, fix3, fix4, fix5; int ahcheck, again, end; int i, max; - int reg1 = alloc_reg(); int reg2 = alloc_reg(); memset(s, 0, sizeof(s)); @@ -1506,7 +2547,7 @@ gen_protochain(v, proto, dir) break; case Q_DEFAULT: b0 = gen_protochain(v, Q_IP, dir); - b = gen_protochain(v, Q_IP, dir); + b = gen_protochain(v, Q_IPV6, dir); gen_or(b0, b); return b; default: @@ -1566,6 +2607,9 @@ gen_protochain(v, proto, dir) fix5 = i; i++; +#ifndef IPPROTO_NONE +#define IPPROTO_NONE 59 +#endif /* if (A == IPPROTO_NONE) goto end */ s[i] = new_stmt(BPF_JMP|BPF_JEQ|BPF_K); s[i]->s.jt = NULL; /*later*/ @@ -1613,15 +2657,22 @@ gen_protochain(v, proto, dir) /* * in short, - * A = P[X + 1]; - * X = X + (P[X] + 1) * 8; + * A = P[X]; + * X = X + (P[X + 1] + 1) * 8; */ /* A = X */ s[i] = new_stmt(BPF_MISC|BPF_TXA); i++; - /* MEM[reg1] = A */ + /* A = P[X + packet head] */ + s[i] = new_stmt(BPF_LD|BPF_IND|BPF_B); + s[i]->s.k = off_nl; + i++; + /* MEM[reg2] = A */ s[i] = new_stmt(BPF_ST); - s[i]->s.k = reg1; + s[i]->s.k = reg2; + i++; + /* A = X */ + s[i] = new_stmt(BPF_MISC|BPF_TXA); i++; /* A += 1 */ s[i] = new_stmt(BPF_ALU|BPF_ADD|BPF_K); @@ -1634,18 +2685,6 @@ gen_protochain(v, proto, dir) s[i] = new_stmt(BPF_LD|BPF_IND|BPF_B); s[i]->s.k = off_nl; i++; - /* MEM[reg2] = A */ - s[i] = new_stmt(BPF_ST); - s[i]->s.k = reg2; - i++; - /* X = MEM[reg1] */ - s[i] = new_stmt(BPF_LDX|BPF_MEM); - s[i]->s.k = reg1; - i++; - /* A = P[X + packet head] */ - s[i] = new_stmt(BPF_LD|BPF_IND|BPF_B); - s[i]->s.k = off_nl; - i++; /* A += 1 */ s[i] = new_stmt(BPF_ALU|BPF_ADD|BPF_K); s[i]->s.k = 1; @@ -1695,23 +2734,12 @@ gen_protochain(v, proto, dir) /* * in short, - * A = P[X + 1]; - * X = X + (P[X] + 2) * 4; + * A = P[X]; + * X = X + (P[X + 1] + 2) * 4; */ /* A = X */ s[i - 1]->s.jt = s[i] = new_stmt(BPF_MISC|BPF_TXA); i++; - /* MEM[reg1] = A */ - s[i] = new_stmt(BPF_ST); - s[i]->s.k = reg1; - i++; - /* A += 1 */ - s[i] = new_stmt(BPF_ALU|BPF_ADD|BPF_K); - s[i]->s.k = 1; - i++; - /* X = A */ - s[i] = new_stmt(BPF_MISC|BPF_TAX); - i++; /* A = P[X + packet head]; */ s[i] = new_stmt(BPF_LD|BPF_IND|BPF_B); s[i]->s.k = off_nl; @@ -1720,9 +2748,15 @@ gen_protochain(v, proto, dir) s[i] = new_stmt(BPF_ST); s[i]->s.k = reg2; i++; - /* X = MEM[reg1] */ - s[i] = new_stmt(BPF_LDX|BPF_MEM); - s[i]->s.k = reg1; + /* A = X */ + s[i - 1]->s.jt = s[i] = new_stmt(BPF_MISC|BPF_TXA); + i++; + /* A += 1 */ + s[i] = new_stmt(BPF_ALU|BPF_ADD|BPF_K); + s[i]->s.k = 1; + i++; + /* X = A */ + s[i] = new_stmt(BPF_MISC|BPF_TAX); i++; /* A = P[X + packet head] */ s[i] = new_stmt(BPF_LD|BPF_IND|BPF_B); @@ -1773,11 +2807,11 @@ gen_protochain(v, proto, dir) b->stmts = s[1]; /*remember, s[0] is dummy*/ b->s.k = v; - free_reg(reg1); free_reg(reg2); gen_and(b0, b); return b; +#endif } static struct block * @@ -1811,6 +2845,12 @@ gen_proto(v, proto, dir) gen_and(b0, b1); return b1; + case Q_ISO: + b0 = gen_linktype(LLCSAP_ISONS); + b1 = gen_cmp(off_nl_nosnap, BPF_B, (long)v); + gen_and(b0, b1); + return b1; + case Q_ARP: bpf_error("arp does not encapsulate another protocol"); /* NOTREACHED */ @@ -1854,6 +2894,10 @@ gen_proto(v, proto, dir) bpf_error("'tcp proto' is bogus"); /* NOTREACHED */ + case Q_SCTP: + bpf_error("'sctp proto' is bogus"); + /* NOTREACHED */ + case Q_ICMP: bpf_error("'icmp proto' is bogus"); /* NOTREACHED */ @@ -1870,6 +2914,10 @@ gen_proto(v, proto, dir) bpf_error("'pim proto' is bogus"); /* NOTREACHED */ + case Q_VRRP: + bpf_error("'vrrp proto' is bogus"); + /* NOTREACHED */ + #ifdef INET6 case Q_IPV6: b0 = gen_linktype(ETHERTYPE_IPV6); @@ -1891,6 +2939,15 @@ gen_proto(v, proto, dir) case Q_ESP: bpf_error("'ah proto' is bogus"); + case Q_STP: + bpf_error("'stp proto' is bogus"); + + case Q_IPX: + bpf_error("'ipx proto' is bogus"); + + case Q_NETBEUI: + bpf_error("'netbeui proto' is bogus"); + default: abort(); /* NOTREACHED */ @@ -1907,8 +2964,10 @@ gen_scode(name, q) int dir = q.dir; int tproto; u_char *eaddr; - bpf_u_int32 mask, addr, **alist; -#ifdef INET6 + bpf_u_int32 mask, addr; +#ifndef INET6 + bpf_u_int32 **alist; +#else int tproto6; struct sockaddr_in *sin; struct sockaddr_in6 *sin6; @@ -1942,18 +3001,31 @@ gen_scode(name, q) if (eaddr == NULL) bpf_error( "unknown ether host '%s'", name); - return gen_ehostop(eaddr, dir); + b = gen_ehostop(eaddr, dir); + free(eaddr); + return b; case DLT_FDDI: eaddr = pcap_ether_hostton(name); if (eaddr == NULL) bpf_error( "unknown FDDI host '%s'", name); - return gen_fhostop(eaddr, dir); + b = gen_fhostop(eaddr, dir); + free(eaddr); + return b; + + case DLT_IEEE802: + eaddr = pcap_ether_hostton(name); + if (eaddr == NULL) + bpf_error( + "unknown token ring host '%s'", name); + b = gen_thostop(eaddr, dir); + free(eaddr); + return b; default: bpf_error( - "only ethernet/FDDI supports link-level host name"); + "only ethernet/FDDI/token ring supports link-level host name"); break; } } else if (proto == Q_DECNET) { @@ -1981,7 +3053,7 @@ gen_scode(name, q) return b; #else memset(&mask128, 0xff, sizeof(mask128)); - res0 = res = pcap_nametoaddr(name); + res0 = res = pcap_nametoaddrinfo(name); if (res == NULL) bpf_error("unknown host '%s'", name); b = tmp = NULL; @@ -1990,40 +3062,55 @@ gen_scode(name, q) tproto = Q_IP; tproto6 = Q_IPV6; } - while (res) { + for (res = res0; res; res = res->ai_next) { switch (res->ai_family) { case AF_INET: + if (tproto == Q_IPV6) + continue; + sin = (struct sockaddr_in *) res->ai_addr; tmp = gen_host(ntohl(sin->sin_addr.s_addr), 0xffffffff, tproto, dir); break; case AF_INET6: + if (tproto6 == Q_IP) + continue; + sin6 = (struct sockaddr_in6 *) res->ai_addr; tmp = gen_host6(&sin6->sin6_addr, &mask128, tproto6, dir); break; + default: + continue; } if (b) gen_or(b, tmp); b = tmp; - - res = res->ai_next; } freeaddrinfo(res0); + if (b == NULL) { + bpf_error("unknown host '%s'%s", name, + (proto == Q_DEFAULT) + ? "" + : " for specified address family"); + } return b; #endif /*INET6*/ } case Q_PORT: - if (proto != Q_DEFAULT && proto != Q_UDP && proto != Q_TCP) + if (proto != Q_DEFAULT && + proto != Q_UDP && proto != Q_TCP && proto != Q_SCTP) 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 if (real_proto == IPPROTO_SCTP) + bpf_error("port '%s' is sctp", name); else /* override PROTO_UNDEF */ real_proto = IPPROTO_UDP; @@ -2031,10 +3118,23 @@ gen_scode(name, q) if (proto == Q_TCP) { if (real_proto == IPPROTO_UDP) bpf_error("port '%s' is udp", name); + + else if (real_proto == IPPROTO_SCTP) + bpf_error("port '%s' is sctp", name); else /* override PROTO_UNDEF */ real_proto = IPPROTO_TCP; } + if (proto == Q_SCTP) { + if (real_proto == IPPROTO_UDP) + bpf_error("port '%s' is udp", name); + + else if (real_proto == IPPROTO_TCP) + bpf_error("port '%s' is tcp", name); + else + /* override PROTO_UNDEF */ + real_proto = IPPROTO_SCTP; + } #ifndef INET6 return gen_port(port, real_proto, dir); #else @@ -2055,7 +3155,9 @@ gen_scode(name, q) alist = pcap_nametoaddr(name); if (alist == NULL || *alist == NULL) bpf_error("unknown host '%s'", name); - return gen_gateway(eaddr, alist, proto, dir); + b = gen_gateway(eaddr, alist, proto, dir); + free(eaddr); + return b; #else bpf_error("'gateway' not supported in this configuration"); #endif /*INET6*/ @@ -2172,6 +3274,8 @@ gen_ncode(s, v, q) proto = IPPROTO_UDP; else if (proto == Q_TCP) proto = IPPROTO_TCP; + else if (proto == Q_SCTP) + proto = IPPROTO_SCTP; else if (proto == Q_DEFAULT) proto = PROTO_UNDEF; else @@ -2225,7 +3329,7 @@ gen_mcode6(s1, s2, masklen, q) if (s2) bpf_error("no mask %s supported", s2); - res = pcap_nametoaddr(s1); + res = pcap_nametoaddrinfo(s1); if (!res) bpf_error("invalid ip6 address %s", s1); if (res->ai_next) @@ -2234,6 +3338,7 @@ gen_mcode6(s1, s2, masklen, q) if (sizeof(mask) * 8 < masklen) bpf_error("mask length must be <= %u", (unsigned int)(sizeof(mask) * 8)); + memset(&mask, 0, sizeof(mask)); memset(&mask, 0xff, masklen / 8); if (masklen % 8) { mask.s6_addr[masklen / 8] = @@ -2277,6 +3382,9 @@ gen_ecode(eaddr, q) return gen_ehostop(eaddr, (int)q.dir); if (linktype == DLT_FDDI) return gen_fhostop(eaddr, (int)q.dir); + if (linktype == DLT_IEEE802) + return gen_thostop(eaddr, (int)q.dir); + bpf_error("ethernet addresses supported only on ethernet, FDDI or token ring"); } bpf_error("ethernet address used in non-ether expression"); /* NOTREACHED */ @@ -2368,7 +3476,7 @@ gen_load(proto, index, size) #ifdef INET6 case Q_IPV6: #endif - /* XXX Note that we assume a fixed link link header here. */ + /* XXX Note that we assume a fixed link header here. */ s = xfer_to_x(index); tmp = new_stmt(BPF_LD|BPF_IND|size); tmp->s.k = off_nl; @@ -2381,12 +3489,14 @@ gen_load(proto, index, size) index->b = b; break; + case Q_SCTP: case Q_TCP: case Q_UDP: case Q_ICMP: case Q_IGMP: case Q_IGRP: case Q_PIM: + case Q_VRRP: s = new_stmt(BPF_LDX|BPF_MSH|BPF_B); s->s.k = off_nl; sappend(s, xfer_to_a(index)); @@ -2605,6 +3715,9 @@ gen_greater(n) return gen_len(BPF_JGE, n); } +/* + * Actually, this is less than or equal. + */ struct block * gen_less(n) int n; @@ -2658,6 +3771,8 @@ gen_byteop(op, idx, val) return b; } +static u_char abroadcast[] = { 0x0 }; + struct block * gen_broadcast(proto) int proto; @@ -2670,10 +3785,14 @@ gen_broadcast(proto) case Q_DEFAULT: case Q_LINK: + if (linktype == DLT_ARCNET) + return gen_ahostop(abroadcast, Q_DST); if (linktype == DLT_EN10MB) return gen_ehostop(ebroadcast, Q_DST); if (linktype == DLT_FDDI) return gen_fhostop(ebroadcast, Q_DST); + if (linktype == DLT_IEEE802) + return gen_thostop(ebroadcast, Q_DST); bpf_error("not a broadcast link"); break; @@ -2701,6 +3820,10 @@ gen_multicast(proto) case Q_DEFAULT: case Q_LINK: + if (linktype == DLT_ARCNET) + /* all ARCnet multicasts use the same address */ + return gen_ahostop(abroadcast, Q_DST); + if (linktype == DLT_EN10MB) { /* ether[0] & 1 != 0 */ s = new_stmt(BPF_LD|BPF_B|BPF_ABS); @@ -2721,6 +3844,10 @@ gen_multicast(proto) b0->stmts = s; return b0; } + + /* TODO - check how token ring handles multicast */ + /* if (linktype == DLT_IEEE802) ... */ + /* Link not known to support multicasts */ break; @@ -2753,9 +3880,116 @@ gen_inbound(dir) { register struct block *b0; - b0 = gen_relation(BPF_JEQ, + /* + * Only some data link types support inbound/outbound qualifiers. + */ + switch (linktype) { + case DLT_SLIP: + case DLT_PPP: + b0 = gen_relation(BPF_JEQ, gen_load(Q_LINK, gen_loadi(0), 1), gen_loadi(0), dir); + break; + + default: + bpf_error("inbound/outbound not supported on linktype %d\n", + linktype); + b0 = NULL; + /* NOTREACHED */ + } + return (b0); +} + +struct block * +gen_acode(eaddr, q) + register const u_char *eaddr; + struct qual q; +{ + if ((q.addr == Q_HOST || q.addr == Q_DEFAULT) && q.proto == Q_LINK) { + if (linktype == DLT_ARCNET) + return gen_ahostop(eaddr, (int)q.dir); + } + bpf_error("ARCnet address used in non-arc expression"); + /* NOTREACHED */ +} + +static struct block * +gen_ahostop(eaddr, dir) + register const u_char *eaddr; + register int dir; +{ + register struct block *b0, *b1; + + switch (dir) { + /* src comes first, different from Ethernet */ + case Q_SRC: + return gen_bcmp(0, 1, eaddr); + + case Q_DST: + return gen_bcmp(1, 1, eaddr); + + case Q_AND: + b0 = gen_ahostop(eaddr, Q_SRC); + b1 = gen_ahostop(eaddr, Q_DST); + gen_and(b0, b1); + return b1; + + case Q_DEFAULT: + case Q_OR: + b0 = gen_ahostop(eaddr, Q_SRC); + b1 = gen_ahostop(eaddr, Q_DST); + gen_or(b0, b1); + return b1; + } + abort(); + /* NOTREACHED */ +} + +/* + * support IEEE 802.1Q VLAN trunk over ethernet + */ +struct block * +gen_vlan(vlan_num) + int vlan_num; +{ + struct block *b0; + + /* + * Change the offsets to point to the type and data fields within + * the VLAN packet. This is somewhat of a kludge. + */ + if (orig_nl == (u_int)-1) { + orig_linktype = off_linktype; /* save original values */ + orig_nl = off_nl; + orig_nl_nosnap = off_nl_nosnap; + + switch (linktype) { + + case DLT_EN10MB: + off_linktype = 16; + off_nl_nosnap = 18; + off_nl = 18; + break; + + default: + bpf_error("no VLAN support for data link type %d", + linktype); + /*NOTREACHED*/ + } + } + + /* check for VLAN */ + b0 = gen_cmp(orig_linktype, BPF_H, (bpf_int32)ETHERTYPE_8021Q); + + /* If a specific VLAN is requested, check VLAN id */ + if (vlan_num >= 0) { + struct block *b1; + + b1 = gen_cmp(orig_nl, BPF_H, (bpf_int32)vlan_num); + gen_and(b0, b1); + b0 = b1; + } + return (b0); }