*/
#ifndef lint
static const char rcsid[] _U_ =
- "@(#) $Header: /tcpdump/master/libpcap/gencode.c,v 1.221.2.53 2007/09/12 19:17:24 guy Exp $ (LBL)";
+ "@(#) $Header: /tcpdump/master/libpcap/gencode.c,v 1.290.2.16 2008-09-22 20:16:01 guy Exp $ (LBL)";
#endif
#ifdef HAVE_CONFIG_H
#include "nlpid.h"
#include "llc.h"
#include "gencode.h"
+#include "ieee80211.h"
#include "atmuni31.h"
#include "sunatmpos.h"
#include "ppp.h"
-#include "sll.h"
+#include "pcap/sll.h"
#include "arcnet.h"
#ifdef HAVE_NET_PFVAR_H
#include <sys/socket.h>
#include <netdb.h> /* for "struct addrinfo" */
#endif /* WIN32 */
#endif /*INET6*/
-#include <pcap-namedb.h>
+#include <pcap/namedb.h>
#define ETHERMTU 1500
static jmp_buf top_ctx;
static pcap_t *bpf_pcap;
-#ifdef WIN32
/* Hack for updating VLAN, MPLS, and PPPoE offsets. */
+#ifdef WIN32
static u_int orig_linktype = (u_int)-1, orig_nl = (u_int)-1, label_stack_depth = (u_int)-1;
#else
static u_int orig_linktype = -1U, orig_nl = -1U, label_stack_depth = -1U;
static void init_linktype(pcap_t *);
+static void init_regs(void);
static int alloc_reg(void);
static void free_reg(int);
*/
enum e_offrel {
OR_PACKET, /* relative to the beginning of the packet */
- OR_LINK, /* relative to the link-layer header */
+ OR_LINK, /* relative to the beginning of the link-layer header */
+ OR_MACPL, /* relative to the end of the MAC-layer header */
OR_NET, /* relative to the network-layer header */
OR_NET_NOSNAP, /* relative to the network-layer header, with no SNAP header at the link layer */
OR_TRAN_IPV4, /* relative to the transport-layer header, with IPv4 network layer */
static struct block *gen_ncmp(enum e_offrel, bpf_u_int32, bpf_u_int32,
bpf_u_int32, bpf_u_int32, int, bpf_int32);
static struct slist *gen_load_llrel(u_int, u_int);
+static struct slist *gen_load_macplrel(u_int, u_int);
static struct slist *gen_load_a(enum e_offrel, u_int, u_int);
static struct slist *gen_loadx_iphdrlen(void);
static struct block *gen_uncond(int);
static inline struct block *gen_false(void);
static struct block *gen_ether_linktype(int);
static struct block *gen_linux_sll_linktype(int);
-static void insert_radiotap_load_llprefixlen(struct block *);
-static void insert_ppi_load_llprefixlen(struct block *);
-static void insert_load_llprefixlen(struct block *);
+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_compute_vloffsets(struct block *);
static struct slist *gen_llprefixlen(void);
+static struct slist *gen_off_macpl(void);
+static int ethertype_to_ppptype(int);
static struct block *gen_linktype(int);
-static struct block *gen_snap(bpf_u_int32, bpf_u_int32, u_int);
+static struct block *gen_snap(bpf_u_int32, bpf_u_int32);
static struct block *gen_llc_linktype(int);
static struct block *gen_hostop(bpf_u_int32, bpf_u_int32, int, int, u_int, u_int);
#ifdef INET6
static struct slist *xfer_to_a(struct arth *);
static struct block *gen_mac_multicast(int);
static struct block *gen_len(int, int);
+static struct block *gen_check_802_11_data_frame(void);
static struct block *gen_ppi_dlt_check(void);
static struct block *gen_msg_abbrev(int type);
n_errors = 0;
root = NULL;
bpf_pcap = p;
+ init_regs();
if (setjmp(top_ctx)) {
lex_cleanup();
freechunks();
*p = b1;
}
-
void
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
* 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.
+ */
+ ppi_dlt_check = gen_ppi_dlt_check();
+ if (ppi_dlt_check != NULL)
+ gen_and(ppi_dlt_check, p);
- insert_load_llprefixlen(root);
+ backpatch(p, gen_retblk(snaplen));
+ p->sense = !p->sense;
+ backpatch(p, gen_retblk(0));
+ root = p->head;
}
void
* Various code constructs need to know the layout of the data link
* layer. These variables give the necessary offsets from the beginning
* of the packet data.
- *
- * If the link layer has variable_length headers, the offsets are offsets
- * from the end of the link-link-layer header, and "reg_ll_size" is
- * the register number for a register containing the length of the
- * link-layer header. Otherwise, "reg_ll_size" is -1.
*/
-static int reg_ll_size;
/*
* This is the offset of the beginning of the link-layer header from
static u_int off_ll;
/*
- * This is the offset of the beginning of the MAC-layer header.
+ * If there's a variable-length header preceding the link-layer header,
+ * "reg_off_ll" is the register number for a register containing the
+ * length of that header, and therefore the offset of the link-layer
+ * header from the beginning of the raw packet data. Otherwise,
+ * "reg_off_ll" is -1.
+ */
+static int reg_off_ll;
+
+/*
+ * This is the offset of the beginning of the MAC-layer header from
+ * the beginning of the link-layer header.
* It's usually 0, except for ATM LANE, where it's the offset, relative
* to the beginning of the raw packet data, of the Ethernet header.
*/
static u_int off_mac;
/*
+ * This is the offset of the beginning of the MAC-layer payload,
+ * from the beginning of the raw packet data.
+ *
+ * I.e., it's the sum of the length of the link-layer header (without,
+ * for example, any 802.2 LLC header, so it's the MAC-layer
+ * portion of that header), plus any prefix preceding the
+ * link-layer header.
+ */
+static u_int off_macpl;
+
+/*
+ * This is 1 if the offset of the beginning of the MAC-layer payload
+ * from the beginning of the link-layer header is variable-length.
+ */
+static int off_macpl_is_variable;
+
+/*
+ * If the link layer has variable_length headers, "reg_off_macpl"
+ * is the register number for a register containing the length of the
+ * link-layer header plus the length of any variable-length header
+ * preceding the link-layer header. Otherwise, "reg_off_macpl"
+ * is -1.
+ */
+static int reg_off_macpl;
+
+/*
* "off_linktype" is the offset to information in the link-layer header
* giving the packet type. This offset is relative to the beginning
* of the link-layer header (i.e., it doesn't include off_ll).
static u_int off_linktype;
/*
+ * TRUE if "pppoes" appeared in the filter; it causes link-layer type
+ * checks to check the PPP header, assumed to follow a LAN-style link-
+ * layer header and a PPPoE session header.
+ */
+static int is_pppoes = 0;
+
+/*
* TRUE if the link layer includes an ATM pseudo-header.
*/
static int is_atm = 0;
/*
* These are offsets to the beginning of the network-layer header.
- * They are relative to the beginning of the link-layer header (i.e.,
- * they don't include off_ll).
+ * They are relative to the beginning of the MAC-layer payload (i.e.,
+ * they don't include off_ll or off_macpl).
*
* If the link layer never uses 802.2 LLC:
*
off_payload = -1;
/*
+ * And that we're not doing PPPoE.
+ */
+ is_pppoes = 0;
+
+ /*
* And assume we're not doing SS7.
*/
off_li = -1;
off_sls = -1;
/*
- * Also assume it's not 802.11 with a fixed-length radio header.
+ * Also assume it's not 802.11.
*/
off_ll = 0;
+ off_macpl = 0;
+ off_macpl_is_variable = 0;
orig_linktype = -1;
orig_nl = -1;
label_stack_depth = 0;
- reg_ll_size = -1;
+ reg_off_ll = -1;
+ reg_off_macpl = -1;
switch (linktype) {
case DLT_ARCNET:
off_linktype = 2;
- off_nl = 6; /* XXX in reality, variable! */
- off_nl_nosnap = 6; /* no 802.2 LLC */
+ off_macpl = 6;
+ off_nl = 0; /* XXX in reality, variable! */
+ off_nl_nosnap = 0; /* no 802.2 LLC */
return;
case DLT_ARCNET_LINUX:
off_linktype = 4;
- off_nl = 8; /* XXX in reality, variable! */
- off_nl_nosnap = 8; /* no 802.2 LLC */
+ off_macpl = 8;
+ off_nl = 0; /* XXX in reality, variable! */
+ off_nl_nosnap = 0; /* no 802.2 LLC */
return;
case DLT_EN10MB:
off_linktype = 12;
- off_nl = 14; /* Ethernet II */
- off_nl_nosnap = 17; /* 802.3+802.2 */
+ off_macpl = 14; /* Ethernet header length */
+ off_nl = 0; /* Ethernet II */
+ off_nl_nosnap = 3; /* 802.3+802.2 */
return;
case DLT_SLIP:
* header is hacked into our SLIP driver.
*/
off_linktype = -1;
- off_nl = 16;
- off_nl_nosnap = 16; /* no 802.2 LLC */
+ off_macpl = 16;
+ off_nl = 0;
+ off_nl_nosnap = 0; /* no 802.2 LLC */
return;
case DLT_SLIP_BSDOS:
/* XXX this may be the same as the DLT_PPP_BSDOS case */
off_linktype = -1;
/* XXX end */
- off_nl = 24;
- off_nl_nosnap = 24; /* no 802.2 LLC */
+ off_macpl = 24;
+ off_nl = 0;
+ off_nl_nosnap = 0; /* no 802.2 LLC */
return;
case DLT_NULL:
case DLT_LOOP:
off_linktype = 0;
- off_nl = 4;
- off_nl_nosnap = 4; /* no 802.2 LLC */
+ off_macpl = 4;
+ off_nl = 0;
+ off_nl_nosnap = 0; /* no 802.2 LLC */
return;
case DLT_ENC:
off_linktype = 0;
- off_nl = 12;
- off_nl_nosnap = 12; /* no 802.2 LLC */
+ off_macpl = 12;
+ off_nl = 0;
+ off_nl_nosnap = 0; /* no 802.2 LLC */
return;
case DLT_PPP:
case DLT_C_HDLC: /* BSD/OS Cisco HDLC */
case DLT_PPP_SERIAL: /* NetBSD sync/async serial PPP */
off_linktype = 2;
- off_nl = 4;
- off_nl_nosnap = 4; /* no 802.2 LLC */
+ off_macpl = 4;
+ off_nl = 0;
+ off_nl_nosnap = 0; /* no 802.2 LLC */
return;
case DLT_PPP_ETHER:
* only covers session state.
*/
off_linktype = 6;
- off_nl = 8;
- off_nl_nosnap = 8; /* no 802.2 LLC */
+ off_macpl = 8;
+ off_nl = 0;
+ off_nl_nosnap = 0; /* no 802.2 LLC */
return;
case DLT_PPP_BSDOS:
off_linktype = 5;
- off_nl = 24;
- off_nl_nosnap = 24; /* no 802.2 LLC */
+ off_macpl = 24;
+ off_nl = 0;
+ off_nl_nosnap = 0; /* no 802.2 LLC */
return;
case DLT_FDDI:
#ifdef PCAP_FDDIPAD
off_linktype += pcap_fddipad;
#endif
- off_nl = 21; /* FDDI+802.2+SNAP */
- off_nl_nosnap = 16; /* FDDI+802.2 */
+ off_macpl = 13; /* FDDI MAC header length */
#ifdef PCAP_FDDIPAD
- off_nl += pcap_fddipad;
- off_nl_nosnap += pcap_fddipad;
+ off_macpl += pcap_fddipad;
#endif
+ off_nl = 8; /* 802.2+SNAP */
+ off_nl_nosnap = 3; /* 802.2 */
return;
case DLT_IEEE802:
* 8 - figure out which byte that is).
*/
off_linktype = 14;
- off_nl = 22; /* Token Ring+802.2+SNAP */
- off_nl_nosnap = 17; /* Token Ring+802.2 */
+ off_macpl = 14; /* Token Ring MAC header length */
+ off_nl = 8; /* 802.2+SNAP */
+ off_nl_nosnap = 3; /* 802.2 */
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.
* 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_nl = 32; /* 802.11+802.2+SNAP */
- off_nl_nosnap = 27; /* 802.11+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_nl = 32; /* Prism+802.11+802.2+SNAP */
- off_nl_nosnap = 27; /* Prism+802.11+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_nl = 32; /* Radio+802.11+802.2+SNAP */
- off_nl_nosnap = 27; /* Radio+802.11+802.2 */
+ 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_nl = 32; /* 802.11+802.2+SNAP */
- off_nl_nosnap = 27; /* 802.11+802.2 */
+ 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_ATM_RFC1483:
* PPPo{A,E} and a PPP protocol of IP and....
*/
off_linktype = 0;
+ off_macpl = 0; /* packet begins with LLC header */
off_nl = 8; /* 802.2+SNAP */
off_nl_nosnap = 3; /* 802.2 */
return;
off_vpi = SUNATM_VPI_POS;
off_vci = SUNATM_VCI_POS;
off_proto = PROTO_POS;
- off_mac = -1; /* LLC-encapsulated, so no MAC-layer header */
+ off_mac = -1; /* assume LLC-encapsulated, so no MAC-layer header */
off_payload = SUNATM_PKT_BEGIN_POS;
off_linktype = off_payload;
- off_nl = off_payload+8; /* 802.2+SNAP */
- off_nl_nosnap = off_payload+3; /* 802.2 */
+ off_macpl = off_payload; /* if LLC-encapsulated */
+ off_nl = 8; /* 802.2+SNAP */
+ off_nl_nosnap = 3; /* 802.2 */
return;
case DLT_RAW:
off_linktype = -1;
+ off_macpl = 0;
off_nl = 0;
off_nl_nosnap = 0; /* no 802.2 LLC */
return;
case DLT_LINUX_SLL: /* fake header for Linux cooked socket */
off_linktype = 14;
- off_nl = 16;
- off_nl_nosnap = 16; /* no 802.2 LLC */
+ off_macpl = 16;
+ off_nl = 0;
+ off_nl_nosnap = 0; /* no 802.2 LLC */
return;
case DLT_LTALK:
* "long" DDP packet following.
*/
off_linktype = -1;
+ off_macpl = 0;
off_nl = 0;
off_nl_nosnap = 0; /* no 802.2 LLC */
return;
* 2625 says SNAP should be used.
*/
off_linktype = 16;
- off_nl = 24; /* IPFC+802.2+SNAP */
- off_nl_nosnap = 19; /* IPFC+802.2 */
+ off_macpl = 16;
+ off_nl = 8; /* 802.2+SNAP */
+ off_nl_nosnap = 3; /* 802.2 */
return;
case DLT_FRELAY:
* frames (NLPID of 0x80).
*/
off_linktype = -1;
+ off_macpl = 0;
off_nl = 0;
off_nl_nosnap = 0; /* no 802.2 LLC */
return;
*/
case DLT_MFR:
off_linktype = -1;
+ off_macpl = 0;
off_nl = 4;
off_nl_nosnap = 0; /* XXX - for now -> no 802.2 LLC */
return;
case DLT_APPLE_IP_OVER_IEEE1394:
off_linktype = 16;
- off_nl = 18;
- off_nl_nosnap = 18; /* no 802.2 LLC */
+ off_macpl = 18;
+ off_nl = 0;
+ off_nl_nosnap = 0; /* no 802.2 LLC */
return;
case DLT_LINUX_IRDA:
* Currently, only raw "link[N:M]" filtering is supported.
*/
off_linktype = -1;
+ off_macpl = -1;
off_nl = -1;
off_nl_nosnap = -1;
return;
* 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_SYMANTEC_FIREWALL:
off_linktype = 6;
- off_nl = 44; /* Ethernet II */
- off_nl_nosnap = 44; /* XXX - what does it do with 802.3 packets? */
+ off_macpl = 44;
+ off_nl = 0; /* Ethernet II */
+ off_nl_nosnap = 0; /* XXX - what does it do with 802.3 packets? */
return;
#ifdef HAVE_NET_PFVAR_H
case DLT_PFLOG:
off_linktype = 0;
- off_nl = PFLOG_HDRLEN;
- off_nl_nosnap = PFLOG_HDRLEN; /* no 802.2 LLC */
+ off_macpl = PFLOG_HDRLEN;
+ off_nl = 0;
+ off_nl_nosnap = 0; /* no 802.2 LLC */
return;
#endif
case DLT_JUNIPER_CHDLC:
case DLT_JUNIPER_FRELAY:
off_linktype = 4;
- off_nl = 4;
+ off_macpl = 4;
+ off_nl = 0;
off_nl_nosnap = -1; /* no 802.2 LLC */
return;
case DLT_JUNIPER_ATM1:
- off_linktype = 4; /* in reality variable between 4-8 */
- off_nl = 4;
- off_nl_nosnap = 14;
+ off_linktype = 4; /* in reality variable between 4-8 */
+ off_macpl = 4; /* in reality variable between 4-8 */
+ off_nl = 0;
+ off_nl_nosnap = 10;
return;
case DLT_JUNIPER_ATM2:
- off_linktype = 8; /* in reality variable between 8-12 */
- off_nl = 8;
- off_nl_nosnap = 18;
+ off_linktype = 8; /* in reality variable between 8-12 */
+ off_macpl = 8; /* in reality variable between 8-12 */
+ off_nl = 0;
+ off_nl_nosnap = 10;
return;
/* frames captured on a Juniper PPPoE service PIC
* contain raw ethernet frames */
case DLT_JUNIPER_PPPOE:
case DLT_JUNIPER_ETHER:
+ off_macpl = 14;
off_linktype = 16;
off_nl = 18; /* Ethernet II */
off_nl_nosnap = 21; /* 802.3+802.2 */
case DLT_JUNIPER_PPPOE_ATM:
off_linktype = 4;
- off_nl = 6;
- off_nl_nosnap = -1; /* no 802.2 LLC */
+ off_macpl = 6;
+ off_nl = 0;
+ off_nl_nosnap = -1; /* no 802.2 LLC */
return;
case DLT_JUNIPER_GGSN:
off_linktype = 6;
- off_nl = 12;
- off_nl_nosnap = -1; /* no 802.2 LLC */
+ off_macpl = 12;
+ off_nl = 0;
+ off_nl_nosnap = -1; /* no 802.2 LLC */
return;
case DLT_JUNIPER_ES:
off_linktype = 6;
- off_nl = -1; /* not really a network layer but raw IP adresses */
+ off_macpl = -1; /* not really a network layer but raw IP addresses */
+ off_nl = -1; /* not really a network layer but raw IP addresses */
off_nl_nosnap = -1; /* no 802.2 LLC */
return;
case DLT_JUNIPER_MONITOR:
off_linktype = 12;
- off_nl = 12; /* raw IP/IP6 header */
+ off_macpl = 12;
+ off_nl = 0; /* raw IP/IP6 header */
off_nl_nosnap = -1; /* no 802.2 LLC */
return;
case DLT_JUNIPER_SERVICES:
off_linktype = 12;
+ off_macpl = -1; /* L3 proto location dep. on cookie type */
off_nl = -1; /* L3 proto location dep. on cookie type */
off_nl_nosnap = -1; /* no 802.2 LLC */
return;
case DLT_JUNIPER_VP:
off_linktype = 18;
+ off_macpl = -1;
+ off_nl = -1;
+ off_nl_nosnap = -1;
+ return;
+
+ case DLT_JUNIPER_ST:
+ off_linktype = 18;
+ off_macpl = -1;
+ off_nl = -1;
+ off_nl_nosnap = -1;
+ return;
+
+ case DLT_JUNIPER_ISM:
+ off_linktype = 8;
+ off_macpl = -1;
off_nl = -1;
off_nl_nosnap = -1;
return;
off_dpc = 4;
off_sls = 7;
off_linktype = -1;
+ off_macpl = -1;
off_nl = -1;
off_nl_nosnap = -1;
return;
off_dpc = 8;
off_sls = 11;
off_linktype = -1;
+ off_macpl = -1;
+ off_nl = -1;
+ off_nl_nosnap = -1;
+ return;
+
+ case DLT_ERF:
+ off_li = 22;
+ off_sio = 23;
+ off_opc = 24;
+ off_dpc = 24;
+ off_sls = 27;
+ off_linktype = -1;
+ off_macpl = -1;
off_nl = -1;
off_nl_nosnap = -1;
return;
#ifdef DLT_PFSYNC
case DLT_PFSYNC:
off_linktype = -1;
- off_nl = 4;
- off_nl_nosnap = 4;
+ off_macpl = 4;
+ off_nl = 0;
+ off_nl_nosnap = 0;
return;
#endif
* Currently, only raw "link[N:M]" filtering is supported.
*/
off_linktype = -1;
+ off_macpl = -1;
off_nl = -1;
off_nl_nosnap = -1;
return;
* Currently, only raw "link[N:M]" filtering is supported.
*/
off_linktype = -1;
+ off_macpl = -1;
off_nl = -1;
off_nl_nosnap = -1;
return;
* 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:
+ /*
+ * 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_CAN20B:
+ /*
+ * 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_IEEE802_15_4_LINUX:
+ /*
+ * 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_IEEE802_16_MAC_CPS_RADIO:
+ /*
+ * 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_IEEE802_15_4:
+ /*
+ * 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_SITA:
+ /*
+ * 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_RAIF1:
+ /*
+ * 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_IPMB:
+ /*
+ * 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_BLUETOOTH_HCI_H4_WITH_PHDR:
+ /*
+ * 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_AX25_KISS:
+ /*
+ * Currently, only raw "link[N:M]" filtering is supported.
+ */
+ off_linktype = -1; /* variable, min 15, max 71 steps of 7 */
+ off_macpl = -1;
+ off_nl = -1; /* variable, min 16, max 71 steps of 7 */
+ 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;
return s;
}
+/*
+ * Load a value relative to the beginning of the MAC-layer payload.
+ */
+static struct slist *
+gen_load_macplrel(offset, size)
+ u_int offset, size;
+{
+ struct slist *s, *s2;
+
+ s = gen_off_macpl();
+
+ /*
+ * If s is non-null, the offset of the MAC-layer payload is
+ * variable, and s points to a list of instructions that
+ * arrange that the X register contains that offset.
+ *
+ * Otherwise, the offset of the MAC-layer payload is constant,
+ * and is in off_macpl.
+ */
+ if (s != NULL) {
+ /*
+ * The offset of the MAC-layer payload is in 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 {
+ /*
+ * The offset of the MAC-layer payload is constant,
+ * and is in off_macpl; load the value at that offset
+ * plus the specified offset.
+ */
+ s = new_stmt(BPF_LD|BPF_ABS|size);
+ s->s.k = off_macpl + offset;
+ }
+ return s;
+}
/*
* Load a value relative to the beginning of the specified header.
s = gen_load_llrel(offset, size);
break;
+ case OR_MACPL:
+ s = gen_load_macplrel(offset, size);
+ break;
+
case OR_NET:
- s = gen_load_llrel(off_nl + offset, size);
+ s = gen_load_macplrel(off_nl + offset, size);
break;
case OR_NET_NOSNAP:
- s = gen_load_llrel(off_nl_nosnap + offset, size);
+ s = gen_load_macplrel(off_nl_nosnap + offset, size);
break;
case OR_TRAN_IPV4:
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} +
+ * Load the item at {offset of the MAC-layer payload} +
+ * {offset, relative to the start of the MAC-layer
+ * paylod, 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.)
+ * (If the offset of the MAC-layer payload is variable,
+ * it's included in the value in the X register, and
+ * off_macpl is 0.)
*/
s2 = new_stmt(BPF_LD|BPF_IND|size);
- s2->s.k = off_ll + off_nl + offset;
+ s2->s.k = off_macpl + off_nl + offset;
sappend(s, s2);
break;
case OR_TRAN_IPV6:
- s = gen_load_llrel(off_nl + 40 + offset, size);
+ s = gen_load_macplrel(off_nl + 40 + offset, size);
break;
default:
{
struct slist *s, *s2;
- s = gen_llprefixlen();
+ s = gen_off_macpl();
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.
+ * link-layer header, or the link-layer header is itself
+ * variable-length. "s" points to a list of statements
+ * that put the offset of the MAC-layer payload 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 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.
+ * IP header. We need to add to it the offset of
+ * the MAC-layer payload, 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.
+ * link-layer header, and the link-layer header is
+ * fixed-length; load the length of the IPv4 header,
+ * which is at an offset of off_nl from the beginning
+ * of the MAC-layer payload, and thus at an offset
+ * of off_mac_pl + off_nl from the beginning of the
+ * raw packet data.
*/
s = new_stmt(BPF_LDX|BPF_MSH|BPF_B);
- s->s.k = off_ll + off_nl;
+ s->s.k = off_macpl + off_nl;
}
return s;
}
*/
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)
+ b1 = gen_cmp(OR_MACPL, 0, BPF_H, (bpf_int32)
((proto << 8) | proto));
gen_and(b0, b1);
return b1;
* This generates code to check both for the
* IPX LSAP (Ethernet_802.2) and for Ethernet_802.3.
*/
- b0 = gen_cmp(OR_LINK, off_linktype + 2, BPF_B,
- (bpf_int32)LLCSAP_IPX);
- b1 = gen_cmp(OR_LINK, off_linktype + 2, BPF_H,
- (bpf_int32)0xFFFF);
+ b0 = gen_cmp(OR_MACPL, 0, BPF_B, (bpf_int32)LLCSAP_IPX);
+ b1 = gen_cmp(OR_MACPL, 0, BPF_H, (bpf_int32)0xFFFF);
gen_or(b0, b1);
/*
* Now we add code to check for SNAP frames with
* ETHERTYPE_IPX, i.e. Ethernet_SNAP.
*/
- b0 = gen_snap(0x000000, ETHERTYPE_IPX, 14);
+ b0 = gen_snap(0x000000, ETHERTYPE_IPX);
gen_or(b0, b1);
/*
* type of ETHERTYPE_AARP (Appletalk ARP).
*/
if (proto == ETHERTYPE_ATALK)
- b1 = gen_snap(0x080007, ETHERTYPE_ATALK, 14);
+ b1 = gen_snap(0x080007, ETHERTYPE_ATALK);
else /* proto == ETHERTYPE_AARP */
- b1 = gen_snap(0x000000, ETHERTYPE_AARP, 14);
+ b1 = gen_snap(0x000000, ETHERTYPE_AARP);
gen_and(b0, b1);
/*
* (i.e., other SAP values)?
*/
b0 = gen_cmp(OR_LINK, off_linktype, BPF_H, LINUX_SLL_P_802_2);
- b1 = gen_cmp(OR_LINK, off_linktype + 2, BPF_H, (bpf_int32)
+ b1 = gen_cmp(OR_MACPL, 0, BPF_H, (bpf_int32)
((proto << 8) | proto));
gen_and(b0, b1);
return b1;
* then put a check for LINUX_SLL_P_802_2 frames
* before it.
*/
- b0 = gen_cmp(OR_LINK, off_linktype + 2, BPF_B,
- (bpf_int32)LLCSAP_IPX);
- b1 = gen_snap(0x000000, ETHERTYPE_IPX,
- off_linktype + 2);
+ b0 = gen_cmp(OR_MACPL, 0, BPF_B, (bpf_int32)LLCSAP_IPX);
+ b1 = gen_snap(0x000000, ETHERTYPE_IPX);
gen_or(b0, b1);
b0 = gen_cmp(OR_LINK, off_linktype, BPF_H, LINUX_SLL_P_802_2);
gen_and(b0, b1);
* type of ETHERTYPE_AARP (Appletalk ARP).
*/
if (proto == ETHERTYPE_ATALK)
- b1 = gen_snap(0x080007, ETHERTYPE_ATALK,
- off_linktype + 2);
+ b1 = gen_snap(0x080007, ETHERTYPE_ATALK);
else /* proto == ETHERTYPE_AARP */
- b1 = gen_snap(0x000000, ETHERTYPE_AARP,
- off_linktype + 2);
+ b1 = gen_snap(0x000000, ETHERTYPE_AARP);
gen_and(b0, b1);
/*
*/
b0 = gen_cmp(OR_LINK, off_linktype, BPF_H,
LINUX_SLL_P_802_2);
- b1 = gen_cmp(OR_LINK, off_linktype + 2, BPF_B,
+ b1 = gen_cmp(OR_LINK, off_macpl, BPF_B,
(bpf_int32)proto);
gen_and(b0, b1);
return b1;
}
}
-static void
-insert_radiotap_load_llprefixlen(b)
- struct block *b;
+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;
/*
- * Prepend to the statements in this block code to load the
- * length of the radiotap header into the register assigned
- * to hold that length, if one has been assigned.
+ * 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_ll_size != -1) {
+ 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()
+{
+ struct slist *s1, *s2;
+
+ /*
+ * Generate code to load the length of the radiotap 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 2 bytes at offsets of 2 and 3 from the beginning
* of the radiotap header are the length of the radiotap
* it.
*/
s2 = new_stmt(BPF_ST);
- s2->s.k = reg_ll_size;
+ s2->s.k = reg_off_ll;
sappend(s1, s2);
/*
s2 = new_stmt(BPF_MISC|BPF_TAX);
sappend(s1, s2);
- /*
- * Now append all the existing statements in this
- * block to these statements.
- */
- sappend(s1, b->stmts);
- b->stmts = s1;
- }
+ return (s1);
+ } else
+ return (NULL);
}
/*
* 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.
+ * DLT_IEEE802_11) we generate code to check for this too;
+ * that's done in finish_parse().
*/
-static void
-insert_ppi_load_llprefixlen(b)
- struct block *b;
+static struct slist *
+gen_load_ppi_llprefixlen()
{
struct slist *s1, *s2;
/*
- * Prepend to the statements in this block code to load the
- * length of the radiotap header into the register assigned
- * to hold that length, if one has been assigned.
+ * Generate code to load the length of the radiotap header
+ * into the register assigned to hold that length, if one has
+ * been assigned.
*/
- if (reg_ll_size != -1) {
- /*
+ if (reg_off_ll != -1) {
+ /*
* The 2 bytes at offsets of 2 and 3 from the beginning
* of the radiotap header are the length of the radiotap
* header; unfortunately, it's little-endian, so we have
* it.
*/
s2 = new_stmt(BPF_ST);
- s2->s.k = reg_ll_size;
+ s2->s.k = reg_off_ll;
sappend(s1, s2);
/*
s2 = new_stmt(BPF_MISC|BPF_TAX);
sappend(s1, s2);
+ return (s1);
+ } else
+ return (NULL);
+}
+
+/*
+ * Load a value relative to the beginning of the link-layer header after the 802.11
+ * header, i.e. LLC_SNAP.
+ * 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_802_11_header_len(struct slist *s, struct slist *snext)
+{
+ struct slist *s2;
+ 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) {
+ /*
+ * No register has been assigned to the offset of
+ * the MAC-layer payload, which means nobody needs
+ * it; don't bother computing it - just return
+ * what we already have.
+ */
+ return (s);
+ }
+
+ /*
+ * This code is not compatible with the optimizer, as
+ * we are generating jmp instructions within a normal
+ * slist of instructions
+ */
+ no_optimize = 1;
+
+ /*
+ * 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 is no variable-length header preceding the
+ * link-layer header.
+ *
+ * Load the length of the fixed-length prefix preceding
+ * the link-layer header (if any) into the X register,
+ * and store it in the reg_off_macpl register.
+ * That length is off_ll.
+ */
+ s = new_stmt(BPF_LDX|BPF_IMM);
+ s->s.k = off_ll;
+ }
+
+ /*
+ * The X register contains the offset of the beginning of the
+ * link-layer header; add 24, which is the minimum length
+ * of the MAC header for a data frame, to that, and store it
+ * in reg_off_macpl, and then load the Frame Control field,
+ * which is at the offset in the X register, with an indexed load.
+ */
+ s2 = new_stmt(BPF_MISC|BPF_TXA);
+ sappend(s, s2);
+ s2 = new_stmt(BPF_ALU|BPF_ADD|BPF_K);
+ s2->s.k = 24;
+ sappend(s, s2);
+ s2 = new_stmt(BPF_ST);
+ s2->s.k = reg_off_macpl;
+ sappend(s, s2);
+
+ s2 = new_stmt(BPF_LD|BPF_IND|BPF_B);
+ s2->s.k = 0;
+ sappend(s, s2);
+
+ /*
+ * Check the Frame Control field to see if this is a data frame;
+ * a data frame has the 0x08 bit (b3) in that field set and the
+ * 0x04 bit (b2) clear.
+ */
+ sjset_data_frame_1 = new_stmt(JMP(BPF_JSET));
+ sjset_data_frame_1->s.k = 0x08;
+ sappend(s, sjset_data_frame_1);
+
+ /*
+ * If b3 is set, test b2, otherwise go to the first statement of
+ * the rest of the program.
+ */
+ sjset_data_frame_1->s.jt = sjset_data_frame_2 = new_stmt(JMP(BPF_JSET));
+ sjset_data_frame_2->s.k = 0x04;
+ sappend(s, sjset_data_frame_2);
+ sjset_data_frame_1->s.jf = snext;
+
+ /*
+ * If b2 is not set, this is a data frame; test the QoS bit.
+ * Otherwise, go to the first statement of the rest of the
+ * program.
+ */
+ sjset_data_frame_2->s.jt = snext;
+ sjset_data_frame_2->s.jf = sjset_qos = new_stmt(JMP(BPF_JSET));
+ sjset_qos->s.k = 0x80; /* QoS bit */
+ sappend(s, sjset_qos);
+
+ /*
+ * If it's set, add 2 to reg_off_macpl, to skip the QoS
+ * field.
+ * Otherwise, go to the first statement of the rest of the
+ * program.
+ */
+ 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_IMM);
+ s2->s.k = 2;
+ sappend(s, s2);
+ s2 = new_stmt(BPF_ST);
+ s2->s.k = reg_off_macpl;
+ sappend(s, s2);
+
+ /*
+ * 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) {
/*
- * Now append all the existing statements in this
- * block to these statements.
+ * Is the IEEE80211_RADIOTAP_FLAGS bit (0x0000002) set
+ * in the presence flag?
*/
- sappend(s1, b->stmts);
- b->stmts = s1;
+ 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_compute_vloffsets(b)
+ struct block *b;
+{
+ struct slist *s;
+
+ /*
+ * 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;
+
+ case DLT_PPI:
+ s = gen_load_ppi_llprefixlen();
+ break;
+
+ default:
+ s = NULL;
+ break;
+ }
+
+ /*
+ * 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_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 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.
+ */
+ if (s != NULL) {
+ sappend(s, b->stmts);
+ b->stmts = s;
}
}
-
+
static struct block *
gen_ppi_dlt_check(void)
{
return b;
}
-static void
-insert_load_llprefixlen(b)
- struct block *b;
+static struct slist *
+gen_prism_llprefixlen(void)
{
- switch (linktype) {
+ 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();
+ }
- /*
- * 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.
+ /*
+ * Load the register containing the radio length
+ * into the X register.
*/
- case DLT_PPI:
- insert_ppi_load_llprefixlen(b);
- break;
+ s = new_stmt(BPF_LDX|BPF_MEM);
+ s->s.k = reg_off_ll;
+ return s;
+}
- case DLT_IEEE802_11_RADIO:
- insert_radiotap_load_llprefixlen(b);
- break;
+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)
{
struct slist *s;
- if (reg_ll_size == -1) {
+ if (reg_off_ll == -1) {
/*
* We haven't yet assigned a register for the length
* of the radiotap header; allocate one.
*/
- reg_ll_size = alloc_reg();
+ reg_off_ll = alloc_reg();
}
/*
* into the X register.
*/
s = new_stmt(BPF_LDX|BPF_MEM);
- s->s.k = reg_ll_size;
+ s->s.k = reg_off_ll;
return s;
}
{
struct slist *s;
- if (reg_ll_size == -1) {
+ if (reg_off_ll == -1) {
/*
* We haven't yet assigned a register for the length
* of the radiotap header; allocate one.
*/
- reg_ll_size = alloc_reg();
+ reg_off_ll = alloc_reg();
}
/*
- * 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);
- s->s.k = reg_ll_size;
+ s->s.k = reg_off_ll;
return s;
}
-
-
/*
* Generate code to compute the link-layer header length, if necessary,
* putting it into the X register, and to return either a pointer to a
{
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;
}
}
/*
+ * Generate code to load the register containing the offset of the
+ * MAC-layer payload into the X register; if no register for that offset
+ * has been allocated, allocate it first.
+ */
+static struct slist *
+gen_off_macpl(void)
+{
+ struct slist *s;
+
+ if (off_macpl_is_variable) {
+ if (reg_off_macpl == -1) {
+ /*
+ * We haven't yet assigned a register for the offset
+ * of the MAC-layer payload; allocate one.
+ */
+ reg_off_macpl = alloc_reg();
+ }
+
+ /*
+ * Load the register containing the offset of the MAC-layer
+ * payload into the X register.
+ */
+ s = new_stmt(BPF_LDX|BPF_MEM);
+ s->s.k = reg_off_macpl;
+ return s;
+ } else {
+ /*
+ * That offset isn't variable, so we don't need to
+ * generate any code.
+ */
+ return NULL;
+ }
+}
+
+/*
+ * Map an Ethernet type to the equivalent PPP type.
+ */
+static int
+ethertype_to_ppptype(proto)
+ int proto;
+{
+ switch (proto) {
+
+ case ETHERTYPE_IP:
+ proto = PPP_IP;
+ break;
+
+#ifdef INET6
+ case ETHERTYPE_IPV6:
+ proto = PPP_IPV6;
+ break;
+#endif
+
+ case ETHERTYPE_DN:
+ proto = PPP_DECNET;
+ break;
+
+ case ETHERTYPE_ATALK:
+ proto = PPP_APPLE;
+ break;
+
+ case ETHERTYPE_NS:
+ proto = PPP_NS;
+ break;
+
+ case LLCSAP_ISONS:
+ proto = PPP_OSI;
+ break;
+
+ case LLCSAP_8021D:
+ /*
+ * I'm assuming the "Bridging PDU"s that go
+ * over PPP are Spanning Tree Protocol
+ * Bridging PDUs.
+ */
+ proto = PPP_BRPDU;
+ break;
+
+ case LLCSAP_IPX:
+ proto = PPP_IPX;
+ break;
+ }
+ return (proto);
+}
+
+/*
* Generate code to match a particular packet type by matching the
* link-layer type field or fields in the 802.2 LLC header.
*
switch (proto) {
case ETHERTYPE_IP:
case PPP_IP:
- /* FIXME add other L3 proto IDs */
+ /* FIXME add other L3 proto IDs */
return gen_mpls_linktype(Q_IP);
case ETHERTYPE_IPV6:
case PPP_IPV6:
- /* FIXME add other L3 proto IDs */
+ /* FIXME add other L3 proto IDs */
return gen_mpls_linktype(Q_IPV6);
default:
}
}
+ /*
+ * Are we testing PPPoE packets?
+ */
+ if (is_pppoes) {
+ /*
+ * The PPPoE session header is part of the
+ * MAC-layer payload, so all references
+ * should be relative to the beginning of
+ * that payload.
+ */
+
+ /*
+ * We use Ethernet protocol types inside libpcap;
+ * map them to the corresponding PPP protocol types.
+ */
+ proto = ethertype_to_ppptype(proto);
+ return gen_cmp(OR_MACPL, off_linktype, BPF_H, (bpf_int32)proto);
+ }
+
switch (linktype) {
case DLT_EN10MB:
}
break;
- case DLT_PPI:
- case DLT_FDDI:
- case DLT_IEEE802:
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:
+ /*
+ * Check that we have a data frame.
+ */
+ b0 = gen_check_802_11_data_frame();
+
+ /*
+ * Now check for the specified link-layer type.
+ */
+ b1 = gen_llc_linktype(proto);
+ gen_and(b0, b1);
+ return b1;
+ /*NOTREACHED*/
+ break;
+
+ case DLT_FDDI:
+ /*
+ * XXX - check for asynchronous frames, as per RFC 1103.
+ */
+ return gen_llc_linktype(proto);
+ /*NOTREACHED*/
+ break;
+
+ case DLT_IEEE802:
+ /*
+ * XXX - check for LLC PDUs, as per IEEE 802.5.
+ */
+ return gen_llc_linktype(proto);
+ /*NOTREACHED*/
+ break;
+
case DLT_ATM_RFC1483:
case DLT_ATM_CLIP:
case DLT_IP_OVER_FC:
* We use Ethernet protocol types inside libpcap;
* map them to the corresponding PPP protocol types.
*/
- switch (proto) {
-
- case ETHERTYPE_IP:
- proto = PPP_IP;
- break;
-
-#ifdef INET6
- case ETHERTYPE_IPV6:
- proto = PPP_IPV6;
- break;
-#endif
-
- case ETHERTYPE_DN:
- proto = PPP_DECNET;
- break;
-
- case ETHERTYPE_ATALK:
- proto = PPP_APPLE;
- break;
-
- case ETHERTYPE_NS:
- proto = PPP_NS;
- break;
-
- case LLCSAP_ISONS:
- proto = PPP_OSI;
- break;
-
- case LLCSAP_8021D:
- /*
- * I'm assuming the "Bridging PDU"s that go
- * over PPP are Spanning Tree Protocol
- * Bridging PDUs.
- */
- proto = PPP_BRPDU;
- break;
-
- case LLCSAP_IPX:
- proto = PPP_IPX;
- break;
- }
+ proto = ethertype_to_ppptype(proto);
+ return gen_cmp(OR_LINK, off_linktype, BPF_H, (bpf_int32)proto);
+ /*NOTREACHED*/
break;
case DLT_PPP_BSDOS:
switch (proto) {
case ETHERTYPE_IP:
+ /*
+ * Also check for Van Jacobson-compressed IP.
+ * XXX - do this for other forms of PPP?
+ */
b0 = gen_cmp(OR_LINK, off_linktype, BPF_H, PPP_IP);
b1 = gen_cmp(OR_LINK, off_linktype, BPF_H, PPP_VJC);
gen_or(b0, b1);
gen_or(b1, b0);
return b0;
-#ifdef INET6
- case ETHERTYPE_IPV6:
- proto = PPP_IPV6;
- /* more to go? */
- break;
-#endif
-
- case ETHERTYPE_DN:
- proto = PPP_DECNET;
- break;
-
- case ETHERTYPE_ATALK:
- proto = PPP_APPLE;
- break;
-
- case ETHERTYPE_NS:
- proto = PPP_NS;
- break;
-
- case LLCSAP_ISONS:
- proto = PPP_OSI;
- break;
-
- case LLCSAP_8021D:
- /*
- * I'm assuming the "Bridging PDU"s that go
- * over PPP are Spanning Tree Protocol
- * Bridging PDUs.
- */
- proto = PPP_BRPDU;
- break;
-
- case LLCSAP_IPX:
- proto = PPP_IPX;
- break;
+ default:
+ proto = ethertype_to_ppptype(proto);
+ return gen_cmp(OR_LINK, off_linktype, BPF_H,
+ (bpf_int32)proto);
}
+ /*NOTREACHED*/
break;
case DLT_NULL:
/*NOTREACHED*/
break;
+ case DLT_MFR:
+ bpf_error("Multi-link Frame Relay link-layer type filtering not implemented");
+
case DLT_JUNIPER_MFR:
case DLT_JUNIPER_MLFR:
case DLT_JUNIPER_MLPPP:
case DLT_JUNIPER_FRELAY:
case DLT_JUNIPER_CHDLC:
case DLT_JUNIPER_VP:
+ case DLT_JUNIPER_ST:
+ case DLT_JUNIPER_ISM:
/* just lets verify the magic number for now -
* on ATM we may have up to 6 different encapsulations on the wire
* and need a lot of heuristics to figure out that the payload
case DLT_DOCSIS:
bpf_error("DOCSIS link-layer type filtering not implemented");
+ case DLT_MTP2:
+ case DLT_MTP2_WITH_PHDR:
+ bpf_error("MTP2 link-layer type filtering not implemented");
+
+ case DLT_ERF:
+ bpf_error("ERF link-layer type filtering not implemented");
+
+#ifdef DLT_PFSYNC
+ case DLT_PFSYNC:
+ bpf_error("PFSYNC link-layer type filtering not implemented");
+#endif
+
case DLT_LINUX_LAPD:
bpf_error("LAPD link-layer type filtering not implemented");
+
+ case DLT_USB:
+ case DLT_USB_LINUX:
+ bpf_error("USB link-layer type filtering not implemented");
+
+ case DLT_BLUETOOTH_HCI_H4:
+ case DLT_BLUETOOTH_HCI_H4_WITH_PHDR:
+ bpf_error("Bluetooth link-layer type filtering not implemented");
+
+ case DLT_CAN20B:
+ bpf_error("CAN20B link-layer type filtering not implemented");
+
+ 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:
+ bpf_error("IEEE 802.16 link-layer type filtering not implemented");
+
+ case DLT_SITA:
+ bpf_error("SITA link-layer type filtering not implemented");
+
+ case DLT_RAIF1:
+ bpf_error("RAIF1 link-layer type filtering not implemented");
+
+ case DLT_IPMB:
+ bpf_error("IPMB link-layer type filtering not implemented");
+
+ case DLT_AX25_KISS:
+ bpf_error("AX.25 link-layer type filtering not implemented");
}
/*
/*
* Any type not handled above should always have an Ethernet
- * type at an offset of "off_linktype". (PPP is partially
- * handled above - the protocol type is mapped from the
- * Ethernet and LLC types we use internally to the corresponding
- * PPP type - but the PPP type is always specified by a value
- * at "off_linktype", so we don't have to do the code generation
- * above.)
+ * type at an offset of "off_linktype".
*/
return gen_cmp(OR_LINK, off_linktype, BPF_H, (bpf_int32)proto);
}
* code and protocol type in the SNAP header.
*/
static struct block *
-gen_snap(orgcode, ptype, offset)
+gen_snap(orgcode, ptype)
bpf_u_int32 orgcode;
bpf_u_int32 ptype;
- u_int offset;
{
u_char snapblock[8];
snapblock[5] = (orgcode >> 0); /* lower 8 bits of organization code */
snapblock[6] = (ptype >> 8); /* upper 8 bits of protocol type */
snapblock[7] = (ptype >> 0); /* lower 8 bits of protocol type */
- return gen_bcmp(OR_LINK, offset, 8, snapblock);
+ return gen_bcmp(OR_MACPL, 0, 8, snapblock);
}
/*
* DSAP, as we do for other types <= ETHERMTU
* (i.e., other SAP values)?
*/
- return gen_cmp(OR_LINK, off_linktype, BPF_H, (bpf_u_int32)
+ return gen_cmp(OR_MACPL, 0, BPF_H, (bpf_u_int32)
((proto << 8) | proto));
case LLCSAP_IPX:
* XXX - are there ever SNAP frames for IPX on
* non-Ethernet 802.x networks?
*/
- return gen_cmp(OR_LINK, off_linktype, BPF_B,
+ return gen_cmp(OR_MACPL, 0, BPF_B,
(bpf_int32)LLCSAP_IPX);
case ETHERTYPE_ATALK:
* XXX - check for an organization code of
* encapsulated Ethernet as well?
*/
- return gen_snap(0x080007, ETHERTYPE_ATALK, off_linktype);
+ return gen_snap(0x080007, ETHERTYPE_ATALK);
default:
/*
* This is an LLC SAP value, so check
* the DSAP.
*/
- return gen_cmp(OR_LINK, off_linktype, BPF_B,
- (bpf_int32)proto);
+ return gen_cmp(OR_MACPL, 0, BPF_B, (bpf_int32)proto);
} else {
/*
* This is an Ethernet type; we assume that it's
* organization code of 0x000000 (encapsulated
* Ethernet), we'd do
*
- * return gen_snap(0x000000, proto,
- * off_linktype);
+ * return gen_snap(0x000000, proto);
*
* here; for now, we don't, as per the above.
* I don't know whether it's worth the extra CPU
* time to do the right check or not.
*/
- return gen_cmp(OR_LINK, off_linktype+6, BPF_H,
- (bpf_int32)proto);
+ return gen_cmp(OR_MACPL, 6, BPF_H, (bpf_int32)proto);
}
}
}
}
/*
- * 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)
register struct block *b0, *b1, *b2;
register struct slist *s;
+#ifdef ENABLE_WLAN_FILTERING_PATCH
+ /*
+ * TODO GV 20070613
+ * We need to disable the optimizer because the optimizer is buggy
+ * and wipes out some LD instructions generated by the below
+ * code to validate the Frame Control bits
+ */
+ no_optimize = 1;
+#endif /* ENABLE_WLAN_FILTERING_PATCH */
+
switch (dir) {
case Q_SRC:
/*
* 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;
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);
case Q_IP:
case Q_ARP:
case Q_RARP:
- switch (linktype) {
- case DLT_EN10MB:
- b0 = gen_ehostop(eaddr, Q_OR);
- break;
- case DLT_FDDI:
- b0 = gen_fhostop(eaddr, Q_OR);
- break;
+ switch (linktype) {
+ case DLT_EN10MB:
+ b0 = gen_ehostop(eaddr, Q_OR);
+ break;
+ case DLT_FDDI:
+ b0 = gen_fhostop(eaddr, Q_OR);
+ break;
case DLT_IEEE802:
- b0 = gen_thostop(eaddr, Q_OR);
- break;
+ 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:
- b0 = gen_wlanhostop(eaddr, Q_OR);
- break;
- case DLT_SUNATM:
- if (is_lane) {
- /*
- * Check that the packet doesn't begin with an
- * LE Control marker. (We've already generated
- * a test for LANE.)
- */
- b1 = gen_cmp(OR_LINK, SUNATM_PKT_BEGIN_POS, BPF_H,
- 0xFF00);
- gen_not(b1);
+ case DLT_PPI:
+ b0 = gen_wlanhostop(eaddr, Q_OR);
+ break;
+ case DLT_SUNATM:
+ if (is_lane) {
+ /*
+ * Check that the packet doesn't begin with an
+ * LE Control marker. (We've already generated
+ * a test for LANE.)
+ */
+ b1 = gen_cmp(OR_LINK, SUNATM_PKT_BEGIN_POS,
+ BPF_H, 0xFF00);
+ gen_not(b1);
- /*
- * Now check the MAC address.
- */
- b0 = gen_ehostop(eaddr, Q_OR);
- gen_and(b1, b0);
- }
- break;
+ /*
+ * Now check the MAC address.
+ */
+ b0 = gen_ehostop(eaddr, Q_OR);
+ gen_and(b1, b0);
+ }
+ break;
case DLT_IP_OVER_FC:
- b0 = gen_ipfchostop(eaddr, Q_OR);
- break;
- default:
- bpf_error(
+ b0 = gen_ipfchostop(eaddr, Q_OR);
+ break;
+ default:
+ bpf_error(
"'gateway' supported only on ethernet/FDDI/token ring/802.11/Fibre Channel");
- }
+ }
b1 = gen_host(**alist++, 0xffffffff, proto, Q_OR, Q_HOST);
while (*alist) {
tmp = gen_host(**alist++, 0xffffffff, proto, Q_OR,
}
/*
- * We don't handle variable-length radiotap here headers yet.
+ * We don't handle variable-length prefixes before the link-layer
+ * header, or variable-length link-layer headers, here yet.
* We might want to add BPF instructions to do the protochain
* work, to simplify that and, on platforms that have a BPF
* interpreter with the new instructions, let the filtering
* branches, and backward branch support is unlikely to appear
* in kernel BPF engines.)
*/
- if (linktype == DLT_IEEE802_11_RADIO)
- bpf_error("'protochain' not supported with radiotap headers");
+ switch (linktype) {
- if (linktype == DLT_PPI)
- bpf_error("'protochain' not supported with PPI headers");
+ case DLT_IEEE802_11:
+ 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");
+ }
no_optimize = 1; /*this code is not compatible with optimzer yet */
/* A = ip->ip_p */
s[i] = new_stmt(BPF_LD|BPF_ABS|BPF_B);
- s[i]->s.k = off_ll + off_nl + 9;
+ s[i]->s.k = off_macpl + off_nl + 9;
i++;
/* X = ip->ip_hl << 2 */
s[i] = new_stmt(BPF_LDX|BPF_MSH|BPF_B);
- s[i]->s.k = off_ll + off_nl;
+ s[i]->s.k = off_macpl + off_nl;
i++;
break;
#ifdef INET6
/* A = ip6->ip_nxt */
s[i] = new_stmt(BPF_LD|BPF_ABS|BPF_B);
- s[i]->s.k = off_ll + off_nl + 6;
+ s[i]->s.k = off_macpl + off_nl + 6;
i++;
/* X = sizeof(struct ip6_hdr) */
s[i] = new_stmt(BPF_LDX|BPF_IMM);
i++;
/* A = P[X + packet head] */
s[i] = new_stmt(BPF_LD|BPF_IND|BPF_B);
- s[i]->s.k = off_ll + off_nl;
+ s[i]->s.k = off_macpl + off_nl;
i++;
/* MEM[reg2] = A */
s[i] = new_stmt(BPF_ST);
i++;
/* A = P[X + packet head]; */
s[i] = new_stmt(BPF_LD|BPF_IND|BPF_B);
- s[i]->s.k = off_ll + off_nl;
+ s[i]->s.k = off_macpl + off_nl;
i++;
/* A += 1 */
s[i] = new_stmt(BPF_ALU|BPF_ADD|BPF_K);
i++;
/* A = P[X + packet head]; */
s[i] = new_stmt(BPF_LD|BPF_IND|BPF_B);
- s[i]->s.k = off_ll + off_nl;
+ s[i]->s.k = off_macpl + off_nl;
i++;
/* MEM[reg2] = A */
s[i] = new_stmt(BPF_ST);
i++;
/* A = P[X + packet head] */
s[i] = new_stmt(BPF_LD|BPF_IND|BPF_B);
- s[i]->s.k = off_ll + off_nl;
+ s[i]->s.k = off_macpl + off_nl;
i++;
/* A += 2 */
s[i] = new_stmt(BPF_ALU|BPF_ADD|BPF_K);
#endif
}
+static struct block *
+gen_check_802_11_data_frame()
+{
+ struct slist *s;
+ struct block *b0, *b1;
+
+ /*
+ * A data frame has the 0x08 bit (b3) in the frame control field set
+ * and the 0x04 bit (b2) clear.
+ */
+ s = gen_load_a(OR_LINK, 0, BPF_B);
+ b0 = new_block(JMP(BPF_JSET));
+ b0->s.k = 0x08;
+ b0->stmts = s;
+
+ s = gen_load_a(OR_LINK, 0, BPF_B);
+ b1 = new_block(JMP(BPF_JSET));
+ b1->s.k = 0x04;
+ b1->stmts = s;
+ gen_not(b1);
+
+ gen_and(b1, b0);
+
+ return b0;
+}
/*
* Generate code that checks whether the packet is a packet for protocol
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)
else
bpf_error("unknown protocol: %s", name);
-
case Q_UNDEF:
syntax();
/* NOTREACHED */
struct block *b, *tmp;
if ((q.addr == Q_HOST || q.addr == Q_DEFAULT) && q.proto == Q_LINK) {
- switch (linktype) {
- case DLT_EN10MB:
- return gen_ehostop(eaddr, (int)q.dir);
- case DLT_FDDI:
- return gen_fhostop(eaddr, (int)q.dir);
- case DLT_IEEE802:
- return gen_thostop(eaddr, (int)q.dir);
- case DLT_IEEE802_11:
- 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:
- if (is_lane) {
- /*
- * Check that the packet doesn't begin with an
- * LE Control marker. (We've already generated
- * a test for LANE.)
- */
- tmp = gen_cmp(OR_LINK, SUNATM_PKT_BEGIN_POS, BPF_H,
- 0xFF00);
- gen_not(tmp);
-
- /*
- * Now check the MAC address.
- */
- b = gen_ehostop(eaddr, (int)q.dir);
- gen_and(tmp, b);
- return b;
- }
- break;
- case DLT_IP_OVER_FC:
- return gen_ipfchostop(eaddr, (int)q.dir);
- default:
- bpf_error("ethernet addresses supported only on ethernet/FDDI/token ring/802.11/ATM LANE/Fibre Channel");
- break;
- }
+ switch (linktype) {
+ case DLT_EN10MB:
+ return gen_ehostop(eaddr, (int)q.dir);
+ case DLT_FDDI:
+ return gen_fhostop(eaddr, (int)q.dir);
+ 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_PPI:
+ return gen_wlanhostop(eaddr, (int)q.dir);
+ case DLT_SUNATM:
+ if (is_lane) {
+ /*
+ * Check that the packet doesn't begin with an
+ * LE Control marker. (We've already generated
+ * a test for LANE.)
+ */
+ tmp = gen_cmp(OR_LINK, SUNATM_PKT_BEGIN_POS, BPF_H,
+ 0xFF00);
+ gen_not(tmp);
+
+ /*
+ * Now check the MAC address.
+ */
+ b = gen_ehostop(eaddr, (int)q.dir);
+ gen_and(tmp, b);
+ return b;
+ }
+ break;
+ case DLT_IP_OVER_FC:
+ return gen_ipfchostop(eaddr, (int)q.dir);
+ default:
+ bpf_error("ethernet addresses supported only on ethernet/FDDI/token ring/802.11/ATM LANE/Fibre Channel");
+ break;
+ }
}
bpf_error("ethernet address used in non-ether expression");
/* NOTREACHED */
* XXX - are there any cases where we want
* off_nl_nosnap?
*/
- s = gen_llprefixlen();
+ s = gen_off_macpl();
/*
* 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. Add to it the offset computed
- * into the register specified by "index", and move that
- * into the X register. Otherwise, just load into the X
+ * X register contains the offset of the MAC-layer
+ * payload. Add to it the offset computed into the
+ * register specified by "index", and move that into
+ * the X register. Otherwise, just load into the X
* register the offset computed into the register specifed
* by "index".
*/
/*
* Load the item at the sum of the offset we've put in the
* X register, the offset of the start of the network
- * layer header, and the offset of the start of the link
- * layer header (which is 0 if the radio header is
- * variable-length; that header length is what we put
- * into the X register and then added to the index).
+ * layer header from the beginning of the MAC-layer
+ * payload, and the purported offset of the start of the
+ * MAC-layer payload (which might be 0 if there's a
+ * variable-length prefix before the link-layer header
+ * or the link-layer header itself is variable-length;
+ * the variable-length offset of the start of the
+ * MAC-layer payload is what we put into the X register
+ * and then added to the index).
*/
tmp = new_stmt(BPF_LD|BPF_IND|size);
- tmp->s.k = off_ll + off_nl;
+ tmp->s.k = off_macpl + off_nl;
sappend(s, tmp);
sappend(inst->s, s);
/*
* The X register now contains the sum of the length
* of any variable-length header preceding the link-layer
- * header and the length of the network-layer header.
+ * header, any variable-length link-layer header, and the
+ * length of the network-layer header.
+ *
* Load into the A register the offset relative to
* the beginning of the transport layer header,
* add the X register to that, move that to the
* X register, and load with an offset from the
* X register equal to the offset of the network
* layer header relative to the beginning of
- * the link-layer header plus the length of any
- * fixed-length header preceding the link-layer
- * header.
+ * the MAC-layer payload plus the fixed-length
+ * portion of the offset of the MAC-layer payload
+ * from the beginning of the raw packet data.
*/
sappend(s, xfer_to_a(inst));
sappend(s, new_stmt(BPF_ALU|BPF_ADD|BPF_X));
sappend(s, new_stmt(BPF_MISC|BPF_TAX));
sappend(s, tmp = new_stmt(BPF_LD|BPF_IND|size));
- tmp->s.k = off_ll + off_nl;
+ tmp->s.k = off_macpl + off_nl;
sappend(inst->s, s);
/*
static int curreg;
/*
+ * Initialize the table of used registers and the current register.
+ */
+static void
+init_regs()
+{
+ curreg = 0;
+ memset(regused, 0, sizeof regused);
+}
+
+/*
* Return the next free register.
*/
static int
case Q_DEFAULT:
case Q_LINK:
- switch (linktype) {
- case DLT_ARCNET:
- case DLT_ARCNET_LINUX:
- return gen_ahostop(abroadcast, Q_DST);
- case DLT_EN10MB:
- return gen_ehostop(ebroadcast, Q_DST);
- case DLT_FDDI:
- return gen_fhostop(ebroadcast, Q_DST);
- case DLT_IEEE802:
- return gen_thostop(ebroadcast, Q_DST);
- case DLT_IEEE802_11:
- 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);
- case DLT_SUNATM:
- if (is_lane) {
- /*
- * Check that the packet doesn't begin with an
- * LE Control marker. (We've already generated
- * a test for LANE.)
- */
- b1 = gen_cmp(OR_LINK, SUNATM_PKT_BEGIN_POS, BPF_H,
- 0xFF00);
- gen_not(b1);
+ switch (linktype) {
+ case DLT_ARCNET:
+ case DLT_ARCNET_LINUX:
+ return gen_ahostop(abroadcast, Q_DST);
+ case DLT_EN10MB:
+ return gen_ehostop(ebroadcast, Q_DST);
+ case DLT_FDDI:
+ return gen_fhostop(ebroadcast, Q_DST);
+ 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:
+ return gen_wlanhostop(ebroadcast, Q_DST);
+ case DLT_IP_OVER_FC:
+ return gen_ipfchostop(ebroadcast, Q_DST);
+ case DLT_SUNATM:
+ if (is_lane) {
+ /*
+ * Check that the packet doesn't begin with an
+ * LE Control marker. (We've already generated
+ * a test for LANE.)
+ */
+ b1 = gen_cmp(OR_LINK, SUNATM_PKT_BEGIN_POS,
+ BPF_H, 0xFF00);
+ gen_not(b1);
- /*
- * Now check the MAC address.
- */
- b0 = gen_ehostop(ebroadcast, Q_DST);
- gen_and(b1, b0);
- return b0;
- }
- break;
- default:
- bpf_error("not a broadcast link");
- }
+ /*
+ * Now check the MAC address.
+ */
+ b0 = gen_ehostop(ebroadcast, Q_DST);
+ gen_and(b1, b0);
+ return b0;
+ }
+ break;
+ default:
+ bpf_error("not a broadcast link");
+ }
break;
case Q_IP:
case Q_DEFAULT:
case Q_LINK:
- switch (linktype) {
- case DLT_ARCNET:
- case DLT_ARCNET_LINUX:
- /* all ARCnet multicasts use the same address */
- return gen_ahostop(abroadcast, Q_DST);
- case DLT_EN10MB:
- /* ether[0] & 1 != 0 */
- return gen_mac_multicast(0);
- case DLT_FDDI:
- /*
- * XXX TEST THIS: MIGHT NOT PORT PROPERLY XXX
- *
- * XXX - was that referring to bit-order issues?
- */
- /* fddi[1] & 1 != 0 */
- return gen_mac_multicast(1);
- case DLT_IEEE802:
- /* tr[2] & 1 != 0 */
- return gen_mac_multicast(2);
- case DLT_IEEE802_11:
- case DLT_IEEE802_11_RADIO_AVS:
- case DLT_PPI:
- case DLT_IEEE802_11_RADIO:
- case DLT_PRISM_HEADER:
- /*
- * Oh, yuk.
- *
- * For control frames, there is no DA.
- *
- * For management frames, DA is at an
- * offset of 4 from the beginning of
- * the packet.
- *
- * For data frames, DA is at an offset
- * of 4 from the beginning of the packet
- * if To DS is clear and at an offset of
- * 16 from the beginning of the packet
- * if To DS is set.
- */
-
- /*
- * Generate the tests to be done for data frames.
- *
- * First, check for To DS set, i.e. "link[1] & 0x01".
- */
- s = gen_load_a(OR_LINK, 1, BPF_B);
- b1 = new_block(JMP(BPF_JSET));
- b1->s.k = 0x01; /* To DS */
- b1->stmts = s;
-
- /*
- * If To DS is set, the DA is at 16.
- */
- b0 = gen_mac_multicast(16);
- gen_and(b1, b0);
-
- /*
- * Now, check for To DS not set, i.e. check
- * "!(link[1] & 0x01)".
- */
- s = gen_load_a(OR_LINK, 1, BPF_B);
- b2 = new_block(JMP(BPF_JSET));
- b2->s.k = 0x01; /* To DS */
- b2->stmts = s;
- gen_not(b2);
-
- /*
- * If To DS is not set, the DA is at 4.
- */
- b1 = gen_mac_multicast(4);
- gen_and(b2, b1);
-
- /*
- * Now OR together the last two checks. That gives
- * the complete set of checks for data frames.
- */
- gen_or(b1, b0);
-
- /*
- * Now check for a data frame.
- * I.e, check "link[0] & 0x08".
- */
- s = gen_load_a(OR_LINK, 0, BPF_B);
- b1 = new_block(JMP(BPF_JSET));
- b1->s.k = 0x08;
- b1->stmts = s;
-
- /*
- * AND that with the checks done for data frames.
- */
- gen_and(b1, b0);
-
- /*
- * If the high-order bit of the type value is 0, this
- * is a management frame.
- * I.e, check "!(link[0] & 0x08)".
- */
- s = gen_load_a(OR_LINK, 0, BPF_B);
- b2 = new_block(JMP(BPF_JSET));
- b2->s.k = 0x08;
- b2->stmts = s;
- gen_not(b2);
-
- /*
- * For management frames, the DA is at 4.
- */
- b1 = gen_mac_multicast(4);
- gen_and(b2, b1);
-
- /*
- * OR that with the checks done for data frames.
- * That gives the checks done for management and
- * data frames.
- */
- gen_or(b1, b0);
-
- /*
- * If the low-order bit of the type value is 1,
- * this is either a control frame or a frame
- * with a reserved type, and thus not a
- * frame with an SA.
- *
- * I.e., check "!(link[0] & 0x04)".
- */
- s = gen_load_a(OR_LINK, 0, BPF_B);
- b1 = new_block(JMP(BPF_JSET));
- b1->s.k = 0x04;
- b1->stmts = s;
- gen_not(b1);
-
- /*
- * AND that with the checks for data and management
- * frames.
- */
- gen_and(b1, b0);
- return b0;
- case DLT_IP_OVER_FC:
- b0 = gen_mac_multicast(2);
- return b0;
- case DLT_SUNATM:
- if (is_lane) {
+ switch (linktype) {
+ case DLT_ARCNET:
+ case DLT_ARCNET_LINUX:
+ /* all ARCnet multicasts use the same address */
+ return gen_ahostop(abroadcast, Q_DST);
+ case DLT_EN10MB:
+ /* ether[0] & 1 != 0 */
+ return gen_mac_multicast(0);
+ case DLT_FDDI:
/*
- * Check that the packet doesn't begin with an
- * LE Control marker. (We've already generated
- * a test for LANE.)
+ * XXX TEST THIS: MIGHT NOT PORT PROPERLY XXX
+ *
+ * XXX - was that referring to bit-order issues?
*/
- b1 = gen_cmp(OR_LINK, SUNATM_PKT_BEGIN_POS, BPF_H,
- 0xFF00);
+ /* fddi[1] & 1 != 0 */
+ return gen_mac_multicast(1);
+ case DLT_IEEE802:
+ /* 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_IEEE802_11_RADIO:
+ case DLT_PPI:
+ /*
+ * Oh, yuk.
+ *
+ * For control frames, there is no DA.
+ *
+ * For management frames, DA is at an
+ * offset of 4 from the beginning of
+ * the packet.
+ *
+ * For data frames, DA is at an offset
+ * of 4 from the beginning of the packet
+ * if To DS is clear and at an offset of
+ * 16 from the beginning of the packet
+ * if To DS is set.
+ */
+
+ /*
+ * Generate the tests to be done for data frames.
+ *
+ * First, check for To DS set, i.e. "link[1] & 0x01".
+ */
+ s = gen_load_a(OR_LINK, 1, BPF_B);
+ b1 = new_block(JMP(BPF_JSET));
+ b1->s.k = 0x01; /* To DS */
+ b1->stmts = s;
+
+ /*
+ * If To DS is set, the DA is at 16.
+ */
+ b0 = gen_mac_multicast(16);
+ gen_and(b1, b0);
+
+ /*
+ * Now, check for To DS not set, i.e. check
+ * "!(link[1] & 0x01)".
+ */
+ s = gen_load_a(OR_LINK, 1, BPF_B);
+ b2 = new_block(JMP(BPF_JSET));
+ b2->s.k = 0x01; /* To DS */
+ b2->stmts = s;
+ gen_not(b2);
+
+ /*
+ * If To DS is not set, the DA is at 4.
+ */
+ b1 = gen_mac_multicast(4);
+ gen_and(b2, b1);
+
+ /*
+ * Now OR together the last two checks. That gives
+ * the complete set of checks for data frames.
+ */
+ gen_or(b1, b0);
+
+ /*
+ * Now check for a data frame.
+ * I.e, check "link[0] & 0x08".
+ */
+ s = gen_load_a(OR_LINK, 0, BPF_B);
+ b1 = new_block(JMP(BPF_JSET));
+ b1->s.k = 0x08;
+ b1->stmts = s;
+
+ /*
+ * AND that with the checks done for data frames.
+ */
+ gen_and(b1, b0);
+
+ /*
+ * If the high-order bit of the type value is 0, this
+ * is a management frame.
+ * I.e, check "!(link[0] & 0x08)".
+ */
+ s = gen_load_a(OR_LINK, 0, BPF_B);
+ b2 = new_block(JMP(BPF_JSET));
+ b2->s.k = 0x08;
+ b2->stmts = s;
+ gen_not(b2);
+
+ /*
+ * For management frames, the DA is at 4.
+ */
+ b1 = gen_mac_multicast(4);
+ gen_and(b2, b1);
+
+ /*
+ * OR that with the checks done for data frames.
+ * That gives the checks done for management and
+ * data frames.
+ */
+ gen_or(b1, b0);
+
+ /*
+ * If the low-order bit of the type value is 1,
+ * this is either a control frame or a frame
+ * with a reserved type, and thus not a
+ * frame with an SA.
+ *
+ * I.e., check "!(link[0] & 0x04)".
+ */
+ s = gen_load_a(OR_LINK, 0, BPF_B);
+ b1 = new_block(JMP(BPF_JSET));
+ b1->s.k = 0x04;
+ b1->stmts = s;
gen_not(b1);
- /* ether[off_mac] & 1 != 0 */
- b0 = gen_mac_multicast(off_mac);
+ /*
+ * AND that with the checks for data and management
+ * frames.
+ */
gen_and(b1, b0);
return b0;
- }
- break;
- default:
- break;
- }
- /* Link not known to support multicasts */
- break;
+ case DLT_IP_OVER_FC:
+ b0 = gen_mac_multicast(2);
+ return b0;
+ case DLT_SUNATM:
+ if (is_lane) {
+ /*
+ * Check that the packet doesn't begin with an
+ * LE Control marker. (We've already generated
+ * a test for LANE.)
+ */
+ b1 = gen_cmp(OR_LINK, SUNATM_PKT_BEGIN_POS,
+ BPF_H, 0xFF00);
+ gen_not(b1);
+
+ /* ether[off_mac] & 1 != 0 */
+ b0 = gen_mac_multicast(off_mac);
+ gen_and(b1, b0);
+ return b0;
+ }
+ break;
+ default:
+ break;
+ }
+ /* Link not known to support multicasts */
+ break;
case Q_IP:
b0 = gen_linktype(ETHERTYPE_IP);
case DLT_JUNIPER_FRELAY:
case DLT_JUNIPER_CHDLC:
case DLT_JUNIPER_VP:
+ case DLT_JUNIPER_ST:
+ case DLT_JUNIPER_ISM:
/* juniper flags (including direction) are stored
* the byte after the 3-byte magic number */
if (dir) {
/* match incoming packets */
b0 = gen_mcmp(OR_LINK, 3, BPF_B, 1, 0x01);
}
- break;
+ break;
default:
bpf_error("inbound/outbound not supported on linktype %d",
struct block *b0;
u_int len, off;
- if (linktype == DLT_PFLOG) {
- len = sizeof(((struct pfloghdr *)0)->ifname);
- off = offsetof(struct pfloghdr, ifname);
- } else {
- bpf_error("ifname not supported on linktype 0x%x", linktype);
+ if (linktype != DLT_PFLOG) {
+ bpf_error("ifname supported only on PF linktype");
/* NOTREACHED */
}
+ len = sizeof(((struct pfloghdr *)0)->ifname);
+ off = offsetof(struct pfloghdr, ifname);
if (strlen(ifname) >= len) {
bpf_error("ifname interface names can only be %d characters",
len-1);
struct block *b0;
if (linktype != DLT_PFLOG) {
- bpf_error("ruleset not supported on linktype 0x%x", linktype);
+ bpf_error("ruleset supported only on PF linktype");
/* NOTREACHED */
}
+
if (strlen(ruleset) >= sizeof(((struct pfloghdr *)0)->ruleset)) {
bpf_error("ruleset names can only be %ld characters",
(long)(sizeof(((struct pfloghdr *)0)->ruleset) - 1));
/* NOTREACHED */
}
+
b0 = gen_bcmp(OR_LINK, offsetof(struct pfloghdr, ruleset),
strlen(ruleset), (const u_char *)ruleset);
return (b0);
{
struct block *b0;
- if (linktype == DLT_PFLOG) {
- b0 = gen_cmp(OR_LINK, offsetof(struct pfloghdr, rulenr), BPF_W,
- (bpf_int32)rnr);
- } else {
- bpf_error("rnr not supported on linktype 0x%x", linktype);
+ if (linktype != DLT_PFLOG) {
+ bpf_error("rnr supported only on PF linktype");
/* NOTREACHED */
}
+ b0 = gen_cmp(OR_LINK, offsetof(struct pfloghdr, rulenr), BPF_W,
+ (bpf_int32)rnr);
return (b0);
}
struct block *b0;
if (linktype != DLT_PFLOG) {
- bpf_error("srnr not supported on linktype 0x%x", linktype);
+ bpf_error("srnr supported only on PF linktype");
/* NOTREACHED */
}
{
struct block *b0;
- if (linktype == DLT_PFLOG) {
- b0 = gen_cmp(OR_LINK, offsetof(struct pfloghdr, reason), BPF_B,
- (bpf_int32)reason);
- } else {
- bpf_error("reason not supported on linktype 0x%x", linktype);
+ if (linktype != DLT_PFLOG) {
+ bpf_error("reason supported only on PF linktype");
/* NOTREACHED */
}
+ b0 = gen_cmp(OR_LINK, offsetof(struct pfloghdr, reason), BPF_B,
+ (bpf_int32)reason);
return (b0);
}
{
struct block *b0;
- if (linktype == DLT_PFLOG) {
- b0 = gen_cmp(OR_LINK, offsetof(struct pfloghdr, action), BPF_B,
- (bpf_int32)action);
- } else {
- bpf_error("action not supported on linktype 0x%x", linktype);
+ if (linktype != DLT_PFLOG) {
+ bpf_error("action supported only on PF linktype");
/* NOTREACHED */
}
+ b0 = gen_cmp(OR_LINK, offsetof(struct pfloghdr, action), BPF_B,
+ (bpf_int32)action);
return (b0);
}
#else /* !HAVE_NET_PFVAR_H */
}
#endif /* HAVE_NET_PFVAR_H */
+/* IEEE 802.11 wireless header */
+struct block *
+gen_p80211_type(int type, int mask)
+{
+ struct block *b0;
+
+ switch (linktype) {
+
+ case DLT_IEEE802_11:
+ case DLT_PRISM_HEADER:
+ case DLT_IEEE802_11_RADIO_AVS:
+ case DLT_IEEE802_11_RADIO:
+ b0 = gen_mcmp(OR_LINK, 0, BPF_B, (bpf_int32)type,
+ (bpf_int32)mask);
+ break;
+
+ default:
+ 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);
+}
+
struct block *
gen_acode(eaddr, q)
register const u_char *eaddr;
struct qual q;
{
- if ((q.addr == Q_HOST || q.addr == Q_DEFAULT) && q.proto == Q_LINK) {
- if (linktype == DLT_ARCNET || linktype == DLT_ARCNET_LINUX)
- return gen_ahostop(eaddr, (int)q.dir);
+ switch (linktype) {
+
+ case DLT_ARCNET:
+ case DLT_ARCNET_LINUX:
+ if ((q.addr == Q_HOST || q.addr == Q_DEFAULT) &&
+ q.proto == Q_LINK)
+ return (gen_ahostop(eaddr, (int)q.dir));
+ else {
+ bpf_error("ARCnet address used in non-arc expression");
+ /* NOTREACHED */
+ }
+ break;
+
+ default:
+ bpf_error("aid supported only on ARCnet");
+ /* NOTREACHED */
}
bpf_error("ARCnet address used in non-arc expression");
/* NOTREACHED */
bpf_error("no VLAN match after MPLS");
/*
- * Change the offsets to point to the type and data fields within
- * the VLAN packet. Just increment the offsets, so that we
- * can support a hierarchy, e.g. "vlan 300 && vlan 200" to
- * capture VLAN 200 encapsulated within VLAN 100.
+ * Check for a VLAN packet, and then change the offsets to point
+ * to the type and data fields within the VLAN packet. Just
+ * increment the offsets, so that we can support a hierarchy, e.g.
+ * "vlan 300 && vlan 200" to capture VLAN 200 encapsulated within
+ * VLAN 100.
*
* XXX - this is a bit of a kludge. If we were to split the
* compiler into a parser that parses an expression and
* be done assuming a VLAN, even though the "or" could be viewed
* as meaning "or, if this isn't a VLAN packet...".
*/
- orig_linktype = off_linktype; /* save original values */
orig_nl = off_nl;
switch (linktype) {
case DLT_EN10MB:
+ /* check for VLAN */
+ b0 = gen_cmp(OR_LINK, off_linktype, BPF_H,
+ (bpf_int32)ETHERTYPE_8021Q);
+
+ /* If a specific VLAN is requested, check VLAN id */
+ if (vlan_num >= 0) {
+ b1 = gen_mcmp(OR_MACPL, 0, BPF_H,
+ (bpf_int32)vlan_num, 0x0fff);
+ gen_and(b0, b1);
+ b0 = b1;
+ }
+
+ off_macpl += 4;
off_linktype += 4;
+#if 0
off_nl_nosnap += 4;
off_nl += 4;
+#endif
break;
default:
/*NOTREACHED*/
}
- /* check for VLAN */
- b0 = gen_cmp(OR_LINK, orig_linktype, BPF_H, (bpf_int32)ETHERTYPE_8021Q);
-
- /* If a specific VLAN is requested, check VLAN id */
- if (vlan_num >= 0) {
- b1 = gen_mcmp(OR_LINK, orig_nl, BPF_H, (bpf_int32)vlan_num,
- 0x0fff);
- gen_and(b0, b1);
- b0 = b1;
- }
-
return (b0);
}
if (label_stack_depth > 0) {
/* just match the bottom-of-stack bit clear */
- b0 = gen_mcmp(OR_LINK, orig_nl-2, BPF_B, 0, 0x01);
+ b0 = gen_mcmp(OR_MACPL, orig_nl-2, BPF_B, 0, 0x01);
} else {
/*
* Indicate that we're checking MPLS-encapsulated headers,
/* If a specific MPLS label is requested, check it */
if (label_num >= 0) {
label_num = label_num << 12; /* label is shifted 12 bits on the wire */
- b1 = gen_mcmp(OR_LINK, orig_nl, BPF_W, (bpf_int32)label_num,
+ b1 = gen_mcmp(OR_MACPL, orig_nl, BPF_W, (bpf_int32)label_num,
0xfffff000); /* only compare the first 20 bits */
gen_and(b0, b1);
b0 = b1;
/*
* Change the offsets to point to the type and data fields within
- * the PPP packet.
+ * 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
*/
orig_linktype = off_linktype; /* save original values */
orig_nl = off_nl;
+ is_pppoes = 1;
/*
* The "network-layer" protocol is PPPoE, which has a 6-byte
- * PPPoE header, followed by PPP payload, so we set the
- * offsets to the network layer offset plus 6 bytes for
- * the PPPoE header plus the values appropriate for PPP when
- * encapsulated in Ethernet (which means there's no HDLC
- * encapsulation).
+ * PPPoE header, followed by a PPP packet.
+ *
+ * There is no HDLC encapsulation for the PPP packet (it's
+ * encapsulated in PPPoES instead), so the link-layer type
+ * starts at the first byte of the PPP packet. For PPPoE,
+ * that offset is relative to the beginning of the total
+ * link-layer payload, including any 802.2 LLC header, so
+ * it's 6 bytes past off_nl.
*/
- off_linktype = orig_nl + 6;
- off_nl = orig_nl + 6 + 2;
- off_nl_nosnap = orig_nl + 6 + 2;
+ off_linktype = off_nl + 6;
/*
- * Set the link-layer type to PPP, as all subsequent tests will
- * be on the encapsulated PPP header.
+ * The network-layer offsets are relative to the beginning
+ * of the MAC-layer payload; that's past the 6-byte
+ * PPPoE header and the 2-byte PPP header.
*/
- linktype = DLT_PPP;
+ off_nl = 6+2;
+ off_nl_nosnap = 6+2;
return b0;
}
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:
case M_FISU:
if ( (linktype != DLT_MTP2) &&
+ (linktype != DLT_ERF) &&
(linktype != DLT_MTP2_WITH_PHDR) )
bpf_error("'fisu' supported only on MTP2");
/* gen_ncmp(offrel, offset, size, mask, jtype, reverse, value) */
case M_LSSU:
if ( (linktype != DLT_MTP2) &&
+ (linktype != DLT_ERF) &&
(linktype != DLT_MTP2_WITH_PHDR) )
bpf_error("'lssu' supported only on MTP2");
b0 = gen_ncmp(OR_PACKET, off_li, BPF_B, 0x3f, BPF_JGT, 1, 2);
case M_MSU:
if ( (linktype != DLT_MTP2) &&
+ (linktype != DLT_ERF) &&
(linktype != DLT_MTP2_WITH_PHDR) )
bpf_error("'msu' supported only on MTP2");
b0 = gen_ncmp(OR_PACKET, off_li, BPF_B, 0x3f, BPF_JGT, 0, 2);
*/
#ifndef lint
static const char rcsid[] _U_ =
- "@(#) $Header: /tcpdump/master/libpcap/pcap-bpf.c,v 1.86.2.12 2007/06/15 17:57:27 guy Exp $ (LBL)";
+ "@(#) $Header: /tcpdump/master/libpcap/pcap-bpf.c,v 1.99.2.17 2008-09-16 18:43:02 guy Exp $ (LBL)";
#endif
#ifdef HAVE_CONFIG_H
#endif
#include <sys/param.h> /* optionally get BSD define */
+#ifdef HAVE_ZEROCOPY_BPF
+#include <sys/mman.h>
+#endif
#include <sys/time.h>
#include <sys/timeb.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/utsname.h>
+#ifdef HAVE_ZEROCOPY_BPF
+#include <machine/atomic.h>
+#endif
+
#include <net/if.h>
#ifdef _AIX
/*
- * Make "pcap.h" not include "pcap-bpf.h"; we are going to include the
+ * Make "pcap.h" not include "pcap/bpf.h"; we are going to include the
* native OS version, as we need "struct bpf_config" from it.
*/
#define PCAP_DONT_INCLUDE_PCAP_BPF_H
#include <string.h>
#include <unistd.h>
+#ifdef HAVE_NET_IF_MEDIA_H
+# include <net/if_media.h>
+#endif
+
#include "pcap-int.h"
#ifdef HAVE_DAG_API
#include "os-proto.h"
#endif
-#include "gencode.h" /* for "no_optimize" */
+#ifdef BIOCGDLTLIST
+# if (defined(HAVE_NET_IF_MEDIA_H) && defined(IFM_IEEE80211)) && !defined(__APPLE__)
+#define HAVE_BSD_IEEE80211
+# endif
+
+# if defined(__APPLE__) || defined(HAVE_BSD_IEEE80211)
+static int find_802_11(struct bpf_dltlist *);
+
+# ifdef HAVE_BSD_IEEE80211
+static int monitor_mode(pcap_t *, int);
+# endif
+
+# if defined(__APPLE__)
+static void remove_en(pcap_t *);
+static void remove_802_11(pcap_t *);
+# endif
+
+# endif /* defined(__APPLE__) || defined(HAVE_BSD_IEEE80211) */
+
+#endif /* BIOCGDLTLIST */
+
+/*
+ * We include the OS's <net/bpf.h>, not our "pcap/bpf.h", so we probably
+ * don't get DLT_DOCSIS defined.
+ */
+#ifndef DLT_DOCSIS
+#define DLT_DOCSIS 143
+#endif
+
+/*
+ * On OS X, we don't even get any of the 802.11-plus-radio-header DLT_'s
+ * defined, even though some of them are used by various Airport drivers.
+ */
+#ifndef DLT_PRISM_HEADER
+#define DLT_PRISM_HEADER 119
+#endif
+#ifndef DLT_AIRONET_HEADER
+#define DLT_AIRONET_HEADER 120
+#endif
+#ifndef DLT_IEEE802_11_RADIO
+#define DLT_IEEE802_11_RADIO 127
+#endif
+#ifndef DLT_IEEE802_11_RADIO_AVS
+#define DLT_IEEE802_11_RADIO_AVS 163
+#endif
+static int pcap_can_set_rfmon_bpf(pcap_t *p);
+static int pcap_activate_bpf(pcap_t *p);
static int pcap_setfilter_bpf(pcap_t *p, struct bpf_program *fp);
static int pcap_setdirection_bpf(pcap_t *, pcap_direction_t);
static int pcap_set_datalink_bpf(pcap_t *p, int dlt);
+#ifdef HAVE_ZEROCOPY_BPF
+/*
+ * For zerocopy bpf, we need to override the setnonblock/getnonblock routines
+ * so we don't call select(2) if the pcap handle is in non-blocking mode. We
+ * preserve the timeout supplied by pcap_open functions to make sure it
+ * does not get clobbered if the pcap handle moves between blocking and non-
+ * blocking mode.
+ */
+static int
+pcap_getnonblock_zbuf(pcap_t *p, char *errbuf)
+{
+ /*
+ * Use a negative value for the timeout to represent that the
+ * pcap handle is in non-blocking mode.
+ */
+ return (p->md.timeout < 0);
+}
+
+static int
+pcap_setnonblock_zbuf(pcap_t *p, int nonblock, char *errbuf)
+{
+ /*
+ * Map each value to the corresponding 2's complement, to
+ * preserve the timeout value provided with pcap_set_timeout.
+ * (from pcap-linux.c).
+ */
+ if (nonblock) {
+ if (p->md.timeout > 0)
+ p->md.timeout = p->md.timeout * -1 - 1;
+ } else
+ if (p->md.timeout < 0)
+ p->md.timeout = (p->md.timeout + 1) * -1;
+ return (0);
+}
+
+/*
+ * Zero-copy specific close method. Un-map the shared buffers then call
+ * pcap_cleanup_live_common.
+ */
+static void
+pcap_cleanup_zbuf(pcap_t *p)
+{
+ /*
+ * Delete the mappings. Note that p->buffer gets initialized to one
+ * of the mmapped regions in this case, so do not try and free it
+ * directly; null it out so that pcap_cleanup_live_common() doesn't
+ * try to free it.
+ */
+ if (p->md.zbuf1 != MAP_FAILED && p->md.zbuf1 != NULL)
+ (void) munmap(p->md.zbuf1, p->md.zbufsize);
+ if (p->md.zbuf2 != MAP_FAILED && p->md.zbuf2 != NULL)
+ (void) munmap(p->md.zbuf2, p->md.zbufsize);
+ p->buffer = NULL;
+ pcap_cleanup_live_common(p);
+}
+
+/*
+ * Zero-copy BPF buffer routines to check for and acknowledge BPF data in
+ * shared memory buffers.
+ *
+ * pcap_next_zbuf_shm(): Check for a newly available shared memory buffer,
+ * and set up p->buffer and cc to reflect one if available. Notice that if
+ * there was no prior buffer, we select zbuf1 as this will be the first
+ * buffer filled for a fresh BPF session.
+ */
+static int
+pcap_next_zbuf_shm(pcap_t *p, int *cc)
+{
+ struct bpf_zbuf_header *bzh;
+
+ if (p->md.zbuffer == p->md.zbuf2 || p->md.zbuffer == NULL) {
+ bzh = (struct bpf_zbuf_header *)p->md.zbuf1;
+ if (bzh->bzh_user_gen !=
+ atomic_load_acq_int(&bzh->bzh_kernel_gen)) {
+ p->md.bzh = bzh;
+ p->md.zbuffer = (u_char *)p->md.zbuf1;
+ p->buffer = p->md.zbuffer + sizeof(*bzh);
+ *cc = bzh->bzh_kernel_len;
+ return (1);
+ }
+ } else if (p->md.zbuffer == p->md.zbuf1) {
+ bzh = (struct bpf_zbuf_header *)p->md.zbuf2;
+ if (bzh->bzh_user_gen !=
+ atomic_load_acq_int(&bzh->bzh_kernel_gen)) {
+ p->md.bzh = bzh;
+ p->md.zbuffer = (u_char *)p->md.zbuf2;
+ p->buffer = p->md.zbuffer + sizeof(*bzh);
+ *cc = bzh->bzh_kernel_len;
+ return (1);
+ }
+ }
+ *cc = 0;
+ return (0);
+}
+
+/*
+ * pcap_next_zbuf() -- Similar to pcap_next_zbuf_shm(), except wait using
+ * select() for data or a timeout, and possibly force rotation of the buffer
+ * in the event we time out or are in immediate mode. Invoke the shared
+ * memory check before doing system calls in order to avoid doing avoidable
+ * work.
+ */
+static int
+pcap_next_zbuf(pcap_t *p, int *cc)
+{
+ struct bpf_zbuf bz;
+ struct timeval tv;
+ struct timespec cur;
+ fd_set r_set;
+ int data, r;
+ int expire, tmout;
+
+#define TSTOMILLI(ts) (((ts)->tv_sec * 1000) + ((ts)->tv_nsec / 1000000))
+ /*
+ * Start out by seeing whether anything is waiting by checking the
+ * next shared memory buffer for data.
+ */
+ data = pcap_next_zbuf_shm(p, cc);
+ if (data)
+ return (data);
+ /*
+ * If a previous sleep was interrupted due to signal delivery, make
+ * sure that the timeout gets adjusted accordingly. This requires
+ * that we analyze when the timeout should be been expired, and
+ * subtract the current time from that. If after this operation,
+ * our timeout is less then or equal to zero, handle it like a
+ * regular timeout.
+ */
+ tmout = p->md.timeout;
+ if (tmout)
+ (void) clock_gettime(CLOCK_MONOTONIC, &cur);
+ if (p->md.interrupted && p->md.timeout) {
+ expire = TSTOMILLI(&p->md.firstsel) + p->md.timeout;
+ tmout = expire - TSTOMILLI(&cur);
+#undef TSTOMILLI
+ if (tmout <= 0) {
+ p->md.interrupted = 0;
+ data = pcap_next_zbuf_shm(p, cc);
+ if (data)
+ return (data);
+ if (ioctl(p->fd, BIOCROTZBUF, &bz) < 0) {
+ (void) snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+ "BIOCROTZBUF: %s", strerror(errno));
+ return (PCAP_ERROR);
+ }
+ return (pcap_next_zbuf_shm(p, cc));
+ }
+ }
+ /*
+ * No data in the buffer, so must use select() to wait for data or
+ * the next timeout. Note that we only call select if the handle
+ * is in blocking mode.
+ */
+ if (p->md.timeout >= 0) {
+ FD_ZERO(&r_set);
+ FD_SET(p->fd, &r_set);
+ if (tmout != 0) {
+ tv.tv_sec = tmout / 1000;
+ tv.tv_usec = (tmout * 1000) % 1000000;
+ }
+ r = select(p->fd + 1, &r_set, NULL, NULL,
+ p->md.timeout != 0 ? &tv : NULL);
+ if (r < 0 && errno == EINTR) {
+ if (!p->md.interrupted && p->md.timeout) {
+ p->md.interrupted = 1;
+ p->md.firstsel = cur;
+ }
+ return (0);
+ } else if (r < 0) {
+ (void) snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+ "select: %s", strerror(errno));
+ return (PCAP_ERROR);
+ }
+ }
+ p->md.interrupted = 0;
+ /*
+ * Check again for data, which may exist now that we've either been
+ * woken up as a result of data or timed out. Try the "there's data"
+ * case first since it doesn't require a system call.
+ */
+ data = pcap_next_zbuf_shm(p, cc);
+ if (data)
+ return (data);
+ /*
+ * Try forcing a buffer rotation to dislodge timed out or immediate
+ * data.
+ */
+ if (ioctl(p->fd, BIOCROTZBUF, &bz) < 0) {
+ (void) snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+ "BIOCROTZBUF: %s", strerror(errno));
+ return (PCAP_ERROR);
+ }
+ return (pcap_next_zbuf_shm(p, cc));
+}
+
+/*
+ * Notify kernel that we are done with the buffer. We don't reset zbuffer so
+ * that we know which buffer to use next time around.
+ */
+static int
+pcap_ack_zbuf(pcap_t *p)
+{
+
+ atomic_store_rel_int(&p->md.bzh->bzh_user_gen,
+ p->md.bzh->bzh_kernel_gen);
+ p->md.bzh = NULL;
+ p->buffer = NULL;
+ return (0);
+}
+#endif
+
+pcap_t *
+pcap_create(const char *device, char *ebuf)
+{
+ pcap_t *p;
+
+#ifdef HAVE_DAG_API
+ if (strstr(device, "dag"))
+ return (dag_create(device, ebuf));
+#endif /* HAVE_DAG_API */
+
+ p = pcap_create_common(device, ebuf);
+ if (p == NULL)
+ return (NULL);
+
+ p->activate_op = pcap_activate_bpf;
+ p->can_set_rfmon_op = pcap_can_set_rfmon_bpf;
+ return (p);
+}
+
+static int
+bpf_open(pcap_t *p)
+{
+ int fd;
+#ifdef HAVE_CLONING_BPF
+ static const char device[] = "/dev/bpf";
+#else
+ int n = 0;
+ char device[sizeof "/dev/bpf0000000000"];
+#endif
+
+#ifdef _AIX
+ /*
+ * Load the bpf driver, if it isn't already loaded,
+ * and create the BPF device entries, if they don't
+ * already exist.
+ */
+ if (bpf_load(p->errbuf) == PCAP_ERROR)
+ return (PCAP_ERROR);
+#endif
+
+#ifdef HAVE_CLONING_BPF
+ if ((fd = open(device, O_RDWR)) == -1 &&
+ (errno != EACCES || (fd = open(device, O_RDONLY)) == -1)) {
+ if (errno == EACCES)
+ fd = PCAP_ERROR_PERM_DENIED;
+ else
+ fd = PCAP_ERROR;
+ snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+ "(cannot open device) %s: %s", device, pcap_strerror(errno));
+ }
+#else
+ /*
+ * Go through all the minors and find one that isn't in use.
+ */
+ do {
+ (void)snprintf(device, sizeof(device), "/dev/bpf%d", n++);
+ /*
+ * Initially try a read/write open (to allow the inject
+ * method to work). If that fails due to permission
+ * issues, fall back to read-only. This allows a
+ * non-root user to be granted specific access to pcap
+ * capabilities via file permissions.
+ *
+ * XXX - we should have an API that has a flag that
+ * controls whether to open read-only or read-write,
+ * so that denial of permission to send (or inability
+ * to send, if sending packets isn't supported on
+ * the device in question) can be indicated at open
+ * time.
+ */
+ fd = open(device, O_RDWR);
+ if (fd == -1 && errno == EACCES)
+ fd = open(device, O_RDONLY);
+ } while (fd < 0 && errno == EBUSY);
+
+ /*
+ * XXX better message for all minors used
+ */
+ if (fd < 0) {
+ if (errno == EACCES)
+ fd = PCAP_ERROR_PERM_DENIED;
+ else
+ fd = PCAP_ERROR;
+ snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "(no devices found) %s: %s",
+ device, pcap_strerror(errno));
+ }
+#endif
+
+ return (fd);
+}
+
+#ifdef BIOCGDLTLIST
+static int
+get_dlt_list(int fd, int v, struct bpf_dltlist *bdlp, char *ebuf)
+{
+ memset(bdlp, 0, sizeof(*bdlp));
+ if (ioctl(fd, BIOCGDLTLIST, (caddr_t)bdlp) == 0) {
+ u_int i;
+ int is_ethernet;
+
+ bdlp->bfl_list = (u_int *) malloc(sizeof(u_int) * (bdlp->bfl_len + 1));
+ if (bdlp->bfl_list == NULL) {
+ (void)snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s",
+ pcap_strerror(errno));
+ return (PCAP_ERROR);
+ }
+
+ if (ioctl(fd, BIOCGDLTLIST, (caddr_t)bdlp) < 0) {
+ (void)snprintf(ebuf, PCAP_ERRBUF_SIZE,
+ "BIOCGDLTLIST: %s", pcap_strerror(errno));
+ free(bdlp->bfl_list);
+ return (PCAP_ERROR);
+ }
+
+ /*
+ * OK, for real Ethernet devices, add DLT_DOCSIS to the
+ * list, so that an application can let you choose it,
+ * in case you're capturing DOCSIS traffic that a Cisco
+ * Cable Modem Termination System is putting out onto
+ * an Ethernet (it doesn't put an Ethernet header onto
+ * the wire, it puts raw DOCSIS frames out on the wire
+ * inside the low-level Ethernet framing).
+ *
+ * A "real Ethernet device" is defined here as a device
+ * that has a link-layer type of DLT_EN10MB and that has
+ * no alternate link-layer types; that's done to exclude
+ * 802.11 interfaces (which might or might not be the
+ * right thing to do, but I suspect it is - Ethernet <->
+ * 802.11 bridges would probably badly mishandle frames
+ * that don't have Ethernet headers).
+ */
+ if (v == DLT_EN10MB) {
+ is_ethernet = 1;
+ for (i = 0; i < bdlp->bfl_len; i++) {
+ if (bdlp->bfl_list[i] != DLT_EN10MB) {
+ is_ethernet = 0;
+ break;
+ }
+ }
+ if (is_ethernet) {
+ /*
+ * We reserved one more slot at the end of
+ * the list.
+ */
+ bdlp->bfl_list[bdlp->bfl_len] = DLT_DOCSIS;
+ bdlp->bfl_len++;
+ }
+ }
+ } else {
+ /*
+ * EINVAL just means "we don't support this ioctl on
+ * this device"; don't treat it as an error.
+ */
+ if (errno != EINVAL) {
+ (void)snprintf(ebuf, PCAP_ERRBUF_SIZE,
+ "BIOCGDLTLIST: %s", pcap_strerror(errno));
+ return (PCAP_ERROR);
+ }
+ }
+ return (0);
+}
+#endif
+
+static int
+pcap_can_set_rfmon_bpf(pcap_t *p)
+{
+#if defined(__APPLE__)
+ struct utsname osinfo;
+ struct ifreq ifr;
+ int fd;
+#ifdef BIOCGDLTLIST
+ struct bpf_dltlist bdl;
+#endif
+
+ /*
+ * The joys of monitor mode on OS X.
+ *
+ * Prior to 10.4, it's not supported at all.
+ *
+ * In 10.4, if adapter enN supports monitor mode, there's a
+ * wltN adapter corresponding to it; you open it, instead of
+ * enN, to get monitor mode. You get whatever link-layer
+ * headers it supplies.
+ *
+ * In 10.5, and, we assume, later releases, if adapter enN
+ * supports monitor mode, it offers, among its selectable
+ * DLT_ values, values that let you get the 802.11 header;
+ * selecting one of those values puts the adapter into monitor
+ * mode (i.e., you can't get 802.11 headers except in monitor
+ * mode, and you can't get Ethernet headers in monitor mode).
+ */
+ if (uname(&osinfo) == -1) {
+ /*
+ * Can't get the OS version; just say "no".
+ */
+ return (0);
+ }
+ /*
+ * We assume osinfo.sysname is "Darwin", because
+ * __APPLE__ is defined. We just check the version.
+ */
+ if (osinfo.release[0] < '8' && osinfo.release[1] == '.') {
+ /*
+ * 10.3 (Darwin 7.x) or earlier.
+ * Monitor mode not supported.
+ */
+ return (0);
+ }
+ if (osinfo.release[0] == '8' && osinfo.release[1] == '.') {
+ /*
+ * 10.4 (Darwin 8.x). s/en/wlt/, and check
+ * whether the device exists.
+ */
+ if (strncmp(p->opt.source, "en", 2) != 0) {
+ /*
+ * Not an enN device; no monitor mode.
+ */
+ return (0);
+ }
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd == -1) {
+ (void)snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+ "socket: %s", pcap_strerror(errno));
+ return (PCAP_ERROR);
+ }
+ strlcpy(ifr.ifr_name, "wlt", sizeof(ifr.ifr_name));
+ strlcat(ifr.ifr_name, p->opt.source + 2, sizeof(ifr.ifr_name));
+ if (ioctl(fd, SIOCGIFFLAGS, (char *)&ifr) < 0) {
+ /*
+ * No such device?
+ */
+ close(fd);
+ return (0);
+ }
+ close(fd);
+ return (1);
+ }
+
+#ifdef BIOCGDLTLIST
+ /*
+ * Everything else is 10.5 or later; for those,
+ * we just open the enN device, and check whether
+ * we have any 802.11 devices.
+ *
+ * First, open a BPF device.
+ */
+ fd = bpf_open(p);
+ if (fd < 0)
+ return (fd);
+
+ /*
+ * Now bind to the device.
+ */
+ (void)strncpy(ifr.ifr_name, p->opt.source, sizeof(ifr.ifr_name));
+ if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) < 0) {
+ if (errno == ENETDOWN) {
+ /*
+ * Return a "network down" indication, so that
+ * the application can report that rather than
+ * saying we had a mysterious failure and
+ * suggest that they report a problem to the
+ * libpcap developers.
+ */
+ close(fd);
+ return (PCAP_ERROR_IFACE_NOT_UP);
+ } else {
+ snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+ "BIOCSETIF: %s: %s",
+ p->opt.source, pcap_strerror(errno));
+ close(fd);
+ return (PCAP_ERROR);
+ }
+ }
+
+ /*
+ * We know the default link type -- now determine all the DLTs
+ * this interface supports. If this fails with EINVAL, it's
+ * not fatal; we just don't get to use the feature later.
+ * (We don't care about DLT_DOCSIS, so we pass DLT_NULL
+ * as the default DLT for this adapter.)
+ */
+ if (get_dlt_list(fd, DLT_NULL, &bdl, p->errbuf) == PCAP_ERROR) {
+ close(fd);
+ return (PCAP_ERROR);
+ }
+ if (find_802_11(&bdl) != -1) {
+ /*
+ * We have an 802.11 DLT, so we can set monitor mode.
+ */
+ free(bdl.bfl_list);
+ close(fd);
+ return (1);
+ }
+ free(bdl.bfl_list);
+#endif /* BIOCGDLTLIST */
+ return (0);
+#elif defined(HAVE_BSD_IEEE80211)
+ int ret;
+
+ ret = monitor_mode(p, 0);
+ if (ret == PCAP_ERROR_RFMON_NOTSUP)
+ return (0); /* not an error, just a "can't do" */
+ if (ret == 0)
+ return (1); /* success */
+ return (ret);
+#else
+ return (0);
+#endif
+}
+
static int
pcap_stats_bpf(pcap_t *p, struct pcap_stat *ps)
{
if (ioctl(p->fd, BIOCGSTATS, (caddr_t)&s) < 0) {
snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCGSTATS: %s",
pcap_strerror(errno));
- return (-1);
+ return (PCAP_ERROR);
}
ps->ps_recv = s.bs_recv;
int n = 0;
register u_char *bp, *ep;
u_char *datap;
- struct bpf_insn *fcode;
#ifdef PCAP_FDDIPAD
register int pad;
#endif
+#ifdef HAVE_ZEROCOPY_BPF
+ int i;
+#endif
- fcode = p->md.use_bpf ? NULL : p->fcode.bf_insns;
again:
/*
* Has "pcap_breakloop()" been called?
if (p->break_loop) {
/*
* Yes - clear the flag that indicates that it
- * has, and return -2 to indicate that we were
- * told to break out of the loop.
+ * has, and return PCAP_ERROR_BREAK to indicate
+ * that we were told to break out of the loop.
*/
p->break_loop = 0;
- return (-2);
+ return (PCAP_ERROR_BREAK);
}
cc = p->cc;
if (p->cc == 0) {
- cc = read(p->fd, (char *)p->buffer, p->bufsize);
+ /*
+ * When reading without zero-copy from a file descriptor, we
+ * use a single buffer and return a length of data in the
+ * buffer. With zero-copy, we update the p->buffer pointer
+ * to point at whatever underlying buffer contains the next
+ * data and update cc to reflect the data found in the
+ * buffer.
+ */
+#ifdef HAVE_ZEROCOPY_BPF
+ if (p->md.zerocopy) {
+ if (p->buffer != NULL)
+ pcap_ack_zbuf(p);
+ i = pcap_next_zbuf(p, &cc);
+ if (i == 0)
+ goto again;
+ if (i < 0)
+ return (PCAP_ERROR);
+ } else
+#endif
+ {
+ cc = read(p->fd, (char *)p->buffer, p->bufsize);
+ }
if (cc < 0) {
/* Don't choke when we get ptraced */
switch (errno) {
*
* For some unknown reason the uiomove()
* operation in the bpf kernel extension
- * used to copy the buffer into user
+ * used to copy the buffer into user
* space sometimes returns EFAULT. I have
* no idea why this is the case given that
- * a kernel debugger shows the user buffer
- * is correct. This problem appears to
- * be mostly mitigated by the memset of
- * the buffer before it is first used.
+ * a kernel debugger shows the user buffer
+ * is correct. This problem appears to
+ * be mostly mitigated by the memset of
+ * the buffer before it is first used.
* Very strange.... Shaun Clowes
*
- * In any case this means that we shouldn't
+ * In any case this means that we shouldn't
* treat EFAULT as a fatal error; as we
* don't have an API for returning
* a "some packets were dropped since
* we just ignore EFAULT and keep reading.
*/
goto again;
-#endif
-
+#endif
+
case EWOULDBLOCK:
return (0);
#if defined(sun) && !defined(BSD)
}
snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "read: %s",
pcap_strerror(errno));
- return (-1);
+ return (PCAP_ERROR);
}
bp = p->buffer;
} else
/*
* Has "pcap_breakloop()" been called?
* If so, return immediately - if we haven't read any
- * packets, clear the flag and return -2 to indicate
- * that we were told to break out of the loop, otherwise
- * leave the flag set, so that the *next* call will break
- * out of the loop without having read any packets, and
- * return the number of packets we've processed so far.
+ * packets, clear the flag and return PCAP_ERROR_BREAK
+ * to indicate that we were told to break out of the loop,
+ * otherwise leave the flag set, so that the *next* call
+ * will break out of the loop without having read any
+ * packets, and return the number of packets we've
+ * processed so far.
*/
if (p->break_loop) {
if (n == 0) {
p->break_loop = 0;
- return (-2);
+ return (PCAP_ERROR_BREAK);
} else {
p->bp = bp;
p->cc = ep - bp;
datap = bp + hdrlen;
/*
* Short-circuit evaluation: if using BPF filter
- * in kernel, no need to do it now.
+ * in kernel, no need to do it now - we already know
+ * the packet passed the filter.
*
#ifdef PCAP_FDDIPAD
* Note: the filter code was generated assuming
* skipping that padding.
#endif
*/
- if (fcode == NULL ||
- bpf_filter(fcode, datap, bhp->bh_datalen, caplen)) {
+ if (p->md.use_bpf ||
+ bpf_filter(p->fcode.bf_insns, datap, bhp->bh_datalen, caplen)) {
struct pcap_pkthdr pkthdr;
pkthdr.ts.tv_sec = bhp->bh_tstamp.tv_sec;
(void)snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
"send: can't turn off BIOCSHDRCMPLT: %s",
pcap_strerror(errno));
- return (-1);
+ return (PCAP_ERROR);
}
/*
if (ret == -1) {
snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "send: %s",
pcap_strerror(errno));
- return (-1);
+ return (PCAP_ERROR);
}
return (ret);
}
#ifdef _AIX
-static int
+static int
bpf_odminit(char *errbuf)
{
char *errstr;
snprintf(errbuf, PCAP_ERRBUF_SIZE,
"bpf_load: odm_initialize failed: %s",
errstr);
- return (-1);
+ return (PCAP_ERROR);
}
if ((odmlockid = odm_lock("/etc/objrepos/config_lock", ODM_WAIT)) == -1) {
snprintf(errbuf, PCAP_ERRBUF_SIZE,
"bpf_load: odm_lock of /etc/objrepos/config_lock failed: %s",
errstr);
- return (-1);
+ return (PCAP_ERROR);
}
return (0);
}
-static int
+static int
bpf_odmcleanup(char *errbuf)
{
char *errstr;
snprintf(errbuf, PCAP_ERRBUF_SIZE,
"bpf_load: odm_unlock failed: %s",
errstr);
- return (-1);
+ return (PCAP_ERROR);
}
if (odm_terminate() == -1) {
snprintf(errbuf, PCAP_ERRBUF_SIZE,
"bpf_load: odm_terminate failed: %s",
errstr);
- return (-1);
+ return (PCAP_ERROR);
}
return (0);
if (bpfloadedflag)
return (0);
- if (bpf_odminit(errbuf) != 0)
- return (-1);
+ if (bpf_odminit(errbuf) == PCAP_ERROR)
+ return (PCAP_ERROR);
major = genmajor(BPF_NAME);
if (major == -1) {
snprintf(errbuf, PCAP_ERRBUF_SIZE,
"bpf_load: genmajor failed: %s", pcap_strerror(errno));
- return (-1);
+ return (PCAP_ERROR);
}
minors = getminor(major, &numminors, BPF_NAME);
snprintf(errbuf, PCAP_ERRBUF_SIZE,
"bpf_load: genminor failed: %s",
pcap_strerror(errno));
- return (-1);
+ return (PCAP_ERROR);
}
}
- if (bpf_odmcleanup(errbuf))
- return (-1);
+ if (bpf_odmcleanup(errbuf) == PCAP_ERROR)
+ return (PCAP_ERROR);
rc = stat(BPF_NODE "0", &sbuf);
if (rc == -1 && errno != ENOENT) {
snprintf(errbuf, PCAP_ERRBUF_SIZE,
"bpf_load: can't stat %s: %s",
BPF_NODE "0", pcap_strerror(errno));
- return (-1);
+ return (PCAP_ERROR);
}
if (rc == -1 || getmajor(sbuf.st_rdev) != major) {
snprintf(errbuf, PCAP_ERRBUF_SIZE,
"bpf_load: can't mknod %s: %s",
buf, pcap_strerror(errno));
- return (-1);
+ return (PCAP_ERROR);
}
}
}
snprintf(errbuf, PCAP_ERRBUF_SIZE,
"bpf_load: could not load driver: %s",
strerror(errno));
- return (-1);
+ return (PCAP_ERROR);
}
}
cfg_km.cmd = CFG_INIT;
cfg_km.kmid = cfg_ld.kmid;
cfg_km.mdilen = sizeof(cfg_bpf);
- cfg_km.mdiptr = (void *)&cfg_bpf;
+ cfg_km.mdiptr = (void *)&cfg_bpf;
for (i = 0; i < BPF_MINORS; i++) {
cfg_bpf.devno = domakedev(major, i);
if (sysconfig(SYS_CFGKMOD, (void *)&cfg_km, sizeof(cfg_km)) == -1) {
snprintf(errbuf, PCAP_ERRBUF_SIZE,
"bpf_load: could not configure driver: %s",
strerror(errno));
- return (-1);
+ return (PCAP_ERROR);
}
}
-
+
bpfloadedflag = 1;
return (0);
}
#endif
-static inline int
-bpf_open(pcap_t *p, char *errbuf)
+/*
+ * Turn off rfmon mode if necessary.
+ */
+static void
+pcap_cleanup_bpf(pcap_t *p)
{
- int fd;
-#ifdef HAVE_CLONING_BPF
- static const char device[] = "/dev/bpf";
-#else
- int n = 0;
- char device[sizeof "/dev/bpf0000000000"];
+#ifdef HAVE_BSD_IEEE80211
+ int sock;
+ struct ifmediareq req;
+ struct ifreq ifr;
#endif
-#ifdef _AIX
- /*
- * Load the bpf driver, if it isn't already loaded,
- * and create the BPF device entries, if they don't
- * already exist.
- */
- if (bpf_load(errbuf) == -1)
- return (-1);
-#endif
+ if (p->md.must_clear != 0) {
+ /*
+ * There's something we have to do when closing this
+ * pcap_t.
+ */
+#ifdef HAVE_BSD_IEEE80211
+ if (p->md.must_clear & MUST_CLEAR_RFMON) {
+ /*
+ * We put the interface into rfmon mode;
+ * take it out of rfmon mode.
+ *
+ * XXX - if somebody else wants it in rfmon
+ * mode, this code cannot know that, so it'll take
+ * it out of rfmon mode.
+ */
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock == -1) {
+ fprintf(stderr,
+ "Can't restore interface flags (socket() failed: %s).\n"
+ "Please adjust manually.\n",
+ strerror(errno));
+ } else {
+ memset(&req, 0, sizeof(req));
+ strncpy(req.ifm_name, p->md.device,
+ sizeof(req.ifm_name));
+ if (ioctl(sock, SIOCGIFMEDIA, &req) < 0) {
+ fprintf(stderr,
+ "Can't restore interface flags (SIOCGIFMEDIA failed: %s).\n"
+ "Please adjust manually.\n",
+ strerror(errno));
+ } else {
+ if (req.ifm_current & IFM_IEEE80211_MONITOR) {
+ /*
+ * Rfmon mode is currently on;
+ * turn it off.
+ */
+ memset(&ifr, 0, sizeof(ifr));
+ (void)strncpy(ifr.ifr_name,
+ p->md.device,
+ sizeof(ifr.ifr_name));
+ ifr.ifr_media =
+ req.ifm_current & ~IFM_IEEE80211_MONITOR;
+ if (ioctl(sock, SIOCSIFMEDIA,
+ &ifr) == -1) {
+ fprintf(stderr,
+ "Can't restore interface flags (SIOCSIFMEDIA failed: %s).\n"
+ "Please adjust manually.\n",
+ strerror(errno));
+ }
+ }
+ }
+ close(sock);
+ }
+ }
+#endif /* HAVE_BSD_IEEE80211 */
-#ifdef HAVE_CLONING_BPF
- if ((fd = open(device, O_RDWR)) == -1 &&
- (errno != EACCES || (fd = open(device, O_RDONLY)) == -1))
- snprintf(errbuf, PCAP_ERRBUF_SIZE,
- "(cannot open device) %s: %s", device, pcap_strerror(errno));
-#else
- /*
- * Go through all the minors and find one that isn't in use.
- */
- do {
- (void)snprintf(device, sizeof(device), "/dev/bpf%d", n++);
/*
- * Initially try a read/write open (to allow the inject
- * method to work). If that fails due to permission
- * issues, fall back to read-only. This allows a
- * non-root user to be granted specific access to pcap
- * capabilities via file permissions.
- *
- * XXX - we should have an API that has a flag that
- * controls whether to open read-only or read-write,
- * so that denial of permission to send (or inability
- * to send, if sending packets isn't supported on
- * the device in question) can be indicated at open
- * time.
+ * Take this pcap out of the list of pcaps for which we
+ * have to take the interface out of some mode.
*/
- fd = open(device, O_RDWR);
- if (fd == -1 && errno == EACCES)
- fd = open(device, O_RDONLY);
- } while (fd < 0 && errno == EBUSY);
+ pcap_remove_from_pcaps_to_close(p);
+ p->md.must_clear = 0;
+ }
+#ifdef HAVE_ZEROCOPY_BPF
/*
- * XXX better message for all minors used
+ * In zero-copy mode, p->buffer is just a pointer into one of the two
+ * memory-mapped buffers, so no need to free it.
*/
- if (fd < 0)
- snprintf(errbuf, PCAP_ERRBUF_SIZE, "(no devices found) %s: %s",
- device, pcap_strerror(errno));
+ if (p->md.zerocopy) {
+ if (p->md.zbuf1 != MAP_FAILED && p->md.zbuf1 != NULL)
+ munmap(p->md.zbuf1, p->md.zbufsize);
+ if (p->md.zbuf2 != MAP_FAILED && p->md.zbuf2 != NULL)
+ munmap(p->md.zbuf2, p->md.zbufsize);
+ }
#endif
-
- return (fd);
+ if (p->md.device != NULL) {
+ free(p->md.device);
+ p->md.device = NULL;
+ }
+ pcap_cleanup_live_common(p);
}
-/*
- * We include the OS's <net/bpf.h>, not our "pcap-bpf.h", so we probably
- * don't get DLT_DOCSIS defined.
- */
-#ifndef DLT_DOCSIS
-#define DLT_DOCSIS 143
+static int
+check_setif_failure(pcap_t *p, int error)
+{
+#ifdef __APPLE__
+ int fd;
+ struct ifreq ifr;
+ int err;
#endif
-pcap_t *
-pcap_open_live(const char *device, int snaplen, int promisc, int to_ms,
- char *ebuf)
+ if (error == ENXIO) {
+ /*
+ * No such device exists.
+ */
+#ifdef __APPLE__
+ if (p->opt.rfmon && strncmp(p->opt.source, "wlt", 3) == 0) {
+ /*
+ * Monitor mode was requested, and we're trying
+ * to open a "wltN" device. Assume that this
+ * is 10.4 and that we were asked to open an
+ * "enN" device; if that device exists, return
+ * "monitor mode not supported on the device".
+ */
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd != -1) {
+ strlcpy(ifr.ifr_name, "en",
+ sizeof(ifr.ifr_name));
+ strlcat(ifr.ifr_name, p->opt.source + 3,
+ sizeof(ifr.ifr_name));
+ if (ioctl(fd, SIOCGIFFLAGS, (char *)&ifr) < 0) {
+ /*
+ * We assume this failed because
+ * the underlying device doesn't
+ * exist.
+ */
+ err = PCAP_ERROR_NO_SUCH_DEVICE;
+ strcpy(p->errbuf, "");
+ } else {
+ /*
+ * The underlying "enN" device
+ * exists, but there's no
+ * corresponding "wltN" device;
+ * that means that the "enN"
+ * device doesn't support
+ * monitor mode, probably because
+ * it's an Ethernet device rather
+ * than a wireless device.
+ */
+ err = PCAP_ERROR_RFMON_NOTSUP;
+ }
+ close(fd);
+ } else {
+ /*
+ * We can't find out whether there's
+ * an underlying "enN" device, so
+ * just report "no such device".
+ */
+ err = PCAP_ERROR_NO_SUCH_DEVICE;
+ strcpy(p->errbuf, "");
+ }
+ return (err);
+ }
+#endif
+ /*
+ * No such device.
+ */
+ strcpy(p->errbuf, "");
+ return (PCAP_ERROR_NO_SUCH_DEVICE);
+ } else if (errno == ENETDOWN) {
+ /*
+ * Return a "network down" indication, so that
+ * the application can report that rather than
+ * saying we had a mysterious failure and
+ * suggest that they report a problem to the
+ * libpcap developers.
+ */
+ return (PCAP_ERROR_IFACE_NOT_UP);
+ } else {
+ /*
+ * Some other error; fill in the error string, and
+ * return PCAP_ERROR.
+ */
+ snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCSETIF: %s: %s",
+ p->opt.source, pcap_strerror(errno));
+ return (PCAP_ERROR);
+ }
+}
+
+static int
+pcap_activate_bpf(pcap_t *p)
{
+ int status = 0;
int fd;
struct ifreq ifr;
struct bpf_version bv;
+#ifdef __APPLE__
+ int sockfd;
+ char *wltdev = NULL;
+#endif
#ifdef BIOCGDLTLIST
struct bpf_dltlist bdl;
+#if defined(__APPLE__) || defined(HAVE_BSD_IEEE80211)
+ int new_dlt;
#endif
+#endif /* BIOCGDLTLIST */
#if defined(BIOCGHDRCMPLT) && defined(BIOCSHDRCMPLT)
u_int spoof_eth_src = 1;
#endif
u_int v;
- pcap_t *p;
struct bpf_insn total_insn;
struct bpf_program total_prog;
struct utsname osinfo;
+ int have_osinfo = 0;
+#ifdef HAVE_ZEROCOPY_BPF
+ struct bpf_zbuf bz;
+ u_int bufmode, zbufmax;
+#endif
-#ifdef HAVE_DAG_API
- if (strstr(device, "dag")) {
- return dag_open_live(device, snaplen, promisc, to_ms, ebuf);
+ fd = bpf_open(p);
+ if (fd < 0) {
+ status = fd;
+ goto bad;
}
-#endif /* HAVE_DAG_API */
-#ifdef BIOCGDLTLIST
- memset(&bdl, 0, sizeof(bdl));
-#endif
+ p->fd = fd;
- p = (pcap_t *)malloc(sizeof(*p));
- if (p == NULL) {
- snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s",
+ if (ioctl(fd, BIOCVERSION, (caddr_t)&bv) < 0) {
+ snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCVERSION: %s",
pcap_strerror(errno));
- return (NULL);
+ status = PCAP_ERROR;
+ goto bad;
}
- memset(p, 0, sizeof(*p));
- fd = bpf_open(p, ebuf);
- if (fd < 0)
+ if (bv.bv_major != BPF_MAJOR_VERSION ||
+ bv.bv_minor < BPF_MINOR_VERSION) {
+ snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+ "kernel bpf filter out of date");
+ status = PCAP_ERROR;
+ goto bad;
+ }
+
+ p->md.device = strdup(p->opt.source);
+ if (p->md.device == NULL) {
+ snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "strdup: %s",
+ pcap_strerror(errno));
+ status = PCAP_ERROR;
goto bad;
+ }
- p->fd = fd;
- p->snapshot = snaplen;
+ /*
+ * Attempt to find out the version of the OS on which we're running.
+ */
+ if (uname(&osinfo) == 0)
+ have_osinfo = 1;
+
+#ifdef __APPLE__
+ /*
+ * See comment in pcap_can_set_rfmon_bpf() for an explanation
+ * of why we check the version number.
+ */
+ if (p->opt.rfmon) {
+ if (have_osinfo) {
+ /*
+ * We assume osinfo.sysname is "Darwin", because
+ * __APPLE__ is defined. We just check the version.
+ */
+ if (osinfo.release[0] < '8' &&
+ osinfo.release[1] == '.') {
+ /*
+ * 10.3 (Darwin 7.x) or earlier.
+ */
+ status = PCAP_ERROR_RFMON_NOTSUP;
+ goto bad;
+ }
+ if (osinfo.release[0] == '8' &&
+ osinfo.release[1] == '.') {
+ /*
+ * 10.4 (Darwin 8.x). s/en/wlt/
+ */
+ if (strncmp(p->opt.source, "en", 2) != 0) {
+ /*
+ * Not an enN device; check
+ * whether the device even exists.
+ */
+ sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sockfd != -1) {
+ strlcpy(ifr.ifr_name,
+ p->opt.source,
+ sizeof(ifr.ifr_name));
+ if (ioctl(sockfd, SIOCGIFFLAGS,
+ (char *)&ifr) < 0) {
+ /*
+ * We assume this
+ * failed because
+ * the underlying
+ * device doesn't
+ * exist.
+ */
+ status = PCAP_ERROR_NO_SUCH_DEVICE;
+ strcpy(p->errbuf, "");
+ } else
+ status = PCAP_ERROR_RFMON_NOTSUP;
+ close(sockfd);
+ } else {
+ /*
+ * We can't find out whether
+ * the device exists, so just
+ * report "no such device".
+ */
+ status = PCAP_ERROR_NO_SUCH_DEVICE;
+ strcpy(p->errbuf, "");
+ }
+ goto bad;
+ }
+ wltdev = malloc(strlen(p->opt.source) + 2);
+ if (wltdev == NULL) {
+ (void)snprintf(p->errbuf,
+ PCAP_ERRBUF_SIZE, "malloc: %s",
+ pcap_strerror(errno));
+ status = PCAP_ERROR;
+ goto bad;
+ }
+ strcpy(wltdev, "wlt");
+ strcat(wltdev, p->opt.source + 2);
+ free(p->opt.source);
+ p->opt.source = wltdev;
+ }
+ /*
+ * Everything else is 10.5 or later; for those,
+ * we just open the enN device, and set the DLT.
+ */
+ }
+ }
+#endif /* __APPLE__ */
+#ifdef HAVE_ZEROCOPY_BPF
+ /*
+ * If the BPF extension to set buffer mode is present, try setting
+ * the mode to zero-copy. If that fails, use regular buffering. If
+ * it succeeds but other setup fails, return an error to the user.
+ */
+ bufmode = BPF_BUFMODE_ZBUF;
+ if (ioctl(fd, BIOCSETBUFMODE, (caddr_t)&bufmode) == 0) {
+ /*
+ * We have zerocopy BPF; use it.
+ */
+ p->md.zerocopy = 1;
+
+ /*
+ * Set the cleanup and set/get nonblocking mode ops
+ * as appropriate for zero-copy mode.
+ */
+ p->cleanup_op = pcap_cleanup_zbuf;
+ p->setnonblock_op = pcap_setnonblock_zbuf;
+ p->getnonblock_op = pcap_getnonblock_zbuf;
+
+ /*
+ * How to pick a buffer size: first, query the maximum buffer
+ * size supported by zero-copy. This also lets us quickly
+ * determine whether the kernel generally supports zero-copy.
+ * Then, if a buffer size was specified, use that, otherwise
+ * query the default buffer size, which reflects kernel
+ * policy for a desired default. Round to the nearest page
+ * size.
+ */
+ if (ioctl(fd, BIOCGETZMAX, (caddr_t)&zbufmax) < 0) {
+ snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCGETZMAX: %s",
+ pcap_strerror(errno));
+ goto bad;
+ }
+
+ if (p->opt.buffer_size != 0) {
+ /*
+ * A buffer size was explicitly specified; use it.
+ */
+ v = p->opt.buffer_size;
+ } else {
+ if ((ioctl(fd, BIOCGBLEN, (caddr_t)&v) < 0) ||
+ v < 32768)
+ v = 32768;
+ }
+#ifndef roundup
+#define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) /* to any y */
+#endif
+ p->md.zbufsize = roundup(v, getpagesize());
+ if (p->md.zbufsize > zbufmax)
+ p->md.zbufsize = zbufmax;
+ p->md.zbuf1 = mmap(NULL, p->md.zbufsize, PROT_READ | PROT_WRITE,
+ MAP_ANON, -1, 0);
+ p->md.zbuf2 = mmap(NULL, p->md.zbufsize, PROT_READ | PROT_WRITE,
+ MAP_ANON, -1, 0);
+ if (p->md.zbuf1 == MAP_FAILED || p->md.zbuf2 == MAP_FAILED) {
+ snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "mmap: %s",
+ pcap_strerror(errno));
+ goto bad;
+ }
+ bzero(&bz, sizeof(bz));
+ bz.bz_bufa = p->md.zbuf1;
+ bz.bz_bufb = p->md.zbuf2;
+ bz.bz_buflen = p->md.zbufsize;
+ if (ioctl(fd, BIOCSETZBUF, (caddr_t)&bz) < 0) {
+ snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCSETZBUF: %s",
+ pcap_strerror(errno));
+ goto bad;
+ }
+ (void)strncpy(ifr.ifr_name, p->opt.source, sizeof(ifr.ifr_name));
+ if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) < 0) {
+ snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCSETIF: %s: %s",
+ p->opt.source, pcap_strerror(errno));
+ goto bad;
+ }
+ v = p->md.zbufsize - sizeof(struct bpf_zbuf_header);
+ } else
+#endif
+ {
+ /*
+ * We don't have zerocopy BPF.
+ * Set the buffer size.
+ */
+ if (p->opt.buffer_size != 0) {
+ /*
+ * A buffer size was explicitly specified; use it.
+ */
+ if (ioctl(fd, BIOCSBLEN,
+ (caddr_t)&p->opt.buffer_size) < 0) {
+ snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+ "BIOCSBLEN: %s: %s", p->opt.source,
+ pcap_strerror(errno));
+ status = PCAP_ERROR;
+ goto bad;
+ }
- if (ioctl(fd, BIOCVERSION, (caddr_t)&bv) < 0) {
- snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCVERSION: %s",
- pcap_strerror(errno));
- goto bad;
- }
- if (bv.bv_major != BPF_MAJOR_VERSION ||
- bv.bv_minor < BPF_MINOR_VERSION) {
- snprintf(ebuf, PCAP_ERRBUF_SIZE,
- "kernel bpf filter out of date");
- goto bad;
- }
+ /*
+ * Now bind to the device.
+ */
+ (void)strncpy(ifr.ifr_name, p->opt.source,
+ sizeof(ifr.ifr_name));
+ if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) < 0) {
+ status = check_setif_failure(p, errno);
+ goto bad;
+ }
+ } else {
+ /*
+ * No buffer size was explicitly specified.
+ *
+ * Try finding a good size for the buffer; 32768 may
+ * be too big, so keep cutting it in half until we
+ * find a size that works, or run out of sizes to try.
+ * If the default is larger, don't make it smaller.
+ */
+ if ((ioctl(fd, BIOCGBLEN, (caddr_t)&v) < 0) ||
+ v < 32768)
+ v = 32768;
+ for ( ; v != 0; v >>= 1) {
+ /*
+ * Ignore the return value - this is because the
+ * call fails on BPF systems that don't have
+ * kernel malloc. And if the call fails, it's
+ * no big deal, we just continue to use the
+ * standard buffer size.
+ */
+ (void) ioctl(fd, BIOCSBLEN, (caddr_t)&v);
- /*
- * Try finding a good size for the buffer; 32768 may be too
- * big, so keep cutting it in half until we find a size
- * that works, or run out of sizes to try. If the default
- * is larger, don't make it smaller.
- *
- * XXX - there should be a user-accessible hook to set the
- * initial buffer size.
- */
- if ((ioctl(fd, BIOCGBLEN, (caddr_t)&v) < 0) || v < 32768)
- v = 32768;
- for ( ; v != 0; v >>= 1) {
- /* Ignore the return value - this is because the call fails
- * on BPF systems that don't have kernel malloc. And if
- * the call fails, it's no big deal, we just continue to
- * use the standard buffer size.
- */
- (void) ioctl(fd, BIOCSBLEN, (caddr_t)&v);
+ (void)strncpy(ifr.ifr_name, p->opt.source,
+ sizeof(ifr.ifr_name));
+ if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) >= 0)
+ break; /* that size worked; we're done */
- (void)strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name));
- if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) >= 0)
- break; /* that size worked; we're done */
+ if (errno != ENOBUFS) {
+ status = check_setif_failure(p, errno);
+ goto bad;
+ }
+ }
- if (errno != ENOBUFS) {
- snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCSETIF: %s: %s",
- device, pcap_strerror(errno));
- goto bad;
+ if (v == 0) {
+ snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+ "BIOCSBLEN: %s: No buffer size worked",
+ p->opt.source);
+ status = PCAP_ERROR;
+ goto bad;
+ }
}
}
- if (v == 0) {
- snprintf(ebuf, PCAP_ERRBUF_SIZE,
- "BIOCSBLEN: %s: No buffer size worked", device);
- goto bad;
- }
-
/* Get the data link layer type. */
if (ioctl(fd, BIOCGDLT, (caddr_t)&v) < 0) {
- snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCGDLT: %s",
+ snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCGDLT: %s",
pcap_strerror(errno));
+ status = PCAP_ERROR;
goto bad;
}
+
#ifdef _AIX
/*
* AIX's BPF returns IFF_ types, not DLT_ types, in BIOCGDLT.
/*
* We don't know what to map this to yet.
*/
- snprintf(ebuf, PCAP_ERRBUF_SIZE, "unknown interface type %u",
+ snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "unknown interface type %u",
v);
+ status = PCAP_ERROR;
goto bad;
}
#endif
break;
}
#endif
-#ifdef PCAP_FDDIPAD
- if (v == DLT_FDDI)
- p->fddipad = PCAP_FDDIPAD;
- else
- p->fddipad = 0;
-#endif
- p->linktype = v;
#ifdef BIOCGDLTLIST
/*
* this interface supports. If this fails with EINVAL, it's
* not fatal; we just don't get to use the feature later.
*/
- if (ioctl(fd, BIOCGDLTLIST, (caddr_t)&bdl) == 0) {
- u_int i;
- int is_ethernet;
+ if (get_dlt_list(fd, v, &bdl, p->errbuf) == -1) {
+ status = PCAP_ERROR;
+ goto bad;
+ }
+ p->dlt_count = bdl.bfl_len;
+ p->dlt_list = bdl.bfl_list;
- bdl.bfl_list = (u_int *) malloc(sizeof(u_int) * (bdl.bfl_len + 1));
- if (bdl.bfl_list == NULL) {
- (void)snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s",
- pcap_strerror(errno));
- goto bad;
+#ifdef __APPLE__
+ /*
+ * Monitor mode fun, continued.
+ *
+ * For 10.5 and, we're assuming, later releases, as noted above,
+ * 802.1 adapters that support monitor mode offer both DLT_EN10MB,
+ * DLT_IEEE802_11, and possibly some 802.11-plus-radio-information
+ * DLT_ value. Choosing one of the 802.11 DLT_ values will turn
+ * monitor mode on.
+ *
+ * Therefore, if the user asked for monitor mode, we filter out
+ * the DLT_EN10MB value, as you can't get that in monitor mode,
+ * and, if the user didn't ask for monitor mode, we filter out
+ * the 802.11 DLT_ values, because selecting those will turn
+ * monitor mode on. Then, for monitor mode, if an 802.11-plus-
+ * radio DLT_ value is offered, we try to select that, otherwise
+ * we try to select DLT_IEEE802_11.
+ */
+ if (have_osinfo) {
+ if (isdigit((unsigned)osinfo.release[0]) &&
+ (osinfo.release[0] == '9' ||
+ isdigit((unsigned)osinfo.release[1]))) {
+ /*
+ * 10.5 (Darwin 9.x), or later.
+ */
+ new_dlt = find_802_11(&bdl);
+ if (new_dlt != -1) {
+ /*
+ * We have at least one 802.11 DLT_ value,
+ * so this is an 802.11 interface.
+ * new_dlt is the best of the 802.11
+ * DLT_ values in the list.
+ */
+ if (p->opt.rfmon) {
+ /*
+ * Our caller wants monitor mode.
+ * Purge DLT_EN10MB from the list
+ * of link-layer types, as selecting
+ * it will keep monitor mode off.
+ */
+ remove_en(p);
+
+ /*
+ * If the new mode we want isn't
+ * the default mode, attempt to
+ * select the new mode.
+ */
+ if (new_dlt != v) {
+ if (ioctl(p->fd, BIOCSDLT,
+ &new_dlt) != -1) {
+ /*
+ * We succeeded;
+ * make this the
+ * new DLT_ value.
+ */
+ v = new_dlt;
+ }
+ }
+ } else {
+ /*
+ * Our caller doesn't want
+ * monitor mode. Unless this
+ * is being done by pcap_open_live(),
+ * purge the 802.11 link-layer types
+ * from the list, as selecting
+ * one of them will turn monitor
+ * mode on.
+ */
+ if (!p->oldstyle)
+ remove_802_11(p);
+ }
+ } else {
+ if (p->opt.rfmon) {
+ /*
+ * The caller requested monitor
+ * mode, but we have no 802.11
+ * link-layer types, so they
+ * can't have it.
+ */
+ status = PCAP_ERROR_RFMON_NOTSUP;
+ goto bad;
+ }
+ }
}
-
- if (ioctl(fd, BIOCGDLTLIST, (caddr_t)&bdl) < 0) {
- (void)snprintf(ebuf, PCAP_ERRBUF_SIZE,
- "BIOCGDLTLIST: %s", pcap_strerror(errno));
- free(bdl.bfl_list);
+ }
+#elif defined(HAVE_BSD_IEEE80211)
+ /*
+ * *BSD with the new 802.11 ioctls.
+ * Do we want monitor mode?
+ */
+ if (p->opt.rfmon) {
+ /*
+ * Try to put the interface into monitor mode.
+ */
+ status = monitor_mode(p, 1);
+ if (status != 0) {
+ /*
+ * We failed.
+ */
goto bad;
}
/*
- * OK, for real Ethernet devices, add DLT_DOCSIS to the
- * list, so that an application can let you choose it,
- * in case you're capturing DOCSIS traffic that a Cisco
- * Cable Modem Termination System is putting out onto
- * an Ethernet (it doesn't put an Ethernet header onto
- * the wire, it puts raw DOCSIS frames out on the wire
- * inside the low-level Ethernet framing).
- *
- * A "real Ethernet device" is defined here as a device
- * that has a link-layer type of DLT_EN10MB and that has
- * no alternate link-layer types; that's done to exclude
- * 802.11 interfaces (which might or might not be the
- * right thing to do, but I suspect it is - Ethernet <->
- * 802.11 bridges would probably badly mishandle frames
- * that don't have Ethernet headers).
+ * We're in monitor mode.
+ * Try to find the best 802.11 DLT_ value and, if we
+ * succeed, try to switch to that mode if we're not
+ * already in that mode.
*/
- if (p->linktype == DLT_EN10MB) {
- is_ethernet = 1;
- for (i = 0; i < bdl.bfl_len; i++) {
- if (bdl.bfl_list[i] != DLT_EN10MB) {
- is_ethernet = 0;
- break;
+ new_dlt = find_802_11(&bdl);
+ if (new_dlt != -1) {
+ /*
+ * We have at least one 802.11 DLT_ value.
+ * new_dlt is the best of the 802.11
+ * DLT_ values in the list.
+ *
+ * If the new mode we want isn't the default mode,
+ * attempt to select the new mode.
+ */
+ if (new_dlt != v) {
+ if (ioctl(p->fd, BIOCSDLT, &new_dlt) != -1) {
+ /*
+ * We succeeded; make this the
+ * new DLT_ value.
+ */
+ v = new_dlt;
}
}
- if (is_ethernet) {
- /*
- * We reserved one more slot at the end of
- * the list.
- */
- bdl.bfl_list[bdl.bfl_len] = DLT_DOCSIS;
- bdl.bfl_len++;
- }
- }
- p->dlt_count = bdl.bfl_len;
- p->dlt_list = bdl.bfl_list;
- } else {
- if (errno != EINVAL) {
- (void)snprintf(ebuf, PCAP_ERRBUF_SIZE,
- "BIOCGDLTLIST: %s", pcap_strerror(errno));
- goto bad;
}
}
-#endif
+#endif /* various platforms */
+#endif /* BIOCGDLTLIST */
/*
* If this is an Ethernet device, and we don't have a DLT_ list,
* some other way of determining whether it's an Ethernet or 802.11
* device.)
*/
- if (p->linktype == DLT_EN10MB && p->dlt_count == 0) {
+ if (v == DLT_EN10MB && p->dlt_count == 0) {