/*#define CHASE_CHAIN*/ /* * Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that: (1) source code distributions * retain the above copyright notice and this paragraph in its entirety, (2) * distributions including binary code include the above copyright notice and * this paragraph in its entirety in the documentation or other materials * provided with the distribution, and (3) all advertising materials mentioning * features or use of this software display the following acknowledgement: * ``This product includes software developed by the University of California, * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of * the University nor the names of its contributors may be used to endorse * or promote products derived from this software without specific prior * written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #ifndef lint static const char rcsid[] _U_ = "@(#) $Header: /tcpdump/master/libpcap/gencode.c,v 1.193.2.8 2004/03/29 20:53:47 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 #include #endif /* WIN32 */ #include #include #include #include #include #include "pcap-int.h" #include "ethertype.h" #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 "pf.h" #ifndef offsetof #define offsetof(s, e) ((size_t)&((s *)0)->e) #endif #ifdef INET6 #ifndef WIN32 #include /* for "struct addrinfo" */ #endif /* WIN32 */ #endif /*INET6*/ #include #define ETHERMTU 1500 #ifndef IPPROTO_SCTP #define IPPROTO_SCTP 132 #endif #ifdef HAVE_OS_PROTO_H #include "os-proto.h" #endif #define JMP(c) ((c)|BPF_JMP|BPF_K) /* Locals */ 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; #else int pcap_fddipad; #endif /* VARARGS */ void bpf_error(const char *fmt, ...) { va_list ap; va_start(ap, fmt); if (bpf_pcap != NULL) (void)vsnprintf(pcap_geterr(bpf_pcap), PCAP_ERRBUF_SIZE, fmt, ap); va_end(ap); longjmp(top_ctx, 1); /* NOTREACHED */ } static void init_linktype(int); static int alloc_reg(void); static void free_reg(int); static struct block *root; /* * We divy out chunks of memory rather than call malloc each time so * we don't have to worry about leaking memory. It's probably * not a big deal if all this memory was wasted but it this ever * goes into a library that would probably not be a good idea. */ #define NCHUNKS 16 #define CHUNK0SIZE 1024 struct chunk { u_int n_left; void *m; }; static struct chunk chunks[NCHUNKS]; static int cur_chunk; static void *newchunk(u_int); static void freechunks(void); static inline struct block *new_block(int); static inline struct slist *new_stmt(int); static struct block *gen_retblk(int); static inline void syntax(void); static void backpatch(struct block *, struct block *); static void merge(struct block *, struct block *); static struct block *gen_cmp(u_int, u_int, 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); #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_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 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 static struct block *gen_portatom6(int, bpf_int32); #endif struct block *gen_portop(int, int, int); static struct block *gen_port(int, int, int); #ifdef INET6 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 *); 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; { struct chunk *cp; int k; size_t size; #ifndef __NetBSD__ /* XXX Round up to nearest long. */ n = (n + sizeof(long) - 1) & ~(sizeof(long) - 1); #else /* XXX Round up to structure boundary. */ n = ALIGN(n); #endif cp = &chunks[cur_chunk]; if (n > cp->n_left) { ++cp, k = ++cur_chunk; if (k >= NCHUNKS) bpf_error("out of memory"); size = CHUNK0SIZE << k; cp->m = (void *)malloc(size); if (cp->m == NULL) bpf_error("out of memory"); memset((char *)cp->m, 0, size); cp->n_left = size; if (n > size) bpf_error("out of memory"); } cp->n_left -= n; return (void *)((char *)cp->m + cp->n_left); } static void freechunks() { int i; cur_chunk = 0; for (i = 0; i < NCHUNKS; ++i) if (chunks[i].m != NULL) { free(chunks[i].m); chunks[i].m = NULL; } } /* * A strdup whose allocations are freed after code generation is over. */ char * sdup(s) register const char *s; { int n = strlen(s) + 1; char *cp = newchunk(n); strlcpy(cp, s, n); return (cp); } static inline struct block * new_block(code) int code; { struct block *p; p = (struct block *)newchunk(sizeof(*p)); p->s.code = code; p->head = p; return p; } static inline struct slist * new_stmt(code) int code; { struct slist *p; p = (struct slist *)newchunk(sizeof(*p)); p->s.code = code; return p; } static struct block * gen_retblk(v) int v; { struct block *b = new_block(BPF_RET|BPF_K); b->s.k = v; return b; } static inline void syntax() { bpf_error("syntax error in filter expression"); } static bpf_u_int32 netmask; static int snaplen; int no_optimize; int pcap_compile(pcap_t *p, struct bpf_program *program, char *buf, int optimize, bpf_u_int32 mask) { extern int n_errors; int len; no_optimize = 0; n_errors = 0; 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)); (void)pcap_parse(); if (n_errors) syntax(); if (root == NULL) root = gen_retblk(snaplen); if (optimize && !no_optimize) { bpf_optimize(&root); if (root == NULL || (root->s.code == (BPF_RET|BPF_K) && root->s.k == 0)) bpf_error("expression rejects all packets"); } program->bf_insns = icode_to_fcode(root, &len); program->bf_len = len; 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 * back to another unresolved block (or nil). At least one of the fields * in each block is already resolved. */ static void backpatch(list, target) struct block *list, *target; { struct block *next; while (list) { if (!list->sense) { next = JT(list); JT(list) = target; } else { next = JF(list); JF(list) = target; } list = next; } } /* * Merge the lists in b0 and b1, using the 'sense' field to indicate * which of jt and jf is the link. */ static void merge(b0, b1) struct block *b0, *b1; { register struct block **p = &b0; /* Find end of list. */ while (*p) p = !((*p)->sense) ? &JT(*p) : &JF(*p); /* Concatenate the lists. */ *p = b1; } void finish_parse(p) struct block *p; { backpatch(p, gen_retblk(snaplen)); p->sense = !p->sense; backpatch(p, gen_retblk(0)); root = p->head; } void gen_and(b0, b1) struct block *b0, *b1; { backpatch(b0, b1->head); b0->sense = !b0->sense; b1->sense = !b1->sense; merge(b1, b0); b1->sense = !b1->sense; b1->head = b0->head; } void gen_or(b0, b1) struct block *b0, *b1; { b0->sense = !b0->sense; backpatch(b0, b1->head); b0->sense = !b0->sense; merge(b1, b0); b1->head = b0->head; } void gen_not(b) struct block *b; { b->sense = !b->sense; } static struct block * gen_cmp(offset, size, v) u_int offset, size; 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_JEQ)); b->stmts = s; b->s.k = 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; bpf_int32 v; bpf_u_int32 mask; { struct block *b = gen_cmp(offset, size, v); struct slist *s; if (mask != 0xffffffff) { s = new_stmt(BPF_ALU|BPF_AND|BPF_K); s->s.k = mask; b->stmts->next = s; } return b; } static struct block * gen_bcmp(offset, size, v) register u_int offset, size; register const u_char *v; { register struct block *b, *tmp; b = NULL; while (size >= 4) { register const u_char *p = &v[size - 4]; bpf_int32 w = ((bpf_int32)p[0] << 24) | ((bpf_int32)p[1] << 16) | ((bpf_int32)p[2] << 8) | p[3]; tmp = gen_cmp(offset + size - 4, BPF_W, w); if (b != NULL) gen_and(b, tmp); b = tmp; size -= 4; } while (size >= 2) { register const u_char *p = &v[size - 2]; bpf_int32 w = ((bpf_int32)p[0] << 8) | p[1]; tmp = gen_cmp(offset + size - 2, BPF_H, w); if (b != NULL) gen_and(b, tmp); b = tmp; size -= 2; } if (size > 0) { tmp = gen_cmp(offset, BPF_B, (bpf_int32)v[0]); if (b != NULL) gen_and(b, tmp); b = tmp; } 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. */ /* * 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 init_linktype(type) int 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_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; /* Ethernet II */ off_nl_nosnap = 17; /* 802.3+802.2 */ return; case DLT_SLIP: /* * SLIP doesn't have a link level type. The 16 byte * header is hacked into our SLIP driver. */ off_linktype = -1; off_nl = 16; off_nl_nosnap = 16; /* no 802.2 LLC */ return; case DLT_SLIP_BSDOS: /* XXX this may be the same as the DLT_PPP_BSDOS case */ 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_PPP: 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 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 = 13; #ifdef PCAP_FDDIPAD off_linktype += pcap_fddipad; #endif 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: /* * 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. If the * fromds and tods 802.11 header bits are both set, * it's actually supposed to be 30 bytes. */ off_linktype = 24; 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_AVS: /* * 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, at least in its * current incarnation. * * 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_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. * * XXX - same variable-length header problem, only * even *more* so; this header is also variable-length, * with the length being the 16-bit number at an offset * of 2 from the beginning of the radio header, and it's * device-dependent (different devices might supply * different amounts of information), so we can't even * assume a fixed length for the current version of the * header. * * Therefore, currently, only raw "link[N:M]" filtering is * supported. */ off_linktype = -1; off_nl = -1; off_nl_nosnap = -1; 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 = 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; 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; 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; case DLT_APPLE_IP_OVER_IEEE1394: off_linktype = 16; off_nl = 18; off_nl_nosnap = 0; /* no 802.2 LLC */ return; case DLT_LINUX_IRDA: /* * Currently, only raw "link[N:M]" filtering is supported. */ off_linktype = -1; off_nl = -1; off_nl_nosnap = -1; return; case DLT_PFLOG: off_linktype = 0; /* XXX read from header? */ off_nl = PFLOG_HDRLEN; off_nl_nosnap = PFLOG_HDRLEN; return; #ifdef DLT_PFSYNC case DLT_PFSYNC: off_linktype = -1; off_nl = 4; off_nl_nosnap = 4; return; #endif } bpf_error("unknown data link type %d", linktype); /* NOTREACHED */ } static struct block * gen_uncond(rsense) int rsense; { struct block *b; struct slist *s; s = new_stmt(BPF_LD|BPF_IMM); s->s.k = !rsense; b = new_block(JMP(BPF_JEQ)); b->stmts = s; return b; } static inline struct block * gen_true() { return gen_uncond(1); } static inline struct block * gen_false() { return gen_uncond(0); } /* * 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_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 * 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: 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: 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; break; #ifdef INET6 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: b0 = gen_cmp(off_linktype, BPF_H, PPP_IP); b1 = gen_cmp(off_linktype, BPF_H, PPP_VJC); gen_or(b0, b1); b0 = gen_cmp(off_linktype, BPF_H, PPP_VJNC); gen_or(b1, b0); return b0; #ifdef INET6 case ETHERTYPE_IPV6: proto = PPP_IPV6; /* more to go? */ 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_NULL: case DLT_LOOP: case DLT_ENC: /* * For DLT_NULL, the link-layer header is a 32-bit * 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 * 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 || linktype == DLT_ENC) { /* * 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_PFLOG: /* * af field is host byte order in contrast to the rest of * the packet. */ if (proto == ETHERTYPE_IP) return (gen_cmp(offsetof(struct pfloghdr, af), BPF_B, (bpf_int32)AF_INET)); #ifdef INET6 else if (proto == ETHERTYPE_IPV6) return (gen_cmp(offsetof(struct pfloghdr, af), BPF_B, (bpf_int32)AF_INET6)); #endif /* INET6 */ else return gen_false(); break; case DLT_ARCNET: case DLT_ARCNET_LINUX: /* * 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(off_linktype, BPF_B, (bpf_int32)ARCTYPE_INET6)); #endif /* INET6 */ case ETHERTYPE_IP: 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); case ETHERTYPE_ARP: 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); case ETHERTYPE_REVARP: return (gen_cmp(off_linktype, BPF_B, (bpf_int32)ARCTYPE_REVARP)); case ETHERTYPE_ATALK: return (gen_cmp(off_linktype, BPF_B, (bpf_int32)ARCTYPE_ATALK)); } break; case DLT_LTALK: switch (proto) { case ETHERTYPE_ATALK: return gen_true(); default: 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; case DLT_LINUX_IRDA: bpf_error("IrDA link-layer type filtering not implemented"); } /* * 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 == (u_int)-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); } /* * 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; bpf_u_int32 mask; int dir, proto; u_int src_off, dst_off; { struct block *b0, *b1; u_int offset; switch (dir) { case Q_SRC: offset = src_off; break; case Q_DST: offset = dst_off; break; case Q_AND: b0 = gen_hostop(addr, mask, Q_SRC, proto, src_off, dst_off); b1 = gen_hostop(addr, mask, Q_DST, proto, src_off, dst_off); gen_and(b0, b1); return b1; case Q_OR: case Q_DEFAULT: b0 = gen_hostop(addr, mask, Q_SRC, proto, src_off, dst_off); b1 = gen_hostop(addr, mask, Q_DST, proto, src_off, dst_off); gen_or(b0, b1); return b1; default: abort(); } b0 = gen_linktype(proto); b1 = gen_mcmp(offset, BPF_W, (bpf_int32)addr, mask); gen_and(b0, b1); return b1; } #ifdef INET6 static struct block * gen_hostop6(addr, mask, dir, proto, src_off, dst_off) struct in6_addr *addr; struct in6_addr *mask; int dir, proto; u_int src_off, dst_off; { struct block *b0, *b1; u_int offset; u_int32_t *a, *m; switch (dir) { case Q_SRC: offset = src_off; break; case Q_DST: offset = dst_off; break; case Q_AND: b0 = gen_hostop6(addr, mask, Q_SRC, proto, src_off, dst_off); b1 = gen_hostop6(addr, mask, Q_DST, proto, src_off, dst_off); gen_and(b0, b1); return b1; case Q_OR: case Q_DEFAULT: b0 = gen_hostop6(addr, mask, Q_SRC, proto, src_off, dst_off); b1 = gen_hostop6(addr, mask, Q_DST, proto, src_off, dst_off); gen_or(b0, b1); return b1; default: abort(); } /* this order is important */ a = (u_int32_t *)addr; m = (u_int32_t *)mask; b1 = gen_mcmp(offset + 12, BPF_W, ntohl(a[3]), ntohl(m[3])); b0 = gen_mcmp(offset + 8, BPF_W, ntohl(a[2]), ntohl(m[2])); gen_and(b0, b1); b0 = gen_mcmp(offset + 4, BPF_W, ntohl(a[1]), ntohl(m[1])); gen_and(b0, b1); b0 = gen_mcmp(offset + 0, BPF_W, ntohl(a[0]), ntohl(m[0])); gen_and(b0, b1); b0 = gen_linktype(proto); gen_and(b0, b1); return b1; } #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_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 */ } /* * Like gen_ehostop, but for DLT_IEEE802_11 (802.11 wireless LAN) */ static struct block * gen_wlanhostop(eaddr, dir) register const u_char *eaddr; register int dir; { register struct block *b0, *b1, *b2; register struct slist *s; switch (dir) { case Q_SRC: /* * 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: /* * 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_wlanhostop(eaddr, Q_SRC); b1 = gen_wlanhostop(eaddr, Q_DST); gen_and(b0, b1); return b1; case Q_DEFAULT: case Q_OR: b0 = gen_wlanhostop(eaddr, Q_SRC); b1 = gen_wlanhostop(eaddr, Q_DST); gen_or(b0, b1); return b1; } abort(); /* NOTREACHED */ } /* * 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_ipfchostop(eaddr, dir) register const u_char *eaddr; register int dir; { register struct block *b0, *b1; switch (dir) { case Q_SRC: return gen_bcmp(10, 6, eaddr); case Q_DST: return gen_bcmp(2, 6, eaddr); case Q_AND: 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_ipfchostop(eaddr, Q_SRC); b1 = gen_ipfchostop(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 * carry both src and dst addresses, plus 5 packet types in a format that * carries only the src node, plus 2 types that use a different format and * also carry just the src node. * * Yuck. * * Instead of doing those all right, we just look for data packets with * 0 or 1 bytes of padding. If you want to look at other packets, that * will require a lot more hacking. * * To add support for filtering on DECNET "areas" (network numbers) * one would want to add a "mask" argument to this routine. That would * make the filter even more inefficient, although one could be clever * and not generate masking instructions if the mask is 0xFFFF. */ static struct block * gen_dnhostop(addr, dir, base_off) bpf_u_int32 addr; int dir; u_int base_off; { struct block *b0, *b1, *b2, *tmp; u_int offset_lh; /* offset if long header is received */ u_int offset_sh; /* offset if short header is received */ switch (dir) { case Q_DST: offset_sh = 1; /* follows flags */ offset_lh = 7; /* flgs,darea,dsubarea,HIORD */ break; case Q_SRC: offset_sh = 3; /* follows flags, dstnode */ offset_lh = 15; /* flgs,darea,dsubarea,did,sarea,ssub,HIORD */ break; case Q_AND: /* Inefficient because we do our Calvinball dance twice */ b0 = gen_dnhostop(addr, Q_SRC, base_off); b1 = gen_dnhostop(addr, Q_DST, base_off); gen_and(b0, b1); return b1; case Q_OR: case Q_DEFAULT: /* Inefficient because we do our Calvinball dance twice */ b0 = gen_dnhostop(addr, Q_SRC, base_off); b1 = gen_dnhostop(addr, Q_DST, base_off); gen_or(b0, b1); return b1; case Q_ISO: bpf_error("ISO host filtering not implemented"); default: abort(); } b0 = gen_linktype(ETHERTYPE_DN); /* Check for pad = 1, long header case */ tmp = gen_mcmp(base_off + 2, BPF_H, (bpf_int32)ntohs(0x0681), (bpf_int32)ntohs(0x07FF)); b1 = gen_cmp(base_off + 2 + 1 + offset_lh, BPF_H, (bpf_int32)ntohs(addr)); gen_and(tmp, b1); /* Check for pad = 0, long header case */ tmp = gen_mcmp(base_off + 2, BPF_B, (bpf_int32)0x06, (bpf_int32)0x7); b2 = gen_cmp(base_off + 2 + offset_lh, BPF_H, (bpf_int32)ntohs(addr)); gen_and(tmp, b2); gen_or(b2, b1); /* Check for pad = 1, short header case */ tmp = gen_mcmp(base_off + 2, BPF_H, (bpf_int32)ntohs(0x0281), (bpf_int32)ntohs(0x07FF)); b2 = gen_cmp(base_off + 2 + 1 + offset_sh, BPF_H, (bpf_int32)ntohs(addr)); gen_and(tmp, b2); gen_or(b2, b1); /* Check for pad = 0, short header case */ tmp = gen_mcmp(base_off + 2, BPF_B, (bpf_int32)0x02, (bpf_int32)0x7); b2 = gen_cmp(base_off + 2 + offset_sh, BPF_H, (bpf_int32)ntohs(addr)); gen_and(tmp, b2); gen_or(b2, b1); /* Combine with test for linktype */ gen_and(b0, b1); return b1; } static struct block * gen_host(addr, mask, proto, dir) bpf_u_int32 addr; bpf_u_int32 mask; int proto; int dir; { struct block *b0, *b1; switch (proto) { case Q_DEFAULT: b0 = gen_host(addr, mask, Q_IP, dir); if (off_linktype != (u_int)-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: return gen_hostop(addr, mask, dir, ETHERTYPE_IP, off_nl + 12, off_nl + 16); case Q_RARP: return gen_hostop(addr, mask, dir, ETHERTYPE_REVARP, off_nl + 14, off_nl + 24); case Q_ARP: return gen_hostop(addr, mask, dir, ETHERTYPE_ARP, off_nl + 14, off_nl + 24); case Q_TCP: bpf_error("'tcp' modifier applied to host"); case Q_SCTP: bpf_error("'sctp' modifier applied to host"); case Q_UDP: bpf_error("'udp' modifier applied to host"); case Q_ICMP: bpf_error("'icmp' modifier applied to host"); case Q_IGMP: bpf_error("'igmp' modifier applied to host"); case Q_IGRP: bpf_error("'igrp' modifier applied to host"); 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); case Q_SCA: bpf_error("SCA host filtering not implemented"); case Q_LAT: bpf_error("LAT host filtering not implemented"); case Q_MOPDL: bpf_error("MOPDL host filtering not implemented"); case Q_MOPRC: bpf_error("MOPRC host filtering not implemented"); #ifdef INET6 case Q_IPV6: bpf_error("'ip6' modifier applied to ip host"); case Q_ICMPV6: bpf_error("'icmp6' modifier applied to host"); #endif /* INET6 */ case Q_AH: bpf_error("'ah' modifier applied to host"); 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(); } /* NOTREACHED */ } #ifdef INET6 static struct block * gen_host6(addr, mask, proto, dir) struct in6_addr *addr; struct in6_addr *mask; int proto; int dir; { switch (proto) { case Q_DEFAULT: return gen_host6(addr, mask, Q_IPV6, dir); case Q_IP: bpf_error("'ip' modifier applied to ip6 host"); case Q_RARP: bpf_error("'rarp' modifier applied to ip6 host"); 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"); case Q_UDP: bpf_error("'udp' modifier applied to host"); case Q_ICMP: bpf_error("'icmp' modifier applied to host"); case Q_IGMP: bpf_error("'igmp' modifier applied to host"); case Q_IGRP: bpf_error("'igrp' modifier applied to host"); 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"); case Q_SCA: bpf_error("SCA host filtering not implemented"); case Q_LAT: bpf_error("LAT host filtering not implemented"); case Q_MOPDL: bpf_error("MOPDL host filtering not implemented"); case Q_MOPRC: bpf_error("MOPRC host filtering not implemented"); case Q_IPV6: return gen_hostop6(addr, mask, dir, ETHERTYPE_IPV6, off_nl + 8, off_nl + 24); case Q_ICMPV6: bpf_error("'icmp6' modifier applied to host"); case Q_AH: bpf_error("'ah' modifier applied to host"); 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(); } /* NOTREACHED */ } #endif /*INET6*/ #ifndef INET6 static struct block * gen_gateway(eaddr, alist, proto, dir) const u_char *eaddr; bpf_u_int32 **alist; int proto; int dir; { struct block *b0, *b1, *tmp; if (dir != 0) bpf_error("direction applied to 'gateway'"); switch (proto) { case Q_DEFAULT: case Q_IP: case Q_ARP: case Q_RARP: if (linktype == DLT_EN10MB) b0 = gen_ehostop(eaddr, Q_OR); else if (linktype == DLT_FDDI) 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/token ring/802.11/Fibre Channel"); b1 = gen_host(**alist++, 0xffffffff, proto, Q_OR); while (*alist) { tmp = gen_host(**alist++, 0xffffffff, proto, Q_OR); gen_or(b1, tmp); b1 = tmp; } gen_not(b1); gen_and(b0, b1); return b1; } bpf_error("illegal modifier of 'gateway'"); /* NOTREACHED */ } #endif struct block * gen_proto_abbrev(proto) int proto; { struct block *b0; 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 b0 = gen_proto(IPPROTO_TCP, Q_IPV6, Q_DEFAULT); gen_or(b0, b1); #endif break; case Q_UDP: b1 = gen_proto(IPPROTO_UDP, Q_IP, Q_DEFAULT); #ifdef INET6 b0 = gen_proto(IPPROTO_UDP, Q_IPV6, Q_DEFAULT); gen_or(b0, b1); #endif break; case Q_ICMP: b1 = gen_proto(IPPROTO_ICMP, Q_IP, Q_DEFAULT); break; #ifndef IPPROTO_IGMP #define IPPROTO_IGMP 2 #endif case Q_IGMP: b1 = gen_proto(IPPROTO_IGMP, Q_IP, Q_DEFAULT); break; #ifndef IPPROTO_IGRP #define IPPROTO_IGRP 9 #endif case Q_IGRP: b1 = gen_proto(IPPROTO_IGRP, Q_IP, Q_DEFAULT); break; #ifndef IPPROTO_PIM #define IPPROTO_PIM 103 #endif case Q_PIM: b1 = gen_proto(IPPROTO_PIM, Q_IP, Q_DEFAULT); #ifdef INET6 b0 = gen_proto(IPPROTO_PIM, Q_IPV6, Q_DEFAULT); gen_or(b0, b1); #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; case Q_ARP: b1 = gen_linktype(ETHERTYPE_ARP); break; case Q_RARP: b1 = gen_linktype(ETHERTYPE_REVARP); break; case Q_LINK: bpf_error("link layer applied in wrong context"); case Q_ATALK: b1 = gen_linktype(ETHERTYPE_ATALK); break; case Q_AARP: b1 = gen_linktype(ETHERTYPE_AARP); break; case Q_DECNET: b1 = gen_linktype(ETHERTYPE_DN); break; case Q_SCA: b1 = gen_linktype(ETHERTYPE_SCA); break; case Q_LAT: b1 = gen_linktype(ETHERTYPE_LAT); break; case Q_MOPDL: b1 = gen_linktype(ETHERTYPE_MOPDL); break; case Q_MOPRC: b1 = gen_linktype(ETHERTYPE_MOPRC); break; #ifdef INET6 case Q_IPV6: b1 = gen_linktype(ETHERTYPE_IPV6); break; #ifndef IPPROTO_ICMPV6 #define IPPROTO_ICMPV6 58 #endif case Q_ICMPV6: b1 = gen_proto(IPPROTO_ICMPV6, Q_IPV6, Q_DEFAULT); break; #endif /* INET6 */ #ifndef IPPROTO_AH #define IPPROTO_AH 51 #endif case Q_AH: b1 = gen_proto(IPPROTO_AH, Q_IP, Q_DEFAULT); #ifdef INET6 b0 = gen_proto(IPPROTO_AH, Q_IPV6, Q_DEFAULT); gen_or(b0, b1); #endif break; #ifndef IPPROTO_ESP #define IPPROTO_ESP 50 #endif case Q_ESP: b1 = gen_proto(IPPROTO_ESP, Q_IP, Q_DEFAULT); #ifdef INET6 b0 = gen_proto(IPPROTO_ESP, Q_IPV6, Q_DEFAULT); gen_or(b0, b1); #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_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_CSNP, Q_ISIS, Q_DEFAULT); b1 = gen_proto(ISIS_L2_CSNP, 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; 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(); } return b1; } static struct block * gen_ipfrag() { struct slist *s; struct block *b; /* not ip frag */ s = new_stmt(BPF_LD|BPF_H|BPF_ABS); s->s.k = off_nl + 6; b = new_block(JMP(BPF_JSET)); b->s.k = 0x1fff; b->stmts = s; gen_not(b); return b; } static struct block * gen_portatom(off, v) int off; bpf_int32 v; { struct slist *s; struct block *b; s = new_stmt(BPF_LDX|BPF_MSH|BPF_B); s->s.k = off_nl; s->next = new_stmt(BPF_LD|BPF_IND|BPF_H); s->next->s.k = off_nl + off; b = new_block(JMP(BPF_JEQ)); b->stmts = s; b->s.k = v; return b; } #ifdef INET6 static struct block * gen_portatom6(off, v) int off; bpf_int32 v; { return gen_cmp(off_nl + 40 + off, BPF_H, v); } #endif/*INET6*/ struct block * gen_portop(port, proto, dir) int port, proto, dir; { struct block *b0, *b1, *tmp; /* ip proto 'proto' */ tmp = gen_cmp(off_nl + 9, BPF_B, (bpf_int32)proto); b0 = gen_ipfrag(); gen_and(tmp, b0); switch (dir) { case Q_SRC: b1 = gen_portatom(0, (bpf_int32)port); break; case Q_DST: b1 = gen_portatom(2, (bpf_int32)port); break; case Q_OR: case Q_DEFAULT: tmp = gen_portatom(0, (bpf_int32)port); b1 = gen_portatom(2, (bpf_int32)port); gen_or(tmp, b1); break; case Q_AND: tmp = gen_portatom(0, (bpf_int32)port); b1 = gen_portatom(2, (bpf_int32)port); gen_and(tmp, b1); break; default: abort(); } gen_and(b0, b1); return b1; } static struct block * gen_port(port, ip_proto, dir) int port; int ip_proto; int dir; { struct block *b0, *b1, *tmp; /* * ether proto ip * * 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) { case IPPROTO_UDP: case IPPROTO_TCP: case IPPROTO_SCTP: b1 = gen_portop(port, ip_proto, dir); break; case PROTO_UNDEF: tmp = gen_portop(port, IPPROTO_TCP, dir); b1 = gen_portop(port, IPPROTO_UDP, dir); gen_or(tmp, b1); tmp = gen_portop(port, IPPROTO_SCTP, dir); gen_or(tmp, b1); break; default: abort(); } gen_and(b0, b1); return b1; } #ifdef INET6 struct block * gen_portop6(port, proto, dir) int port, proto, dir; { struct block *b0, *b1, *tmp; /* ip proto 'proto' */ b0 = gen_cmp(off_nl + 6, BPF_B, (bpf_int32)proto); switch (dir) { case Q_SRC: b1 = gen_portatom6(0, (bpf_int32)port); break; case Q_DST: b1 = gen_portatom6(2, (bpf_int32)port); break; case Q_OR: case Q_DEFAULT: tmp = gen_portatom6(0, (bpf_int32)port); b1 = gen_portatom6(2, (bpf_int32)port); gen_or(tmp, b1); break; case Q_AND: tmp = gen_portatom6(0, (bpf_int32)port); b1 = gen_portatom6(2, (bpf_int32)port); gen_and(tmp, b1); break; default: abort(); } gen_and(b0, b1); return b1; } static struct block * gen_port6(port, ip_proto, dir) int port; int ip_proto; int dir; { struct block *b0, *b1, *tmp; /* ether proto ip */ b0 = gen_linktype(ETHERTYPE_IPV6); switch (ip_proto) { case IPPROTO_UDP: case IPPROTO_TCP: case IPPROTO_SCTP: b1 = gen_portop6(port, ip_proto, dir); break; case PROTO_UNDEF: 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: abort(); } gen_and(b0, b1); return b1; } #endif /* INET6 */ static int lookup_proto(name, proto) register const char *name; register int proto; { register int v; switch (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); break; case Q_LINK: /* XXX should look up h/w protocol type based on linktype */ v = pcap_nametoeproto(name); if (v == PROTO_UNDEF) bpf_error("unknown ether proto '%s'", name); break; 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; } return v; } #if 0 struct stmt * gen_joinsp(s, n) struct stmt **s; int n; { return NULL; } #endif 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]; int fix2, fix3, fix4, fix5; int ahcheck, again, end; int i, max; int reg2 = alloc_reg(); memset(s, 0, sizeof(s)); fix2 = fix3 = fix4 = fix5 = 0; switch (proto) { case Q_IP: case Q_IPV6: break; case Q_DEFAULT: b0 = gen_protochain(v, Q_IP, dir); b = gen_protochain(v, Q_IPV6, dir); gen_or(b0, b); return b; default: bpf_error("bad protocol applied for 'protochain'"); /*NOTREACHED*/ } no_optimize = 1; /*this code is not compatible with optimzer yet */ /* * s[0] is a dummy entry to protect other BPF insn from damaged * by s[fix] = foo with uninitialized variable "fix". It is somewhat * hard to find interdependency made by jump table fixup. */ i = 0; s[i] = new_stmt(0); /*dummy*/ i++; switch (proto) { case Q_IP: b0 = gen_linktype(ETHERTYPE_IP); /* A = ip->ip_p */ s[i] = new_stmt(BPF_LD|BPF_ABS|BPF_B); s[i]->s.k = off_nl + 9; i++; /* X = ip->ip_hl << 2 */ s[i] = new_stmt(BPF_LDX|BPF_MSH|BPF_B); s[i]->s.k = off_nl; i++; break; #ifdef INET6 case Q_IPV6: b0 = gen_linktype(ETHERTYPE_IPV6); /* A = ip6->ip_nxt */ s[i] = new_stmt(BPF_LD|BPF_ABS|BPF_B); s[i]->s.k = off_nl + 6; i++; /* X = sizeof(struct ip6_hdr) */ s[i] = new_stmt(BPF_LDX|BPF_IMM); s[i]->s.k = 40; i++; break; #endif default: bpf_error("unsupported proto to gen_protochain"); /*NOTREACHED*/ } /* again: if (A == v) goto end; else fall through; */ again = i; s[i] = new_stmt(BPF_JMP|BPF_JEQ|BPF_K); s[i]->s.k = v; s[i]->s.jt = NULL; /*later*/ s[i]->s.jf = NULL; /*update in next stmt*/ 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*/ s[i]->s.jf = NULL; /*update in next stmt*/ s[i]->s.k = IPPROTO_NONE; s[fix5]->s.jf = s[i]; fix2 = i; i++; #ifdef INET6 if (proto == Q_IPV6) { int v6start, v6end, v6advance, j; v6start = i; /* if (A == IPPROTO_HOPOPTS) goto v6advance */ s[i] = new_stmt(BPF_JMP|BPF_JEQ|BPF_K); s[i]->s.jt = NULL; /*later*/ s[i]->s.jf = NULL; /*update in next stmt*/ s[i]->s.k = IPPROTO_HOPOPTS; s[fix2]->s.jf = s[i]; i++; /* if (A == IPPROTO_DSTOPTS) goto v6advance */ s[i - 1]->s.jf = s[i] = new_stmt(BPF_JMP|BPF_JEQ|BPF_K); s[i]->s.jt = NULL; /*later*/ s[i]->s.jf = NULL; /*update in next stmt*/ s[i]->s.k = IPPROTO_DSTOPTS; i++; /* if (A == IPPROTO_ROUTING) goto v6advance */ s[i - 1]->s.jf = s[i] = new_stmt(BPF_JMP|BPF_JEQ|BPF_K); s[i]->s.jt = NULL; /*later*/ s[i]->s.jf = NULL; /*update in next stmt*/ s[i]->s.k = IPPROTO_ROUTING; i++; /* if (A == IPPROTO_FRAGMENT) goto v6advance; else goto ahcheck; */ s[i - 1]->s.jf = s[i] = new_stmt(BPF_JMP|BPF_JEQ|BPF_K); s[i]->s.jt = NULL; /*later*/ s[i]->s.jf = NULL; /*later*/ s[i]->s.k = IPPROTO_FRAGMENT; fix3 = i; v6end = i; i++; /* v6advance: */ v6advance = i; /* * in short, * A = P[X]; * X = X + (P[X + 1] + 1) * 8; */ /* A = X */ s[i] = new_stmt(BPF_MISC|BPF_TXA); i++; /* 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 = 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); 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; i++; /* A += 1 */ s[i] = new_stmt(BPF_ALU|BPF_ADD|BPF_K); s[i]->s.k = 1; i++; /* A *= 8 */ s[i] = new_stmt(BPF_ALU|BPF_MUL|BPF_K); s[i]->s.k = 8; i++; /* X = A; */ s[i] = new_stmt(BPF_MISC|BPF_TAX); i++; /* A = MEM[reg2] */ s[i] = new_stmt(BPF_LD|BPF_MEM); s[i]->s.k = reg2; i++; /* goto again; (must use BPF_JA for backward jump) */ s[i] = new_stmt(BPF_JMP|BPF_JA); s[i]->s.k = again - i - 1; s[i - 1]->s.jf = s[i]; i++; /* fixup */ for (j = v6start; j <= v6end; j++) s[j]->s.jt = s[v6advance]; } else #endif { /* nop */ s[i] = new_stmt(BPF_ALU|BPF_ADD|BPF_K); s[i]->s.k = 0; s[fix2]->s.jf = s[i]; i++; } /* ahcheck: */ ahcheck = i; /* if (A == IPPROTO_AH) then fall through; else goto end; */ s[i] = new_stmt(BPF_JMP|BPF_JEQ|BPF_K); s[i]->s.jt = NULL; /*later*/ s[i]->s.jf = NULL; /*later*/ s[i]->s.k = IPPROTO_AH; if (fix3) s[fix3]->s.jf = s[ahcheck]; fix4 = i; i++; /* * in short, * 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++; /* 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 = reg2; i++; /* 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); s[i]->s.k = off_nl; i++; /* A += 2 */ s[i] = new_stmt(BPF_ALU|BPF_ADD|BPF_K); s[i]->s.k = 2; i++; /* A *= 4 */ s[i] = new_stmt(BPF_ALU|BPF_MUL|BPF_K); s[i]->s.k = 4; i++; /* X = A; */ s[i] = new_stmt(BPF_MISC|BPF_TAX); i++; /* A = MEM[reg2] */ s[i] = new_stmt(BPF_LD|BPF_MEM); s[i]->s.k = reg2; i++; /* goto again; (must use BPF_JA for backward jump) */ s[i] = new_stmt(BPF_JMP|BPF_JA); s[i]->s.k = again - i - 1; i++; /* end: nop */ end = i; s[i] = new_stmt(BPF_ALU|BPF_ADD|BPF_K); s[i]->s.k = 0; s[fix2]->s.jt = s[end]; s[fix4]->s.jf = s[end]; s[fix5]->s.jt = s[end]; i++; /* * make slist chain */ max = i; for (i = 0; i < max - 1; i++) s[i]->next = s[i + 1]; s[max - 1]->next = NULL; /* * emit final check */ b = new_block(JMP(BPF_JEQ)); b->stmts = s[1]; /*remember, s[0] is dummy*/ b->s.k = v; free_reg(reg2); gen_and(b0, b); return b; #endif } static struct block * gen_proto(v, proto, dir) int v; int proto; int dir; { struct block *b0, *b1; if (dir != Q_DEFAULT) bpf_error("direction applied to 'proto'"); switch (proto) { case Q_DEFAULT: #ifdef INET6 b0 = gen_proto(v, Q_IP, dir); b1 = gen_proto(v, Q_IPV6, dir); gen_or(b0, b1); return b1; #else /*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); #else b1 = gen_protochain(v, Q_IP); #endif gen_and(b0, b1); return b1; case Q_ISO: 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; case Q_ARP: bpf_error("arp does not encapsulate another protocol"); /* NOTREACHED */ case Q_RARP: bpf_error("rarp does not encapsulate another protocol"); /* NOTREACHED */ case Q_ATALK: bpf_error("atalk encapsulation is not specifiable"); /* NOTREACHED */ case Q_DECNET: bpf_error("decnet encapsulation is not specifiable"); /* NOTREACHED */ case Q_SCA: bpf_error("sca does not encapsulate another protocol"); /* NOTREACHED */ case Q_LAT: bpf_error("lat does not encapsulate another protocol"); /* NOTREACHED */ case Q_MOPRC: bpf_error("moprc does not encapsulate another protocol"); /* NOTREACHED */ case Q_MOPDL: bpf_error("mopdl does not encapsulate another protocol"); /* NOTREACHED */ case Q_LINK: return gen_linktype(v); case Q_UDP: bpf_error("'udp proto' is bogus"); /* NOTREACHED */ case Q_TCP: bpf_error("'tcp proto' is bogus"); /* NOTREACHED */ case Q_SCTP: bpf_error("'sctp proto' is bogus"); /* NOTREACHED */ case Q_ICMP: bpf_error("'icmp proto' is bogus"); /* NOTREACHED */ case Q_IGMP: bpf_error("'igmp proto' is bogus"); /* NOTREACHED */ case Q_IGRP: bpf_error("'igrp proto' is bogus"); /* NOTREACHED */ case Q_PIM: 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); #ifndef CHASE_CHAIN b1 = gen_cmp(off_nl + 6, BPF_B, (bpf_int32)v); #else b1 = gen_protochain(v, Q_IPV6); #endif gen_and(b0, b1); return b1; case Q_ICMPV6: bpf_error("'icmp6 proto' is bogus"); #endif /* INET6 */ case Q_AH: bpf_error("'ah proto' is bogus"); 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 */ } /* NOTREACHED */ } struct block * gen_scode(name, q) register const char *name; struct qual q; { int proto = q.proto; int dir = q.dir; int tproto; u_char *eaddr; bpf_u_int32 mask, addr; #ifndef INET6 bpf_u_int32 **alist; #else int tproto6; struct sockaddr_in *sin; struct sockaddr_in6 *sin6; struct addrinfo *res, *res0; struct in6_addr mask128; #endif /*INET6*/ struct block *b, *tmp; int port, real_proto; switch (q.addr) { case Q_NET: addr = pcap_nametonetaddr(name); if (addr == 0) bpf_error("unknown network '%s'", name); /* Left justify network addr and calculate its network mask */ mask = 0xffffffff; while (addr && (addr & 0xff000000) == 0) { addr <<= 8; mask <<= 8; } return gen_host(addr, mask, proto, dir); case Q_DEFAULT: case Q_HOST: if (proto == Q_LINK) { switch (linktype) { case DLT_EN10MB: eaddr = pcap_ether_hostton(name); if (eaddr == NULL) bpf_error( "unknown ether host '%s'", name); 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); 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; 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; /* * 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); /* * I don't think DECNET hosts can be multihomed, so * there is no need to build up a list of addresses */ return (gen_host(dn_addr, 0, proto, dir)); } else { #ifndef INET6 alist = pcap_nametoaddr(name); if (alist == NULL || *alist == NULL) bpf_error("unknown host '%s'", name); tproto = proto; if (off_linktype == (u_int)-1 && tproto == Q_DEFAULT) tproto = Q_IP; b = gen_host(**alist++, 0xffffffff, tproto, dir); while (*alist) { tmp = gen_host(**alist++, 0xffffffff, tproto, dir); gen_or(b, tmp); b = tmp; } return b; #else memset(&mask128, 0xff, sizeof(mask128)); res0 = res = pcap_nametoaddrinfo(name); if (res == NULL) bpf_error("unknown host '%s'", name); b = tmp = NULL; tproto = tproto6 = proto; if (off_linktype == -1 && tproto == Q_DEFAULT) { tproto = Q_IP; tproto6 = Q_IPV6; } 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; } 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 && 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; } 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 { struct block *b; b = gen_port(port, real_proto, dir); gen_or(gen_port6(port, real_proto, dir), b); return b; } #endif /* INET6 */ case Q_GATEWAY: #ifndef INET6 eaddr = pcap_ether_hostton(name); if (eaddr == NULL) bpf_error("unknown ether host: %s", name); alist = pcap_nametoaddr(name); if (alist == NULL || *alist == NULL) bpf_error("unknown host '%s'", name); b = gen_gateway(eaddr, alist, proto, dir); free(eaddr); return b; #else bpf_error("'gateway' not supported in this configuration"); #endif /*INET6*/ case Q_PROTO: real_proto = lookup_proto(name, proto); if (real_proto >= 0) return gen_proto(real_proto, proto, dir); else bpf_error("unknown protocol: %s", name); case Q_PROTOCHAIN: real_proto = lookup_proto(name, proto); if (real_proto >= 0) return gen_protochain(real_proto, proto, dir); else bpf_error("unknown protocol: %s", name); case Q_UNDEF: syntax(); /* NOTREACHED */ } abort(); /* NOTREACHED */ } struct block * gen_mcode(s1, s2, masklen, q) register const char *s1, *s2; register int masklen; struct qual q; { register int nlen, mlen; bpf_u_int32 n, m; nlen = __pcap_atoin(s1, &n); /* Promote short ipaddr */ n <<= 32 - nlen; if (s2 != NULL) { mlen = __pcap_atoin(s2, &m); /* Promote short ipaddr */ m <<= 32 - mlen; if ((n & ~m) != 0) bpf_error("non-network bits set in \"%s mask %s\"", s1, s2); } else { /* Convert mask len to mask */ if (masklen > 32) bpf_error("mask length must be <= 32"); m = 0xffffffff << (32 - masklen); if ((n & ~m) != 0) bpf_error("non-network bits set in \"%s/%d\"", s1, masklen); } switch (q.addr) { case Q_NET: return gen_host(n, m, q.proto, q.dir); default: bpf_error("Mask syntax for networks only"); /* NOTREACHED */ } } struct block * gen_ncode(s, v, q) register const char *s; bpf_u_int32 v; struct qual q; { bpf_u_int32 mask; int proto = q.proto; int dir = q.dir; register int vlen; if (s == NULL) vlen = 32; else if (q.proto == Q_DECNET) vlen = __pcap_atodn(s, &v); else vlen = __pcap_atoin(s, &v); switch (q.addr) { case Q_DEFAULT: case Q_HOST: case Q_NET: if (proto == Q_DECNET) return gen_host(v, 0, proto, dir); else if (proto == Q_LINK) { bpf_error("illegal link layer address"); } else { mask = 0xffffffff; if (s == NULL && q.addr == Q_NET) { /* Promote short net number */ while (v && (v & 0xff000000) == 0) { v <<= 8; mask <<= 8; } } else { /* Promote short ipaddr */ v <<= 32 - vlen; mask <<= 32 - vlen; } return gen_host(v, mask, proto, dir); } case Q_PORT: if (proto == Q_UDP) proto = IPPROTO_UDP; else if (proto == Q_TCP) proto = IPPROTO_TCP; else if (proto == Q_SCTP) proto = IPPROTO_SCTP; else if (proto == Q_DEFAULT) proto = PROTO_UNDEF; else bpf_error("illegal qualifier of 'port'"); #ifndef INET6 return gen_port((int)v, proto, dir); #else { struct block *b; b = gen_port((int)v, proto, dir); gen_or(gen_port6((int)v, proto, dir), b); return b; } #endif /* INET6 */ case Q_GATEWAY: bpf_error("'gateway' requires a name"); /* NOTREACHED */ case Q_PROTO: return gen_proto((int)v, proto, dir); case Q_PROTOCHAIN: return gen_protochain((int)v, proto, dir); case Q_UNDEF: syntax(); /* NOTREACHED */ default: abort(); /* NOTREACHED */ } /* NOTREACHED */ } #ifdef INET6 struct block * gen_mcode6(s1, s2, masklen, q) register const char *s1, *s2; register int masklen; struct qual q; { struct addrinfo *res; struct in6_addr *addr; struct in6_addr mask; struct block *b; u_int32_t *a, *m; if (s2) bpf_error("no mask %s supported", s2); res = pcap_nametoaddrinfo(s1); if (!res) bpf_error("invalid ip6 address %s", s1); if (res->ai_next) bpf_error("%s resolved to multiple address", s1); addr = &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr; 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] = (0xff << (8 - masklen % 8)) & 0xff; } a = (u_int32_t *)addr; m = (u_int32_t *)&mask; if ((a[0] & ~m[0]) || (a[1] & ~m[1]) || (a[2] & ~m[2]) || (a[3] & ~m[3])) { bpf_error("non-network bits set in \"%s/%d\"", s1, masklen); } switch (q.addr) { case Q_DEFAULT: case Q_HOST: if (masklen != 128) bpf_error("Mask syntax for networks only"); /* FALLTHROUGH */ case Q_NET: b = gen_host6(addr, &mask, q.proto, q.dir); freeaddrinfo(res); return b; default: bpf_error("invalid qualifier against IPv6 address"); /* NOTREACHED */ } } #endif /*INET6*/ struct block * 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); if (linktype == DLT_FDDI) return gen_fhostop(eaddr, (int)q.dir); if (linktype == DLT_IEEE802) return gen_thostop(eaddr, (int)q.dir); 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 */ } void sappend(s0, s1) struct slist *s0, *s1; { /* * This is definitely not the best way to do this, but the * lists will rarely get long. */ while (s0->next) s0 = s0->next; s0->next = s1; } static struct slist * xfer_to_x(a) struct arth *a; { struct slist *s; s = new_stmt(BPF_LDX|BPF_MEM); s->s.k = a->regno; return s; } static struct slist * xfer_to_a(a) struct arth *a; { struct slist *s; s = new_stmt(BPF_LD|BPF_MEM); s->s.k = a->regno; return s; } struct arth * gen_load(proto, index, size) int proto; struct arth *index; int size; { struct slist *s, *tmp; struct block *b; int regno = alloc_reg(); free_reg(index->regno); switch (size) { default: bpf_error("data size must be 1, 2, or 4"); case 1: size = BPF_B; break; case 2: size = BPF_H; break; case 4: size = BPF_W; break; } switch (proto) { default: bpf_error("unsupported index operation"); case Q_LINK: /* * 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); sappend(index->s, s); break; case Q_IP: case Q_ARP: case Q_RARP: case Q_ATALK: case Q_DECNET: case Q_SCA: case Q_LAT: case Q_MOPRC: case Q_MOPDL: #ifdef INET6 case Q_IPV6: #endif /* 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; sappend(s, tmp); sappend(index->s, s); b = gen_proto_abbrev(proto); if (index->b) gen_and(index->b, b); index->b = b; break; case Q_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)); sappend(s, new_stmt(BPF_ALU|BPF_ADD|BPF_X)); sappend(s, new_stmt(BPF_MISC|BPF_TAX)); sappend(s, tmp = new_stmt(BPF_LD|BPF_IND|size)); tmp->s.k = off_nl; sappend(index->s, s); gen_and(gen_proto_abbrev(proto), b = gen_ipfrag()); if (index->b) gen_and(index->b, b); #ifdef INET6 gen_and(gen_proto_abbrev(Q_IP), b); #endif index->b = b; break; #ifdef INET6 case Q_ICMPV6: bpf_error("IPv6 upper-layer protocol is not supported by proto[x]"); /*NOTREACHED*/ #endif } index->regno = regno; s = new_stmt(BPF_ST); s->s.k = regno; sappend(index->s, s); return index; } struct block * gen_relation(code, a0, a1, reversed) int code; struct arth *a0, *a1; int reversed; { struct slist *s0, *s1, *s2; struct block *b, *tmp; s0 = xfer_to_x(a1); s1 = xfer_to_a(a0); 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(s0, s1); sappend(a1->s, s0); sappend(a0->s, a1->s); b->stmts = a0->s; free_reg(a0->regno); free_reg(a1->regno); /* 'and' together protocol checks */ if (a0->b) { if (a1->b) { gen_and(a0->b, tmp = a1->b); } else tmp = a0->b; } else tmp = a1->b; if (tmp) gen_and(tmp, b); return b; } struct arth * gen_loadlen() { int regno = alloc_reg(); struct arth *a = (struct arth *)newchunk(sizeof(*a)); struct slist *s; s = new_stmt(BPF_LD|BPF_LEN); s->next = new_stmt(BPF_ST); s->next->s.k = regno; a->s = s; a->regno = regno; return a; } struct arth * gen_loadi(val) int val; { struct arth *a; struct slist *s; int reg; a = (struct arth *)newchunk(sizeof(*a)); reg = alloc_reg(); s = new_stmt(BPF_LD|BPF_IMM); s->s.k = val; s->next = new_stmt(BPF_ST); s->next->s.k = reg; a->s = s; a->regno = reg; return a; } struct arth * gen_neg(a) struct arth *a; { struct slist *s; s = xfer_to_a(a); sappend(a->s, s); s = new_stmt(BPF_ALU|BPF_NEG); s->s.k = 0; sappend(a->s, s); s = new_stmt(BPF_ST); s->s.k = a->regno; sappend(a->s, s); return a; } struct arth * gen_arth(code, a0, a1) int code; struct arth *a0, *a1; { struct slist *s0, *s1, *s2; s0 = xfer_to_x(a1); s1 = xfer_to_a(a0); s2 = new_stmt(BPF_ALU|BPF_X|code); sappend(s1, s2); sappend(s0, s1); sappend(a1->s, s0); sappend(a0->s, a1->s); free_reg(a0->regno); free_reg(a1->regno); s0 = new_stmt(BPF_ST); a0->regno = s0->s.k = alloc_reg(); sappend(a0->s, s0); return a0; } /* * Here we handle simple allocation of the scratch registers. * If too many registers are alloc'd, the allocator punts. */ static int regused[BPF_MEMWORDS]; static int curreg; /* * Return the next free register. */ static int alloc_reg() { int n = BPF_MEMWORDS; while (--n >= 0) { if (regused[curreg]) curreg = (curreg + 1) % BPF_MEMWORDS; else { regused[curreg] = 1; return curreg; } } bpf_error("too many registers needed to evaluate expression"); /* NOTREACHED */ } /* * Return a register to the table so it can * be used later. */ static void free_reg(n) int n; { regused[n] = 0; } static struct block * gen_len(jmp, n) int jmp, n; { struct slist *s; struct block *b; s = new_stmt(BPF_LD|BPF_LEN); b = new_block(JMP(jmp)); b->stmts = s; b->s.k = n; return b; } struct block * gen_greater(n) int n; { return gen_len(BPF_JGE, n); } /* * Actually, this is less than or equal. */ struct block * gen_less(n) int n; { struct block *b; b = gen_len(BPF_JGT, n); gen_not(b); return b; } struct block * gen_byteop(op, idx, val) int op, idx, val; { struct block *b; struct slist *s; switch (op) { default: abort(); case '=': return gen_cmp((u_int)idx, BPF_B, (bpf_int32)val); case '<': b = gen_cmp((u_int)idx, BPF_B, (bpf_int32)val); b->s.code = JMP(BPF_JGE); gen_not(b); return b; case '>': b = gen_cmp((u_int)idx, BPF_B, (bpf_int32)val); b->s.code = JMP(BPF_JGT); return b; case '|': s = new_stmt(BPF_ALU|BPF_OR|BPF_K); break; case '&': s = new_stmt(BPF_ALU|BPF_AND|BPF_K); break; } s->s.k = val; b = new_block(JMP(BPF_JEQ)); b->stmts = s; gen_not(b); return b; } static u_char abroadcast[] = { 0x0 }; struct block * gen_broadcast(proto) int proto; { bpf_u_int32 hostmask; struct block *b0, *b1, *b2; static u_char ebroadcast[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; switch (proto) { case Q_DEFAULT: case Q_LINK: if (linktype == DLT_ARCNET || linktype == DLT_ARCNET_LINUX) 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); 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; case Q_IP: b0 = gen_linktype(ETHERTYPE_IP); hostmask = ~netmask; b1 = gen_mcmp(off_nl + 16, BPF_W, (bpf_int32)0, hostmask); b2 = gen_mcmp(off_nl + 16, BPF_W, (bpf_int32)(~0 & hostmask), hostmask); gen_or(b1, b2); gen_and(b0, b2); return b2; } 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, *b2; register struct slist *s; switch (proto) { case Q_DEFAULT: case Q_LINK: 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 */ return gen_mac_multicast(0); } if (linktype == DLT_FDDI) { /* * 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; 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; } 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; case Q_IP: b0 = gen_linktype(ETHERTYPE_IP); b1 = gen_cmp(off_nl + 16, BPF_B, (bpf_int32)224); b1->s.code = JMP(BPF_JGE); gen_and(b0, b1); return b1; #ifdef INET6 case Q_IPV6: b0 = gen_linktype(ETHERTYPE_IPV6); b1 = gen_cmp(off_nl + 24, BPF_B, (bpf_int32)255); gen_and(b0, b1); return b1; #endif /* INET6 */ } bpf_error("link-layer multicast filters supported only on ethernet/FDDI/token ring/ARCNET/802.11/ATM LANE/Fibre Channel"); } /* * generate command for inbound/outbound. It's here so we can * make it link-type specific. 'dir' = 0 implies "inbound", * = 1 implies "outbound". */ struct block * gen_inbound(dir) int dir; { register struct block *b0; /* * Only some data link types support inbound/outbound qualifiers. */ switch (linktype) { case DLT_SLIP: 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(offsetof(struct pfloghdr, dir), BPF_B, (bpf_int32)((dir == 0) ? PF_IN : PF_OUT)); break; default: bpf_error("inbound/outbound not supported on linktype %d", linktype); b0 = NULL; /* NOTREACHED */ } return (b0); } /* PF firewall log matched interface */ struct block * gen_pf_ifname(const char *ifname) { struct block *b0; u_int len, off; if (linktype == DLT_PFLOG) { len = sizeof(((struct pfloghdr *)0)->ifname); off = offsetof(struct pfloghdr, ifname); } else { bpf_error("ifname not supported on linktype 0x%x", linktype); /* NOTREACHED */ } if (strlen(ifname) >= len) { bpf_error("ifname interface names can only be %d characters", len-1); /* NOTREACHED */ } b0 = gen_bcmp(off, strlen(ifname), ifname); return (b0); } /* PF firewall log matched interface */ struct block * gen_pf_ruleset(char *ruleset) { struct block *b0; if (linktype != DLT_PFLOG) { bpf_error("ruleset not supported on linktype 0x%x", linktype); /* NOTREACHED */ } if (strlen(ruleset) >= sizeof(((struct pfloghdr *)0)->ruleset)) { bpf_error("ruleset names can only be %ld characters", (long)(sizeof(((struct pfloghdr *)0)->ruleset) - 1)); /* NOTREACHED */ } b0 = gen_bcmp(offsetof(struct pfloghdr, ruleset), strlen(ruleset), ruleset); return (b0); } /* PF firewall log rule number */ struct block * gen_pf_rnr(int rnr) { struct block *b0; if (linktype == DLT_PFLOG) { b0 = gen_cmp(offsetof(struct pfloghdr, rulenr), BPF_W, (bpf_int32)rnr); } else { bpf_error("rnr not supported on linktype 0x%x", linktype); /* NOTREACHED */ } return (b0); } /* PF firewall log sub-rule number */ struct block * gen_pf_srnr(int srnr) { struct block *b0; if (linktype != DLT_PFLOG) { bpf_error("srnr not supported on linktype 0x%x", linktype); /* NOTREACHED */ } b0 = gen_cmp(offsetof(struct pfloghdr, subrulenr), BPF_W, (bpf_int32)srnr); return (b0); } /* PF firewall log reason code */ struct block * gen_pf_reason(int reason) { struct block *b0; if (linktype == DLT_PFLOG) { b0 = gen_cmp(offsetof(struct pfloghdr, reason), BPF_B, (bpf_int32)reason); } else { bpf_error("reason not supported on linktype 0x%x", linktype); /* NOTREACHED */ } return (b0); } /* PF firewall log action */ struct block * gen_pf_action(int action) { struct block *b0; if (linktype == DLT_PFLOG) { b0 = gen_cmp(offsetof(struct pfloghdr, action), BPF_B, (bpf_int32)action); } else { bpf_error("action not supported on linktype 0x%x", linktype); /* 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 || linktype == DLT_ARCNET_LINUX) 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); } 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 == (u_int)-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 == (u_int)-1) abort(); b0 = gen_ncmp(BPF_H, off_vci, 0xffffffff, (u_int)jtype, (u_int)jvalue, reverse); break; case A_PROTOTYPE: if (off_proto == (u_int)-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 == (u_int)-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 == (u_int)-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; }