diff options
author | sam <sam@FreeBSD.org> | 2005-05-29 17:46:52 +0000 |
---|---|---|
committer | sam <sam@FreeBSD.org> | 2005-05-29 17:46:52 +0000 |
commit | 70f7ae46f5124d9a1805484495b57250a715bed4 (patch) | |
tree | b733a54335042f876f8969020dc9dc4408f485f4 /contrib/libpcap/pcap-bpf.c | |
parent | da13a5a9d72229b2a6026a49b9977934bdef13ed (diff) | |
download | FreeBSD-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.c | 274 |
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); } |