X-Git-Url: https://git.tcpdump.org/libpcap/blobdiff_plain/24dcde9062f83c82a24de833e673206319384f4b..4e145de1143f597ef8277b51c5524af1e994f277:/gencode.c diff --git a/gencode.c b/gencode.c index 0cf2bfa6..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 */ }; @@ -193,6 +181,11 @@ struct addrinfo { (cs)->is_geneve = 0; \ } +/* + * Offset "not set" value. + */ +#define OFFSET_NOT_SET 0xffffffffU + /* * Absolute offsets, which are offsets from the beginning of the raw * packet data, are, in the general case, the sum of a variable value @@ -270,7 +263,7 @@ struct _compiler_state { u_int vlan_stack_depth; /* XXX */ - int pcap_fddipad; + u_int pcap_fddipad; #ifdef INET6 /* @@ -335,8 +328,8 @@ struct _compiler_state { * * For Linux cooked sockets, it's the offset of the type field. * - * off_linktype.constant_part is set to -1 for no encapsulation, - * in which case, IP is assumed. + * off_linktype.constant_part is set to OFFSET_NOT_SET for no + * encapsulation, in which case, IP is assumed. */ bpf_abs_offset off_linktype; @@ -352,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. */ @@ -518,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); @@ -658,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; @@ -665,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. @@ -680,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; @@ -1066,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. */ @@ -1107,7 +1135,7 @@ init_linktype(compiler_state_t *cstate, pcap_t *p) * SLIP doesn't have a link level type. The 16 byte * header is hacked into our SLIP driver. */ - cstate->off_linktype.constant_part = -1; + cstate->off_linktype.constant_part = OFFSET_NOT_SET; cstate->off_linkpl.constant_part = 16; cstate->off_nl = 0; cstate->off_nl_nosnap = 0; /* no 802.2 LLC */ @@ -1115,7 +1143,7 @@ init_linktype(compiler_state_t *cstate, pcap_t *p) case DLT_SLIP_BSDOS: /* XXX this may be the same as the DLT_PPP_BSDOS case */ - cstate->off_linktype.constant_part = -1; + cstate->off_linktype.constant_part = OFFSET_NOT_SET; /* XXX end */ cstate->off_linkpl.constant_part = 24; cstate->off_nl = 0; @@ -1301,7 +1329,7 @@ init_linktype(compiler_state_t *cstate, pcap_t *p) case DLT_RAW: case DLT_IPV4: case DLT_IPV6: - cstate->off_linktype.constant_part = -1; + cstate->off_linktype.constant_part = OFFSET_NOT_SET; cstate->off_linkpl.constant_part = 0; cstate->off_nl = 0; cstate->off_nl_nosnap = 0; /* no 802.2 LLC */ @@ -1320,7 +1348,7 @@ init_linktype(compiler_state_t *cstate, pcap_t *p) * but really it just indicates whether there is a "short" or * "long" DDP packet following. */ - cstate->off_linktype.constant_part = -1; + cstate->off_linktype.constant_part = OFFSET_NOT_SET; cstate->off_linkpl.constant_part = 0; cstate->off_nl = 0; cstate->off_nl_nosnap = 0; /* no 802.2 LLC */ @@ -1348,7 +1376,7 @@ init_linktype(compiler_state_t *cstate, pcap_t *p) * XXX - we should set this to handle SNAP-encapsulated * frames (NLPID of 0x80). */ - cstate->off_linktype.constant_part = -1; + cstate->off_linktype.constant_part = OFFSET_NOT_SET; cstate->off_linkpl.constant_part = 0; cstate->off_nl = 0; cstate->off_nl_nosnap = 0; /* no 802.2 LLC */ @@ -1360,7 +1388,7 @@ init_linktype(compiler_state_t *cstate, pcap_t *p) * so lets start with offset 4 for now and increments later on (FIXME); */ case DLT_MFR: - cstate->off_linktype.constant_part = -1; + cstate->off_linktype.constant_part = OFFSET_NOT_SET; cstate->off_linkpl.constant_part = 0; cstate->off_nl = 4; cstate->off_nl_nosnap = 0; /* XXX - for now -> no 802.2 LLC */ @@ -1441,7 +1469,7 @@ init_linktype(compiler_state_t *cstate, pcap_t *p) case DLT_JUNIPER_ES: cstate->off_linktype.constant_part = 6; - cstate->off_linkpl.constant_part = -1; /* not really a network layer but raw IP addresses */ + cstate->off_linkpl.constant_part = OFFSET_NOT_SET; /* not really a network layer but raw IP addresses */ cstate->off_nl = -1; /* not really a network layer but raw IP addresses */ cstate->off_nl_nosnap = -1; /* no 802.2 LLC */ break; @@ -1454,36 +1482,36 @@ init_linktype(compiler_state_t *cstate, pcap_t *p) break; case DLT_BACNET_MS_TP: - cstate->off_linktype.constant_part = -1; - cstate->off_linkpl.constant_part = -1; + cstate->off_linktype.constant_part = OFFSET_NOT_SET; + cstate->off_linkpl.constant_part = OFFSET_NOT_SET; cstate->off_nl = -1; cstate->off_nl_nosnap = -1; break; case DLT_JUNIPER_SERVICES: cstate->off_linktype.constant_part = 12; - cstate->off_linkpl.constant_part = -1; /* L3 proto location dep. on cookie type */ + cstate->off_linkpl.constant_part = OFFSET_NOT_SET; /* L3 proto location dep. on cookie type */ cstate->off_nl = -1; /* L3 proto location dep. on cookie type */ cstate->off_nl_nosnap = -1; /* no 802.2 LLC */ break; case DLT_JUNIPER_VP: cstate->off_linktype.constant_part = 18; - cstate->off_linkpl.constant_part = -1; + cstate->off_linkpl.constant_part = OFFSET_NOT_SET; cstate->off_nl = -1; cstate->off_nl_nosnap = -1; break; case DLT_JUNIPER_ST: cstate->off_linktype.constant_part = 18; - cstate->off_linkpl.constant_part = -1; + cstate->off_linkpl.constant_part = OFFSET_NOT_SET; cstate->off_nl = -1; cstate->off_nl_nosnap = -1; break; case DLT_JUNIPER_ISM: cstate->off_linktype.constant_part = 8; - cstate->off_linkpl.constant_part = -1; + cstate->off_linkpl.constant_part = OFFSET_NOT_SET; cstate->off_nl = -1; cstate->off_nl_nosnap = -1; break; @@ -1493,7 +1521,7 @@ init_linktype(compiler_state_t *cstate, pcap_t *p) case DLT_JUNIPER_FIBRECHANNEL: case DLT_JUNIPER_ATM_CEMIC: cstate->off_linktype.constant_part = 8; - cstate->off_linkpl.constant_part = -1; + cstate->off_linkpl.constant_part = OFFSET_NOT_SET; cstate->off_nl = -1; cstate->off_nl_nosnap = -1; break; @@ -1505,8 +1533,8 @@ init_linktype(compiler_state_t *cstate, pcap_t *p) cstate->off_opc = 4; cstate->off_dpc = 4; cstate->off_sls = 7; - cstate->off_linktype.constant_part = -1; - cstate->off_linkpl.constant_part = -1; + cstate->off_linktype.constant_part = OFFSET_NOT_SET; + cstate->off_linkpl.constant_part = OFFSET_NOT_SET; cstate->off_nl = -1; cstate->off_nl_nosnap = -1; break; @@ -1518,8 +1546,8 @@ init_linktype(compiler_state_t *cstate, pcap_t *p) cstate->off_opc = 8; cstate->off_dpc = 8; cstate->off_sls = 11; - cstate->off_linktype.constant_part = -1; - cstate->off_linkpl.constant_part = -1; + cstate->off_linktype.constant_part = OFFSET_NOT_SET; + cstate->off_linkpl.constant_part = OFFSET_NOT_SET; cstate->off_nl = -1; cstate->off_nl_nosnap = -1; break; @@ -1531,14 +1559,14 @@ init_linktype(compiler_state_t *cstate, pcap_t *p) cstate->off_opc = 24; cstate->off_dpc = 24; cstate->off_sls = 27; - cstate->off_linktype.constant_part = -1; - cstate->off_linkpl.constant_part = -1; + cstate->off_linktype.constant_part = OFFSET_NOT_SET; + cstate->off_linkpl.constant_part = OFFSET_NOT_SET; cstate->off_nl = -1; cstate->off_nl_nosnap = -1; break; case DLT_PFSYNC: - cstate->off_linktype.constant_part = -1; + cstate->off_linktype.constant_part = OFFSET_NOT_SET; cstate->off_linkpl.constant_part = 4; cstate->off_nl = 0; cstate->off_nl_nosnap = 0; @@ -1548,8 +1576,8 @@ init_linktype(compiler_state_t *cstate, pcap_t *p) /* * Currently, only raw "link[N:M]" filtering is supported. */ - cstate->off_linktype.constant_part = -1; /* variable, min 15, max 71 steps of 7 */ - cstate->off_linkpl.constant_part = -1; + cstate->off_linktype.constant_part = OFFSET_NOT_SET; /* variable, min 15, max 71 steps of 7 */ + cstate->off_linkpl.constant_part = OFFSET_NOT_SET; cstate->off_nl = -1; /* variable, min 16, max 71 steps of 7 */ cstate->off_nl_nosnap = -1; /* no 802.2 LLC */ break; @@ -1584,8 +1612,8 @@ init_linktype(compiler_state_t *cstate, pcap_t *p) */ if (cstate->linktype >= DLT_MATCHING_MIN && cstate->linktype <= DLT_MATCHING_MAX) { - cstate->off_linktype.constant_part = -1; - cstate->off_linkpl.constant_part = -1; + cstate->off_linktype.constant_part = OFFSET_NOT_SET; + cstate->off_linkpl.constant_part = OFFSET_NOT_SET; cstate->off_nl = -1; cstate->off_nl_nosnap = -1; } else { @@ -1989,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. @@ -2757,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, @@ -2925,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. @@ -3145,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: /* @@ -3188,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: /* @@ -3428,9 +3534,9 @@ gen_linktype(compiler_state_t *cstate, int proto) * Does this link-layer header type have a field * indicating the type of the next protocol? If * so, off_linktype.constant_part will be the offset of that - * field in the packet; if not, it will be -1. + * field in the packet; if not, it will be OFFSET_NOT_SET. */ - if (cstate->off_linktype.constant_part != (u_int)-1) { + if (cstate->off_linktype.constant_part != OFFSET_NOT_SET) { /* * Yes; assume it's an Ethernet type. (If * it's not, it needs to be handled specially @@ -3782,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) { @@ -3811,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); @@ -4841,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; @@ -6248,7 +6351,7 @@ gen_scode(compiler_state_t *cstate, const char *name, struct qual q) if (alist == NULL || *alist == NULL) bpf_error(cstate, "unknown host '%s'", name); tproto = proto; - if (cstate->off_linktype.constant_part == (u_int)-1 && + if (cstate->off_linktype.constant_part == OFFSET_NOT_SET && tproto == Q_DEFAULT) tproto = Q_IP; b = gen_host(cstate, **alist++, 0xffffffff, tproto, dir, q.addr); @@ -6267,7 +6370,7 @@ gen_scode(compiler_state_t *cstate, const char *name, struct qual q) cstate->ai = res; b = tmp = NULL; tproto = tproto6 = proto; - if (cstate->off_linktype.constant_part == -1 && + if (cstate->off_linktype.constant_part == OFFSET_NOT_SET && tproto == Q_DEFAULT) { tproto = Q_IP; tproto6 = Q_IPV6; @@ -6409,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 @@ -6602,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); @@ -6624,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); @@ -7886,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 @@ -7902,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