X-Git-Url: https://git.tcpdump.org/libpcap/blobdiff_plain/2e69dd906a9660f1bf4ed5a85d06d3b89114fb0e..refs/heads/libpcap-0.9:/gencode.c diff --git a/gencode.c b/gencode.c index f8765fb5..177ff925 100644 --- a/gencode.c +++ b/gencode.c @@ -20,8 +20,8 @@ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #ifndef lint -static const char rcsid[] = - "@(#) $Header: /tcpdump/master/libpcap/gencode.c,v 1.191 2003-05-02 08:37:43 guy Exp $ (LBL)"; +static const char rcsid[] _U_ = + "@(#) $Header: /tcpdump/master/libpcap/gencode.c,v 1.221.2.54 2007-10-26 00:47:36 guy Exp $ (LBL)"; #endif #ifdef HAVE_CONFIG_H @@ -33,7 +33,6 @@ static const char rcsid[] = #else /* WIN32 */ #include #include -#include #endif /* WIN32 */ /* @@ -59,6 +58,10 @@ static const char rcsid[] = #include #include +#ifdef MSDOS +#include "pcap-dos.h" +#endif + #include "pcap-int.h" #include "ethertype.h" @@ -70,7 +73,15 @@ static const char rcsid[] = #include "ppp.h" #include "sll.h" #include "arcnet.h" -#include "pf.h" +#ifdef HAVE_NET_PFVAR_H +#include +#include +#include +#include +#endif +#ifndef offsetof +#define offsetof(s, e) ((size_t)&((s *)0)->e) +#endif #ifdef INET6 #ifndef WIN32 #include /* for "struct addrinfo" */ @@ -94,20 +105,21 @@ static const char rcsid[] = 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; +#ifdef WIN32 +/* Hack for updating VLAN, MPLS, and PPPoE offsets. */ +static u_int orig_linktype = (u_int)-1, orig_nl = (u_int)-1, label_stack_depth = (u_int)-1; +#else +static u_int orig_linktype = -1U, orig_nl = -1U, label_stack_depth = -1U; +#endif /* XXX */ #ifdef PCAP_FDDIPAD -int pcap_fddipad = PCAP_FDDIPAD; -#else -int pcap_fddipad; +static int pcap_fddipad; #endif /* VARARGS */ void bpf_error(const char *fmt, ...) - { va_list ap; @@ -120,18 +132,34 @@ bpf_error(const char *fmt, ...) /* NOTREACHED */ } -static void init_linktype(int); +static void init_linktype(pcap_t *); +static void init_regs(void); static int alloc_reg(void); static void free_reg(int); static struct block *root; +/* + * Value passed to gen_load_a() to indicate what the offset argument + * is relative to. + */ +enum e_offrel { + OR_PACKET, /* relative to the beginning of the packet */ + OR_LINK, /* relative to the link-layer header */ + OR_NET, /* relative to the network-layer header */ + OR_NET_NOSNAP, /* relative to the network-layer header, with no SNAP header at the link layer */ + OR_TRAN_IPV4, /* relative to the transport-layer header, with IPv4 network layer */ + OR_TRAN_IPV6 /* relative to the transport-layer header, with IPv6 network layer */ +}; + /* * 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 + * not a big deal if all this memory was wasted but if this ever * goes into a library that would probably not be a good idea. + * + * XXX - this *is* in a library.... */ #define NCHUNKS 16 #define CHUNK0SIZE 1024 @@ -152,19 +180,31 @@ 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_cmp(enum e_offrel, u_int, u_int, bpf_int32); +static struct block *gen_cmp_gt(enum e_offrel, u_int, u_int, bpf_int32); +static struct block *gen_cmp_ge(enum e_offrel, u_int, u_int, bpf_int32); +static struct block *gen_cmp_lt(enum e_offrel, u_int, u_int, bpf_int32); +static struct block *gen_cmp_le(enum e_offrel, u_int, u_int, bpf_int32); +static struct block *gen_mcmp(enum e_offrel, u_int, u_int, bpf_int32, + bpf_u_int32); +static struct block *gen_bcmp(enum e_offrel, u_int, u_int, const u_char *); +static struct block *gen_ncmp(enum e_offrel, bpf_u_int32, bpf_u_int32, + bpf_u_int32, bpf_u_int32, int, bpf_int32); +static struct slist *gen_load_llrel(u_int, u_int); +static struct slist *gen_load_a(enum e_offrel, u_int, u_int); +static struct slist *gen_loadx_iphdrlen(void); 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_linux_sll_linktype(int); +static void insert_radiotap_load_llprefixlen(struct block *); +static void insert_ppi_load_llprefixlen(struct block *); +static void insert_load_llprefixlen(struct block *); +static struct slist *gen_llprefixlen(void); 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_llc_linktype(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); @@ -175,24 +215,31 @@ 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); +static struct block *gen_dnhostop(bpf_u_int32, int); +static struct block *gen_mpls_linktype(int); +static struct block *gen_host(bpf_u_int32, bpf_u_int32, int, int, int); #ifdef INET6 -static struct block *gen_host6(struct in6_addr *, struct in6_addr *, int, int); +static struct block *gen_host6(struct in6_addr *, struct in6_addr *, int, 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); +static struct block *gen_portrangeatom(int, bpf_int32, bpf_int32); #ifdef INET6 static struct block *gen_portatom6(int, bpf_int32); +static struct block *gen_portrangeatom6(int, bpf_int32, bpf_int32); #endif struct block *gen_portop(int, int, int); static struct block *gen_port(int, int, int); +struct block *gen_portrangeop(int, int, int, int); +static struct block *gen_portrange(int, int, int, int); #ifdef INET6 struct block *gen_portop6(int, int, int); static struct block *gen_port6(int, int, int); +struct block *gen_portrangeop6(int, int, int, int); +static struct block *gen_portrange6(int, int, int, int); #endif static int lookup_proto(const char *, int); static struct block *gen_protochain(int, int, int); @@ -202,6 +249,7 @@ 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_ppi_dlt_check(void); static struct block *gen_msg_abbrev(int type); static void * @@ -209,7 +257,8 @@ newchunk(n) u_int n; { struct chunk *cp; - int k, size; + int k; + size_t size; #ifndef __NetBSD__ /* XXX Round up to nearest long. */ @@ -311,15 +360,17 @@ int no_optimize; int pcap_compile(pcap_t *p, struct bpf_program *program, - char *buf, int optimize, bpf_u_int32 mask) + const char *buf, int optimize, bpf_u_int32 mask) { extern int n_errors; + const char * volatile xbuf = buf; int len; no_optimize = 0; n_errors = 0; root = NULL; bpf_pcap = p; + init_regs(); if (setjmp(top_ctx)) { lex_cleanup(); freechunks(); @@ -335,8 +386,8 @@ pcap_compile(pcap_t *p, struct bpf_program *program, return -1; } - lex_init(buf ? buf : ""); - init_linktype(pcap_datalink(p)); + lex_init(xbuf ? xbuf : ""); + init_linktype(p); (void)pcap_parse(); if (n_errors) @@ -366,7 +417,7 @@ pcap_compile(pcap_t *p, struct bpf_program *program, int pcap_compile_nopcap(int snaplen_arg, int linktype_arg, struct bpf_program *program, - char *buf, int optimize, bpf_u_int32 mask) + const char *buf, int optimize, bpf_u_int32 mask) { pcap_t *p; int ret; @@ -435,14 +486,42 @@ merge(b0, b1) *p = b1; } + void finish_parse(p) struct block *p; { + struct block *ppi_dlt_check; + + ppi_dlt_check = gen_ppi_dlt_check(); + + if (ppi_dlt_check != NULL) + { + gen_and(ppi_dlt_check, p); + } + backpatch(p, gen_retblk(snaplen)); p->sense = !p->sense; backpatch(p, gen_retblk(0)); root = p->head; + + /* + * Insert before the statements of the first (root) block any + * statements needed to load the lengths of any variable-length + * headers into registers. + * + * XXX - a fancier strategy would be to insert those before the + * statements of all blocks that use those lengths and that + * have no predecessors that use them, so that we only compute + * the lengths if we need them. There might be even better + * approaches than that. However, as we're currently only + * handling variable-length radiotap headers, and as all + * filtering expressions other than raw link[M:N] tests + * require the length of that header, doing more for that + * header length isn't really worth the effort. + */ + + insert_load_llprefixlen(root); } void @@ -476,60 +555,63 @@ gen_not(b) } static struct block * -gen_cmp(offset, size, v) +gen_cmp(offrel, offset, size, v) + enum e_offrel offrel; 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; + return gen_ncmp(offrel, offset, size, 0xffffffff, BPF_JEQ, 0, v); } static struct block * -gen_cmp_gt(offset, size, v) +gen_cmp_gt(offrel, offset, size, v) + enum e_offrel offrel; u_int offset, size; bpf_int32 v; { - struct slist *s; - struct block *b; + return gen_ncmp(offrel, offset, size, 0xffffffff, BPF_JGT, 0, v); +} - s = new_stmt(BPF_LD|BPF_ABS|size); - s->s.k = offset; +static struct block * +gen_cmp_ge(offrel, offset, size, v) + enum e_offrel offrel; + u_int offset, size; + bpf_int32 v; +{ + return gen_ncmp(offrel, offset, size, 0xffffffff, BPF_JGE, 0, v); +} - b = new_block(JMP(BPF_JGT)); - b->stmts = s; - b->s.k = v; +static struct block * +gen_cmp_lt(offrel, offset, size, v) + enum e_offrel offrel; + u_int offset, size; + bpf_int32 v; +{ + return gen_ncmp(offrel, offset, size, 0xffffffff, BPF_JGE, 1, v); +} - return b; +static struct block * +gen_cmp_le(offrel, offset, size, v) + enum e_offrel offrel; + u_int offset, size; + bpf_int32 v; +{ + return gen_ncmp(offrel, offset, size, 0xffffffff, BPF_JGT, 1, v); } static struct block * -gen_mcmp(offset, size, v, mask) +gen_mcmp(offrel, offset, size, v, mask) + enum e_offrel offrel; 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; + return gen_ncmp(offrel, offset, size, mask, BPF_JEQ, 0, v); } static struct block * -gen_bcmp(offset, size, v) +gen_bcmp(offrel, offset, size, v) + enum e_offrel offrel; register u_int offset, size; register const u_char *v; { @@ -541,7 +623,7 @@ gen_bcmp(offset, size, v) 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); + tmp = gen_cmp(offrel, offset + size - 4, BPF_W, w); if (b != NULL) gen_and(b, tmp); b = tmp; @@ -551,14 +633,14 @@ gen_bcmp(offset, size, v) 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); + tmp = gen_cmp(offrel, 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]); + tmp = gen_cmp(offrel, offset, BPF_B, (bpf_int32)v[0]); if (b != NULL) gen_and(b, tmp); b = tmp; @@ -566,25 +648,33 @@ gen_bcmp(offset, size, v) return b; } +/* + * AND the field of size "size" at offset "offset" relative to the header + * specified by "offrel" with "mask", and compare it with the value "v" + * with the test specified by "jtype"; if "reverse" is true, the test + * should test the opposite of "jtype". + */ static struct block * -gen_ncmp(datasize, offset, mask, jtype, jvalue, reverse) - bpf_u_int32 datasize, offset, mask, jtype, jvalue; +gen_ncmp(offrel, offset, size, mask, jtype, reverse, v) + enum e_offrel offrel; + bpf_int32 v; + bpf_u_int32 offset, size, mask, jtype; int reverse; { - struct slist *s; + struct slist *s, *s2; struct block *b; - - s = new_stmt(BPF_LD|datasize|BPF_ABS); - s->s.k = offset; - + + s = gen_load_a(offrel, offset, size); + if (mask != 0xffffffff) { - s->next = new_stmt(BPF_ALU|BPF_AND|BPF_K); - s->next->s.k = mask; + s2 = new_stmt(BPF_ALU|BPF_AND|BPF_K); + s2->s.k = mask; + sappend(s, s2); } - + b = new_block(JMP(jtype)); b->stmts = s; - b->s.k = jvalue; + b->s.k = v; if (reverse && (jtype == BPF_JGT || jtype == BPF_JGE)) gen_not(b); return b; @@ -592,18 +682,37 @@ gen_ncmp(datasize, offset, mask, jtype, jvalue, reverse) /* * Various code constructs need to know the layout of the data link - * layer. These variables give the necessary offsets. + * layer. These variables give the necessary offsets from the beginning + * of the packet data. + * + * If the link layer has variable_length headers, the offsets are offsets + * from the end of the link-link-layer header, and "reg_ll_size" is + * the register number for a register containing the length of the + * link-layer header. Otherwise, "reg_ll_size" is -1. */ +static int reg_ll_size; + +/* + * This is the offset of the beginning of the link-layer header from + * the beginning of the raw packet data. + * + * It's usually 0, except for 802.11 with a fixed-length radio header. + * (For 802.11 with a variable-length radio header, we have to generate + * code to compute that offset; off_ll is 0 in that case.) + */ +static u_int off_ll; /* * This is the offset of the beginning of the MAC-layer header. - * It's usually 0, except for ATM LANE. + * It's usually 0, except for ATM LANE, where it's the offset, relative + * to the beginning of the raw packet data, of the Ethernet header. */ static u_int off_mac; /* * "off_linktype" is the offset to information in the link-layer header - * giving the packet type. + * giving the packet type. This offset is relative to the beginning + * of the link-layer header (i.e., it doesn't include off_ll). * * For Ethernet, it's the offset of the Ethernet type field. * @@ -640,6 +749,19 @@ static u_int off_vpi; static u_int off_vci; static u_int off_proto; +/* + * These are offsets for the MTP2 fields. + */ +static u_int off_li; + +/* + * These are offsets for the MTP3 fields. + */ +static u_int off_sio; +static u_int off_opc; +static u_int off_dpc; +static u_int off_sls; + /* * This is the offset of the first byte after the ATM pseudo_header, * or -1 if there is no ATM pseudo-header. @@ -648,6 +770,8 @@ static u_int off_payload; /* * These are offsets to the beginning of the network-layer header. + * They are relative to the beginning of the link-layer header (i.e., + * they don't include off_ll). * * If the link layer never uses 802.2 LLC: * @@ -674,10 +798,13 @@ static u_int off_nl_nosnap; static int linktype; static void -init_linktype(type) - int type; +init_linktype(p) + pcap_t *p; { - linktype = type; + linktype = pcap_datalink(p); +#ifdef PCAP_FDDIPAD + pcap_fddipad = p->fddipad; +#endif /* * Assume it's not raw ATM with a pseudo-header, for now. @@ -690,11 +817,27 @@ init_linktype(type) off_proto = -1; off_payload = -1; + /* + * And assume we're not doing SS7. + */ + off_li = -1; + off_sio = -1; + off_opc = -1; + off_dpc = -1; + off_sls = -1; + + /* + * Also assume it's not 802.11 with a fixed-length radio header. + */ + off_ll = 0; + orig_linktype = -1; orig_nl = -1; - orig_nl_nosnap = -1; + label_stack_depth = 0; - switch (type) { + reg_ll_size = -1; + + switch (linktype) { case DLT_ARCNET: off_linktype = 2; @@ -745,13 +888,8 @@ init_linktype(type) off_nl_nosnap = 12; /* no 802.2 LLC */ return; - case DLT_PFLOG: - off_linktype = 0; - off_nl = 28; - off_nl_nosnap = 28; /* no 802.2 LLC */ - return; - case DLT_PPP: + case DLT_PPP_PPPD: case DLT_C_HDLC: /* BSD/OS Cisco HDLC */ case DLT_PPP_SERIAL: /* NetBSD sync/async serial PPP */ off_linktype = 2; @@ -836,7 +974,9 @@ init_linktype(type) * * 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. + * 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 */ @@ -854,28 +994,68 @@ init_linktype(type) * 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 */ + off_ll = 144; + off_linktype = 24; + off_nl = 32; /* Prism+802.11+802.2+SNAP */ + off_nl_nosnap = 27; /* Prism+802.11+802.2 */ return; - case DLT_IEEE802_11_RADIO: + 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. + * 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. + * of the radio header. We should handle that the + * same way we handle the length at the beginning + * of the radiotap header. + * + * XXX - in Linux, do any drivers that supply an AVS + * header supply a link-layer type other than + * ARPHRD_IEEE80211_PRISM? If so, we should map that + * to DLT_IEEE802_11_RADIO_AVS; if not, or if there are + * any drivers that supply an AVS header but supply + * an ARPHRD value of ARPHRD_IEEE80211_PRISM, we'll + * have to check the header in the generated code to + * determine whether it's Prism or AVS. + */ + off_ll = 64; + off_linktype = 24; + off_nl = 32; /* Radio+802.11+802.2+SNAP */ + off_nl_nosnap = 27; /* Radio+802.11+802.2 */ + return; + + + /* + * At the moment we treat PPI as normal Radiotap encoded + * packets. The difference is in the function that generates + * the code at the beginning to compute the header length. + * Since this code generator of PPI supports bare 802.11 + * encapsulation only (i.e. the encapsulated DLT should be + * DLT_IEEE802_11) we generate code to check for this too. + */ + case DLT_PPI: + case DLT_IEEE802_11_RADIO: + /* + * Same as 802.11, but with an additional header before + * the 802.11 header, containing a bunch of additional + * information including radio-level information. + * + * The radiotap header is variable length, and we + * generate code to compute its length and store it + * in a register. These offsets are relative to the + * beginning of the 802.11 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 */ + off_linktype = 24; + off_nl = 32; /* 802.11+802.2+SNAP */ + off_nl_nosnap = 27; /* 802.11+802.2 */ return; case DLT_ATM_RFC1483: @@ -883,6 +1063,13 @@ init_linktype(type) /* * assume routed, non-ISO PDUs * (i.e., LLC = 0xAA-AA-03, OUT = 0x00-00-00) + * + * XXX - what about ISO PDUs, e.g. CLNP, ISIS, ESIS, + * or PPP with the PPP NLPID (e.g., PPPoA)? The + * latter would presumably be treated the way PPPoE + * should be, so you can do "pppoe and udp port 2049" + * or "pppoa and tcp port 80" and have it check for + * PPPo{A,E} and a PPP protocol of IP and.... */ off_linktype = 0; off_nl = 8; /* 802.2+SNAP */ @@ -898,7 +1085,7 @@ init_linktype(type) 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_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 */ @@ -953,87 +1140,418 @@ init_linktype(type) off_nl = 0; off_nl_nosnap = 0; /* no 802.2 LLC */ return; + + /* + * the only BPF-interesting FRF.16 frames are non-control frames; + * Frame Relay has a variable length link-layer + * so lets start with offset 4 for now and increments later on (FIXME); + */ + case DLT_MFR: + off_linktype = -1; + off_nl = 4; + off_nl_nosnap = 0; /* XXX - for now -> no 802.2 LLC */ + return; + + case DLT_APPLE_IP_OVER_IEEE1394: + off_linktype = 16; + off_nl = 18; + off_nl_nosnap = 18; /* 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_DOCSIS: + /* + * Currently, only raw "link[N:M]" filtering is supported. + */ + off_linktype = -1; + off_nl = -1; + off_nl_nosnap = -1; + return; + + case DLT_SYMANTEC_FIREWALL: + off_linktype = 6; + off_nl = 44; /* Ethernet II */ + off_nl_nosnap = 44; /* XXX - what does it do with 802.3 packets? */ + return; + +#ifdef HAVE_NET_PFVAR_H + case DLT_PFLOG: + off_linktype = 0; + off_nl = PFLOG_HDRLEN; + off_nl_nosnap = PFLOG_HDRLEN; /* no 802.2 LLC */ + return; +#endif + + case DLT_JUNIPER_MFR: + case DLT_JUNIPER_MLFR: + case DLT_JUNIPER_MLPPP: + case DLT_JUNIPER_PPP: + case DLT_JUNIPER_CHDLC: + case DLT_JUNIPER_FRELAY: + off_linktype = 4; + off_nl = 4; + off_nl_nosnap = -1; /* no 802.2 LLC */ + return; + + case DLT_JUNIPER_ATM1: + off_linktype = 4; /* in reality variable between 4-8 */ + off_nl = 4; + off_nl_nosnap = 14; + return; + + case DLT_JUNIPER_ATM2: + off_linktype = 8; /* in reality variable between 8-12 */ + off_nl = 8; + off_nl_nosnap = 18; + return; + + /* frames captured on a Juniper PPPoE service PIC + * contain raw ethernet frames */ + case DLT_JUNIPER_PPPOE: + case DLT_JUNIPER_ETHER: + off_linktype = 16; + off_nl = 18; /* Ethernet II */ + off_nl_nosnap = 21; /* 802.3+802.2 */ + return; + + case DLT_JUNIPER_PPPOE_ATM: + off_linktype = 4; + off_nl = 6; + off_nl_nosnap = -1; /* no 802.2 LLC */ + return; + + case DLT_JUNIPER_GGSN: + off_linktype = 6; + off_nl = 12; + off_nl_nosnap = -1; /* no 802.2 LLC */ + return; + + case DLT_JUNIPER_ES: + off_linktype = 6; + off_nl = -1; /* not really a network layer but raw IP adresses */ + off_nl_nosnap = -1; /* no 802.2 LLC */ + return; + + case DLT_JUNIPER_MONITOR: + off_linktype = 12; + off_nl = 12; /* raw IP/IP6 header */ + off_nl_nosnap = -1; /* no 802.2 LLC */ + return; + + case DLT_JUNIPER_SERVICES: + off_linktype = 12; + off_nl = -1; /* L3 proto location dep. on cookie type */ + off_nl_nosnap = -1; /* no 802.2 LLC */ + return; + + case DLT_JUNIPER_VP: + off_linktype = 18; + off_nl = -1; + off_nl_nosnap = -1; + return; + + case DLT_MTP2: + off_li = 2; + off_sio = 3; + off_opc = 4; + off_dpc = 4; + off_sls = 7; + off_linktype = -1; + off_nl = -1; + off_nl_nosnap = -1; + return; + + case DLT_MTP2_WITH_PHDR: + off_li = 6; + off_sio = 7; + off_opc = 8; + off_dpc = 8; + off_sls = 11; + off_linktype = -1; + off_nl = -1; + off_nl_nosnap = -1; + return; + +#ifdef DLT_PFSYNC + case DLT_PFSYNC: + off_linktype = -1; + off_nl = 4; + off_nl_nosnap = 4; + return; +#endif + + case DLT_LINUX_LAPD: + /* + * Currently, only raw "link[N:M]" filtering is supported. + */ + off_linktype = -1; + off_nl = -1; + off_nl_nosnap = -1; + return; + + case DLT_USB: + /* + * Currently, only raw "link[N:M]" filtering is supported. + */ + off_linktype = -1; + off_nl = -1; + off_nl_nosnap = -1; + return; + + case DLT_BLUETOOTH_HCI_H4: + /* + * Currently, only raw "link[N:M]" filtering is supported. + */ + off_linktype = -1; + off_nl = -1; + off_nl_nosnap = -1; + return; } bpf_error("unknown data link type %d", linktype); /* NOTREACHED */ } -static struct block * -gen_uncond(rsense) - int rsense; +/* + * Load a value relative to the beginning of the link-layer header. + * The link-layer header doesn't necessarily begin at the beginning + * of the packet data; there might be a variable-length prefix containing + * radio information. + */ +static struct slist * +gen_load_llrel(offset, size) + u_int offset, size; { - 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; + struct slist *s, *s2; - return b; -} + s = gen_llprefixlen(); -static inline struct block * -gen_true() -{ - return gen_uncond(1); + /* + * If "s" is non-null, it has code to arrange that the X register + * contains the length of the prefix preceding the link-layer + * header. + * + * Otherwise, the length of the prefix preceding the link-layer + * header is "off_ll". + */ + if (s != NULL) { + /* + * There's a variable-length prefix preceding the + * link-layer header. "s" points to a list of statements + * that put the length of that prefix into the X register. + * do an indirect load, to use the X register as an offset. + */ + s2 = new_stmt(BPF_LD|BPF_IND|size); + s2->s.k = offset; + sappend(s, s2); + } else { + /* + * There is no variable-length header preceding the + * link-layer header; add in off_ll, which, if there's + * a fixed-length header preceding the link-layer header, + * is the length of that header. + */ + s = new_stmt(BPF_LD|BPF_ABS|size); + s->s.k = offset + off_ll; + } + return s; } -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.) + * Load a value relative to the beginning of the specified header. */ -#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; +static struct slist * +gen_load_a(offrel, offset, size) + enum e_offrel offrel; + u_int offset, size; { - struct block *b0, *b1; + struct slist *s, *s2; - switch (proto) { + switch (offrel) { - case LLCSAP_ISONS: + case OR_PACKET: + s = new_stmt(BPF_LD|BPF_ABS|size); + s->s.k = offset; + break; + + case OR_LINK: + s = gen_load_llrel(offset, size); + break; + + case OR_NET: + s = gen_load_llrel(off_nl + offset, size); + break; + + case OR_NET_NOSNAP: + s = gen_load_llrel(off_nl_nosnap + offset, size); + break; + + case OR_TRAN_IPV4: /* - * 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? + * Load the X register with the length of the IPv4 header + * (plus the offset of the link-layer header, if it's + * preceded by a variable-length header such as a radio + * header), in bytes. */ - 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; + s = gen_loadx_iphdrlen(); - 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; + /* + * Load the item at {offset of the link-layer header} + + * {offset, relative to the start of the link-layer + * header, of the IPv4 header} + {length of the IPv4 header} + + * {specified offset}. + * + * (If the link-layer is variable-length, it's included + * in the value in the X register, and off_ll is 0.) + */ + s2 = new_stmt(BPF_LD|BPF_IND|size); + s2->s.k = off_ll + off_nl + offset; + sappend(s, s2); + break; + + case OR_TRAN_IPV6: + s = gen_load_llrel(off_nl + 40 + offset, size); + break; + + default: + abort(); + return NULL; + } + return s; +} + +/* + * Generate code to load into the X register the sum of the length of + * the IPv4 header and any variable-length header preceding the link-layer + * header. + */ +static struct slist * +gen_loadx_iphdrlen() +{ + struct slist *s, *s2; + + s = gen_llprefixlen(); + if (s != NULL) { + /* + * There's a variable-length prefix preceding the + * link-layer header. "s" points to a list of statements + * that put the length of that prefix into the X register. + * The 4*([k]&0xf) addressing mode can't be used, as we + * don't have a constant offset, so we have to load the + * value in question into the A register and add to it + * the value from the X register. + */ + s2 = new_stmt(BPF_LD|BPF_IND|BPF_B); + s2->s.k = off_nl; + sappend(s, s2); + s2 = new_stmt(BPF_ALU|BPF_AND|BPF_K); + s2->s.k = 0xf; + sappend(s, s2); + s2 = new_stmt(BPF_ALU|BPF_LSH|BPF_K); + s2->s.k = 2; + sappend(s, s2); + + /* + * The A register now contains the length of the + * IP header. We need to add to it the length + * of the prefix preceding the link-layer + * header, which is still in the X register, and + * move the result into the X register. + */ + sappend(s, new_stmt(BPF_ALU|BPF_ADD|BPF_X)); + sappend(s, new_stmt(BPF_MISC|BPF_TAX)); + } else { + /* + * There is no variable-length header preceding the + * link-layer header; add in off_ll, which, if there's + * a fixed-length header preceding the link-layer header, + * is the length of that header. + */ + s = new_stmt(BPF_LDX|BPF_MSH|BPF_B); + s->s.k = off_ll + off_nl; + } + return s; +} + +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)) + +/* + * Generate code to match a particular packet type. + * + * "proto" is an Ethernet type value, if > ETHERMTU, or an LLC SAP + * value, if <= ETHERMTU. We use that to determine whether to + * match the type/length field or to check the type/length field for + * a value <= ETHERMTU to see whether it's a type field and then do + * the appropriate test. + */ +static struct block * +gen_ether_linktype(proto) + register int proto; +{ + struct block *b0, *b1; + + switch (proto) { + + case LLCSAP_ISONS: + case LLCSAP_IP: case LLCSAP_NETBEUI: /* - * NetBEUI always uses 802.2 encapsulation. + * OSI protocols and NetBEUI always use 802.2 encapsulation, + * so we check the DSAP and SSAP. + * + * LLCSAP_IP checks for IP-over-802.2, rather + * than IP-over-Ethernet or IP-over-SNAP. + * * XXX - should we check both the DSAP and the * SSAP, like this, or should we check just the - * DSAP? + * DSAP, as we do for other types <= ETHERMTU + * (i.e., other SAP values)? */ - b0 = gen_cmp_gt(off_linktype, BPF_H, ETHERMTU); + b0 = gen_cmp_gt(OR_LINK, off_linktype, BPF_H, ETHERMTU); gen_not(b0); - b1 = gen_cmp(off_linktype + 2, BPF_H, (bpf_int32) - ((LLCSAP_NETBEUI << 8) | LLCSAP_NETBEUI)); + b1 = gen_cmp(OR_LINK, off_linktype + 2, BPF_H, (bpf_int32) + ((proto << 8) | proto)); gen_and(b0, b1); return b1; @@ -1070,8 +1588,10 @@ gen_ether_linktype(proto) * 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); + b0 = gen_cmp(OR_LINK, off_linktype + 2, BPF_B, + (bpf_int32)LLCSAP_IPX); + b1 = gen_cmp(OR_LINK, off_linktype + 2, BPF_H, + (bpf_int32)0xFFFF); gen_or(b0, b1); /* @@ -1085,7 +1605,7 @@ gen_ether_linktype(proto) * Now we generate code to check for 802.3 * frames in general. */ - b0 = gen_cmp_gt(off_linktype, BPF_H, ETHERMTU); + b0 = gen_cmp_gt(OR_LINK, off_linktype, BPF_H, ETHERMTU); gen_not(b0); /* @@ -1101,7 +1621,8 @@ gen_ether_linktype(proto) * do that before checking for the other frame * types. */ - b0 = gen_cmp(off_linktype, BPF_H, (bpf_int32)ETHERTYPE_IPX); + b0 = gen_cmp(OR_LINK, off_linktype, BPF_H, + (bpf_int32)ETHERTYPE_IPX); gen_or(b0, b1); return b1; @@ -1117,7 +1638,7 @@ gen_ether_linktype(proto) * 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); + b0 = gen_cmp_gt(OR_LINK, off_linktype, BPF_H, ETHERMTU); gen_not(b0); /* @@ -1142,7 +1663,7 @@ gen_ether_linktype(proto) * phase 1?); we just check for the Ethernet * protocol type. */ - b0 = gen_cmp(off_linktype, BPF_H, (bpf_int32)proto); + b0 = gen_cmp(OR_LINK, off_linktype, BPF_H, (bpf_int32)proto); gen_or(b0, b1); return b1; @@ -1157,9 +1678,10 @@ gen_ether_linktype(proto) * a length field, <= ETHERMTU) and * then check the DSAP. */ - b0 = gen_cmp_gt(off_linktype, BPF_H, ETHERMTU); + b0 = gen_cmp_gt(OR_LINK, off_linktype, BPF_H, ETHERMTU); gen_not(b0); - b1 = gen_cmp(off_linktype + 2, BPF_B, (bpf_int32)proto); + b1 = gen_cmp(OR_LINK, off_linktype + 2, BPF_B, + (bpf_int32)proto); gen_and(b0, b1); return b1; } else { @@ -1172,240 +1694,547 @@ gen_ether_linktype(proto) * will fail and the frame won't match, * which is what we want). */ - return gen_cmp(off_linktype, BPF_H, (bpf_int32)proto); + return gen_cmp(OR_LINK, off_linktype, BPF_H, + (bpf_int32)proto); } } } +/* + * Generate code to match a particular packet type. + * + * "proto" is an Ethernet type value, if > ETHERMTU, or an LLC SAP + * value, if <= ETHERMTU. We use that to determine whether to + * match the type field or to check the type field for the special + * LINUX_SLL_P_802_2 value and then do the appropriate test. + */ static struct block * -gen_linktype(proto) +gen_linux_sll_linktype(proto) register int proto; { - struct block *b0, *b1, *b2; + struct block *b0, *b1; - switch (linktype) { + switch (proto) { - case DLT_EN10MB: - return gen_ether_linktype(proto); - break; + case LLCSAP_ISONS: + case LLCSAP_IP: + case LLCSAP_NETBEUI: + /* + * OSI protocols and NetBEUI always use 802.2 encapsulation, + * so we check the DSAP and SSAP. + * + * LLCSAP_IP checks for IP-over-802.2, rather + * than IP-over-Ethernet or IP-over-SNAP. + * + * XXX - should we check both the DSAP and the + * SSAP, like this, or should we check just the + * DSAP, as we do for other types <= ETHERMTU + * (i.e., other SAP values)? + */ + b0 = gen_cmp(OR_LINK, off_linktype, BPF_H, LINUX_SLL_P_802_2); + b1 = gen_cmp(OR_LINK, off_linktype + 2, BPF_H, (bpf_int32) + ((proto << 8) | proto)); + gen_and(b0, b1); + return b1; - case DLT_C_HDLC: - switch (proto) { + 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(OR_LINK, 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(OR_LINK, off_linktype, BPF_H, LINUX_SLL_P_802_2); + gen_and(b0, b1); - case LLCSAP_ISONS: - proto = (proto << 8 | LLCSAP_ISONS); - /* fall through */ + /* + * Now check for 802.3 frames and OR that with + * the previous test. + */ + b0 = gen_cmp(OR_LINK, off_linktype, BPF_H, LINUX_SLL_P_802_3); + gen_or(b0, b1); - default: - return gen_cmp(off_linktype, BPF_H, (bpf_int32)proto); - break; - } - break; + /* + * Now add the check for Ethernet_II frames, and + * do that before checking for the other frame + * types. + */ + b0 = gen_cmp(OR_LINK, off_linktype, BPF_H, + (bpf_int32)ETHERTYPE_IPX); + gen_or(b0, b1); + return b1; - 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 ETHERTYPE_ATALK: + case ETHERTYPE_AARP: + /* + * EtherTalk (AppleTalk protocols on Ethernet link + * layer) may use 802.2 encapsulation. + */ - 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. + * Check for 802.2 encapsulation (EtherTalk phase 2?); + * we check for the 802.2 protocol type in the + * "Ethernet type" field. + */ + b0 = gen_cmp(OR_LINK, 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). * - * We assume LANE means Ethernet, not Token Ring. + * 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 (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); + 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(OR_LINK, off_linktype, BPF_H, (bpf_int32)proto); + gen_or(b0, b1); + return b1; + + default: + if (proto <= ETHERMTU) { /* - * Now generate an Ethernet test. + * 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. */ - b1 = gen_ether_linktype(proto); + b0 = gen_cmp(OR_LINK, off_linktype, BPF_H, + LINUX_SLL_P_802_2); + b1 = gen_cmp(OR_LINK, off_linktype + 2, BPF_B, + (bpf_int32)proto); gen_and(b0, b1); return b1; } else { /* - * Check for LLC encapsulation and then check the - * protocol. + * 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). */ - b0 = gen_atmfield_code(A_PROTOTYPE, PT_LLC, BPF_JEQ, 0); - b1 = gen_llc(proto); - gen_and(b0, b1); - return b1; + return gen_cmp(OR_LINK, off_linktype, BPF_H, + (bpf_int32)proto); } + } +} - case DLT_LINUX_SLL: +static void +insert_radiotap_load_llprefixlen(b) + struct block *b; +{ + struct slist *s1, *s2; + + /* + * Prepend to the statements in this block code to load the + * length of the radiotap header into the register assigned + * to hold that length, if one has been assigned. + */ + if (reg_ll_size != -1) { + /* + * The 2 bytes at offsets of 2 and 3 from the beginning + * of the radiotap header are the length of the radiotap + * header; unfortunately, it's little-endian, so we have + * to load it a byte at a time and construct the value. + */ + + /* + * Load the high-order byte, at an offset of 3, shift it + * left a byte, and put the result in the X register. + */ + s1 = new_stmt(BPF_LD|BPF_B|BPF_ABS); + s1->s.k = 3; + s2 = new_stmt(BPF_ALU|BPF_LSH|BPF_K); + sappend(s1, s2); + s2->s.k = 8; + s2 = new_stmt(BPF_MISC|BPF_TAX); + sappend(s1, s2); + + /* + * Load the next byte, at an offset of 2, and OR the + * value from the X register into it. + */ + s2 = new_stmt(BPF_LD|BPF_B|BPF_ABS); + sappend(s1, s2); + s2->s.k = 2; + s2 = new_stmt(BPF_ALU|BPF_OR|BPF_X); + sappend(s1, s2); + + /* + * Now allocate a register to hold that value and store + * it. + */ + s2 = new_stmt(BPF_ST); + s2->s.k = reg_ll_size; + sappend(s1, s2); + + /* + * Now move it into the X register. + */ + s2 = new_stmt(BPF_MISC|BPF_TAX); + sappend(s1, s2); + + /* + * Now append all the existing statements in this + * block to these statements. + */ + sappend(s1, b->stmts); + b->stmts = s1; + } +} + +/* + * At the moment we treat PPI as normal Radiotap encoded + * packets. The difference is in the function that generates + * the code at the beginning to compute the header length. + * Since this code generator of PPI supports bare 802.11 + * encapsulation only (i.e. the encapsulated DLT should be + * DLT_IEEE802_11) we generate code to check for this too. + */ +static void +insert_ppi_load_llprefixlen(b) + struct block *b; +{ + struct slist *s1, *s2; + + /* + * Prepend to the statements in this block code to load the + * length of the radiotap header into the register assigned + * to hold that length, if one has been assigned. + */ + if (reg_ll_size != -1) { + /* + * The 2 bytes at offsets of 2 and 3 from the beginning + * of the radiotap header are the length of the radiotap + * header; unfortunately, it's little-endian, so we have + * to load it a byte at a time and construct the value. + */ + + /* + * Load the high-order byte, at an offset of 3, shift it + * left a byte, and put the result in the X register. + */ + s1 = new_stmt(BPF_LD|BPF_B|BPF_ABS); + s1->s.k = 3; + s2 = new_stmt(BPF_ALU|BPF_LSH|BPF_K); + sappend(s1, s2); + s2->s.k = 8; + s2 = new_stmt(BPF_MISC|BPF_TAX); + sappend(s1, s2); + + /* + * Load the next byte, at an offset of 2, and OR the + * value from the X register into it. + */ + s2 = new_stmt(BPF_LD|BPF_B|BPF_ABS); + sappend(s1, s2); + s2->s.k = 2; + s2 = new_stmt(BPF_ALU|BPF_OR|BPF_X); + sappend(s1, s2); + + /* + * Now allocate a register to hold that value and store + * it. + */ + s2 = new_stmt(BPF_ST); + s2->s.k = reg_ll_size; + sappend(s1, s2); + + /* + * Now move it into the X register. + */ + s2 = new_stmt(BPF_MISC|BPF_TAX); + sappend(s1, s2); + + /* + * Now append all the existing statements in this + * block to these statements. + */ + sappend(s1, b->stmts); + b->stmts = s1; + + } +} + +static struct block * +gen_ppi_dlt_check(void) +{ + struct slist *s_load_dlt; + struct block *b; + + if (linktype == DLT_PPI) + { + /* Create the statements that check for the DLT + */ + s_load_dlt = new_stmt(BPF_LD|BPF_W|BPF_ABS); + s_load_dlt->s.k = 4; + + b = new_block(JMP(BPF_JEQ)); + + b->stmts = s_load_dlt; + b->s.k = SWAPLONG(DLT_IEEE802_11); + } + else + { + b = NULL; + } + + return b; +} + +static void +insert_load_llprefixlen(b) + struct block *b; +{ + switch (linktype) { + + /* + * At the moment we treat PPI as normal Radiotap encoded + * packets. The difference is in the function that generates + * the code at the beginning to compute the header length. + * Since this code generator of PPI supports bare 802.11 + * encapsulation only (i.e. the encapsulated DLT should be + * DLT_IEEE802_11) we generate code to check for this too. + */ + case DLT_PPI: + insert_ppi_load_llprefixlen(b); + break; + + case DLT_IEEE802_11_RADIO: + insert_radiotap_load_llprefixlen(b); + break; + } +} + + +static struct slist * +gen_radiotap_llprefixlen(void) +{ + struct slist *s; + + if (reg_ll_size == -1) { + /* + * We haven't yet assigned a register for the length + * of the radiotap header; allocate one. + */ + reg_ll_size = alloc_reg(); + } + + /* + * Load the register containing the radiotap length + * into the X register. + */ + s = new_stmt(BPF_LDX|BPF_MEM); + s->s.k = reg_ll_size; + return s; +} + +/* + * At the moment we treat PPI as normal Radiotap encoded + * packets. The difference is in the function that generates + * the code at the beginning to compute the header length. + * Since this code generator of PPI supports bare 802.11 + * encapsulation only (i.e. the encapsulated DLT should be + * DLT_IEEE802_11) we generate code to check for this too. + */ +static struct slist * +gen_ppi_llprefixlen(void) +{ + struct slist *s; + + if (reg_ll_size == -1) { + /* + * We haven't yet assigned a register for the length + * of the radiotap header; allocate one. + */ + reg_ll_size = alloc_reg(); + } + + /* + * Load the register containing the radiotap length + * into the X register. + */ + s = new_stmt(BPF_LDX|BPF_MEM); + s->s.k = reg_ll_size; + return s; +} + + + +/* + * Generate code to compute the link-layer header length, if necessary, + * putting it into the X register, and to return either a pointer to a + * "struct slist" for the list of statements in that code, or NULL if + * no code is necessary. + */ +static struct slist * +gen_llprefixlen(void) +{ + switch (linktype) { + + case DLT_PPI: + return gen_ppi_llprefixlen(); + + + case DLT_IEEE802_11_RADIO: + return gen_radiotap_llprefixlen(); + + default: + return NULL; + } +} + +/* + * Generate code to match a particular packet type by matching the + * link-layer type field or fields in the 802.2 LLC header. + * + * "proto" is an Ethernet type value, if > ETHERMTU, or an LLC SAP + * value, if <= ETHERMTU. + */ +static struct block * +gen_linktype(proto) + register int proto; +{ + struct block *b0, *b1, *b2; + + /* are we checking MPLS-encapsulated packets? */ + if (label_stack_depth > 0) { switch (proto) { + case ETHERTYPE_IP: + case PPP_IP: + /* FIXME add other L3 proto IDs */ + return gen_mpls_linktype(Q_IP); - 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 ETHERTYPE_IPV6: + case PPP_IPV6: + /* FIXME add other L3 proto IDs */ + return gen_mpls_linktype(Q_IPV6); - 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; + default: + bpf_error("unsupported protocol over mpls"); + /* NOTREACHED */ + } + } - 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; + switch (linktype) { - 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); + case DLT_EN10MB: + return gen_ether_linktype(proto); + /*NOTREACHED*/ + break; - /* - * 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); + case DLT_C_HDLC: + switch (proto) { - /* - * 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 LLCSAP_ISONS: + proto = (proto << 8 | LLCSAP_ISONS); + /* fall through */ - case ETHERTYPE_ATALK: - case ETHERTYPE_AARP: - /* - * EtherTalk (AppleTalk protocols on Ethernet link - * layer) may use 802.2 encapsulation. - */ + default: + return gen_cmp(OR_LINK, off_linktype, BPF_H, + (bpf_int32)proto); + /*NOTREACHED*/ + break; + } + break; + + case DLT_PPI: + case DLT_FDDI: + case DLT_IEEE802: + case DLT_IEEE802_11: + case DLT_IEEE802_11_RADIO_AVS: + case DLT_IEEE802_11_RADIO: + case DLT_PRISM_HEADER: + case DLT_ATM_RFC1483: + case DLT_ATM_CLIP: + case DLT_IP_OVER_FC: + return gen_llc_linktype(proto); + /*NOTREACHED*/ + 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 for 802.2 encapsulation (EtherTalk phase 2?); - * we check for the 802.2 protocol type in the - * "Ethernet type" field. + * Check that the packet doesn't begin with an + * LE Control marker. (We've already generated + * a test for LANE.) */ - b0 = gen_cmp(off_linktype, BPF_H, LINUX_SLL_P_802_2); + b0 = gen_cmp(OR_LINK, SUNATM_PKT_BEGIN_POS, BPF_H, + 0xFF00); + 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). + * Now generate an Ethernet test. */ - 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); + b1 = gen_ether_linktype(proto); gen_and(b0, b1); - + return b1; + } else { /* - * Check for Ethernet encapsulation (Ethertalk - * phase 1?); we just check for the Ethernet - * protocol type. + * Check for LLC encapsulation and then check the + * protocol. */ - b0 = gen_cmp(off_linktype, BPF_H, (bpf_int32)proto); - - gen_or(b0, b1); + b0 = gen_atmfield_code(A_PROTOTYPE, PT_LLC, BPF_JEQ, 0); + b1 = gen_llc_linktype(proto); + gen_and(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); - } } + /*NOTREACHED*/ + break; + + case DLT_LINUX_SLL: + return gen_linux_sll_linktype(proto); + /*NOTREACHED*/ break; case DLT_SLIP: @@ -1413,7 +2242,7 @@ gen_linktype(proto) case DLT_RAW: /* * These types don't provide any type field; packets - * are always IP. + * are always IPv4 or IPv6. * * XXX - for IPv4, check for a version number of 4, and, * for IPv6, check for a version number of 6? @@ -1421,17 +2250,22 @@ gen_linktype(proto) switch (proto) { case ETHERTYPE_IP: + /* Check for a version number of 4. */ + return gen_mcmp(OR_LINK, 0, BPF_B, 0x40, 0xF0); #ifdef INET6 case ETHERTYPE_IPV6: + /* Check for a version number of 6. */ + return gen_mcmp(OR_LINK, 0, BPF_B, 0x60, 0xF0); #endif - return gen_true(); /* always true */ default: return gen_false(); /* always false */ } + /*NOTREACHED*/ break; case DLT_PPP: + case DLT_PPP_PPPD: case DLT_PPP_SERIAL: case DLT_PPP_ETHER: /* @@ -1489,10 +2323,10 @@ gen_linktype(proto) switch (proto) { case ETHERTYPE_IP: - b0 = gen_cmp(off_linktype, BPF_H, PPP_IP); - b1 = gen_cmp(off_linktype, BPF_H, PPP_VJC); + b0 = gen_cmp(OR_LINK, off_linktype, BPF_H, PPP_IP); + b1 = gen_cmp(OR_LINK, off_linktype, BPF_H, PPP_VJC); gen_or(b0, b1); - b0 = gen_cmp(off_linktype, BPF_H, PPP_VJNC); + b0 = gen_cmp(OR_LINK, off_linktype, BPF_H, PPP_VJNC); gen_or(b1, b0); return b0; @@ -1537,7 +2371,6 @@ gen_linktype(proto) case DLT_NULL: case DLT_LOOP: case DLT_ENC: - case DLT_PFLOG: /* * For DLT_NULL, the link-layer header is a 32-bit * word containing an AF_ value in *host* byte order, @@ -1559,8 +2392,6 @@ gen_linktype(proto) * This means that, when reading a capture file, just * checking for our AF_INET6 value won't work if the * capture file came from another OS. - * - * XXX - what's the byte order for DLT_PFLOG? */ switch (proto) { @@ -1601,7 +2432,27 @@ gen_linktype(proto) proto = SWAPLONG(proto); proto = htonl(proto); } - return (gen_cmp(0, BPF_W, (bpf_int32)proto)); + return (gen_cmp(OR_LINK, 0, BPF_W, (bpf_int32)proto)); + +#ifdef HAVE_NET_PFVAR_H + case DLT_PFLOG: + /* + * af field is host byte order in contrast to the rest of + * the packet. + */ + if (proto == ETHERTYPE_IP) + return (gen_cmp(OR_LINK, offsetof(struct pfloghdr, af), + BPF_B, (bpf_int32)AF_INET)); +#ifdef INET6 + else if (proto == ETHERTYPE_IPV6) + return (gen_cmp(OR_LINK, offsetof(struct pfloghdr, af), + BPF_B, (bpf_int32)AF_INET6)); +#endif /* INET6 */ + else + return gen_false(); + /*NOTREACHED*/ + break; +#endif /* HAVE_NET_PFVAR_H */ case DLT_ARCNET: case DLT_ARCNET_LINUX: @@ -1616,34 +2467,35 @@ gen_linktype(proto) #ifdef INET6 case ETHERTYPE_IPV6: - return (gen_cmp(off_linktype, BPF_B, + return (gen_cmp(OR_LINK, off_linktype, BPF_B, (bpf_int32)ARCTYPE_INET6)); #endif /* INET6 */ case ETHERTYPE_IP: - b0 = gen_cmp(off_linktype, BPF_B, + b0 = gen_cmp(OR_LINK, off_linktype, BPF_B, (bpf_int32)ARCTYPE_IP); - b1 = gen_cmp(off_linktype, BPF_B, + b1 = gen_cmp(OR_LINK, 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, + b0 = gen_cmp(OR_LINK, off_linktype, BPF_B, (bpf_int32)ARCTYPE_ARP); - b1 = gen_cmp(off_linktype, BPF_B, + b1 = gen_cmp(OR_LINK, 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, + return (gen_cmp(OR_LINK, off_linktype, BPF_B, (bpf_int32)ARCTYPE_REVARP)); case ETHERTYPE_ATALK: - return (gen_cmp(off_linktype, BPF_B, + return (gen_cmp(OR_LINK, off_linktype, BPF_B, (bpf_int32)ARCTYPE_ATALK)); } + /*NOTREACHED*/ break; case DLT_LTALK: @@ -1653,6 +2505,7 @@ gen_linktype(proto) default: return gen_false(); } + /*NOTREACHED*/ break; case DLT_FRELAY: @@ -1666,14 +2519,14 @@ gen_linktype(proto) /* * Check for the special NLPID for IP. */ - return gen_cmp(2, BPF_H, (0x03<<8) | 0xcc); + return gen_cmp(OR_LINK, 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); + return gen_cmp(OR_LINK, 2, BPF_H, (0x03<<8) | 0x8e); #endif case LLCSAP_ISONS: @@ -1688,9 +2541,9 @@ gen_linktype(proto) * 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); + b0 = gen_cmp(OR_LINK, 2, BPF_H, (0x03<<8) | ISO8473_CLNP); + b1 = gen_cmp(OR_LINK, 2, BPF_H, (0x03<<8) | ISO9542_ESIS); + b2 = gen_cmp(OR_LINK, 2, BPF_H, (0x03<<8) | ISO10589_ISIS); gen_or(b1, b2); gen_or(b0, b2); return b2; @@ -1698,7 +2551,42 @@ gen_linktype(proto) default: return gen_false(); } + /*NOTREACHED*/ break; + + case DLT_JUNIPER_MFR: + case DLT_JUNIPER_MLFR: + case DLT_JUNIPER_MLPPP: + case DLT_JUNIPER_ATM1: + case DLT_JUNIPER_ATM2: + case DLT_JUNIPER_PPPOE: + case DLT_JUNIPER_PPPOE_ATM: + case DLT_JUNIPER_GGSN: + case DLT_JUNIPER_ES: + case DLT_JUNIPER_MONITOR: + case DLT_JUNIPER_SERVICES: + case DLT_JUNIPER_ETHER: + case DLT_JUNIPER_PPP: + case DLT_JUNIPER_FRELAY: + case DLT_JUNIPER_CHDLC: + case DLT_JUNIPER_VP: + /* just lets verify the magic number for now - + * on ATM we may have up to 6 different encapsulations on the wire + * and need a lot of heuristics to figure out that the payload + * might be; + * + * FIXME encapsulation specific BPF_ filters + */ + return gen_mcmp(OR_LINK, 0, BPF_W, 0x4d474300, 0xffffff00); /* compare the magic number */ + + case DLT_LINUX_IRDA: + bpf_error("IrDA link-layer type filtering not implemented"); + + case DLT_DOCSIS: + bpf_error("DOCSIS link-layer type filtering not implemented"); + + case DLT_LINUX_LAPD: + bpf_error("LAPD link-layer type filtering not implemented"); } /* @@ -1711,7 +2599,7 @@ gen_linktype(proto) * * Therefore, if "off_linktype" is -1, there's an error. */ - if (off_linktype == -1) + if (off_linktype == (u_int)-1) abort(); /* @@ -1723,7 +2611,7 @@ gen_linktype(proto) * at "off_linktype", so we don't have to do the code generation * above.) */ - return gen_cmp(off_linktype, BPF_H, (bpf_int32)proto); + return gen_cmp(OR_LINK, off_linktype, BPF_H, (bpf_int32)proto); } /* @@ -1749,14 +2637,23 @@ gen_snap(orgcode, ptype, offset) 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); + return gen_bcmp(OR_LINK, offset, 8, snapblock); } /* - * Check for a given protocol value assuming an 802.2 LLC header. + * Generate code to match a particular packet type, for link-layer types + * using 802.2 LLC headers. + * + * This is *NOT* used for Ethernet; "gen_ether_linktype()" is used + * for that - it handles the D/I/X Ethernet vs. 802.3+802.2 issues. + * + * "proto" is an Ethernet type value, if > ETHERMTU, or an LLC SAP + * value, if <= ETHERMTU. We use that to determine whether to + * match the DSAP or both DSAP and LSAP or to check the OUI and + * protocol ID in a SNAP header. */ static struct block * -gen_llc(proto) +gen_llc_linktype(proto) int proto; { /* @@ -1765,23 +2662,24 @@ gen_llc(proto) 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)); + /* + * XXX - should we check both the DSAP and the + * SSAP, like this, or should we check just the + * DSAP, as we do for other types <= ETHERMTU + * (i.e., other SAP values)? + */ + return gen_cmp(OR_LINK, off_linktype, BPF_H, (bpf_u_int32) + ((proto << 8) | proto)); 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); + return gen_cmp(OR_LINK, off_linktype, BPF_B, + (bpf_int32)LLCSAP_IPX); case ETHERTYPE_ATALK: /* @@ -1805,7 +2703,8 @@ gen_llc(proto) * This is an LLC SAP value, so check * the DSAP. */ - return gen_cmp(off_linktype, BPF_B, (bpf_int32)proto); + return gen_cmp(OR_LINK, off_linktype, BPF_B, + (bpf_int32)proto); } else { /* * This is an Ethernet type; we assume that it's @@ -1827,7 +2726,8 @@ gen_llc(proto) * 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); + return gen_cmp(OR_LINK, off_linktype+6, BPF_H, + (bpf_int32)proto); } } } @@ -1869,7 +2769,7 @@ gen_hostop(addr, mask, dir, proto, src_off, dst_off) abort(); } b0 = gen_linktype(proto); - b1 = gen_mcmp(offset, BPF_W, (bpf_int32)addr, mask); + b1 = gen_mcmp(OR_NET, offset, BPF_W, (bpf_int32)addr, mask); gen_and(b0, b1); return b1; } @@ -1915,12 +2815,12 @@ gen_hostop6(addr, mask, dir, proto, src_off, dst_off) /* 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])); + b1 = gen_mcmp(OR_NET, offset + 12, BPF_W, ntohl(a[3]), ntohl(m[3])); + b0 = gen_mcmp(OR_NET, 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])); + b0 = gen_mcmp(OR_NET, 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])); + b0 = gen_mcmp(OR_NET, offset + 0, BPF_W, ntohl(a[0]), ntohl(m[0])); gen_and(b0, b1); b0 = gen_linktype(proto); gen_and(b0, b1); @@ -1937,10 +2837,10 @@ gen_ehostop(eaddr, dir) switch (dir) { case Q_SRC: - return gen_bcmp(off_mac + 6, 6, eaddr); + return gen_bcmp(OR_LINK, off_mac + 6, 6, eaddr); case Q_DST: - return gen_bcmp(off_mac + 0, 6, eaddr); + return gen_bcmp(OR_LINK, off_mac + 0, 6, eaddr); case Q_AND: b0 = gen_ehostop(eaddr, Q_SRC); @@ -1972,16 +2872,16 @@ gen_fhostop(eaddr, dir) switch (dir) { case Q_SRC: #ifdef PCAP_FDDIPAD - return gen_bcmp(6 + 1 + pcap_fddipad, 6, eaddr); + return gen_bcmp(OR_LINK, 6 + 1 + pcap_fddipad, 6, eaddr); #else - return gen_bcmp(6 + 1, 6, eaddr); + return gen_bcmp(OR_LINK, 6 + 1, 6, eaddr); #endif case Q_DST: #ifdef PCAP_FDDIPAD - return gen_bcmp(0 + 1 + pcap_fddipad, 6, eaddr); + return gen_bcmp(OR_LINK, 0 + 1 + pcap_fddipad, 6, eaddr); #else - return gen_bcmp(0 + 1, 6, eaddr); + return gen_bcmp(OR_LINK, 0 + 1, 6, eaddr); #endif case Q_AND: @@ -2013,10 +2913,10 @@ gen_thostop(eaddr, dir) switch (dir) { case Q_SRC: - return gen_bcmp(8, 6, eaddr); + return gen_bcmp(OR_LINK, 8, 6, eaddr); case Q_DST: - return gen_bcmp(2, 6, eaddr); + return gen_bcmp(OR_LINK, 2, 6, eaddr); case Q_AND: b0 = gen_thostop(eaddr, Q_SRC); @@ -2073,8 +2973,7 @@ gen_wlanhostop(eaddr, dir) * * 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; + s = gen_load_a(OR_LINK, 1, BPF_B); b1 = new_block(JMP(BPF_JSET)); b1->s.k = 0x01; /* To DS */ b1->stmts = s; @@ -2082,15 +2981,14 @@ gen_wlanhostop(eaddr, dir) /* * If To DS is set, the SA is at 24. */ - b0 = gen_bcmp(24, 6, eaddr); + b0 = gen_bcmp(OR_LINK, 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; + s = gen_load_a(OR_LINK, 1, BPF_B); b2 = new_block(JMP(BPF_JSET)); b2->s.k = 0x01; /* To DS */ b2->stmts = s; @@ -2099,7 +2997,7 @@ gen_wlanhostop(eaddr, dir) /* * If To DS is not set, the SA is at 16. */ - b1 = gen_bcmp(16, 6, eaddr); + b1 = gen_bcmp(OR_LINK, 16, 6, eaddr); gen_and(b2, b1); /* @@ -2113,8 +3011,7 @@ gen_wlanhostop(eaddr, dir) * 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; + s = gen_load_a(OR_LINK, 1, BPF_B); b1 = new_block(JMP(BPF_JSET)); b1->s.k = 0x02; /* From DS */ b1->stmts = s; @@ -2123,8 +3020,7 @@ gen_wlanhostop(eaddr, dir) /* * Now check for data frames with From DS not set. */ - s = new_stmt(BPF_LD|BPF_B|BPF_ABS); - s->s.k = 1; + s = gen_load_a(OR_LINK, 1, BPF_B); b2 = new_block(JMP(BPF_JSET)); b2->s.k = 0x02; /* From DS */ b2->stmts = s; @@ -2133,7 +3029,7 @@ gen_wlanhostop(eaddr, dir) /* * If From DS isn't set, the SA is at 10. */ - b1 = gen_bcmp(10, 6, eaddr); + b1 = gen_bcmp(OR_LINK, 10, 6, eaddr); gen_and(b2, b1); /* @@ -2147,8 +3043,7 @@ gen_wlanhostop(eaddr, dir) * 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; + gen_load_a(OR_LINK, 0, BPF_B); b1 = new_block(JMP(BPF_JSET)); b1->s.k = 0x08; b1->stmts = s; @@ -2163,8 +3058,7 @@ gen_wlanhostop(eaddr, dir) * is a management frame. * I.e, check "!(link[0] & 0x08)". */ - s = new_stmt(BPF_LD|BPF_B|BPF_ABS); - s->s.k = 0; + s = gen_load_a(OR_LINK, 0, BPF_B); b2 = new_block(JMP(BPF_JSET)); b2->s.k = 0x08; b2->stmts = s; @@ -2173,7 +3067,7 @@ gen_wlanhostop(eaddr, dir) /* * For management frames, the SA is at 10. */ - b1 = gen_bcmp(10, 6, eaddr); + b1 = gen_bcmp(OR_LINK, 10, 6, eaddr); gen_and(b2, b1); /* @@ -2191,8 +3085,7 @@ gen_wlanhostop(eaddr, dir) * * I.e., check "!(link[0] & 0x04)". */ - s = new_stmt(BPF_LD|BPF_B|BPF_ABS); - s->s.k = 0; + s = gen_load_a(OR_LINK, 0, BPF_B); b1 = new_block(JMP(BPF_JSET)); b1->s.k = 0x04; b1->stmts = s; @@ -2227,8 +3120,7 @@ gen_wlanhostop(eaddr, dir) * * First, check for To DS set, i.e. "link[1] & 0x01". */ - s = new_stmt(BPF_LD|BPF_B|BPF_ABS); - s->s.k = 1; + s = gen_load_a(OR_LINK, 1, BPF_B); b1 = new_block(JMP(BPF_JSET)); b1->s.k = 0x01; /* To DS */ b1->stmts = s; @@ -2236,15 +3128,14 @@ gen_wlanhostop(eaddr, dir) /* * If To DS is set, the DA is at 16. */ - b0 = gen_bcmp(16, 6, eaddr); + b0 = gen_bcmp(OR_LINK, 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; + s = gen_load_a(OR_LINK, 1, BPF_B); b2 = new_block(JMP(BPF_JSET)); b2->s.k = 0x01; /* To DS */ b2->stmts = s; @@ -2253,7 +3144,7 @@ gen_wlanhostop(eaddr, dir) /* * If To DS is not set, the DA is at 4. */ - b1 = gen_bcmp(4, 6, eaddr); + b1 = gen_bcmp(OR_LINK, 4, 6, eaddr); gen_and(b2, b1); /* @@ -2266,8 +3157,7 @@ gen_wlanhostop(eaddr, dir) * 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; + s = gen_load_a(OR_LINK, 0, BPF_B); b1 = new_block(JMP(BPF_JSET)); b1->s.k = 0x08; b1->stmts = s; @@ -2282,8 +3172,7 @@ gen_wlanhostop(eaddr, dir) * is a management frame. * I.e, check "!(link[0] & 0x08)". */ - s = new_stmt(BPF_LD|BPF_B|BPF_ABS); - s->s.k = 0; + s = gen_load_a(OR_LINK, 0, BPF_B); b2 = new_block(JMP(BPF_JSET)); b2->s.k = 0x08; b2->stmts = s; @@ -2292,7 +3181,7 @@ gen_wlanhostop(eaddr, dir) /* * For management frames, the DA is at 4. */ - b1 = gen_bcmp(4, 6, eaddr); + b1 = gen_bcmp(OR_LINK, 4, 6, eaddr); gen_and(b2, b1); /* @@ -2310,8 +3199,7 @@ gen_wlanhostop(eaddr, dir) * * I.e., check "!(link[0] & 0x04)". */ - s = new_stmt(BPF_LD|BPF_B|BPF_ABS); - s->s.k = 0; + s = gen_load_a(OR_LINK, 0, BPF_B); b1 = new_block(JMP(BPF_JSET)); b1->s.k = 0x04; b1->stmts = s; @@ -2355,10 +3243,10 @@ gen_ipfchostop(eaddr, dir) switch (dir) { case Q_SRC: - return gen_bcmp(10, 6, eaddr); + return gen_bcmp(OR_LINK, 10, 6, eaddr); case Q_DST: - return gen_bcmp(2, 6, eaddr); + return gen_bcmp(OR_LINK, 2, 6, eaddr); case Q_AND: b0 = gen_ipfchostop(eaddr, Q_SRC); @@ -2396,10 +3284,9 @@ gen_ipfchostop(eaddr, dir) * and not generate masking instructions if the mask is 0xFFFF. */ static struct block * -gen_dnhostop(addr, dir, base_off) +gen_dnhostop(addr, dir) 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 */ @@ -2419,47 +3306,46 @@ gen_dnhostop(addr, dir, base_off) 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); + b0 = gen_dnhostop(addr, Q_SRC); + b1 = gen_dnhostop(addr, Q_DST); 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); + b0 = gen_dnhostop(addr, Q_SRC); + b1 = gen_dnhostop(addr, Q_DST); gen_or(b0, b1); return b1; case Q_ISO: - bpf_error("ISO host filtering not implemented"); + 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, + tmp = gen_mcmp(OR_NET, 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)); + b1 = gen_cmp(OR_NET, 2 + 1 + offset_lh, + BPF_H, (bpf_int32)ntohs((u_short)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)); + tmp = gen_mcmp(OR_NET, 2, BPF_B, (bpf_int32)0x06, (bpf_int32)0x7); + b2 = gen_cmp(OR_NET, 2 + offset_lh, BPF_H, (bpf_int32)ntohs((u_short)addr)); gen_and(tmp, b2); gen_or(b2, b1); /* Check for pad = 1, short header case */ - tmp = gen_mcmp(base_off + 2, BPF_H, + tmp = gen_mcmp(OR_NET, 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)); + b2 = gen_cmp(OR_NET, 2 + 1 + offset_sh, BPF_H, (bpf_int32)ntohs((u_short)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)); + tmp = gen_mcmp(OR_NET, 2, BPF_B, (bpf_int32)0x02, (bpf_int32)0x7); + b2 = gen_cmp(OR_NET, 2 + offset_sh, BPF_H, (bpf_int32)ntohs((u_short)addr)); gen_and(tmp, b2); gen_or(b2, b1); @@ -2468,62 +3354,104 @@ gen_dnhostop(addr, dir, base_off) return b1; } +/* + * Generate a check for IPv4 or IPv6 for MPLS-encapsulated packets; + * test the bottom-of-stack bit, and then check the version number + * field in the IP header. + */ +static struct block * +gen_mpls_linktype(proto) + int proto; +{ + struct block *b0, *b1; + + switch (proto) { + + case Q_IP: + /* match the bottom-of-stack bit */ + b0 = gen_mcmp(OR_NET, -2, BPF_B, 0x01, 0x01); + /* match the IPv4 version number */ + b1 = gen_mcmp(OR_NET, 0, BPF_B, 0x40, 0xf0); + gen_and(b0, b1); + return b1; + + case Q_IPV6: + /* match the bottom-of-stack bit */ + b0 = gen_mcmp(OR_NET, -2, BPF_B, 0x01, 0x01); + /* match the IPv4 version number */ + b1 = gen_mcmp(OR_NET, 0, BPF_B, 0x60, 0xf0); + gen_and(b0, b1); + return b1; + + default: + abort(); + } +} + static struct block * -gen_host(addr, mask, proto, dir) +gen_host(addr, mask, proto, dir, type) bpf_u_int32 addr; bpf_u_int32 mask; int proto; int dir; + int type; { struct block *b0, *b1; + const char *typestr; + + if (type == Q_NET) + typestr = "net"; + else + typestr = "host"; switch (proto) { case Q_DEFAULT: - b0 = gen_host(addr, mask, Q_IP, dir); - if (off_linktype != -1) { - b1 = gen_host(addr, mask, Q_ARP, dir); - gen_or(b0, b1); - b0 = gen_host(addr, mask, Q_RARP, dir); - gen_or(b1, b0); + b0 = gen_host(addr, mask, Q_IP, dir, type); + /* + * Only check for non-IPv4 addresses if we're not + * checking MPLS-encapsulated packets. + */ + if (label_stack_depth == 0) { + b1 = gen_host(addr, mask, Q_ARP, dir, type); + gen_or(b0, b1); + b0 = gen_host(addr, mask, Q_RARP, dir, type); + gen_or(b1, b0); } return b0; case Q_IP: - return gen_hostop(addr, mask, dir, ETHERTYPE_IP, - off_nl + 12, off_nl + 16); + return gen_hostop(addr, mask, dir, ETHERTYPE_IP, 12, 16); case Q_RARP: - return gen_hostop(addr, mask, dir, ETHERTYPE_REVARP, - off_nl + 14, off_nl + 24); + return gen_hostop(addr, mask, dir, ETHERTYPE_REVARP, 14, 24); case Q_ARP: - return gen_hostop(addr, mask, dir, ETHERTYPE_ARP, - off_nl + 14, off_nl + 24); + return gen_hostop(addr, mask, dir, ETHERTYPE_ARP, 14, 24); case Q_TCP: - bpf_error("'tcp' modifier applied to host"); + bpf_error("'tcp' modifier applied to %s", typestr); case Q_SCTP: - bpf_error("'sctp' modifier applied to host"); + bpf_error("'sctp' modifier applied to %s", typestr); case Q_UDP: - bpf_error("'udp' modifier applied to host"); + bpf_error("'udp' modifier applied to %s", typestr); case Q_ICMP: - bpf_error("'icmp' modifier applied to host"); + bpf_error("'icmp' modifier applied to %s", typestr); case Q_IGMP: - bpf_error("'igmp' modifier applied to host"); + bpf_error("'igmp' modifier applied to %s", typestr); case Q_IGRP: - bpf_error("'igrp' modifier applied to host"); + bpf_error("'igrp' modifier applied to %s", typestr); case Q_PIM: - bpf_error("'pim' modifier applied to host"); + bpf_error("'pim' modifier applied to %s", typestr); case Q_VRRP: - bpf_error("'vrrp' modifier applied to host"); + bpf_error("'vrrp' modifier applied to %s", typestr); case Q_ATALK: bpf_error("ATALK host filtering not implemented"); @@ -2532,7 +3460,7 @@ gen_host(addr, mask, proto, dir) bpf_error("AARP host filtering not implemented"); case Q_DECNET: - return gen_dnhostop(addr, dir, off_nl); + return gen_dnhostop(addr, dir); case Q_SCA: bpf_error("SCA host filtering not implemented"); @@ -2551,35 +3479,38 @@ gen_host(addr, mask, proto, dir) bpf_error("'ip6' modifier applied to ip host"); case Q_ICMPV6: - bpf_error("'icmp6' modifier applied to host"); + bpf_error("'icmp6' modifier applied to %s", typestr); #endif /* INET6 */ case Q_AH: - bpf_error("'ah' modifier applied to host"); + bpf_error("'ah' modifier applied to %s", typestr); case Q_ESP: - bpf_error("'esp' modifier applied to host"); + bpf_error("'esp' modifier applied to %s", typestr); case Q_ISO: bpf_error("ISO host filtering not implemented"); case Q_ESIS: - bpf_error("'esis' modifier applied to host"); + bpf_error("'esis' modifier applied to %s", typestr); case Q_ISIS: - bpf_error("'isis' modifier applied to host"); + bpf_error("'isis' modifier applied to %s", typestr); case Q_CLNP: - bpf_error("'clnp' modifier applied to host"); + bpf_error("'clnp' modifier applied to %s", typestr); case Q_STP: - bpf_error("'stp' modifier applied to host"); + bpf_error("'stp' modifier applied to %s", typestr); case Q_IPX: bpf_error("IPX host filtering not implemented"); case Q_NETBEUI: - bpf_error("'netbeui' modifier applied to host"); + bpf_error("'netbeui' modifier applied to %s", typestr); + + case Q_RADIO: + bpf_error("'radio' modifier applied to %s", typestr); default: abort(); @@ -2589,49 +3520,57 @@ gen_host(addr, mask, proto, dir) #ifdef INET6 static struct block * -gen_host6(addr, mask, proto, dir) +gen_host6(addr, mask, proto, dir, type) struct in6_addr *addr; struct in6_addr *mask; int proto; int dir; + int type; { + const char *typestr; + + if (type == Q_NET) + typestr = "net"; + else + typestr = "host"; + switch (proto) { case Q_DEFAULT: - return gen_host6(addr, mask, Q_IPV6, dir); + return gen_host6(addr, mask, Q_IPV6, dir, type); case Q_IP: - bpf_error("'ip' modifier applied to ip6 host"); + bpf_error("'ip' modifier applied to ip6 %s", typestr); case Q_RARP: - bpf_error("'rarp' modifier applied to ip6 host"); + bpf_error("'rarp' modifier applied to ip6 %s", typestr); case Q_ARP: - bpf_error("'arp' modifier applied to ip6 host"); + bpf_error("'arp' modifier applied to ip6 %s", typestr); case Q_SCTP: - bpf_error("'sctp' modifier applied to host"); + bpf_error("'sctp' modifier applied to %s", typestr); case Q_TCP: - bpf_error("'tcp' modifier applied to host"); + bpf_error("'tcp' modifier applied to %s", typestr); case Q_UDP: - bpf_error("'udp' modifier applied to host"); + bpf_error("'udp' modifier applied to %s", typestr); case Q_ICMP: - bpf_error("'icmp' modifier applied to host"); + bpf_error("'icmp' modifier applied to %s", typestr); case Q_IGMP: - bpf_error("'igmp' modifier applied to host"); + bpf_error("'igmp' modifier applied to %s", typestr); case Q_IGRP: - bpf_error("'igrp' modifier applied to host"); + bpf_error("'igrp' modifier applied to %s", typestr); case Q_PIM: - bpf_error("'pim' modifier applied to host"); + bpf_error("'pim' modifier applied to %s", typestr); case Q_VRRP: - bpf_error("'vrrp' modifier applied to host"); + bpf_error("'vrrp' modifier applied to %s", typestr); case Q_ATALK: bpf_error("ATALK host filtering not implemented"); @@ -2640,7 +3579,7 @@ gen_host6(addr, mask, proto, dir) bpf_error("AARP host filtering not implemented"); case Q_DECNET: - bpf_error("'decnet' modifier applied to ip6 host"); + bpf_error("'decnet' modifier applied to ip6 %s", typestr); case Q_SCA: bpf_error("SCA host filtering not implemented"); @@ -2655,38 +3594,40 @@ gen_host6(addr, mask, proto, dir) bpf_error("MOPRC host filtering not implemented"); case Q_IPV6: - return gen_hostop6(addr, mask, dir, ETHERTYPE_IPV6, - off_nl + 8, off_nl + 24); + return gen_hostop6(addr, mask, dir, ETHERTYPE_IPV6, 8, 24); case Q_ICMPV6: - bpf_error("'icmp6' modifier applied to host"); + bpf_error("'icmp6' modifier applied to %s", typestr); case Q_AH: - bpf_error("'ah' modifier applied to host"); + bpf_error("'ah' modifier applied to %s", typestr); case Q_ESP: - bpf_error("'esp' modifier applied to host"); + bpf_error("'esp' modifier applied to %s", typestr); case Q_ISO: bpf_error("ISO host filtering not implemented"); case Q_ESIS: - bpf_error("'esis' modifier applied to host"); + bpf_error("'esis' modifier applied to %s", typestr); case Q_ISIS: - bpf_error("'isis' modifier applied to host"); + bpf_error("'isis' modifier applied to %s", typestr); case Q_CLNP: - bpf_error("'clnp' modifier applied to host"); + bpf_error("'clnp' modifier applied to %s", typestr); case Q_STP: - bpf_error("'stp' modifier applied to host"); + bpf_error("'stp' modifier applied to %s", typestr); case Q_IPX: bpf_error("IPX host filtering not implemented"); case Q_NETBEUI: - bpf_error("'netbeui' modifier applied to host"); + bpf_error("'netbeui' modifier applied to %s", typestr); + + case Q_RADIO: + bpf_error("'radio' modifier applied to %s", typestr); default: abort(); @@ -2713,21 +3654,32 @@ gen_gateway(eaddr, alist, proto, dir) 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) { + switch (linktype) { + case DLT_EN10MB: + b0 = gen_ehostop(eaddr, Q_OR); + break; + case DLT_FDDI: + b0 = gen_fhostop(eaddr, Q_OR); + break; + case DLT_IEEE802: + b0 = gen_thostop(eaddr, Q_OR); + break; + case DLT_IEEE802_11: + case DLT_IEEE802_11_RADIO_AVS: + case DLT_PPI: + case DLT_IEEE802_11_RADIO: + case DLT_PRISM_HEADER: + b0 = gen_wlanhostop(eaddr, Q_OR); + break; + case DLT_SUNATM: + if (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); + b1 = gen_cmp(OR_LINK, SUNATM_PKT_BEGIN_POS, BPF_H, + 0xFF00); gen_not(b1); /* @@ -2735,15 +3687,19 @@ gen_gateway(eaddr, alist, proto, dir) */ 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( + } + break; + case DLT_IP_OVER_FC: + b0 = gen_ipfchostop(eaddr, Q_OR); + break; + default: + bpf_error( "'gateway' supported only on ethernet/FDDI/token ring/802.11/Fibre Channel"); - - b1 = gen_host(**alist++, 0xffffffff, proto, Q_OR); + } + b1 = gen_host(**alist++, 0xffffffff, proto, Q_OR, Q_HOST); while (*alist) { - tmp = gen_host(**alist++, 0xffffffff, proto, Q_OR); + tmp = gen_host(**alist++, 0xffffffff, proto, Q_OR, + Q_HOST); gen_or(b1, tmp); b1 = tmp; } @@ -2907,177 +3863,368 @@ gen_proto_abbrev(proto) break; case Q_ISO: - b1 = gen_linktype(LLCSAP_ISONS); + b1 = gen_linktype(LLCSAP_ISONS); break; case Q_ESIS: - b1 = gen_proto(ISO9542_ESIS, Q_ISO, Q_DEFAULT); + b1 = gen_proto(ISO9542_ESIS, Q_ISO, Q_DEFAULT); break; case Q_ISIS: - b1 = gen_proto(ISO10589_ISIS, Q_ISO, Q_DEFAULT); + 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 */ + 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); + b0 = gen_proto(ISIS_L1_LSP, Q_ISIS, Q_DEFAULT); gen_or(b0, b1); - b0 = gen_proto(ISIS_L1_CSNP, Q_ISIS, Q_DEFAULT); + b0 = gen_proto(ISIS_L1_CSNP, Q_ISIS, Q_DEFAULT); gen_or(b0, b1); - b0 = gen_proto(ISIS_L1_PSNP, Q_ISIS, Q_DEFAULT); + 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 */ + 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); + b0 = gen_proto(ISIS_L2_LSP, Q_ISIS, Q_DEFAULT); gen_or(b0, b1); - b0 = gen_proto(ISIS_L2_CSNP, Q_ISIS, Q_DEFAULT); + b0 = gen_proto(ISIS_L2_CSNP, Q_ISIS, Q_DEFAULT); gen_or(b0, b1); - b0 = gen_proto(ISIS_L2_PSNP, Q_ISIS, Q_DEFAULT); + 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); + 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); + 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); + 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); + 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); + b0 = gen_proto(ISIS_L1_PSNP, Q_ISIS, Q_DEFAULT); gen_or(b0, b1); - b0 = gen_proto(ISIS_L2_PSNP, Q_ISIS, Q_DEFAULT); + b0 = gen_proto(ISIS_L2_PSNP, Q_ISIS, Q_DEFAULT); gen_or(b0, b1); break; case Q_ISIS_CSNP: - b0 = gen_proto(ISIS_L1_PSNP, Q_ISIS, Q_DEFAULT); - b1 = gen_proto(ISIS_L2_PSNP, Q_ISIS, Q_DEFAULT); + 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); + 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); + b1 = gen_proto(ISO8473_CLNP, Q_ISO, Q_DEFAULT); break; case Q_STP: - b1 = gen_linktype(LLCSAP_8021D); + b1 = gen_linktype(LLCSAP_8021D); break; case Q_IPX: - b1 = gen_linktype(LLCSAP_IPX); + b1 = gen_linktype(LLCSAP_IPX); break; case Q_NETBEUI: - b1 = gen_linktype(LLCSAP_NETBEUI); + b1 = gen_linktype(LLCSAP_NETBEUI); + break; + + case Q_RADIO: + bpf_error("'radio' is not a valid protocol type"); + + default: + abort(); + } + return b1; +} + +static struct block * +gen_ipfrag() +{ + struct slist *s; + struct block *b; + + /* not ip frag */ + s = gen_load_a(OR_NET, 6, BPF_H); + b = new_block(JMP(BPF_JSET)); + b->s.k = 0x1fff; + b->stmts = s; + gen_not(b); + + return b; +} + +/* + * Generate a comparison to a port value in the transport-layer header + * at the specified offset from the beginning of that header. + * + * XXX - this handles a variable-length prefix preceding the link-layer + * header, such as the radiotap or AVS radio prefix, but doesn't handle + * variable-length link-layer headers (such as Token Ring or 802.11 + * headers). + */ +static struct block * +gen_portatom(off, v) + int off; + bpf_int32 v; +{ + return gen_cmp(OR_TRAN_IPV4, off, BPF_H, v); +} + +#ifdef INET6 +static struct block * +gen_portatom6(off, v) + int off; + bpf_int32 v; +{ + return gen_cmp(OR_TRAN_IPV6, 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(OR_NET, 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; + + /* ip6 proto 'proto' */ + b0 = gen_cmp(OR_NET, 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_ipfrag() +gen_port6(port, ip_proto, dir) + int port; + int ip_proto; + int dir; { - struct slist *s; - struct block *b; + struct block *b0, *b1, *tmp; - /* 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); + /* link proto ip6 */ + b0 = gen_linktype(ETHERTYPE_IPV6); - return b; + 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 */ +/* gen_portrange code */ static struct block * -gen_portatom(off, v) +gen_portrangeatom(off, v1, v2) int off; - bpf_int32 v; + bpf_int32 v1, v2; { - struct slist *s; - struct block *b; + struct block *b1, *b2; - s = new_stmt(BPF_LDX|BPF_MSH|BPF_B); - s->s.k = off_nl; + if (v1 > v2) { + /* + * Reverse the order of the ports, so v1 is the lower one. + */ + bpf_int32 vtemp; - s->next = new_stmt(BPF_LD|BPF_IND|BPF_H); - s->next->s.k = off_nl + off; + vtemp = v1; + v1 = v2; + v2 = vtemp; + } - b = new_block(JMP(BPF_JEQ)); - b->stmts = s; - b->s.k = v; + b1 = gen_cmp_ge(OR_TRAN_IPV4, off, BPF_H, v1); + b2 = gen_cmp_le(OR_TRAN_IPV4, off, BPF_H, v2); - return b; -} + gen_and(b1, b2); -#ifdef INET6 -static struct block * -gen_portatom6(off, v) - int off; - bpf_int32 v; -{ - return gen_cmp(off_nl + 40 + off, BPF_H, v); + return b2; } -#endif/*INET6*/ struct block * -gen_portop(port, proto, dir) - int port, proto, dir; +gen_portrangeop(port1, port2, proto, dir) + int port1, port2; + int proto; + int dir; { struct block *b0, *b1, *tmp; /* ip proto 'proto' */ - tmp = gen_cmp(off_nl + 9, BPF_B, (bpf_int32)proto); + tmp = gen_cmp(OR_NET, 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); + b1 = gen_portrangeatom(0, (bpf_int32)port1, (bpf_int32)port2); break; case Q_DST: - b1 = gen_portatom(2, (bpf_int32)port); + b1 = gen_portrangeatom(2, (bpf_int32)port1, (bpf_int32)port2); break; case Q_OR: case Q_DEFAULT: - tmp = gen_portatom(0, (bpf_int32)port); - b1 = gen_portatom(2, (bpf_int32)port); + tmp = gen_portrangeatom(0, (bpf_int32)port1, (bpf_int32)port2); + b1 = gen_portrangeatom(2, (bpf_int32)port1, (bpf_int32)port2); gen_or(tmp, b1); break; case Q_AND: - tmp = gen_portatom(0, (bpf_int32)port); - b1 = gen_portatom(2, (bpf_int32)port); + tmp = gen_portrangeatom(0, (bpf_int32)port1, (bpf_int32)port2); + b1 = gen_portrangeatom(2, (bpf_int32)port1, (bpf_int32)port2); gen_and(tmp, b1); break; @@ -3090,44 +4237,28 @@ gen_portop(port, proto, dir) } static struct block * -gen_port(port, ip_proto, dir) - int port; +gen_portrange(port1, port2, ip_proto, dir) + int port1, port2; 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. - */ + /* link proto 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); + b1 = gen_portrangeop(port1, port2, ip_proto, dir); break; case PROTO_UNDEF: - tmp = gen_portop(port, IPPROTO_TCP, dir); - b1 = gen_portop(port, IPPROTO_UDP, dir); + tmp = gen_portrangeop(port1, port2, IPPROTO_TCP, dir); + b1 = gen_portrangeop(port1, port2, IPPROTO_UDP, dir); gen_or(tmp, b1); - tmp = gen_portop(port, IPPROTO_SCTP, dir); + tmp = gen_portrangeop(port1, port2, IPPROTO_SCTP, dir); gen_or(tmp, b1); break; @@ -3139,34 +4270,62 @@ gen_port(port, ip_proto, dir) } #ifdef INET6 +static struct block * +gen_portrangeatom6(off, v1, v2) + int off; + bpf_int32 v1, v2; +{ + struct block *b1, *b2; + + if (v1 > v2) { + /* + * Reverse the order of the ports, so v1 is the lower one. + */ + bpf_int32 vtemp; + + vtemp = v1; + v1 = v2; + v2 = vtemp; + } + + b1 = gen_cmp_ge(OR_TRAN_IPV6, off, BPF_H, v1); + b2 = gen_cmp_le(OR_TRAN_IPV6, off, BPF_H, v2); + + gen_and(b1, b2); + + return b2; +} + struct block * -gen_portop6(port, proto, dir) - int port, proto, dir; +gen_portrangeop6(port1, port2, proto, dir) + int port1, port2; + int proto; + int dir; { struct block *b0, *b1, *tmp; - /* ip proto 'proto' */ - b0 = gen_cmp(off_nl + 6, BPF_B, (bpf_int32)proto); + /* ip6 proto 'proto' */ + b0 = gen_cmp(OR_NET, 6, BPF_B, (bpf_int32)proto); switch (dir) { case Q_SRC: - b1 = gen_portatom6(0, (bpf_int32)port); + b1 = gen_portrangeatom6(0, (bpf_int32)port1, (bpf_int32)port2); break; case Q_DST: - b1 = gen_portatom6(2, (bpf_int32)port); + b1 = gen_portrangeatom6(2, (bpf_int32)port1, (bpf_int32)port2); break; case Q_OR: case Q_DEFAULT: - tmp = gen_portatom6(0, (bpf_int32)port); - b1 = gen_portatom6(2, (bpf_int32)port); + tmp = gen_portrangeatom6(0, (bpf_int32)port1, (bpf_int32)port2); + b1 = gen_portrangeatom6(2, (bpf_int32)port1, (bpf_int32)port2); gen_or(tmp, b1); break; case Q_AND: - tmp = gen_portatom6(0, (bpf_int32)port); - b1 = gen_portatom6(2, (bpf_int32)port); + tmp = gen_portrangeatom6(0, (bpf_int32)port1, (bpf_int32)port2); + b1 = gen_portrangeatom6(2, (bpf_int32)port1, (bpf_int32)port2); gen_and(tmp, b1); break; @@ -3179,28 +4338,28 @@ gen_portop6(port, proto, dir) } static struct block * -gen_port6(port, ip_proto, dir) - int port; +gen_portrange6(port1, port2, ip_proto, dir) + int port1, port2; int ip_proto; int dir; { struct block *b0, *b1, *tmp; - /* ether proto ip */ + /* link proto ip6 */ b0 = gen_linktype(ETHERTYPE_IPV6); switch (ip_proto) { case IPPROTO_UDP: case IPPROTO_TCP: case IPPROTO_SCTP: - b1 = gen_portop6(port, ip_proto, dir); + b1 = gen_portrangeop6(port1, port2, ip_proto, dir); break; case PROTO_UNDEF: - tmp = gen_portop6(port, IPPROTO_TCP, dir); - b1 = gen_portop6(port, IPPROTO_UDP, dir); + tmp = gen_portrangeop6(port1, port2, IPPROTO_TCP, dir); + b1 = gen_portrangeop6(port1, port2, IPPROTO_UDP, dir); gen_or(tmp, b1); - tmp = gen_portop6(port, IPPROTO_SCTP, dir); + tmp = gen_portrangeop6(port1, port2, IPPROTO_SCTP, dir); gen_or(tmp, b1); break; @@ -3232,8 +4391,11 @@ lookup_proto(name, proto) 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); + if (v == PROTO_UNDEF) { + v = pcap_nametollc(name); + if (v == PROTO_UNDEF) + bpf_error("unknown ether proto '%s'", name); + } break; case Q_ISO: @@ -3297,10 +4459,26 @@ gen_protochain(v, proto, dir) /*NOTREACHED*/ } + /* + * We don't handle variable-length radiotap here headers yet. + * We might want to add BPF instructions to do the protochain + * work, to simplify that and, on platforms that have a BPF + * interpreter with the new instructions, let the filtering + * be done in the kernel. (We already require a modified BPF + * engine to do the protochain stuff, to support backward + * branches, and backward branch support is unlikely to appear + * in kernel BPF engines.) + */ + if (linktype == DLT_IEEE802_11_RADIO) + bpf_error("'protochain' not supported with radiotap headers"); + + if (linktype == DLT_PPI) + bpf_error("'protochain' not supported with PPI headers"); + no_optimize = 1; /*this code is not compatible with optimzer yet */ /* - * s[0] is a dummy entry to protect other BPF insn from damaged + * s[0] is a dummy entry to protect other BPF insn from damage * by s[fix] = foo with uninitialized variable "fix". It is somewhat * hard to find interdependency made by jump table fixup. */ @@ -3314,11 +4492,11 @@ gen_protochain(v, proto, dir) /* A = ip->ip_p */ s[i] = new_stmt(BPF_LD|BPF_ABS|BPF_B); - s[i]->s.k = off_nl + 9; + s[i]->s.k = off_ll + 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; + s[i]->s.k = off_ll + off_nl; i++; break; #ifdef INET6 @@ -3327,7 +4505,7 @@ gen_protochain(v, proto, dir) /* A = ip6->ip_nxt */ s[i] = new_stmt(BPF_LD|BPF_ABS|BPF_B); - s[i]->s.k = off_nl + 6; + s[i]->s.k = off_ll + off_nl + 6; i++; /* X = sizeof(struct ip6_hdr) */ s[i] = new_stmt(BPF_LDX|BPF_IMM); @@ -3407,7 +4585,7 @@ gen_protochain(v, proto, dir) i++; /* A = P[X + packet head] */ s[i] = new_stmt(BPF_LD|BPF_IND|BPF_B); - s[i]->s.k = off_nl; + s[i]->s.k = off_ll + off_nl; i++; /* MEM[reg2] = A */ s[i] = new_stmt(BPF_ST); @@ -3425,7 +4603,7 @@ gen_protochain(v, proto, dir) i++; /* A = P[X + packet head]; */ s[i] = new_stmt(BPF_LD|BPF_IND|BPF_B); - s[i]->s.k = off_nl; + s[i]->s.k = off_ll + off_nl; i++; /* A += 1 */ s[i] = new_stmt(BPF_ALU|BPF_ADD|BPF_K); @@ -3484,7 +4662,7 @@ gen_protochain(v, proto, dir) i++; /* A = P[X + packet head]; */ s[i] = new_stmt(BPF_LD|BPF_IND|BPF_B); - s[i]->s.k = off_nl; + s[i]->s.k = off_ll + off_nl; i++; /* MEM[reg2] = A */ s[i] = new_stmt(BPF_ST); @@ -3502,7 +4680,7 @@ gen_protochain(v, proto, dir) i++; /* A = P[X + packet head] */ s[i] = new_stmt(BPF_LD|BPF_IND|BPF_B); - s[i]->s.k = off_nl; + s[i]->s.k = off_ll + off_nl; i++; /* A += 2 */ s[i] = new_stmt(BPF_ALU|BPF_ADD|BPF_K); @@ -3556,6 +4734,16 @@ gen_protochain(v, proto, dir) #endif } + +/* + * Generate code that checks whether the packet is a packet for protocol + * and whether the type field in that protocol's header has + * the value , e.g. if is Q_IP, it checks whether it's an + * IP packet and checks the protocol number in the IP header against . + * + * If is Q_DEFAULT, i.e. just "proto" was specified, it checks + * against Q_IP and Q_IPV6. + */ static struct block * gen_proto(v, proto, dir) int v; @@ -3595,7 +4783,7 @@ gen_proto(v, proto, dir) */ b0 = gen_linktype(ETHERTYPE_IP); #ifndef CHASE_CHAIN - b1 = gen_cmp(off_nl + 9, BPF_B, (bpf_int32)v); + b1 = gen_cmp(OR_NET, 9, BPF_B, (bpf_int32)v); #else b1 = gen_protochain(v, Q_IP); #endif @@ -3624,7 +4812,8 @@ gen_proto(v, proto, dir) * * XXX - what about SNAP-encapsulated frames? */ - return gen_cmp(2, BPF_H, (0x03<<8) | v); + return gen_cmp(OR_LINK, 2, BPF_H, (0x03<<8) | v); + /*NOTREACHED*/ break; case DLT_C_HDLC: @@ -3634,13 +4823,13 @@ gen_proto(v, proto, dir) */ 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); + b1 = gen_cmp(OR_NET_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); + b1 = gen_cmp(OR_NET_NOSNAP, 0, BPF_B, (long)v); gen_and(b0, b1); return b1; } @@ -3651,7 +4840,7 @@ gen_proto(v, proto, dir) * 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); + b1 = gen_cmp(OR_NET_NOSNAP, 4, BPF_B, (long)v); gen_and(b0, b1); return b1; @@ -3726,7 +4915,7 @@ gen_proto(v, proto, dir) case Q_IPV6: b0 = gen_linktype(ETHERTYPE_IPV6); #ifndef CHASE_CHAIN - b1 = gen_cmp(off_nl + 6, BPF_B, (bpf_int32)v); + b1 = gen_cmp(OR_NET, 6, BPF_B, (bpf_int32)v); #else b1 = gen_protochain(v, Q_IPV6); #endif @@ -3752,6 +4941,9 @@ gen_proto(v, proto, dir) case Q_NETBEUI: bpf_error("'netbeui proto' is bogus"); + case Q_RADIO: + bpf_error("'radio proto' is bogus"); + default: abort(); /* NOTREACHED */ @@ -3773,13 +4965,14 @@ gen_scode(name, q) bpf_u_int32 **alist; #else int tproto6; - struct sockaddr_in *sin; + struct sockaddr_in *sin4; struct sockaddr_in6 *sin6; struct addrinfo *res, *res0; struct in6_addr mask128; #endif /*INET6*/ struct block *b, *tmp; int port, real_proto; + int port1, port2; switch (q.addr) { @@ -3793,7 +4986,7 @@ gen_scode(name, q) addr <<= 8; mask <<= 8; } - return gen_host(addr, mask, proto, dir); + return gen_host(addr, mask, proto, dir, q.addr); case Q_DEFAULT: case Q_HOST: @@ -3828,6 +5021,10 @@ gen_scode(name, q) return b; case DLT_IEEE802_11: + case DLT_IEEE802_11_RADIO_AVS: + case DLT_IEEE802_11_RADIO: + case DLT_PRISM_HEADER: + case DLT_PPI: eaddr = pcap_ether_hostton(name); if (eaddr == NULL) bpf_error( @@ -3854,8 +5051,8 @@ gen_scode(name, q) * with an LE Control marker. (We've * already generated a test for LANE.) */ - tmp = gen_cmp(SUNATM_PKT_BEGIN_POS, BPF_H, - 0xFF00); + tmp = gen_cmp(OR_LINK, SUNATM_PKT_BEGIN_POS, + BPF_H, 0xFF00); gen_not(tmp); eaddr = pcap_ether_hostton(name); @@ -3875,19 +5072,19 @@ gen_scode(name, q) * 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)); + return (gen_host(dn_addr, 0, proto, dir, q.addr)); } else { #ifndef INET6 alist = pcap_nametoaddr(name); if (alist == NULL || *alist == NULL) bpf_error("unknown host '%s'", name); tproto = proto; - if (off_linktype == -1 && tproto == Q_DEFAULT) + if (off_linktype == (u_int)-1 && tproto == Q_DEFAULT) tproto = Q_IP; - b = gen_host(**alist++, 0xffffffff, tproto, dir); + b = gen_host(**alist++, 0xffffffff, tproto, dir, q.addr); while (*alist) { tmp = gen_host(**alist++, 0xffffffff, - tproto, dir); + tproto, dir, q.addr); gen_or(b, tmp); b = tmp; } @@ -3909,10 +5106,10 @@ gen_scode(name, q) if (tproto == Q_IPV6) continue; - sin = (struct sockaddr_in *) + sin4 = (struct sockaddr_in *) res->ai_addr; - tmp = gen_host(ntohl(sin->sin_addr.s_addr), - 0xffffffff, tproto, dir); + tmp = gen_host(ntohl(sin4->sin_addr.s_addr), + 0xffffffff, tproto, dir, q.addr); break; case AF_INET6: if (tproto6 == Q_IP) @@ -3921,7 +5118,7 @@ gen_scode(name, q) sin6 = (struct sockaddr_in6 *) res->ai_addr; tmp = gen_host6(&sin6->sin6_addr, - &mask128, tproto6, dir); + &mask128, tproto6, dir, q.addr); break; default: continue; @@ -3979,12 +5176,50 @@ gen_scode(name, q) #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_PORTRANGE: + if (proto != Q_DEFAULT && + proto != Q_UDP && proto != Q_TCP && proto != Q_SCTP) + bpf_error("illegal qualifier of 'portrange'"); + if (pcap_nametoportrange(name, &port1, &port2, &real_proto) == 0) + bpf_error("unknown port in range '%s'", name); + if (proto == Q_UDP) { + if (real_proto == IPPROTO_TCP) + bpf_error("port in range '%s' is tcp", name); + else if (real_proto == IPPROTO_SCTP) + bpf_error("port in range '%s' is sctp", name); + else + /* override PROTO_UNDEF */ + real_proto = IPPROTO_UDP; + } + if (proto == Q_TCP) { + if (real_proto == IPPROTO_UDP) + bpf_error("port in range '%s' is udp", name); + else if (real_proto == IPPROTO_SCTP) + bpf_error("port in range '%s' is sctp", name); + else + /* override PROTO_UNDEF */ + real_proto = IPPROTO_TCP; + } + if (proto == Q_SCTP) { + if (real_proto == IPPROTO_UDP) + bpf_error("port in range '%s' is udp", name); + else if (real_proto == IPPROTO_TCP) + bpf_error("port in range '%s' is tcp", name); + else + /* override PROTO_UNDEF */ + real_proto = IPPROTO_SCTP; + } +#ifndef INET6 + return gen_portrange(port1, port2, real_proto, dir); +#else + b = gen_portrange(port1, port2, real_proto, dir); + gen_or(gen_portrange6(port1, port2, real_proto, dir), b); + return b; #endif /* INET6 */ case Q_GATEWAY: @@ -4050,7 +5285,14 @@ gen_mcode(s1, s2, masklen, q) /* Convert mask len to mask */ if (masklen > 32) bpf_error("mask length must be <= 32"); - m = 0xffffffff << (32 - masklen); + if (masklen == 0) { + /* + * X << 32 is not guaranteed by C to be 0; it's + * undefined. + */ + m = 0; + } else + m = 0xffffffff << (32 - masklen); if ((n & ~m) != 0) bpf_error("non-network bits set in \"%s/%d\"", s1, masklen); @@ -4059,12 +5301,14 @@ gen_mcode(s1, s2, masklen, q) switch (q.addr) { case Q_NET: - return gen_host(n, m, q.proto, q.dir); + return gen_host(n, m, q.proto, q.dir, q.addr); default: bpf_error("Mask syntax for networks only"); /* NOTREACHED */ } + /* NOTREACHED */ + return NULL; } struct block * @@ -4091,7 +5335,7 @@ gen_ncode(s, v, q) case Q_HOST: case Q_NET: if (proto == Q_DECNET) - return gen_host(v, 0, proto, dir); + return gen_host(v, 0, proto, dir, q.addr); else if (proto == Q_LINK) { bpf_error("illegal link layer address"); } else { @@ -4107,7 +5351,7 @@ gen_ncode(s, v, q) v <<= 32 - vlen; mask <<= 32 - vlen; } - return gen_host(v, mask, proto, dir); + return gen_host(v, mask, proto, dir, q.addr); } case Q_PORT: @@ -4133,6 +5377,29 @@ gen_ncode(s, v, q) } #endif /* INET6 */ + case Q_PORTRANGE: + 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 'portrange'"); + +#ifndef INET6 + return gen_portrange((int)v, (int)v, proto, dir); +#else + { + struct block *b; + b = gen_portrange((int)v, (int)v, proto, dir); + gen_or(gen_portrange6((int)v, (int)v, proto, dir), b); + return b; + } +#endif /* INET6 */ + case Q_GATEWAY: bpf_error("'gateway' requires a name"); /* NOTREACHED */ @@ -4202,7 +5469,7 @@ gen_mcode6(s1, s2, masklen, q) /* FALLTHROUGH */ case Q_NET: - b = gen_host6(addr, &mask, q.proto, q.dir); + b = gen_host6(addr, &mask, q.proto, q.dir, q.addr); freeaddrinfo(res); return b; @@ -4210,6 +5477,7 @@ gen_mcode6(s1, s2, masklen, q) bpf_error("invalid qualifier against IPv6 address"); /* NOTREACHED */ } + return NULL; } #endif /*INET6*/ @@ -4221,36 +5489,48 @@ gen_ecode(eaddr, 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"); + switch (linktype) { + case DLT_EN10MB: + return gen_ehostop(eaddr, (int)q.dir); + case DLT_FDDI: + return gen_fhostop(eaddr, (int)q.dir); + case DLT_IEEE802: + return gen_thostop(eaddr, (int)q.dir); + case DLT_IEEE802_11: + case DLT_IEEE802_11_RADIO_AVS: + case DLT_IEEE802_11_RADIO: + case DLT_PRISM_HEADER: + case DLT_PPI: + return gen_wlanhostop(eaddr, (int)q.dir); + case DLT_SUNATM: + if (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(OR_LINK, 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; + } + break; + case DLT_IP_OVER_FC: + return gen_ipfchostop(eaddr, (int)q.dir); + default: + bpf_error("ethernet addresses supported only on ethernet/FDDI/token ring/802.11/ATM LANE/Fibre Channel"); + break; + } } bpf_error("ethernet address used in non-ether expression"); /* NOTREACHED */ + return NULL; } void @@ -4288,17 +5568,24 @@ xfer_to_a(a) return s; } +/* + * Modify "index" to use the value stored into its register as an + * offset relative to the beginning of the header for the protocol + * "proto", and allocate a register and put an item "size" bytes long + * (1, 2, or 4) at that offset into that register, making it the register + * for "index". + */ struct arth * -gen_load(proto, index, size) +gen_load(proto, inst, size) int proto; - struct arth *index; + struct arth *inst; int size; { struct slist *s, *tmp; struct block *b; int regno = alloc_reg(); - free_reg(index->regno); + free_reg(inst->regno); switch (size) { default: @@ -4320,8 +5607,36 @@ gen_load(proto, index, size) default: bpf_error("unsupported index operation"); + case Q_RADIO: + /* + * The offset is relative to the beginning of the packet + * data, if we have a radio header. (If we don't, this + * is an error.) + */ + if (linktype != DLT_IEEE802_11_RADIO_AVS && + linktype != DLT_IEEE802_11_RADIO && + linktype != DLT_PRISM_HEADER) + bpf_error("radio information not present in capture"); + + /* + * Load into the X register the offset computed into the + * register specifed by "index". + */ + s = xfer_to_x(inst); + + /* + * Load the item at that offset. + */ + tmp = new_stmt(BPF_LD|BPF_IND|size); + sappend(s, tmp); + sappend(inst->s, s); + break; + case Q_LINK: /* + * The offset is relative to the beginning of + * the link-layer header. + * * 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 @@ -4329,10 +5644,35 @@ gen_load(proto, index, size) * frame, so that 0 refers, for Ethernet LANE, to * the beginning of the destination address? */ - s = xfer_to_x(index); + s = gen_llprefixlen(); + + /* + * If "s" is non-null, it has code to arrange that the + * X register contains the length of the prefix preceding + * the link-layer header. Add to it the offset computed + * into the register specified by "index", and move that + * into the X register. Otherwise, just load into the X + * register the offset computed into the register specifed + * by "index". + */ + if (s != NULL) { + sappend(s, xfer_to_a(inst)); + sappend(s, new_stmt(BPF_ALU|BPF_ADD|BPF_X)); + sappend(s, new_stmt(BPF_MISC|BPF_TAX)); + } else + s = xfer_to_x(inst); + + /* + * Load the item at the sum of the offset we've put in the + * X register and the offset of the start of the link + * layer header (which is 0 if the radio header is + * variable-length; that header length is what we put + * into the X register and then added to the index). + */ tmp = new_stmt(BPF_LD|BPF_IND|size); + tmp->s.k = off_ll; sappend(s, tmp); - sappend(index->s, s); + sappend(inst->s, s); break; case Q_IP: @@ -4347,17 +5687,51 @@ gen_load(proto, index, size) #ifdef INET6 case Q_IPV6: #endif - /* XXX Note that we assume a fixed link header here. */ - s = xfer_to_x(index); + /* + * The offset is relative to the beginning of + * the network-layer header. + * XXX - are there any cases where we want + * off_nl_nosnap? + */ + s = gen_llprefixlen(); + + /* + * If "s" is non-null, it has code to arrange that the + * X register contains the length of the prefix preceding + * the link-layer header. Add to it the offset computed + * into the register specified by "index", and move that + * into the X register. Otherwise, just load into the X + * register the offset computed into the register specifed + * by "index". + */ + if (s != NULL) { + sappend(s, xfer_to_a(inst)); + sappend(s, new_stmt(BPF_ALU|BPF_ADD|BPF_X)); + sappend(s, new_stmt(BPF_MISC|BPF_TAX)); + } else + s = xfer_to_x(inst); + + /* + * Load the item at the sum of the offset we've put in the + * X register, the offset of the start of the network + * layer header, and the offset of the start of the link + * layer header (which is 0 if the radio header is + * variable-length; that header length is what we put + * into the X register and then added to the index). + */ tmp = new_stmt(BPF_LD|BPF_IND|size); - tmp->s.k = off_nl; + tmp->s.k = off_ll + off_nl; sappend(s, tmp); - sappend(index->s, s); + sappend(inst->s, s); + /* + * Do the computation only if the packet contains + * the protocol in question. + */ b = gen_proto_abbrev(proto); - if (index->b) - gen_and(index->b, b); - index->b = b; + if (inst->b) + gen_and(inst->b, b); + inst->b = b; break; case Q_SCTP: @@ -4368,22 +5742,56 @@ gen_load(proto, index, size) 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)); + /* + * The offset is relative to the beginning of + * the transport-layer header. + * + * Load the X register with the length of the IPv4 header + * (plus the offset of the link-layer header, if it's + * a variable-length header), in bytes. + * + * XXX - are there any cases where we want + * off_nl_nosnap? + * XXX - we should, if we're built with + * IPv6 support, generate code to load either + * IPv4, IPv6, or both, as appropriate. + */ + s = gen_loadx_iphdrlen(); + + /* + * The X register now contains the sum of the length + * of any variable-length header preceding the link-layer + * header and the length of the network-layer header. + * Load into the A register the offset relative to + * the beginning of the transport layer header, + * add the X register to that, move that to the + * X register, and load with an offset from the + * X register equal to the offset of the network + * layer header relative to the beginning of + * the link-layer header plus the length of any + * fixed-length header preceding the link-layer + * header. + */ + sappend(s, xfer_to_a(inst)); 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); + tmp->s.k = off_ll + off_nl; + sappend(inst->s, s); + /* + * Do the computation only if the packet contains + * the protocol in question - which is true only + * if this is an IP datagram and is the first or + * only fragment of that datagram. + */ gen_and(gen_proto_abbrev(proto), b = gen_ipfrag()); - if (index->b) - gen_and(index->b, b); + if (inst->b) + gen_and(inst->b, b); #ifdef INET6 gen_and(gen_proto_abbrev(Q_IP), b); #endif - index->b = b; + inst->b = b; break; #ifdef INET6 case Q_ICMPV6: @@ -4391,12 +5799,12 @@ gen_load(proto, index, size) /*NOTREACHED*/ #endif } - index->regno = regno; + inst->regno = regno; s = new_stmt(BPF_ST); s->s.k = regno; - sappend(index->s, s); + sappend(inst->s, s); - return index; + return inst; } struct block * @@ -4534,6 +5942,16 @@ gen_arth(code, a0, a1) static int regused[BPF_MEMWORDS]; static int curreg; +/* + * Initialize the table of used registers and the current register. + */ +static void +init_regs() +{ + curreg = 0; + memset(regused, 0, sizeof regused); +} + /* * Return the next free register. */ @@ -4552,6 +5970,7 @@ alloc_reg() } bpf_error("too many registers needed to evaluate expression"); /* NOTREACHED */ + return 0; } /* @@ -4602,6 +6021,16 @@ gen_less(n) return b; } +/* + * This is for "byte {idx} {op} {val}"; "idx" is treated as relative to + * the beginning of the link-layer header. + * XXX - that means you can't test values in the radiotap header, but + * as that header is difficult if not impossible to parse generally + * without a loop, that might not be a severe problem. A new keyword + * "radio" could be added for that, although what you'd really want + * would be a way of testing particular radio header values, which + * would generate code appropriate to the radio header in question. + */ struct block * gen_byteop(op, idx, val) int op, idx, val; @@ -4614,17 +6043,14 @@ gen_byteop(op, idx, val) abort(); case '=': - return gen_cmp((u_int)idx, BPF_B, (bpf_int32)val); + return gen_cmp(OR_LINK, (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); + b = gen_cmp_lt(OR_LINK, (u_int)idx, BPF_B, (bpf_int32)val); return b; case '>': - b = gen_cmp((u_int)idx, BPF_B, (bpf_int32)val); - b->s.code = JMP(BPF_JGT); + b = gen_cmp_gt(OR_LINK, (u_int)idx, BPF_B, (bpf_int32)val); return b; case '|': @@ -4657,25 +6083,33 @@ gen_broadcast(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) { + switch (linktype) { + case DLT_ARCNET: + case DLT_ARCNET_LINUX: + return gen_ahostop(abroadcast, Q_DST); + case DLT_EN10MB: + return gen_ehostop(ebroadcast, Q_DST); + case DLT_FDDI: + return gen_fhostop(ebroadcast, Q_DST); + case DLT_IEEE802: + return gen_thostop(ebroadcast, Q_DST); + case DLT_IEEE802_11: + case DLT_IEEE802_11_RADIO_AVS: + case DLT_IEEE802_11_RADIO: + case DLT_PPI: + case DLT_PRISM_HEADER: + return gen_wlanhostop(ebroadcast, Q_DST); + case DLT_IP_OVER_FC: + return gen_ipfchostop(ebroadcast, Q_DST); + case DLT_SUNATM: + if (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); + b1 = gen_cmp(OR_LINK, SUNATM_PKT_BEGIN_POS, BPF_H, + 0xFF00); gen_not(b1); /* @@ -4684,21 +6118,26 @@ gen_broadcast(proto) b0 = gen_ehostop(ebroadcast, Q_DST); gen_and(b1, b0); return b0; - } - bpf_error("not a broadcast link"); + } + break; + default: + 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, + b1 = gen_mcmp(OR_NET, 16, BPF_W, (bpf_int32)0, hostmask); + b2 = gen_mcmp(OR_NET, 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"); + /* NOTREACHED */ + return NULL; } /* @@ -4713,8 +6152,7 @@ gen_mac_multicast(offset) register struct slist *s; /* link[offset] & 1 != 0 */ - s = new_stmt(BPF_LD|BPF_B|BPF_ABS); - s->s.k = offset; + s = gen_load_a(OR_LINK, offset, BPF_B); b0 = new_block(JMP(BPF_JSET)); b0->s.k = 1; b0->stmts = s; @@ -4732,189 +6170,185 @@ gen_multicast(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) { + switch (linktype) { + case DLT_ARCNET: + case DLT_ARCNET_LINUX: + /* all ARCnet multicasts use the same address */ + return gen_ahostop(abroadcast, Q_DST); + case DLT_EN10MB: + /* ether[0] & 1 != 0 */ + return gen_mac_multicast(0); + case 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); + case DLT_IEEE802: + /* tr[2] & 1 != 0 */ + return gen_mac_multicast(2); + case DLT_IEEE802_11: + case DLT_IEEE802_11_RADIO_AVS: + case DLT_PPI: + case DLT_IEEE802_11_RADIO: + case DLT_PRISM_HEADER: + /* + * 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 = gen_load_a(OR_LINK, 1, BPF_B); + 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 = gen_load_a(OR_LINK, 1, BPF_B); + 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 = gen_load_a(OR_LINK, 0, BPF_B); + 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 = gen_load_a(OR_LINK, 0, BPF_B); + 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 = gen_load_a(OR_LINK, 0, BPF_B); + 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 DLT_IP_OVER_FC: + b0 = gen_mac_multicast(2); + return b0; + case DLT_SUNATM: + if (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); + b1 = gen_cmp(OR_LINK, 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; + } + break; + default: + break; + } + /* 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); + b1 = gen_cmp_ge(OR_NET, 16, BPF_B, (bpf_int32)224); 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); + b1 = gen_cmp(OR_NET, 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"); + /* NOTREACHED */ + return NULL; } /* @@ -4944,7 +6378,7 @@ gen_inbound(dir) /* * Match packets sent by this machine. */ - b0 = gen_cmp(0, BPF_H, LINUX_SLL_OUTGOING); + b0 = gen_cmp(OR_LINK, 0, BPF_H, LINUX_SLL_OUTGOING); } else { /* * Match packets sent to this machine. @@ -4956,14 +6390,53 @@ gen_inbound(dir) * shouldn't be matched, but what about broadcast * or multicast packets we received? */ - b0 = gen_cmp(0, BPF_H, LINUX_SLL_HOST); + b0 = gen_cmp(OR_LINK, 0, BPF_H, LINUX_SLL_HOST); } break; +#ifdef HAVE_NET_PFVAR_H case DLT_PFLOG: - b0 = gen_cmp(26, BPF_H, + b0 = gen_cmp(OR_LINK, offsetof(struct pfloghdr, dir), BPF_B, (bpf_int32)((dir == 0) ? PF_IN : PF_OUT)); break; +#endif + + case DLT_PPP_PPPD: + if (dir) { + /* match outgoing packets */ + b0 = gen_cmp(OR_LINK, 0, BPF_B, PPP_PPPD_OUT); + } else { + /* match incoming packets */ + b0 = gen_cmp(OR_LINK, 0, BPF_B, PPP_PPPD_IN); + } + break; + + case DLT_JUNIPER_MFR: + case DLT_JUNIPER_MLFR: + case DLT_JUNIPER_MLPPP: + case DLT_JUNIPER_ATM1: + case DLT_JUNIPER_ATM2: + case DLT_JUNIPER_PPPOE: + case DLT_JUNIPER_PPPOE_ATM: + case DLT_JUNIPER_GGSN: + case DLT_JUNIPER_ES: + case DLT_JUNIPER_MONITOR: + case DLT_JUNIPER_SERVICES: + case DLT_JUNIPER_ETHER: + case DLT_JUNIPER_PPP: + case DLT_JUNIPER_FRELAY: + case DLT_JUNIPER_CHDLC: + case DLT_JUNIPER_VP: + /* juniper flags (including direction) are stored + * the byte after the 3-byte magic number */ + if (dir) { + /* match outgoing packets */ + b0 = gen_mcmp(OR_LINK, 3, BPF_B, 0, 0x01); + } else { + /* match incoming packets */ + b0 = gen_mcmp(OR_LINK, 3, BPF_B, 1, 0x01); + } + break; default: bpf_error("inbound/outbound not supported on linktype %d", @@ -4974,57 +6447,165 @@ gen_inbound(dir) return (b0); } +#ifdef HAVE_NET_PFVAR_H /* PF firewall log matched interface */ struct block * gen_pf_ifname(const char *ifname) { - if (linktype != DLT_PFLOG) { - bpf_error("ifname supported only for DLT_PFLOG"); + 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) >= 16) { - bpf_error("ifname interface names can't be larger than 16 characters"); + if (strlen(ifname) >= len) { + bpf_error("ifname interface names can only be %d characters", + len-1); /* NOTREACHED */ } - return (gen_bcmp(4, strlen(ifname), (const u_char *)ifname)); + b0 = gen_bcmp(OR_LINK, off, strlen(ifname), (const u_char *)ifname); + return (b0); } +/* PF firewall log ruleset name */ +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(OR_LINK, offsetof(struct pfloghdr, ruleset), + strlen(ruleset), (const u_char *)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(OR_LINK, 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("rnr supported only for DLT_PFLOG"); + bpf_error("srnr not supported on linktype 0x%x", linktype); /* NOTREACHED */ } - return (gen_cmp(20, BPF_H, (bpf_int32)rnr)); + b0 = gen_cmp(OR_LINK, offsetof(struct pfloghdr, subrulenr), BPF_W, + (bpf_int32)srnr); + return (b0); } /* PF firewall log reason code */ struct block * gen_pf_reason(int reason) { - if (linktype != DLT_PFLOG) { - bpf_error("reason supported only for DLT_PFLOG"); + struct block *b0; + + if (linktype == DLT_PFLOG) { + b0 = gen_cmp(OR_LINK, offsetof(struct pfloghdr, reason), BPF_B, + (bpf_int32)reason); + } else { + bpf_error("reason not supported on linktype 0x%x", linktype); /* NOTREACHED */ } - return (gen_cmp(22, BPF_H, (bpf_int32)reason)); + return (b0); } /* PF firewall log action */ struct block * gen_pf_action(int action) { - if (linktype != DLT_PFLOG) { - bpf_error("action supported only for DLT_PFLOG"); + struct block *b0; + + if (linktype == DLT_PFLOG) { + b0 = gen_cmp(OR_LINK, offsetof(struct pfloghdr, action), BPF_B, + (bpf_int32)action); + } else { + bpf_error("action not supported on linktype 0x%x", linktype); /* NOTREACHED */ } - return (gen_cmp(24, BPF_H, (bpf_int32)action)); + return (b0); +} +#else /* !HAVE_NET_PFVAR_H */ +struct block * +gen_pf_ifname(const char *ifname) +{ + bpf_error("libpcap was compiled without pf support"); + /* NOTREACHED */ + return (NULL); +} + +struct block * +gen_pf_ruleset(char *ruleset) +{ + bpf_error("libpcap was compiled on a machine without pf support"); + /* NOTREACHED */ + return (NULL); +} + +struct block * +gen_pf_rnr(int rnr) +{ + bpf_error("libpcap was compiled on a machine without pf support"); + /* NOTREACHED */ + return (NULL); +} + +struct block * +gen_pf_srnr(int srnr) +{ + bpf_error("libpcap was compiled on a machine without pf support"); + /* NOTREACHED */ + return (NULL); +} + +struct block * +gen_pf_reason(int reason) +{ + bpf_error("libpcap was compiled on a machine without pf support"); + /* NOTREACHED */ + return (NULL); +} + +struct block * +gen_pf_action(int action) +{ + bpf_error("libpcap was compiled on a machine without pf support"); + /* NOTREACHED */ + return (NULL); } +#endif /* HAVE_NET_PFVAR_H */ struct block * gen_acode(eaddr, q) @@ -5037,6 +6618,7 @@ gen_acode(eaddr, q) } bpf_error("ARCnet address used in non-arc expression"); /* NOTREACHED */ + return NULL; } static struct block * @@ -5049,10 +6631,10 @@ gen_ahostop(eaddr, dir) switch (dir) { /* src comes first, different from Ethernet */ case Q_SRC: - return gen_bcmp(0, 1, eaddr); + return gen_bcmp(OR_LINK, 0, 1, eaddr); case Q_DST: - return gen_bcmp(1, 1, eaddr); + return gen_bcmp(OR_LINK, 1, 1, eaddr); case Q_AND: b0 = gen_ahostop(eaddr, Q_SRC); @@ -5078,51 +6660,218 @@ struct block * gen_vlan(vlan_num) int vlan_num; { - struct block *b0; + struct block *b0, *b1; + + /* can't check for VLAN-encapsulated packets inside MPLS */ + if (label_stack_depth > 0) + bpf_error("no VLAN match after MPLS"); /* * Change the offsets to point to the type and data fields within - * the VLAN packet. This is somewhat of a kludge. + * the VLAN packet. Just increment the offsets, so that we + * can support a hierarchy, e.g. "vlan 300 && vlan 200" to + * capture VLAN 200 encapsulated within VLAN 100. + * + * XXX - this is a bit of a kludge. If we were to split the + * compiler into a parser that parses an expression and + * generates an expression tree, and a code generator that + * takes an expression tree (which could come from our + * parser or from some other parser) and generates BPF code, + * we could perhaps make the offsets parameters of routines + * and, in the handler for an "AND" node, pass to subnodes + * other than the VLAN node the adjusted offsets. + * + * This would mean that "vlan" would, instead of changing the + * behavior of *all* tests after it, change only the behavior + * of tests ANDed with it. That would change the documented + * semantics of "vlan", which might break some expressions. + * However, it would mean that "(vlan and ip) or ip" would check + * both for VLAN-encapsulated IP and IP-over-Ethernet, rather than + * checking only for VLAN-encapsulated IP, so that could still + * be considered worth doing; it wouldn't break expressions + * that are of the form "vlan and ..." or "vlan N and ...", + * which I suspect are the most common expressions involving + * "vlan". "vlan or ..." doesn't necessarily do what the user + * would really want, now, as all the "or ..." tests would + * be done assuming a VLAN, even though the "or" could be viewed + * as meaning "or, if this isn't a VLAN packet...". */ - if (orig_nl == (u_int)-1) { - orig_linktype = off_linktype; /* save original values */ - orig_nl = off_nl; - orig_nl_nosnap = off_nl_nosnap; + orig_linktype = off_linktype; /* save original values */ + orig_nl = off_nl; - switch (linktype) { + switch (linktype) { - case DLT_EN10MB: - off_linktype = 16; - off_nl_nosnap = 18; - off_nl = 18; - break; + case DLT_EN10MB: + off_linktype += 4; + off_nl_nosnap += 4; + off_nl += 4; + break; - default: - bpf_error("no VLAN support for data link type %d", - linktype); - /*NOTREACHED*/ - } + 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); + b0 = gen_cmp(OR_LINK, 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_mcmp(OR_LINK, orig_nl, BPF_H, (bpf_int32)vlan_num, + 0x0fff); + gen_and(b0, b1); + b0 = b1; + } + + return (b0); +} - b1 = gen_cmp(orig_nl, BPF_H, (bpf_int32)vlan_num); +/* + * support for MPLS + */ +struct block * +gen_mpls(label_num) + int label_num; +{ + struct block *b0,*b1; + + /* + * Change the offsets to point to the type and data fields within + * the MPLS packet. Just increment the offsets, so that we + * can support a hierarchy, e.g. "mpls 100000 && mpls 1024" to + * capture packets with an outer label of 100000 and an inner + * label of 1024. + * + * XXX - this is a bit of a kludge. See comments in gen_vlan(). + */ + orig_nl = off_nl; + + if (label_stack_depth > 0) { + /* just match the bottom-of-stack bit clear */ + b0 = gen_mcmp(OR_LINK, orig_nl-2, BPF_B, 0, 0x01); + } else { + /* + * Indicate that we're checking MPLS-encapsulated headers, + * to make sure higher level code generators don't try to + * match against IP-related protocols such as Q_ARP, Q_RARP + * etc. + */ + switch (linktype) { + + case DLT_C_HDLC: /* fall through */ + case DLT_EN10MB: + b0 = gen_linktype(ETHERTYPE_MPLS); + break; + + case DLT_PPP: + b0 = gen_linktype(PPP_MPLS_UCAST); + break; + + /* FIXME add other DLT_s ... + * for Frame-Relay/and ATM this may get messy due to SNAP headers + * leave it for now */ + + default: + bpf_error("no MPLS support for data link type %d", + linktype); + b0 = NULL; + /*NOTREACHED*/ + break; + } + } + + /* If a specific MPLS label is requested, check it */ + if (label_num >= 0) { + label_num = label_num << 12; /* label is shifted 12 bits on the wire */ + b1 = gen_mcmp(OR_LINK, orig_nl, BPF_W, (bpf_int32)label_num, + 0xfffff000); /* only compare the first 20 bits */ gen_and(b0, b1); b0 = b1; } + off_nl_nosnap += 4; + off_nl += 4; + label_stack_depth++; return (b0); } +/* + * Support PPPOE discovery and session. + */ +struct block * +gen_pppoed() +{ + /* check for PPPoE discovery */ + return gen_linktype((bpf_int32)ETHERTYPE_PPPOED); +} + +struct block * +gen_pppoes() +{ + struct block *b0; + + /* + * Test against the PPPoE session link-layer type. + */ + b0 = gen_linktype((bpf_int32)ETHERTYPE_PPPOES); + + /* + * Change the offsets to point to the type and data fields within + * the PPP packet. + * + * XXX - this is a bit of a kludge. If we were to split the + * compiler into a parser that parses an expression and + * generates an expression tree, and a code generator that + * takes an expression tree (which could come from our + * parser or from some other parser) and generates BPF code, + * we could perhaps make the offsets parameters of routines + * and, in the handler for an "AND" node, pass to subnodes + * other than the PPPoE node the adjusted offsets. + * + * This would mean that "pppoes" would, instead of changing the + * behavior of *all* tests after it, change only the behavior + * of tests ANDed with it. That would change the documented + * semantics of "pppoes", which might break some expressions. + * However, it would mean that "(pppoes and ip) or ip" would check + * both for VLAN-encapsulated IP and IP-over-Ethernet, rather than + * checking only for VLAN-encapsulated IP, so that could still + * be considered worth doing; it wouldn't break expressions + * that are of the form "pppoes and ..." which I suspect are the + * most common expressions involving "pppoes". "pppoes or ..." + * doesn't necessarily do what the user would really want, now, + * as all the "or ..." tests would be done assuming PPPoE, even + * though the "or" could be viewed as meaning "or, if this isn't + * a PPPoE packet...". + */ + orig_linktype = off_linktype; /* save original values */ + orig_nl = off_nl; + + /* + * The "network-layer" protocol is PPPoE, which has a 6-byte + * PPPoE header, followed by PPP payload, so we set the + * offsets to the network layer offset plus 6 bytes for + * the PPPoE header plus the values appropriate for PPP when + * encapsulated in Ethernet (which means there's no HDLC + * encapsulation). + */ + off_linktype = orig_nl + 6; + off_nl = orig_nl + 6 + 2; + off_nl_nosnap = orig_nl + 6 + 2; + + /* + * Set the link-layer type to PPP, as all subsequent tests will + * be on the encapsulated PPP header. + */ + linktype = DLT_PPP; + + return b0; +} + struct block * gen_atmfield_code(atmfield, jvalue, jtype, reverse) int atmfield; - bpf_u_int32 jvalue; + bpf_int32 jvalue; bpf_u_int32 jtype; int reverse; { @@ -5133,42 +6882,42 @@ gen_atmfield_code(atmfield, jvalue, jtype, reverse) case A_VPI: if (!is_atm) bpf_error("'vpi' supported only on raw ATM"); - if (off_vpi == -1) + if (off_vpi == (u_int)-1) abort(); - b0 = gen_ncmp(BPF_B, off_vpi, 0xffffffff, (u_int)jtype, - (u_int)jvalue, reverse); + b0 = gen_ncmp(OR_LINK, off_vpi, BPF_B, 0xffffffff, jtype, + reverse, jvalue); break; case A_VCI: if (!is_atm) bpf_error("'vci' supported only on raw ATM"); - if (off_vci == -1) + if (off_vci == (u_int)-1) abort(); - b0 = gen_ncmp(BPF_H, off_vci, 0xffffffff, (u_int)jtype, - (u_int)jvalue, reverse); + b0 = gen_ncmp(OR_LINK, off_vci, BPF_H, 0xffffffff, jtype, + reverse, jvalue); break; case A_PROTOTYPE: - if (off_proto == -1) + 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); + b0 = gen_ncmp(OR_LINK, off_proto, BPF_B, 0x0f, jtype, + reverse, jvalue); break; case A_MSGTYPE: - if (off_payload == -1) + 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); + b0 = gen_ncmp(OR_LINK, off_payload + MSG_TYPE_POS, BPF_B, + 0xffffffff, jtype, reverse, jvalue); break; case A_CALLREFTYPE: if (!is_atm) bpf_error("'callref' supported only on raw ATM"); - if (off_proto == -1) + if (off_proto == (u_int)-1) abort(); - b0 = gen_ncmp(BPF_B, off_proto, 0xffffffff, (u_int)jtype, - (u_int)jvalue, reverse); + b0 = gen_ncmp(OR_LINK, off_proto, BPF_B, 0xffffffff, + jtype, reverse, jvalue); break; default: @@ -5278,6 +7027,130 @@ gen_atmtype_abbrev(type) return b1; } +/* + * Filtering for MTP2 messages based on li value + * FISU, length is null + * LSSU, length is 1 or 2 + * MSU, length is 3 or more + */ +struct block * +gen_mtp2type_abbrev(type) + int type; +{ + struct block *b0, *b1; + + switch (type) { + + case M_FISU: + if ( (linktype != DLT_MTP2) && + (linktype != DLT_MTP2_WITH_PHDR) ) + bpf_error("'fisu' supported only on MTP2"); + /* gen_ncmp(offrel, offset, size, mask, jtype, reverse, value) */ + b0 = gen_ncmp(OR_PACKET, off_li, BPF_B, 0x3f, BPF_JEQ, 0, 0); + break; + + case M_LSSU: + if ( (linktype != DLT_MTP2) && + (linktype != DLT_MTP2_WITH_PHDR) ) + bpf_error("'lssu' supported only on MTP2"); + b0 = gen_ncmp(OR_PACKET, off_li, BPF_B, 0x3f, BPF_JGT, 1, 2); + b1 = gen_ncmp(OR_PACKET, off_li, BPF_B, 0x3f, BPF_JGT, 0, 0); + gen_and(b1, b0); + break; + + case M_MSU: + if ( (linktype != DLT_MTP2) && + (linktype != DLT_MTP2_WITH_PHDR) ) + bpf_error("'msu' supported only on MTP2"); + b0 = gen_ncmp(OR_PACKET, off_li, BPF_B, 0x3f, BPF_JGT, 0, 2); + break; + + default: + abort(); + } + return b0; +} + +struct block * +gen_mtp3field_code(mtp3field, jvalue, jtype, reverse) + int mtp3field; + bpf_u_int32 jvalue; + bpf_u_int32 jtype; + int reverse; +{ + struct block *b0; + bpf_u_int32 val1 , val2 , val3; + + switch (mtp3field) { + + case M_SIO: + if (off_sio == (u_int)-1) + bpf_error("'sio' supported only on SS7"); + /* sio coded on 1 byte so max value 255 */ + if(jvalue > 255) + bpf_error("sio value %u too big; max value = 255", + jvalue); + b0 = gen_ncmp(OR_PACKET, off_sio, BPF_B, 0xffffffff, + (u_int)jtype, reverse, (u_int)jvalue); + break; + + case M_OPC: + if (off_opc == (u_int)-1) + bpf_error("'opc' supported only on SS7"); + /* opc coded on 14 bits so max value 16383 */ + if (jvalue > 16383) + bpf_error("opc value %u too big; max value = 16383", + jvalue); + /* the following instructions are made to convert jvalue + * to the form used to write opc in an ss7 message*/ + val1 = jvalue & 0x00003c00; + val1 = val1 >>10; + val2 = jvalue & 0x000003fc; + val2 = val2 <<6; + val3 = jvalue & 0x00000003; + val3 = val3 <<22; + jvalue = val1 + val2 + val3; + b0 = gen_ncmp(OR_PACKET, off_opc, BPF_W, 0x00c0ff0f, + (u_int)jtype, reverse, (u_int)jvalue); + break; + + case M_DPC: + if (off_dpc == (u_int)-1) + bpf_error("'dpc' supported only on SS7"); + /* dpc coded on 14 bits so max value 16383 */ + if (jvalue > 16383) + bpf_error("dpc value %u too big; max value = 16383", + jvalue); + /* the following instructions are made to convert jvalue + * to the forme used to write dpc in an ss7 message*/ + val1 = jvalue & 0x000000ff; + val1 = val1 << 24; + val2 = jvalue & 0x00003f00; + val2 = val2 << 8; + jvalue = val1 + val2; + b0 = gen_ncmp(OR_PACKET, off_dpc, BPF_W, 0xff3f0000, + (u_int)jtype, reverse, (u_int)jvalue); + break; + + case M_SLS: + if (off_sls == (u_int)-1) + bpf_error("'sls' supported only on SS7"); + /* sls coded on 4 bits so max value 15 */ + if (jvalue > 15) + bpf_error("sls value %u too big; max value = 15", + jvalue); + /* the following instruction is made to convert jvalue + * to the forme used to write sls in an ss7 message*/ + jvalue = jvalue << 4; + b0 = gen_ncmp(OR_PACKET, off_sls, BPF_B, 0xf0, + (u_int)jtype,reverse, (u_int)jvalue); + break; + + default: + abort(); + } + return b0; +} static struct block * gen_msg_abbrev(type) @@ -5296,15 +7169,15 @@ gen_msg_abbrev(type) break; case A_CALLPROCEED: - b1 = gen_atmfield_code(A_MSGTYPE, CALL_PROCEED, BPF_JEQ, 0); + 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; + break; case A_CONNECTACK: - b1 = gen_atmfield_code(A_MSGTYPE, CONNECT_ACK, BPF_JEQ, 0); + b1 = gen_atmfield_code(A_MSGTYPE, CONNECT_ACK, BPF_JEQ, 0); break; case A_RELEASE: @@ -5312,7 +7185,7 @@ gen_msg_abbrev(type) break; case A_RELEASE_DONE: - b1 = gen_atmfield_code(A_MSGTYPE, RELEASE_DONE, BPF_JEQ, 0); + b1 = gen_atmfield_code(A_MSGTYPE, RELEASE_DONE, BPF_JEQ, 0); break; default: @@ -5339,9 +7212,9 @@ gen_atmmulti_abbrev(type) if (!is_atm) bpf_error("'oamf4' supported only on raw ATM"); /* OAM F4 type */ - b0 = gen_atmfield_code(A_VCI, 3, BPF_JEQ, 0); + b0 = gen_atmfield_code(A_VCI, 3, BPF_JEQ, 0); b1 = gen_atmfield_code(A_VCI, 4, BPF_JEQ, 0); - gen_or(b0, b1); + gen_or(b0, b1); b0 = gen_atmfield_code(A_VPI, 0, BPF_JEQ, 0); gen_and(b0, b1); break;