X-Git-Url: https://git.tcpdump.org/libpcap/blobdiff_plain/6aa0f48883bf69a5c8fb09532ec47edfb9750ee7..4e145de1143f597ef8277b51c5524af1e994f277:/gencode.c diff --git a/gencode.c b/gencode.c index d1326f70..5a91747d 100644 --- a/gencode.c +++ b/gencode.c @@ -21,33 +21,21 @@ */ #ifdef HAVE_CONFIG_H -#include "config.h" +#include #endif +#include #ifdef _WIN32 -#include -#else /* _WIN32 */ -#if HAVE_INTTYPES_H -#include -#elif HAVE_STDINT_H -#include -#endif -#ifdef HAVE_SYS_BITYPES_H -#include -#endif -#include -#include -#endif /* _WIN32 */ - -#ifndef _WIN32 - -#ifdef __NetBSD__ -#include -#endif + #include +#else + #include -#include -#include + #ifdef __NetBSD__ + #include + #endif + #include + #include #endif /* _WIN32 */ #include @@ -102,9 +90,9 @@ struct in6_addr { union { - u_int8_t u6_addr8[16]; - u_int16_t u6_addr16[8]; - u_int32_t u6_addr32[4]; + uint8_t u6_addr8[16]; + uint16_t u6_addr16[8]; + uint32_t u6_addr32[4]; } in6_u; #define s6_addr in6_u.u6_addr8 #define s6_addr16 in6_u.u6_addr16 @@ -121,8 +109,8 @@ typedef unsigned short sa_family_t; struct sockaddr_in6 { __SOCKADDR_COMMON (sin6_); - u_int16_t sin6_port; /* Transport layer port # */ - u_int32_t sin6_flowinfo; /* IPv6 flow information */ + uint16_t sin6_port; /* Transport layer port # */ + uint32_t sin6_flowinfo; /* IPv6 flow information */ struct in6_addr sin6_addr; /* IPv6 address */ }; @@ -357,6 +345,11 @@ struct _compiler_state { */ int is_geneve; + /* + * TRUE if we need variable length part of VLAN offset + */ + int is_vlan_vloffset; + /* * These are offsets for the ATM pseudo-header. */ @@ -523,7 +516,8 @@ static struct block *gen_host6(compiler_state_t *, 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); +static struct block *gen_gateway(compiler_state_t *, const u_char *, + bpf_u_int32 **, int, int); #endif static struct block *gen_ipfrag(compiler_state_t *); static struct block *gen_portatom(compiler_state_t *, int, bpf_int32); @@ -663,6 +657,9 @@ int pcap_compile(pcap_t *p, struct bpf_program *program, const char *buf, int optimize, bpf_u_int32 mask) { +#ifdef _WIN32 + static int done = 0; +#endif compiler_state_t cstate; const char * volatile xbuf = buf; yyscan_t scanner = NULL; @@ -670,14 +667,6 @@ pcap_compile(pcap_t *p, struct bpf_program *program, u_int len; int rc; -#ifdef _WIN32 - static int done = 0; - - if (!done) - pcap_wsockinit(); - done = 1; -#endif - /* * If this pcap_t hasn't been activated, it doesn't have a * link-layer type, so we can't use it. @@ -685,12 +674,41 @@ pcap_compile(pcap_t *p, struct bpf_program *program, if (!p->activated) { pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "not-yet-activated pcap_t passed to pcap_compile"); - rc = -1; - goto quit; + return (-1); } + +#ifdef _WIN32 + if (!done) + pcap_wsockinit(); + done = 1; +#endif + +#ifdef HAVE_REMOTE + /* + * If the device on which we're capturing need to be notified + * that a new filter is being compiled, do so. + * + * This allows them to save a copy of it, in case, for example, + * they're implementing a form of remote packet capture, and + * want the remote machine to filter out the packets in which + * it's sending the packets it's captured. + * + * XXX - the fact that we happen to be compiling a filter + * doesn't necessarily mean we'll be installing it as the + * filter for this pcap_t; we might be running it from userland + * on captured packets to do packet classification. We really + * need a better way of handling this, but this is all that + * the WinPcap code did. + */ + if (p->save_current_filter_op != NULL) + (p->save_current_filter_op)(p, buf); +#endif + initchunks(&cstate); cstate.no_optimize = 0; +#ifdef INET6 cstate.ai = NULL; +#endif cstate.ic.root = NULL; cstate.ic.cur_mark = 0; cstate.bpf_pcap = p; @@ -1071,6 +1089,11 @@ init_linktype(compiler_state_t *cstate, pcap_t *p) */ cstate->is_geneve = 0; + /* + * No variable length VLAN offset by default + */ + cstate->is_vlan_vloffset = 0; + /* * And assume we're not doing SS7. */ @@ -1994,6 +2017,41 @@ gen_ether_linktype(compiler_state_t *cstate, int proto) } } +static struct block * +gen_loopback_linktype(compiler_state_t *cstate, int proto) +{ + /* + * 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 + * word 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. + */ + if (cstate->linktype == DLT_NULL || cstate->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 (cstate->bpf_pcap->rfile != NULL && cstate->bpf_pcap->swapped) + proto = SWAPLONG(proto); + proto = htonl(proto); + } + return (gen_cmp(cstate, OR_LINKHDR, 0, BPF_W, (bpf_int32)proto)); +} + /* * "proto" is an Ethernet type value and for IPNET, if it is not IPv4 * or IPv6 then we have an error. @@ -2762,6 +2820,28 @@ insert_compute_vloffsets(compiler_state_t *cstate, struct block *b) break; } + /* + * If there there is no initialization yet and we need variable + * length offsets for VLAN, initialize them to zero + */ + if (s == NULL && cstate->is_vlan_vloffset) { + struct slist *s2; + + if (cstate->off_linkpl.reg == -1) + cstate->off_linkpl.reg = alloc_reg(cstate); + if (cstate->off_linktype.reg == -1) + cstate->off_linktype.reg = alloc_reg(cstate); + + s = new_stmt(cstate, BPF_LD|BPF_W|BPF_IMM); + s->s.k = 0; + s2 = new_stmt(cstate, BPF_ST); + s2->s.k = cstate->off_linkpl.reg; + sappend(s, s2); + s2 = new_stmt(cstate, BPF_ST); + s2->s.k = cstate->off_linktype.reg; + sappend(s, s2); + } + /* * If we have any offset-loading code, append all the * existing statements in the block to those statements, @@ -2930,6 +3010,14 @@ gen_prevlinkhdr_check(compiler_state_t *cstate) /*NOTREACHED*/ } +/* + * The three different values we should check for when checking for an + * IPv6 packet with DLT_NULL. + */ +#define BSD_AFNUM_INET6_BSD 24 /* NetBSD, OpenBSD, BSD/OS, Npcap */ +#define BSD_AFNUM_INET6_FREEBSD 28 /* FreeBSD */ +#define BSD_AFNUM_INET6_DARWIN 30 /* OS X, iOS, other Darwin-based OSes */ + /* * Generate code to match a particular packet type by matching the * link-layer type field or fields in the 802.2 LLC header. @@ -3150,39 +3238,71 @@ gen_linktype(compiler_state_t *cstate, int proto) 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; + return (gen_loopback_linktype(cstate, AF_INET)); -#ifdef INET6 case ETHERTYPE_IPV6: - proto = AF_INET6; - break; -#endif + /* + * AF_ values may, unfortunately, be platform- + * dependent; AF_INET isn't, because everybody + * used 4.2BSD's value, but AF_INET6 is, because + * 4.2BSD didn't have a value for it (given that + * IPv6 didn't exist back in the early 1980's), + * and they all picked their own values. + * + * This means that, if we're reading from a + * savefile, we need to check for all the + * possible values. + * + * If we're doing a live capture, we only need + * to check for this platform's value; however, + * Npcap uses 24, which isn't Windows's AF_INET6 + * value. (Given the multiple different values, + * programs that read pcap files shouldn't be + * checking for their platform's AF_INET6 value + * anyway, they should check for all of the + * possible values. and they might as well do + * that even for live captures.) + */ + if (cstate->bpf_pcap->rfile != NULL) { + /* + * Savefile - check for all three + * possible IPv6 values. + */ + b0 = gen_loopback_linktype(cstate, BSD_AFNUM_INET6_BSD); + b1 = gen_loopback_linktype(cstate, BSD_AFNUM_INET6_FREEBSD); + gen_or(b0, b1); + b0 = gen_loopback_linktype(cstate, BSD_AFNUM_INET6_DARWIN); + gen_or(b0, b1); + return (b1); + } else { + /* + * Live capture, so we only need to + * check for the value used on this + * platform. + */ +#ifdef _WIN32 + /* + * Npcap doesn't use Windows's AF_INET6, + * as that collides with AF_IPX on + * some BSDs (both have the value 23). + * Instead, it uses 24. + */ + return (gen_loopback_linktype(cstate, 24)); +#else /* _WIN32 */ +#ifdef AF_INET6 + return (gen_loopback_linktype(cstate, AF_INET6)); +#else /* AF_INET6 */ + /* + * I guess this platform doesn't support + * IPv6, so we just reject all packets. + */ + return gen_false(cstate); +#endif /* AF_INET6 */ +#endif /* _WIN32 */ + } default: /* @@ -3193,25 +3313,6 @@ gen_linktype(compiler_state_t *cstate, int proto) return gen_false(cstate); } - if (cstate->linktype == DLT_NULL || cstate->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 (cstate->bpf_pcap->rfile != NULL && cstate->bpf_pcap->swapped) - proto = SWAPLONG(proto); - proto = htonl(proto); - } - return (gen_cmp(cstate, OR_LINKHDR, 0, BPF_W, (bpf_int32)proto)); - #ifdef HAVE_NET_PFVAR_H case DLT_PFLOG: /* @@ -3397,8 +3498,7 @@ gen_linktype(compiler_state_t *cstate, int proto) bpf_error(cstate, "Bluetooth link-layer type filtering not implemented"); case DLT_CAN20B: - case DLT_CAN_SOCKETCAN_BIGENDIAN: - case DLT_CAN_SOCKETCAN_HOSTENDIAN: + case DLT_CAN_SOCKETCAN: bpf_error(cstate, "CAN link-layer type filtering not implemented"); case DLT_IEEE802_15_4: @@ -3788,7 +3888,7 @@ gen_hostop6(compiler_state_t *cstate, struct in6_addr *addr, { struct block *b0, *b1; u_int offset; - u_int32_t *a, *m; + uint32_t *a, *m; switch (dir) { @@ -3817,8 +3917,8 @@ gen_hostop6(compiler_state_t *cstate, struct in6_addr *addr, abort(); } /* this order is important */ - a = (u_int32_t *)addr; - m = (u_int32_t *)mask; + a = (uint32_t *)addr; + m = (uint32_t *)mask; b1 = gen_mcmp(cstate, OR_LINKPL, offset + 12, BPF_W, ntohl(a[3]), ntohl(m[3])); b0 = gen_mcmp(cstate, OR_LINKPL, offset + 8, BPF_W, ntohl(a[2]), ntohl(m[2])); gen_and(b0, b1); @@ -4847,11 +4947,8 @@ gen_host6(compiler_state_t *cstate, struct in6_addr *addr, #ifndef INET6 static struct block * -gen_gateway(eaddr, alist, proto, dir) - const u_char *eaddr; - bpf_u_int32 **alist; - int proto; - int dir; +gen_gateway(compiler_state_t *cstate, const u_char *eaddr, bpf_u_int32 **alist, + int proto, int dir) { struct block *b0, *b1, *tmp; @@ -6415,7 +6512,7 @@ gen_scode(compiler_state_t *cstate, const char *name, struct qual q) alist = pcap_nametoaddr(name); if (alist == NULL || *alist == NULL) bpf_error(cstate, "unknown host '%s'", name); - b = gen_gateway(eaddr, alist, proto, dir); + b = gen_gateway(cstate, eaddr, alist, proto, dir); free(eaddr); return b; #else @@ -6608,7 +6705,7 @@ gen_mcode6(compiler_state_t *cstate, const char *s1, const char *s2, struct in6_addr *addr; struct in6_addr mask; struct block *b; - u_int32_t *a, *m; + uint32_t *a, *m; if (s2) bpf_error(cstate, "no mask %s supported", s2); @@ -6630,8 +6727,8 @@ gen_mcode6(compiler_state_t *cstate, const char *s1, const char *s2, (0xff << (8 - masklen % 8)) & 0xff; } - a = (u_int32_t *)addr; - m = (u_int32_t *)&mask; + a = (uint32_t *)addr; + m = (uint32_t *)&mask; if ((a[0] & ~m[0]) || (a[1] & ~m[1]) || (a[2] & ~m[2]) || (a[3] & ~m[3])) { bpf_error(cstate, "non-network bits set in \"%s/%d\"", s1, masklen); @@ -7892,11 +7989,143 @@ gen_ahostop(compiler_state_t *cstate, const u_char *eaddr, int dir) /* NOTREACHED */ } +static struct block * +gen_vlan_tpid_test(compiler_state_t *cstate) +{ + struct block *b0, *b1; + + /* check for VLAN, including QinQ */ + b0 = gen_linktype(cstate, ETHERTYPE_8021Q); + b1 = gen_linktype(cstate, ETHERTYPE_8021AD); + gen_or(b0,b1); + b0 = b1; + b1 = gen_linktype(cstate, ETHERTYPE_8021QINQ); + gen_or(b0,b1); + + return b1; +} + +static struct block * +gen_vlan_vid_test(compiler_state_t *cstate, int vlan_num) +{ + return gen_mcmp(cstate, OR_LINKPL, 0, BPF_H, (bpf_int32)vlan_num, 0x0fff); +} + +static struct block * +gen_vlan_no_bpf_extensions(compiler_state_t *cstate, int vlan_num) +{ + struct block *b0, *b1; + + b0 = gen_vlan_tpid_test(cstate); + + if (vlan_num >= 0) { + b1 = gen_vlan_vid_test(cstate, vlan_num); + gen_and(b0, b1); + b0 = b1; + } + + /* + * Both payload and link header type follow the VLAN tags so that + * both need to be updated. + */ + cstate->off_linkpl.constant_part += 4; + cstate->off_linktype.constant_part += 4; + + return b0; +} + #if defined(SKF_AD_VLAN_TAG) && defined(SKF_AD_VLAN_TAG_PRESENT) +/* add v to variable part of off */ +static void +gen_vlan_vloffset_add(compiler_state_t *cstate, bpf_abs_offset *off, int v, struct slist *s) +{ + struct slist *s2; + + if (!off->is_variable) + off->is_variable = 1; + if (off->reg == -1) + off->reg = alloc_reg(cstate); + + s2 = new_stmt(cstate, BPF_LD|BPF_MEM); + s2->s.k = off->reg; + sappend(s, s2); + s2 = new_stmt(cstate, BPF_ALU|BPF_ADD|BPF_IMM); + s2->s.k = v; + sappend(s, s2); + s2 = new_stmt(cstate, BPF_ST); + s2->s.k = off->reg; + sappend(s, s2); +} + +/* + * patch block b_tpid (VLAN TPID test) to update variable parts of link payload + * and link type offsets first + */ +static void +gen_vlan_patch_tpid_test(compiler_state_t *cstate, struct block *b_tpid) +{ + struct slist s; + + /* offset determined at run time, shift variable part */ + s.next = NULL; + cstate->is_vlan_vloffset = 1; + gen_vlan_vloffset_add(cstate, &cstate->off_linkpl, 4, &s); + gen_vlan_vloffset_add(cstate, &cstate->off_linktype, 4, &s); + + /* we get a pointer to a chain of or-ed blocks, patch first of them */ + sappend(s.next, b_tpid->head->stmts); + b_tpid->head->stmts = s.next; +} + +/* + * patch block b_vid (VLAN id test) to load VID value either from packet + * metadata (using BPF extensions) if SKF_AD_VLAN_TAG_PRESENT is true + */ +static void +gen_vlan_patch_vid_test(compiler_state_t *cstate, struct block *b_vid) +{ + struct slist *s, *s2, *sjeq; + unsigned cnt; + + s = new_stmt(cstate, BPF_LD|BPF_B|BPF_ABS); + s->s.k = SKF_AD_OFF + SKF_AD_VLAN_TAG_PRESENT; + + /* true -> next instructions, false -> beginning of b_vid */ + sjeq = new_stmt(cstate, JMP(BPF_JEQ)); + sjeq->s.k = 1; + sjeq->s.jf = b_vid->stmts; + sappend(s, sjeq); + + s2 = new_stmt(cstate, BPF_LD|BPF_B|BPF_ABS); + s2->s.k = SKF_AD_OFF + SKF_AD_VLAN_TAG; + sappend(s, s2); + sjeq->s.jt = s2; + + /* jump to the test in b_vid (bypass loading VID from packet data) */ + cnt = 0; + for (s2 = b_vid->stmts; s2; s2 = s2->next) + cnt++; + s2 = new_stmt(cstate, JMP(BPF_JA)); + s2->s.k = cnt; + sappend(s, s2); + + /* insert our statements at the beginning of b_vid */ + sappend(s, b_vid->stmts); + b_vid->stmts = s; +} + +/* + * Generate check for "vlan" or "vlan " on systems with support for BPF + * extensions. Even if kernel supports VLAN BPF extensions, (outermost) VLAN + * tag can be either in metadata or in packet data; therefore if the + * SKF_AD_VLAN_TAG_PRESENT test is negative, we need to check link + * header for VLAN tag. As the decision is done at run time, we need + * update variable part of the offsets + */ static struct block * gen_vlan_bpf_extensions(compiler_state_t *cstate, int vlan_num) { - struct block *b0, *b1; + struct block *b0, *b_tpid, *b_vid; struct slist *s; /* generate new filter code based on extracting packet @@ -7908,58 +8137,33 @@ gen_vlan_bpf_extensions(compiler_state_t *cstate, int vlan_num) b0->stmts = s; b0->s.k = 1; - if (vlan_num >= 0) { - s = new_stmt(cstate, BPF_LD|BPF_B|BPF_ABS); - s->s.k = SKF_AD_OFF + SKF_AD_VLAN_TAG; - - b1 = new_block(cstate, JMP(BPF_JEQ)); - b1->stmts = s; - b1->s.k = (bpf_int32) vlan_num; - - gen_and(b0,b1); - b0 = b1; - } - - return b0; -} -#endif - -static struct block * -gen_vlan_no_bpf_extensions(compiler_state_t *cstate, int vlan_num) -{ - struct block *b0, *b1; - - /* check for VLAN, including QinQ */ - b0 = gen_linktype(cstate, ETHERTYPE_8021Q); - b1 = gen_linktype(cstate, ETHERTYPE_8021AD); - gen_or(b0,b1); - b0 = b1; - b1 = gen_linktype(cstate, ETHERTYPE_8021QINQ); - gen_or(b0,b1); - b0 = b1; - - /* If a specific VLAN is requested, check VLAN id */ - if (vlan_num >= 0) { - b1 = gen_mcmp(cstate, OR_LINKPL, 0, BPF_H, - (bpf_int32)vlan_num, 0x0fff); - gen_and(b0, b1); - b0 = b1; - } - /* - * The payload follows the full header, including the - * VLAN tags, so skip past this VLAN tag. + * This is tricky. We need to insert the statements updating variable + * parts of offsets before the the traditional TPID and VID tests so + * that they are called whenever SKF_AD_VLAN_TAG_PRESENT fails but + * we do not want this update to affect those checks. That's why we + * generate both test blocks first and insert the statements updating + * variable parts of both offsets after that. This wouldn't work if + * there already were variable length link header when entering this + * function but gen_vlan_bpf_extensions() isn't called in that case. */ - cstate->off_linkpl.constant_part += 4; - - /* - * The link-layer type information follows the VLAN tags, so - * skip past this VLAN tag. - */ - cstate->off_linktype.constant_part += 4; + b_tpid = gen_vlan_tpid_test(cstate); + if (vlan_num >= 0) + b_vid = gen_vlan_vid_test(cstate, vlan_num); + + gen_vlan_patch_tpid_test(cstate, b_tpid); + gen_or(b0, b_tpid); + b0 = b_tpid; + + if (vlan_num >= 0) { + gen_vlan_patch_vid_test(cstate, b_vid); + gen_and(b0, b_vid); + b0 = b_vid; + } return b0; } +#endif /* * support IEEE 802.1Q VLAN trunk over ethernet