+ * Currently, only raw "link[N:M]" filtering is supported.
+ */
+ off_linktype = -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 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_NET:
+ s = gen_load_llrel(off_nl + offset, size);
+ break;
+
+ case OR_NET_NOSNAP:
+ s = gen_load_llrel(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 link-layer header} +
+ * {offset, relative to the start of the link-layer
+ * header, of the IPv4 header} + {length of the IPv4 header} +
+ * {specified offset}.
+ *
+ * (If the link-layer is variable-length, it's included
+ * in the value in the X register, and off_ll is 0.)
+ */
+ s2 = new_stmt(BPF_LD|BPF_IND|size);
+ s2->s.k = off_ll + off_nl + offset;
+ sappend(s, s2);
+ break;
+
+ case OR_TRAN_IPV6:
+ s = gen_load_llrel(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_llprefixlen();
+ 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.
+ * 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 length
+ * of the prefix preceding the link-layer
+ * header, 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; 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_LDX|BPF_MSH|BPF_B);
+ s->s.k = off_ll + 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_LINK, off_linktype + 2, 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