]> The Tcpdump Group git mirrors - libpcap/blobdiff - gencode.c
Another change from Debian.
[libpcap] / gencode.c
index e2fbca9317ae73587b7b6af89a9d746aaa68c8cb..5548e6fd1f0c4dadfdff9b0778a0f6dde3e0daa6 100644 (file)
--- a/gencode.c
+++ b/gencode.c
@@ -21,7 +21,7 @@
  */
 #ifndef lint
 static const char rcsid[] _U_ =
-    "@(#) $Header: /tcpdump/master/libpcap/gencode.c,v 1.290.2.6 2007-11-05 21:52:30 guy Exp $ (LBL)";
+    "@(#) $Header: /tcpdump/master/libpcap/gencode.c,v 1.290.2.19 2008-12-23 20:14:12 guy Exp $ (LBL)";
 #endif
 
 #ifdef HAVE_CONFIG_H
@@ -68,6 +68,7 @@ static const char rcsid[] _U_ =
 #include "nlpid.h"
 #include "llc.h"
 #include "gencode.h"
+#include "ieee80211.h"
 #include "atmuni31.h"
 #include "sunatmpos.h"
 #include "ppp.h"
@@ -200,9 +201,11 @@ static inline struct block *gen_true(void);
 static inline struct block *gen_false(void);
 static struct block *gen_ether_linktype(int);
 static struct block *gen_linux_sll_linktype(int);
+static struct slist *gen_load_prism_llprefixlen(void);
+static struct slist *gen_load_avs_llprefixlen(void);
 static struct slist *gen_load_radiotap_llprefixlen(void);
 static struct slist *gen_load_ppi_llprefixlen(void);
-static void insert_load_llprefixlen(struct block *);
+static void insert_compute_vloffsets(struct block *);
 static struct slist *gen_llprefixlen(void);
 static struct slist *gen_off_macpl(void);
 static int ethertype_to_ppptype(int);
@@ -496,18 +499,6 @@ finish_parse(p)
        struct block *p;
 {
        struct block *ppi_dlt_check;
-       
-       ppi_dlt_check = gen_ppi_dlt_check();
-
-       if (ppi_dlt_check != NULL)
-       {
-               gen_and(ppi_dlt_check, p);
-       }
-
-       backpatch(p, gen_retblk(snaplen));
-       p->sense = !p->sense;
-       backpatch(p, gen_retblk(0));
-       root = p->head;
 
        /*
         * Insert before the statements of the first (root) block any
@@ -518,13 +509,30 @@ finish_parse(p)
         * statements of all blocks that use those lengths and that
         * have no predecessors that use them, so that we only compute
         * the lengths if we need them.  There might be even better
-        * approaches than that.  However, as we're currently only
-        * handling variable-length radiotap headers, and as all
-        * filtering expressions other than raw link[M:N] tests
-        * require the length of that header, doing more for that
-        * header length isn't really worth the effort.
+        * approaches than that.
+        *
+        * However, those strategies would be more complicated, and
+        * as we don't generate code to compute a length if the
+        * program has no tests that use the length, and as most
+        * tests will probably use those lengths, we would just
+        * postpone computing the lengths so that it's not done
+        * for tests that fail early, and it's not clear that's
+        * worth the effort.
+        */
+       insert_compute_vloffsets(p->head);
+       
+       /*
+        * For DLT_PPI captures, generate a check of the per-packet
+        * DLT value to make sure it's DLT_IEEE802_11.
         */
-       insert_load_llprefixlen(root);
+       ppi_dlt_check = gen_ppi_dlt_check();
+       if (ppi_dlt_check != NULL)
+               gen_and(ppi_dlt_check, p);
+
+       backpatch(p, gen_retblk(snaplen));
+       p->sense = !p->sense;
+       backpatch(p, gen_retblk(0));
+       root = p->head;
 }
 
 void
@@ -1023,6 +1031,9 @@ init_linktype(p)
                return;
 
        case DLT_IEEE802_11:
