summaryrefslogtreecommitdiffstats
path: root/contrib/libpcap/pcap-bpf.c
diff options
context:
space:
mode:
authorsam <sam@FreeBSD.org>2005-05-29 17:46:52 +0000
committersam <sam@FreeBSD.org>2005-05-29 17:46:52 +0000
commit70f7ae46f5124d9a1805484495b57250a715bed4 (patch)
treeb733a54335042f876f8969020dc9dc4408f485f4 /contrib/libpcap/pcap-bpf.c
parentda13a5a9d72229b2a6026a49b9977934bdef13ed (diff)
downloadFreeBSD-src-70f7ae46f5124d9a1805484495b57250a715bed4.zip
FreeBSD-src-70f7ae46f5124d9a1805484495b57250a715bed4.tar.gz
Virgin import of libpcap v0.9.1 (alpha 096) from tcpdump.org
Diffstat (limited to 'contrib/libpcap/pcap-bpf.c')
-rw-r--r--contrib/libpcap/pcap-bpf.c274
1 files changed, 233 insertions, 41 deletions
diff --git a/contrib/libpcap/pcap-bpf.c b/contrib/libpcap/pcap-bpf.c
index 5b0eab6..085f181 100644
--- a/contrib/libpcap/pcap-bpf.c
+++ b/contrib/libpcap/pcap-bpf.c
@@ -20,7 +20,7 @@
*/
#ifndef lint
static const char rcsid[] _U_ =
- "@(#) $Header: /tcpdump/master/libpcap/pcap-bpf.c,v 1.67.2.4 2003/11/22 00:06:28 guy Exp $ (LBL)";
+ "@(#) $Header: /tcpdump/master/libpcap/pcap-bpf.c,v 1.86 2005/02/26 21:58:05 guy Exp $ (LBL)";
#endif
#ifdef HAVE_CONFIG_H
@@ -142,7 +142,11 @@ pcap_read_bpf(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
int cc;
int n = 0;
register u_char *bp, *ep;
+ u_char *datap;
struct bpf_insn *fcode;
+#ifdef PCAP_FDDIPAD
+ register int pad;
+#endif
fcode = p->md.use_bpf ? NULL : p->fcode.bf_insns;
again:
@@ -224,6 +228,9 @@ pcap_read_bpf(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
*/
#define bhp ((struct bpf_hdr *)bp)
ep = bp + cc;
+#ifdef PCAP_FDDIPAD
+ pad = p->fddipad;
+#endif
while (bp < ep) {
register int caplen, hdrlen;
@@ -249,31 +256,48 @@ pcap_read_bpf(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
caplen = bhp->bh_caplen;
hdrlen = bhp->bh_hdrlen;
+ datap = bp + hdrlen;
/*
* Short-circuit evaluation: if using BPF filter
* in kernel, no need to do it now.
+ *
+#ifdef PCAP_FDDIPAD
+ * Note: the filter code was generated assuming
+ * that p->fddipad was the amount of padding
+ * before the header, as that's what's required
+ * in the kernel, so we run the filter before
+ * skipping that padding.
+#endif
*/
if (fcode == NULL ||
- bpf_filter(fcode, bp + hdrlen, bhp->bh_datalen, caplen)) {
+ bpf_filter(fcode, datap, bhp->bh_datalen, caplen)) {
+ struct pcap_pkthdr pkthdr;
+
+ pkthdr.ts.tv_sec = bhp->bh_tstamp.tv_sec;
#ifdef _AIX
/*
* AIX's BPF returns seconds/nanoseconds time
* stamps, not seconds/microseconds time stamps.
- *
- * XXX - I'm guessing here that it's a "struct
- * timestamp"; if not, this code won't compile,
- * but, if not, you want to send us a bug report
- * and fall back on using DLPI. It's not as if
- * BPF used to work right on AIX before this
- * change; this change attempts to fix the fact
- * that it didn't....
*/
- bhp->bh_tstamp.tv_usec = bhp->bh_tstamp.tv_usec/1000;
+ pkthdr.ts.tv_usec = bhp->bh_tstamp.tv_usec/1000;
+#else
+ pkthdr.ts.tv_usec = bhp->bh_tstamp.tv_usec;
#endif
- /*
- * XXX A bpf_hdr matches a pcap_pkthdr.
- */
- (*callback)(user, (struct pcap_pkthdr*)bp, bp + hdrlen);
+#ifdef PCAP_FDDIPAD
+ if (caplen > pad)
+ pkthdr.caplen = caplen - pad;
+ else
+ pkthdr.caplen = 0;
+ if (bhp->bh_datalen > pad)
+ pkthdr.len = bhp->bh_datalen - pad;
+ else
+ pkthdr.len = 0;
+ datap += pad;
+#else
+ pkthdr.caplen = caplen;
+ pkthdr.len = bhp->bh_datalen;
+#endif
+ (*callback)(user, &pkthdr, datap);
bp += BPF_WORDALIGN(caplen + hdrlen);
if (++n >= cnt && cnt > 0) {
p->bp = bp;
@@ -292,6 +316,55 @@ pcap_read_bpf(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
return (n);
}
+static int
+pcap_inject_bpf(pcap_t *p, const void *buf, size_t size)
+{
+ int ret;
+
+ ret = write(p->fd, buf, size);
+#ifdef __APPLE__
+ if (ret == -1 && errno == EAFNOSUPPORT) {
+ /*
+ * In Mac OS X, there's a bug wherein setting the
+ * BIOCSHDRCMPLT flag causes writes to fail; see,
+ * for example:
+ *
+ * http://cerberus.sourcefire.com/~jeff/archives/patches/macosx/BIOCSHDRCMPLT-10.3.3.patch
+ *
+ * So, if, on OS X, we get EAFNOSUPPORT from the write, we
+ * assume it's due to that bug, and turn off that flag
+ * and try again. If we succeed, it either means that
+ * somebody applied the fix from that URL, or other patches
+ * for that bug from
+ *
+ * http://cerberus.sourcefire.com/~jeff/archives/patches/macosx/
+ *
+ * and are running a Darwin kernel with those fixes, or
+ * that Apple fixed the problem in some OS X release.
+ */
+ u_int spoof_eth_src = 0;
+
+ if (ioctl(p->fd, BIOCSHDRCMPLT, &spoof_eth_src) == -1) {
+ (void)snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+ "send: can't turn off BIOCSHDRCMPLT: %s",
+ pcap_strerror(errno));
+ return (-1);
+ }
+
+ /*
+ * Now try the write again.
+ */
+ ret = write(p->fd, buf, size);
+ }
+#endif /* __APPLE__ */
+ if (ret == -1) {
+ snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "send: %s",
+ pcap_strerror(errno));
+ return (-1);
+ }
+ return (ret);
+}
+
#ifdef _AIX
static int
bpf_odminit(char *errbuf)
@@ -467,7 +540,23 @@ bpf_open(pcap_t *p, char *errbuf)
*/
do {
(void)snprintf(device, sizeof(device), "/dev/bpf%d", n++);
- fd = open(device, O_RDONLY);
+ /*
+ * 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);
/*
@@ -480,25 +569,14 @@ bpf_open(pcap_t *p, char *errbuf)
return (fd);
}
-static void
-pcap_close_bpf(pcap_t *p)
-{
- if (p->buffer != NULL)
- free(p->buffer);
- if (p->fd >= 0)
- close(p->fd);
-}
-
/*
- * XXX - on AIX, IBM's tcpdump (and perhaps the incompatible-with-everybody-
- * else's libpcap in AIX 5.1) appears to forcibly load the BPF driver
- * if it's not already loaded, and to create the BPF devices if they
- * don't exist.
- *
- * It'd be nice if we could do the same, although the code to do so
- * might be version-dependent, alas (the way to do it isn't necessarily
- * documented).
+ * 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
+
pcap_t *
pcap_open_live(const char *device, int snaplen, int promisc, int to_ms,
char *ebuf)
@@ -509,8 +587,13 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms,
#ifdef BIOCGDLTLIST
struct bpf_dltlist bdl;
#endif
+#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;
#ifdef HAVE_DAG_API
@@ -644,6 +727,12 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms,
break;
}
#endif
+#ifdef PCAP_FDDIPAD
+ if (v == DLT_FDDI)
+ p->fddipad = PCAP_FDDIPAD:
+ else
+ p->fddipad = 0;
+#endif
p->linktype = v;
#ifdef BIOCGDLTLIST
@@ -653,7 +742,10 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms,
* not fatal; we just don't get to use the feature later.
*/
if (ioctl(fd, BIOCGDLTLIST, (caddr_t)&bdl) == 0) {
- bdl.bfl_list = (u_int *) malloc(sizeof(u_int) * bdl.bfl_len);
+ u_int i;
+ int is_ethernet;
+
+ 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));
@@ -663,9 +755,44 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms,
if (ioctl(fd, BIOCGDLTLIST, (caddr_t)&bdl) < 0) {
(void)snprintf(ebuf, PCAP_ERRBUF_SIZE,
"BIOCGDLTLIST: %s", pcap_strerror(errno));
+ free(bdl.bfl_list);
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).
+ */
+ 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;
+ }
+ }
+ 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 {
@@ -677,6 +804,42 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms,
}
#endif
+ /*
+ * If this is an Ethernet device, and we don't have a DLT_ list,
+ * give it a list with DLT_EN10MB and DLT_DOCSIS. (That'd give
+ * 802.11 interfaces DLT_DOCSIS, which isn't the right thing to
+ * do, but there's not much we can do about that without finding
+ * some other way of determining whether it's an Ethernet or 802.11
+ * device.)
+ */
+ if (p->linktype == DLT_EN10MB && p->dlt_count == 0) {
+ p->dlt_list = (u_int *) malloc(sizeof(u_int) * 2);
+ /*
+ * If that fails, just leave the list empty.
+ */
+ if (p->dlt_list != NULL) {
+ p->dlt_list[0] = DLT_EN10MB;
+ p->dlt_list[1] = DLT_DOCSIS;
+ p->dlt_count = 2;
+ }
+ }
+
+#if defined(BIOCGHDRCMPLT) && defined(BIOCSHDRCMPLT)
+ /*
+ * Do a BIOCSHDRCMPLT, if defined, to turn that flag on, so
+ * the link-layer source address isn't forcibly overwritten.
+ * (Should we ignore errors? Should we do this only if
+ * we're open for writing?)
+ *
+ * XXX - I seem to remember some packet-sending bug in some
+ * BSDs - check CVS log for "bpf.c"?
+ */
+ if (ioctl(fd, BIOCSHDRCMPLT, &spoof_eth_src) == -1) {
+ (void)snprintf(ebuf, PCAP_ERRBUF_SIZE,
+ "BIOCSHDRCMPLT: %s", pcap_strerror(errno));
+ goto bad;
+ }
+#endif
/* set timeout */
if (to_ms != 0) {
/*
@@ -778,6 +941,28 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms,
#endif
/*
+ * If there's no filter program installed, there's
+ * no indication to the kernel of what the snapshot
+ * length should be, so no snapshotting is done.
+ *
+ * Therefore, when we open the device, we install
+ * an "accept everything" filter with the specified
+ * snapshot length.
+ */
+ total_insn.code = (u_short)(BPF_RET | BPF_K);
+ total_insn.jt = 0;
+ total_insn.jf = 0;
+ total_insn.k = snaplen;
+
+ total_prog.bf_len = 1;
+ total_prog.bf_insns = &total_insn;
+ if (ioctl(p->fd, BIOCSETF, (caddr_t)&total_prog) < 0) {
+ snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCSETF: %s",
+ pcap_strerror(errno));
+ goto bad;
+ }
+
+ /*
* On most BPF platforms, either you can do a "select()" or
* "poll()" on a BPF file descriptor and it works correctly,
* or you can do it and it will return "readable" if the
@@ -818,8 +1003,8 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms,
* We can check what OS this is.
*/
if (strcmp(osinfo.sysname, "FreeBSD") == 0 &&
- (strcmp(osinfo.release, "4.3") == 0 ||
- strcmp(osinfo.release, "4.4") == 0))
+ (strncmp(osinfo.release, "4.3-", 4) == 0 ||
+ strncmp(osinfo.release, "4.4-", 4) == 0))
p->selectable_fd = -1;
else
p->selectable_fd = p->fd;
@@ -832,20 +1017,19 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms,
}
p->read_op = pcap_read_bpf;
+ p->inject_op = pcap_inject_bpf;
p->setfilter_op = pcap_setfilter_bpf;
p->set_datalink_op = pcap_set_datalink_bpf;
p->getnonblock_op = pcap_getnonblock_fd;
p->setnonblock_op = pcap_setnonblock_fd;
p->stats_op = pcap_stats_bpf;
- p->close_op = pcap_close_bpf;
+ p->close_op = pcap_close_common;
return (p);
bad:
(void)close(fd);
-#ifdef BIOCGDLTLIST
- if (bdl.bfl_list != NULL)
- free(bdl.bfl_list);
-#endif
+ if (p->dlt_list != NULL)
+ free(p->dlt_list);
free(p);
return (NULL);
}
@@ -893,6 +1077,14 @@ pcap_setfilter_bpf(pcap_t *p, struct bpf_program *fp)
return (-1);
}
p->md.use_bpf = 1; /* filtering in the kernel */
+
+ /*
+ * Discard any previously-received packets, as they might have
+ * passed whatever filter was formerly in effect, but might
+ * not pass this filter (BIOCSETF discards packets buffered
+ * in the kernel, so you can lose packets in any case).
+ */
+ p->cc = 0;
return (0);
}
OpenPOWER on IntegriCloud