| /*#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 <pcap-stdinc.h> |
| #else /* WIN32 */ |
| #include <sys/types.h> |
| #include <sys/socket.h> |
| #endif /* WIN32 */ |
| |
| /* |
| * XXX - why was this included even on UNIX? |
| */ |
| #ifdef __MINGW32__ |
| #include "IP6_misc.h" |
| #endif |
| |
| #ifndef WIN32 |
| |
| #ifdef __NetBSD__ |
| #include <sys/param.h> |
| #endif |
| |
| #include <netinet/in.h> |
| |
| #endif /* WIN32 */ |
| |
| #include <stdlib.h> |
| #include <string.h> |
| #include <memory.h> |
| #include <setjmp.h> |
| #include <stdarg.h> |
| |
| #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 <netdb.h> /* for "struct addrinfo" */ |
| #endif /* WIN32 */ |
| #endif /*INET6*/ |
| #include <pcap-namedb.h> |
| |
| #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 |
| * <proto> and whether the type field in that protocol's header has |
| * the value <v>, e.g. if <proto> is Q_IP, it checks whether it's an |
| * IP packet and checks the protocol number in the IP header against <v>. |
| * |
| * If <proto> 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; |
| } |