| /*#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.309 2008-12-23 20:13:29 guy Exp $ (LBL)"; |
| #endif |
| |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #endif |
| |
| #ifdef WIN32 |
| #include <pcap-stdinc.h> |
| #else /* WIN32 */ |
| #if HAVE_INTTYPES_H |
| #include <inttypes.h> |
| #elif HAVE_STDINT_H |
| #include <stdint.h> |
| #endif |
| #ifdef HAVE_SYS_BITYPES_H |
| #include <sys/bitypes.h> |
| #endif |
| #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> |
| #include <arpa/inet.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 "ieee80211.h" |
| #include "atmuni31.h" |
| #include "sunatmpos.h" |
| #include "ppp.h" |
| #include "pcap/sll.h" |
| #include "pcap/ipnet.h" |
| #include "arcnet.h" |
| #if defined(PF_PACKET) && defined(SO_ATTACH_FILTER) |
| #include <linux/types.h> |
| #include <linux/if_packet.h> |
| #include <linux/filter.h> |
| #endif |
| #ifdef HAVE_NET_PFVAR_H |
| #include <sys/socket.h> |
| #include <net/if.h> |
| #include <net/pfvar.h> |
| #include <net/if_pflog.h> |
| #endif |
| #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; |
| |
| /* Hack for updating VLAN, MPLS, and PPPoE offsets. */ |
| #ifdef WIN32 |
| 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 void init_regs(void); |
| static int alloc_reg(void); |
| static void free_reg(int); |
| |
| static struct block *root; |
| |
| /* |
| * Value passed to gen_load_a() to indicate what the offset argument |
| * is relative to. |
| */ |
| enum e_offrel { |
| OR_PACKET, /* relative to the beginning of the packet */ |
| OR_LINK, /* relative to the beginning of the link-layer header */ |
| OR_MACPL, /* relative to the end of the MAC-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 */ |
| }; |
| |
| #ifdef INET6 |
| /* |
| * As errors are handled by a longjmp, anything allocated must be freed |
| * in the longjmp handler, so it must be reachable from that handler. |
| * One thing that's allocated is the result of pcap_nametoaddrinfo(); |
| * it must be freed with freeaddrinfo(). This variable points to any |
| * addrinfo structure that would need to be freed. |
| */ |
| static struct addrinfo *ai; |
| #endif |
| |
| /* |
| * 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_macplrel(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_ipnet_linktype(int); |
| static struct block *gen_linux_sll_linktype(int); |
| static struct slist *gen_load_prism_llprefixlen(void); |
| static struct slist *gen_load_avs_llprefixlen(void); |
| static struct slist *gen_load_radiotap_llprefixlen(void); |
| static struct slist *gen_load_ppi_llprefixlen(void); |
| static void insert_compute_vloffsets(struct block *); |
| static struct slist *gen_llprefixlen(void); |
| static struct slist *gen_off_macpl(void); |
| static int ethertype_to_ppptype(int); |
| static struct block *gen_linktype(int); |
| static struct block *gen_snap(bpf_u_int32, bpf_u_int32); |
| 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_check_802_11_data_frame(void); |
| |
| 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; |
| #ifdef WIN32 |
| static int |
| pcap_compile_unsafe(pcap_t *p, struct bpf_program *program, |
| const char *buf, int optimize, bpf_u_int32 mask); |
| |
| int |
| pcap_compile(pcap_t *p, struct bpf_program *program, |
| const char *buf, int optimize, bpf_u_int32 mask) |
| { |
| int result; |
| |
| EnterCriticalSection(&g_PcapCompileCriticalSection); |
| |
| result = pcap_compile_unsafe(p, program, buf, optimize, mask); |
| |
| LeaveCriticalSection(&g_PcapCompileCriticalSection); |
| |
| return result; |
| } |
| |
| static int |
| pcap_compile_unsafe(pcap_t *p, struct bpf_program *program, |
| const char *buf, int optimize, bpf_u_int32 mask) |
| #else /* WIN32 */ |
| int |
| pcap_compile(pcap_t *p, struct bpf_program *program, |
| const char *buf, int optimize, bpf_u_int32 mask) |
| #endif /* WIN32 */ |
| { |
| extern int n_errors; |
| const char * volatile xbuf = buf; |
| u_int len; |
| |
| no_optimize = 0; |
| n_errors = 0; |
| root = NULL; |
| bpf_pcap = p; |
| init_regs(); |
| if (setjmp(top_ctx)) { |
| #ifdef INET6 |
| if (ai != NULL) { |
| freeaddrinfo(ai); |
| ai = NULL; |
| } |
| #endif |
| 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; |
| |
| /* |
| * 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, those strategies would be more complicated, and |
| * as we don't generate code to compute a length if the |
| * program has no tests that use the length, and as most |
| * tests will probably use those lengths, we would just |
| * postpone computing the lengths so that it's not done |
| * for tests that fail early, and it's not clear that's |
| * worth the effort. |
| */ |
| insert_compute_vloffsets(p->head); |
| |
| /* |
| * For DLT_PPI captures, generate a check of the per-packet |
| * DLT value to make sure it's DLT_IEEE802_11. |
| */ |
| 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; |
| } |
| |
| 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. |
| */ |
| |
| /* |
| * 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; |
| |
| /* |
| * If there's a variable-length header preceding the link-layer header, |
| * "reg_off_ll" is the register number for a register containing the |
| * length of that header, and therefore the offset of the link-layer |
| * header from the beginning of the raw packet data. Otherwise, |
| * "reg_off_ll" is -1. |
| */ |
| static int reg_off_ll; |
| |
| /* |
| * This is the offset of the beginning of the MAC-layer header from |
| * the beginning of the link-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, and |
| * for Ethernet with various additional information. |
| */ |
| static u_int off_mac; |
| |
| /* |
| * This is the offset of the beginning of the MAC-layer payload, |
| * from the beginning of the raw packet data. |
| * |
| * I.e., it's the sum of the length of the link-layer header (without, |
| * for example, any 802.2 LLC header, so it's the MAC-layer |
| * portion of that header), plus any prefix preceding the |
| * link-layer header. |
| */ |
| static u_int off_macpl; |
| |
| /* |
| * This is 1 if the offset of the beginning of the MAC-layer payload |
| * from the beginning of the link-layer header is variable-length. |
| */ |
| static int off_macpl_is_variable; |
| |
| /* |
| * If the link layer has variable_length headers, "reg_off_macpl" |
| * is the register number for a register containing the length of the |
| * link-layer header plus the length of any variable-length header |
| * preceding the link-layer header. Otherwise, "reg_off_macpl" |
| * is -1. |
| */ |
| static int reg_off_macpl; |
| |
| /* |
| * "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 "pppoes" appeared in the filter; it causes link-layer type |
| * checks to check the PPP header, assumed to follow a LAN-style link- |
| * layer header and a PPPoE session header. |
| */ |
| static int is_pppoes = 0; |
| |
| /* |
| * 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 MAC-layer payload (i.e., |
| * they don't include off_ll or off_macpl). |
| * |
| * 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 that we're not doing PPPoE. |
| */ |
| is_pppoes = 0; |
| |
| /* |
| * 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. |
| */ |
| off_ll = 0; |
| off_macpl = 0; |
| off_macpl_is_variable = 0; |
| |
| orig_linktype = -1; |
| orig_nl = -1; |
| label_stack_depth = 0; |
| |
| reg_off_ll = -1; |
| reg_off_macpl = -1; |
| |
| switch (linktype) { |
| |
| case DLT_ARCNET: |
| off_linktype = 2; |
| off_macpl = 6; |
| off_nl = 0; /* XXX in reality, variable! */ |
| off_nl_nosnap = 0; /* no 802.2 LLC */ |
| return; |
| |
| case DLT_ARCNET_LINUX: |
| off_linktype = 4; |
| off_macpl = 8; |
| off_nl = 0; /* XXX in reality, variable! */ |
| off_nl_nosnap = 0; /* no 802.2 LLC */ |
| return; |
| |
| case DLT_EN10MB: |
| off_linktype = 12; |
| off_macpl = 14; /* Ethernet header length */ |
| off_nl = 0; /* Ethernet II */ |
| off_nl_nosnap = 3; /* 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_macpl = 16; |
| off_nl = 0; |
| off_nl_nosnap = 0; /* 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_macpl = 24; |
| off_nl = 0; |
| off_nl_nosnap = 0; /* no 802.2 LLC */ |
| return; |
| |
| case DLT_NULL: |
| case DLT_LOOP: |
| off_linktype = 0; |
| off_macpl = 4; |
| off_nl = 0; |
| off_nl_nosnap = 0; /* no 802.2 LLC */ |
| return; |
| |
| case DLT_ENC: |
| off_linktype = 0; |
| off_macpl = 12; |
| off_nl = 0; |
| off_nl_nosnap = 0; /* 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_macpl = 4; |
| off_nl = 0; |
| off_nl_nosnap = 0; /* 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_macpl = 8; |
| off_nl = 0; |
| off_nl_nosnap = 0; /* no 802.2 LLC */ |
| return; |
| |
| case DLT_PPP_BSDOS: |
| off_linktype = 5; |
| off_macpl = 24; |
| off_nl = 0; |
| off_nl_nosnap = 0; /* 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_macpl = 13; /* FDDI MAC header length */ |
| #ifdef PCAP_FDDIPAD |
| off_macpl += pcap_fddipad; |
| #endif |
| off_nl = 8; /* 802.2+SNAP */ |
| off_nl_nosnap = 3; /* 802.2 */ |
| 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_macpl = 14; /* Token Ring MAC header length */ |
| off_nl = 8; /* 802.2+SNAP */ |
| off_nl_nosnap = 3; /* 802.2 */ |
| return; |
| |
| case DLT_IEEE802_11: |
| case DLT_PRISM_HEADER: |
| case DLT_IEEE802_11_RADIO_AVS: |
| case DLT_IEEE802_11_RADIO: |
| /* |
| * 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? |
| * |
| * We also handle variable-length radio headers here. |
| * The Prism header is in theory variable-length, but in |
| * practice it's always 144 bytes long. However, some |
| * drivers on Linux use ARPHRD_IEEE80211_PRISM, but |
| * sometimes or always supply an AVS header, so we |
| * have to check whether the radio header is a Prism |
| * header or an AVS header, so, in practice, it's |
| * variable-length. |
| */ |
| off_linktype = 24; |
| off_macpl = 0; /* link-layer header is variable-length */ |
| off_macpl_is_variable = 1; |
| off_nl = 8; /* 802.2+SNAP */ |
| off_nl_nosnap = 3; /* 802.2 */ |
| return; |
| |
| case DLT_PPI: |
| /* |
| * At the moment we treat PPI the same way that we treat |
| * 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. |
| */ |
| off_linktype = 24; |
| off_macpl = 0; /* link-layer header is variable-length */ |
| off_macpl_is_variable = 1; |
| off_nl = 8; /* 802.2+SNAP */ |
| off_nl_nosnap = 3; /* 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_macpl = 0; /* packet begins with LLC header */ |
| 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; /* assume LLC-encapsulated, so no MAC-layer header */ |
| off_payload = SUNATM_PKT_BEGIN_POS; |
| off_linktype = off_payload; |
| off_macpl = off_payload; /* if LLC-encapsulated */ |
| off_nl = 8; /* 802.2+SNAP */ |
| off_nl_nosnap = 3; /* 802.2 */ |
| return; |
| |
| case DLT_RAW: |
| case DLT_IPV4: |
| case DLT_IPV6: |
| off_linktype = -1; |
| off_macpl = 0; |
| 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_macpl = 16; |
| off_nl = 0; |
| off_nl_nosnap = 0; /* 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_macpl = 0; |
| 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_macpl = 16; |
| off_nl = 8; /* 802.2+SNAP */ |
| off_nl_nosnap = 3; /* 802.2 */ |
| return; |
| |
| case DLT_FRELAY: |
| /* |
| * XXX - we should set this to handle SNAP-encapsulated |
| * frames (NLPID of 0x80). |
| */ |
| off_linktype = -1; |
| off_macpl = 0; |
| 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_macpl = 0; |
| 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_macpl = 18; |
| off_nl = 0; |
| off_nl_nosnap = 0; /* no 802.2 LLC */ |
| return; |
| |
| case DLT_SYMANTEC_FIREWALL: |
| off_linktype = 6; |
| off_macpl = 44; |
| off_nl = 0; /* Ethernet II */ |
| off_nl_nosnap = 0; /* XXX - what does it do with 802.3 packets? */ |
| return; |
| |
| #ifdef HAVE_NET_PFVAR_H |
| case DLT_PFLOG: |
| off_linktype = 0; |
| off_macpl = PFLOG_HDRLEN; |
| off_nl = 0; |
| off_nl_nosnap = 0; /* no 802.2 LLC */ |
| return; |
| #endif |
| |
| case DLT_JUNIPER_MFR: |
| case DLT_JUNIPER_MLFR: |
| case DLT_JUNIPER_MLPPP: |
| case DLT_JUNIPER_PPP: |
| case DLT_JUNIPER_CHDLC: |
| case DLT_JUNIPER_FRELAY: |
| off_linktype = 4; |
| off_macpl = 4; |
| off_nl = 0; |
| off_nl_nosnap = -1; /* no 802.2 LLC */ |
| return; |
| |
| case DLT_JUNIPER_ATM1: |
| off_linktype = 4; /* in reality variable between 4-8 */ |
| off_macpl = 4; /* in reality variable between 4-8 */ |
| off_nl = 0; |
| off_nl_nosnap = 10; |
| return; |
| |
| case DLT_JUNIPER_ATM2: |
| off_linktype = 8; /* in reality variable between 8-12 */ |
| off_macpl = 8; /* in reality variable between 8-12 */ |
| off_nl = 0; |
| off_nl_nosnap = 10; |
| return; |
| |
| /* frames captured on a Juniper PPPoE service PIC |
| * contain raw ethernet frames */ |
| case DLT_JUNIPER_PPPOE: |
| case DLT_JUNIPER_ETHER: |
| off_macpl = 14; |
| 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_macpl = 6; |
| off_nl = 0; |
| off_nl_nosnap = -1; /* no 802.2 LLC */ |
| return; |
| |
| case DLT_JUNIPER_GGSN: |
| off_linktype = 6; |
| off_macpl = 12; |
| off_nl = 0; |
| off_nl_nosnap = -1; /* no 802.2 LLC */ |
| return; |
| |
| case DLT_JUNIPER_ES: |
| off_linktype = 6; |
| off_macpl = -1; /* not really a network layer but raw IP addresses */ |
| off_nl = -1; /* not really a network layer but raw IP addresses */ |
| off_nl_nosnap = -1; /* no 802.2 LLC */ |
| return; |
| |
| case DLT_JUNIPER_MONITOR: |
| off_linktype = 12; |
| off_macpl = 12; |
| off_nl = 0; /* raw IP/IP6 header */ |
| off_nl_nosnap = -1; /* no 802.2 LLC */ |
| return; |
| |
| case DLT_JUNIPER_SERVICES: |
| off_linktype = 12; |
| off_macpl = -1; /* L3 proto location dep. on cookie type */ |
| 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_macpl = -1; |
| off_nl = -1; |
| off_nl_nosnap = -1; |
| return; |
| |
| case DLT_JUNIPER_ST: |
| off_linktype = 18; |
| off_macpl = -1; |
| off_nl = -1; |
| off_nl_nosnap = -1; |
| return; |
| |
| case DLT_JUNIPER_ISM: |
| off_linktype = 8; |
| off_macpl = -1; |
| off_nl = -1; |
| off_nl_nosnap = -1; |
| return; |
| |
| case DLT_JUNIPER_VS: |
| case DLT_JUNIPER_SRX_E2E: |
| case DLT_JUNIPER_FIBRECHANNEL: |
| case DLT_JUNIPER_ATM_CEMIC: |
| off_linktype = 8; |
| off_macpl = -1; |
| 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_macpl = -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_macpl = -1; |
| off_nl = -1; |
| off_nl_nosnap = -1; |
| return; |
| |
| case DLT_ERF: |
| off_li = 22; |
| off_sio = 23; |
| off_opc = 24; |
| off_dpc = 24; |
| off_sls = 27; |
| off_linktype = -1; |
| off_macpl = -1; |
| off_nl = -1; |
| off_nl_nosnap = -1; |
| return; |
| |
| case DLT_PFSYNC: |
| off_linktype = -1; |
| off_macpl = 4; |
| off_nl = 0; |
| off_nl_nosnap = 0; |
| return; |
| |
| case DLT_AX25_KISS: |
| /* |
| * Currently, only raw "link[N:M]" filtering is supported. |
| */ |
| off_linktype = -1; /* variable, min 15, max 71 steps of 7 */ |
| off_macpl = -1; |
| off_nl = -1; /* variable, min 16, max 71 steps of 7 */ |
| off_nl_nosnap = -1; /* no 802.2 LLC */ |
| off_mac = 1; /* step over the kiss length byte */ |
| return; |
| |
| case DLT_IPNET: |
| off_linktype = 1; |
| off_macpl = 24; /* ipnet header length */ |
| off_nl = 0; |
| off_nl_nosnap = -1; |
| return; |
| |
| case DLT_NETANALYZER: |
| off_mac = 4; /* MAC header is past 4-byte pseudo-header */ |
| off_linktype = 16; /* includes 4-byte pseudo-header */ |
| off_macpl = 18; /* pseudo-header+Ethernet header length */ |
| off_nl = 0; /* Ethernet II */ |
| off_nl_nosnap = 3; /* 802.3+802.2 */ |
| return; |
| |
| case DLT_NETANALYZER_TRANSPARENT: |
| off_mac = 12; /* MAC header is past 4-byte pseudo-header, preamble, and SFD */ |
| off_linktype = 24; /* includes 4-byte pseudo-header+preamble+SFD */ |
| off_macpl = 26; /* pseudo-header+preamble+SFD+Ethernet header length */ |
| off_nl = 0; /* Ethernet II */ |
| off_nl_nosnap = 3; /* 802.3+802.2 */ |
| return; |
| |
| default: |
| /* |
| * For values in the range in which we've assigned new |
| * DLT_ values, only raw "link[N:M]" filtering is supported. |
| */ |
| if (linktype >= DLT_MATCHING_MIN && |
| linktype <= DLT_MATCHING_MAX) { |
| off_linktype = -1; |
| off_macpl = -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 MAC-layer payload. |
| */ |
| static struct slist * |
| gen_load_macplrel(offset, size) |
| u_int offset, size; |
| { |
| struct slist *s, *s2; |
| |
| s = gen_off_macpl(); |
| |
| /* |
| * If s is non-null, the offset of the MAC-layer payload is |
| * variable, and s points to a list of instructions that |
| * arrange that the X register contains that offset. |
| * |
| * Otherwise, the offset of the MAC-layer payload is constant, |
| * and is in off_macpl. |
| */ |
| if (s != NULL) { |
| /* |
| * The offset of the MAC-layer payload is in 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 { |
| /* |
| * The offset of the MAC-layer payload is constant, |
| * and is in off_macpl; load the value at that offset |
| * plus the specified offset. |
| */ |
| s = new_stmt(BPF_LD|BPF_ABS|size); |
| s->s.k = off_macpl + offset; |
| } |
| 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_MACPL: |
| s = gen_load_macplrel(offset, size); |
| break; |
| |
| case OR_NET: |
| s = gen_load_macplrel(off_nl + offset, size); |
| break; |
| |
| case OR_NET_NOSNAP: |
| s = gen_load_macplrel(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 MAC-layer payload} + |
| * {offset, relative to the start of the MAC-layer |
| * paylod, of the IPv4 header} + {length of the IPv4 header} + |
| * {specified offset}. |
| * |
| * (If the offset of the MAC-layer payload is variable, |
| * it's included in the value in the X register, and |
| * off_macpl is 0.) |
| */ |
| s2 = new_stmt(BPF_LD|BPF_IND|size); |
| s2->s.k = off_macpl + off_nl + offset; |
| sappend(s, s2); |
| break; |
| |
| case OR_TRAN_IPV6: |
| s = gen_load_macplrel(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_off_macpl(); |
| if (s != NULL) { |
| /* |
| * There's a variable-length prefix preceding the |
| * link-layer header, or the link-layer header is itself |
| * variable-length. "s" points to a list of statements |
| * that put the offset of the MAC-layer payload 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 offset of |
| * the MAC-layer payload, 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, and the link-layer header is |
| * fixed-length; load the length of the IPv4 header, |
| * which is at an offset of off_nl from the beginning |
| * of the MAC-layer payload, and thus at an offset |
| * of off_mac_pl + off_nl from the beginning of the |
| * raw packet data. |
| */ |
| s = new_stmt(BPF_LDX|BPF_MSH|BPF_B); |
| s->s.k = off_macpl + 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_MACPL, 0, 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_MACPL, 0, BPF_B, (bpf_int32)LLCSAP_IPX); |
| b1 = gen_cmp(OR_MACPL, 0, 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); |
| 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); |
| else /* proto == ETHERTYPE_AARP */ |
| b1 = gen_snap(0x000000, ETHERTYPE_AARP); |
| 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); |
| } |
| } |
| } |
| |
| /* |
| * "proto" is an Ethernet type value and for IPNET, if it is not IPv4 |
| * or IPv6 then we have an error. |
| */ |
| static struct block * |
| gen_ipnet_linktype(proto) |
| register int proto; |
| { |
| switch (proto) { |
| |
| case ETHERTYPE_IP: |
| return gen_cmp(OR_LINK, off_linktype, BPF_B, |
| (bpf_int32)IPH_AF_INET); |
| /* NOTREACHED */ |
| |
| case ETHERTYPE_IPV6: |
| return gen_cmp(OR_LINK, off_linktype, BPF_B, |
| (bpf_int32)IPH_AF_INET6); |
| /* NOTREACHED */ |
| |
| default: |
| break; |
| } |
| |
| return gen_false(); |
| } |
| |
| /* |
| * 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_MACPL, 0, 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_MACPL, 0, BPF_B, (bpf_int32)LLCSAP_IPX); |
| b1 = gen_snap(0x000000, ETHERTYPE_IPX); |
| 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); |
| else /* proto == ETHERTYPE_AARP */ |
| b1 = gen_snap(0x000000, ETHERTYPE_AARP); |
| 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_macpl, 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 struct slist * |
| gen_load_prism_llprefixlen() |
| { |
| struct slist *s1, *s2; |
| struct slist *sjeq_avs_cookie; |
| struct slist *sjcommon; |
| |
| /* |
| * This code is not compatible with the optimizer, as |
| * we are generating jmp instructions within a normal |
| * slist of instructions |
| */ |
| no_optimize = 1; |
| |
| /* |
| * Generate code to load the length of the radio header into |
| * the register assigned to hold that length, if one has been |
| * assigned. (If one hasn't been assigned, no code we've |
| * generated uses that prefix, so we don't need to generate any |
| * code to load it.) |
| * |
| * Some Linux drivers use ARPHRD_IEEE80211_PRISM but sometimes |
| * or always use the AVS header rather than the Prism header. |
| * We load a 4-byte big-endian value at the beginning of the |
| * raw packet data, and see whether, when masked with 0xFFFFF000, |
| * it's equal to 0x80211000. If so, that indicates that it's |
| * an AVS header (the masked-out bits are the version number). |
| * Otherwise, it's a Prism header. |
| * |
| * XXX - the Prism header is also, in theory, variable-length, |
| * but no known software generates headers that aren't 144 |
| * bytes long. |
| */ |
| if (reg_off_ll != -1) { |
| /* |
| * Load the cookie. |
| */ |
| s1 = new_stmt(BPF_LD|BPF_W|BPF_ABS); |
| s1->s.k = 0; |
| |
| /* |
| * AND it with 0xFFFFF000. |
| */ |
| s2 = new_stmt(BPF_ALU|BPF_AND|BPF_K); |
| s2->s.k = 0xFFFFF000; |
| sappend(s1, s2); |
| |
| /* |
| * Compare with 0x80211000. |
| */ |
| sjeq_avs_cookie = new_stmt(JMP(BPF_JEQ)); |
| sjeq_avs_cookie->s.k = 0x80211000; |
| sappend(s1, sjeq_avs_cookie); |
| |
| /* |
| * If it's AVS: |
| * |
| * The 4 bytes at an offset of 4 from the beginning of |
| * the AVS header are the length of the AVS header. |
| * That field is big-endian. |
| */ |
| s2 = new_stmt(BPF_LD|BPF_W|BPF_ABS); |
| s2->s.k = 4; |
| sappend(s1, s2); |
| sjeq_avs_cookie->s.jt = s2; |
| |
| /* |
| * Now jump to the code to allocate a register |
| * into which to save the header length and |
| * store the length there. (The "jump always" |
| * instruction needs to have the k field set; |
| * it's added to the PC, so, as we're jumping |
| * over a single instruction, it should be 1.) |
| */ |
| sjcommon = new_stmt(JMP(BPF_JA)); |
| sjcommon->s.k = 1; |
| sappend(s1, sjcommon); |
| |
| /* |
| * Now for the code that handles the Prism header. |
| * Just load the length of the Prism header (144) |
| * into the A register. Have the test for an AVS |
| * header branch here if we don't have an AVS header. |
| */ |
| s2 = new_stmt(BPF_LD|BPF_W|BPF_IMM); |
| s2->s.k = 144; |
| sappend(s1, s2); |
| sjeq_avs_cookie->s.jf = s2; |
| |
| /* |
| * Now allocate a register to hold that value and store |
| * it. The code for the AVS header will jump here after |
| * loading the length of the AVS header. |
| */ |
| s2 = new_stmt(BPF_ST); |
| s2->s.k = reg_off_ll; |
| sappend(s1, s2); |
| sjcommon->s.jf = s2; |
| |
| /* |
| * Now move it into the X register. |
| */ |
| s2 = new_stmt(BPF_MISC|BPF_TAX); |
| sappend(s1, s2); |
| |
| return (s1); |
| } else |
| return (NULL); |
| } |
| |
| static struct slist * |
| gen_load_avs_llprefixlen() |
| { |
| struct slist *s1, *s2; |
| |
| /* |
| * Generate code to load the length of the AVS header into |
| * the register assigned to hold that length, if one has been |
| * assigned. (If one hasn't been assigned, no code we've |
| * generated uses that prefix, so we don't need to generate any |
| * code to load it.) |
| */ |
| if (reg_off_ll != -1) { |
| /* |
| * The 4 bytes at an offset of 4 from the beginning of |
| * the AVS header are the length of the AVS header. |
| * That field is big-endian. |
| */ |
| s1 = new_stmt(BPF_LD|BPF_W|BPF_ABS); |
| s1->s.k = 4; |
| |
| /* |
| * Now allocate a register to hold that value and store |
| * it. |
| */ |
| s2 = new_stmt(BPF_ST); |
| s2->s.k = reg_off_ll; |
| sappend(s1, s2); |
| |
| /* |
| * Now move it into the X register. |
| */ |
| s2 = new_stmt(BPF_MISC|BPF_TAX); |
| sappend(s1, s2); |
| |
| return (s1); |
| } else |
| return (NULL); |
| } |
| |
| static struct slist * |
| gen_load_radiotap_llprefixlen() |
| { |
| struct slist *s1, *s2; |
| |
| /* |
| * Generate code to load the length of the radiotap header into |
| * the register assigned to hold that length, if one has been |
| * assigned. (If one hasn't been assigned, no code we've |
| * generated uses that prefix, so we don't need to generate any |
| * code to load it.) |
| */ |
| if (reg_off_ll != -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_off_ll; |
| sappend(s1, s2); |
| |
| /* |
| * Now move it into the X register. |
| */ |
| s2 = new_stmt(BPF_MISC|BPF_TAX); |
| sappend(s1, s2); |
| |
| return (s1); |
| } else |
| return (NULL); |
| } |
| |
| /* |
| * 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; |
| * that's done in finish_parse(). |
| */ |
| static struct slist * |
| gen_load_ppi_llprefixlen() |
| { |
| struct slist *s1, *s2; |
| |
| /* |
| * Generate code to load the length of the radiotap header |
| * into the register assigned to hold that length, if one has |
| * been assigned. |
| */ |
| if (reg_off_ll != -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_off_ll; |
| sappend(s1, s2); |
| |
| /* |
| * Now move it into the X register. |
| */ |
| s2 = new_stmt(BPF_MISC|BPF_TAX); |
| sappend(s1, s2); |
| |
| return (s1); |
| } else |
| return (NULL); |
| } |
| |
| /* |
| * Load a value relative to the beginning of the link-layer header after the 802.11 |
| * header, i.e. LLC_SNAP. |
| * 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_802_11_header_len(struct slist *s, struct slist *snext) |
| { |
| struct slist *s2; |
| struct slist *sjset_data_frame_1; |
| struct slist *sjset_data_frame_2; |
| struct slist *sjset_qos; |
| struct slist *sjset_radiotap_flags; |
| struct slist *sjset_radiotap_tsft; |
| struct slist *sjset_tsft_datapad, *sjset_notsft_datapad; |
| struct slist *s_roundup; |
| |
| if (reg_off_macpl == -1) { |
| /* |
| * No register has been assigned to the offset of |
| * the MAC-layer payload, which means nobody needs |
| * it; don't bother computing it - just return |
| * what we already have. |
| */ |
| return (s); |
| } |
| |
| /* |
| * This code is not compatible with the optimizer, as |
| * we are generating jmp instructions within a normal |
| * slist of instructions |
| */ |
| no_optimize = 1; |
| |
| /* |
| * If "s" is non-null, it has code to arrange that the X register |
| * contains the length of the prefix preceding the link-layer |
| * header. |
| * |
| * Otherwise, the length of the prefix preceding the link-layer |
| * header is "off_ll". |
| */ |
| if (s == NULL) { |
| /* |
| * There is no variable-length header preceding the |
| * link-layer header. |
| * |
| * Load the length of the fixed-length prefix preceding |
| * the link-layer header (if any) into the X register, |
| * and store it in the reg_off_macpl register. |
| * That length is off_ll. |
| */ |
| s = new_stmt(BPF_LDX|BPF_IMM); |
| s->s.k = off_ll; |
| } |
| |
| /* |
| * The X register contains the offset of the beginning of the |
| * link-layer header; add 24, which is the minimum length |
| * of the MAC header for a data frame, to that, and store it |
| * in reg_off_macpl, and then load the Frame Control field, |
| * which is at the offset in the X register, with an indexed load. |
| */ |
| s2 = new_stmt(BPF_MISC|BPF_TXA); |
| sappend(s, s2); |
| s2 = new_stmt(BPF_ALU|BPF_ADD|BPF_K); |
| s2->s.k = 24; |
| sappend(s, s2); |
| s2 = new_stmt(BPF_ST); |
| s2->s.k = reg_off_macpl; |
| sappend(s, s2); |
| |
| s2 = new_stmt(BPF_LD|BPF_IND|BPF_B); |
| s2->s.k = 0; |
| sappend(s, s2); |
| |
| /* |
| * Check the Frame Control field to see if this is a data frame; |
| * a data frame has the 0x08 bit (b3) in that field set and the |
| * 0x04 bit (b2) clear. |
| */ |
| sjset_data_frame_1 = new_stmt(JMP(BPF_JSET)); |
| sjset_data_frame_1->s.k = 0x08; |
| sappend(s, sjset_data_frame_1); |
| |
| /* |
| * If b3 is set, test b2, otherwise go to the first statement of |
| * the rest of the program. |
| */ |
| sjset_data_frame_1->s.jt = sjset_data_frame_2 = new_stmt(JMP(BPF_JSET)); |
| sjset_data_frame_2->s.k = 0x04; |
| sappend(s, sjset_data_frame_2); |
| sjset_data_frame_1->s.jf = snext; |
| |
| /* |
| * If b2 is not set, this is a data frame; test the QoS bit. |
| * Otherwise, go to the first statement of the rest of the |
| * program. |
| */ |
| sjset_data_frame_2->s.jt = snext; |
| sjset_data_frame_2->s.jf = sjset_qos = new_stmt(JMP(BPF_JSET)); |
| sjset_qos->s.k = 0x80; /* QoS bit */ |
| sappend(s, sjset_qos); |
| |
| /* |
| * If it's set, add 2 to reg_off_macpl, to skip the QoS |
| * field. |
| * Otherwise, go to the first statement of the rest of the |
| * program. |
| */ |
| sjset_qos->s.jt = s2 = new_stmt(BPF_LD|BPF_MEM); |
| s2->s.k = reg_off_macpl; |
| sappend(s, s2); |
| s2 = new_stmt(BPF_ALU|BPF_ADD|BPF_IMM); |
| s2->s.k = 2; |
| sappend(s, s2); |
| s2 = new_stmt(BPF_ST); |
| s2->s.k = reg_off_macpl; |
| sappend(s, s2); |
| |
| /* |
| * If we have a radiotap header, look at it to see whether |
| * there's Atheros padding between the MAC-layer header |
| * and the payload. |
| * |
| * Note: all of the fields in the radiotap header are |
| * little-endian, so we byte-swap all of the values |
| * we test against, as they will be loaded as big-endian |
| * values. |
| */ |
| if (linktype == DLT_IEEE802_11_RADIO) { |
| /* |
| * Is the IEEE80211_RADIOTAP_FLAGS bit (0x0000002) set |
| * in the presence flag? |
| */ |
| sjset_qos->s.jf = s2 = new_stmt(BPF_LD|BPF_ABS|BPF_W); |
| s2->s.k = 4; |
| sappend(s, s2); |
| |
| sjset_radiotap_flags = new_stmt(JMP(BPF_JSET)); |
| sjset_radiotap_flags->s.k = SWAPLONG(0x00000002); |
| sappend(s, sjset_radiotap_flags); |
| |
| /* |
| * If not, skip all of this. |
| */ |
| sjset_radiotap_flags->s.jf = snext; |
| |
| /* |
| * Otherwise, is the IEEE80211_RADIOTAP_TSFT bit set? |
| */ |
| sjset_radiotap_tsft = sjset_radiotap_flags->s.jt = |
| new_stmt(JMP(BPF_JSET)); |
| sjset_radiotap_tsft->s.k = SWAPLONG(0x00000001); |
| sappend(s, sjset_radiotap_tsft); |
| |
| /* |
| * If IEEE80211_RADIOTAP_TSFT is set, the flags field is |
| * at an offset of 16 from the beginning of the raw packet |
| * data (8 bytes for the radiotap header and 8 bytes for |
| * the TSFT field). |
| * |
| * Test whether the IEEE80211_RADIOTAP_F_DATAPAD bit (0x20) |
| * is set. |
| */ |
| sjset_radiotap_tsft->s.jt = s2 = new_stmt(BPF_LD|BPF_ABS|BPF_B); |
| s2->s.k = 16; |
| sappend(s, s2); |
| |
| sjset_tsft_datapad = new_stmt(JMP(BPF_JSET)); |
| sjset_tsft_datapad->s.k = 0x20; |
| sappend(s, sjset_tsft_datapad); |
| |
| /* |
| * If IEEE80211_RADIOTAP_TSFT is not set, the flags field is |
| * at an offset of 8 from the beginning of the raw packet |
| * data (8 bytes for the radiotap header). |
| * |
| * Test whether the IEEE80211_RADIOTAP_F_DATAPAD bit (0x20) |
| * is set. |
| */ |
| sjset_radiotap_tsft->s.jf = s2 = new_stmt(BPF_LD|BPF_ABS|BPF_B); |
| s2->s.k = 8; |
| sappend(s, s2); |
| |
| sjset_notsft_datapad = new_stmt(JMP(BPF_JSET)); |
| sjset_notsft_datapad->s.k = 0x20; |
| sappend(s, sjset_notsft_datapad); |
| |
| /* |
| * In either case, if IEEE80211_RADIOTAP_F_DATAPAD is |
| * set, round the length of the 802.11 header to |
| * a multiple of 4. Do that by adding 3 and then |
| * dividing by and multiplying by 4, which we do by |
| * ANDing with ~3. |
| */ |
| s_roundup = new_stmt(BPF_LD|BPF_MEM); |
| s_roundup->s.k = reg_off_macpl; |
| sappend(s, s_roundup); |
| s2 = new_stmt(BPF_ALU|BPF_ADD|BPF_IMM); |
| s2->s.k = 3; |
| sappend(s, s2); |
| s2 = new_stmt(BPF_ALU|BPF_AND|BPF_IMM); |
| s2->s.k = ~3; |
| sappend(s, s2); |
| s2 = new_stmt(BPF_ST); |
| s2->s.k = reg_off_macpl; |
| sappend(s, s2); |
| |
| sjset_tsft_datapad->s.jt = s_roundup; |
| sjset_tsft_datapad->s.jf = snext; |
| sjset_notsft_datapad->s.jt = s_roundup; |
| sjset_notsft_datapad->s.jf = snext; |
| } else |
| sjset_qos->s.jf = snext; |
| |
| return s; |
| } |
| |
| static void |
| insert_compute_vloffsets(b) |
| struct block *b; |
| { |
| struct slist *s; |
| |
| /* |
| * For link-layer types that have a variable-length header |
| * preceding the link-layer header, generate code to load |
| * the offset of the link-layer header into the register |
| * assigned to that offset, if any. |
| */ |
| switch (linktype) { |
| |
| case DLT_PRISM_HEADER: |
| s = gen_load_prism_llprefixlen(); |
| break; |
| |
| case DLT_IEEE802_11_RADIO_AVS: |
| s = gen_load_avs_llprefixlen(); |
| break; |
| |
| case DLT_IEEE802_11_RADIO: |
| s = gen_load_radiotap_llprefixlen(); |
| break; |
| |
| case DLT_PPI: |
| s = gen_load_ppi_llprefixlen(); |
| break; |
| |
| default: |
| s = NULL; |
| break; |
| } |
| |
| /* |
| * For link-layer types that have a variable-length link-layer |
| * header, generate code to load the offset of the MAC-layer |
| * payload into the register assigned to that offset, if any. |
| */ |
| switch (linktype) { |
| |
| case DLT_IEEE802_11: |
| case DLT_PRISM_HEADER: |
| case DLT_IEEE802_11_RADIO_AVS: |
| case DLT_IEEE802_11_RADIO: |
| case DLT_PPI: |
| s = gen_load_802_11_header_len(s, b->stmts); |
| break; |
| } |
| |
| /* |
| * If we have any offset-loading code, append all the |
| * existing statements in the block to those statements, |
| * and make the resulting list the list of statements |
| * for the block. |
| */ |
| if (s != NULL) { |
| sappend(s, b->stmts); |
| b->stmts = s; |
| } |
| } |
| |
| 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 struct slist * |
| gen_prism_llprefixlen(void) |
| { |
| struct slist *s; |
| |
| if (reg_off_ll == -1) { |
| /* |
| * We haven't yet assigned a register for the length |
| * of the radio header; allocate one. |
| */ |
| reg_off_ll = alloc_reg(); |
| } |
| |
| /* |
| * Load the register containing the radio length |
| * into the X register. |
| */ |
| s = new_stmt(BPF_LDX|BPF_MEM); |
| s->s.k = reg_off_ll; |
| return s; |
| } |
| |
| static struct slist * |
| gen_avs_llprefixlen(void) |
| { |
| struct slist *s; |
| |
| if (reg_off_ll == -1) { |
| /* |
| * We haven't yet assigned a register for the length |
| * of the AVS header; allocate one. |
| */ |
| reg_off_ll = alloc_reg(); |
| } |
| |
| /* |
| * Load the register containing the AVS length |
| * into the X register. |
| */ |
| s = new_stmt(BPF_LDX|BPF_MEM); |
| s->s.k = reg_off_ll; |
| return s; |
| } |
| |
| static struct slist * |
| gen_radiotap_llprefixlen(void) |
| { |
| struct slist *s; |
| |
| if (reg_off_ll == -1) { |
| /* |
| * We haven't yet assigned a register for the length |
| * of the radiotap header; allocate one. |
| */ |
| reg_off_ll = alloc_reg(); |
| } |
| |
| /* |
| * Load the register containing the radiotap length |
| * into the X register. |
| */ |
| s = new_stmt(BPF_LDX|BPF_MEM); |
| s->s.k = reg_off_ll; |
| 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_off_ll == -1) { |
| /* |
| * We haven't yet assigned a register for the length |
| * of the radiotap header; allocate one. |
| */ |
| reg_off_ll = alloc_reg(); |
| } |
| |
| /* |
| * Load the register containing the PPI length |
| * into the X register. |
| */ |
| s = new_stmt(BPF_LDX|BPF_MEM); |
| s->s.k = reg_off_ll; |
| 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_PRISM_HEADER: |
| return gen_prism_llprefixlen(); |
| |
| case DLT_IEEE802_11_RADIO_AVS: |
| return gen_avs_llprefixlen(); |
| |
| case DLT_IEEE802_11_RADIO: |
| return gen_radiotap_llprefixlen(); |
| |
| case DLT_PPI: |
| return gen_ppi_llprefixlen(); |
| |
| default: |
| return NULL; |
| } |
| } |
| |
| /* |
| * Generate code to load the register containing the offset of the |
| * MAC-layer payload into the X register; if no register for that offset |
| * has been allocated, allocate it first. |
| */ |
| static struct slist * |
| gen_off_macpl(void) |
| { |
| struct slist *s; |
| |
| if (off_macpl_is_variable) { |
| if (reg_off_macpl == -1) { |
| /* |
| * We haven't yet assigned a register for the offset |
| * of the MAC-layer payload; allocate one. |
| */ |
| reg_off_macpl = alloc_reg(); |
| } |
| |
| /* |
| * Load the register containing the offset of the MAC-layer |
| * payload into the X register. |
| */ |
| s = new_stmt(BPF_LDX|BPF_MEM); |
| s->s.k = reg_off_macpl; |
| return s; |
| } else { |
| /* |
| * That offset isn't variable, so we don't need to |
| * generate any code. |
| */ |
| return NULL; |
| } |
| } |
| |
| /* |
| * Map an Ethernet type to the equivalent PPP type. |
| */ |
| static int |
| ethertype_to_ppptype(proto) |
| int proto; |
| { |
| 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; |
| } |
| return (proto); |
| } |
| |
| /* |
| * 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 */ |
| } |
| } |
| |
| /* |
| * Are we testing PPPoE packets? |
| */ |
| if (is_pppoes) { |
| /* |
| * The PPPoE session header is part of the |
| * MAC-layer payload, so all references |
| * should be relative to the beginning of |
| * that payload. |
| */ |
| |
| /* |
| * We use Ethernet protocol types inside libpcap; |
| * map them to the corresponding PPP protocol types. |
| */ |
| proto = ethertype_to_ppptype(proto); |
| return gen_cmp(OR_MACPL, off_linktype, BPF_H, (bpf_int32)proto); |
| } |
| |
| switch (linktype) { |
| |
| case DLT_EN10MB: |
| case DLT_NETANALYZER: |
| case DLT_NETANALYZER_TRANSPARENT: |
| 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_IEEE802_11: |
| case DLT_PRISM_HEADER: |
| case DLT_IEEE802_11_RADIO_AVS: |
| case DLT_IEEE802_11_RADIO: |
| case DLT_PPI: |
| /* |
| * Check that we have a data frame. |
| */ |
| b0 = gen_check_802_11_data_frame(); |
| |
| /* |
| * Now check for the specified link-layer type. |
| */ |
| b1 = gen_llc_linktype(proto); |
| gen_and(b0, b1); |
| return b1; |
| /*NOTREACHED*/ |
| break; |
| |
| case DLT_FDDI: |
| /* |
| * XXX - check for asynchronous frames, as per RFC 1103. |
| */ |
| return gen_llc_linktype(proto); |
| /*NOTREACHED*/ |
| break; |
| |
| case DLT_IEEE802: |
| /* |
| * XXX - check for LLC PDUs, as per IEEE 802.5. |
| */ |
| return gen_llc_linktype(proto); |
| /*NOTREACHED*/ |
| break; |
| |
| 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_IPV4: |
| /* |
| * Raw IPv4, so no type field. |
| */ |
| if (proto == ETHERTYPE_IP) |
| return gen_true(); /* always true */ |
| |
| /* Checking for something other than IPv4; always false */ |
| return gen_false(); |
| /*NOTREACHED*/ |
| break; |
| |
| case DLT_IPV6: |
| /* |
| * Raw IPv6, so no type field. |
| */ |
| #ifdef INET6 |
| if (proto == ETHERTYPE_IPV6) |
| return gen_true(); /* always true */ |
| #endif |
| |
| /* Checking for something other than IPv6; always false */ |
| return gen_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. |
| */ |
| proto = ethertype_to_ppptype(proto); |
| return gen_cmp(OR_LINK, off_linktype, BPF_H, (bpf_int32)proto); |
| /*NOTREACHED*/ |
| 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: |
| /* |
| * Also check for Van Jacobson-compressed IP. |
| * XXX - do this for other forms of PPP? |
| */ |
| 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; |
| |
| default: |
| proto = ethertype_to_ppptype(proto); |
| return gen_cmp(OR_LINK, off_linktype, BPF_H, |
| (bpf_int32)proto); |
| } |
| /*NOTREACHED*/ |
| 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)); |
| |
| #ifdef HAVE_NET_PFVAR_H |
| case DLT_PFLOG: |
| /* |
| * af field is host byte order in contrast to the rest of |
| * the packet. |
| */ |
| if (proto == ETHERTYPE_IP) |
| return (gen_cmp(OR_LINK, offsetof(struct pfloghdr, af), |
| BPF_B, (bpf_int32)AF_INET)); |
| #ifdef INET6 |
| else if (proto == ETHERTYPE_IPV6) |
| return (gen_cmp(OR_LINK, offsetof(struct pfloghdr, af), |
| BPF_B, (bpf_int32)AF_INET6)); |
| #endif /* INET6 */ |
| else |
| return gen_false(); |
| /*NOTREACHED*/ |
| break; |
| #endif /* HAVE_NET_PFVAR_H */ |
| |
| case DLT_ARCNET: |
| case DLT_ARCNET_LINUX: |
| /* |
| * 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_MFR: |
| bpf_error("Multi-link Frame Relay link-layer type filtering not implemented"); |
| |
| 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: |
| case DLT_JUNIPER_ST: |
| case DLT_JUNIPER_ISM: |
| case DLT_JUNIPER_VS: |
| case DLT_JUNIPER_SRX_E2E: |
| case DLT_JUNIPER_FIBRECHANNEL: |
| case DLT_JUNIPER_ATM_CEMIC: |
| |
| /* 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_IPNET: |
| return gen_ipnet_linktype(proto); |
| |
| 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_MTP2: |
| case DLT_MTP2_WITH_PHDR: |
| bpf_error("MTP2 link-layer type filtering not implemented"); |
| |
| case DLT_ERF: |
| bpf_error("ERF link-layer type filtering not implemented"); |
| |
| case DLT_PFSYNC: |
| bpf_error("PFSYNC link-layer type filtering not implemented"); |
| |
| case DLT_LINUX_LAPD: |
| bpf_error("LAPD link-layer type filtering not implemented"); |
| |
| case DLT_USB: |
| case DLT_USB_LINUX: |
| case DLT_USB_LINUX_MMAPPED: |
| bpf_error("USB link-layer type filtering not implemented"); |
| |
| case DLT_BLUETOOTH_HCI_H4: |
| case DLT_BLUETOOTH_HCI_H4_WITH_PHDR: |
| bpf_error("Bluetooth link-layer type filtering not implemented"); |
| |
| case DLT_CAN20B: |
| case DLT_CAN_SOCKETCAN: |
| bpf_error("CAN link-layer type filtering not implemented"); |
| |
| case DLT_IEEE802_15_4: |
| case DLT_IEEE802_15_4_LINUX: |
| case DLT_IEEE802_15_4_NONASK_PHY: |
| case DLT_IEEE802_15_4_NOFCS: |
| bpf_error("IEEE 802.15.4 link-layer type filtering not implemented"); |
| |
| case DLT_IEEE802_16_MAC_CPS_RADIO: |
| bpf_error("IEEE 802.16 link-layer type filtering not implemented"); |
| |
| case DLT_SITA: |
| bpf_error("SITA link-layer type filtering not implemented"); |
| |
| case DLT_RAIF1: |
| bpf_error("RAIF1 link-layer type filtering not implemented"); |
| |
| case DLT_IPMB: |
| bpf_error("IPMB link-layer type filtering not implemented"); |
| |
| case DLT_AX25_KISS: |
| bpf_error("AX.25 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". |
| */ |
| 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) |
| bpf_u_int32 orgcode; |
| bpf_u_int32 ptype; |
| { |
| 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_MACPL, 0, 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. |
| |