+       case DLT_PRISM_HEADER:
+       case DLT_IEEE802_11_RADIO_AVS:
+       case DLT_IEEE802_11_RADIO:
                /*
                 * 802.11 doesn't really have a link-level type field.
                 * We set "off_linktype" to the offset of the LLC header.
@@ -1031,31 +1042,15 @@ init_linktype(p)
                 * is being used and pick out the encapsulated Ethernet type.
                 * XXX - should we generate code to check for SNAP?
                 *
-                * XXX - the header is actually variable-length.  We
-                * assume a 24-byte link-layer header, as appears in
-                * data frames in networks with no bridges.  If the
-                * fromds and tods 802.11 header bits are both set,
-                * it's actually supposed to be 30 bytes.
-                */
-               off_linktype = 24;
-               off_macpl = 0;          /* link-layer header is variable-length */
-               off_macpl_is_variable = 1;
-               off_nl = 8;             /* 802.2+SNAP */
-               off_nl_nosnap = 3;      /* 802.2 */
-               return;
-
-       case DLT_PRISM_HEADER:
-               /*
-                * Same as 802.11, but with an additional header before
-                * the 802.11 header, containing a bunch of additional
-                * information including radio-level information.
-                *
-                * The header is 144 bytes long.
-                *
-                * XXX - same variable-length header problem; at least
-                * the Prism header is fixed-length.
+                * We also handle variable-length radio headers here.
+                * The Prism header is in theory variable-length, but in
+                * practice it's always 144 bytes long.  However, some
+                * drivers on Linux use ARPHRD_IEEE80211_PRISM, but
+                * sometimes or always supply an AVS header, so we
+                * have to check whether the radio header is a Prism
+                * header or an AVS header, so, in practice, it's
+                * variable-length.
                 */
-               off_ll = 144;
                off_linktype = 24;
                off_macpl = 0;          /* link-layer header is variable-length */
                off_macpl_is_variable = 1;
@@ -1063,59 +1058,15 @@ init_linktype(p)
                off_nl_nosnap = 3;      /* 802.2 */
                return;
 
-       case DLT_IEEE802_11_RADIO_AVS:
-               /*
-                * Same as 802.11, but with an additional header before
-                * the 802.11 header, containing a bunch of additional
-                * information including radio-level information.
-                *
-                * The header is 64 bytes long, at least in its
-                * current incarnation.
-                *
-                * XXX - same variable-length header problem, only
-                * more so; this header is also variable-length,
-                * with the length being the 32-bit big-endian
-                * number at an offset of 4 from the beginning
-                * of the radio header.  We should handle that the
-                * same way we handle the length at the beginning
-                * of the radiotap header.
-                *
-                * XXX - in Linux, do any drivers that supply an AVS
-                * header supply a link-layer type other than
-                * ARPHRD_IEEE80211_PRISM?  If so, we should map that
-                * to DLT_IEEE802_11_RADIO_AVS; if not, or if there are
-                * any drivers that supply an AVS header but supply
-                * an ARPHRD value of ARPHRD_IEEE80211_PRISM, we'll
-                * have to check the header in the generated code to
-                * determine whether it's Prism or AVS.
-                */
-               off_ll = 64;
-               off_linktype = 24;
-               off_macpl = 0;          /* link-layer header is variable-length */
-               off_macpl_is_variable = 1;
-               off_nl = 8;             /* 802.2+SNAP */
-               off_nl_nosnap = 3;      /* 802.2 */
-               return;
-               
-               /* 
-                * At the moment we treat PPI as normal Radiotap encoded
-                * packets. The difference is in the function that generates
-                * the code at the beginning to compute the header length.
-                * Since this code generator of PPI supports bare 802.11
-                * encapsulation only (i.e. the encapsulated DLT should be
-                * DLT_IEEE802_11) we generate code to check for this too.
-                */
        case DLT_PPI:
-       case DLT_IEEE802_11_RADIO:
-               /*
-                * Same as 802.11, but with an additional header before
-                * the 802.11 header, containing a bunch of additional
-                * information including radio-level information.
-                *
-                * The radiotap header is variable length, and we
-                * generate code to compute its length and store it
-                * in a register.  These offsets are relative to the
-                * beginning of the 802.11 header.
+               /* 
+                * At the moment we treat PPI the same way that we treat
+                * normal Radiotap encoded packets. The difference is in
+                * the function that generates the code at the beginning
+                * to compute the header length.  Since this code generator
+                * of PPI supports bare 802.11 encapsulation only (i.e.
+                * the encapsulated DLT should be DLT_IEEE802_11) we
+                * generate code to check for this too.
                 */
                off_linktype = 24;
                off_macpl = 0;          /* link-layer header is variable-length */
