diff --git a/gencode.c.orig b/gencode.c.orig deleted file mode 100644 index 5067abc6d..000000000 --- a/gencode.c.orig +++ /dev/null @@ -1,7192 +0,0 @@ -/*#define CHASE_CHAIN*/ -/* - * Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that: (1) source code distributions - * retain the above copyright notice and this paragraph in its entirety, (2) - * distributions including binary code include the above copyright notice and - * this paragraph in its entirety in the documentation or other materials - * provided with the distribution, and (3) all advertising materials mentioning - * features or use of this software display the following acknowledgement: - * ``This product includes software developed by the University of California, - * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of - * the University nor the names of its contributors may be used to endorse - * or promote products derived from this software without specific prior - * written permission. - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. - */ -#ifndef lint -static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/gencode.c,v 1.221.2.52 2007/06/22 06:43:58 guy Exp $ (LBL)"; -#endif - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef WIN32 -#include -#else /* WIN32 */ -#include -#include -#endif /* WIN32 */ - -/* - * XXX - why was this included even on UNIX? - */ -#ifdef __MINGW32__ -#include "IP6_misc.h" -#endif - -#ifndef WIN32 - -#ifdef __NetBSD__ -#include -#endif - -#include - -#endif /* WIN32 */ - -#include -#include -#include -#include -#include - -#ifdef MSDOS -#include "pcap-dos.h" -#endif - -#include "pcap-int.h" - -#include "ethertype.h" -#include "nlpid.h" -#include "llc.h" -#include "gencode.h" -#include "atmuni31.h" -#include "sunatmpos.h" -#include "ppp.h" -#include "sll.h" -#include "arcnet.h" -#include "pf.h" -#ifndef offsetof -#define offsetof(s, e) ((size_t)&((s *)0)->e) -#endif -#ifdef INET6 -#ifndef WIN32 -#include /* for "struct addrinfo" */ -#endif /* WIN32 */ -#endif /*INET6*/ -#include - -#define ETHERMTU 1500 - -#ifndef IPPROTO_SCTP -#define IPPROTO_SCTP 132 -#endif - -#ifdef HAVE_OS_PROTO_H -#include "os-proto.h" -#endif - -#define JMP(c) ((c)|BPF_JMP|BPF_K) - -/* Locals */ -static jmp_buf top_ctx; -static pcap_t *bpf_pcap; - -#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 -static int pcap_fddipad; -#endif - -/* VARARGS */ -void -bpf_error(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - if (bpf_pcap != NULL) - (void)vsnprintf(pcap_geterr(bpf_pcap), PCAP_ERRBUF_SIZE, - fmt, ap); - va_end(ap); - longjmp(top_ctx, 1); - /* NOTREACHED */ -} - -static void init_linktype(pcap_t *); - -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 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 -struct chunk { - u_int n_left; - void *m; -}; - -static struct chunk chunks[NCHUNKS]; -static int cur_chunk; - -static void *newchunk(u_int); -static void freechunks(void); -static inline struct block *new_block(int); -static inline struct slist *new_stmt(int); -static struct block *gen_retblk(int); -static inline void syntax(void); - -static void backpatch(struct block *, struct block *); -static void merge(struct block *, struct block *); -static struct block *gen_cmp(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_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); -#endif -static struct block *gen_ahostop(const u_char *, int); -static struct block *gen_ehostop(const u_char *, int); -static struct block *gen_fhostop(const u_char *, int); -static struct block *gen_thostop(const u_char *, int); -static struct block *gen_wlanhostop(const u_char *, int); -static struct block *gen_ipfchostop(const u_char *, int); -static struct block *gen_dnhostop(bpf_u_int32, int); -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, 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); -static struct block *gen_proto(int, int, int); -static struct slist *xfer_to_x(struct arth *); -static struct slist *xfer_to_a(struct arth *); -static struct block *gen_mac_multicast(int); -static struct block *gen_len(int, int); - -static struct block *gen_ppi_dlt_check(void); -static struct block *gen_msg_abbrev(int type); - -static void * -newchunk(n) - u_int n; -{ - struct chunk *cp; - int k; - size_t size; - -#ifndef __NetBSD__ - /* XXX Round up to nearest long. */ - n = (n + sizeof(long) - 1) & ~(sizeof(long) - 1); -#else - /* XXX Round up to structure boundary. */ - n = ALIGN(n); -#endif - - cp = &chunks[cur_chunk]; - if (n > cp->n_left) { - ++cp, k = ++cur_chunk; - if (k >= NCHUNKS) - bpf_error("out of memory"); - size = CHUNK0SIZE << k; - cp->m = (void *)malloc(size); - if (cp->m == NULL) - bpf_error("out of memory"); - memset((char *)cp->m, 0, size); - cp->n_left = size; - if (n > size) - bpf_error("out of memory"); - } - cp->n_left -= n; - return (void *)((char *)cp->m + cp->n_left); -} - -static void -freechunks() -{ - int i; - - cur_chunk = 0; - for (i = 0; i < NCHUNKS; ++i) - if (chunks[i].m != NULL) { - free(chunks[i].m); - chunks[i].m = NULL; - } -} - -/* - * A strdup whose allocations are freed after code generation is over. - */ -char * -sdup(s) - register const char *s; -{ - int n = strlen(s) + 1; - char *cp = newchunk(n); - - strlcpy(cp, s, n); - return (cp); -} - -static inline struct block * -new_block(code) - int code; -{ - struct block *p; - - p = (struct block *)newchunk(sizeof(*p)); - p->s.code = code; - p->head = p; - - return p; -} - -static inline struct slist * -new_stmt(code) - int code; -{ - struct slist *p; - - p = (struct slist *)newchunk(sizeof(*p)); - p->s.code = code; - - return p; -} - -static struct block * -gen_retblk(v) - int v; -{ - struct block *b = new_block(BPF_RET|BPF_K); - - b->s.k = v; - return b; -} - -static inline void -syntax() -{ - bpf_error("syntax error in filter expression"); -} - -static bpf_u_int32 netmask; -static int snaplen; -int no_optimize; - -int -pcap_compile(pcap_t *p, struct bpf_program *program, - 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; - if (setjmp(top_ctx)) { - lex_cleanup(); - freechunks(); - return (-1); - } - - netmask = mask; - - snaplen = pcap_snapshot(p); - if (snaplen == 0) { - snprintf(p->errbuf, PCAP_ERRBUF_SIZE, - "snaplen of 0 rejects all packets"); - return -1; - } - - lex_init(xbuf ? xbuf : ""); - init_linktype(p); - (void)pcap_parse(); - - if (n_errors) - syntax(); - - if (root == NULL) - root = gen_retblk(snaplen); - - if (optimize && !no_optimize) { - bpf_optimize(&root); - if (root == NULL || - (root->s.code == (BPF_RET|BPF_K) && root->s.k == 0)) - bpf_error("expression rejects all packets"); - } - program->bf_insns = icode_to_fcode(root, &len); - program->bf_len = len; - - lex_cleanup(); - freechunks(); - return (0); -} - -/* - * entry point for using the compiler with no pcap open - * pass in all the stuff that is needed explicitly instead. - */ -int -pcap_compile_nopcap(int snaplen_arg, int linktype_arg, - struct bpf_program *program, - const char *buf, int optimize, bpf_u_int32 mask) -{ - pcap_t *p; - int ret; - - p = pcap_open_dead(linktype_arg, snaplen_arg); - if (p == NULL) - return (-1); - ret = pcap_compile(p, program, buf, optimize, mask); - pcap_close(p); - return (ret); -} - -/* - * Clean up a "struct bpf_program" by freeing all the memory allocated - * in it. - */ -void -pcap_freecode(struct bpf_program *program) -{ - program->bf_len = 0; - if (program->bf_insns != NULL) { - free((char *)program->bf_insns); - program->bf_insns = NULL; - } -} - -/* - * Backpatch the blocks in 'list' to 'target'. The 'sense' field indicates - * which of the jt and jf fields has been resolved and which is a pointer - * back to another unresolved block (or nil). At least one of the fields - * in each block is already resolved. - */ -static void -backpatch(list, target) - struct block *list, *target; -{ - struct block *next; - - while (list) { - if (!list->sense) { - next = JT(list); - JT(list) = target; - } else { - next = JF(list); - JF(list) = target; - } - list = next; - } -} - -/* - * Merge the lists in b0 and b1, using the 'sense' field to indicate - * which of jt and jf is the link. - */ -static void -merge(b0, b1) - struct block *b0, *b1; -{ - register struct block **p = &b0; - - /* Find end of list. */ - while (*p) - p = !((*p)->sense) ? &JT(*p) : &JF(*p); - - /* Concatenate the lists. */ - *p = b1; -} - - -void -finish_parse(p) - struct block *p; -{ - 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 -gen_and(b0, b1) - struct block *b0, *b1; -{ - backpatch(b0, b1->head); - b0->sense = !b0->sense; - b1->sense = !b1->sense; - merge(b1, b0); - b1->sense = !b1->sense; - b1->head = b0->head; -} - -void -gen_or(b0, b1) - struct block *b0, *b1; -{ - b0->sense = !b0->sense; - backpatch(b0, b1->head); - b0->sense = !b0->sense; - merge(b1, b0); - b1->head = b0->head; -} - -void -gen_not(b) - struct block *b; -{ - b->sense = !b->sense; -} - -static struct block * -gen_cmp(offrel, offset, size, v) - enum e_offrel offrel; - u_int offset, size; - bpf_int32 v; -{ - return gen_ncmp(offrel, offset, size, 0xffffffff, BPF_JEQ, 0, v); -} - -static struct block * -gen_cmp_gt(offrel, offset, size, v) - enum e_offrel offrel; - u_int offset, size; - bpf_int32 v; -{ - return gen_ncmp(offrel, offset, size, 0xffffffff, BPF_JGT, 0, v); -} - -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); -} - -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); -} - -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(offrel, offset, size, v, mask) - enum e_offrel offrel; - u_int offset, size; - bpf_int32 v; - bpf_u_int32 mask; -{ - return gen_ncmp(offrel, offset, size, mask, BPF_JEQ, 0, v); -} - -static struct block * -gen_bcmp(offrel, offset, size, v) - enum e_offrel offrel; - register u_int offset, size; - register const u_char *v; -{ - register struct block *b, *tmp; - - b = NULL; - while (size >= 4) { - register const u_char *p = &v[size - 4]; - bpf_int32 w = ((bpf_int32)p[0] << 24) | - ((bpf_int32)p[1] << 16) | ((bpf_int32)p[2] << 8) | p[3]; - - tmp = gen_cmp(offrel, offset + size - 4, BPF_W, w); - if (b != NULL) - gen_and(b, tmp); - b = tmp; - size -= 4; - } - while (size >= 2) { - register const u_char *p = &v[size - 2]; - bpf_int32 w = ((bpf_int32)p[0] << 8) | p[1]; - - tmp = gen_cmp(offrel, offset + size - 2, BPF_H, w); - if (b != NULL) - gen_and(b, tmp); - b = tmp; - size -= 2; - } - if (size > 0) { - tmp = gen_cmp(offrel, offset, BPF_B, (bpf_int32)v[0]); - if (b != NULL) - gen_and(b, tmp); - b = tmp; - } - 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(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, *s2; - struct block *b; - - s = gen_load_a(offrel, offset, size); - - if (mask != 0xffffffff) { - 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 = v; - if (reverse && (jtype == BPF_JGT || jtype == BPF_JGE)) - gen_not(b); - return b; -} - -/* - * Various code constructs need to know the layout of the data link - * layer. These variables give the necessary offsets 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, 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. 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. - * - * For link-layer types that always use 802.2 headers, it's the - * offset of the LLC header. - * - * For PPP, it's the offset of the PPP type field. - * - * For Cisco HDLC, it's the offset of the CHDLC type field. - * - * For BSD loopback, it's the offset of the AF_ value. - * - * For Linux cooked sockets, it's the offset of the type field. - * - * It's set to -1 for no encapsulation, in which case, IP is assumed. - */ -static u_int off_linktype; - -/* - * TRUE if the link layer includes an ATM pseudo-header. - */ -static int is_atm = 0; - -/* - * TRUE if "lane" appeared in the filter; it causes us to generate - * code that assumes LANE rather than LLC-encapsulated traffic in SunATM. - */ -static int is_lane = 0; - -/* - * These are offsets for the ATM pseudo-header. - */ -static u_int off_vpi; -static u_int off_vci; -static u_int off_proto; - -/* - * 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. - */ -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: - * - * "off_nl" and "off_nl_nosnap" are the same. - * - * If the link layer always uses 802.2 LLC: - * - * "off_nl" is the offset if there's a SNAP header following - * the 802.2 header; - * - * "off_nl_nosnap" is the offset if there's no SNAP header. - * - * If the link layer is Ethernet: - * - * "off_nl" is the offset if the packet is an Ethernet II packet - * (we assume no 802.3+802.2+SNAP); - * - * "off_nl_nosnap" is the offset if the packet is an 802.3 packet - * with an 802.2 header following it. - */ -static u_int off_nl; -static u_int off_nl_nosnap; - -static int linktype; - -static void -init_linktype(p) - pcap_t *p; -{ - linktype = pcap_datalink(p); -#ifdef PCAP_FDDIPAD - pcap_fddipad = p->fddipad; -#endif - - /* - * Assume it's not raw ATM with a pseudo-header, for now. - */ - off_mac = 0; - is_atm = 0; - is_lane = 0; - off_vpi = -1; - off_vci = -1; - off_proto = -1; - off_payload = -1; - - /* - * 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; - label_stack_depth = 0; - - reg_ll_size = -1; - - switch (linktype) { - - case DLT_ARCNET: - off_linktype = 2; - off_nl = 6; /* XXX in reality, variable! */ - off_nl_nosnap = 6; /* no 802.2 LLC */ - return; - - case DLT_ARCNET_LINUX: - off_linktype = 4; - off_nl = 8; /* XXX in reality, variable! */ - off_nl_nosnap = 8; /* no 802.2 LLC */ - return; - - case DLT_EN10MB: - off_linktype = 12; - off_nl = 14; /* Ethernet II */ - off_nl_nosnap = 17; /* 802.3+802.2 */ - return; - - case DLT_SLIP: - /* - * SLIP doesn't have a link level type. The 16 byte - * header is hacked into our SLIP driver. - */ - off_linktype = -1; - off_nl = 16; - off_nl_nosnap = 16; /* no 802.2 LLC */ - return; - - case DLT_SLIP_BSDOS: - /* XXX this may be the same as the DLT_PPP_BSDOS case */ - off_linktype = -1; - /* XXX end */ - off_nl = 24; - off_nl_nosnap = 24; /* no 802.2 LLC */ - return; - - case DLT_NULL: - case DLT_LOOP: - off_linktype = 0; - off_nl = 4; - off_nl_nosnap = 4; /* no 802.2 LLC */ - return; - - case DLT_ENC: - off_linktype = 0; - off_nl = 12; - off_nl_nosnap = 12; /* no 802.2 LLC */ - return; - - case DLT_PPP: - case DLT_PPP_PPPD: - case DLT_C_HDLC: /* BSD/OS Cisco HDLC */ - case DLT_PPP_SERIAL: /* NetBSD sync/async serial PPP */ - off_linktype = 2; - off_nl = 4; - off_nl_nosnap = 4; /* no 802.2 LLC */ - return; - - case DLT_PPP_ETHER: - /* - * This does no include the Ethernet header, and - * only covers session state. - */ - off_linktype = 6; - off_nl = 8; - off_nl_nosnap = 8; /* no 802.2 LLC */ - return; - - case DLT_PPP_BSDOS: - off_linktype = 5; - off_nl = 24; - off_nl_nosnap = 24; /* no 802.2 LLC */ - return; - - case DLT_FDDI: - /* - * FDDI doesn't really have a link-level type field. - * We set "off_linktype" to the offset of the LLC header. - * - * To check for Ethernet types, we assume that SSAP = SNAP - * is being used and pick out the encapsulated Ethernet type. - * XXX - should we generate code to check for SNAP? - */ - off_linktype = 13; -#ifdef PCAP_FDDIPAD - off_linktype += pcap_fddipad; -#endif - off_nl = 21; /* FDDI+802.2+SNAP */ - off_nl_nosnap = 16; /* FDDI+802.2 */ -#ifdef PCAP_FDDIPAD - off_nl += pcap_fddipad; - off_nl_nosnap += pcap_fddipad; -#endif - return; - - case DLT_IEEE802: - /* - * Token Ring doesn't really have a link-level type field. - * We set "off_linktype" to the offset of the LLC header. - * - * To check for Ethernet types, we assume that SSAP = SNAP - * is being used and pick out the encapsulated Ethernet type. - * XXX - should we generate code to check for SNAP? - * - * XXX - the header is actually variable-length. - * Some various Linux patched versions gave 38 - * as "off_linktype" and 40 as "off_nl"; however, - * if a token ring packet has *no* routing - * information, i.e. is not source-routed, the correct - * values are 20 and 22, as they are in the vanilla code. - * - * A packet is source-routed iff the uppermost bit - * of the first byte of the source address, at an - * offset of 8, has the uppermost bit set. If the - * packet is source-routed, the total number of bytes - * of routing information is 2 plus bits 0x1F00 of - * the 16-bit value at an offset of 14 (shifted right - * 8 - figure out which byte that is). - */ - off_linktype = 14; - off_nl = 22; /* Token Ring+802.2+SNAP */ - off_nl_nosnap = 17; /* Token Ring+802.2 */ - return; - - case DLT_IEEE802_11: - /* - * 802.11 doesn't really have a link-level type field. - * We set "off_linktype" to the offset of the LLC header. - * - * To check for Ethernet types, we assume that SSAP = SNAP - * is being used and pick out the encapsulated Ethernet type. - * XXX - should we generate code to check for SNAP? - * - * XXX - the header is actually variable-length. We - * assume a 24-byte link-layer header, as appears in - * data frames in networks with no bridges. If the - * fromds and tods 802.11 header bits are both set, - * it's actually supposed to be 30 bytes. - */ - off_linktype = 24; - off_nl = 32; /* 802.11+802.2+SNAP */ - off_nl_nosnap = 27; /* 802.11+802.2 */ - return; - - case DLT_PRISM_HEADER: - /* - * Same as 802.11, but with an additional header before - * the 802.11 header, containing a bunch of additional - * information including radio-level information. - * - * The header is 144 bytes long. - * - * XXX - same variable-length header problem; at least - * the Prism header is fixed-length. - */ - off_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_AVS: - /* - * Same as 802.11, but with an additional header before - * the 802.11 header, containing a bunch of additional - * information including radio-level information. - * - * The header is 64 bytes long, at least in its - * current incarnation. - * - * XXX - same variable-length header problem, only - * more so; this header is also variable-length, - * with the length being the 32-bit big-endian - * number at an offset of 4 from the beginning - * of the radio header. 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 = 24; - off_nl = 32; /* 802.11+802.2+SNAP */ - off_nl_nosnap = 27; /* 802.11+802.2 */ - return; - - case DLT_ATM_RFC1483: - case DLT_ATM_CLIP: /* Linux ATM defines this */ - /* - * assume routed, non-ISO PDUs - * (i.e., LLC = 0xAA-AA-03, OUT = 0x00-00-00) - * - * 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 */ - off_nl_nosnap = 3; /* 802.2 */ - return; - - case DLT_SUNATM: - /* - * Full Frontal ATM; you get AALn PDUs with an ATM - * pseudo-header. - */ - is_atm = 1; - off_vpi = SUNATM_VPI_POS; - off_vci = SUNATM_VCI_POS; - off_proto = PROTO_POS; - off_mac = -1; /* LLC-encapsulated, so no MAC-layer header */ - off_payload = SUNATM_PKT_BEGIN_POS; - off_linktype = off_payload; - off_nl = off_payload+8; /* 802.2+SNAP */ - off_nl_nosnap = off_payload+3; /* 802.2 */ - return; - - case DLT_RAW: - off_linktype = -1; - off_nl = 0; - off_nl_nosnap = 0; /* no 802.2 LLC */ - return; - - case DLT_LINUX_SLL: /* fake header for Linux cooked socket */ - off_linktype = 14; - off_nl = 16; - off_nl_nosnap = 16; /* no 802.2 LLC */ - return; - - case DLT_LTALK: - /* - * LocalTalk does have a 1-byte type field in the LLAP header, - * but really it just indicates whether there is a "short" or - * "long" DDP packet following. - */ - off_linktype = -1; - off_nl = 0; - off_nl_nosnap = 0; /* no 802.2 LLC */ - return; - - case DLT_IP_OVER_FC: - /* - * RFC 2625 IP-over-Fibre-Channel doesn't really have a - * link-level type field. We set "off_linktype" to the - * offset of the LLC header. - * - * To check for Ethernet types, we assume that SSAP = SNAP - * is being used and pick out the encapsulated Ethernet type. - * XXX - should we generate code to check for SNAP? RFC - * 2625 says SNAP should be used. - */ - off_linktype = 16; - off_nl = 24; /* IPFC+802.2+SNAP */ - off_nl_nosnap = 19; /* IPFC+802.2 */ - return; - - case DLT_FRELAY: - /* - * XXX - we should set this to handle SNAP-encapsulated - * frames (NLPID of 0x80). - */ - off_linktype = -1; - off_nl = 0; - off_nl_nosnap = 0; /* no 802.2 LLC */ - return; - - /* - * 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; - - case DLT_PFLOG: - off_linktype = 0; - /* XXX read this from pf.h? */ - off_nl = PFLOG_HDRLEN; - off_nl_nosnap = PFLOG_HDRLEN; /* no 802.2 LLC */ - return; - - 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 */ -} - -/* - * 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 slist *s, *s2; - - 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. - * - * 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; -} - - -/* - * Load a value relative to the beginning of the specified header. - */ -static struct slist * -gen_load_a(offrel, offset, size) - enum e_offrel offrel; - u_int offset, size; -{ - struct slist *s, *s2; - - switch (offrel) { - - 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: - /* - * 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. - */ - s = gen_loadx_iphdrlen(); - - /* - * 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: - /* - * 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_gt(OR_LINK, off_linktype, BPF_H, ETHERMTU); - gen_not(b0); - b1 = gen_cmp(OR_LINK, off_linktype + 2, BPF_H, (bpf_int32) - ((proto << 8) | proto)); - gen_and(b0, b1); - return b1; - - case LLCSAP_IPX: - /* - * Check for; - * - * Ethernet_II frames, which are Ethernet - * frames with a frame type of ETHERTYPE_IPX; - * - * Ethernet_802.3 frames, which are 802.3 - * frames (i.e., the type/length field is - * a length field, <= ETHERMTU, rather than - * a type field) with the first two bytes - * after the Ethernet/802.3 header being - * 0xFFFF; - * - * Ethernet_802.2 frames, which are 802.3 - * frames with an 802.2 LLC header and - * with the IPX LSAP as the DSAP in the LLC - * header; - * - * Ethernet_SNAP frames, which are 802.3 - * frames with an LLC header and a SNAP - * header and with an OUI of 0x000000 - * (encapsulated Ethernet) and a protocol - * ID of ETHERTYPE_IPX in the SNAP header. - * - * XXX - should we generate the same code both - * for tests for LLCSAP_IPX and for ETHERTYPE_IPX? - */ - - /* - * This generates code to check both for the - * IPX LSAP (Ethernet_802.2) and for Ethernet_802.3. - */ - b0 = gen_cmp(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); - - /* - * Now we add code to check for SNAP frames with - * ETHERTYPE_IPX, i.e. Ethernet_SNAP. - */ - b0 = gen_snap(0x000000, ETHERTYPE_IPX, 14); - gen_or(b0, b1); - - /* - * Now we generate code to check for 802.3 - * frames in general. - */ - b0 = gen_cmp_gt(OR_LINK, off_linktype, BPF_H, ETHERMTU); - gen_not(b0); - - /* - * Now add the check for 802.3 frames before the - * check for Ethernet_802.2 and Ethernet_802.3, - * as those checks should only be done on 802.3 - * frames, not on Ethernet frames. - */ - gen_and(b0, b1); - - /* - * Now add the check for Ethernet_II frames, and - * do that before checking for the other frame - * types. - */ - b0 = gen_cmp(OR_LINK, off_linktype, BPF_H, - (bpf_int32)ETHERTYPE_IPX); - gen_or(b0, b1); - return b1; - - case ETHERTYPE_ATALK: - case ETHERTYPE_AARP: - /* - * EtherTalk (AppleTalk protocols on Ethernet link - * layer) may use 802.2 encapsulation. - */ - - /* - * Check for 802.2 encapsulation (EtherTalk phase 2?); - * we check for an Ethernet type field less than - * 1500, which means it's an 802.3 length field. - */ - b0 = gen_cmp_gt(OR_LINK, off_linktype, BPF_H, ETHERMTU); - gen_not(b0); - - /* - * 802.2-encapsulated ETHERTYPE_ATALK packets are - * SNAP packets with an organization code of - * 0x080007 (Apple, for Appletalk) and a protocol - * type of ETHERTYPE_ATALK (Appletalk). - * - * 802.2-encapsulated ETHERTYPE_AARP packets are - * SNAP packets with an organization code of - * 0x000000 (encapsulated Ethernet) and a protocol - * type of ETHERTYPE_AARP (Appletalk ARP). - */ - if (proto == ETHERTYPE_ATALK) - b1 = gen_snap(0x080007, ETHERTYPE_ATALK, 14); - else /* proto == ETHERTYPE_AARP */ - b1 = gen_snap(0x000000, ETHERTYPE_AARP, 14); - gen_and(b0, b1); - - /* - * Check for Ethernet encapsulation (Ethertalk - * phase 1?); we just check for the Ethernet - * protocol type. - */ - b0 = gen_cmp(OR_LINK, off_linktype, BPF_H, (bpf_int32)proto); - - gen_or(b0, b1); - return b1; - - default: - if (proto <= ETHERMTU) { - /* - * This is an LLC SAP value, so the frames - * that match would be 802.2 frames. - * Check that the frame is an 802.2 frame - * (i.e., that the length/type field is - * a length field, <= ETHERMTU) and - * then check the DSAP. - */ - b0 = gen_cmp_gt(OR_LINK, off_linktype, BPF_H, ETHERMTU); - gen_not(b0); - b1 = gen_cmp(OR_LINK, 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(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_linux_sll_linktype(proto) - register int proto; -{ - struct block *b0, *b1; - - switch (proto) { - - 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 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); - - /* - * 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); - - /* - * 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 ETHERTYPE_ATALK: - case ETHERTYPE_AARP: - /* - * EtherTalk (AppleTalk protocols on Ethernet link - * layer) may use 802.2 encapsulation. - */ - - /* - * Check for 802.2 encapsulation (EtherTalk phase 2?); - * we check for the 802.2 protocol type in the - * "Ethernet type" field. - */ - b0 = gen_cmp(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). - * - * 802.2-encapsulated ETHERTYPE_AARP packets are - * SNAP packets with an organization code of - * 0x000000 (encapsulated Ethernet) and a protocol - * type of ETHERTYPE_AARP (Appletalk ARP). - */ - if (proto == ETHERTYPE_ATALK) - b1 = gen_snap(0x080007, ETHERTYPE_ATALK, - off_linktype + 2); - else /* proto == ETHERTYPE_AARP */ - b1 = gen_snap(0x000000, ETHERTYPE_AARP, - off_linktype + 2); - gen_and(b0, b1); - - /* - * Check for Ethernet encapsulation (Ethertalk - * phase 1?); we just check for the Ethernet - * protocol type. - */ - b0 = gen_cmp(OR_LINK, off_linktype, BPF_H, (bpf_int32)proto); - - gen_or(b0, b1); - return b1; - - default: - if (proto <= ETHERMTU) { - /* - * This is an LLC SAP value, so the frames - * that match would be 802.2 frames. - * Check for the 802.2 protocol type - * in the "Ethernet type" field, and - * then check the DSAP. - */ - b0 = gen_cmp(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 { - /* - * 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(OR_LINK, off_linktype, BPF_H, - (bpf_int32)proto); - } - } -} - -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 ETHERTYPE_IPV6: - case PPP_IPV6: - /* FIXME add other L3 proto IDs */ - return gen_mpls_linktype(Q_IPV6); - - default: - bpf_error("unsupported protocol over mpls"); - /* NOTREACHED */ - } - } - - switch (linktype) { - - case DLT_EN10MB: - return gen_ether_linktype(proto); - /*NOTREACHED*/ - break; - - case DLT_C_HDLC: - switch (proto) { - - case LLCSAP_ISONS: - proto = (proto << 8 | LLCSAP_ISONS); - /* fall through */ - - 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 that the packet doesn't begin with an - * LE Control marker. (We've already generated - * a test for LANE.) - */ - b0 = gen_cmp(OR_LINK, SUNATM_PKT_BEGIN_POS, BPF_H, - 0xFF00); - gen_not(b0); - - /* - * Now generate an Ethernet test. - */ - b1 = gen_ether_linktype(proto); - gen_and(b0, b1); - return b1; - } else { - /* - * Check for LLC encapsulation and then check the - * protocol. - */ - b0 = gen_atmfield_code(A_PROTOTYPE, PT_LLC, BPF_JEQ, 0); - b1 = gen_llc_linktype(proto); - gen_and(b0, b1); - return b1; - } - /*NOTREACHED*/ - break; - - case DLT_LINUX_SLL: - return gen_linux_sll_linktype(proto); - /*NOTREACHED*/ - break; - - case DLT_SLIP: - case DLT_SLIP_BSDOS: - case DLT_RAW: - /* - * These types don't provide any type field; packets - * 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? - */ - 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 - - default: - return gen_false(); /* always false */ - } - /*NOTREACHED*/ - break; - - case DLT_PPP: - case DLT_PPP_PPPD: - case DLT_PPP_SERIAL: - case DLT_PPP_ETHER: - /* - * We use Ethernet protocol types inside libpcap; - * map them to the corresponding PPP protocol types. - */ - switch (proto) { - - case ETHERTYPE_IP: - proto = PPP_IP; - break; - -#ifdef INET6 - case ETHERTYPE_IPV6: - proto = PPP_IPV6; - break; -#endif - - case ETHERTYPE_DN: - proto = PPP_DECNET; - break; - - case ETHERTYPE_ATALK: - proto = PPP_APPLE; - break; - - case ETHERTYPE_NS: - proto = PPP_NS; - break; - - case LLCSAP_ISONS: - proto = PPP_OSI; - break; - - case LLCSAP_8021D: - /* - * I'm assuming the "Bridging PDU"s that go - * over PPP are Spanning Tree Protocol - * Bridging PDUs. - */ - proto = PPP_BRPDU; - break; - - case LLCSAP_IPX: - proto = PPP_IPX; - break; - } - break; - - case DLT_PPP_BSDOS: - /* - * We use Ethernet protocol types inside libpcap; - * map them to the corresponding PPP protocol types. - */ - switch (proto) { - - case ETHERTYPE_IP: - b0 = gen_cmp(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(OR_LINK, off_linktype, BPF_H, PPP_VJNC); - gen_or(b1, b0); - return b0; - -#ifdef INET6 - case ETHERTYPE_IPV6: - proto = PPP_IPV6; - /* more to go? */ - break; -#endif - - case ETHERTYPE_DN: - proto = PPP_DECNET; - break; - - case ETHERTYPE_ATALK: - proto = PPP_APPLE; - break; - - case ETHERTYPE_NS: - proto = PPP_NS; - break; - - case LLCSAP_ISONS: - proto = PPP_OSI; - break; - - case LLCSAP_8021D: - /* - * I'm assuming the "Bridging PDU"s that go - * over PPP are Spanning Tree Protocol - * Bridging PDUs. - */ - proto = PPP_BRPDU; - break; - - case LLCSAP_IPX: - proto = PPP_IPX; - break; - } - break; - - case DLT_NULL: - case DLT_LOOP: - case DLT_ENC: - /* - * For DLT_NULL, the link-layer header is a 32-bit - * word containing an AF_ value in *host* byte order, - * and for DLT_ENC, the link-layer header begins - * with a 32-bit work containing an AF_ value in - * host byte order. - * - * In addition, if we're reading a saved capture file, - * the host byte order in the capture may not be the - * same as the host byte order on this machine. - * - * For DLT_LOOP, the link-layer header is a 32-bit - * word containing an AF_ value in *network* byte order. - * - * XXX - AF_ values may, unfortunately, be platform- - * dependent; for example, FreeBSD's AF_INET6 is 24 - * whilst NetBSD's and OpenBSD's is 26. - * - * This means that, when reading a capture file, just - * checking for our AF_INET6 value won't work if the - * capture file came from another OS. - */ - switch (proto) { - - case ETHERTYPE_IP: - proto = AF_INET; - break; - -#ifdef INET6 - case ETHERTYPE_IPV6: - proto = AF_INET6; - break; -#endif - - default: - /* - * Not a type on which we support filtering. - * XXX - support those that have AF_ values - * #defined on this platform, at least? - */ - return gen_false(); - } - - if (linktype == DLT_NULL || linktype == DLT_ENC) { - /* - * The AF_ value is in host byte order, but - * the BPF interpreter will convert it to - * network byte order. - * - * If this is a save file, and it's from a - * machine with the opposite byte order to - * ours, we byte-swap the AF_ value. - * - * Then we run it through "htonl()", and - * generate code to compare against the result. - */ - if (bpf_pcap->sf.rfile != NULL && - bpf_pcap->sf.swapped) - proto = SWAPLONG(proto); - proto = htonl(proto); - } - return (gen_cmp(OR_LINK, 0, BPF_W, (bpf_int32)proto)); - - case DLT_PFLOG: - /* - * af field is host byte order in contrast to the rest of - * the packet. - */ - if (proto == ETHERTYPE_IP) - return (gen_cmp(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; - - case DLT_ARCNET: - case DLT_ARCNET_LINUX: - /* - * XXX should we check for first fragment if the protocol - * uses PHDS? - */ - switch (proto) { - - default: - return gen_false(); - -#ifdef INET6 - case ETHERTYPE_IPV6: - return (gen_cmp(OR_LINK, off_linktype, BPF_B, - (bpf_int32)ARCTYPE_INET6)); -#endif /* INET6 */ - - case ETHERTYPE_IP: - b0 = gen_cmp(OR_LINK, off_linktype, BPF_B, - (bpf_int32)ARCTYPE_IP); - 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(OR_LINK, off_linktype, BPF_B, - (bpf_int32)ARCTYPE_ARP); - 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(OR_LINK, off_linktype, BPF_B, - (bpf_int32)ARCTYPE_REVARP)); - - case ETHERTYPE_ATALK: - return (gen_cmp(OR_LINK, off_linktype, BPF_B, - (bpf_int32)ARCTYPE_ATALK)); - } - /*NOTREACHED*/ - break; - - case DLT_LTALK: - switch (proto) { - case ETHERTYPE_ATALK: - return gen_true(); - default: - return gen_false(); - } - /*NOTREACHED*/ - break; - - case DLT_FRELAY: - /* - * XXX - assumes a 2-byte Frame Relay header with - * DLCI and flags. What if the address is longer? - */ - switch (proto) { - - case ETHERTYPE_IP: - /* - * Check for the special NLPID for IP. - */ - return gen_cmp(OR_LINK, 2, BPF_H, (0x03<<8) | 0xcc); - -#ifdef INET6 - case ETHERTYPE_IPV6: - /* - * Check for the special NLPID for IPv6. - */ - return gen_cmp(OR_LINK, 2, BPF_H, (0x03<<8) | 0x8e); -#endif - - case LLCSAP_ISONS: - /* - * Check for several OSI protocols. - * - * Frame Relay packets typically have an OSI - * NLPID at the beginning; we check for each - * of them. - * - * What we check for is the NLPID and a frame - * control field of UI, i.e. 0x03 followed - * by the NLPID. - */ - b0 = gen_cmp(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; - - 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"); - } - - /* - * All the types that have no encapsulation should either be - * handled as DLT_SLIP, DLT_SLIP_BSDOS, and DLT_RAW are, if - * all packets are IP packets, or should be handled in some - * special case, if none of them are (if some are and some - * aren't, the lack of encapsulation is a problem, as we'd - * have to find some other way of determining the packet type). - * - * Therefore, if "off_linktype" is -1, there's an error. - */ - if (off_linktype == (u_int)-1) - abort(); - - /* - * Any type not handled above should always have an Ethernet - * type at an offset of "off_linktype". (PPP is partially - * handled above - the protocol type is mapped from the - * Ethernet and LLC types we use internally to the corresponding - * PPP type - but the PPP type is always specified by a value - * at "off_linktype", so we don't have to do the code generation - * above.) - */ - return gen_cmp(OR_LINK, off_linktype, BPF_H, (bpf_int32)proto); -} - -/* - * Check for an LLC SNAP packet with a given organization code and - * protocol type; we check the entire contents of the 802.2 LLC and - * snap headers, checking for DSAP and SSAP of SNAP and a control - * field of 0x03 in the LLC header, and for the specified organization - * code and protocol type in the SNAP header. - */ -static struct block * -gen_snap(orgcode, ptype, offset) - bpf_u_int32 orgcode; - bpf_u_int32 ptype; - u_int offset; -{ - u_char snapblock[8]; - - snapblock[0] = LLCSAP_SNAP; /* DSAP = SNAP */ - snapblock[1] = LLCSAP_SNAP; /* SSAP = SNAP */ - snapblock[2] = 0x03; /* control = UI */ - snapblock[3] = (orgcode >> 16); /* upper 8 bits of organization code */ - snapblock[4] = (orgcode >> 8); /* middle 8 bits of organization code */ - snapblock[5] = (orgcode >> 0); /* lower 8 bits of organization code */ - snapblock[6] = (ptype >> 8); /* upper 8 bits of protocol type */ - snapblock[7] = (ptype >> 0); /* lower 8 bits of protocol type */ - return gen_bcmp(OR_LINK, offset, 8, snapblock); -} - -/* - * 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_linktype(proto) - int proto; -{ - /* - * XXX - handle token-ring variable-length header. - */ - switch (proto) { - - case LLCSAP_IP: - case LLCSAP_ISONS: - case 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(OR_LINK, off_linktype, BPF_B, - (bpf_int32)LLCSAP_IPX); - - case ETHERTYPE_ATALK: - /* - * 802.2-encapsulated ETHERTYPE_ATALK packets are - * SNAP packets with an organization code of - * 0x080007 (Apple, for Appletalk) and a protocol - * type of ETHERTYPE_ATALK (Appletalk). - * - * XXX - check for an organization code of - * encapsulated Ethernet as well? - */ - return gen_snap(0x080007, ETHERTYPE_ATALK, off_linktype); - - default: - /* - * XXX - we don't have to check for IPX 802.3 - * here, but should we check for the IPX Ethertype? - */ - if (proto <= ETHERMTU) { - /* - * This is an LLC SAP value, so check - * the DSAP. - */ - return gen_cmp(OR_LINK, off_linktype, BPF_B, - (bpf_int32)proto); - } else { - /* - * This is an Ethernet type; we assume that it's - * unlikely that it'll appear in the right place - * at random, and therefore check only the - * location that would hold the Ethernet type - * in a SNAP frame with an organization code of - * 0x000000 (encapsulated Ethernet). - * - * XXX - if we were to check for the SNAP DSAP and - * LSAP, as per XXX, and were also to check for an - * organization code of 0x000000 (encapsulated - * Ethernet), we'd do - * - * return gen_snap(0x000000, proto, - * off_linktype); - * - * here; for now, we don't, as per the above. - * I don't know whether it's worth the extra CPU - * time to do the right check or not. - */ - return gen_cmp(OR_LINK, off_linktype+6, BPF_H, - (bpf_int32)proto); - } - } -} - -static struct block * -gen_hostop(addr, mask, dir, proto, src_off, dst_off) - bpf_u_int32 addr; - bpf_u_int32 mask; - int dir, proto; - u_int src_off, dst_off; -{ - struct block *b0, *b1; - u_int offset; - - switch (dir) { - - case Q_SRC: - offset = src_off; - break; - - case Q_DST: - offset = dst_off; - break; - - case Q_AND: - b0 = gen_hostop(addr, mask, Q_SRC, proto, src_off, dst_off); - b1 = gen_hostop(addr, mask, Q_DST, proto, src_off, dst_off); - gen_and(b0, b1); - return b1; - - case Q_OR: - case Q_DEFAULT: - b0 = gen_hostop(addr, mask, Q_SRC, proto, src_off, dst_off); - b1 = gen_hostop(addr, mask, Q_DST, proto, src_off, dst_off); - gen_or(b0, b1); - return b1; - - default: - abort(); - } - b0 = gen_linktype(proto); - b1 = gen_mcmp(OR_NET, offset, BPF_W, (bpf_int32)addr, mask); - gen_and(b0, b1); - return b1; -} - -#ifdef INET6 -static struct block * -gen_hostop6(addr, mask, dir, proto, src_off, dst_off) - struct in6_addr *addr; - struct in6_addr *mask; - int dir, proto; - u_int src_off, dst_off; -{ - struct block *b0, *b1; - u_int offset; - u_int32_t *a, *m; - - switch (dir) { - - case Q_SRC: - offset = src_off; - break; - - case Q_DST: - offset = dst_off; - break; - - case Q_AND: - b0 = gen_hostop6(addr, mask, Q_SRC, proto, src_off, dst_off); - b1 = gen_hostop6(addr, mask, Q_DST, proto, src_off, dst_off); - gen_and(b0, b1); - return b1; - - case Q_OR: - case Q_DEFAULT: - b0 = gen_hostop6(addr, mask, Q_SRC, proto, src_off, dst_off); - b1 = gen_hostop6(addr, mask, Q_DST, proto, src_off, dst_off); - gen_or(b0, b1); - return b1; - - default: - abort(); - } - /* this order is important */ - a = (u_int32_t *)addr; - m = (u_int32_t *)mask; - b1 = gen_mcmp(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(OR_NET, offset + 4, BPF_W, ntohl(a[1]), ntohl(m[1])); - gen_and(b0, b1); - 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); - return b1; -} -#endif /*INET6*/ - -static struct block * -gen_ehostop(eaddr, dir) - register const u_char *eaddr; - register int dir; -{ - register struct block *b0, *b1; - - switch (dir) { - case Q_SRC: - return gen_bcmp(OR_LINK, off_mac + 6, 6, eaddr); - - case Q_DST: - return gen_bcmp(OR_LINK, off_mac + 0, 6, eaddr); - - case Q_AND: - b0 = gen_ehostop(eaddr, Q_SRC); - b1 = gen_ehostop(eaddr, Q_DST); - gen_and(b0, b1); - return b1; - - case Q_DEFAULT: - case Q_OR: - b0 = gen_ehostop(eaddr, Q_SRC); - b1 = gen_ehostop(eaddr, Q_DST); - gen_or(b0, b1); - return b1; - } - abort(); - /* NOTREACHED */ -} - -/* - * Like gen_ehostop, but for DLT_FDDI - */ -static struct block * -gen_fhostop(eaddr, dir) - register const u_char *eaddr; - register int dir; -{ - struct block *b0, *b1; - - switch (dir) { - case Q_SRC: -#ifdef PCAP_FDDIPAD - return gen_bcmp(OR_LINK, 6 + 1 + pcap_fddipad, 6, eaddr); -#else - return gen_bcmp(OR_LINK, 6 + 1, 6, eaddr); -#endif - - case Q_DST: -#ifdef PCAP_FDDIPAD - return gen_bcmp(OR_LINK, 0 + 1 + pcap_fddipad, 6, eaddr); -#else - return gen_bcmp(OR_LINK, 0 + 1, 6, eaddr); -#endif - - case Q_AND: - b0 = gen_fhostop(eaddr, Q_SRC); - b1 = gen_fhostop(eaddr, Q_DST); - gen_and(b0, b1); - return b1; - - case Q_DEFAULT: - case Q_OR: - b0 = gen_fhostop(eaddr, Q_SRC); - b1 = gen_fhostop(eaddr, Q_DST); - gen_or(b0, b1); - return b1; - } - abort(); - /* NOTREACHED */ -} - -/* - * Like gen_ehostop, but for DLT_IEEE802 (Token Ring) - */ -static struct block * -gen_thostop(eaddr, dir) - register const u_char *eaddr; - register int dir; -{ - register struct block *b0, *b1; - - switch (dir) { - case Q_SRC: - return gen_bcmp(OR_LINK, 8, 6, eaddr); - - case Q_DST: - return gen_bcmp(OR_LINK, 2, 6, eaddr); - - case Q_AND: - b0 = gen_thostop(eaddr, Q_SRC); - b1 = gen_thostop(eaddr, Q_DST); - gen_and(b0, b1); - return b1; - - case Q_DEFAULT: - case Q_OR: - b0 = gen_thostop(eaddr, Q_SRC); - b1 = gen_thostop(eaddr, Q_DST); - gen_or(b0, b1); - return b1; - } - abort(); - /* NOTREACHED */ -} - -/* - * Like gen_ehostop, but for DLT_IEEE802_11 (802.11 wireless LAN) - */ -static struct block * -gen_wlanhostop(eaddr, dir) - register const u_char *eaddr; - register int dir; -{ - register struct block *b0, *b1, *b2; - register struct slist *s; - - switch (dir) { - case Q_SRC: - /* - * Oh, yuk. - * - * For control frames, there is no SA. - * - * For management frames, SA is at an - * offset of 10 from the beginning of - * the packet. - * - * For data frames, SA is at an offset - * of 10 from the beginning of the packet - * if From DS is clear, at an offset of - * 16 from the beginning of the packet - * if From DS is set and To DS is clear, - * and an offset of 24 from the beginning - * of the packet if From DS is set and To DS - * is set. - */ - - /* - * Generate the tests to be done for data frames - * with From DS set. - * - * First, check for To DS set, i.e. check "link[1] & 0x01". - */ - s = 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 SA is at 24. - */ - 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 = 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 SA is at 16. - */ - b1 = gen_bcmp(OR_LINK, 16, 6, eaddr); - gen_and(b2, b1); - - /* - * Now OR together the last two checks. That gives - * the complete set of checks for data frames with - * From DS set. - */ - gen_or(b1, b0); - - /* - * Now check for From DS being set, and AND that with - * the ORed-together checks. - */ - s = gen_load_a(OR_LINK, 1, BPF_B); - b1 = new_block(JMP(BPF_JSET)); - b1->s.k = 0x02; /* From DS */ - b1->stmts = s; - gen_and(b1, b0); - - /* - * Now check for data frames with From DS not set. - */ - s = gen_load_a(OR_LINK, 1, BPF_B); - b2 = new_block(JMP(BPF_JSET)); - b2->s.k = 0x02; /* From DS */ - b2->stmts = s; - gen_not(b2); - - /* - * If From DS isn't set, the SA is at 10. - */ - b1 = gen_bcmp(OR_LINK, 10, 6, eaddr); - gen_and(b2, b1); - - /* - * Now OR together the checks for data frames with - * From DS not set and for data frames with From DS - * set; that gives the checks done for data frames. - */ - gen_or(b1, b0); - - /* - * Now check for a data frame. - * I.e, check "link[0] & 0x08". - */ - 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 SA is at 10. - */ - b1 = gen_bcmp(OR_LINK, 10, 6, eaddr); - gen_and(b2, b1); - - /* - * OR that with the checks done for data frames. - * That gives the checks done for management and - * data frames. - */ - gen_or(b1, b0); - - /* - * If the low-order bit of the type value is 1, - * this is either a control frame or a frame - * with a reserved type, and thus not a - * frame with an SA. - * - * I.e., check "!(link[0] & 0x04)". - */ - s = 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 Q_DST: - /* - * Oh, yuk. - * - * For control frames, there is no DA. - * - * For management frames, DA is at an - * offset of 4 from the beginning of - * the packet. - * - * For data frames, DA is at an offset - * of 4 from the beginning of the packet - * if To DS is clear and at an offset of - * 16 from the beginning of the packet - * if To DS is set. - */ - - /* - * Generate the tests to be done for data frames. - * - * First, check for To DS set, i.e. "link[1] & 0x01". - */ - s = 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_bcmp(OR_LINK, 16, 6, eaddr); - 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_bcmp(OR_LINK, 4, 6, eaddr); - gen_and(b2, b1); - - /* - * Now OR together the last two checks. That gives - * the complete set of checks for data frames. - */ - gen_or(b1, b0); - - /* - * Now check for a data frame. - * I.e, check "link[0] & 0x08". - */ - s = 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_bcmp(OR_LINK, 4, 6, eaddr); - gen_and(b2, b1); - - /* - * OR that with the checks done for data frames. - * That gives the checks done for management and - * data frames. - */ - gen_or(b1, b0); - - /* - * If the low-order bit of the type value is 1, - * this is either a control frame or a frame - * with a reserved type, and thus not a - * frame with an SA. - * - * I.e., check "!(link[0] & 0x04)". - */ - s = 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 Q_AND: - b0 = gen_wlanhostop(eaddr, Q_SRC); - b1 = gen_wlanhostop(eaddr, Q_DST); - gen_and(b0, b1); - return b1; - - case Q_DEFAULT: - case Q_OR: - b0 = gen_wlanhostop(eaddr, Q_SRC); - b1 = gen_wlanhostop(eaddr, Q_DST); - gen_or(b0, b1); - return b1; - } - abort(); - /* NOTREACHED */ -} - -/* - * Like gen_ehostop, but for RFC 2625 IP-over-Fibre-Channel. - * (We assume that the addresses are IEEE 48-bit MAC addresses, - * as the RFC states.) - */ -static struct block * -gen_ipfchostop(eaddr, dir) - register const u_char *eaddr; - register int dir; -{ - register struct block *b0, *b1; - - switch (dir) { - case Q_SRC: - return gen_bcmp(OR_LINK, 10, 6, eaddr); - - case Q_DST: - return gen_bcmp(OR_LINK, 2, 6, eaddr); - - case Q_AND: - b0 = gen_ipfchostop(eaddr, Q_SRC); - b1 = gen_ipfchostop(eaddr, Q_DST); - gen_and(b0, b1); - return b1; - - case Q_DEFAULT: - case Q_OR: - b0 = gen_ipfchostop(eaddr, Q_SRC); - b1 = gen_ipfchostop(eaddr, Q_DST); - gen_or(b0, b1); - return b1; - } - abort(); - /* NOTREACHED */ -} - -/* - * This is quite tricky because there may be pad bytes in front of the - * DECNET header, and then there are two possible data packet formats that - * carry both src and dst addresses, plus 5 packet types in a format that - * carries only the src node, plus 2 types that use a different format and - * also carry just the src node. - * - * Yuck. - * - * Instead of doing those all right, we just look for data packets with - * 0 or 1 bytes of padding. If you want to look at other packets, that - * will require a lot more hacking. - * - * To add support for filtering on DECNET "areas" (network numbers) - * one would want to add a "mask" argument to this routine. That would - * make the filter even more inefficient, although one could be clever - * and not generate masking instructions if the mask is 0xFFFF. - */ -static struct block * -gen_dnhostop(addr, dir) - bpf_u_int32 addr; - int dir; -{ - struct block *b0, *b1, *b2, *tmp; - u_int offset_lh; /* offset if long header is received */ - u_int offset_sh; /* offset if short header is received */ - - switch (dir) { - - case Q_DST: - offset_sh = 1; /* follows flags */ - offset_lh = 7; /* flgs,darea,dsubarea,HIORD */ - break; - - case Q_SRC: - offset_sh = 3; /* follows flags, dstnode */ - offset_lh = 15; /* flgs,darea,dsubarea,did,sarea,ssub,HIORD */ - break; - - case Q_AND: - /* Inefficient because we do our Calvinball dance twice */ - b0 = gen_dnhostop(addr, Q_SRC); - 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); - b1 = gen_dnhostop(addr, Q_DST); - gen_or(b0, b1); - return b1; - - case Q_ISO: - bpf_error("ISO host filtering not implemented"); - - default: - abort(); - } - b0 = gen_linktype(ETHERTYPE_DN); - /* Check for pad = 1, long header case */ - tmp = gen_mcmp(OR_NET, 2, BPF_H, - (bpf_int32)ntohs(0x0681), (bpf_int32)ntohs(0x07FF)); - 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(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(OR_NET, 2, BPF_H, - (bpf_int32)ntohs(0x0281), (bpf_int32)ntohs(0x07FF)); - 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(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); - - /* Combine with test for linktype */ - gen_and(b0, b1); - 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, 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, 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, 12, 16); - - case Q_RARP: - return gen_hostop(addr, mask, dir, ETHERTYPE_REVARP, 14, 24); - - case Q_ARP: - return gen_hostop(addr, mask, dir, ETHERTYPE_ARP, 14, 24); - - case Q_TCP: - bpf_error("'tcp' modifier applied to %s", typestr); - - case Q_SCTP: - bpf_error("'sctp' modifier applied to %s", typestr); - - case Q_UDP: - bpf_error("'udp' modifier applied to %s", typestr); - - case Q_ICMP: - bpf_error("'icmp' modifier applied to %s", typestr); - - case Q_IGMP: - bpf_error("'igmp' modifier applied to %s", typestr); - - case Q_IGRP: - bpf_error("'igrp' modifier applied to %s", typestr); - - case Q_PIM: - bpf_error("'pim' modifier applied to %s", typestr); - - case Q_VRRP: - bpf_error("'vrrp' modifier applied to %s", typestr); - - case Q_ATALK: - bpf_error("ATALK host filtering not implemented"); - - case Q_AARP: - bpf_error("AARP host filtering not implemented"); - - case Q_DECNET: - return gen_dnhostop(addr, dir); - - case Q_SCA: - bpf_error("SCA host filtering not implemented"); - - case Q_LAT: - bpf_error("LAT host filtering not implemented"); - - case Q_MOPDL: - bpf_error("MOPDL host filtering not implemented"); - - case Q_MOPRC: - bpf_error("MOPRC host filtering not implemented"); - -#ifdef INET6 - case Q_IPV6: - bpf_error("'ip6' modifier applied to ip host"); - - case Q_ICMPV6: - bpf_error("'icmp6' modifier applied to %s", typestr); -#endif /* INET6 */ - - case Q_AH: - bpf_error("'ah' modifier applied to %s", typestr); - - case Q_ESP: - 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 %s", typestr); - - case Q_ISIS: - bpf_error("'isis' modifier applied to %s", typestr); - - case Q_CLNP: - bpf_error("'clnp' modifier applied to %s", typestr); - - case Q_STP: - 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 %s", typestr); - - case Q_RADIO: - bpf_error("'radio' modifier applied to %s", typestr); - - default: - abort(); - } - /* NOTREACHED */ -} - -#ifdef INET6 -static struct block * -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, type); - - case Q_IP: - bpf_error("'ip' modifier applied to ip6 %s", typestr); - - case Q_RARP: - bpf_error("'rarp' modifier applied to ip6 %s", typestr); - - case Q_ARP: - bpf_error("'arp' modifier applied to ip6 %s", typestr); - - case Q_SCTP: - bpf_error("'sctp' modifier applied to %s", typestr); - - case Q_TCP: - bpf_error("'tcp' modifier applied to %s", typestr); - - case Q_UDP: - bpf_error("'udp' modifier applied to %s", typestr); - - case Q_ICMP: - bpf_error("'icmp' modifier applied to %s", typestr); - - case Q_IGMP: - bpf_error("'igmp' modifier applied to %s", typestr); - - case Q_IGRP: - bpf_error("'igrp' modifier applied to %s", typestr); - - case Q_PIM: - bpf_error("'pim' modifier applied to %s", typestr); - - case Q_VRRP: - bpf_error("'vrrp' modifier applied to %s", typestr); - - case Q_ATALK: - bpf_error("ATALK host filtering not implemented"); - - case Q_AARP: - bpf_error("AARP host filtering not implemented"); - - case Q_DECNET: - bpf_error("'decnet' modifier applied to ip6 %s", typestr); - - case Q_SCA: - bpf_error("SCA host filtering not implemented"); - - case Q_LAT: - bpf_error("LAT host filtering not implemented"); - - case Q_MOPDL: - bpf_error("MOPDL host filtering not implemented"); - - case Q_MOPRC: - bpf_error("MOPRC host filtering not implemented"); - - case Q_IPV6: - return gen_hostop6(addr, mask, dir, ETHERTYPE_IPV6, 8, 24); - - case Q_ICMPV6: - bpf_error("'icmp6' modifier applied to %s", typestr); - - case Q_AH: - bpf_error("'ah' modifier applied to %s", typestr); - - case Q_ESP: - 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 %s", typestr); - - case Q_ISIS: - bpf_error("'isis' modifier applied to %s", typestr); - - case Q_CLNP: - bpf_error("'clnp' modifier applied to %s", typestr); - - case Q_STP: - 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 %s", typestr); - - case Q_RADIO: - bpf_error("'radio' modifier applied to %s", typestr); - - default: - abort(); - } - /* NOTREACHED */ -} -#endif /*INET6*/ - -#ifndef INET6 -static struct block * -gen_gateway(eaddr, alist, proto, dir) - const u_char *eaddr; - bpf_u_int32 **alist; - int proto; - int dir; -{ - struct block *b0, *b1, *tmp; - - if (dir != 0) - bpf_error("direction applied to 'gateway'"); - - switch (proto) { - case Q_DEFAULT: - case Q_IP: - case Q_ARP: - case Q_RARP: - 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(OR_LINK, SUNATM_PKT_BEGIN_POS, BPF_H, - 0xFF00); - gen_not(b1); - - /* - * Now check the MAC address. - */ - b0 = gen_ehostop(eaddr, Q_OR); - gen_and(b1, b0); - } - 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, Q_HOST); - while (*alist) { - tmp = gen_host(**alist++, 0xffffffff, proto, Q_OR, - Q_HOST); - gen_or(b1, tmp); - b1 = tmp; - } - gen_not(b1); - gen_and(b0, b1); - return b1; - } - bpf_error("illegal modifier of 'gateway'"); - /* NOTREACHED */ -} -#endif - -struct block * -gen_proto_abbrev(proto) - int proto; -{ - struct block *b0; - struct block *b1; - - switch (proto) { - - case Q_SCTP: - b1 = gen_proto(IPPROTO_SCTP, Q_IP, Q_DEFAULT); -#ifdef INET6 - b0 = gen_proto(IPPROTO_SCTP, Q_IPV6, Q_DEFAULT); - gen_or(b0, b1); -#endif - break; - - case Q_TCP: - b1 = gen_proto(IPPROTO_TCP, Q_IP, Q_DEFAULT); -#ifdef INET6 - b0 = gen_proto(IPPROTO_TCP, Q_IPV6, Q_DEFAULT); - gen_or(b0, b1); -#endif - break; - - case Q_UDP: - b1 = gen_proto(IPPROTO_UDP, Q_IP, Q_DEFAULT); -#ifdef INET6 - b0 = gen_proto(IPPROTO_UDP, Q_IPV6, Q_DEFAULT); - gen_or(b0, b1); -#endif - break; - - case Q_ICMP: - b1 = gen_proto(IPPROTO_ICMP, Q_IP, Q_DEFAULT); - break; - -#ifndef IPPROTO_IGMP -#define IPPROTO_IGMP 2 -#endif - - case Q_IGMP: - b1 = gen_proto(IPPROTO_IGMP, Q_IP, Q_DEFAULT); - break; - -#ifndef IPPROTO_IGRP -#define IPPROTO_IGRP 9 -#endif - case Q_IGRP: - b1 = gen_proto(IPPROTO_IGRP, Q_IP, Q_DEFAULT); - break; - -#ifndef IPPROTO_PIM -#define IPPROTO_PIM 103 -#endif - - case Q_PIM: - b1 = gen_proto(IPPROTO_PIM, Q_IP, Q_DEFAULT); -#ifdef INET6 - b0 = gen_proto(IPPROTO_PIM, Q_IPV6, Q_DEFAULT); - gen_or(b0, b1); -#endif - break; - -#ifndef IPPROTO_VRRP -#define IPPROTO_VRRP 112 -#endif - - case Q_VRRP: - b1 = gen_proto(IPPROTO_VRRP, Q_IP, Q_DEFAULT); - break; - - case Q_IP: - b1 = gen_linktype(ETHERTYPE_IP); - break; - - case Q_ARP: - b1 = gen_linktype(ETHERTYPE_ARP); - break; - - case Q_RARP: - b1 = gen_linktype(ETHERTYPE_REVARP); - break; - - case Q_LINK: - bpf_error("link layer applied in wrong context"); - - case Q_ATALK: - b1 = gen_linktype(ETHERTYPE_ATALK); - break; - - case Q_AARP: - b1 = gen_linktype(ETHERTYPE_AARP); - break; - - case Q_DECNET: - b1 = gen_linktype(ETHERTYPE_DN); - break; - - case Q_SCA: - b1 = gen_linktype(ETHERTYPE_SCA); - break; - - case Q_LAT: - b1 = gen_linktype(ETHERTYPE_LAT); - break; - - case Q_MOPDL: - b1 = gen_linktype(ETHERTYPE_MOPDL); - break; - - case Q_MOPRC: - b1 = gen_linktype(ETHERTYPE_MOPRC); - break; - -#ifdef INET6 - case Q_IPV6: - b1 = gen_linktype(ETHERTYPE_IPV6); - break; - -#ifndef IPPROTO_ICMPV6 -#define IPPROTO_ICMPV6 58 -#endif - case Q_ICMPV6: - b1 = gen_proto(IPPROTO_ICMPV6, Q_IPV6, Q_DEFAULT); - break; -#endif /* INET6 */ - -#ifndef IPPROTO_AH -#define IPPROTO_AH 51 -#endif - case Q_AH: - b1 = gen_proto(IPPROTO_AH, Q_IP, Q_DEFAULT); -#ifdef INET6 - b0 = gen_proto(IPPROTO_AH, Q_IPV6, Q_DEFAULT); - gen_or(b0, b1); -#endif - break; - -#ifndef IPPROTO_ESP -#define IPPROTO_ESP 50 -#endif - case Q_ESP: - b1 = gen_proto(IPPROTO_ESP, Q_IP, Q_DEFAULT); -#ifdef INET6 - b0 = gen_proto(IPPROTO_ESP, Q_IPV6, Q_DEFAULT); - gen_or(b0, b1); -#endif - break; - - case Q_ISO: - b1 = gen_linktype(LLCSAP_ISONS); - break; - - case Q_ESIS: - b1 = gen_proto(ISO9542_ESIS, Q_ISO, Q_DEFAULT); - break; - - case Q_ISIS: - b1 = gen_proto(ISO10589_ISIS, Q_ISO, Q_DEFAULT); - break; - - case Q_ISIS_L1: /* all IS-IS Level1 PDU-Types */ - b0 = gen_proto(ISIS_L1_LAN_IIH, Q_ISIS, Q_DEFAULT); - b1 = gen_proto(ISIS_PTP_IIH, Q_ISIS, Q_DEFAULT); /* FIXME extract the circuit-type bits */ - gen_or(b0, b1); - b0 = gen_proto(ISIS_L1_LSP, Q_ISIS, Q_DEFAULT); - gen_or(b0, b1); - b0 = gen_proto(ISIS_L1_CSNP, Q_ISIS, Q_DEFAULT); - gen_or(b0, b1); - b0 = gen_proto(ISIS_L1_PSNP, Q_ISIS, Q_DEFAULT); - gen_or(b0, b1); - break; - - case Q_ISIS_L2: /* all IS-IS Level2 PDU-Types */ - b0 = gen_proto(ISIS_L2_LAN_IIH, Q_ISIS, Q_DEFAULT); - b1 = gen_proto(ISIS_PTP_IIH, Q_ISIS, Q_DEFAULT); /* FIXME extract the circuit-type bits */ - gen_or(b0, b1); - b0 = gen_proto(ISIS_L2_LSP, Q_ISIS, Q_DEFAULT); - gen_or(b0, b1); - b0 = gen_proto(ISIS_L2_CSNP, Q_ISIS, Q_DEFAULT); - gen_or(b0, b1); - b0 = gen_proto(ISIS_L2_PSNP, Q_ISIS, Q_DEFAULT); - gen_or(b0, b1); - break; - - case Q_ISIS_IIH: /* all IS-IS Hello PDU-Types */ - b0 = gen_proto(ISIS_L1_LAN_IIH, Q_ISIS, Q_DEFAULT); - b1 = gen_proto(ISIS_L2_LAN_IIH, Q_ISIS, Q_DEFAULT); - gen_or(b0, b1); - b0 = gen_proto(ISIS_PTP_IIH, Q_ISIS, Q_DEFAULT); - gen_or(b0, b1); - break; - - case Q_ISIS_LSP: - b0 = gen_proto(ISIS_L1_LSP, Q_ISIS, Q_DEFAULT); - b1 = gen_proto(ISIS_L2_LSP, Q_ISIS, Q_DEFAULT); - gen_or(b0, b1); - break; - - case Q_ISIS_SNP: - b0 = gen_proto(ISIS_L1_CSNP, Q_ISIS, Q_DEFAULT); - b1 = gen_proto(ISIS_L2_CSNP, Q_ISIS, Q_DEFAULT); - gen_or(b0, b1); - b0 = gen_proto(ISIS_L1_PSNP, Q_ISIS, Q_DEFAULT); - gen_or(b0, b1); - b0 = gen_proto(ISIS_L2_PSNP, Q_ISIS, Q_DEFAULT); - gen_or(b0, b1); - break; - - case Q_ISIS_CSNP: - b0 = gen_proto(ISIS_L1_CSNP, Q_ISIS, Q_DEFAULT); - b1 = gen_proto(ISIS_L2_CSNP, Q_ISIS, Q_DEFAULT); - gen_or(b0, b1); - break; - - case Q_ISIS_PSNP: - b0 = gen_proto(ISIS_L1_PSNP, Q_ISIS, Q_DEFAULT); - b1 = gen_proto(ISIS_L2_PSNP, Q_ISIS, Q_DEFAULT); - gen_or(b0, b1); - break; - - case Q_CLNP: - b1 = gen_proto(ISO8473_CLNP, Q_ISO, Q_DEFAULT); - break; - - case Q_STP: - b1 = gen_linktype(LLCSAP_8021D); - break; - - case Q_IPX: - b1 = gen_linktype(LLCSAP_IPX); - break; - - case Q_NETBEUI: - b1 = gen_linktype(LLCSAP_NETBEUI); - break; - - 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_port6(port, ip_proto, dir) - int port; - int ip_proto; - int dir; -{ - struct block *b0, *b1, *tmp; - - /* 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); - 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_portrangeatom(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_IPV4, off, BPF_H, v1); - b2 = gen_cmp_le(OR_TRAN_IPV4, off, BPF_H, v2); - - gen_and(b1, b2); - - return b2; -} - -struct block * -gen_portrangeop(port1, port2, proto, dir) - int port1, port2; - int proto; - int 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_portrangeatom(0, (bpf_int32)port1, (bpf_int32)port2); - break; - - case Q_DST: - b1 = gen_portrangeatom(2, (bpf_int32)port1, (bpf_int32)port2); - break; - - case Q_OR: - case Q_DEFAULT: - 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_portrangeatom(0, (bpf_int32)port1, (bpf_int32)port2); - b1 = gen_portrangeatom(2, (bpf_int32)port1, (bpf_int32)port2); - gen_and(tmp, b1); - break; - - default: - abort(); - } - gen_and(b0, b1); - - return b1; -} - -static struct block * -gen_portrange(port1, port2, ip_proto, dir) - int port1, port2; - int ip_proto; - int dir; -{ - struct block *b0, *b1, *tmp; - - /* link proto ip */ - b0 = gen_linktype(ETHERTYPE_IP); - - switch (ip_proto) { - case IPPROTO_UDP: - case IPPROTO_TCP: - case IPPROTO_SCTP: - b1 = gen_portrangeop(port1, port2, ip_proto, dir); - break; - - case PROTO_UNDEF: - tmp = gen_portrangeop(port1, port2, IPPROTO_TCP, dir); - b1 = gen_portrangeop(port1, port2, IPPROTO_UDP, dir); - gen_or(tmp, b1); - tmp = gen_portrangeop(port1, port2, IPPROTO_SCTP, dir); - gen_or(tmp, b1); - break; - - default: - abort(); - } - gen_and(b0, b1); - return b1; -} - -#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_portrangeop6(port1, port2, proto, dir) - int port1, port2; - int proto; - int 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_portrangeatom6(0, (bpf_int32)port1, (bpf_int32)port2); - break; - - case Q_DST: - b1 = gen_portrangeatom6(2, (bpf_int32)port1, (bpf_int32)port2); - break; - - case Q_OR: - case Q_DEFAULT: - 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_portrangeatom6(0, (bpf_int32)port1, (bpf_int32)port2); - b1 = gen_portrangeatom6(2, (bpf_int32)port1, (bpf_int32)port2); - gen_and(tmp, b1); - break; - - default: - abort(); - } - gen_and(b0, b1); - - return b1; -} - -static struct block * -gen_portrange6(port1, port2, ip_proto, dir) - int port1, port2; - int ip_proto; - int dir; -{ - struct block *b0, *b1, *tmp; - - /* link proto ip6 */ - b0 = gen_linktype(ETHERTYPE_IPV6); - - switch (ip_proto) { - case IPPROTO_UDP: - case IPPROTO_TCP: - case IPPROTO_SCTP: - b1 = gen_portrangeop6(port1, port2, ip_proto, dir); - break; - - case PROTO_UNDEF: - tmp = gen_portrangeop6(port1, port2, IPPROTO_TCP, dir); - b1 = gen_portrangeop6(port1, port2, IPPROTO_UDP, dir); - gen_or(tmp, b1); - tmp = gen_portrangeop6(port1, port2, IPPROTO_SCTP, dir); - gen_or(tmp, b1); - break; - - default: - abort(); - } - gen_and(b0, b1); - return b1; -} -#endif /* INET6 */ - -static int -lookup_proto(name, proto) - register const char *name; - register int proto; -{ - register int v; - - switch (proto) { - - case Q_DEFAULT: - case Q_IP: - case Q_IPV6: - v = pcap_nametoproto(name); - if (v == PROTO_UNDEF) - bpf_error("unknown ip proto '%s'", name); - break; - - case Q_LINK: - /* XXX should look up h/w protocol type based on linktype */ - v = pcap_nametoeproto(name); - if (v == PROTO_UNDEF) { - v = pcap_nametollc(name); - if (v == PROTO_UNDEF) - bpf_error("unknown ether proto '%s'", name); - } - break; - - case Q_ISO: - if (strcmp(name, "esis") == 0) - v = ISO9542_ESIS; - else if (strcmp(name, "isis") == 0) - v = ISO10589_ISIS; - else if (strcmp(name, "clnp") == 0) - v = ISO8473_CLNP; - else - bpf_error("unknown osi proto '%s'", name); - break; - - default: - v = PROTO_UNDEF; - break; - } - return v; -} - -#if 0 -struct stmt * -gen_joinsp(s, n) - struct stmt **s; - int n; -{ - return NULL; -} -#endif - -static struct block * -gen_protochain(v, proto, dir) - int v; - int proto; - int dir; -{ -#ifdef NO_PROTOCHAIN - return gen_proto(v, proto, dir); -#else - struct block *b0, *b; - struct slist *s[100]; - int fix2, fix3, fix4, fix5; - int ahcheck, again, end; - int i, max; - int reg2 = alloc_reg(); - - memset(s, 0, sizeof(s)); - fix2 = fix3 = fix4 = fix5 = 0; - - switch (proto) { - case Q_IP: - case Q_IPV6: - break; - case Q_DEFAULT: - b0 = gen_protochain(v, Q_IP, dir); - b = gen_protochain(v, Q_IPV6, dir); - gen_or(b0, b); - return b; - default: - bpf_error("bad protocol applied for 'protochain'"); - /*NOTREACHED*/ - } - - /* - * 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 damage - * by s[fix] = foo with uninitialized variable "fix". It is somewhat - * hard to find interdependency made by jump table fixup. - */ - i = 0; - s[i] = new_stmt(0); /*dummy*/ - i++; - - switch (proto) { - case Q_IP: - b0 = gen_linktype(ETHERTYPE_IP); - - /* A = ip->ip_p */ - s[i] = new_stmt(BPF_LD|BPF_ABS|BPF_B); - s[i]->s.k = off_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_ll + off_nl; - i++; - break; -#ifdef INET6 - case Q_IPV6: - b0 = gen_linktype(ETHERTYPE_IPV6); - - /* A = ip6->ip_nxt */ - s[i] = new_stmt(BPF_LD|BPF_ABS|BPF_B); - s[i]->s.k = off_ll + off_nl + 6; - i++; - /* X = sizeof(struct ip6_hdr) */ - s[i] = new_stmt(BPF_LDX|BPF_IMM); - s[i]->s.k = 40; - i++; - break; -#endif - default: - bpf_error("unsupported proto to gen_protochain"); - /*NOTREACHED*/ - } - - /* again: if (A == v) goto end; else fall through; */ - again = i; - s[i] = new_stmt(BPF_JMP|BPF_JEQ|BPF_K); - s[i]->s.k = v; - s[i]->s.jt = NULL; /*later*/ - s[i]->s.jf = NULL; /*update in next stmt*/ - fix5 = i; - i++; - -#ifndef IPPROTO_NONE -#define IPPROTO_NONE 59 -#endif - /* if (A == IPPROTO_NONE) goto end */ - s[i] = new_stmt(BPF_JMP|BPF_JEQ|BPF_K); - s[i]->s.jt = NULL; /*later*/ - s[i]->s.jf = NULL; /*update in next stmt*/ - s[i]->s.k = IPPROTO_NONE; - s[fix5]->s.jf = s[i]; - fix2 = i; - i++; - -#ifdef INET6 - if (proto == Q_IPV6) { - int v6start, v6end, v6advance, j; - - v6start = i; - /* if (A == IPPROTO_HOPOPTS) goto v6advance */ - s[i] = new_stmt(BPF_JMP|BPF_JEQ|BPF_K); - s[i]->s.jt = NULL; /*later*/ - s[i]->s.jf = NULL; /*update in next stmt*/ - s[i]->s.k = IPPROTO_HOPOPTS; - s[fix2]->s.jf = s[i]; - i++; - /* if (A == IPPROTO_DSTOPTS) goto v6advance */ - s[i - 1]->s.jf = s[i] = new_stmt(BPF_JMP|BPF_JEQ|BPF_K); - s[i]->s.jt = NULL; /*later*/ - s[i]->s.jf = NULL; /*update in next stmt*/ - s[i]->s.k = IPPROTO_DSTOPTS; - i++; - /* if (A == IPPROTO_ROUTING) goto v6advance */ - s[i - 1]->s.jf = s[i] = new_stmt(BPF_JMP|BPF_JEQ|BPF_K); - s[i]->s.jt = NULL; /*later*/ - s[i]->s.jf = NULL; /*update in next stmt*/ - s[i]->s.k = IPPROTO_ROUTING; - i++; - /* if (A == IPPROTO_FRAGMENT) goto v6advance; else goto ahcheck; */ - s[i - 1]->s.jf = s[i] = new_stmt(BPF_JMP|BPF_JEQ|BPF_K); - s[i]->s.jt = NULL; /*later*/ - s[i]->s.jf = NULL; /*later*/ - s[i]->s.k = IPPROTO_FRAGMENT; - fix3 = i; - v6end = i; - i++; - - /* v6advance: */ - v6advance = i; - - /* - * in short, - * A = P[X]; - * X = X + (P[X + 1] + 1) * 8; - */ - /* A = X */ - s[i] = new_stmt(BPF_MISC|BPF_TXA); - i++; - /* A = P[X + packet head] */ - s[i] = new_stmt(BPF_LD|BPF_IND|BPF_B); - s[i]->s.k = off_ll + off_nl; - i++; - /* MEM[reg2] = A */ - s[i] = new_stmt(BPF_ST); - s[i]->s.k = reg2; - i++; - /* A = X */ - s[i] = new_stmt(BPF_MISC|BPF_TXA); - i++; - /* A += 1 */ - s[i] = new_stmt(BPF_ALU|BPF_ADD|BPF_K); - s[i]->s.k = 1; - i++; - /* X = A */ - s[i] = new_stmt(BPF_MISC|BPF_TAX); - i++; - /* A = P[X + packet head]; */ - s[i] = new_stmt(BPF_LD|BPF_IND|BPF_B); - s[i]->s.k = off_ll + off_nl; - i++; - /* A += 1 */ - s[i] = new_stmt(BPF_ALU|BPF_ADD|BPF_K); - s[i]->s.k = 1; - i++; - /* A *= 8 */ - s[i] = new_stmt(BPF_ALU|BPF_MUL|BPF_K); - s[i]->s.k = 8; - i++; - /* X = A; */ - s[i] = new_stmt(BPF_MISC|BPF_TAX); - i++; - /* A = MEM[reg2] */ - s[i] = new_stmt(BPF_LD|BPF_MEM); - s[i]->s.k = reg2; - i++; - - /* goto again; (must use BPF_JA for backward jump) */ - s[i] = new_stmt(BPF_JMP|BPF_JA); - s[i]->s.k = again - i - 1; - s[i - 1]->s.jf = s[i]; - i++; - - /* fixup */ - for (j = v6start; j <= v6end; j++) - s[j]->s.jt = s[v6advance]; - } else -#endif - { - /* nop */ - s[i] = new_stmt(BPF_ALU|BPF_ADD|BPF_K); - s[i]->s.k = 0; - s[fix2]->s.jf = s[i]; - i++; - } - - /* ahcheck: */ - ahcheck = i; - /* if (A == IPPROTO_AH) then fall through; else goto end; */ - s[i] = new_stmt(BPF_JMP|BPF_JEQ|BPF_K); - s[i]->s.jt = NULL; /*later*/ - s[i]->s.jf = NULL; /*later*/ - s[i]->s.k = IPPROTO_AH; - if (fix3) - s[fix3]->s.jf = s[ahcheck]; - fix4 = i; - i++; - - /* - * in short, - * A = P[X]; - * X = X + (P[X + 1] + 2) * 4; - */ - /* A = X */ - s[i - 1]->s.jt = s[i] = new_stmt(BPF_MISC|BPF_TXA); - i++; - /* A = P[X + packet head]; */ - s[i] = new_stmt(BPF_LD|BPF_IND|BPF_B); - s[i]->s.k = off_ll + off_nl; - i++; - /* MEM[reg2] = A */ - s[i] = new_stmt(BPF_ST); - s[i]->s.k = reg2; - i++; - /* A = X */ - s[i - 1]->s.jt = s[i] = new_stmt(BPF_MISC|BPF_TXA); - i++; - /* A += 1 */ - s[i] = new_stmt(BPF_ALU|BPF_ADD|BPF_K); - s[i]->s.k = 1; - i++; - /* X = A */ - s[i] = new_stmt(BPF_MISC|BPF_TAX); - i++; - /* A = P[X + packet head] */ - s[i] = new_stmt(BPF_LD|BPF_IND|BPF_B); - s[i]->s.k = off_ll + off_nl; - i++; - /* A += 2 */ - s[i] = new_stmt(BPF_ALU|BPF_ADD|BPF_K); - s[i]->s.k = 2; - i++; - /* A *= 4 */ - s[i] = new_stmt(BPF_ALU|BPF_MUL|BPF_K); - s[i]->s.k = 4; - i++; - /* X = A; */ - s[i] = new_stmt(BPF_MISC|BPF_TAX); - i++; - /* A = MEM[reg2] */ - s[i] = new_stmt(BPF_LD|BPF_MEM); - s[i]->s.k = reg2; - i++; - - /* goto again; (must use BPF_JA for backward jump) */ - s[i] = new_stmt(BPF_JMP|BPF_JA); - s[i]->s.k = again - i - 1; - i++; - - /* end: nop */ - end = i; - s[i] = new_stmt(BPF_ALU|BPF_ADD|BPF_K); - s[i]->s.k = 0; - s[fix2]->s.jt = s[end]; - s[fix4]->s.jf = s[end]; - s[fix5]->s.jt = s[end]; - i++; - - /* - * make slist chain - */ - max = i; - for (i = 0; i < max - 1; i++) - s[i]->next = s[i + 1]; - s[max - 1]->next = NULL; - - /* - * emit final check - */ - b = new_block(JMP(BPF_JEQ)); - b->stmts = s[1]; /*remember, s[0] is dummy*/ - b->s.k = v; - - free_reg(reg2); - - gen_and(b0, b); - return b; -#endif -} - - -/* - * 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; - int proto; - int dir; -{ - struct block *b0, *b1; - - if (dir != Q_DEFAULT) - bpf_error("direction applied to 'proto'"); - - switch (proto) { - case Q_DEFAULT: -#ifdef INET6 - b0 = gen_proto(v, Q_IP, dir); - b1 = gen_proto(v, Q_IPV6, dir); - gen_or(b0, b1); - return b1; -#else - /*FALLTHROUGH*/ -#endif - case Q_IP: - /* - * For FDDI, RFC 1188 says that SNAP encapsulation is used, - * not LLC encapsulation with LLCSAP_IP. - * - * For IEEE 802 networks - which includes 802.5 token ring - * (which is what DLT_IEEE802 means) and 802.11 - RFC 1042 - * says that SNAP encapsulation is used, not LLC encapsulation - * with LLCSAP_IP. - * - * For LLC-encapsulated ATM/"Classical IP", RFC 1483 and - * RFC 2225 say that SNAP encapsulation is used, not LLC - * encapsulation with LLCSAP_IP. - * - * So we always check for ETHERTYPE_IP. - */ - b0 = gen_linktype(ETHERTYPE_IP); -#ifndef CHASE_CHAIN - b1 = gen_cmp(OR_NET, 9, BPF_B, (bpf_int32)v); -#else - b1 = gen_protochain(v, Q_IP); -#endif - gen_and(b0, b1); - return b1; - - case Q_ISO: - switch (linktype) { - - case DLT_FRELAY: - /* - * Frame Relay packets typically have an OSI - * NLPID at the beginning; "gen_linktype(LLCSAP_ISONS)" - * generates code to check for all the OSI - * NLPIDs, so calling it and then adding a check - * for the particular NLPID for which we're - * looking is bogus, as we can just check for - * the NLPID. - * - * What we check for is the NLPID and a frame - * control field value of UI, i.e. 0x03 followed - * by the NLPID. - * - * XXX - assumes a 2-byte Frame Relay header with - * DLCI and flags. What if the address is longer? - * - * XXX - what about SNAP-encapsulated frames? - */ - return gen_cmp(OR_LINK, 2, BPF_H, (0x03<<8) | v); - /*NOTREACHED*/ - break; - - case DLT_C_HDLC: - /* - * Cisco uses an Ethertype lookalike - for OSI, - * it's 0xfefe. - */ - b0 = gen_linktype(LLCSAP_ISONS<<8 | LLCSAP_ISONS); - /* OSI in C-HDLC is stuffed with a fudge byte */ - b1 = gen_cmp(OR_NET_NOSNAP, 1, BPF_B, (long)v); - gen_and(b0, b1); - return b1; - - default: - b0 = gen_linktype(LLCSAP_ISONS); - b1 = gen_cmp(OR_NET_NOSNAP, 0, BPF_B, (long)v); - gen_and(b0, b1); - return b1; - } - - case Q_ISIS: - b0 = gen_proto(ISO10589_ISIS, Q_ISO, Q_DEFAULT); - /* - * 4 is the offset of the PDU type relative to the IS-IS - * header. - */ - b1 = gen_cmp(OR_NET_NOSNAP, 4, BPF_B, (long)v); - gen_and(b0, b1); - return b1; - - case Q_ARP: - bpf_error("arp does not encapsulate another protocol"); - /* NOTREACHED */ - - case Q_RARP: - bpf_error("rarp does not encapsulate another protocol"); - /* NOTREACHED */ - - case Q_ATALK: - bpf_error("atalk encapsulation is not specifiable"); - /* NOTREACHED */ - - case Q_DECNET: - bpf_error("decnet encapsulation is not specifiable"); - /* NOTREACHED */ - - case Q_SCA: - bpf_error("sca does not encapsulate another protocol"); - /* NOTREACHED */ - - case Q_LAT: - bpf_error("lat does not encapsulate another protocol"); - /* NOTREACHED */ - - case Q_MOPRC: - bpf_error("moprc does not encapsulate another protocol"); - /* NOTREACHED */ - - case Q_MOPDL: - bpf_error("mopdl does not encapsulate another protocol"); - /* NOTREACHED */ - - case Q_LINK: - return gen_linktype(v); - - case Q_UDP: - bpf_error("'udp proto' is bogus"); - /* NOTREACHED */ - - case Q_TCP: - bpf_error("'tcp proto' is bogus"); - /* NOTREACHED */ - - case Q_SCTP: - bpf_error("'sctp proto' is bogus"); - /* NOTREACHED */ - - case Q_ICMP: - bpf_error("'icmp proto' is bogus"); - /* NOTREACHED */ - - case Q_IGMP: - bpf_error("'igmp proto' is bogus"); - /* NOTREACHED */ - - case Q_IGRP: - bpf_error("'igrp proto' is bogus"); - /* NOTREACHED */ - - case Q_PIM: - bpf_error("'pim proto' is bogus"); - /* NOTREACHED */ - - case Q_VRRP: - bpf_error("'vrrp proto' is bogus"); - /* NOTREACHED */ - -#ifdef INET6 - case Q_IPV6: - b0 = gen_linktype(ETHERTYPE_IPV6); -#ifndef CHASE_CHAIN - b1 = gen_cmp(OR_NET, 6, BPF_B, (bpf_int32)v); -#else - b1 = gen_protochain(v, Q_IPV6); -#endif - gen_and(b0, b1); - return b1; - - case Q_ICMPV6: - bpf_error("'icmp6 proto' is bogus"); -#endif /* INET6 */ - - case Q_AH: - bpf_error("'ah proto' is bogus"); - - case Q_ESP: - bpf_error("'ah proto' is bogus"); - - case Q_STP: - bpf_error("'stp proto' is bogus"); - - case Q_IPX: - bpf_error("'ipx proto' is bogus"); - - case Q_NETBEUI: - bpf_error("'netbeui proto' is bogus"); - - case Q_RADIO: - bpf_error("'radio proto' is bogus"); - - default: - abort(); - /* NOTREACHED */ - } - /* NOTREACHED */ -} - -struct block * -gen_scode(name, q) - register const char *name; - struct qual q; -{ - int proto = q.proto; - int dir = q.dir; - int tproto; - u_char *eaddr; - bpf_u_int32 mask, addr; -#ifndef INET6 - bpf_u_int32 **alist; -#else - int tproto6; - struct sockaddr_in *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) { - - case Q_NET: - addr = pcap_nametonetaddr(name); - if (addr == 0) - bpf_error("unknown network '%s'", name); - /* Left justify network addr and calculate its network mask */ - mask = 0xffffffff; - while (addr && (addr & 0xff000000) == 0) { - addr <<= 8; - mask <<= 8; - } - return gen_host(addr, mask, proto, dir, q.addr); - - case Q_DEFAULT: - case Q_HOST: - if (proto == Q_LINK) { - switch (linktype) { - - case DLT_EN10MB: - eaddr = pcap_ether_hostton(name); - if (eaddr == NULL) - bpf_error( - "unknown ether host '%s'", name); - b = gen_ehostop(eaddr, dir); - free(eaddr); - return b; - - case DLT_FDDI: - eaddr = pcap_ether_hostton(name); - if (eaddr == NULL) - bpf_error( - "unknown FDDI host '%s'", name); - b = gen_fhostop(eaddr, dir); - free(eaddr); - return b; - - case DLT_IEEE802: - eaddr = pcap_ether_hostton(name); - if (eaddr == NULL) - bpf_error( - "unknown token ring host '%s'", name); - b = gen_thostop(eaddr, dir); - free(eaddr); - return b; - - case DLT_IEEE802_11: - 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( - "unknown 802.11 host '%s'", name); - b = gen_wlanhostop(eaddr, dir); - free(eaddr); - return b; - - case DLT_IP_OVER_FC: - eaddr = pcap_ether_hostton(name); - if (eaddr == NULL) - bpf_error( - "unknown Fibre Channel host '%s'", name); - b = gen_ipfchostop(eaddr, dir); - free(eaddr); - return b; - - case DLT_SUNATM: - if (!is_lane) - break; - - /* - * Check that the packet doesn't begin - * with an LE Control marker. (We've - * already generated a test for LANE.) - */ - tmp = gen_cmp(OR_LINK, SUNATM_PKT_BEGIN_POS, - BPF_H, 0xFF00); - gen_not(tmp); - - eaddr = pcap_ether_hostton(name); - if (eaddr == NULL) - bpf_error( - "unknown ether host '%s'", name); - b = gen_ehostop(eaddr, dir); - gen_and(tmp, b); - free(eaddr); - return b; - } - - bpf_error("only ethernet/FDDI/token ring/802.11/ATM LANE/Fibre Channel supports link-level host name"); - } else if (proto == Q_DECNET) { - unsigned short dn_addr = __pcap_nametodnaddr(name); - /* - * I don't think DECNET hosts can be multihomed, so - * there is no need to build up a list of addresses - */ - return (gen_host(dn_addr, 0, proto, dir, 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 == (u_int)-1 && tproto == Q_DEFAULT) - tproto = Q_IP; - b = gen_host(**alist++, 0xffffffff, tproto, dir, q.addr); - while (*alist) { - tmp = gen_host(**alist++, 0xffffffff, - tproto, dir, q.addr); - gen_or(b, tmp); - b = tmp; - } - return b; -#else - memset(&mask128, 0xff, sizeof(mask128)); - res0 = res = pcap_nametoaddrinfo(name); - if (res == NULL) - bpf_error("unknown host '%s'", name); - b = tmp = NULL; - tproto = tproto6 = proto; - if (off_linktype == -1 && tproto == Q_DEFAULT) { - tproto = Q_IP; - tproto6 = Q_IPV6; - } - for (res = res0; res; res = res->ai_next) { - switch (res->ai_family) { - case AF_INET: - if (tproto == Q_IPV6) - continue; - - sin4 = (struct sockaddr_in *) - res->ai_addr; - tmp = gen_host(ntohl(sin4->sin_addr.s_addr), - 0xffffffff, tproto, dir, q.addr); - break; - case AF_INET6: - if (tproto6 == Q_IP) - continue; - - sin6 = (struct sockaddr_in6 *) - res->ai_addr; - tmp = gen_host6(&sin6->sin6_addr, - &mask128, tproto6, dir, q.addr); - break; - default: - continue; - } - if (b) - gen_or(b, tmp); - b = tmp; - } - freeaddrinfo(res0); - if (b == NULL) { - bpf_error("unknown host '%s'%s", name, - (proto == Q_DEFAULT) - ? "" - : " for specified address family"); - } - return b; -#endif /*INET6*/ - } - - case Q_PORT: - if (proto != Q_DEFAULT && - proto != Q_UDP && proto != Q_TCP && proto != Q_SCTP) - bpf_error("illegal qualifier of 'port'"); - if (pcap_nametoport(name, &port, &real_proto) == 0) - bpf_error("unknown port '%s'", name); - if (proto == Q_UDP) { - if (real_proto == IPPROTO_TCP) - bpf_error("port '%s' is tcp", name); - else if (real_proto == IPPROTO_SCTP) - bpf_error("port '%s' is sctp", name); - else - /* override PROTO_UNDEF */ - real_proto = IPPROTO_UDP; - } - if (proto == Q_TCP) { - if (real_proto == IPPROTO_UDP) - bpf_error("port '%s' is udp", name); - - else if (real_proto == IPPROTO_SCTP) - bpf_error("port '%s' is sctp", name); - else - /* override PROTO_UNDEF */ - real_proto = IPPROTO_TCP; - } - if (proto == Q_SCTP) { - if (real_proto == IPPROTO_UDP) - bpf_error("port '%s' is udp", name); - - else if (real_proto == IPPROTO_TCP) - bpf_error("port '%s' is tcp", name); - else - /* override PROTO_UNDEF */ - real_proto = IPPROTO_SCTP; - } -#ifndef INET6 - return gen_port(port, real_proto, dir); -#else - 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: -#ifndef INET6 - eaddr = pcap_ether_hostton(name); - if (eaddr == NULL) - bpf_error("unknown ether host: %s", name); - - alist = pcap_nametoaddr(name); - if (alist == NULL || *alist == NULL) - bpf_error("unknown host '%s'", name); - b = gen_gateway(eaddr, alist, proto, dir); - free(eaddr); - return b; -#else - bpf_error("'gateway' not supported in this configuration"); -#endif /*INET6*/ - - case Q_PROTO: - real_proto = lookup_proto(name, proto); - if (real_proto >= 0) - return gen_proto(real_proto, proto, dir); - else - bpf_error("unknown protocol: %s", name); - - case Q_PROTOCHAIN: - real_proto = lookup_proto(name, proto); - if (real_proto >= 0) - return gen_protochain(real_proto, proto, dir); - else - bpf_error("unknown protocol: %s", name); - - - case Q_UNDEF: - syntax(); - /* NOTREACHED */ - } - abort(); - /* NOTREACHED */ -} - -struct block * -gen_mcode(s1, s2, masklen, q) - register const char *s1, *s2; - register int masklen; - struct qual q; -{ - register int nlen, mlen; - bpf_u_int32 n, m; - - nlen = __pcap_atoin(s1, &n); - /* Promote short ipaddr */ - n <<= 32 - nlen; - - if (s2 != NULL) { - mlen = __pcap_atoin(s2, &m); - /* Promote short ipaddr */ - m <<= 32 - mlen; - if ((n & ~m) != 0) - bpf_error("non-network bits set in \"%s mask %s\"", - s1, s2); - } else { - /* Convert mask len to mask */ - if (masklen > 32) - bpf_error("mask length must be <= 32"); - 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); - } - - switch (q.addr) { - - case Q_NET: - 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 * -gen_ncode(s, v, q) - register const char *s; - bpf_u_int32 v; - struct qual q; -{ - bpf_u_int32 mask; - int proto = q.proto; - int dir = q.dir; - register int vlen; - - if (s == NULL) - vlen = 32; - else if (q.proto == Q_DECNET) - vlen = __pcap_atodn(s, &v); - else - vlen = __pcap_atoin(s, &v); - - switch (q.addr) { - - case Q_DEFAULT: - case Q_HOST: - case Q_NET: - if (proto == Q_DECNET) - return gen_host(v, 0, proto, dir, q.addr); - else if (proto == Q_LINK) { - bpf_error("illegal link layer address"); - } else { - mask = 0xffffffff; - if (s == NULL && q.addr == Q_NET) { - /* Promote short net number */ - while (v && (v & 0xff000000) == 0) { - v <<= 8; - mask <<= 8; - } - } else { - /* Promote short ipaddr */ - v <<= 32 - vlen; - mask <<= 32 - vlen; - } - return gen_host(v, mask, proto, dir, q.addr); - } - - case Q_PORT: - if (proto == Q_UDP) - proto = IPPROTO_UDP; - else if (proto == Q_TCP) - proto = IPPROTO_TCP; - else if (proto == Q_SCTP) - proto = IPPROTO_SCTP; - else if (proto == Q_DEFAULT) - proto = PROTO_UNDEF; - else - bpf_error("illegal qualifier of 'port'"); - -#ifndef INET6 - return gen_port((int)v, proto, dir); -#else - { - struct block *b; - b = gen_port((int)v, proto, dir); - gen_or(gen_port6((int)v, proto, dir), b); - return b; - } -#endif /* INET6 */ - - case Q_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 */ - - case Q_PROTO: - return gen_proto((int)v, proto, dir); - - case Q_PROTOCHAIN: - return gen_protochain((int)v, proto, dir); - - case Q_UNDEF: - syntax(); - /* NOTREACHED */ - - default: - abort(); - /* NOTREACHED */ - } - /* NOTREACHED */ -} - -#ifdef INET6 -struct block * -gen_mcode6(s1, s2, masklen, q) - register const char *s1, *s2; - register int masklen; - struct qual q; -{ - struct addrinfo *res; - struct in6_addr *addr; - struct in6_addr mask; - struct block *b; - u_int32_t *a, *m; - - if (s2) - bpf_error("no mask %s supported", s2); - - res = pcap_nametoaddrinfo(s1); - if (!res) - bpf_error("invalid ip6 address %s", s1); - if (res->ai_next) - bpf_error("%s resolved to multiple address", s1); - addr = &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr; - - if (sizeof(mask) * 8 < masklen) - bpf_error("mask length must be <= %u", (unsigned int)(sizeof(mask) * 8)); - memset(&mask, 0, sizeof(mask)); - memset(&mask, 0xff, masklen / 8); - if (masklen % 8) { - mask.s6_addr[masklen / 8] = - (0xff << (8 - masklen % 8)) & 0xff; - } - - a = (u_int32_t *)addr; - m = (u_int32_t *)&mask; - if ((a[0] & ~m[0]) || (a[1] & ~m[1]) - || (a[2] & ~m[2]) || (a[3] & ~m[3])) { - bpf_error("non-network bits set in \"%s/%d\"", s1, masklen); - } - - switch (q.addr) { - - case Q_DEFAULT: - case Q_HOST: - if (masklen != 128) - bpf_error("Mask syntax for networks only"); - /* FALLTHROUGH */ - - case Q_NET: - b = gen_host6(addr, &mask, q.proto, q.dir, q.addr); - freeaddrinfo(res); - return b; - - default: - bpf_error("invalid qualifier against IPv6 address"); - /* NOTREACHED */ - } - return NULL; -} -#endif /*INET6*/ - -struct block * -gen_ecode(eaddr, q) - register const u_char *eaddr; - struct qual q; -{ - struct block *b, *tmp; - - if ((q.addr == Q_HOST || q.addr == Q_DEFAULT) && q.proto == Q_LINK) { - 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 -sappend(s0, s1) - struct slist *s0, *s1; -{ - /* - * This is definitely not the best way to do this, but the - * lists will rarely get long. - */ - while (s0->next) - s0 = s0->next; - s0->next = s1; -} - -static struct slist * -xfer_to_x(a) - struct arth *a; -{ - struct slist *s; - - s = new_stmt(BPF_LDX|BPF_MEM); - s->s.k = a->regno; - return s; -} - -static struct slist * -xfer_to_a(a) - struct arth *a; -{ - struct slist *s; - - s = new_stmt(BPF_LD|BPF_MEM); - s->s.k = a->regno; - return s; -} - -/* - * 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, inst, size) - int proto; - struct arth *inst; - int size; -{ - struct slist *s, *tmp; - struct block *b; - int regno = alloc_reg(); - - free_reg(inst->regno); - switch (size) { - - default: - bpf_error("data size must be 1, 2, or 4"); - - case 1: - size = BPF_B; - break; - - case 2: - size = BPF_H; - break; - - case 4: - size = BPF_W; - break; - } - switch (proto) { - default: - bpf_error("unsupported index operation"); - - case Q_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 - * field, or relative to the beginning of the LAN - * frame, so that 0 refers, for Ethernet LANE, to - * the beginning of the destination address? - */ - s = 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(inst->s, s); - break; - - case Q_IP: - case Q_ARP: - case Q_RARP: - case Q_ATALK: - case Q_DECNET: - case Q_SCA: - case Q_LAT: - case Q_MOPRC: - case Q_MOPDL: -#ifdef INET6 - case Q_IPV6: -#endif - /* - * 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_ll + off_nl; - sappend(s, tmp); - sappend(inst->s, s); - - /* - * Do the computation only if the packet contains - * the protocol in question. - */ - b = gen_proto_abbrev(proto); - if (inst->b) - gen_and(inst->b, b); - inst->b = b; - break; - - case Q_SCTP: - case Q_TCP: - case Q_UDP: - case Q_ICMP: - case Q_IGMP: - case Q_IGRP: - case Q_PIM: - case Q_VRRP: - /* - * 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_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 (inst->b) - gen_and(inst->b, b); -#ifdef INET6 - gen_and(gen_proto_abbrev(Q_IP), b); -#endif - inst->b = b; - break; -#ifdef INET6 - case Q_ICMPV6: - bpf_error("IPv6 upper-layer protocol is not supported by proto[x]"); - /*NOTREACHED*/ -#endif - } - inst->regno = regno; - s = new_stmt(BPF_ST); - s->s.k = regno; - sappend(inst->s, s); - - return inst; -} - -struct block * -gen_relation(code, a0, a1, reversed) - int code; - struct arth *a0, *a1; - int reversed; -{ - struct slist *s0, *s1, *s2; - struct block *b, *tmp; - - s0 = xfer_to_x(a1); - s1 = xfer_to_a(a0); - if (code == BPF_JEQ) { - s2 = new_stmt(BPF_ALU|BPF_SUB|BPF_X); - b = new_block(JMP(code)); - sappend(s1, s2); - } - else - b = new_block(BPF_JMP|code|BPF_X); - if (reversed) - gen_not(b); - - sappend(s0, s1); - sappend(a1->s, s0); - sappend(a0->s, a1->s); - - b->stmts = a0->s; - - free_reg(a0->regno); - free_reg(a1->regno); - - /* 'and' together protocol checks */ - if (a0->b) { - if (a1->b) { - gen_and(a0->b, tmp = a1->b); - } - else - tmp = a0->b; - } else - tmp = a1->b; - - if (tmp) - gen_and(tmp, b); - - return b; -} - -struct arth * -gen_loadlen() -{ - int regno = alloc_reg(); - struct arth *a = (struct arth *)newchunk(sizeof(*a)); - struct slist *s; - - s = new_stmt(BPF_LD|BPF_LEN); - s->next = new_stmt(BPF_ST); - s->next->s.k = regno; - a->s = s; - a->regno = regno; - - return a; -} - -struct arth * -gen_loadi(val) - int val; -{ - struct arth *a; - struct slist *s; - int reg; - - a = (struct arth *)newchunk(sizeof(*a)); - - reg = alloc_reg(); - - s = new_stmt(BPF_LD|BPF_IMM); - s->s.k = val; - s->next = new_stmt(BPF_ST); - s->next->s.k = reg; - a->s = s; - a->regno = reg; - - return a; -} - -struct arth * -gen_neg(a) - struct arth *a; -{ - struct slist *s; - - s = xfer_to_a(a); - sappend(a->s, s); - s = new_stmt(BPF_ALU|BPF_NEG); - s->s.k = 0; - sappend(a->s, s); - s = new_stmt(BPF_ST); - s->s.k = a->regno; - sappend(a->s, s); - - return a; -} - -struct arth * -gen_arth(code, a0, a1) - int code; - struct arth *a0, *a1; -{ - struct slist *s0, *s1, *s2; - - s0 = xfer_to_x(a1); - s1 = xfer_to_a(a0); - s2 = new_stmt(BPF_ALU|BPF_X|code); - - sappend(s1, s2); - sappend(s0, s1); - sappend(a1->s, s0); - sappend(a0->s, a1->s); - - free_reg(a0->regno); - free_reg(a1->regno); - - s0 = new_stmt(BPF_ST); - a0->regno = s0->s.k = alloc_reg(); - sappend(a0->s, s0); - - return a0; -} - -/* - * Here we handle simple allocation of the scratch registers. - * If too many registers are alloc'd, the allocator punts. - */ -static int regused[BPF_MEMWORDS]; -static int curreg; - -/* - * Return the next free register. - */ -static int -alloc_reg() -{ - int n = BPF_MEMWORDS; - - while (--n >= 0) { - if (regused[curreg]) - curreg = (curreg + 1) % BPF_MEMWORDS; - else { - regused[curreg] = 1; - return curreg; - } - } - bpf_error("too many registers needed to evaluate expression"); - /* NOTREACHED */ - return 0; -} - -/* - * Return a register to the table so it can - * be used later. - */ -static void -free_reg(n) - int n; -{ - regused[n] = 0; -} - -static struct block * -gen_len(jmp, n) - int jmp, n; -{ - struct slist *s; - struct block *b; - - s = new_stmt(BPF_LD|BPF_LEN); - b = new_block(JMP(jmp)); - b->stmts = s; - b->s.k = n; - - return b; -} - -struct block * -gen_greater(n) - int n; -{ - return gen_len(BPF_JGE, n); -} - -/* - * Actually, this is less than or equal. - */ -struct block * -gen_less(n) - int n; -{ - struct block *b; - - b = gen_len(BPF_JGT, n); - gen_not(b); - - return b; -} - -/* - * 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; -{ - struct block *b; - struct slist *s; - - switch (op) { - default: - abort(); - - case '=': - return gen_cmp(OR_LINK, (u_int)idx, BPF_B, (bpf_int32)val); - - case '<': - b = gen_cmp_lt(OR_LINK, (u_int)idx, BPF_B, (bpf_int32)val); - return b; - - case '>': - b = gen_cmp_gt(OR_LINK, (u_int)idx, BPF_B, (bpf_int32)val); - return b; - - case '|': - s = new_stmt(BPF_ALU|BPF_OR|BPF_K); - break; - - case '&': - s = new_stmt(BPF_ALU|BPF_AND|BPF_K); - break; - } - s->s.k = val; - b = new_block(JMP(BPF_JEQ)); - b->stmts = s; - gen_not(b); - - return b; -} - -static u_char abroadcast[] = { 0x0 }; - -struct block * -gen_broadcast(proto) - int proto; -{ - bpf_u_int32 hostmask; - struct block *b0, *b1, *b2; - static u_char ebroadcast[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - - switch (proto) { - - case Q_DEFAULT: - case Q_LINK: - 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(OR_LINK, SUNATM_PKT_BEGIN_POS, BPF_H, - 0xFF00); - gen_not(b1); - - /* - * Now check the MAC address. - */ - b0 = gen_ehostop(ebroadcast, Q_DST); - gen_and(b1, b0); - return b0; - } - break; - default: - bpf_error("not a broadcast link"); - } - break; - - case Q_IP: - b0 = gen_linktype(ETHERTYPE_IP); - hostmask = ~netmask; - 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; -} - -/* - * Generate code to test the low-order bit of a MAC address (that's - * the bottom bit of the *first* byte). - */ -static struct block * -gen_mac_multicast(offset) - int offset; -{ - register struct block *b0; - register struct slist *s; - - /* link[offset] & 1 != 0 */ - s = gen_load_a(OR_LINK, offset, BPF_B); - b0 = new_block(JMP(BPF_JSET)); - b0->s.k = 1; - b0->stmts = s; - return b0; -} - -struct block * -gen_multicast(proto) - int proto; -{ - register struct block *b0, *b1, *b2; - register struct slist *s; - - switch (proto) { - - case Q_DEFAULT: - case Q_LINK: - 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(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; - } - break; - default: - break; - } - /* Link not known to support multicasts */ - break; - - case Q_IP: - b0 = gen_linktype(ETHERTYPE_IP); - 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(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; -} - -/* - * generate command for inbound/outbound. It's here so we can - * make it link-type specific. 'dir' = 0 implies "inbound", - * = 1 implies "outbound". - */ -struct block * -gen_inbound(dir) - int dir; -{ - register struct block *b0; - - /* - * Only some data link types support inbound/outbound qualifiers. - */ - switch (linktype) { - case DLT_SLIP: - b0 = gen_relation(BPF_JEQ, - gen_load(Q_LINK, gen_loadi(0), 1), - gen_loadi(0), - dir); - break; - - case DLT_LINUX_SLL: - if (dir) { - /* - * Match packets sent by this machine. - */ - b0 = gen_cmp(OR_LINK, 0, BPF_H, LINUX_SLL_OUTGOING); - } else { - /* - * Match packets sent to this machine. - * (No broadcast or multicast packets, or - * packets sent to some other machine and - * received promiscuously.) - * - * XXX - packets sent to other machines probably - * shouldn't be matched, but what about broadcast - * or multicast packets we received? - */ - b0 = gen_cmp(OR_LINK, 0, BPF_H, LINUX_SLL_HOST); - } - break; - - case DLT_PFLOG: - b0 = gen_cmp(OR_LINK, offsetof(struct pfloghdr, dir), BPF_B, - (bpf_int32)((dir == 0) ? PF_IN : PF_OUT)); - break; - - 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", - linktype); - b0 = NULL; - /* NOTREACHED */ - } - return (b0); -} - -/* PF firewall log matched interface */ -struct block * -gen_pf_ifname(const char *ifname) -{ - struct block *b0; - u_int len, off; - - if (linktype == DLT_PFLOG) { - len = sizeof(((struct pfloghdr *)0)->ifname); - off = offsetof(struct pfloghdr, ifname); - } else { - bpf_error("ifname not supported on linktype 0x%x", linktype); - /* NOTREACHED */ - } - if (strlen(ifname) >= len) { - bpf_error("ifname interface names can only be %d characters", - len-1); - /* NOTREACHED */ - } - b0 = gen_bcmp(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("srnr not supported on linktype 0x%x", linktype); - /* NOTREACHED */ - } - - 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) -{ - 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 (b0); -} - -/* PF firewall log action */ -struct block * -gen_pf_action(int action) -{ - 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 (b0); -} - -struct block * -gen_acode(eaddr, q) - register const u_char *eaddr; - struct qual q; -{ - if ((q.addr == Q_HOST || q.addr == Q_DEFAULT) && q.proto == Q_LINK) { - if (linktype == DLT_ARCNET || linktype == DLT_ARCNET_LINUX) - return gen_ahostop(eaddr, (int)q.dir); - } - bpf_error("ARCnet address used in non-arc expression"); - /* NOTREACHED */ - return NULL; -} - -static struct block * -gen_ahostop(eaddr, dir) - register const u_char *eaddr; - register int dir; -{ - register struct block *b0, *b1; - - switch (dir) { - /* src comes first, different from Ethernet */ - case Q_SRC: - return gen_bcmp(OR_LINK, 0, 1, eaddr); - - case Q_DST: - return gen_bcmp(OR_LINK, 1, 1, eaddr); - - case Q_AND: - b0 = gen_ahostop(eaddr, Q_SRC); - b1 = gen_ahostop(eaddr, Q_DST); - gen_and(b0, b1); - return b1; - - case Q_DEFAULT: - case Q_OR: - b0 = gen_ahostop(eaddr, Q_SRC); - b1 = gen_ahostop(eaddr, Q_DST); - gen_or(b0, b1); - return b1; - } - abort(); - /* NOTREACHED */ -} - -/* - * support IEEE 802.1Q VLAN trunk over ethernet - */ -struct block * -gen_vlan(vlan_num) - int vlan_num; -{ - struct block *b0, *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. 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...". - */ - orig_linktype = off_linktype; /* save original values */ - orig_nl = off_nl; - - switch (linktype) { - - 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*/ - } - - /* check for VLAN */ - 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) { - b1 = gen_mcmp(OR_LINK, orig_nl, BPF_H, (bpf_int32)vlan_num, - 0x0fff); - gen_and(b0, b1); - b0 = b1; - } - - return (b0); -} - -/* - * 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_int32 jvalue; - bpf_u_int32 jtype; - int reverse; -{ - struct block *b0; - - switch (atmfield) { - - case A_VPI: - if (!is_atm) - bpf_error("'vpi' supported only on raw ATM"); - if (off_vpi == (u_int)-1) - abort(); - b0 = gen_ncmp(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 == (u_int)-1) - abort(); - b0 = gen_ncmp(OR_LINK, off_vci, BPF_H, 0xffffffff, jtype, - reverse, jvalue); - break; - - case A_PROTOTYPE: - if (off_proto == (u_int)-1) - abort(); /* XXX - this isn't on FreeBSD */ - b0 = gen_ncmp(OR_LINK, off_proto, BPF_B, 0x0f, jtype, - reverse, jvalue); - break; - - case A_MSGTYPE: - if (off_payload == (u_int)-1) - abort(); - 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 == (u_int)-1) - abort(); - b0 = gen_ncmp(OR_LINK, off_proto, BPF_B, 0xffffffff, - jtype, reverse, jvalue); - break; - - default: - abort(); - } - return b0; -} - -struct block * -gen_atmtype_abbrev(type) - int type; -{ - struct block *b0, *b1; - - switch (type) { - - case A_METAC: - /* Get all packets in Meta signalling Circuit */ - if (!is_atm) - bpf_error("'metac' supported only on raw ATM"); - b0 = gen_atmfield_code(A_VPI, 0, BPF_JEQ, 0); - b1 = gen_atmfield_code(A_VCI, 1, BPF_JEQ, 0); - gen_and(b0, b1); - break; - - case A_BCC: - /* Get all packets in Broadcast Circuit*/ - if (!is_atm) - bpf_error("'bcc' supported only on raw ATM"); - b0 = gen_atmfield_code(A_VPI, 0, BPF_JEQ, 0); - b1 = gen_atmfield_code(A_VCI, 2, BPF_JEQ, 0); - gen_and(b0, b1); - break; - - case A_OAMF4SC: - /* Get all cells in Segment OAM F4 circuit*/ - if (!is_atm) - bpf_error("'oam4sc' supported only on raw ATM"); - b0 = gen_atmfield_code(A_VPI, 0, BPF_JEQ, 0); - b1 = gen_atmfield_code(A_VCI, 3, BPF_JEQ, 0); - gen_and(b0, b1); - break; - - case A_OAMF4EC: - /* Get all cells in End-to-End OAM F4 Circuit*/ - if (!is_atm) - bpf_error("'oam4ec' supported only on raw ATM"); - b0 = gen_atmfield_code(A_VPI, 0, BPF_JEQ, 0); - b1 = gen_atmfield_code(A_VCI, 4, BPF_JEQ, 0); - gen_and(b0, b1); - break; - - case A_SC: - /* Get all packets in connection Signalling Circuit */ - if (!is_atm) - bpf_error("'sc' supported only on raw ATM"); - b0 = gen_atmfield_code(A_VPI, 0, BPF_JEQ, 0); - b1 = gen_atmfield_code(A_VCI, 5, BPF_JEQ, 0); - gen_and(b0, b1); - break; - - case A_ILMIC: - /* Get all packets in ILMI Circuit */ - if (!is_atm) - bpf_error("'ilmic' supported only on raw ATM"); - b0 = gen_atmfield_code(A_VPI, 0, BPF_JEQ, 0); - b1 = gen_atmfield_code(A_VCI, 16, BPF_JEQ, 0); - gen_and(b0, b1); - break; - - case A_LANE: - /* Get all LANE packets */ - if (!is_atm) - bpf_error("'lane' supported only on raw ATM"); - b1 = gen_atmfield_code(A_PROTOTYPE, PT_LANE, BPF_JEQ, 0); - - /* - * Arrange that all subsequent tests assume LANE - * rather than LLC-encapsulated packets, and set - * the offsets appropriately for LANE-encapsulated - * Ethernet. - * - * "off_mac" is the offset of the Ethernet header, - * which is 2 bytes past the ATM pseudo-header - * (skipping the pseudo-header and 2-byte LE Client - * field). The other offsets are Ethernet offsets - * relative to "off_mac". - */ - is_lane = 1; - off_mac = off_payload + 2; /* MAC header */ - off_linktype = off_mac + 12; - off_nl = off_mac + 14; /* Ethernet II */ - off_nl_nosnap = off_mac + 17; /* 802.3+802.2 */ - break; - - case A_LLC: - /* Get all LLC-encapsulated packets */ - if (!is_atm) - bpf_error("'llc' supported only on raw ATM"); - b1 = gen_atmfield_code(A_PROTOTYPE, PT_LLC, BPF_JEQ, 0); - is_lane = 0; - break; - - default: - abort(); - } - return b1; -} - -/* - * 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) - int type; -{ - struct block *b1; - - /* - * Q.2931 signalling protocol messages for handling virtual circuits - * establishment and teardown - */ - switch (type) { - - case A_SETUP: - b1 = gen_atmfield_code(A_MSGTYPE, SETUP, BPF_JEQ, 0); - break; - - case A_CALLPROCEED: - b1 = gen_atmfield_code(A_MSGTYPE, CALL_PROCEED, BPF_JEQ, 0); - break; - - case A_CONNECT: - b1 = gen_atmfield_code(A_MSGTYPE, CONNECT, BPF_JEQ, 0); - break; - - case A_CONNECTACK: - b1 = gen_atmfield_code(A_MSGTYPE, CONNECT_ACK, BPF_JEQ, 0); - break; - - case A_RELEASE: - b1 = gen_atmfield_code(A_MSGTYPE, RELEASE, BPF_JEQ, 0); - break; - - case A_RELEASE_DONE: - b1 = gen_atmfield_code(A_MSGTYPE, RELEASE_DONE, BPF_JEQ, 0); - break; - - default: - abort(); - } - return b1; -} - -struct block * -gen_atmmulti_abbrev(type) - int type; -{ - struct block *b0, *b1; - - switch (type) { - - case A_OAM: - if (!is_atm) - bpf_error("'oam' supported only on raw ATM"); - b1 = gen_atmmulti_abbrev(A_OAMF4); - break; - - case A_OAMF4: - if (!is_atm) - bpf_error("'oamf4' supported only on raw ATM"); - /* OAM F4 type */ - b0 = gen_atmfield_code(A_VCI, 3, BPF_JEQ, 0); - b1 = gen_atmfield_code(A_VCI, 4, BPF_JEQ, 0); - gen_or(b0, b1); - b0 = gen_atmfield_code(A_VPI, 0, BPF_JEQ, 0); - gen_and(b0, b1); - break; - - case A_CONNECTMSG: - /* - * Get Q.2931 signalling messages for switched - * virtual connection - */ - if (!is_atm) - bpf_error("'connectmsg' supported only on raw ATM"); - b0 = gen_msg_abbrev(A_SETUP); - b1 = gen_msg_abbrev(A_CALLPROCEED); - gen_or(b0, b1); - b0 = gen_msg_abbrev(A_CONNECT); - gen_or(b0, b1); - b0 = gen_msg_abbrev(A_CONNECTACK); - gen_or(b0, b1); - b0 = gen_msg_abbrev(A_RELEASE); - gen_or(b0, b1); - b0 = gen_msg_abbrev(A_RELEASE_DONE); - gen_or(b0, b1); - b0 = gen_atmtype_abbrev(A_SC); - gen_and(b0, b1); - break; - - case A_METACONNECT: - if (!is_atm) - bpf_error("'metaconnect' supported only on raw ATM"); - b0 = gen_msg_abbrev(A_SETUP); - b1 = gen_msg_abbrev(A_CALLPROCEED); - gen_or(b0, b1); - b0 = gen_msg_abbrev(A_CONNECT); - gen_or(b0, b1); - b0 = gen_msg_abbrev(A_RELEASE); - gen_or(b0, b1); - b0 = gen_msg_abbrev(A_RELEASE_DONE); - gen_or(b0, b1); - b0 = gen_atmtype_abbrev(A_METAC); - gen_and(b0, b1); - break; - - default: - abort(); - } - return b1; -}