diff options
Diffstat (limited to 'contrib/libpcap/pcap-pf.c')
-rw-r--r-- | contrib/libpcap/pcap-pf.c | 183 |
1 files changed, 145 insertions, 38 deletions
diff --git a/contrib/libpcap/pcap-pf.c b/contrib/libpcap/pcap-pf.c index f29bc99..e84d1c7 100644 --- a/contrib/libpcap/pcap-pf.c +++ b/contrib/libpcap/pcap-pf.c @@ -23,8 +23,8 @@ */ #ifndef lint -static const char rcsid[] = - "@(#) $Header: /tcpdump/master/libpcap/pcap-pf.c,v 1.65 2001/12/10 07:14:19 guy Exp $ (LBL)"; +static const char rcsid[] _U_ = + "@(#) $Header: /tcpdump/master/libpcap/pcap-pf.c,v 1.79.2.5 2003/11/22 00:32:55 guy Exp $ (LBL)"; #endif #ifdef HAVE_CONFIG_H @@ -61,12 +61,21 @@ struct rtentry; #include <string.h> #include <unistd.h> +/* + * Make "pcap.h" not include "pcap-bpf.h"; we are going to include the + * native OS version, as we need various BPF ioctls from it. + */ +#define PCAP_DONT_INCLUDE_PCAP_BPF_H +#include <net/bpf.h> + #include "pcap-int.h" #ifdef HAVE_OS_PROTO_H #include "os-proto.h" #endif +static int pcap_setfilter_pf(pcap_t *, struct bpf_program *); + /* * BUFSPACE is the size in bytes of the packet read buffer. Most tcpdump * applications aren't going to need more than 200 bytes of packet header @@ -75,8 +84,8 @@ struct rtentry; */ #define BUFSPACE (200 * 256) -int -pcap_read(pcap_t *pc, int cnt, pcap_handler callback, u_char *user) +static int +pcap_read_pf(pcap_t *pc, int cnt, pcap_handler callback, u_char *user) { register u_char *p, *bp; struct bpf_insn *fcode; @@ -126,6 +135,25 @@ pcap_read(pcap_t *pc, int cnt, pcap_handler callback, u_char *user) pad = 0; #endif while (cc > 0) { + /* + * 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. + */ + if (pc->break_loop) { + if (n == 0) { + pc->break_loop = 0; + return (-2); + } else { + pc->cc = cc; + pc->bp = bp; + return (n); + } + } if (cc < sizeof(*sp)) { snprintf(pc->errbuf, sizeof(pc->errbuf), "pf short read (%d)", cc); @@ -191,8 +219,8 @@ pcap_read(pcap_t *pc, int cnt, pcap_handler callback, u_char *user) return (n); } -int -pcap_stats(pcap_t *p, struct pcap_stat *ps) +static int +pcap_stats_pf(pcap_t *p, struct pcap_stat *ps) { /* @@ -237,8 +265,18 @@ pcap_stats(pcap_t *p, struct pcap_stat *ps) return (0); } +static void +pcap_close_pf(pcap_t *p) +{ + if (p->buffer != NULL) + free(p->buffer); + if (p->fd >= 0) + close(p->fd); +} + pcap_t * -pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf) +pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, + char *ebuf) { pcap_t *p; short enmode; @@ -253,10 +291,17 @@ pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf) return (0); } memset(p, 0, sizeof(*p)); + + /* + * XXX - we assume here that "pfopen()" does not, in fact, modify + * its argument, even though it takes a "char *" rather than a + * "const char *" as its first argument. That appears to be + * the case, at least on Digital UNIX 4.0. + */ p->fd = pfopen(device, O_RDONLY); if (p->fd < 0) { snprintf(ebuf, PCAP_ERRBUF_SIZE, "pf open: %s: %s\n\ -your system may not be properly configured; see \"man packetfilter(4)\"\n", +your system may not be properly configured; see the packetfilter(4) man page\n", device, pcap_strerror(errno)); goto bad; } @@ -345,7 +390,7 @@ your system may not be properly configured; see \"man packetfilter(4)\"\n", * framing", there's not much we can do, as that * doesn't specify a particular type of header. */ - snprintf(ebuf, PCAP_ERRBUF_SIZE, "unknown data-link type %lu", + snprintf(ebuf, PCAP_ERRBUF_SIZE, "unknown data-link type %u", devparams.end_dev_type); goto bad; } @@ -381,51 +426,113 @@ your system may not be properly configured; see \"man packetfilter(4)\"\n", goto bad; } } + p->bufsize = BUFSPACE; p->buffer = (u_char*)malloc(p->bufsize + p->offset); + if (p->buffer == NULL) { + strlcpy(ebuf, pcap_strerror(errno), PCAP_ERRBUF_SIZE); + goto bad; + } + + /* + * "select()" and "poll()" work on packetfilter devices. + */ + p->selectable_fd = p->fd; + + p->read_op = pcap_read_pf; + p->setfilter_op = pcap_setfilter_pf; + p->set_datalink_op = NULL; /* can't change data link type */ + p->getnonblock_op = pcap_getnonblock_fd; + p->setnonblock_op = pcap_setnonblock_fd; + p->stats_op = pcap_stats_pf; + p->close_op = pcap_close_pf; return (p); bad: - if (p->fd >= 0) - close(p->fd); + if (p->fd >= 0) + close(p->fd); free(p); return (NULL); } int -pcap_setfilter(pcap_t *p, struct bpf_program *fp) +pcap_platform_finddevs(pcap_if_t **alldevsp, char *errbuf) +{ + return (0); +} + +static int +pcap_setfilter_pf(pcap_t *p, struct bpf_program *fp) { + struct bpf_version bv; + /* - * See if BIOCSETF works. If it does, the kernel supports - * BPF-style filters, and we do not need to do post-filtering. + * See if BIOCVERSION works. If not, we assume the kernel doesn't + * support BPF-style filters (it's not documented in the bpf(7) + * or packetfiler(7) man pages, but the code used to fail if + * BIOCSETF worked but BIOCVERSION didn't, and I've seen it do + * kernel filtering in DU 4.0, so presumably BIOCVERSION works + * there, at least). */ - p->md.use_bpf = (ioctl(p->fd, BIOCSETF, (caddr_t)fp) >= 0); - if (p->md.use_bpf) { - struct bpf_version bv; + if (ioctl(p->fd, BIOCVERSION, (caddr_t)&bv) >= 0) { + /* + * OK, we have the version of the BPF interpreter; + * is it the same major version as us, and the same + * or better minor version? + */ + if (bv.bv_major == BPF_MAJOR_VERSION && + bv.bv_minor >= BPF_MINOR_VERSION) { + /* + * Yes. Try to install the filter. + */ + if (ioctl(p->fd, BIOCSETF, (caddr_t)fp) < 0) { + snprintf(p->errbuf, sizeof(p->errbuf), + "BIOCSETF: %s", pcap_strerror(errno)); + return (-1); + } - if (ioctl(p->fd, BIOCVERSION, (caddr_t)&bv) < 0) { - snprintf(p->errbuf, sizeof(p->errbuf), - "BIOCVERSION: %s", pcap_strerror(errno)); - return (-1); - } - else if (bv.bv_major != BPF_MAJOR_VERSION || - bv.bv_minor < BPF_MINOR_VERSION) { - fprintf(stderr, - "requires bpf language %d.%d or higher; kernel is %d.%d", - BPF_MAJOR_VERSION, BPF_MINOR_VERSION, - bv.bv_major, bv.bv_minor); - /* don't give up, just be inefficient */ - p->md.use_bpf = 0; + /* + * OK, that succeeded. We're doing filtering in + * the kernel. (We assume we don't have a + * userland filter installed - that'd require + * a previous version check to have failed but + * this one to succeed.) + * + * XXX - this message should be supplied to the + * application as a warning of some sort, + * except that if it's a GUI application, it's + * not clear that it should be displayed in + * a window to annoy the user. + */ + fprintf(stderr, "tcpdump: Using kernel BPF filter\n"); + p->md.use_bpf = 1; + return (0); } - } else { - if (install_bpf_program(p, fp) < 0) - return (-1); + + /* + * We can't use the kernel's BPF interpreter; don't give + * up, just log a message and be inefficient. + * + * XXX - this should really be supplied to the application + * as a warning of some sort. + */ + fprintf(stderr, + "tcpdump: Requires BPF language %d.%d or higher; kernel is %d.%d\n", + BPF_MAJOR_VERSION, BPF_MINOR_VERSION, + bv.bv_major, bv.bv_minor); } - /*XXX this goes in tcpdump*/ - if (p->md.use_bpf) - fprintf(stderr, "tcpdump: Using kernel BPF filter\n"); - else - fprintf(stderr, "tcpdump: Filtering in user process\n"); + /* + * We couldn't do filtering in the kernel; do it in userland. + */ + if (install_bpf_program(p, fp) < 0) + return (-1); + + /* + * XXX - this message should be supplied by the application as + * a warning of some sort. + */ + fprintf(stderr, "tcpdump: Filtering in user process\n"); + p->md.use_bpf = 0; return (0); } |