@@ -1536,6 +1487,36 @@ init_linktype(p)
                off_nl_nosnap = -1;     /* no 802.2 LLC */
                off_mac = 1;            /* step over the kiss length byte */
                return;
+
+       case DLT_IEEE802_15_4_NONASK_PHY:
+               /*
+                * Currently, only raw "link[N:M]" filtering is supported.
+                */
+               off_linktype = -1;
+               off_macpl = -1;
+               off_nl = -1;
+               off_nl_nosnap = -1;
+               return;
+
+       case DLT_MPLS:
+               /*
+                * Currently, only raw "link[N:M]" filtering is supported.
+                */
+               off_linktype = -1;
+               off_macpl = -1;
+               off_nl = -1;
+               off_nl_nosnap = -1;
+               return;
+
+       case DLT_USB_LINUX_MMAPPED:
+               /*
+                * Currently, only raw "link[N:M]" filtering is supported.
+                */
+               off_linktype = -1;
+               off_macpl = -1;
+               off_nl = -1;
+               off_nl_nosnap = -1;
+               return;
        }
        bpf_error("unknown data link type %d", linktype);
        /* NOTREACHED */
@@ -2127,6 +2108,156 @@ gen_linux_sll_linktype(proto)
        }
 }
 
+static struct slist *
+gen_load_prism_llprefixlen()
+{
+       struct slist *s1, *s2;
+       struct slist *sjeq_avs_cookie;
+       struct slist *sjcommon;
+
+       /*
+        * This code is not compatible with the optimizer, as
+        * we are generating jmp instructions within a normal
+        * slist of instructions
+        */
+       no_optimize = 1;
+
+       /*
+        * Generate code to load the length of the radio 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.)
+        *
+        * Some Linux drivers use ARPHRD_IEEE80211_PRISM but sometimes
+        * or always use the AVS header rather than the Prism header.
+        * We load a 4-byte big-endian value at the beginning of the
+        * raw packet data, and see whether, when masked with 0xFFFFF000,
+        * it's equal to 0x80211000.  If so, that indicates that it's
+        * an AVS header (the masked-out bits are the version number).
+        * Otherwise, it's a Prism header.
+        *
+        * XXX - the Prism header is also, in theory, variable-length,
+        * but no known software generates headers that aren't 144
+        * bytes long.
+        */
+       if (reg_off_ll != -1) {
+               /*
+                * Load the cookie.
+                */
+               s1 = new_stmt(BPF_LD|BPF_W|BPF_ABS);
+               s1->s.k = 0;
+
+               /*
+                * AND it with 0xFFFFF000.
+                */
+               s2 = new_stmt(BPF_ALU|BPF_AND|BPF_K);
+               s2->s.k = 0xFFFFF000;
+               sappend(s1, s2);
+
+               /*
+                * Compare with 0x80211000.
+                */
+               sjeq_avs_cookie = new_stmt(JMP(BPF_JEQ));
+               sjeq_avs_cookie->s.k = 0x80211000;
+               sappend(s1, sjeq_avs_cookie);
+
+               /*
+                * If it's AVS:
+                *
+                * The 4 bytes at an offset of 4 from the beginning of
+                * the AVS header are the length of the AVS header.
+                * That field is big-endian.
+                */
+               s2 = new_stmt(BPF_LD|BPF_W|BPF_ABS);
+               s2->s.k = 4;
+               sappend(s1, s2);
+               sjeq_avs_cookie->s.jt = s2;
+
+               /*
+                * Now jump to the code to allocate a register
+                * into which to save the header length and
+                * store the length there.  (The "jump always"
+                * instruction needs to have the k field set;
+                * it's added to the PC, so, as we're jumping
+                * over a single instruction, it should be 1.)
+                */
+               sjcommon = new_stmt(JMP(BPF_JA));
+               sjcommon->s.k = 1;
+               sappend(s1, sjcommon);
+
+               /*
+                * Now for the code that handles the Prism header.
+                * Just load the length of the Prism header (144)
+                * into the A register.  Have the test for an AVS
+                * header branch here if we don't have an AVS header.
+                */
+               s2 = new_stmt(BPF_LD|BPF_W|BPF_IMM);
+               s2->s.k = 144;
+               sappend(s1, s2);
+               sjeq_avs_cookie->s.jf = s2;
+
+               /*
+                * Now allocate a register to hold that value and store
+                * it.  The code for the AVS header will jump here after
+                * loading the length of the AVS header.
+                */
+               s2 = new_stmt(BPF_ST);
+               s2->s.k = reg_off_ll;
+               sappend(s1, s2);
+               sjcommon->s.jf = s2;
+
+               /*
+                * Now move it into the X register.
+                */
+               s2 = new_stmt(BPF_MISC|BPF_TAX);
+               sappend(s1, s2);
+
+               return (s1);
+       } else
+               return (NULL);
+}
+
+static struct slist *
+gen_load_avs_llprefixlen()
+{
+       struct slist *s1, *s2;
+
+       /*
+        * Generate code to load the length of the AVS 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 (reg_off_ll != -1) {
+               /*
+                * The 4 bytes at an offset of 4 from the beginning of
+                * the AVS header are the length of the AVS header.
+                * That field is big-endian.
+                */
+               s1 = new_stmt(BPF_LD|BPF_W|BPF_ABS);
+               s1->s.k = 4;
+
+               /*
+                * Now allocate a register to hold that value and store
+                * it.
+                */
+               s2 = new_stmt(BPF_ST);
+               s2->s.k = reg_off_ll;
+               sappend(s1, s2);
+
+               /*
+                * Now move it into the X register.
+                */
+               s2 = new_stmt(BPF_MISC|BPF_TAX);
+               sappend(s1, s2);
+
+               return (s1);
+       } else
+               return (NULL);
+}
+
 static struct slist *
 gen_load_radiotap_llprefixlen()
 {
@@ -2270,6 +2401,10 @@ gen_load_802_11_header_len(struct slist *s, struct slist *snext)
        struct slist *sjset_data_frame_1;
        struct slist *sjset_data_frame_2;
        struct slist *sjset_qos;
+       struct slist *sjset_radiotap_flags;
+       struct slist *sjset_radiotap_tsft;
+       struct slist *sjset_tsft_datapad, *sjset_notsft_datapad;
+       struct slist *s_roundup;
 
        if (reg_off_macpl == -1) {
                /*
@@ -2360,37 +2495,141 @@ gen_load_802_11_header_len(struct slist *s, struct slist *snext)
                
        /*
         * If it's set, add 2 to reg_off_macpl, to skip the QoS
-        * field (the current value of reg_off_macpl is in the
-        * X register, so store 2 + X in reg_off_macpl).
+        * field.
         * Otherwise, go to the first statement of the rest of the
         * program.
         */
-       sjset_qos->s.jt = s2 = new_stmt(BPF_LD|BPF_IMM);
-       s2->s.k = 2;
+       sjset_qos->s.jt = s2 = new_stmt(BPF_LD|BPF_MEM);
+       s2->s.k = reg_off_macpl;
        sappend(s, s2);
-       s2 = new_stmt(BPF_ALU|BPF_ADD|BPF_X);
+       s2 = new_stmt(BPF_ALU|BPF_ADD|BPF_IMM);
+       s2->s.k = 2;
        sappend(s, s2);
        s2 = new_stmt(BPF_ST);
        s2->s.k = reg_off_macpl;
        sappend(s, s2);
-       sjset_qos->s.jf = snext;
+
+       /*
+        * If we have a radiotap header, look at it to see whether
+        * there's Atheros padding between the MAC-layer header
+        * and the payload.
+        *
+        * Note: all of the fields in the radiotap header are
+        * little-endian, so we byte-swap all of the values
+        * we test against, as they will be loaded as big-endian
+        * values.
+        */
+       if (linktype == DLT_IEEE802_11_RADIO) {
+               /*
+                * Is the IEEE80211_RADIOTAP_FLAGS bit (0x0000002) set
+                * in the presence flag?
+                */
+               sjset_qos->s.jf = s2 = new_stmt(BPF_LD|BPF_ABS|BPF_W);
+               s2->s.k = 4;
+               sappend(s, s2);
+
+               sjset_radiotap_flags = new_stmt(JMP(BPF_JSET));
+               sjset_radiotap_flags->s.k = SWAPLONG(0x00000002);
+               sappend(s, sjset_radiotap_flags);
+
+               /*
+                * If not, skip all of this.
+                */
+               sjset_radiotap_flags->s.jf = snext;
+
+               /*
+                * Otherwise, is the IEEE80211_RADIOTAP_TSFT bit set?
+                */
+               sjset_radiotap_tsft = sjset_radiotap_flags->s.jt =
+                   new_stmt(JMP(BPF_JSET));
+               sjset_radiotap_tsft->s.k = SWAPLONG(0x00000001);
+               sappend(s, sjset_radiotap_tsft);
+
+               /*
+                * If IEEE80211_RADIOTAP_TSFT is set, the flags field is
+                * at an offset of 16 from the beginning of the raw packet
+                * data (8 bytes for the radiotap header and 8 bytes for
+                * the TSFT field).
+                *
+                * Test whether the IEEE80211_RADIOTAP_F_DATAPAD bit (0x20)
+                * is set.
+                */
+               sjset_radiotap_tsft->s.jt = s2 = new_stmt(BPF_LD|BPF_ABS|BPF_B);
+               s2->s.k = 16;
+               sappend(s, s2);
+
+               sjset_tsft_datapad = new_stmt(JMP(BPF_JSET));
+               sjset_tsft_datapad->s.k = 0x20;
+               sappend(s, sjset_tsft_datapad);
+
+               /*
+                * If IEEE80211_RADIOTAP_TSFT is not set, the flags field is
+                * at an offset of 8 from the beginning of the raw packet
+                * data (8 bytes for the radiotap header).
+                *
+                * Test whether the IEEE80211_RADIOTAP_F_DATAPAD bit (0x20)
+                * is set.
+                */
+               sjset_radiotap_tsft->s.jf = s2 = new_stmt(BPF_LD|BPF_ABS|BPF_B);
+               s2->s.k = 8;
+               sappend(s, s2);
+
+               sjset_notsft_datapad = new_stmt(JMP(BPF_JSET));
+               sjset_notsft_datapad->s.k = 0x20;
+               sappend(s, sjset_notsft_datapad);
+
+               /*
+                * In either case, if IEEE80211_RADIOTAP_F_DATAPAD is
+                * set, round the length of the 802.11 header to
+                * a multiple of 4.  Do that by adding 3 and then
+                * dividing by and multiplying by 4, which we do by
+                * ANDing with ~3.
+                */
+               s_roundup = new_stmt(BPF_LD|BPF_MEM);
+               s_roundup->s.k = reg_off_macpl;
+               sappend(s, s_roundup);
+               s2 = new_stmt(BPF_ALU|BPF_ADD|BPF_IMM);
+               s2->s.k = 3;
+               sappend(s, s2);
+               s2 = new_stmt(BPF_ALU|BPF_AND|BPF_IMM);
+               s2->s.k = ~3;
+               sappend(s, s2);
+               s2 = new_stmt(BPF_ST);
+               s2->s.k = reg_off_macpl;
+               sappend(s, s2);
+
+               sjset_tsft_datapad->s.jt = s_roundup;
+               sjset_tsft_datapad->s.jf = snext;
+               sjset_notsft_datapad->s.jt = s_roundup;
+               sjset_notsft_datapad->s.jf = snext;
+       } else
+               sjset_qos->s.jf = snext;
 
        return s;
 }
 
 static void
-insert_load_llprefixlen(b)
+insert_compute_vloffsets(b)
        struct block *b;
 {
        struct slist *s;
 
        /*
-        * Generate code to load the length of any variable-length
-        * header preceding the link-layer header into the register
-        * assigned to that length, if any.
+        * For link-layer types that have a variable-length header
+        * preceding the link-layer header, generate code to load
+        * the offset of the link-layer header into the register
+        * assigned to that offset, if any.
         */
        switch (linktype) {
 
+       case DLT_PRISM_HEADER:
+               s = gen_load_prism_llprefixlen();
+               break;
+
+       case DLT_IEEE802_11_RADIO_AVS:
+               s = gen_load_avs_llprefixlen();
+               break;
+
        case DLT_IEEE802_11_RADIO:
                s = gen_load_radiotap_llprefixlen();
                break;
@@ -2405,23 +2644,23 @@ insert_load_llprefixlen(b)
        }
 
        /*
-        * Now generate code to load the length of any variable-length
-        * header preceding the link-layer header into the register
-        * assigned to that length, if any.
+        * For link-layer types that have a variable-length link-layer
+        * header, generate code to load the offset of the MAC-layer
+        * payload into the register assigned to that offset, if any.
         */
        switch (linktype) {
 
        case DLT_IEEE802_11:
-       case DLT_IEEE802_11_RADIO:
-       case DLT_IEEE802_11_RADIO_AVS:
        case DLT_PRISM_HEADER:
+       case DLT_IEEE802_11_RADIO_AVS:
+       case DLT_IEEE802_11_RADIO:
        case DLT_PPI:
                s = gen_load_802_11_header_len(s, b->stmts);
                break;
        }
 
        /*
-        * If we have any length loading code, append all the
+        * If we have any offset-loading code, append all the
         * existing statements in the block to those statements,
         * and make the resulting list the list of statements
         * for the block.
@@ -2458,6 +2697,50 @@ gen_ppi_dlt_check(void)
        return b;
 }
 
+static struct slist *
+gen_prism_llprefixlen(void)
+{
+       struct slist *s;
+
+       if (reg_off_ll == -1) {
+               /*
+                * We haven't yet assigned a register for the length
+                * of the radio header; allocate one.
+                */
+               reg_off_ll = alloc_reg();
+       }
+
+       /*
+        * Load the register containing the radio length
+        * into the X register.
+        */
+       s = new_stmt(BPF_LDX|BPF_MEM);
+       s->s.k = reg_off_ll;
+       return s;
+}
+
+static struct slist *
+gen_avs_llprefixlen(void)
+{
+       struct slist *s;
+
+       if (reg_off_ll == -1) {
+               /*
+                * We haven't yet assigned a register for the length
+                * of the AVS header; allocate one.
+                */
+               reg_off_ll = alloc_reg();
+       }
+
+       /*
+        * Load the register containing the AVS length
+        * into the X register.
+        */
+       s = new_stmt(BPF_LDX|BPF_MEM);
+       s->s.k = reg_off_ll;
+       return s;
+}
+
 static struct slist *
 gen_radiotap_llprefixlen(void)
 {
@@ -2502,7 +2785,7 @@ gen_ppi_llprefixlen(void)
        }
 
        /*
-        * Load the register containing the radiotap length
+        * Load the register containing the PPI length
         * into the X register.
         */
        s = new_stmt(BPF_LDX|BPF_MEM);
