*/
#ifdef HAVE_CONFIG_H
-#include "config.h"
+#include <config.h>
#endif
+#include <pcap-types.h>
#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 */
-
-#ifndef _WIN32
-
-#ifdef __NetBSD__
-#include <sys/param.h>
-#endif
+ #include <ws2tcpip.h>
+#else
+ #include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
+ #ifdef __NetBSD__
+ #include <sys/param.h>
+ #endif
+ #include <netinet/in.h>
+ #include <arpa/inet.h>
#endif /* _WIN32 */
#include <stdlib.h>
{
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
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 */
};
(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
u_int vlan_stack_depth;
/* XXX */
- int pcap_fddipad;
+ u_int pcap_fddipad;
#ifdef INET6
/*
*
* 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;
*/
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.
*/
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);
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;
int rc;
/*
- * XXX - single-thread this code path with pthread calls on
- * UN*X, if the platform supports pthreads? If that requires
- * a separate -lpthread, we might not want to do that.
+ * If this pcap_t hasn't been activated, it doesn't have a
+ * link-layer type, so we can't use it.
*/
-#ifdef _WIN32
- static int done = 0;
+ if (!p->activated) {
+ pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+ "not-yet-activated pcap_t passed to pcap_compile");
+ return (-1);
+ }
+#ifdef _WIN32
if (!done)
pcap_wsockinit();
done = 1;
#endif
+#ifdef HAVE_REMOTE
/*
- * If this pcap_t hasn't been activated, it doesn't have a
- * link-layer type, so we can't use it.
+ * 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->activated) {
- pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
- "not-yet-activated pcap_t passed to pcap_compile");
- rc = -1;
- goto quit;
- }
+ 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;
*/
cstate->is_geneve = 0;
+ /*
+ * No variable length VLAN offset by default
+ */
+ cstate->is_vlan_vloffset = 0;
+
/*
* And assume we're not doing SS7.
*/
* 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 */
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;
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 */
* 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 */
* 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 */
* 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 */
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;
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;
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;
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;
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;
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;
/*
* 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;
*/
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 {
}
}
+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.
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,
/*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.
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:
/*
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:
/*
case DLT_LINUX_LAPD:
bpf_error(cstate, "LAPD link-layer type filtering not implemented");
- case DLT_USB:
+ case DLT_USB_FREEBSD:
case DLT_USB_LINUX:
case DLT_USB_LINUX_MMAPPED:
+ case DLT_USBPCAP:
bpf_error(cstate, "USB link-layer type filtering not implemented");
case DLT_BLUETOOTH_HCI_H4:
* 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
{
struct block *b0, *b1;
u_int offset;
- u_int32_t *a, *m;
+ uint32_t *a, *m;
switch (dir) {
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);
#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;
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);
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;
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
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);
(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);
/* NOTREACHED */
}
-#if defined(SKF_AD_VLAN_TAG) && defined(SKF_AD_VLAN_TAG_PRESENT)
static struct block *
-gen_vlan_bpf_extensions(int vlan_num)
+gen_vlan_tpid_test(compiler_state_t *cstate)
{
- struct block *b0, *b1;
- struct slist *s;
-
- /* generate new filter code based on extracting packet
- * metadata */
- s = new_stmt(cstate, BPF_LD|BPF_B|BPF_ABS);
- s->s.k = SKF_AD_OFF + SKF_AD_VLAN_TAG_PRESENT;
-
- b0 = new_block(cstate, JMP(BPF_JEQ));
- 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;
+ struct block *b0, *b1;
- b1 = new_block(cstate, JMP(BPF_JEQ));
- b1->stmts = s;
- b1->s.k = (bpf_int32) vlan_num;
+ /* 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);
- gen_and(b0,b1);
- b0 = b1;
- }
+ return b1;
+}
- return b0;
+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);
}
-#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;
- }
+ 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;
+ }
/*
- * The payload follows the full header, including the
- * VLAN tags, so skip past this VLAN tag.
+ * 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_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 <id>" 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, *b_tpid, *b_vid;
+ struct slist *s;
+
+ /* generate new filter code based on extracting packet
+ * metadata */
+ s = new_stmt(cstate, BPF_LD|BPF_B|BPF_ABS);
+ s->s.k = SKF_AD_OFF + SKF_AD_VLAN_TAG_PRESENT;
+
+ b0 = new_block(cstate, JMP(BPF_JEQ));
+ b0->stmts = s;
+ b0->s.k = 1;
/*
- * The link-layer type information follows 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_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
* not encapsulated somehow. */
if (cstate->vlan_stack_depth == 0 && !cstate->off_linkhdr.is_variable &&
cstate->off_linkhdr.constant_part ==
- off_outermostlinkhdr.constant_part) {
+ cstate->off_outermostlinkhdr.constant_part) {
/*
* Do we need special VLAN handling?
*/
if (cstate->bpf_pcap->bpf_codegen_flags & BPF_SPECIAL_VLAN_HANDLING)
- b0 = gen_vlan_bpf_extensions(vlan_num);
+ b0 = gen_vlan_bpf_extensions(cstate, vlan_num);
else
b0 = gen_vlan_no_bpf_extensions(cstate, vlan_num);
} else