-/*#define CHASE_CHAIN*/
/*
* Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998
* The Regents of the University of California. All rights reserved.
#include "ieee80211.h"
#include "atmuni31.h"
#include "sunatmpos.h"
+#include "pflog.h"
#include "ppp.h"
#include "pcap/sll.h"
#include "pcap/ipnet.h"
#include "arcnet.h"
+#include "diag-control.h"
-#include "grammar.h"
#include "scanner.h"
-#if defined(linux) && defined(PF_PACKET) && defined(SO_ATTACH_FILTER)
+#if defined(linux)
#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
#define ETHERMTU 1500
-#ifndef ETHERTYPE_TEB
-#define ETHERTYPE_TEB 0x6558
-#endif
-
#ifndef IPPROTO_HOPOPTS
#define IPPROTO_HOPOPTS 0
#endif
*/
if (!cstate->error_set) {
va_start(ap, fmt);
- (void)pcap_vsnprintf(cstate->bpf_pcap->errbuf, PCAP_ERRBUF_SIZE,
+ (void)vsnprintf(cstate->bpf_pcap->errbuf, PCAP_ERRBUF_SIZE,
fmt, ap);
va_end(ap);
cstate->error_set = 1;
va_list ap;
va_start(ap, fmt);
- (void)pcap_vsnprintf(cstate->bpf_pcap->errbuf, PCAP_ERRBUF_SIZE,
+ (void)vsnprintf(cstate->bpf_pcap->errbuf, PCAP_ERRBUF_SIZE,
fmt, ap);
va_end(ap);
longjmp(cstate->top_ctx, 1);
/*NOTREACHED*/
+#ifdef _AIX
+ PCAP_UNREACHABLE
+#endif /* _AIX */
}
static int init_linktype(compiler_state_t *, pcap_t *);
static struct block *gen_ether_linktype(compiler_state_t *, bpf_u_int32);
static struct block *gen_ipnet_linktype(compiler_state_t *, bpf_u_int32);
static struct block *gen_linux_sll_linktype(compiler_state_t *, bpf_u_int32);
+static struct slist *gen_load_pflog_llprefixlen(compiler_state_t *);
static struct slist *gen_load_prism_llprefixlen(compiler_state_t *);
static struct slist *gen_load_avs_llprefixlen(compiler_state_t *);
static struct slist *gen_load_radiotap_llprefixlen(compiler_state_t *);
bpf_u_int32, int);
static struct block *gen_portrange6(compiler_state_t *, u_int, u_int, int, int);
static int lookup_proto(compiler_state_t *, const char *, int);
+#if !defined(NO_PROTOCHAIN)
static struct block *gen_protochain(compiler_state_t *, bpf_u_int32, int);
+#endif /* !defined(NO_PROTOCHAIN) */
static struct block *gen_proto(compiler_state_t *, bpf_u_int32, int, int);
static struct slist *xfer_to_x(compiler_state_t *, struct arth *);
static struct slist *xfer_to_a(compiler_state_t *, struct arth *);
* link-layer type, so we can't use it.
*/
if (!p->activated) {
- pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+ snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
"not-yet-activated pcap_t passed to pcap_compile");
- return (-1);
+ return (PCAP_ERROR);
}
#ifdef _WIN32
cstate.snaplen = pcap_snapshot(p);
if (cstate.snaplen == 0) {
- pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+ snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
"snaplen of 0 rejects all packets");
- rc = -1;
+ rc = PCAP_ERROR;
goto quit;
}
pcap_set_extra(&cstate, scanner);
if (init_linktype(&cstate, p) == -1) {
- rc = -1;
+ rc = PCAP_ERROR;
goto quit;
}
if (pcap_parse(scanner, &cstate) != 0) {
#endif
if (cstate.e != NULL)
free(cstate.e);
- rc = -1;
+ rc = PCAP_ERROR;
goto quit;
}
* Catch errors reported by gen_retblk().
*/
if (setjmp(cstate.top_ctx)) {
- rc = -1;
+ rc = PCAP_ERROR;
goto quit;
}
cstate.ic.root = gen_retblk(&cstate, cstate.snaplen);
if (optimize && !cstate.no_optimize) {
if (bpf_optimize(&cstate.ic, p->errbuf) == -1) {
/* Failure */
- rc = -1;
+ rc = PCAP_ERROR;
goto quit;
}
if (cstate.ic.root == NULL ||
(cstate.ic.root->s.code == (BPF_RET|BPF_K) && cstate.ic.root->s.k == 0)) {
- (void)pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+ (void)snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
"expression rejects all packets");
- rc = -1;
+ rc = PCAP_ERROR;
goto quit;
}
}
cstate.ic.root, &len, p->errbuf);
if (program->bf_insns == NULL) {
/* Failure */
- rc = -1;
+ rc = PCAP_ERROR;
goto quit;
}
program->bf_len = len;
p = pcap_open_dead(linktype_arg, snaplen_arg);
if (p == NULL)
- return (-1);
+ return (PCAP_ERROR);
ret = pcap_compile(p, program, buf, optimize, mask);
pcap_close(p);
return (ret);
case DLT_PPP:
case DLT_PPP_PPPD:
case DLT_C_HDLC: /* BSD/OS Cisco HDLC */
+ case DLT_HDLC: /* NetBSD (Cisco) HDLC */
case DLT_PPP_SERIAL: /* NetBSD sync/async serial PPP */
cstate->off_linktype.constant_part = 2; /* skip HDLC-like framing */
cstate->off_linkpl.constant_part = 4; /* skip HDLC-like framing and protocol field */
cstate->off_nl_nosnap = 0; /* XXX - what does it do with 802.3 packets? */
break;
-#ifdef HAVE_NET_PFVAR_H
case DLT_PFLOG:
cstate->off_linktype.constant_part = 0;
- cstate->off_linkpl.constant_part = PFLOG_HDRLEN;
+ cstate->off_linkpl.constant_part = 0; /* link-layer header is variable-length */
+ cstate->off_linkpl.is_variable = 1;
cstate->off_nl = 0;
cstate->off_nl_nosnap = 0; /* no 802.2 LLC */
break;
-#endif
case DLT_JUNIPER_MFR:
case DLT_JUNIPER_MLFR:
cstate->off_nl = OFFSET_NOT_SET;
cstate->off_nl_nosnap = OFFSET_NOT_SET;
} else {
- bpf_set_error(cstate, "unknown data link type %d", cstate->linktype);
+ bpf_set_error(cstate, "unknown data link type %d (min %d, max %d)",
+ cstate->linktype, DLT_MATCHING_MIN, DLT_MATCHING_MAX);
return (-1);
}
break;
}
}
+/*
+ * Load a value relative to the beginning of the link-layer header after the
+ * pflog header.
+ */
+static struct slist *
+gen_load_pflog_llprefixlen(compiler_state_t *cstate)
+{
+ struct slist *s1, *s2;
+
+ /*
+ * Generate code to load the length of the pflog 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 (cstate->off_linkpl.reg != -1) {
+ /*
+ * The length is in the first byte of the header.
+ */
+ s1 = new_stmt(cstate, BPF_LD|BPF_B|BPF_ABS);
+ s1->s.k = 0;
+
+ /*
+ * Round it up to a multiple of 4.
+ * Add 3, and clear the lower 2 bits.
+ */
+ s2 = new_stmt(cstate, BPF_ALU|BPF_ADD|BPF_K);
+ s2->s.k = 3;
+ sappend(s1, s2);
+ s2 = new_stmt(cstate, BPF_ALU|BPF_AND|BPF_K);
+ s2->s.k = 0xfffffffc;
+ sappend(s1, s2);
+
+ /*
+ * Now allocate a register to hold that value and store
+ * it.
+ */
+ s2 = new_stmt(cstate, BPF_ST);
+ s2->s.k = cstate->off_linkpl.reg;
+ sappend(s1, s2);
+
+ /*
+ * Now move it into the X register.
+ */
+ s2 = new_stmt(cstate, BPF_MISC|BPF_TAX);
+ sappend(s1, s2);
+
+ return (s1);
+ } else
+ return (NULL);
+}
+
static struct slist *
gen_load_prism_llprefixlen(compiler_state_t *cstate)
{
case DLT_PPI:
s = gen_load_802_11_header_len(cstate, s, b->stmts);
break;
+
+ case DLT_PFLOG:
+ s = gen_load_pflog_llprefixlen(cstate);
+ break;
}
/*
- * If there there is no initialization yet and we need variable
+ * If there is no initialization yet and we need variable
* length offsets for VLAN, initialize them to zero
*/
if (s == NULL && cstate->is_vlan_vloffset) {
/*NOTREACHED*/
case DLT_C_HDLC:
+ case DLT_HDLC:
switch (ll_proto) {
case LLCSAP_ISONS:
return gen_false(cstate);
}
-#ifdef HAVE_NET_PFVAR_H
case DLT_PFLOG:
/*
* af field is host byte order in contrast to the rest of
else
return gen_false(cstate);
/*NOTREACHED*/
-#endif /* HAVE_NET_PFVAR_H */
case DLT_ARCNET:
case DLT_ARCNET_LINUX:
switch (proto) {
case Q_SCTP:
- b1 = gen_proto(cstate, IPPROTO_SCTP, Q_IP, Q_DEFAULT);
- b0 = gen_proto(cstate, IPPROTO_SCTP, Q_IPV6, Q_DEFAULT);
- gen_or(b0, b1);
+ b1 = gen_proto(cstate, IPPROTO_SCTP, Q_DEFAULT, Q_DEFAULT);
break;
case Q_TCP:
- b1 = gen_proto(cstate, IPPROTO_TCP, Q_IP, Q_DEFAULT);
- b0 = gen_proto(cstate, IPPROTO_TCP, Q_IPV6, Q_DEFAULT);
- gen_or(b0, b1);
+ b1 = gen_proto(cstate, IPPROTO_TCP, Q_DEFAULT, Q_DEFAULT);
break;
case Q_UDP:
- b1 = gen_proto(cstate, IPPROTO_UDP, Q_IP, Q_DEFAULT);
- b0 = gen_proto(cstate, IPPROTO_UDP, Q_IPV6, Q_DEFAULT);
- gen_or(b0, b1);
+ b1 = gen_proto(cstate, IPPROTO_UDP, Q_DEFAULT, Q_DEFAULT);
break;
case Q_ICMP:
#endif
case Q_PIM:
- b1 = gen_proto(cstate, IPPROTO_PIM, Q_IP, Q_DEFAULT);
- b0 = gen_proto(cstate, IPPROTO_PIM, Q_IPV6, Q_DEFAULT);
- gen_or(b0, b1);
+ b1 = gen_proto(cstate, IPPROTO_PIM, Q_DEFAULT, Q_DEFAULT);
break;
#ifndef IPPROTO_VRRP
#define IPPROTO_AH 51
#endif
case Q_AH:
- b1 = gen_proto(cstate, IPPROTO_AH, Q_IP, Q_DEFAULT);
- b0 = gen_proto(cstate, IPPROTO_AH, Q_IPV6, Q_DEFAULT);
- gen_or(b0, b1);
+ b1 = gen_proto(cstate, IPPROTO_AH, Q_DEFAULT, Q_DEFAULT);
break;
#ifndef IPPROTO_ESP
#define IPPROTO_ESP 50
#endif
case Q_ESP:
- b1 = gen_proto(cstate, IPPROTO_ESP, Q_IP, Q_DEFAULT);
- b0 = gen_proto(cstate, IPPROTO_ESP, Q_IPV6, Q_DEFAULT);
- gen_or(b0, b1);
+ b1 = gen_proto(cstate, IPPROTO_ESP, Q_DEFAULT, Q_DEFAULT);
break;
case Q_ISO:
return v;
}
-#if 0
-struct stmt *
-gen_joinsp(struct stmt **s, int n)
-{
- return NULL;
-}
-#endif
-
+#if !defined(NO_PROTOCHAIN)
static struct block *
gen_protochain(compiler_state_t *cstate, bpf_u_int32 v, int proto)
{
-#ifdef NO_PROTOCHAIN
- return gen_proto(cstate, v, proto);
-#else
struct block *b0, *b;
struct slist *s[100];
int fix2, fix3, fix4, fix5;
if (cstate->off_linkpl.is_variable)
bpf_error(cstate, "'protochain' not supported with variable length headers");
- cstate->no_optimize = 1; /* this code is not compatible with optimizer yet */
+ /*
+ * To quote a comment in optimize.c:
+ *
+ * "These data structures are used in a Cocke and Shwarz style
+ * value numbering scheme. Since the flowgraph is acyclic,
+ * exit values can be propagated from a node's predecessors
+ * provided it is uniquely defined."
+ *
+ * "Acyclic" means "no backward branches", which means "no
+ * loops", so we have to turn the optimizer off.
+ */
+ cstate->no_optimize = 1;
/*
* s[0] is a dummy entry to protect other BPF insn from damage
gen_and(b0, b);
return b;
-#endif
}
+#endif /* !defined(NO_PROTOCHAIN) */
static struct block *
gen_check_802_11_data_frame(compiler_state_t *cstate)
gen_proto(compiler_state_t *cstate, bpf_u_int32 v, int proto, int dir)
{
struct block *b0, *b1;
-#ifndef CHASE_CHAIN
struct block *b2;
-#endif
if (dir != Q_DEFAULT)
bpf_error(cstate, "direction applied to 'proto'");
* So we always check for ETHERTYPE_IP.
*/
b0 = gen_linktype(cstate, ETHERTYPE_IP);
-#ifndef CHASE_CHAIN
b1 = gen_cmp(cstate, OR_LINKPL, 9, BPF_B, v);
-#else
- b1 = gen_protochain(cstate, v, Q_IP);
-#endif
gen_and(b0, b1);
return b1;
case Q_IPV6:
b0 = gen_linktype(cstate, ETHERTYPE_IPV6);
-#ifndef CHASE_CHAIN
/*
* Also check for a fragment header before the final
* header.
gen_and(b2, b1);
b2 = gen_cmp(cstate, OR_LINKPL, 6, BPF_B, v);
gen_or(b2, b1);
-#else
- b1 = gen_protochain(cstate, v, Q_IPV6);
-#endif
gen_and(b0, b1);
return b1;
/*NOTREACHED*/
case Q_ESP:
- bpf_error(cstate, "'ah proto' is bogus");
+ bpf_error(cstate, "'esp proto' is bogus");
/*NOTREACHED*/
case Q_PIM:
/*NOTREACHED*/
case DLT_C_HDLC:
+ case DLT_HDLC:
/*
* Cisco uses an Ethertype lookalike - for OSI,
* it's 0xfefe.
else
bpf_error(cstate, "unknown protocol: %s", name);
+#if !defined(NO_PROTOCHAIN)
case Q_PROTOCHAIN:
real_proto = lookup_proto(cstate, name, proto);
if (real_proto >= 0)
return gen_protochain(cstate, real_proto, proto);
else
bpf_error(cstate, "unknown protocol: %s", name);
+#endif /* !defined(NO_PROTOCHAIN) */
case Q_UNDEF:
syntax(cstate);
return (NULL);
nlen = __pcap_atoin(s1, &n);
+ if (nlen < 0)
+ bpf_error(cstate, "invalid IPv4 address '%s'", s1);
/* Promote short ipaddr */
n <<= 32 - nlen;
if (s2 != NULL) {
mlen = __pcap_atoin(s2, &m);
+ if (mlen < 0)
+ bpf_error(cstate, "invalid IPv4 address '%s'", s2);
/* Promote short ipaddr */
m <<= 32 - mlen;
if ((n & ~m) != 0)
vlen = __pcap_atodn(s, &v);
if (vlen == 0)
bpf_error(cstate, "malformed decnet address '%s'", s);
- } else
+ } else {
vlen = __pcap_atoin(s, &v);
+ if (vlen < 0)
+ bpf_error(cstate, "invalid IPv4 address '%s'", s);
+ }
switch (q.addr) {
case Q_PROTO:
return gen_proto(cstate, v, proto, dir);
+#if !defined(NO_PROTOCHAIN)
case Q_PROTOCHAIN:
return gen_protochain(cstate, v, proto);
+#endif
case Q_UNDEF:
syntax(cstate);
bpf_error(cstate, "%s resolved to multiple address", s1);
addr = &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
- if (sizeof(mask) * 8 < masklen)
- bpf_error(cstate, "mask length must be <= %u", (unsigned int)(sizeof(mask) * 8));
+ if (masklen > sizeof(mask.s6_addr) * 8)
+ bpf_error(cstate, "mask length must be <= %u", (unsigned int)(sizeof(mask.s6_addr) * 8));
memset(&mask, 0, sizeof(mask));
- memset(&mask, 0xff, masklen / 8);
+ memset(&mask.s6_addr, 0xff, masklen / 8);
if (masklen % 8) {
mask.s6_addr[masklen / 8] =
(0xff << (8 - masklen % 8)) & 0xff;
/*NOTREACHED*/
}
+struct block *
+gen_ifindex(compiler_state_t *cstate, int ifindex)
+{
+ register struct block *b0;
+
+ /*
+ * Catch errors reported by us and routines below us, and return NULL
+ * on an error.
+ */
+ if (setjmp(cstate->top_ctx))
+ return (NULL);
+
+ /*
+ * Only some data link types support ifindex qualifiers.
+ */
+ switch (cstate->linktype) {
+ case DLT_LINUX_SLL2:
+ /* match packets on this interface */
+ b0 = gen_cmp(cstate, OR_LINKHDR, 4, BPF_W, ifindex);
+ break;
+ default:
+#if defined(linux)
+ /*
+ * This is Linux; we require PF_PACKET support.
+ * If this is a *live* capture, we can look at
+ * special meta-data in the filter expression;
+ * if it's a savefile, we can't.
+ */
+ if (cstate->bpf_pcap->rfile != NULL) {
+ /* We have a FILE *, so this is a savefile */
+ bpf_error(cstate, "ifindex not supported on %s when reading savefiles",
+ pcap_datalink_val_to_description_or_dlt(cstate->linktype));
+ b0 = NULL;
+ /*NOTREACHED*/
+ }
+ /* match ifindex */
+ b0 = gen_cmp(cstate, OR_LINKHDR, SKF_AD_OFF + SKF_AD_IFINDEX, BPF_W,
+ ifindex);
+#else /* defined(linux) */
+ bpf_error(cstate, "ifindex not supported on %s",
+ pcap_datalink_val_to_description_or_dlt(cstate->linktype));
+ /*NOTREACHED*/
+#endif /* defined(linux) */
+ }
+ return (b0);
+}
+
/*
* Filter on inbound (dir == 0) or outbound (dir == 1) traffic.
* Outbound traffic is sent by this machine, while inbound traffic is
}
break;
-#ifdef HAVE_NET_PFVAR_H
case DLT_PFLOG:
b0 = gen_cmp(cstate, OR_LINKHDR, offsetof(struct pfloghdr, dir), BPF_B,
((dir == 0) ? PF_IN : PF_OUT));
break;
-#endif
case DLT_PPP_PPPD:
if (dir) {
* with newer capture APIs, allowing it to be saved
* in pcapng files.
*/
-#if defined(linux) && defined(PF_PACKET) && defined(SO_ATTACH_FILTER)
+#if defined(linux)
/*
- * This is Linux with PF_PACKET support.
+ * This is Linux; we require PF_PACKET support.
* If this is a *live* capture, we can look at
* special meta-data in the filter expression;
* if it's a savefile, we can't.
/* We have a FILE *, so this is a savefile */
bpf_error(cstate, "inbound/outbound not supported on %s when reading savefiles",
pcap_datalink_val_to_description_or_dlt(cstate->linktype));
- b0 = NULL;
/*NOTREACHED*/
}
/* match outgoing packets */
/* to filter on inbound traffic, invert the match */
gen_not(b0);
}
-#else /* defined(linux) && defined(PF_PACKET) && defined(SO_ATTACH_FILTER) */
+#else /* defined(linux) */
bpf_error(cstate, "inbound/outbound not supported on %s",
pcap_datalink_val_to_description_or_dlt(cstate->linktype));
/*NOTREACHED*/
-#endif /* defined(linux) && defined(PF_PACKET) && defined(SO_ATTACH_FILTER) */
+#endif /* defined(linux) */
}
return (b0);
}
-#ifdef HAVE_NET_PFVAR_H
/* PF firewall log matched interface */
struct block *
gen_pf_ifname(compiler_state_t *cstate, const char *ifname)
(bpf_u_int32)action);
return (b0);
}
-#else /* !HAVE_NET_PFVAR_H */
-struct block *
-gen_pf_ifname(compiler_state_t *cstate, const char *ifname _U_)
-{
- /*
- * Catch errors reported by us and routines below us, and return NULL
- * on an error.
- */
- if (setjmp(cstate->top_ctx))
- return (NULL);
-
- bpf_error(cstate, "libpcap was compiled without pf support");
- /*NOTREACHED*/
-}
-
-struct block *
-gen_pf_ruleset(compiler_state_t *cstate, char *ruleset _U_)
-{
- /*
- * Catch errors reported by us and routines below us, and return NULL
- * on an error.
- */
- if (setjmp(cstate->top_ctx))
- return (NULL);
-
- bpf_error(cstate, "libpcap was compiled on a machine without pf support");
- /*NOTREACHED*/
-}
-
-struct block *
-gen_pf_rnr(compiler_state_t *cstate, int rnr _U_)
-{
- /*
- * Catch errors reported by us and routines below us, and return NULL
- * on an error.
- */
- if (setjmp(cstate->top_ctx))
- return (NULL);
-
- bpf_error(cstate, "libpcap was compiled on a machine without pf support");
- /*NOTREACHED*/
-}
-
-struct block *
-gen_pf_srnr(compiler_state_t *cstate, int srnr _U_)
-{
- /*
- * Catch errors reported by us and routines below us, and return NULL
- * on an error.
- */
- if (setjmp(cstate->top_ctx))
- return (NULL);
-
- bpf_error(cstate, "libpcap was compiled on a machine without pf support");
- /*NOTREACHED*/
-}
-
-struct block *
-gen_pf_reason(compiler_state_t *cstate, int reason _U_)
-{
- /*
- * Catch errors reported by us and routines below us, and return NULL
- * on an error.
- */
- if (setjmp(cstate->top_ctx))
- return (NULL);
-
- bpf_error(cstate, "libpcap was compiled on a machine without pf support");
- /*NOTREACHED*/
-}
-
-struct block *
-gen_pf_action(compiler_state_t *cstate, int action _U_)
-{
- /*
- * Catch errors reported by us and routines below us, and return NULL
- * on an error.
- */
- if (setjmp(cstate->top_ctx))
- return (NULL);
-
- bpf_error(cstate, "libpcap was compiled on a machine without pf support");
- /*NOTREACHED*/
-}
-#endif /* HAVE_NET_PFVAR_H */
/* IEEE 802.11 wireless header */
struct block *
/*
* This is tricky. We need to insert the statements updating variable
- * parts of offsets before the the traditional TPID and VID tests so
+ * parts of offsets before 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
switch (cstate->linktype) {
case DLT_C_HDLC: /* fall through */
+ case DLT_HDLC:
case DLT_EN10MB:
case DLT_NETANALYZER:
case DLT_NETANALYZER_TRANSPARENT:
* the PPP packet, and note that this is PPPoE rather than
* raw PPP.
*
- * XXX - this is a bit of a kludge. If we were to split the
- * compiler into a parser that parses an expression and
- * generates an expression tree, and a code generator that
- * takes an expression tree (which could come from our
- * parser or from some other parser) and generates BPF code,
- * we could perhaps make the offsets parameters of routines
- * and, in the handler for an "AND" node, pass to subnodes
- * other than the PPPoE node the adjusted offsets.
- *
- * This would mean that "pppoes" would, instead of changing the
- * behavior of *all* tests after it, change only the behavior
- * of tests ANDed with it. That would change the documented
- * semantics of "pppoes", which might break some expressions.
- * However, it would mean that "(pppoes and ip) or ip" would check
- * both for VLAN-encapsulated IP and IP-over-Ethernet, rather than
- * checking only for VLAN-encapsulated IP, so that could still
- * be considered worth doing; it wouldn't break expressions
- * that are of the form "pppoes and ..." which I suspect are the
- * most common expressions involving "pppoes". "pppoes or ..."
- * doesn't necessarily do what the user would really want, now,
- * as all the "or ..." tests would be done assuming PPPoE, even
- * though the "or" could be viewed as meaning "or, if this isn't
- * a PPPoE packet...".
+ * XXX - this is a bit of a kludge. See the comments in
+ * gen_vlan().
*
* The "network-layer" protocol is PPPoE, which has a 6-byte
* PPPoE header, followed by a PPP packet.