@@ -2521,12 +2804,18 @@ gen_llprefixlen(void)
 {
        switch (linktype) {
 
-       case DLT_PPI:
-               return gen_ppi_llprefixlen();
-       
+       case DLT_PRISM_HEADER:
+               return gen_prism_llprefixlen();
+
+       case DLT_IEEE802_11_RADIO_AVS:
+               return gen_avs_llprefixlen();
+
        case DLT_IEEE802_11_RADIO:
                return gen_radiotap_llprefixlen();
 
+       case DLT_PPI:
+               return gen_ppi_llprefixlen();
+
        default:
                return NULL;
        }
@@ -2691,11 +2980,11 @@ gen_linktype(proto)
                }
                break;
 
-       case DLT_PPI:
        case DLT_IEEE802_11:
-       case DLT_IEEE802_11_RADIO:
-       case DLT_IEEE802_11_RADIO_AVS:
        case DLT_PRISM_HEADER:
+       case DLT_IEEE802_11_RADIO_AVS:
+       case DLT_IEEE802_11_RADIO:
+       case DLT_PPI:
                /*
                 * Check that we have a data frame.
                 */
@@ -3081,6 +3370,7 @@ gen_linktype(proto)
 
        case DLT_USB:
        case DLT_USB_LINUX:
+       case DLT_USB_LINUX_MMAPPED:
                bpf_error("USB link-layer type filtering not implemented");
 
        case DLT_BLUETOOTH_HCI_H4:
