X-Git-Url: https://git.tcpdump.org/libpcap/blobdiff_plain/77d52cac810982c0b361ce6a053e31f61317dd0a..2e69dd906a9660f1bf4ed5a85d06d3b89114fb0e:/gencode.c diff --git a/gencode.c b/gencode.c index 1dbd44bc..f8765fb5 100644 --- a/gencode.c +++ b/gencode.c @@ -21,26 +21,38 @@ */ #ifndef lint static const char rcsid[] = - "@(#) $Header: /tcpdump/master/libpcap/gencode.c,v 1.155 2001-06-18 08:46:29 guy Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/gencode.c,v 1.191 2003-05-02 08:37:43 guy Exp $ (LBL)"; #endif #ifdef HAVE_CONFIG_H #include "config.h" #endif +#ifdef WIN32 +#include +#else /* WIN32 */ #include #include #include +#endif /* WIN32 */ + +/* + * XXX - why was this included even on UNIX? + */ +#ifdef __MINGW32__ +#include "IP6_misc.h" +#endif + +#ifndef WIN32 + #ifdef __NetBSD__ #include #endif -struct mbuf; -struct rtentry; -#include - #include +#endif /* WIN32 */ + #include #include #include @@ -53,14 +65,18 @@ struct rtentry; #include "nlpid.h" #include "llc.h" #include "gencode.h" +#include "atmuni31.h" +#include "sunatmpos.h" #include "ppp.h" #include "sll.h" #include "arcnet.h" -#include +#include "pf.h" #ifdef INET6 -#include -#include +#ifndef WIN32 +#include /* for "struct addrinfo" */ +#endif /* WIN32 */ #endif /*INET6*/ +#include #define ETHERMTU 1500 @@ -78,6 +94,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; @@ -137,11 +156,15 @@ 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_ncmp(bpf_u_int32, bpf_u_int32, bpf_u_int32, + bpf_u_int32, bpf_u_int32, int); static struct block *gen_uncond(int); static inline struct block *gen_true(void); static inline struct block *gen_false(void); +static struct block *gen_ether_linktype(int); static struct block *gen_linktype(int); static struct block *gen_snap(bpf_u_int32, bpf_u_int32, u_int); +static struct block *gen_llc(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); @@ -150,6 +173,8 @@ 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_wlanhostop(const u_char *, int); +static struct block *gen_ipfchostop(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 @@ -174,8 +199,11 @@ 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 *); +static struct block *gen_mac_multicast(int); static struct block *gen_len(int, int); +static struct block *gen_msg_abbrev(int type); + static void * newchunk(n) u_int n; @@ -198,6 +226,8 @@ newchunk(n) bpf_error("out of memory"); size = CHUNK0SIZE << k; cp->m = (void *)malloc(size); + if (cp->m == NULL) + bpf_error("out of memory"); memset((char *)cp->m, 0, size); cp->n_left = size; if (n > size) @@ -297,7 +327,7 @@ pcap_compile(pcap_t *p, struct bpf_program *program, } netmask = mask; - + snaplen = pcap_snapshot(p); if (snaplen == 0) { snprintf(p->errbuf, PCAP_ERRBUF_SIZE, @@ -536,13 +566,111 @@ gen_bcmp(offset, size, v) return b; } +static struct block * +gen_ncmp(datasize, offset, mask, jtype, jvalue, reverse) + bpf_u_int32 datasize, offset, mask, jtype, jvalue; + int reverse; +{ + struct slist *s; + struct block *b; + + s = new_stmt(BPF_LD|datasize|BPF_ABS); + s->s.k = offset; + + if (mask != 0xffffffff) { + s->next = new_stmt(BPF_ALU|BPF_AND|BPF_K); + s->next->s.k = mask; + } + + b = new_block(JMP(jtype)); + b->stmts = s; + b->s.k = jvalue; + if (reverse && (jtype == BPF_JGT || jtype == BPF_JGE)) + gen_not(b); + 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. + * layer. These variables give the necessary offsets. + */ + +/* + * This is the offset of the beginning of the MAC-layer header. + * It's usually 0, except for ATM LANE. + */ +static u_int off_mac; + +/* + * "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; + +/* + * TRUE if the link layer includes an ATM pseudo-header. + */ +static int is_atm = 0; + +/* + * TRUE if "lane" appeared in the filter; it causes us to generate + * code that assumes LANE rather than LLC-encapsulated traffic in SunATM. + */ +static int is_lane = 0; + +/* + * These are offsets for the ATM pseudo-header. + */ +static u_int off_vpi; +static u_int off_vci; +static u_int off_proto; + +/* + * This is the offset of the first byte after the ATM pseudo_header, + * or -1 if there is no ATM pseudo-header. + */ +static u_int off_payload; + +/* + * 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 @@ -551,16 +679,39 @@ init_linktype(type) { linktype = type; + /* + * Assume it's not raw ATM with a pseudo-header, for now. + */ + off_mac = 0; + is_atm = 0; + is_lane = 0; + off_vpi = -1; + off_vci = -1; + off_proto = -1; + off_payload = -1; + + 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 = 6; /* XXX in reality, variable! */ + off_nl_nosnap = 6; /* no 802.2 LLC */ + return; + + case DLT_ARCNET_LINUX: + off_linktype = 4; + off_nl = 8; /* XXX in reality, variable! */ + off_nl_nosnap = 8; /* 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: @@ -570,6 +721,7 @@ init_linktype(type) */ off_linktype = -1; off_nl = 16; + off_nl_nosnap = 16; /* no 802.2 LLC */ return; case DLT_SLIP_BSDOS: @@ -577,12 +729,26 @@ 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_ENC: + off_linktype = 0; + off_nl = 12; + off_nl_nosnap = 12; /* no 802.2 LLC */ + return; + + case DLT_PFLOG: + off_linktype = 0; + off_nl = 28; + off_nl_nosnap = 28; /* no 802.2 LLC */ return; case DLT_PPP: @@ -590,21 +756,23 @@ init_linktype(type) 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: /* NetBSD PPP over Ethernet */ + case DLT_PPP_ETHER: /* - * This includes the Ethernet header (since we need - * the ethertype to dispatch Session vs. Discovery) - * and the PPPoE (RFC 2516) header. + * This does no include the Ethernet header, and + * only covers session state. */ - off_linktype = 20; - off_nl = 22; + 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: @@ -620,9 +788,11 @@ init_linktype(type) #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; @@ -651,7 +821,8 @@ init_linktype(type) * 8 - figure out which byte that is). */ off_linktype = 14; - off_nl = 22; + off_nl = 22; /* Token Ring+802.2+SNAP */ + off_nl_nosnap = 17; /* Token Ring+802.2 */ return; case DLT_IEEE802_11: @@ -668,31 +839,82 @@ init_linktype(type) * data frames in networks with no bridges. */ off_linktype = 24; - off_nl = 30; + off_nl = 32; /* 802.11+802.2+SNAP */ + off_nl_nosnap = 27; /* 802.11+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_IEEE802_11_RADIO: + /* + * 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 64 bytes long. + * + * XXX - same variable-length header problem, only + * more so; this header is also variable-length, + * with the length being the 32-bit big-endian + * number at an offset of 4 from the beginning + * of the radio header. + */ + off_linktype = 64+24; + off_nl = 64+32; /* Radio+802.11+802.2+SNAP */ + off_nl_nosnap = 64+27; /* Radio+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_SUNATM: + /* + * Full Frontal ATM; you get AALn PDUs with an ATM + * pseudo-header. + */ + is_atm = 1; + off_vpi = SUNATM_VPI_POS; + off_vci = SUNATM_VCI_POS; + off_proto = PROTO_POS; + off_mac = -1; /* LLC-encapsulated, so no MAC-layer header */ + off_payload = SUNATM_PKT_BEGIN_POS; + off_linktype = off_payload; + off_nl = off_payload+8; /* 802.2+SNAP */ + off_nl_nosnap = off_payload+3; /* 802.2 */ return; case DLT_RAW: off_linktype = -1; off_nl = 0; - return; - - case DLT_ATM_CLIP: /* Linux ATM defines this */ - off_linktype = 6; - off_nl = 8; + 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: @@ -703,6 +925,33 @@ init_linktype(type) */ off_linktype = -1; off_nl = 0; + off_nl_nosnap = 0; /* no 802.2 LLC */ + return; + + case DLT_IP_OVER_FC: + /* + * RFC 2625 IP-over-Fibre-Channel 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? RFC + * 2625 says SNAP should be used. + */ + off_linktype = 16; + off_nl = 24; /* IPFC+802.2+SNAP */ + off_nl_nosnap = 19; /* IPFC+802.2 */ + return; + + case DLT_FRELAY: + /* + * XXX - we should set this to handle SNAP-encapsulated + * frames (NLPID of 0x80). + */ + off_linktype = -1; + off_nl = 0; + off_nl_nosnap = 0; /* no 802.2 LLC */ return; } bpf_error("unknown data link type %d", linktype); @@ -745,25 +994,277 @@ gen_false() ((((y)&0xff)<<24) | (((y)&0xff00)<<8) | (((y)&0xff0000)>>8) | (((y)>>24)&0xff)) static struct block * -gen_linktype(proto) +gen_ether_linktype(proto) register int proto; { struct block *b0, *b1; + 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_IP: + b0 = gen_cmp_gt(off_linktype, BPF_H, ETHERMTU); + gen_not(b0); + b1 = gen_cmp(off_linktype + 2, BPF_H, (bpf_int32) + ((LLCSAP_IP << 8) | LLCSAP_IP)); + 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); + } + } +} + +static struct block * +gen_linktype(proto) + register int proto; +{ + struct block *b0, *b1, *b2; + switch (linktype) { case DLT_EN10MB: + return gen_ether_linktype(proto); + break; + + case DLT_C_HDLC: + switch (proto) { + + case LLCSAP_ISONS: + proto = (proto << 8 | LLCSAP_ISONS); + /* fall through */ + + default: + return gen_cmp(off_linktype, BPF_H, (bpf_int32)proto); + break; + } + break; + + case DLT_IEEE802_11: + case DLT_PRISM_HEADER: + case DLT_IEEE802_11_RADIO: + case DLT_FDDI: + case DLT_IEEE802: + case DLT_ATM_RFC1483: + case DLT_ATM_CLIP: + case DLT_IP_OVER_FC: + return gen_llc(proto); + break; + + case DLT_SUNATM: + /* + * If "is_lane" is set, check for a LANE-encapsulated + * version of this protocol, otherwise check for an + * LLC-encapsulated version of this protocol. + * + * We assume LANE means Ethernet, not Token Ring. + */ + if (is_lane) { + /* + * Check that the packet doesn't begin with an + * LE Control marker. (We've already generated + * a test for LANE.) + */ + b0 = gen_cmp(SUNATM_PKT_BEGIN_POS, BPF_H, 0xFF00); + gen_not(b0); + + /* + * Now generate an Ethernet test. + */ + b1 = gen_ether_linktype(proto); + gen_and(b0, b1); + return b1; + } else { + /* + * Check for LLC encapsulation and then check the + * protocol. + */ + b0 = gen_atmfield_code(A_PROTOTYPE, PT_LLC, BPF_JEQ, 0); + b1 = gen_llc(proto); + gen_and(b0, b1); + return b1; + } + + case DLT_LINUX_SLL: switch (proto) { + case LLCSAP_IP: + b0 = gen_cmp(off_linktype, BPF_H, LINUX_SLL_P_802_2); + b1 = gen_cmp(off_linktype + 2, BPF_H, (bpf_int32) + ((LLCSAP_IP << 8) | LLCSAP_IP)); + gen_and(b0, b1); + return b1; + 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 + * LSAP, like this, or should we check just the * DSAP? */ - b0 = gen_cmp_gt(off_linktype, BPF_H, ETHERMTU); - gen_not(b0); + 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); @@ -773,11 +1274,10 @@ gen_linktype(proto) /* * NetBEUI always uses 802.2 encapsulation. * XXX - should we check both the DSAP and the - * SSAP, like this, or should we check just the + * LSAP, like this, or should we check just the * DSAP? */ - b0 = gen_cmp_gt(off_linktype, BPF_H, ETHERMTU); - gen_not(b0); + 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); @@ -785,20 +1285,15 @@ gen_linktype(proto) 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.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 and + * 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; * @@ -808,261 +1303,11 @@ gen_linktype(proto) * (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_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. + * 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); @@ -1188,6 +1433,7 @@ gen_linktype(proto) case DLT_PPP: case DLT_PPP_SERIAL: + case DLT_PPP_ETHER: /* * We use Ethernet protocol types inside libpcap; * map them to the corresponding PPP protocol types. @@ -1195,7 +1441,7 @@ gen_linktype(proto) switch (proto) { case ETHERTYPE_IP: - proto = PPP_IP; /* XXX was 0x21 */ + proto = PPP_IP; break; #ifdef INET6 @@ -1290,9 +1536,14 @@ gen_linktype(proto) case DLT_NULL: case DLT_LOOP: + case DLT_ENC: + case DLT_PFLOG: /* * For DLT_NULL, the link-layer header is a 32-bit - * word containing an AF_ value in *host* byte order. + * word containing an AF_ value in *host* byte order, + * and for DLT_ENC, the link-layer header begins + * with a 32-bit work 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 @@ -1308,6 +1559,8 @@ gen_linktype(proto) * 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. + * + * XXX - what's the byte order for DLT_PFLOG? */ switch (proto) { @@ -1330,7 +1583,7 @@ gen_linktype(proto) return gen_false(); } - if (linktype == DLT_NULL) { + if (linktype == DLT_NULL || linktype == DLT_ENC) { /* * The AF_ value is in host byte order, but * the BPF interpreter will convert it to @@ -1351,36 +1604,45 @@ gen_linktype(proto) return (gen_cmp(0, BPF_W, (bpf_int32)proto)); case DLT_ARCNET: + case DLT_ARCNET_LINUX: /* * XXX should we check for first fragment if the protocol * uses PHDS? */ - switch(proto) { + switch (proto) { + default: return gen_false(); + #ifdef INET6 case ETHERTYPE_IPV6: - return(gen_cmp(2, BPF_B, - (bpf_int32)htonl(ARCTYPE_INET6))); + return (gen_cmp(off_linktype, BPF_B, + (bpf_int32)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)); + b0 = gen_cmp(off_linktype, BPF_B, + (bpf_int32)ARCTYPE_IP); + b1 = gen_cmp(off_linktype, BPF_B, + (bpf_int32)ARCTYPE_IP_OLD); gen_or(b0, b1); - return(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)); + b0 = gen_cmp(off_linktype, BPF_B, + (bpf_int32)ARCTYPE_ARP); + b1 = gen_cmp(off_linktype, BPF_B, + (bpf_int32)ARCTYPE_ARP_OLD); gen_or(b0, b1); - return(b1); + return (b1); + case ETHERTYPE_REVARP: - return(gen_cmp(2, BPF_B, - (bpf_int32)htonl(ARCTYPE_REVARP))); + return (gen_cmp(off_linktype, BPF_B, + (bpf_int32)ARCTYPE_REVARP)); + case ETHERTYPE_ATALK: - return(gen_cmp(2, BPF_B, - (bpf_int32)htonl(ARCTYPE_ATALK))); + return (gen_cmp(off_linktype, BPF_B, + (bpf_int32)ARCTYPE_ATALK)); } break; @@ -1392,6 +1654,51 @@ gen_linktype(proto) return gen_false(); } break; + + case DLT_FRELAY: + /* + * XXX - assumes a 2-byte Frame Relay header with + * DLCI and flags. What if the address is longer? + */ + switch (proto) { + + case ETHERTYPE_IP: + /* + * Check for the special NLPID for IP. + */ + return gen_cmp(2, BPF_H, (0x03<<8) | 0xcc); + +#ifdef INET6 + case ETHERTYPE_IPV6: + /* + * Check for the special NLPID for IPv6. + */ + return gen_cmp(2, BPF_H, (0x03<<8) | 0x8e); +#endif + + case LLCSAP_ISONS: + /* + * Check for several OSI protocols. + * + * Frame Relay packets typically have an OSI + * NLPID at the beginning; we check for each + * of them. + * + * What we check for is the NLPID and a frame + * control field of UI, i.e. 0x03 followed + * by the NLPID. + */ + b0 = gen_cmp(2, BPF_H, (0x03<<8) | ISO8473_CLNP); + b1 = gen_cmp(2, BPF_H, (0x03<<8) | ISO9542_ESIS); + b2 = gen_cmp(2, BPF_H, (0x03<<8) | ISO10589_ISIS); + gen_or(b1, b2); + gen_or(b0, b2); + return b2; + + default: + return gen_false(); + } + break; } /* @@ -1436,7 +1743,7 @@ gen_snap(orgcode, ptype, offset) snapblock[0] = LLCSAP_SNAP; /* DSAP = SNAP */ snapblock[1] = LLCSAP_SNAP; /* SSAP = SNAP */ - snapblock[2] = 0x03; /* control = UI */ + 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 */ @@ -1445,6 +1752,86 @@ gen_snap(orgcode, ptype, offset) return gen_bcmp(offset, 8, snapblock); } +/* + * Check for a given protocol value assuming an 802.2 LLC header. + */ +static struct block * +gen_llc(proto) + int proto; +{ + /* + * XXX - handle token-ring variable-length header. + */ + switch (proto) { + + case LLCSAP_IP: + return gen_cmp(off_linktype, BPF_H, (long) + ((LLCSAP_IP << 8) | LLCSAP_IP)); + + 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); + + 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); + } + } +} + static struct block * gen_hostop(addr, mask, dir, proto, src_off, dst_off) bpf_u_int32 addr; @@ -1539,10 +1926,86 @@ gen_hostop6(addr, mask, dir, proto, src_off, dst_off) gen_and(b0, b1); return b1; } -#endif /*INET6*/ +#endif /*INET6*/ + +static struct block * +gen_ehostop(eaddr, dir) + register const u_char *eaddr; + register int dir; +{ + register struct block *b0, *b1; + + switch (dir) { + case Q_SRC: + return gen_bcmp(off_mac + 6, 6, eaddr); + + case Q_DST: + return gen_bcmp(off_mac + 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 */ +} + +/* + * Like gen_ehostop, but for DLT_FDDI + */ +static struct block * +gen_fhostop(eaddr, dir) + register const u_char *eaddr; + register int dir; +{ + struct block *b0, *b1; + + switch (dir) { + case Q_SRC: +#ifdef PCAP_FDDIPAD + return gen_bcmp(6 + 1 + pcap_fddipad, 6, eaddr); +#else + return gen_bcmp(6 + 1, 6, eaddr); +#endif + + case Q_DST: +#ifdef PCAP_FDDIPAD + return gen_bcmp(0 + 1 + pcap_fddipad, 6, eaddr); +#else + return gen_bcmp(0 + 1, 6, eaddr); +#endif + + 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 */ +} +/* + * Like gen_ehostop, but for DLT_IEEE802 (Token Ring) + */ static struct block * -gen_ehostop(eaddr, dir) +gen_thostop(eaddr, dir) register const u_char *eaddr; register int dir; { @@ -1550,21 +2013,21 @@ gen_ehostop(eaddr, dir) switch (dir) { case Q_SRC: - return gen_bcmp(6, 6, eaddr); + return gen_bcmp(8, 6, eaddr); case Q_DST: - return gen_bcmp(0, 6, eaddr); + return gen_bcmp(2, 6, eaddr); case Q_AND: - b0 = gen_ehostop(eaddr, Q_SRC); - b1 = gen_ehostop(eaddr, Q_DST); + 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_ehostop(eaddr, Q_SRC); - b1 = gen_ehostop(eaddr, Q_DST); + b0 = gen_thostop(eaddr, Q_SRC); + b1 = gen_thostop(eaddr, Q_DST); gen_or(b0, b1); return b1; } @@ -1573,40 +2036,304 @@ gen_ehostop(eaddr, dir) } /* - * Like gen_ehostop, but for DLT_FDDI + * Like gen_ehostop, but for DLT_IEEE802_11 (802.11 wireless LAN) */ static struct block * -gen_fhostop(eaddr, dir) +gen_wlanhostop(eaddr, dir) register const u_char *eaddr; register int dir; { - struct block *b0, *b1; + register struct block *b0, *b1, *b2; + register struct slist *s; switch (dir) { case Q_SRC: -#ifdef PCAP_FDDIPAD - return gen_bcmp(6 + 1 + pcap_fddipad, 6, eaddr); -#else - return gen_bcmp(6 + 1, 6, eaddr); -#endif + /* + * Oh, yuk. + * + * For control frames, there is no SA. + * + * For management frames, SA is at an + * offset of 10 from the beginning of + * the packet. + * + * For data frames, SA is at an offset + * of 10 from the beginning of the packet + * if From DS is clear, at an offset of + * 16 from the beginning of the packet + * if From DS is set and To DS is clear, + * and an offset of 24 from the beginning + * of the packet if From DS is set and To DS + * is set. + */ + + /* + * Generate the tests to be done for data frames + * with From DS set. + * + * First, check for To DS set, i.e. check "link[1] & 0x01". + */ + s = new_stmt(BPF_LD|BPF_B|BPF_ABS); + s->s.k = 1; + b1 = new_block(JMP(BPF_JSET)); + b1->s.k = 0x01; /* To DS */ + b1->stmts = s; + + /* + * If To DS is set, the SA is at 24. + */ + b0 = gen_bcmp(24, 6, eaddr); + gen_and(b1, b0); + + /* + * Now, check for To DS not set, i.e. check + * "!(link[1] & 0x01)". + */ + s = new_stmt(BPF_LD|BPF_B|BPF_ABS); + s->s.k = 1; + b2 = new_block(JMP(BPF_JSET)); + b2->s.k = 0x01; /* To DS */ + b2->stmts = s; + gen_not(b2); + + /* + * If To DS is not set, the SA is at 16. + */ + b1 = gen_bcmp(16, 6, eaddr); + gen_and(b2, b1); + + /* + * Now OR together the last two checks. That gives + * the complete set of checks for data frames with + * From DS set. + */ + gen_or(b1, b0); + + /* + * Now check for From DS being set, and AND that with + * the ORed-together checks. + */ + s = new_stmt(BPF_LD|BPF_B|BPF_ABS); + s->s.k = 1; + b1 = new_block(JMP(BPF_JSET)); + b1->s.k = 0x02; /* From DS */ + b1->stmts = s; + gen_and(b1, b0); + + /* + * Now check for data frames with From DS not set. + */ + s = new_stmt(BPF_LD|BPF_B|BPF_ABS); + s->s.k = 1; + b2 = new_block(JMP(BPF_JSET)); + b2->s.k = 0x02; /* From DS */ + b2->stmts = s; + gen_not(b2); + + /* + * If From DS isn't set, the SA is at 10. + */ + b1 = gen_bcmp(10, 6, eaddr); + gen_and(b2, b1); + + /* + * Now OR together the checks for data frames with + * From DS not set and for data frames with From DS + * set; that gives the checks done for data frames. + */ + gen_or(b1, b0); + + /* + * Now check for a data frame. + * I.e, check "link[0] & 0x08". + */ + s = new_stmt(BPF_LD|BPF_B|BPF_ABS); + s->s.k = 0; + b1 = new_block(JMP(BPF_JSET)); + b1->s.k = 0x08; + b1->stmts = s; + + /* + * AND that with the checks done for data frames. + */ + gen_and(b1, b0); + + /* + * If the high-order bit of the type value is 0, this + * is a management frame. + * I.e, check "!(link[0] & 0x08)". + */ + s = new_stmt(BPF_LD|BPF_B|BPF_ABS); + s->s.k = 0; + b2 = new_block(JMP(BPF_JSET)); + b2->s.k = 0x08; + b2->stmts = s; + gen_not(b2); + + /* + * For management frames, the SA is at 10. + */ + b1 = gen_bcmp(10, 6, eaddr); + gen_and(b2, b1); + + /* + * OR that with the checks done for data frames. + * That gives the checks done for management and + * data frames. + */ + gen_or(b1, b0); + + /* + * If the low-order bit of the type value is 1, + * this is either a control frame or a frame + * with a reserved type, and thus not a + * frame with an SA. + * + * I.e., check "!(link[0] & 0x04)". + */ + s = new_stmt(BPF_LD|BPF_B|BPF_ABS); + s->s.k = 0; + b1 = new_block(JMP(BPF_JSET)); + b1->s.k = 0x04; + b1->stmts = s; + gen_not(b1); + + /* + * AND that with the checks for data and management + * frames. + */ + gen_and(b1, b0); + return b0; case Q_DST: -#ifdef PCAP_FDDIPAD - return gen_bcmp(0 + 1 + pcap_fddipad, 6, eaddr); -#else - return gen_bcmp(0 + 1, 6, eaddr); -#endif + /* + * Oh, yuk. + * + * For control frames, there is no DA. + * + * For management frames, DA is at an + * offset of 4 from the beginning of + * the packet. + * + * For data frames, DA is at an offset + * of 4 from the beginning of the packet + * if To DS is clear and at an offset of + * 16 from the beginning of the packet + * if To DS is set. + */ + + /* + * Generate the tests to be done for data frames. + * + * First, check for To DS set, i.e. "link[1] & 0x01". + */ + s = new_stmt(BPF_LD|BPF_B|BPF_ABS); + s->s.k = 1; + b1 = new_block(JMP(BPF_JSET)); + b1->s.k = 0x01; /* To DS */ + b1->stmts = s; + + /* + * If To DS is set, the DA is at 16. + */ + b0 = gen_bcmp(16, 6, eaddr); + gen_and(b1, b0); + + /* + * Now, check for To DS not set, i.e. check + * "!(link[1] & 0x01)". + */ + s = new_stmt(BPF_LD|BPF_B|BPF_ABS); + s->s.k = 1; + b2 = new_block(JMP(BPF_JSET)); + b2->s.k = 0x01; /* To DS */ + b2->stmts = s; + gen_not(b2); + + /* + * If To DS is not set, the DA is at 4. + */ + b1 = gen_bcmp(4, 6, eaddr); + gen_and(b2, b1); + + /* + * Now OR together the last two checks. That gives + * the complete set of checks for data frames. + */ + gen_or(b1, b0); + + /* + * Now check for a data frame. + * I.e, check "link[0] & 0x08". + */ + s = new_stmt(BPF_LD|BPF_B|BPF_ABS); + s->s.k = 0; + b1 = new_block(JMP(BPF_JSET)); + b1->s.k = 0x08; + b1->stmts = s; + + /* + * AND that with the checks done for data frames. + */ + gen_and(b1, b0); + + /* + * If the high-order bit of the type value is 0, this + * is a management frame. + * I.e, check "!(link[0] & 0x08)". + */ + s = new_stmt(BPF_LD|BPF_B|BPF_ABS); + s->s.k = 0; + b2 = new_block(JMP(BPF_JSET)); + b2->s.k = 0x08; + b2->stmts = s; + gen_not(b2); + + /* + * For management frames, the DA is at 4. + */ + b1 = gen_bcmp(4, 6, eaddr); + gen_and(b2, b1); + + /* + * OR that with the checks done for data frames. + * That gives the checks done for management and + * data frames. + */ + gen_or(b1, b0); + + /* + * If the low-order bit of the type value is 1, + * this is either a control frame or a frame + * with a reserved type, and thus not a + * frame with an SA. + * + * I.e., check "!(link[0] & 0x04)". + */ + s = new_stmt(BPF_LD|BPF_B|BPF_ABS); + s->s.k = 0; + b1 = new_block(JMP(BPF_JSET)); + b1->s.k = 0x04; + b1->stmts = s; + gen_not(b1); + + /* + * AND that with the checks for data and management + * frames. + */ + gen_and(b1, b0); + return b0; case Q_AND: - b0 = gen_fhostop(eaddr, Q_SRC); - b1 = gen_fhostop(eaddr, Q_DST); + b0 = gen_wlanhostop(eaddr, Q_SRC); + b1 = gen_wlanhostop(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); + b0 = gen_wlanhostop(eaddr, Q_SRC); + b1 = gen_wlanhostop(eaddr, Q_DST); gen_or(b0, b1); return b1; } @@ -1615,10 +2342,12 @@ gen_fhostop(eaddr, dir) } /* - * Like gen_ehostop, but for DLT_IEEE802 (Token Ring) + * Like gen_ehostop, but for RFC 2625 IP-over-Fibre-Channel. + * (We assume that the addresses are IEEE 48-bit MAC addresses, + * as the RFC states.) */ static struct block * -gen_thostop(eaddr, dir) +gen_ipfchostop(eaddr, dir) register const u_char *eaddr; register int dir; { @@ -1626,21 +2355,21 @@ gen_thostop(eaddr, dir) switch (dir) { case Q_SRC: - return gen_bcmp(8, 6, eaddr); + return gen_bcmp(10, 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); + b0 = gen_ipfchostop(eaddr, Q_SRC); + b1 = gen_ipfchostop(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); + b0 = gen_ipfchostop(eaddr, Q_SRC); + b1 = gen_ipfchostop(eaddr, Q_DST); gen_or(b0, b1); return b1; } @@ -1705,7 +2434,7 @@ gen_dnhostop(addr, dir, base_off) case Q_ISO: bpf_error("ISO host filtering not implemented"); - + default: abort(); } @@ -1990,9 +2719,27 @@ gen_gateway(eaddr, alist, proto, dir) b0 = gen_fhostop(eaddr, Q_OR); else if (linktype == DLT_IEEE802) b0 = gen_thostop(eaddr, Q_OR); + else if (linktype == DLT_IEEE802_11) + b0 = gen_wlanhostop(eaddr, Q_OR); + else if (linktype == DLT_SUNATM && is_lane) { + /* + * Check that the packet doesn't begin with an + * LE Control marker. (We've already generated + * a test for LANE.) + */ + b1 = gen_cmp(SUNATM_PKT_BEGIN_POS, BPF_H, 0xFF00); + gen_not(b1); + + /* + * Now check the MAC address. + */ + b0 = gen_ehostop(eaddr, Q_OR); + gen_and(b1, b0); + } else if (linktype == DLT_IP_OVER_FC) + b0 = gen_ipfchostop(eaddr, Q_OR); else bpf_error( - "'gateway' supported only on ethernet, FDDI or token ring"); + "'gateway' supported only on ethernet/FDDI/token ring/802.11/Fibre Channel"); b1 = gen_host(**alist++, 0xffffffff, proto, Q_OR); while (*alist) { @@ -2013,9 +2760,7 @@ struct block * gen_proto_abbrev(proto) int proto; { -#ifdef INET6 struct block *b0; -#endif struct block *b1; switch (proto) { @@ -2173,6 +2918,66 @@ gen_proto_abbrev(proto) b1 = gen_proto(ISO10589_ISIS, Q_ISO, Q_DEFAULT); break; + case Q_ISIS_L1: /* all IS-IS Level1 PDU-Types */ + b0 = gen_proto(ISIS_L1_LAN_IIH, Q_ISIS, Q_DEFAULT); + b1 = gen_proto(ISIS_PTP_IIH, Q_ISIS, Q_DEFAULT); /* FIXME extract the circuit-type bits */ + gen_or(b0, b1); + b0 = gen_proto(ISIS_L1_LSP, Q_ISIS, Q_DEFAULT); + gen_or(b0, b1); + b0 = gen_proto(ISIS_L1_CSNP, Q_ISIS, Q_DEFAULT); + gen_or(b0, b1); + b0 = gen_proto(ISIS_L1_PSNP, Q_ISIS, Q_DEFAULT); + gen_or(b0, b1); + break; + + case Q_ISIS_L2: /* all IS-IS Level2 PDU-Types */ + b0 = gen_proto(ISIS_L2_LAN_IIH, Q_ISIS, Q_DEFAULT); + b1 = gen_proto(ISIS_PTP_IIH, Q_ISIS, Q_DEFAULT); /* FIXME extract the circuit-type bits */ + gen_or(b0, b1); + b0 = gen_proto(ISIS_L2_LSP, Q_ISIS, Q_DEFAULT); + gen_or(b0, b1); + b0 = gen_proto(ISIS_L2_CSNP, Q_ISIS, Q_DEFAULT); + gen_or(b0, b1); + b0 = gen_proto(ISIS_L2_PSNP, Q_ISIS, Q_DEFAULT); + gen_or(b0, b1); + break; + + case Q_ISIS_IIH: /* all IS-IS Hello PDU-Types */ + b0 = gen_proto(ISIS_L1_LAN_IIH, Q_ISIS, Q_DEFAULT); + b1 = gen_proto(ISIS_L2_LAN_IIH, Q_ISIS, Q_DEFAULT); + gen_or(b0, b1); + b0 = gen_proto(ISIS_PTP_IIH, Q_ISIS, Q_DEFAULT); + gen_or(b0, b1); + break; + + case Q_ISIS_LSP: + b0 = gen_proto(ISIS_L1_LSP, Q_ISIS, Q_DEFAULT); + b1 = gen_proto(ISIS_L2_LSP, Q_ISIS, Q_DEFAULT); + gen_or(b0, b1); + break; + + case Q_ISIS_SNP: + b0 = gen_proto(ISIS_L1_CSNP, Q_ISIS, Q_DEFAULT); + b1 = gen_proto(ISIS_L2_CSNP, Q_ISIS, Q_DEFAULT); + gen_or(b0, b1); + b0 = gen_proto(ISIS_L1_PSNP, Q_ISIS, Q_DEFAULT); + gen_or(b0, b1); + b0 = gen_proto(ISIS_L2_PSNP, Q_ISIS, Q_DEFAULT); + gen_or(b0, b1); + break; + + case Q_ISIS_CSNP: + b0 = gen_proto(ISIS_L1_PSNP, Q_ISIS, Q_DEFAULT); + b1 = gen_proto(ISIS_L2_PSNP, Q_ISIS, Q_DEFAULT); + gen_or(b0, b1); + break; + + case Q_ISIS_PSNP: + b0 = gen_proto(ISIS_L1_PSNP, Q_ISIS, Q_DEFAULT); + b1 = gen_proto(ISIS_L2_PSNP, Q_ISIS, Q_DEFAULT); + gen_or(b0, b1); + break; + case Q_CLNP: b1 = gen_proto(ISO8473_CLNP, Q_ISO, Q_DEFAULT); break; @@ -2292,7 +3097,23 @@ gen_port(port, ip_proto, dir) { struct block *b0, *b1, *tmp; - /* ether proto ip */ + /* + * ether proto ip + * + * For FDDI, RFC 1188 says that SNAP encapsulation is used, + * not LLC encapsulation with LLCSAP_IP. + * + * For IEEE 802 networks - which includes 802.5 token ring + * (which is what DLT_IEEE802 means) and 802.11 - RFC 1042 + * says that SNAP encapsulation is used, not LLC encapsulation + * with LLCSAP_IP. + * + * For LLC-encapsulated ATM/"Classical IP", RFC 1483 and + * RFC 2225 say that SNAP encapsulation is used, not LLC + * encapsulation with LLCSAP_IP. + * + * So we always check for ETHERTYPE_IP. + */ b0 = gen_linktype(ETHERTYPE_IP); switch (ip_proto) { @@ -2757,6 +3578,21 @@ gen_proto(v, proto, dir) /*FALLTHROUGH*/ #endif case Q_IP: + /* + * For FDDI, RFC 1188 says that SNAP encapsulation is used, + * not LLC encapsulation with LLCSAP_IP. + * + * For IEEE 802 networks - which includes 802.5 token ring + * (which is what DLT_IEEE802 means) and 802.11 - RFC 1042 + * says that SNAP encapsulation is used, not LLC encapsulation + * with LLCSAP_IP. + * + * For LLC-encapsulated ATM/"Classical IP", RFC 1483 and + * RFC 2225 say that SNAP encapsulation is used, not LLC + * encapsulation with LLCSAP_IP. + * + * So we always check for ETHERTYPE_IP. + */ b0 = gen_linktype(ETHERTYPE_IP); #ifndef CHASE_CHAIN b1 = gen_cmp(off_nl + 9, BPF_B, (bpf_int32)v); @@ -2767,8 +3603,55 @@ gen_proto(v, proto, dir) return b1; case Q_ISO: - b0 = gen_linktype(LLCSAP_ISONS); - b1 = gen_cmp(off_nl + 3, BPF_B, (long)v); + switch (linktype) { + + case DLT_FRELAY: + /* + * Frame Relay packets typically have an OSI + * NLPID at the beginning; "gen_linktype(LLCSAP_ISONS)" + * generates code to check for all the OSI + * NLPIDs, so calling it and then adding a check + * for the particular NLPID for which we're + * looking is bogus, as we can just check for + * the NLPID. + * + * What we check for is the NLPID and a frame + * control field value of UI, i.e. 0x03 followed + * by the NLPID. + * + * XXX - assumes a 2-byte Frame Relay header with + * DLCI and flags. What if the address is longer? + * + * XXX - what about SNAP-encapsulated frames? + */ + return gen_cmp(2, BPF_H, (0x03<<8) | v); + break; + + case DLT_C_HDLC: + /* + * Cisco uses an Ethertype lookalike - for OSI, + * it's 0xfefe. + */ + b0 = gen_linktype(LLCSAP_ISONS<<8 | LLCSAP_ISONS); + /* OSI in C-HDLC is stuffed with a fudge byte */ + b1 = gen_cmp(off_nl_nosnap+1, BPF_B, (long)v); + gen_and(b0, b1); + return b1; + + default: + b0 = gen_linktype(LLCSAP_ISONS); + b1 = gen_cmp(off_nl_nosnap, BPF_B, (long)v); + gen_and(b0, b1); + return b1; + } + + case Q_ISIS: + b0 = gen_proto(ISO10589_ISIS, Q_ISO, Q_DEFAULT); + /* + * 4 is the offset of the PDU type relative to the IS-IS + * header. + */ + b1 = gen_cmp(off_nl_nosnap+4, BPF_B, (long)v); gen_and(b0, b1); return b1; @@ -2922,27 +3805,70 @@ 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); - return gen_thostop(eaddr, dir); + b = gen_thostop(eaddr, dir); + free(eaddr); + return b; + + case DLT_IEEE802_11: + eaddr = pcap_ether_hostton(name); + if (eaddr == NULL) + bpf_error( + "unknown 802.11 host '%s'", name); + b = gen_wlanhostop(eaddr, dir); + free(eaddr); + return b; + + case DLT_IP_OVER_FC: + eaddr = pcap_ether_hostton(name); + if (eaddr == NULL) + bpf_error( + "unknown Fibre Channel host '%s'", name); + b = gen_ipfchostop(eaddr, dir); + free(eaddr); + return b; + + case DLT_SUNATM: + if (!is_lane) + break; - default: - bpf_error( - "only ethernet/FDDI/token ring supports link-level host name"); - break; + /* + * Check that the packet doesn't begin + * with an LE Control marker. (We've + * already generated a test for LANE.) + */ + tmp = gen_cmp(SUNATM_PKT_BEGIN_POS, BPF_H, + 0xFF00); + gen_not(tmp); + + eaddr = pcap_ether_hostton(name); + if (eaddr == NULL) + bpf_error( + "unknown ether host '%s'", name); + b = gen_ehostop(eaddr, dir); + gen_and(tmp, b); + free(eaddr); + return b; } + + bpf_error("only ethernet/FDDI/token ring/802.11/ATM LANE/Fibre Channel supports link-level host name"); } else if (proto == Q_DECNET) { unsigned short dn_addr = __pcap_nametodnaddr(name); /* @@ -3070,7 +3996,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*/ @@ -3251,6 +4179,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] = @@ -3289,6 +4218,8 @@ gen_ecode(eaddr, q) register const u_char *eaddr; struct qual q; { + struct block *b, *tmp; + if ((q.addr == Q_HOST || q.addr == Q_DEFAULT) && q.proto == Q_LINK) { if (linktype == DLT_EN10MB) return gen_ehostop(eaddr, (int)q.dir); @@ -3296,7 +4227,27 @@ gen_ecode(eaddr, q) 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"); + if (linktype == DLT_IEEE802_11) + return gen_wlanhostop(eaddr, (int)q.dir); + if (linktype == DLT_SUNATM && is_lane) { + /* + * Check that the packet doesn't begin with an + * LE Control marker. (We've already generated + * a test for LANE.) + */ + tmp = gen_cmp(SUNATM_PKT_BEGIN_POS, BPF_H, 0xFF00); + gen_not(tmp); + + /* + * Now check the MAC address. + */ + b = gen_ehostop(eaddr, (int)q.dir); + gen_and(tmp, b); + return b; + } + if (linktype == DLT_IP_OVER_FC) + return gen_ipfchostop(eaddr, (int)q.dir); + bpf_error("ethernet addresses supported only on ethernet/FDDI/token ring/802.11/ATM LANE/Fibre Channel"); } bpf_error("ethernet address used in non-ether expression"); /* NOTREACHED */ @@ -3370,6 +4321,14 @@ gen_load(proto, index, size) bpf_error("unsupported index operation"); case Q_LINK: + /* + * XXX - what about ATM LANE? Should the index be + * relative to the beginning of the AAL5 frame, so + * that 0 refers to the beginning of the LE Control + * field, or relative to the beginning of the LAN + * frame, so that 0 refers, for Ethernet LANE, to + * the beginning of the destination address? + */ s = xfer_to_x(index); tmp = new_stmt(BPF_LD|BPF_IND|size); sappend(s, tmp); @@ -3451,16 +4410,16 @@ gen_relation(code, a0, a1, reversed) 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 (code == BPF_JGT || code == BPF_JGE) { - reversed = !reversed; - b->s.k = 0x80000000; + if (code == BPF_JEQ) { + s2 = new_stmt(BPF_ALU|BPF_SUB|BPF_X); + b = new_block(JMP(code)); + sappend(s1, s2); } + else + b = new_block(BPF_JMP|code|BPF_X); if (reversed) gen_not(b); - sappend(s1, s2); sappend(s0, s1); sappend(a1->s, s0); sappend(a0->s, a1->s); @@ -3558,6 +4517,7 @@ gen_arth(code, a0, a1) sappend(a1->s, s0); sappend(a0->s, a1->s); + free_reg(a0->regno); free_reg(a1->regno); s0 = new_stmt(BPF_ST); @@ -3697,7 +4657,7 @@ gen_broadcast(proto) case Q_DEFAULT: case Q_LINK: - if (linktype == DLT_ARCNET) + if (linktype == DLT_ARCNET || linktype == DLT_ARCNET_LINUX) return gen_ahostop(abroadcast, Q_DST); if (linktype == DLT_EN10MB) return gen_ehostop(ebroadcast, Q_DST); @@ -3705,6 +4665,26 @@ gen_broadcast(proto) return gen_fhostop(ebroadcast, Q_DST); if (linktype == DLT_IEEE802) return gen_thostop(ebroadcast, Q_DST); + if (linktype == DLT_IEEE802_11) + return gen_wlanhostop(ebroadcast, Q_DST); + if (linktype == DLT_IP_OVER_FC) + return gen_ipfchostop(ebroadcast, Q_DST); + if (linktype == DLT_SUNATM && is_lane) { + /* + * Check that the packet doesn't begin with an + * LE Control marker. (We've already generated + * a test for LANE.) + */ + b1 = gen_cmp(SUNATM_PKT_BEGIN_POS, BPF_H, 0xFF00); + gen_not(b1); + + /* + * Now check the MAC address. + */ + b0 = gen_ehostop(ebroadcast, Q_DST); + gen_and(b1, b0); + return b0; + } bpf_error("not a broadcast link"); break; @@ -3718,47 +4698,203 @@ gen_broadcast(proto) gen_and(b0, b2); return b2; } - bpf_error("only ether/ip broadcast filters supported"); + bpf_error("only link-layer/IP broadcast filters supported"); +} + +/* + * Generate code to test the low-order bit of a MAC address (that's + * the bottom bit of the *first* byte). + */ +static struct block * +gen_mac_multicast(offset) + int offset; +{ + register struct block *b0; + register struct slist *s; + + /* link[offset] & 1 != 0 */ + s = new_stmt(BPF_LD|BPF_B|BPF_ABS); + s->s.k = offset; + b0 = new_block(JMP(BPF_JSET)); + b0->s.k = 1; + b0->stmts = s; + return b0; } struct block * gen_multicast(proto) int proto; { - register struct block *b0, *b1; + register struct block *b0, *b1, *b2; register struct slist *s; switch (proto) { case Q_DEFAULT: case Q_LINK: - if (linktype == DLT_ARCNET) + if (linktype == DLT_ARCNET || linktype == DLT_ARCNET_LINUX) /* 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); - s->s.k = 0; - b0 = new_block(JMP(BPF_JSET)); - b0->s.k = 1; - b0->stmts = s; - return b0; + return gen_mac_multicast(0); } if (linktype == DLT_FDDI) { - /* XXX TEST THIS: MIGHT NOT PORT PROPERLY XXX */ + /* + * XXX TEST THIS: MIGHT NOT PORT PROPERLY XXX + * + * XXX - was that referring to bit-order issues? + */ /* fddi[1] & 1 != 0 */ + return gen_mac_multicast(1); + } + + if (linktype == DLT_IEEE802) { + /* tr[2] & 1 != 0 */ + return gen_mac_multicast(2); + } + + if (linktype == DLT_IEEE802_11) { + /* + * Oh, yuk. + * + * For control frames, there is no DA. + * + * For management frames, DA is at an + * offset of 4 from the beginning of + * the packet. + * + * For data frames, DA is at an offset + * of 4 from the beginning of the packet + * if To DS is clear and at an offset of + * 16 from the beginning of the packet + * if To DS is set. + */ + + /* + * Generate the tests to be done for data frames. + * + * First, check for To DS set, i.e. "link[1] & 0x01". + */ + s = new_stmt(BPF_LD|BPF_B|BPF_ABS); + s->s.k = 1; + b1 = new_block(JMP(BPF_JSET)); + b1->s.k = 0x01; /* To DS */ + b1->stmts = s; + + /* + * If To DS is set, the DA is at 16. + */ + b0 = gen_mac_multicast(16); + gen_and(b1, b0); + + /* + * Now, check for To DS not set, i.e. check + * "!(link[1] & 0x01)". + */ 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; + b2 = new_block(JMP(BPF_JSET)); + b2->s.k = 0x01; /* To DS */ + b2->stmts = s; + gen_not(b2); + + /* + * If To DS is not set, the DA is at 4. + */ + b1 = gen_mac_multicast(4); + gen_and(b2, b1); + + /* + * Now OR together the last two checks. That gives + * the complete set of checks for data frames. + */ + gen_or(b1, b0); + + /* + * Now check for a data frame. + * I.e, check "link[0] & 0x08". + */ + s = new_stmt(BPF_LD|BPF_B|BPF_ABS); + s->s.k = 0; + b1 = new_block(JMP(BPF_JSET)); + b1->s.k = 0x08; + b1->stmts = s; + + /* + * AND that with the checks done for data frames. + */ + gen_and(b1, b0); + + /* + * If the high-order bit of the type value is 0, this + * is a management frame. + * I.e, check "!(link[0] & 0x08)". + */ + s = new_stmt(BPF_LD|BPF_B|BPF_ABS); + s->s.k = 0; + b2 = new_block(JMP(BPF_JSET)); + b2->s.k = 0x08; + b2->stmts = s; + gen_not(b2); + + /* + * For management frames, the DA is at 4. + */ + b1 = gen_mac_multicast(4); + gen_and(b2, b1); + + /* + * OR that with the checks done for data frames. + * That gives the checks done for management and + * data frames. + */ + gen_or(b1, b0); + + /* + * If the low-order bit of the type value is 1, + * this is either a control frame or a frame + * with a reserved type, and thus not a + * frame with an SA. + * + * I.e., check "!(link[0] & 0x04)". + */ + s = new_stmt(BPF_LD|BPF_B|BPF_ABS); + s->s.k = 0; + b1 = new_block(JMP(BPF_JSET)); + b1->s.k = 0x04; + b1->stmts = s; + gen_not(b1); + + /* + * AND that with the checks for data and management + * frames. + */ + gen_and(b1, b0); return b0; } - /* TODO - check how token ring handles multicast */ - /* if (linktype == DLT_IEEE802) ... */ + if (linktype == DLT_IP_OVER_FC) { + b0 = gen_mac_multicast(2); + return b0; + } + + if (linktype == DLT_SUNATM && is_lane) { + /* + * Check that the packet doesn't begin with an + * LE Control marker. (We've already generated + * a test for LANE.) + */ + b1 = gen_cmp(SUNATM_PKT_BEGIN_POS, BPF_H, 0xFF00); + gen_not(b1); + + /* ether[off_mac] & 1 != 0 */ + b0 = gen_mac_multicast(off_mac); + gen_and(b1, b0); + return b0; + } /* Link not known to support multicasts */ break; @@ -3778,7 +4914,7 @@ gen_multicast(proto) return b1; #endif /* INET6 */ } - bpf_error("only IP multicast filters supported on ethernet/FDDI"); + bpf_error("link-layer multicast filters supported only on ethernet/FDDI/token ring/ARCNET/802.11/ATM LANE/Fibre Channel"); } /* @@ -3797,15 +4933,40 @@ gen_inbound(dir) */ 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; + case DLT_LINUX_SLL: + if (dir) { + /* + * Match packets sent by this machine. + */ + b0 = gen_cmp(0, BPF_H, LINUX_SLL_OUTGOING); + } else { + /* + * Match packets sent to this machine. + * (No broadcast or multicast packets, or + * packets sent to some other machine and + * received promiscuously.) + * + * XXX - packets sent to other machines probably + * shouldn't be matched, but what about broadcast + * or multicast packets we received? + */ + b0 = gen_cmp(0, BPF_H, LINUX_SLL_HOST); + } + break; + + case DLT_PFLOG: + b0 = gen_cmp(26, BPF_H, + (bpf_int32)((dir == 0) ? PF_IN : PF_OUT)); + break; + default: - bpf_error("inbound/outbound not supported on linktype %d\n", + bpf_error("inbound/outbound not supported on linktype %d", linktype); b0 = NULL; /* NOTREACHED */ @@ -3813,13 +4974,65 @@ gen_inbound(dir) return (b0); } +/* PF firewall log matched interface */ +struct block * +gen_pf_ifname(const char *ifname) +{ + if (linktype != DLT_PFLOG) { + bpf_error("ifname supported only for DLT_PFLOG"); + /* NOTREACHED */ + } + if (strlen(ifname) >= 16) { + bpf_error("ifname interface names can't be larger than 16 characters"); + /* NOTREACHED */ + } + return (gen_bcmp(4, strlen(ifname), (const u_char *)ifname)); +} + + +/* PF firewall log rule number */ +struct block * +gen_pf_rnr(int rnr) +{ + if (linktype != DLT_PFLOG) { + bpf_error("rnr supported only for DLT_PFLOG"); + /* NOTREACHED */ + } + + return (gen_cmp(20, BPF_H, (bpf_int32)rnr)); +} + +/* PF firewall log reason code */ +struct block * +gen_pf_reason(int reason) +{ + if (linktype != DLT_PFLOG) { + bpf_error("reason supported only for DLT_PFLOG"); + /* NOTREACHED */ + } + + return (gen_cmp(22, BPF_H, (bpf_int32)reason)); +} + +/* PF firewall log action */ +struct block * +gen_pf_action(int action) +{ + if (linktype != DLT_PFLOG) { + bpf_error("action supported only for DLT_PFLOG"); + /* NOTREACHED */ + } + + return (gen_cmp(24, BPF_H, (bpf_int32)action)); +} + 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) + if (linktype == DLT_ARCNET || linktype == DLT_ARCNET_LINUX) return gen_ahostop(eaddr, (int)q.dir); } bpf_error("ARCnet address used in non-arc expression"); @@ -3865,7 +5078,6 @@ struct block * gen_vlan(vlan_num) int vlan_num; { - static u_int orig_linktype = -1, orig_nl = -1; struct block *b0; /* @@ -3875,11 +5087,13 @@ gen_vlan(vlan_num) 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; @@ -3904,3 +5118,274 @@ gen_vlan(vlan_num) return (b0); } + +struct block * +gen_atmfield_code(atmfield, jvalue, jtype, reverse) + int atmfield; + bpf_u_int32 jvalue; + bpf_u_int32 jtype; + int reverse; +{ + struct block *b0; + + switch (atmfield) { + + case A_VPI: + if (!is_atm) + bpf_error("'vpi' supported only on raw ATM"); + if (off_vpi == -1) + abort(); + b0 = gen_ncmp(BPF_B, off_vpi, 0xffffffff, (u_int)jtype, + (u_int)jvalue, reverse); + break; + + case A_VCI: + if (!is_atm) + bpf_error("'vci' supported only on raw ATM"); + if (off_vci == -1) + abort(); + b0 = gen_ncmp(BPF_H, off_vci, 0xffffffff, (u_int)jtype, + (u_int)jvalue, reverse); + break; + + case A_PROTOTYPE: + if (off_proto == -1) + abort(); /* XXX - this isn't on FreeBSD */ + b0 = gen_ncmp(BPF_B, off_proto, 0x0f, (u_int)jtype, + (u_int)jvalue, reverse); + break; + + case A_MSGTYPE: + if (off_payload == -1) + abort(); + b0 = gen_ncmp(BPF_B, off_payload + MSG_TYPE_POS, 0xffffffff, + (u_int)jtype, (u_int)jvalue, reverse); + break; + + case A_CALLREFTYPE: + if (!is_atm) + bpf_error("'callref' supported only on raw ATM"); + if (off_proto == -1) + abort(); + b0 = gen_ncmp(BPF_B, off_proto, 0xffffffff, (u_int)jtype, + (u_int)jvalue, reverse); + break; + + default: + abort(); + } + return b0; +} + +struct block * +gen_atmtype_abbrev(type) + int type; +{ + struct block *b0, *b1; + + switch (type) { + + case A_METAC: + /* Get all packets in Meta signalling Circuit */ + if (!is_atm) + bpf_error("'metac' supported only on raw ATM"); + b0 = gen_atmfield_code(A_VPI, 0, BPF_JEQ, 0); + b1 = gen_atmfield_code(A_VCI, 1, BPF_JEQ, 0); + gen_and(b0, b1); + break; + + case A_BCC: + /* Get all packets in Broadcast Circuit*/ + if (!is_atm) + bpf_error("'bcc' supported only on raw ATM"); + b0 = gen_atmfield_code(A_VPI, 0, BPF_JEQ, 0); + b1 = gen_atmfield_code(A_VCI, 2, BPF_JEQ, 0); + gen_and(b0, b1); + break; + + case A_OAMF4SC: + /* Get all cells in Segment OAM F4 circuit*/ + if (!is_atm) + bpf_error("'oam4sc' supported only on raw ATM"); + b0 = gen_atmfield_code(A_VPI, 0, BPF_JEQ, 0); + b1 = gen_atmfield_code(A_VCI, 3, BPF_JEQ, 0); + gen_and(b0, b1); + break; + + case A_OAMF4EC: + /* Get all cells in End-to-End OAM F4 Circuit*/ + if (!is_atm) + bpf_error("'oam4ec' supported only on raw ATM"); + b0 = gen_atmfield_code(A_VPI, 0, BPF_JEQ, 0); + b1 = gen_atmfield_code(A_VCI, 4, BPF_JEQ, 0); + gen_and(b0, b1); + break; + + case A_SC: + /* Get all packets in connection Signalling Circuit */ + if (!is_atm) + bpf_error("'sc' supported only on raw ATM"); + b0 = gen_atmfield_code(A_VPI, 0, BPF_JEQ, 0); + b1 = gen_atmfield_code(A_VCI, 5, BPF_JEQ, 0); + gen_and(b0, b1); + break; + + case A_ILMIC: + /* Get all packets in ILMI Circuit */ + if (!is_atm) + bpf_error("'ilmic' supported only on raw ATM"); + b0 = gen_atmfield_code(A_VPI, 0, BPF_JEQ, 0); + b1 = gen_atmfield_code(A_VCI, 16, BPF_JEQ, 0); + gen_and(b0, b1); + break; + + case A_LANE: + /* Get all LANE packets */ + if (!is_atm) + bpf_error("'lane' supported only on raw ATM"); + b1 = gen_atmfield_code(A_PROTOTYPE, PT_LANE, BPF_JEQ, 0); + + /* + * Arrange that all subsequent tests assume LANE + * rather than LLC-encapsulated packets, and set + * the offsets appropriately for LANE-encapsulated + * Ethernet. + * + * "off_mac" is the offset of the Ethernet header, + * which is 2 bytes past the ATM pseudo-header + * (skipping the pseudo-header and 2-byte LE Client + * field). The other offsets are Ethernet offsets + * relative to "off_mac". + */ + is_lane = 1; + off_mac = off_payload + 2; /* MAC header */ + off_linktype = off_mac + 12; + off_nl = off_mac + 14; /* Ethernet II */ + off_nl_nosnap = off_mac + 17; /* 802.3+802.2 */ + break; + + case A_LLC: + /* Get all LLC-encapsulated packets */ + if (!is_atm) + bpf_error("'llc' supported only on raw ATM"); + b1 = gen_atmfield_code(A_PROTOTYPE, PT_LLC, BPF_JEQ, 0); + is_lane = 0; + break; + + default: + abort(); + } + return b1; +} + + +static struct block * +gen_msg_abbrev(type) + int type; +{ + struct block *b1; + + /* + * Q.2931 signalling protocol messages for handling virtual circuits + * establishment and teardown + */ + switch (type) { + + case A_SETUP: + b1 = gen_atmfield_code(A_MSGTYPE, SETUP, BPF_JEQ, 0); + break; + + case A_CALLPROCEED: + b1 = gen_atmfield_code(A_MSGTYPE, CALL_PROCEED, BPF_JEQ, 0); + break; + + case A_CONNECT: + b1 = gen_atmfield_code(A_MSGTYPE, CONNECT, BPF_JEQ, 0); + break; + + case A_CONNECTACK: + b1 = gen_atmfield_code(A_MSGTYPE, CONNECT_ACK, BPF_JEQ, 0); + break; + + case A_RELEASE: + b1 = gen_atmfield_code(A_MSGTYPE, RELEASE, BPF_JEQ, 0); + break; + + case A_RELEASE_DONE: + b1 = gen_atmfield_code(A_MSGTYPE, RELEASE_DONE, BPF_JEQ, 0); + break; + + default: + abort(); + } + return b1; +} + +struct block * +gen_atmmulti_abbrev(type) + int type; +{ + struct block *b0, *b1; + + switch (type) { + + case A_OAM: + if (!is_atm) + bpf_error("'oam' supported only on raw ATM"); + b1 = gen_atmmulti_abbrev(A_OAMF4); + break; + + case A_OAMF4: + if (!is_atm) + bpf_error("'oamf4' supported only on raw ATM"); + /* OAM F4 type */ + b0 = gen_atmfield_code(A_VCI, 3, BPF_JEQ, 0); + b1 = gen_atmfield_code(A_VCI, 4, BPF_JEQ, 0); + gen_or(b0, b1); + b0 = gen_atmfield_code(A_VPI, 0, BPF_JEQ, 0); + gen_and(b0, b1); + break; + + case A_CONNECTMSG: + /* + * Get Q.2931 signalling messages for switched + * virtual connection + */ + if (!is_atm) + bpf_error("'connectmsg' supported only on raw ATM"); + b0 = gen_msg_abbrev(A_SETUP); + b1 = gen_msg_abbrev(A_CALLPROCEED); + gen_or(b0, b1); + b0 = gen_msg_abbrev(A_CONNECT); + gen_or(b0, b1); + b0 = gen_msg_abbrev(A_CONNECTACK); + gen_or(b0, b1); + b0 = gen_msg_abbrev(A_RELEASE); + gen_or(b0, b1); + b0 = gen_msg_abbrev(A_RELEASE_DONE); + gen_or(b0, b1); + b0 = gen_atmtype_abbrev(A_SC); + gen_and(b0, b1); + break; + + case A_METACONNECT: + if (!is_atm) + bpf_error("'metaconnect' supported only on raw ATM"); + b0 = gen_msg_abbrev(A_SETUP); + b1 = gen_msg_abbrev(A_CALLPROCEED); + gen_or(b0, b1); + b0 = gen_msg_abbrev(A_CONNECT); + gen_or(b0, b1); + b0 = gen_msg_abbrev(A_RELEASE); + gen_or(b0, b1); + b0 = gen_msg_abbrev(A_RELEASE_DONE); + gen_or(b0, b1); + b0 = gen_atmtype_abbrev(A_METAC); + gen_and(b0, b1); + break; + + default: + abort(); + } + return b1; +}