@@ -3092,6 +3382,7 @@ gen_linktype(proto)
 
        case DLT_IEEE802_15_4:
        case DLT_IEEE802_15_4_LINUX:
+       case DLT_IEEE802_15_4_NONASK_PHY:
                bpf_error("IEEE 802.15.4 link-layer type filtering not implemented");
 
        case DLT_IEEE802_16_MAC_CPS_RADIO:
@@ -3448,7 +3739,8 @@ gen_thostop(eaddr, dir)
 }
 
 /*
- * Like gen_ehostop, but for DLT_IEEE802_11 (802.11 wireless LAN)
+ * Like gen_ehostop, but for DLT_IEEE802_11 (802.11 wireless LAN) and
+ * various 802.11 + radio headers.
  */
 static struct block *
 gen_wlanhostop(eaddr, dir)
@@ -3565,7 +3857,7 @@ gen_wlanhostop(eaddr, dir)
                 * Now check for a data frame.
                 * I.e, check "link[0] & 0x08".
                 */
-               gen_load_a(OR_LINK, 0, BPF_B);
+               s = gen_load_a(OR_LINK, 0, BPF_B);
                b1 = new_block(JMP(BPF_JSET));
                b1->s.k = 0x08;
                b1->stmts = s;
@@ -3734,6 +4026,55 @@ gen_wlanhostop(eaddr, dir)
                gen_and(b1, b0);
                return b0;
 
+       /*
+        * XXX - add RA, TA, and BSSID keywords?
+        */
+       case Q_ADDR1:
+               return (gen_bcmp(OR_LINK, 4, 6, eaddr));
+
+       case Q_ADDR2:
+               /*
+                * Not present in CTS or ACK control frames.
+                */
+               b0 = gen_mcmp(OR_LINK, 0, BPF_B, IEEE80211_FC0_TYPE_CTL,
+                       IEEE80211_FC0_TYPE_MASK);
+               gen_not(b0);
+               b1 = gen_mcmp(OR_LINK, 0, BPF_B, IEEE80211_FC0_SUBTYPE_CTS,
+                       IEEE80211_FC0_SUBTYPE_MASK);
+               gen_not(b1);
+               b2 = gen_mcmp(OR_LINK, 0, BPF_B, IEEE80211_FC0_SUBTYPE_ACK,
+                       IEEE80211_FC0_SUBTYPE_MASK);
+               gen_not(b2);
+               gen_and(b1, b2);
+               gen_or(b0, b2);
+               b1 = gen_bcmp(OR_LINK, 10, 6, eaddr);
+               gen_and(b2, b1);
+               return b1;
+
+       case Q_ADDR3:
+               /*
+                * Not present in control frames.
+                */
+               b0 = gen_mcmp(OR_LINK, 0, BPF_B, IEEE80211_FC0_TYPE_CTL,
+                       IEEE80211_FC0_TYPE_MASK);
+               gen_not(b0);
+               b1 = gen_bcmp(OR_LINK, 16, 6, eaddr);
+               gen_and(b0, b1);
+               return b1;
+
+       case Q_ADDR4:
+               /*
+                * Present only if the direction mask has both "From DS"
+                * and "To DS" set.  Neither control frames nor management
+                * frames should have both of those set, so we don't
+                * check the frame type.
+                */
+               b0 = gen_mcmp(OR_LINK, 1, BPF_B,
+                       IEEE80211_FC1_DIR_DSTODS, IEEE80211_FC1_DIR_MASK);
+               b1 = gen_bcmp(OR_LINK, 24, 6, eaddr);
+               gen_and(b0, b1);
+               return b1;
+
        case Q_AND:
                b0 = gen_wlanhostop(eaddr, Q_SRC);
                b1 = gen_wlanhostop(eaddr, Q_DST);
@@ -4187,10 +4528,10 @@ gen_gateway(eaddr, alist, proto, dir)
                        b0 = gen_thostop(eaddr, Q_OR);
                        break;
                case DLT_IEEE802_11:
+               case DLT_PRISM_HEADER:
                case DLT_IEEE802_11_RADIO_AVS:
-               case DLT_PPI:
                case DLT_IEEE802_11_RADIO:
-               case DLT_PRISM_HEADER:
+               case DLT_PPI:
                        b0 = gen_wlanhostop(eaddr, Q_OR);
                        break;
                case DLT_SUNATM:
@@ -4995,9 +5336,9 @@ gen_protochain(v, proto, dir)
        switch (linktype) {
 
        case DLT_IEEE802_11:
-       case DLT_IEEE802_11_RADIO:
-       case DLT_IEEE802_11_RADIO_AVS:
        case DLT_PRISM_HEADER:
+       case DLT_IEEE802_11_RADIO_AVS:
+       case DLT_IEEE802_11_RADIO:
        case DLT_PPI:
                bpf_error("'protochain' not supported with 802.11");
        }
@@ -5573,9 +5914,9 @@ gen_scode(name, q)
                                return b;
 
                        case DLT_IEEE802_11:
+                       case DLT_PRISM_HEADER:
                        case DLT_IEEE802_11_RADIO_AVS:
                        case DLT_IEEE802_11_RADIO:
-                       case DLT_PRISM_HEADER:
                        case DLT_PPI:
                                eaddr = pcap_ether_hostton(name);
                                if (eaddr == NULL)
@@ -6048,9 +6389,9 @@ gen_ecode(eaddr, q)
                case DLT_IEEE802:
                        return gen_thostop(eaddr, (int)q.dir);
                case DLT_IEEE802_11:
+               case DLT_PRISM_HEADER:
                case DLT_IEEE802_11_RADIO_AVS:
                case DLT_IEEE802_11_RADIO:
-               case DLT_PRISM_HEADER:
                case DLT_PPI:
                        return gen_wlanhostop(eaddr, (int)q.dir);
                case DLT_SUNATM:
@@ -6651,10 +6992,10 @@ gen_broadcast(proto)
                case DLT_IEEE802:
                        return gen_thostop(ebroadcast, Q_DST);
                case DLT_IEEE802_11:
+               case DLT_PRISM_HEADER:
                case DLT_IEEE802_11_RADIO_AVS:
                case DLT_IEEE802_11_RADIO:
                case DLT_PPI:
-               case DLT_PRISM_HEADER:
                        return gen_wlanhostop(ebroadcast, Q_DST);
                case DLT_IP_OVER_FC:
                        return gen_ipfchostop(ebroadcast, Q_DST);
@@ -6747,10 +7088,10 @@ gen_multicast(proto)
                        /* tr[2] & 1 != 0 */
                        return gen_mac_multicast(2);
                case DLT_IEEE802_11:
+               case DLT_PRISM_HEADER:
                case DLT_IEEE802_11_RADIO_AVS:
-               case DLT_PPI:
                case DLT_IEEE802_11_RADIO:
-               case DLT_PRISM_HEADER:
+               case DLT_PPI:
                        /*
                         * Oh, yuk.
                         *
@@ -7184,6 +7525,31 @@ gen_p80211_type(int type, int mask)
                bpf_error("802.11 link-layer types supported only on 802.11");
                /* NOTREACHED */
        }
+
+       return (b0);
+}
+
+struct block *
+gen_p80211_fcdir(int fcdir)
+{
+       struct block *b0;
+
+       switch (linktype) {
+
+       case DLT_IEEE802_11:
+       case DLT_PRISM_HEADER:
+       case DLT_IEEE802_11_RADIO_AVS:
+       case DLT_IEEE802_11_RADIO:
+               break;
+
+       default:
+               bpf_error("frame direction supported only with 802.11 headers");
+               /* NOTREACHED */
+       }
+
+       b0 = gen_mcmp(OR_LINK, 1, BPF_B, (bpf_int32)fcdir,
+               (bpf_u_int32)IEEE80211_FC1_DIR_MASK);
+
        return (b0);
 }
 
@@ -7611,8 +7977,9 @@ gen_atmtype_abbrev(type)
                is_lane = 1;
                off_mac = off_payload + 2;      /* MAC header */
                off_linktype = off_mac + 12;
-               off_nl = off_mac + 14;          /* Ethernet II */
-               off_nl_nosnap = off_mac + 17;   /* 802.3+802.2 */
+               off_macpl = off_mac + 14;       /* Ethernet */
+               off_nl = 0;                     /* Ethernet II */
+               off_nl_nosnap = 3;              /* 802.3+802.2 */
                break;
 
        case A_LLC: