X-Git-Url: https://git.tcpdump.org/libpcap/blobdiff_plain/b231bfe26cdeaa2bb9375052d5985a82eeda84ec..09b51d326c38ea8e10ce4da09c09d50e08c5aeb8:/pcap-bpf.c diff --git a/pcap-bpf.c b/pcap-bpf.c index b4482e9f..2898e598 100644 --- a/pcap-bpf.c +++ b/pcap-bpf.c @@ -18,26 +18,19 @@ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ -#ifndef lint -static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/pcap-bpf.c,v 1.116 2008-09-16 18:42:29 guy Exp $ (LBL)"; -#endif #ifdef HAVE_CONFIG_H -#include "config.h" +#include #endif #include /* optionally get BSD define */ -#ifdef HAVE_ZEROCOPY_BPF -#include -#endif -#include #include +#include /* * defines ioctls, but doesn't include . * * We include as it might be necessary to declare ioctl(); - * at least on *BSD and Mac OS X, it also defines various SIOC ioctls - + * at least on *BSD and macOS, it also defines various SIOC ioctls - * we could include , but if we're already including * , which includes on those platforms, * there's not much point in doing so. @@ -52,8 +45,13 @@ static const char rcsid[] _U_ = #endif #include -#ifdef HAVE_ZEROCOPY_BPF -#include +#if defined(__FreeBSD__) && defined(SIOCIFCREATE2) +/* + * Add support for capturing on FreeBSD usbusN interfaces. + */ +static const char usbus_prefix[] = "usbus"; +#define USBUS_PREFIX_LEN (sizeof(usbus_prefix) - 1) +#include #endif #include @@ -77,6 +75,16 @@ static const char rcsid[] _U_ = #include #define _AIX +/* + * If both BIOCROTZBUF and BPF_BUFMODE_ZBUF are defined, we have + * zero-copy BPF. + */ +#if defined(BIOCROTZBUF) && defined(BPF_BUFMODE_ZBUF) + #define HAVE_ZEROCOPY_BPF + #include + #include +#endif + #include /* for IFT_ values */ #include #include @@ -107,7 +115,6 @@ static int bpf_load(char *errbuf); #endif /* _AIX */ -#include #include #include #include @@ -116,27 +123,78 @@ static int bpf_load(char *errbuf); #include #include -#ifdef HAVE_NET_IF_MEDIA_H +#ifdef SIOCGIFMEDIA # include #endif #include "pcap-int.h" -#ifdef HAVE_DAG_API -#include "pcap-dag.h" -#endif /* HAVE_DAG_API */ - -#ifdef HAVE_SNF_API -#include "pcap-snf.h" -#endif /* HAVE_SNF_API */ - #ifdef HAVE_OS_PROTO_H #include "os-proto.h" #endif +/* + * Later versions of NetBSD stick padding in front of FDDI frames + * to align the IP header on a 4-byte boundary. + */ +#if defined(__NetBSD__) && __NetBSD_Version__ > 106000000 +#define PCAP_FDDIPAD 3 +#endif + +/* + * Private data for capturing on BPF devices. + */ +struct pcap_bpf { +#ifdef HAVE_ZEROCOPY_BPF + /* + * Zero-copy read buffer -- for zero-copy BPF. 'buffer' above will + * alternative between these two actual mmap'd buffers as required. + * As there is a header on the front size of the mmap'd buffer, only + * some of the buffer is exposed to libpcap as a whole via bufsize; + * zbufsize is the true size. zbuffer tracks the current zbuf + * associated with buffer so that it can be used to decide which the + * next buffer to read will be. + */ + u_char *zbuf1, *zbuf2, *zbuffer; + u_int zbufsize; + u_int zerocopy; + u_int interrupted; + struct timespec firstsel; + /* + * If there's currently a buffer being actively processed, then it is + * referenced here; 'buffer' is also pointed at it, but offset by the + * size of the header. + */ + struct bpf_zbuf_header *bzh; + int nonblock; /* true if in nonblocking mode */ +#endif /* HAVE_ZEROCOPY_BPF */ + + char *device; /* device name */ + int filtering_in_kernel; /* using kernel filter */ + int must_do_on_close; /* stuff we must do when we close */ +}; + +/* + * Stuff to do when we close. + */ +#define MUST_CLEAR_RFMON 0x00000001 /* clear rfmon (monitor) mode */ +#define MUST_DESTROY_USBUS 0x00000002 /* destroy usbusN interface */ + #ifdef BIOCGDLTLIST # if (defined(HAVE_NET_IF_MEDIA_H) && defined(IFM_IEEE80211)) && !defined(__APPLE__) #define HAVE_BSD_IEEE80211 + +/* + * The ifm_ulist member of a struct ifmediareq is an int * on most systems, + * but it's a uint64_t on newer versions of OpenBSD. + * + * We check this by checking whether IFM_GMASK is defined and > 2^32-1. + */ +# if defined(IFM_GMASK) && IFM_GMASK > 0xFFFFFFFF +# define IFM_ULIST_TYPE uint64_t +# else +# define IFM_ULIST_TYPE int +# endif # endif # if defined(__APPLE__) || defined(HAVE_BSD_IEEE80211) @@ -147,7 +205,7 @@ static int monitor_mode(pcap_t *, int); # endif # if defined(__APPLE__) -static void remove_en(pcap_t *); +static void remove_non_802_11(pcap_t *); static void remove_802_11(pcap_t *); # endif @@ -155,6 +213,10 @@ static void remove_802_11(pcap_t *); #endif /* BIOCGDLTLIST */ +#if defined(sun) && defined(LIFNAMSIZ) && defined(lifr_zoneid) +#include +#endif + /* * We include the OS's , not our "pcap/bpf.h", so we probably * don't get DLT_DOCSIS defined. @@ -164,8 +226,9 @@ static void remove_802_11(pcap_t *); #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. + * In some versions of macOS, we might not even get any of the + * 802.11-plus-radio-header DLT_'s defined, even though some + * of them are used by various Airport drivers in those versions. */ #ifndef DLT_PRISM_HEADER #define DLT_PRISM_HEADER 119 @@ -186,77 +249,38 @@ 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- + * For zerocopy bpf, the setnonblock/getnonblock routines need to modify + * pb->nonblock so we don't call select(2) if the pcap handle is in 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); -} +pcap_getnonblock_bpf(pcap_t *p) +{ +#ifdef HAVE_ZEROCOPY_BPF + struct pcap_bpf *pb = p->priv; -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) { - /* - * Timeout is non-negative, so we're not already - * in non-blocking mode; set it to the 2's - * complement, to make it negative, as an - * indication that we're in non-blocking mode. - */ - p->md.timeout = p->md.timeout * -1 - 1; - } - } else { - if (p->md.timeout < 0) { - /* - * Timeout is negative, so we're not already - * in blocking mode; reverse the previous - * operation, to make the timeout non-negative - * again. - */ - p->md.timeout = (p->md.timeout + 1) * -1; - } - } - return (0); + if (pb->zerocopy) + return (pb->nonblock); +#endif + return (pcap_getnonblock_fd(p)); } -/* - * Zero-copy specific close method. Un-map the shared buffers then call - * pcap_cleanup_live_common. - */ -static void -pcap_cleanup_zbuf(pcap_t *p) +static int +pcap_setnonblock_bpf(pcap_t *p, int nonblock) { - /* - * 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); +#ifdef HAVE_ZEROCOPY_BPF + struct pcap_bpf *pb = p->priv; + + if (pb->zerocopy) { + pb->nonblock = nonblock; + return (0); + } +#endif + return (pcap_setnonblock_fd(p, nonblock)); } +#ifdef HAVE_ZEROCOPY_BPF /* * Zero-copy BPF buffer routines to check for and acknowledge BPF data in * shared memory buffers. @@ -269,25 +293,26 @@ pcap_cleanup_zbuf(pcap_t *p) static int pcap_next_zbuf_shm(pcap_t *p, int *cc) { + struct pcap_bpf *pb = p->priv; 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 (pb->zbuffer == pb->zbuf2 || pb->zbuffer == NULL) { + bzh = (struct bpf_zbuf_header *)pb->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); + pb->bzh = bzh; + pb->zbuffer = (u_char *)pb->zbuf1; + p->buffer = pb->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; + } else if (pb->zbuffer == pb->zbuf1) { + bzh = (struct bpf_zbuf_header *)pb->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); + pb->bzh = bzh; + pb->zbuffer = (u_char *)pb->zbuf2; + p->buffer = pb->zbuffer + sizeof(*bzh); *cc = bzh->bzh_kernel_len; return (1); } @@ -306,6 +331,7 @@ pcap_next_zbuf_shm(pcap_t *p, int *cc) static int pcap_next_zbuf(pcap_t *p, int *cc) { + struct pcap_bpf *pb = p->priv; struct bpf_zbuf bz; struct timeval tv; struct timespec cur; @@ -329,21 +355,21 @@ pcap_next_zbuf(pcap_t *p, int *cc) * our timeout is less then or equal to zero, handle it like a * regular timeout. */ - tmout = p->md.timeout; + tmout = p->opt.timeout; if (tmout) (void) clock_gettime(CLOCK_MONOTONIC, &cur); - if (p->md.interrupted && p->md.timeout) { - expire = TSTOMILLI(&p->md.firstsel) + p->md.timeout; + if (pb->interrupted && p->opt.timeout) { + expire = TSTOMILLI(&pb->firstsel) + p->opt.timeout; tmout = expire - TSTOMILLI(&cur); #undef TSTOMILLI if (tmout <= 0) { - p->md.interrupted = 0; + pb->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)); + pcap_fmt_errmsg_for_errno(p->errbuf, + PCAP_ERRBUF_SIZE, errno, "BIOCROTZBUF"); return (PCAP_ERROR); } return (pcap_next_zbuf_shm(p, cc)); @@ -354,7 +380,7 @@ pcap_next_zbuf(pcap_t *p, int *cc) * the next timeout. Note that we only call select if the handle * is in blocking mode. */ - if (p->md.timeout >= 0) { + if (!pb->nonblock) { FD_ZERO(&r_set); FD_SET(p->fd, &r_set); if (tmout != 0) { @@ -362,20 +388,20 @@ pcap_next_zbuf(pcap_t *p, int *cc) tv.tv_usec = (tmout * 1000) % 1000000; } r = select(p->fd + 1, &r_set, NULL, NULL, - p->md.timeout != 0 ? &tv : NULL); + p->opt.timeout != 0 ? &tv : NULL); if (r < 0 && errno == EINTR) { - if (!p->md.interrupted && p->md.timeout) { - p->md.interrupted = 1; - p->md.firstsel = cur; + if (!pb->interrupted && p->opt.timeout) { + pb->interrupted = 1; + pb->firstsel = cur; } return (0); } else if (r < 0) { - (void) snprintf(p->errbuf, PCAP_ERRBUF_SIZE, - "select: %s", strerror(errno)); + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "select"); return (PCAP_ERROR); } } - p->md.interrupted = 0; + pb->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" @@ -389,8 +415,8 @@ pcap_next_zbuf(pcap_t *p, int *cc) * data. */ if (ioctl(p->fd, BIOCROTZBUF, &bz) < 0) { - (void) snprintf(p->errbuf, PCAP_ERRBUF_SIZE, - "BIOCROTZBUF: %s", strerror(errno)); + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "BIOCROTZBUF"); return (PCAP_ERROR); } return (pcap_next_zbuf_shm(p, cc)); @@ -403,48 +429,58 @@ pcap_next_zbuf(pcap_t *p, int *cc) static int pcap_ack_zbuf(pcap_t *p) { + struct pcap_bpf *pb = p->priv; - atomic_store_rel_int(&p->md.bzh->bzh_user_gen, - p->md.bzh->bzh_kernel_gen); - p->md.bzh = NULL; + atomic_store_rel_int(&pb->bzh->bzh_user_gen, + pb->bzh->bzh_kernel_gen); + pb->bzh = NULL; p->buffer = NULL; return (0); } -#endif +#endif /* HAVE_ZEROCOPY_BPF */ pcap_t * -pcap_create(const char *device, char *ebuf) +pcap_create_interface(const char *device _U_, char *ebuf) { pcap_t *p; -#ifdef HAVE_DAG_API - if (strstr(device, "dag")) - return (dag_create(device, ebuf)); -#endif /* HAVE_DAG_API */ -#ifdef HAVE_SNF_API - if (strstr(device, "snf")) - return (snf_create(device, ebuf)); -#endif /* HAVE_SNF_API */ - - p = pcap_create_common(device, ebuf); + p = PCAP_CREATE_COMMON(ebuf, struct pcap_bpf); if (p == NULL) return (NULL); p->activate_op = pcap_activate_bpf; p->can_set_rfmon_op = pcap_can_set_rfmon_bpf; +#ifdef BIOCSTSTAMP + /* + * We claim that we support microsecond and nanosecond time + * stamps. + */ + p->tstamp_precision_list = malloc(2 * sizeof(u_int)); + if (p->tstamp_precision_list == NULL) { + pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, errno, + "malloc"); + free(p); + return (NULL); + } + p->tstamp_precision_list[0] = PCAP_TSTAMP_PRECISION_MICRO; + p->tstamp_precision_list[1] = PCAP_TSTAMP_PRECISION_NANO; + p->tstamp_precision_count = 2; +#endif /* BIOCSTSTAMP */ return (p); } +/* + * On success, returns a file descriptor for a BPF device. + * On failure, returns a PCAP_ERROR_ value, and sets p->errbuf. + */ static int -bpf_open(pcap_t *p) +bpf_open(char *errbuf) { - int fd; -#ifdef HAVE_CLONING_BPF - static const char device[] = "/dev/bpf"; -#else - int n = 0; + int fd = -1; + static const char cloning_device[] = "/dev/bpf"; + u_int n = 0; char device[sizeof "/dev/bpf0000000000"]; -#endif + static int no_cloning_bpf = 0; #ifdef _AIX /* @@ -452,61 +488,304 @@ bpf_open(pcap_t *p) * and create the BPF device entries, if they don't * already exist. */ - if (bpf_load(p->errbuf) == PCAP_ERROR) + if (bpf_load(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. + * First, unless we've already tried opening /dev/bpf and + * gotten ENOENT, try opening /dev/bpf. + * If it fails with ENOENT, remember that, so we don't try + * again, and try /dev/bpfN. */ - do { - (void)snprintf(device, sizeof(device), "/dev/bpf%d", n++); + if (!no_cloning_bpf && + (fd = open(cloning_device, O_RDWR)) == -1 && + ((errno != EACCES && errno != ENOENT) || + (fd = open(cloning_device, O_RDONLY)) == -1)) { + if (errno != ENOENT) { + if (errno == EACCES) { + fd = PCAP_ERROR_PERM_DENIED; + snprintf(errbuf, PCAP_ERRBUF_SIZE, + "Attempt to open %s failed - root privileges may be required", + cloning_device); + } else { + fd = PCAP_ERROR; + pcap_fmt_errmsg_for_errno(errbuf, + PCAP_ERRBUF_SIZE, errno, + "(cannot open device) %s", cloning_device); + } + return (fd); + } + no_cloning_bpf = 1; + } + + if (no_cloning_bpf) { /* - * 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. + * We don't have /dev/bpf. + * Go through all the /dev/bpfN minors and find one + * that isn't in use. */ - fd = open(device, O_RDWR); - if (fd == -1 && errno == EACCES) - fd = open(device, O_RDONLY); - } while (fd < 0 && errno == EBUSY); + do { + (void)snprintf(device, sizeof(device), "/dev/bpf%u", 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) + switch (errno) { + + case ENOENT: + fd = PCAP_ERROR; + if (n == 1) { + /* + * /dev/bpf0 doesn't exist, which + * means we probably have no BPF + * devices. + */ + snprintf(errbuf, PCAP_ERRBUF_SIZE, + "(there are no BPF devices)"); + } else { + /* + * We got EBUSY on at least one + * BPF device, so we have BPF + * devices, but all the ones + * that exist are busy. + */ + snprintf(errbuf, PCAP_ERRBUF_SIZE, + "(all BPF devices are busy)"); + } + break; + + case EACCES: + /* + * Got EACCES on the last device we tried, + * and EBUSY on all devices before that, + * if any. + */ fd = PCAP_ERROR_PERM_DENIED; - else + snprintf(errbuf, PCAP_ERRBUF_SIZE, + "Attempt to open %s failed - root privileges may be required", + device); + break; + + default: + /* + * Some other problem. + */ fd = PCAP_ERROR; - snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "(no devices found) %s: %s", - device, pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, + errno, "(cannot open BPF device) %s", device); + break; + } + } + + return (fd); +} + +/* + * Bind a network adapter to a BPF device, given a descriptor for the + * BPF device and the name of the network adapter. + * + * Use BIOCSETLIF if available (meaning "on Solaris"), as it supports + * longer device names. + * + * If the name is longer than will fit, return PCAP_ERROR_NO_SUCH_DEVICE + * before trying to bind the interface, as there cannot be such a device. + * + * If the attempt succeeds, return BPF_BIND_SUCCEEDED. + * + * If the attempt fails: + * + * if it fails with ENXIO, return PCAP_ERROR_NO_SUCH_DEVICE, as + * the device doesn't exist; + * + * if it fails with ENETDOWN, return PCAP_ERROR_IFACE_NOT_UP, as + * the interface exists but isn't up and the OS doesn't allow + * binding to an interface that isn't up; + * + * if it fails with ENOBUFS, return BPF_BIND_BUFFER_TOO_BIG, and + * fill in an error message, as the buffer being requested is too + * large; + * + * otherwise, return PCAP_ERROR and fill in an error message. + */ +#define BPF_BIND_SUCCEEDED 0 +#define BPF_BIND_BUFFER_TOO_BIG 1 + +static int +bpf_bind(int fd, const char *name, char *errbuf) +{ + int status; +#ifdef LIFNAMSIZ + struct lifreq ifr; + + if (strlen(name) >= sizeof(ifr.lifr_name)) { + /* The name is too long, so it can't possibly exist. */ + return (PCAP_ERROR_NO_SUCH_DEVICE); + } + (void)pcap_strlcpy(ifr.lifr_name, name, sizeof(ifr.lifr_name)); + status = ioctl(fd, BIOCSETLIF, (caddr_t)&ifr); +#else + struct ifreq ifr; + + if (strlen(name) >= sizeof(ifr.ifr_name)) { + /* The name is too long, so it can't possibly exist. */ + return (PCAP_ERROR_NO_SUCH_DEVICE); } + (void)pcap_strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + status = ioctl(fd, BIOCSETIF, (caddr_t)&ifr); #endif + if (status < 0) { + switch (errno) { + + case ENXIO: + /* + * There's no such device. + * + * There's nothing more to say, so clear out the + * error message. + */ + errbuf[0] = '\0'; + return (PCAP_ERROR_NO_SUCH_DEVICE); + + case 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); + + case ENOBUFS: + /* + * The buffer size is too big. + * Return a special indication so that, if we're + * trying to crank the buffer size down, we know + * we have to continue; add an error message that + * tells the user what needs to be fixed. + */ + pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, + errno, "The requested buffer size for %s is too large", + name); + return (BPF_BIND_BUFFER_TOO_BIG); + + default: + pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, + errno, "Binding interface %s to BPF device failed", + name); + return (PCAP_ERROR); + } + } + return (BPF_BIND_SUCCEEDED); +} + +/* + * Open and bind to a device; used if we're not actually going to use + * the device, but are just testing whether it can be opened, or opening + * it to get information about it. + * + * Returns an error code on failure (always negative), and an FD for + * the now-bound BPF device on success (always non-negative). + */ +static int +bpf_open_and_bind(const char *name, char *errbuf) +{ + int fd; + int status; + + /* + * First, open a BPF device. + */ + fd = bpf_open(errbuf); + if (fd < 0) + return (fd); /* fd is the appropriate error code */ + + /* + * Now bind to the device. + */ + status = bpf_bind(fd, name, errbuf); + if (status != BPF_BIND_SUCCEEDED) { + close(fd); + if (status == BPF_BIND_BUFFER_TOO_BIG) { + /* + * We didn't specify a buffer size, so + * this *really* shouldn't fail because + * there's no buffer space. Fail. + */ + return (PCAP_ERROR); + } + return (status); + } + + /* + * Success. + */ return (fd); } +#ifdef __APPLE__ +static int +device_exists(int fd, const char *name, char *errbuf) +{ + int status; + struct ifreq ifr; + + if (strlen(name) >= sizeof(ifr.ifr_name)) { + /* The name is too long, so it can't possibly exist. */ + return (PCAP_ERROR_NO_SUCH_DEVICE); + } + (void)pcap_strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + status = ioctl(fd, SIOCGIFFLAGS, (caddr_t)&ifr); + + if (status < 0) { + if (errno == ENXIO || errno == EINVAL) { + /* + * macOS and *BSD return one of those two + * errors if the device doesn't exist. + * Don't fill in an error, as this is + * an "expected" condition. + */ + return (PCAP_ERROR_NO_SUCH_DEVICE); + } + + /* + * Some other error - provide a message for it, as + * it's "unexpected". + */ + pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, errno, + "Can't get interface flags on %s", name); + return (PCAP_ERROR); + } + + /* + * The device exists. + */ + return (0); +} +#endif + #ifdef BIOCGDLTLIST static int get_dlt_list(int fd, int v, struct bpf_dltlist *bdlp, char *ebuf) @@ -518,14 +797,14 @@ get_dlt_list(int fd, int v, struct bpf_dltlist *bdlp, char *ebuf) 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)); + pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, + errno, "malloc"); return (PCAP_ERROR); } if (ioctl(fd, BIOCGDLTLIST, (caddr_t)bdlp) < 0) { - (void)snprintf(ebuf, PCAP_ERRBUF_SIZE, - "BIOCGDLTLIST: %s", pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, + errno, "BIOCGDLTLIST"); free(bdlp->bfl_list); return (PCAP_ERROR); } @@ -579,8 +858,8 @@ get_dlt_list(int fd, int v, struct bpf_dltlist *bdlp, char *ebuf) * this device"; don't treat it as an error. */ if (errno != EINVAL) { - (void)snprintf(ebuf, PCAP_ERRBUF_SIZE, - "BIOCGDLTLIST: %s", pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, + errno, "BIOCGDLTLIST"); return (PCAP_ERROR); } } @@ -588,19 +867,19 @@ get_dlt_list(int fd, int v, struct bpf_dltlist *bdlp, char *ebuf) } #endif +#if defined(__APPLE__) 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; + int err; #endif /* - * The joys of monitor mode on OS X. + * The joys of monitor mode on Mac OS X/OS X/macOS. * * Prior to 10.4, it's not supported at all. * @@ -634,11 +913,14 @@ pcap_can_set_rfmon_bpf(pcap_t *p) return (0); } if (osinfo.release[0] == '8' && osinfo.release[1] == '.') { + char *wlt_name; + int status; + /* * 10.4 (Darwin 8.x). s/en/wlt/, and check * whether the device exists. */ - if (strncmp(p->opt.source, "en", 2) != 0) { + if (strncmp(p->opt.device, "en", 2) != 0) { /* * Not an enN device; no monitor mode. */ @@ -646,20 +928,28 @@ pcap_can_set_rfmon_bpf(pcap_t *p) } fd = socket(AF_INET, SOCK_DGRAM, 0); if (fd == -1) { - (void)snprintf(p->errbuf, PCAP_ERRBUF_SIZE, - "socket: %s", pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "socket"); 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? - */ + if (pcap_asprintf(&wlt_name, "wlt%s", p->opt.device + 2) == -1) { + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "malloc"); close(fd); - return (0); + return (PCAP_ERROR); } + status = device_exists(fd, wlt_name, p->errbuf); + free(wlt_name); close(fd); + if (status != 0) { + if (status == PCAP_ERROR_NO_SUCH_DEVICE) + return (0); + + /* + * Error. + */ + return (status); + } return (1); } @@ -671,32 +961,25 @@ pcap_can_set_rfmon_bpf(pcap_t *p) * * First, open a BPF device. */ - fd = bpf_open(p); + fd = bpf_open(p->errbuf); if (fd < 0) - return (fd); + return (fd); /* fd is the appropriate error code */ /* * 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) { + err = bpf_bind(fd, p->opt.device, p->errbuf); + if (err != BPF_BIND_SUCCEEDED) { + close(fd); + if (err == BPF_BIND_BUFFER_TOO_BIG) { /* - * 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. + * We didn't specify a buffer size, so + * this *really* shouldn't fail because + * there's no buffer space. Fail. */ - 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); } + return (err); } /* @@ -719,9 +1002,14 @@ pcap_can_set_rfmon_bpf(pcap_t *p) return (1); } free(bdl.bfl_list); + close(fd); #endif /* BIOCGDLTLIST */ return (0); +} #elif defined(HAVE_BSD_IEEE80211) +static int +pcap_can_set_rfmon_bpf(pcap_t *p) +{ int ret; ret = monitor_mode(p, 0); @@ -730,10 +1018,14 @@ pcap_can_set_rfmon_bpf(pcap_t *p) if (ret == 0) return (1); /* success */ return (ret); +} #else +static int +pcap_can_set_rfmon_bpf(pcap_t *p _U_) +{ return (0); -#endif } +#endif static int pcap_stats_bpf(pcap_t *p, struct pcap_stat *ps) @@ -754,8 +1046,8 @@ pcap_stats_bpf(pcap_t *p, struct pcap_stat *ps) * by libpcap, and thus not yet seen by the application. */ if (ioctl(p->fd, BIOCGSTATS, (caddr_t)&s) < 0) { - snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCGSTATS: %s", - pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "BIOCGSTATS"); return (PCAP_ERROR); } @@ -768,12 +1060,13 @@ pcap_stats_bpf(pcap_t *p, struct pcap_stat *ps) static int pcap_read_bpf(pcap_t *p, int cnt, pcap_handler callback, u_char *user) { + struct pcap_bpf *pb = p->priv; int cc; int n = 0; register u_char *bp, *ep; u_char *datap; #ifdef PCAP_FDDIPAD - register int pad; + register u_int pad; #endif #ifdef HAVE_ZEROCOPY_BPF int i; @@ -803,7 +1096,7 @@ pcap_read_bpf(pcap_t *p, int cnt, pcap_handler callback, u_char *user) * buffer. */ #ifdef HAVE_ZEROCOPY_BPF - if (p->md.zerocopy) { + if (pb->zerocopy) { if (p->buffer != NULL) pcap_ack_zbuf(p); i = pcap_next_zbuf(p, &cc); @@ -814,7 +1107,7 @@ pcap_read_bpf(pcap_t *p, int cnt, pcap_handler callback, u_char *user) } else #endif { - cc = read(p->fd, (char *)p->buffer, p->bufsize); + cc = (int)read(p->fd, p->buffer, p->bufsize); } if (cc < 0) { /* Don't choke when we get ptraced */ @@ -852,18 +1145,21 @@ pcap_read_bpf(pcap_t *p, int cnt, pcap_handler callback, u_char *user) case EWOULDBLOCK: return (0); - case ENXIO: + case ENXIO: /* FreeBSD, DragonFly BSD, and Darwin */ + case EIO: /* OpenBSD */ + /* NetBSD appears not to return an error in this case */ /* * The device on which we're capturing * went away. * * XXX - we should really return - * PCAP_ERROR_IFACE_NOT_UP, but - * pcap_dispatch() etc. aren't - * defined to retur that. + * an appropriate error for that, + * but pcap_dispatch() etc. aren't + * documented as having error returns + * other than PCAP_ERROR or PCAP_ERROR_BREAK. */ snprintf(p->errbuf, PCAP_ERRBUF_SIZE, - "The interface went down"); + "The interface disappeared"); return (PCAP_ERROR); #if defined(sun) && !defined(BSD) && !defined(__svr4__) && !defined(__SVR4) @@ -881,24 +1177,31 @@ pcap_read_bpf(pcap_t *p, int cnt, pcap_handler callback, u_char *user) /* fall through */ #endif } - snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "read: %s", - pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "read"); return (PCAP_ERROR); } - bp = p->buffer; + bp = (u_char *)p->buffer; } else bp = p->bp; /* * Loop through each packet. + * + * This assumes that a single buffer of packets will have + * <= INT_MAX packets, so the packet count doesn't overflow. */ +#ifdef BIOCSTSTAMP +#define bhp ((struct bpf_xhdr *)bp) +#else #define bhp ((struct bpf_hdr *)bp) +#endif ep = bp + cc; #ifdef PCAP_FDDIPAD pad = p->fddipad; #endif while (bp < ep) { - register int caplen, hdrlen; + register u_int caplen, hdrlen; /* * Has "pcap_breakloop()" been called? @@ -911,14 +1214,28 @@ pcap_read_bpf(pcap_t *p, int cnt, pcap_handler callback, u_char *user) * processed so far. */ if (p->break_loop) { + p->bp = bp; + p->cc = (int)(ep - bp); + /* + * ep is set based on the return value of read(), + * but read() from a BPF device doesn't necessarily + * return a value that's a multiple of the alignment + * value for BPF_WORDALIGN(). However, whenever we + * increment bp, we round up the increment value by + * a value rounded up by BPF_WORDALIGN(), so we + * could increment bp past ep after processing the + * last packet in the buffer. + * + * We treat ep < bp as an indication that this + * happened, and just set p->cc to 0. + */ + if (p->cc < 0) + p->cc = 0; if (n == 0) { p->break_loop = 0; return (PCAP_ERROR_BREAK); - } else { - p->bp = bp; - p->cc = ep - bp; + } else return (n); - } } caplen = bhp->bh_caplen; @@ -937,10 +1254,28 @@ pcap_read_bpf(pcap_t *p, int cnt, pcap_handler callback, u_char *user) * skipping that padding. #endif */ - if (p->md.use_bpf || - bpf_filter(p->fcode.bf_insns, datap, bhp->bh_datalen, caplen)) { + if (pb->filtering_in_kernel || + pcap_filter(p->fcode.bf_insns, datap, bhp->bh_datalen, caplen)) { struct pcap_pkthdr pkthdr; +#ifdef BIOCSTSTAMP + struct bintime bt; + bt.sec = bhp->bh_tstamp.bt_sec; + bt.frac = bhp->bh_tstamp.bt_frac; + if (p->opt.tstamp_precision == PCAP_TSTAMP_PRECISION_NANO) { + struct timespec ts; + + bintime2timespec(&bt, &ts); + pkthdr.ts.tv_sec = ts.tv_sec; + pkthdr.ts.tv_usec = ts.tv_nsec; + } else { + struct timeval tv; + + bintime2timeval(&bt, &tv); + pkthdr.ts.tv_sec = tv.tv_sec; + pkthdr.ts.tv_usec = tv.tv_usec; + } +#else pkthdr.ts.tv_sec = bhp->bh_tstamp.tv_sec; #ifdef _AIX /* @@ -951,6 +1286,7 @@ pcap_read_bpf(pcap_t *p, int cnt, pcap_handler callback, u_char *user) #else pkthdr.ts.tv_usec = bhp->bh_tstamp.tv_usec; #endif +#endif /* BIOCSTSTAMP */ #ifdef PCAP_FDDIPAD if (caplen > pad) pkthdr.caplen = caplen - pad; @@ -967,9 +1303,14 @@ pcap_read_bpf(pcap_t *p, int cnt, pcap_handler callback, u_char *user) #endif (*callback)(user, &pkthdr, datap); bp += BPF_WORDALIGN(caplen + hdrlen); - if (++n >= cnt && cnt > 0) { + if (++n >= cnt && !PACKET_COUNT_IS_UNLIMITED(cnt)) { p->bp = bp; - p->cc = ep - bp; + p->cc = (int)(ep - bp); + /* + * See comment above about p->cc < 0. + */ + if (p->cc < 0) + p->cc = 0; return (n); } } else { @@ -985,21 +1326,21 @@ pcap_read_bpf(pcap_t *p, int cnt, pcap_handler callback, u_char *user) } static int -pcap_inject_bpf(pcap_t *p, const void *buf, size_t size) +pcap_inject_bpf(pcap_t *p, const void *buf, int size) { int ret; - ret = write(p->fd, buf, size); + ret = (int)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: + * In some versions of macOS, 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 + * So, if, on macOS, 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 @@ -1008,26 +1349,25 @@ pcap_inject_bpf(pcap_t *p, const void *buf, size_t size) * 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. + * that Apple fixed the problem in some macOS 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)); + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "send: can't turn off BIOCSHDRCMPLT"); return (PCAP_ERROR); } /* * Now try the write again. */ - ret = write(p->fd, buf, size); + ret = (int)write(p->fd, buf, size); } #endif /* __APPLE__ */ if (ret == -1) { - snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "send: %s", - pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "send"); return (PCAP_ERROR); } return (ret); @@ -1115,8 +1455,8 @@ bpf_load(char *errbuf) major = genmajor(BPF_NAME); if (major == -1) { - snprintf(errbuf, PCAP_ERRBUF_SIZE, - "bpf_load: genmajor failed: %s", pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, + errno, "bpf_load: genmajor failed"); (void)bpf_odmcleanup(NULL); return (PCAP_ERROR); } @@ -1125,9 +1465,8 @@ bpf_load(char *errbuf) if (!minors) { minors = genminor("bpf", major, 0, BPF_MINORS, 1, 1); if (!minors) { - snprintf(errbuf, PCAP_ERRBUF_SIZE, - "bpf_load: genminor failed: %s", - pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, + errno, "bpf_load: genminor failed"); (void)bpf_odmcleanup(NULL); return (PCAP_ERROR); } @@ -1138,20 +1477,19 @@ bpf_load(char *errbuf) 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)); + pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, + errno, "bpf_load: can't stat %s", BPF_NODE "0"); return (PCAP_ERROR); } if (rc == -1 || getmajor(sbuf.st_rdev) != major) { for (i = 0; i < BPF_MINORS; i++) { - sprintf(buf, "%s%d", BPF_NODE, i); + snprintf(buf, sizeof(buf), "%s%d", BPF_NODE, i); unlink(buf); if (mknod(buf, S_IRUSR | S_IFCHR, domakedev(major, i)) == -1) { - snprintf(errbuf, PCAP_ERRBUF_SIZE, - "bpf_load: can't mknod %s: %s", - buf, pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(errbuf, + PCAP_ERRBUF_SIZE, errno, + "bpf_load: can't mknod %s", buf); return (PCAP_ERROR); } } @@ -1159,15 +1497,14 @@ bpf_load(char *errbuf) /* Check if the driver is loaded */ memset(&cfg_ld, 0x0, sizeof(cfg_ld)); + snprintf(buf, sizeof(buf), "%s/%s", DRIVER_PATH, BPF_NAME); cfg_ld.path = buf; - sprintf(cfg_ld.path, "%s/%s", DRIVER_PATH, BPF_NAME); if ((sysconfig(SYS_QUERYLOAD, (void *)&cfg_ld, sizeof(cfg_ld)) == -1) || (cfg_ld.kmid == 0)) { /* Driver isn't loaded, load it now */ if (sysconfig(SYS_SINGLELOAD, (void *)&cfg_ld, sizeof(cfg_ld)) == -1) { - snprintf(errbuf, PCAP_ERRBUF_SIZE, - "bpf_load: could not load driver: %s", - strerror(errno)); + pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, + errno, "bpf_load: could not load driver"); return (PCAP_ERROR); } } @@ -1180,9 +1517,8 @@ bpf_load(char *errbuf) 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)); + pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, + errno, "bpf_load: could not configure driver"); return (PCAP_ERROR); } } @@ -1194,24 +1530,25 @@ bpf_load(char *errbuf) #endif /* - * Turn off rfmon mode if necessary. + * Undo any operations done when opening the device when necessary. */ static void pcap_cleanup_bpf(pcap_t *p) { + struct pcap_bpf *pb = p->priv; #ifdef HAVE_BSD_IEEE80211 int sock; struct ifmediareq req; struct ifreq ifr; #endif - if (p->md.must_do_on_close != 0) { + if (pb->must_do_on_close != 0) { /* * There's something we have to do when closing this * pcap_t. */ #ifdef HAVE_BSD_IEEE80211 - if (p->md.must_do_on_close & MUST_CLEAR_RFMON) { + if (pb->must_do_on_close & MUST_CLEAR_RFMON) { /* * We put the interface into rfmon mode; * take it out of rfmon mode. @@ -1228,7 +1565,7 @@ pcap_cleanup_bpf(pcap_t *p) strerror(errno)); } else { memset(&req, 0, sizeof(req)); - strncpy(req.ifm_name, p->md.device, + pcap_strlcpy(req.ifm_name, pb->device, sizeof(req.ifm_name)); if (ioctl(sock, SIOCGIFMEDIA, &req) < 0) { fprintf(stderr, @@ -1242,8 +1579,8 @@ pcap_cleanup_bpf(pcap_t *p) * turn it off. */ memset(&ifr, 0, sizeof(ifr)); - (void)strncpy(ifr.ifr_name, - p->md.device, + (void)pcap_strlcpy(ifr.ifr_name, + pb->device, sizeof(ifr.ifr_name)); ifr.ifr_media = req.ifm_current & ~IFM_IEEE80211_MONITOR; @@ -1261,48 +1598,67 @@ pcap_cleanup_bpf(pcap_t *p) } #endif /* HAVE_BSD_IEEE80211 */ +#if defined(__FreeBSD__) && defined(SIOCIFCREATE2) + /* + * Attempt to destroy the usbusN interface that we created. + */ + if (pb->must_do_on_close & MUST_DESTROY_USBUS) { + if (if_nametoindex(pb->device) > 0) { + int s; + + s = socket(AF_LOCAL, SOCK_DGRAM, 0); + if (s >= 0) { + pcap_strlcpy(ifr.ifr_name, pb->device, + sizeof(ifr.ifr_name)); + ioctl(s, SIOCIFDESTROY, &ifr); + close(s); + } + } + } +#endif /* defined(__FreeBSD__) && defined(SIOCIFCREATE2) */ /* * Take this pcap out of the list of pcaps for which we * have to take the interface out of some mode. */ pcap_remove_from_pcaps_to_close(p); - p->md.must_do_on_close = 0; + pb->must_do_on_close = 0; } #ifdef HAVE_ZEROCOPY_BPF - /* - * 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 (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); + if (pb->zerocopy) { + /* + * 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 (pb->zbuf1 != MAP_FAILED && pb->zbuf1 != NULL) + (void) munmap(pb->zbuf1, pb->zbufsize); + if (pb->zbuf2 != MAP_FAILED && pb->zbuf2 != NULL) + (void) munmap(pb->zbuf2, pb->zbufsize); + p->buffer = NULL; } #endif - if (p->md.device != NULL) { - free(p->md.device); - p->md.device = NULL; + if (pb->device != NULL) { + free(pb->device); + pb->device = NULL; } pcap_cleanup_live_common(p); } +#ifdef __APPLE__ static int check_setif_failure(pcap_t *p, int error) { -#ifdef __APPLE__ int fd; - struct ifreq ifr; int err; -#endif - if (error == ENXIO) { + if (error == PCAP_ERROR_NO_SUCH_DEVICE) { /* * No such device exists. */ -#ifdef __APPLE__ - if (p->opt.rfmon && strncmp(p->opt.source, "wlt", 3) == 0) { + if (p->opt.rfmon && strncmp(p->opt.device, "wlt", 3) == 0) { /* * Monitor mode was requested, and we're trying * to open a "wltN" device. Assume that this @@ -1312,32 +1668,38 @@ check_setif_failure(pcap_t *p, int error) */ 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; - snprintf(p->errbuf, PCAP_ERRBUF_SIZE, - "SIOCGIFFLAGS on %s failed: %s", - ifr.ifr_name, pcap_strerror(errno)); - } else { + char *en_name; + + if (pcap_asprintf(&en_name, "en%s", + p->opt.device + 3) == -1) { /* - * 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. + * We can't find out whether there's + * an underlying "enN" device, so + * just report "no such device". */ - err = PCAP_ERROR_RFMON_NOTSUP; + pcap_fmt_errmsg_for_errno(p->errbuf, + PCAP_ERRBUF_SIZE, errno, + "malloc"); + close(fd); + return (PCAP_ERROR_NO_SUCH_DEVICE); + } + err = device_exists(fd, en_name, p->errbuf); + free(en_name); + if (err != 0) { + if (err == PCAP_ERROR_NO_SUCH_DEVICE) { + /* + * 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 { @@ -1347,52 +1709,65 @@ check_setif_failure(pcap_t *p, int error) * just report "no such device". */ err = PCAP_ERROR_NO_SUCH_DEVICE; - snprintf(p->errbuf, PCAP_ERRBUF_SIZE, - "socket() failed: %s", - pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(p->errbuf, + errno, PCAP_ERRBUF_SIZE, + "socket() failed"); } return (err); } -#endif + /* * No such device. */ - snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCSETIF failed: %s", - pcap_strerror(errno)); 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); } + + /* + * Just return the error status; it's what we want, and, if it's + * PCAP_ERROR, the error string has been filled in. + */ + return (error); +} +#else +static int +check_setif_failure(pcap_t *p _U_, int error) +{ + /* + * Just return the error status; it's what we want, and, if it's + * PCAP_ERROR, the error string has been filled in. + */ + return (error); } +#endif /* * Default capture buffer size. * 32K isn't very much for modern machines with fast networks; we * pick .5M, as that's the maximum on at least some systems with BPF. + * + * However, on AIX 3.5, the larger buffer sized caused unrecoverable + * read failures under stress, so we leave it as 32K; yet another + * place where AIX's BPF is broken. */ +#ifdef _AIX +#define DEFAULT_BUFSIZE 32768 +#else #define DEFAULT_BUFSIZE 524288 +#endif static int pcap_activate_bpf(pcap_t *p) { + struct pcap_bpf *pb = p->priv; int status = 0; +#ifdef HAVE_BSD_IEEE80211 + int retv; +#endif int fd; - struct ifreq ifr; +#if defined(LIFNAMSIZ) && defined(ZONENAME_MAX) && defined(lifr_zoneid) + struct lifreq ifr; + char *zonesep; +#endif struct bpf_version bv; #ifdef __APPLE__ int sockfd; @@ -1417,7 +1792,7 @@ pcap_activate_bpf(pcap_t *p) u_int bufmode, zbufmax; #endif - fd = bpf_open(p); + fd = bpf_open(p->errbuf); if (fd < 0) { status = fd; goto bad; @@ -1426,8 +1801,8 @@ pcap_activate_bpf(pcap_t *p) p->fd = fd; if (ioctl(fd, BIOCVERSION, (caddr_t)&bv) < 0) { - snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCVERSION: %s", - pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "BIOCVERSION"); status = PCAP_ERROR; goto bad; } @@ -1439,10 +1814,70 @@ pcap_activate_bpf(pcap_t *p) 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)); + /* + * Turn a negative snapshot value (invalid), a snapshot value of + * 0 (unspecified), or a value bigger than the normal maximum + * value, into the maximum allowed value. + * + * If some application really *needs* a bigger snapshot + * length, we should just increase MAXIMUM_SNAPLEN. + */ + if (p->snapshot <= 0 || p->snapshot > MAXIMUM_SNAPLEN) + p->snapshot = MAXIMUM_SNAPLEN; + +#if defined(LIFNAMSIZ) && defined(ZONENAME_MAX) && defined(lifr_zoneid) + /* + * Retrieve the zoneid of the zone we are currently executing in. + */ + if ((ifr.lifr_zoneid = getzoneid()) == -1) { + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "getzoneid()"); + status = PCAP_ERROR; + goto bad; + } + /* + * Check if the given source datalink name has a '/' separated + * zonename prefix string. The zonename prefixed source datalink can + * be used by pcap consumers in the Solaris global zone to capture + * traffic on datalinks in non-global zones. Non-global zones + * do not have access to datalinks outside of their own namespace. + */ + if ((zonesep = strchr(p->opt.device, '/')) != NULL) { + char path_zname[ZONENAME_MAX]; + int znamelen; + char *lnamep; + + if (ifr.lifr_zoneid != GLOBAL_ZONEID) { + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "zonename/linkname only valid in global zone."); + status = PCAP_ERROR; + goto bad; + } + znamelen = zonesep - p->opt.device; + (void) pcap_strlcpy(path_zname, p->opt.device, znamelen + 1); + ifr.lifr_zoneid = getzoneidbyname(path_zname); + if (ifr.lifr_zoneid == -1) { + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "getzoneidbyname(%s)", path_zname); + status = PCAP_ERROR; + goto bad; + } + lnamep = strdup(zonesep + 1); + if (lnamep == NULL) { + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "strdup"); + status = PCAP_ERROR; + goto bad; + } + free(p->opt.device); + p->opt.device = lnamep; + } +#endif + + pb->device = strdup(p->opt.device); + if (pb->device == NULL) { + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "strdup"); status = PCAP_ERROR; goto bad; } @@ -1477,32 +1912,26 @@ pcap_activate_bpf(pcap_t *p) /* * 10.4 (Darwin 8.x). s/en/wlt/ */ - if (strncmp(p->opt.source, "en", 2) != 0) { + if (strncmp(p->opt.device, "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) { + status = device_exists(sockfd, + p->opt.device, p->errbuf); + if (status == 0) { /* - * We assume this - * failed because - * the underlying - * device doesn't - * exist. + * The device exists, + * but it's not an + * enN device; that + * means it doesn't + * support monitor + * mode. */ - status = PCAP_ERROR_NO_SUCH_DEVICE; - snprintf(p->errbuf, - PCAP_ERRBUF_SIZE, - "SIOCGIFFLAGS failed: %s", - pcap_strerror(errno)); - } else status = PCAP_ERROR_RFMON_NOTSUP; + } close(sockfd); } else { /* @@ -1511,25 +1940,24 @@ pcap_activate_bpf(pcap_t *p) * report "no such device". */ status = PCAP_ERROR_NO_SUCH_DEVICE; - snprintf(p->errbuf, - PCAP_ERRBUF_SIZE, - "socket() failed: %s", - pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(p->errbuf, + PCAP_ERRBUF_SIZE, errno, + "socket() failed"); } goto bad; } - wltdev = malloc(strlen(p->opt.source) + 2); + wltdev = malloc(strlen(p->opt.device) + 2); if (wltdev == NULL) { - (void)snprintf(p->errbuf, - PCAP_ERRBUF_SIZE, "malloc: %s", - pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(p->errbuf, + PCAP_ERRBUF_SIZE, errno, + "malloc"); status = PCAP_ERROR; goto bad; } strcpy(wltdev, "wlt"); - strcat(wltdev, p->opt.source + 2); - free(p->opt.source); - p->opt.source = wltdev; + strcat(wltdev, p->opt.device + 2); + free(p->opt.device); + p->opt.device = wltdev; } /* * Everything else is 10.5 or later; for those, @@ -1538,6 +1966,87 @@ pcap_activate_bpf(pcap_t *p) } } #endif /* __APPLE__ */ + + /* + * If this is FreeBSD, and the device name begins with "usbus", + * try to create the interface if it's not available. + */ +#if defined(__FreeBSD__) && defined(SIOCIFCREATE2) + if (strncmp(p->opt.device, usbus_prefix, USBUS_PREFIX_LEN) == 0) { + /* + * Do we already have an interface with that name? + */ + if (if_nametoindex(p->opt.device) == 0) { + /* + * No. We need to create it, and, if we + * succeed, remember that we should destroy + * it when the pcap_t is closed. + */ + int s; + struct ifreq ifr; + + /* + * Open a socket to use for ioctls to + * create the interface. + */ + s = socket(AF_LOCAL, SOCK_DGRAM, 0); + if (s < 0) { + pcap_fmt_errmsg_for_errno(p->errbuf, + PCAP_ERRBUF_SIZE, errno, + "Can't open socket"); + status = PCAP_ERROR; + goto bad; + } + + /* + * If we haven't already done so, arrange to have + * "pcap_close_all()" called when we exit. + */ + if (!pcap_do_addexit(p)) { + /* + * "atexit()" failed; don't create the + * interface, just give up. + */ + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "atexit failed"); + close(s); + status = PCAP_ERROR; + goto bad; + } + + /* + * Create the interface. + */ + pcap_strlcpy(ifr.ifr_name, p->opt.device, sizeof(ifr.ifr_name)); + if (ioctl(s, SIOCIFCREATE2, &ifr) < 0) { + if (errno == EINVAL) { + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "Invalid USB bus interface %s", + p->opt.device); + } else { + pcap_fmt_errmsg_for_errno(p->errbuf, + PCAP_ERRBUF_SIZE, errno, + "Can't create interface for %s", + p->opt.device); + } + close(s); + status = PCAP_ERROR; + goto bad; + } + + /* + * Make sure we clean this up when we close. + */ + pb->must_do_on_close |= MUST_DESTROY_USBUS; + + /* + * Add this to the list of pcaps to close when we exit. + */ + pcap_add_to_pcaps_to_close(p); + } + } +#endif /* defined(__FreeBSD__) && defined(SIOCIFCREATE2) */ + #ifdef HAVE_ZEROCOPY_BPF /* * If the BPF extension to set buffer mode is present, try setting @@ -1549,15 +2058,7 @@ pcap_activate_bpf(pcap_t *p) /* * 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; + pb->zerocopy = 1; /* * How to pick a buffer size: first, query the maximum buffer @@ -1569,8 +2070,9 @@ pcap_activate_bpf(pcap_t *p) * size. */ if (ioctl(fd, BIOCGETZMAX, (caddr_t)&zbufmax) < 0) { - snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCGETZMAX: %s", - pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "BIOCGETZMAX"); + status = PCAP_ERROR; goto bad; } @@ -1587,34 +2089,45 @@ pcap_activate_bpf(pcap_t *p) #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, + pb->zbufsize = roundup(v, getpagesize()); + if (pb->zbufsize > zbufmax) + pb->zbufsize = zbufmax; + pb->zbuf1 = mmap(NULL, pb->zbufsize, PROT_READ | PROT_WRITE, MAP_ANON, -1, 0); - p->md.zbuf2 = mmap(NULL, p->md.zbufsize, PROT_READ | PROT_WRITE, + pb->zbuf2 = mmap(NULL, pb->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)); + if (pb->zbuf1 == MAP_FAILED || pb->zbuf2 == MAP_FAILED) { + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "mmap"); + status = PCAP_ERROR; goto bad; } - bzero(&bz, sizeof(bz)); - bz.bz_bufa = p->md.zbuf1; - bz.bz_bufb = p->md.zbuf2; - bz.bz_buflen = p->md.zbufsize; + memset(&bz, 0, sizeof(bz)); /* bzero() deprecated, replaced with memset() */ + bz.bz_bufa = pb->zbuf1; + bz.bz_bufb = pb->zbuf2; + bz.bz_buflen = pb->zbufsize; if (ioctl(fd, BIOCSETZBUF, (caddr_t)&bz) < 0) { - snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCSETZBUF: %s", - pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "BIOCSETZBUF"); + status = PCAP_ERROR; 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)); + status = bpf_bind(fd, p->opt.device, ifnamsiz, p->errbuf); + if (status != BPF_BIND_SUCCEEDED) { + if (status == BPF_BIND_BUFFER_TOO_BIG) { + /* + * The requested buffer size + * is too big. Fail. + * + * XXX - should we do the "keep cutting + * the buffer size in half" loop here if + * we're using the default buffer size? + */ + status = PCAP_ERROR; + } goto bad; } - v = p->md.zbufsize - sizeof(struct bpf_zbuf_header); + v = pb->zbufsize - sizeof(struct bpf_zbuf_header); } else #endif { @@ -1628,9 +2141,9 @@ pcap_activate_bpf(pcap_t *p) */ 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)); + pcap_fmt_errmsg_for_errno(p->errbuf, + PCAP_ERRBUF_SIZE, errno, + "BIOCSBLEN: %s", p->opt.device); status = PCAP_ERROR; goto bad; } @@ -1638,10 +2151,23 @@ pcap_activate_bpf(pcap_t *p) /* * 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); + status = bpf_bind(fd, p->opt.device, p->errbuf); + if (status != BPF_BIND_SUCCEEDED) { + if (status == BPF_BIND_BUFFER_TOO_BIG) { + /* + * The requested buffer size + * is too big. Fail. + */ + status = PCAP_ERROR; + goto bad; + } + + /* + * Special checks on macOS to deal with + * the way monitor mode was done on + * 10.4 Tiger. + */ + status = check_setif_failure(p, status); goto bad; } } else { @@ -1667,13 +2193,24 @@ pcap_activate_bpf(pcap_t *p) */ (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) + status = bpf_bind(fd, p->opt.device, p->errbuf); + if (status == BPF_BIND_SUCCEEDED) break; /* that size worked; we're done */ - if (errno != ENOBUFS) { - status = check_setif_failure(p, errno); + /* + * If the attempt failed because the + * buffer was too big, cut the buffer + * size in half and try again. + * + * Otherwise, fail. + */ + if (status != BPF_BIND_BUFFER_TOO_BIG) { + /* + * Special checks on macOS to deal + * with the way monitor mode was + * done on 10.4 Tiger. + */ + status = check_setif_failure(p, status); goto bad; } } @@ -1681,7 +2218,7 @@ pcap_activate_bpf(pcap_t *p) if (v == 0) { snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCSBLEN: %s: No buffer size worked", - p->opt.source); + p->opt.device); status = PCAP_ERROR; goto bad; } @@ -1690,8 +2227,8 @@ pcap_activate_bpf(pcap_t *p) /* Get the data link layer type. */ if (ioctl(fd, BIOCGDLT, (caddr_t)&v) < 0) { - snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCGDLT: %s", - pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "BIOCGDLT"); status = PCAP_ERROR; goto bad; } @@ -1783,9 +2320,9 @@ pcap_activate_bpf(pcap_t *p) * we try to select DLT_IEEE802_11. */ if (have_osinfo) { - if (isdigit((unsigned)osinfo.release[0]) && + if (PCAP_ISDIGIT((unsigned)osinfo.release[0]) && (osinfo.release[0] == '9' || - isdigit((unsigned)osinfo.release[1]))) { + PCAP_ISDIGIT((unsigned)osinfo.release[1]))) { /* * 10.5 (Darwin 9.x), or later. */ @@ -1804,14 +2341,14 @@ pcap_activate_bpf(pcap_t *p) * of link-layer types, as selecting * it will keep monitor mode off. */ - remove_en(p); + remove_non_802_11(p); /* * If the new mode we want isn't * the default mode, attempt to * select the new mode. */ - if (new_dlt != v) { + if ((u_int)new_dlt != v) { if (ioctl(p->fd, BIOCSDLT, &new_dlt) != -1) { /* @@ -1858,11 +2395,12 @@ pcap_activate_bpf(pcap_t *p) /* * Try to put the interface into monitor mode. */ - status = monitor_mode(p, 1); - if (status != 0) { + retv = monitor_mode(p, 1); + if (retv != 0) { /* * We failed. */ + status = retv; goto bad; } @@ -1882,7 +2420,7 @@ pcap_activate_bpf(pcap_t *p) * If the new mode we want isn't the default mode, * attempt to select the new mode. */ - if (new_dlt != v) { + if ((u_int)new_dlt != v) { if (ioctl(p->fd, BIOCSDLT, &new_dlt) != -1) { /* * We succeeded; make this the @@ -1919,8 +2457,8 @@ pcap_activate_bpf(pcap_t *p) if (v == DLT_FDDI) p->fddipad = PCAP_FDDIPAD; else - p->fddipad = 0; #endif + p->fddipad = 0; p->linktype = v; #if defined(BIOCGHDRCMPLT) && defined(BIOCSHDRCMPLT) @@ -1934,17 +2472,22 @@ pcap_activate_bpf(pcap_t *p) * BSDs - check CVS log for "bpf.c"? */ if (ioctl(fd, BIOCSHDRCMPLT, &spoof_eth_src) == -1) { - (void)snprintf(p->errbuf, PCAP_ERRBUF_SIZE, - "BIOCSHDRCMPLT: %s", pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "BIOCSHDRCMPLT"); status = PCAP_ERROR; goto bad; } #endif /* set timeout */ #ifdef HAVE_ZEROCOPY_BPF - if (p->md.timeout != 0 && !p->md.zerocopy) { + /* + * In zero-copy mode, we just use the timeout in select(). + * XXX - what if we're in non-blocking mode and the *application* + * is using select() or poll() or kqueues or....? + */ + if (p->opt.timeout && !pb->zerocopy) { #else - if (p->md.timeout) { + if (p->opt.timeout) { #endif /* * XXX - is this seconds/nanoseconds in AIX? @@ -1968,21 +2511,21 @@ pcap_activate_bpf(pcap_t *p) struct BPF_TIMEVAL bpf_to; if (IOCPARM_LEN(BIOCSRTIMEOUT) != sizeof(struct timeval)) { - bpf_to.tv_sec = p->md.timeout / 1000; - bpf_to.tv_usec = (p->md.timeout * 1000) % 1000000; + bpf_to.tv_sec = p->opt.timeout / 1000; + bpf_to.tv_usec = (p->opt.timeout * 1000) % 1000000; if (ioctl(p->fd, BIOCSRTIMEOUT, (caddr_t)&bpf_to) < 0) { - snprintf(p->errbuf, PCAP_ERRBUF_SIZE, - "BIOCSRTIMEOUT: %s", pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(p->errbuf, + errno, PCAP_ERRBUF_SIZE, "BIOCSRTIMEOUT"); status = PCAP_ERROR; goto bad; } } else { #endif - to.tv_sec = p->md.timeout / 1000; - to.tv_usec = (p->md.timeout * 1000) % 1000000; + to.tv_sec = p->opt.timeout / 1000; + to.tv_usec = (p->opt.timeout * 1000) % 1000000; if (ioctl(p->fd, BIOCSRTIMEOUT, (caddr_t)&to) < 0) { - snprintf(p->errbuf, PCAP_ERRBUF_SIZE, - "BIOCSRTIMEOUT: %s", pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(p->errbuf, + errno, PCAP_ERRBUF_SIZE, "BIOCSRTIMEOUT"); status = PCAP_ERROR; goto bad; } @@ -1991,7 +2534,6 @@ pcap_activate_bpf(pcap_t *p) #endif } -#ifdef _AIX #ifdef BIOCIMMEDIATE /* * Darren Reed notes that @@ -2003,75 +2545,72 @@ pcap_activate_bpf(pcap_t *p) * is reducing things to only a few packets (i.e. one every * second or so). * - * so we turn BIOCIMMEDIATE mode on if this is AIX. + * so we always turn BIOCIMMEDIATE mode on if this is AIX. * - * We don't turn it on for other platforms, as that means we - * get woken up for every packet, which may not be what we want; - * in the Winter 1993 USENIX paper on BPF, they say: + * For other platforms, we don't turn immediate mode on by default, + * as that would mean we get woken up for every packet, which + * probably isn't what you want for a packet sniffer. * - * Since a process might want to look at every packet on a - * network and the time between packets can be only a few - * microseconds, it is not possible to do a read system call - * per packet and BPF must collect the data from several - * packets and return it as a unit when the monitoring - * application does a read. - * - * which I infer is the reason for the timeout - it means we - * wait that amount of time, in the hopes that more packets - * will arrive and we'll get them all with one read. - * - * Setting BIOCIMMEDIATE mode on FreeBSD (and probably other - * BSDs) causes the timeout to be ignored. - * - * On the other hand, some platforms (e.g., Linux) don't support - * timeouts, they just hand stuff to you as soon as it arrives; - * if that doesn't cause a problem on those platforms, it may - * be OK to have BIOCIMMEDIATE mode on BSD as well. - * - * (Note, though, that applications may depend on the read - * completing, even if no packets have arrived, when the timeout - * expires, e.g. GUI applications that have to check for input - * while waiting for packets to arrive; a non-zero timeout - * prevents "select()" from working right on FreeBSD and - * possibly other BSDs, as the timer doesn't start until a - * "read()" is done, so the timer isn't in effect if the - * application is blocked on a "select()", and the "select()" - * doesn't get woken up for a BPF device until the buffer - * fills up.) - */ - v = 1; - if (ioctl(p->fd, BIOCIMMEDIATE, &v) < 0) { - snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCIMMEDIATE: %s", - pcap_strerror(errno)); + * We set immediate mode if the caller requested it by calling + * pcap_set_immediate() before calling pcap_activate(). + */ +#ifndef _AIX + if (p->opt.immediate) { +#endif /* _AIX */ + v = 1; + if (ioctl(p->fd, BIOCIMMEDIATE, &v) < 0) { + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "BIOCIMMEDIATE"); + status = PCAP_ERROR; + goto bad; + } +#ifndef _AIX + } +#endif /* _AIX */ +#else /* BIOCIMMEDIATE */ + if (p->opt.immediate) { + /* + * We don't support immediate mode. Fail. + */ + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Immediate mode not supported"); status = PCAP_ERROR; goto bad; } -#endif /* BIOCIMMEDIATE */ -#endif /* _AIX */ +#endif /* BIOCIMMEDIATE */ if (p->opt.promisc) { /* set promiscuous mode, just warn if it fails */ if (ioctl(p->fd, BIOCPROMISC, NULL) < 0) { - snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCPROMISC: %s", - pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "BIOCPROMISC"); status = PCAP_WARNING_PROMISC_NOTSUP; } } +#ifdef BIOCSTSTAMP + v = BPF_T_BINTIME; + if (ioctl(p->fd, BIOCSTSTAMP, &v) < 0) { + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "BIOCSTSTAMP"); + status = PCAP_ERROR; + goto bad; + } +#endif /* BIOCSTSTAMP */ + if (ioctl(fd, BIOCGBLEN, (caddr_t)&v) < 0) { - snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCGBLEN: %s", - pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "BIOCGBLEN"); status = PCAP_ERROR; goto bad; } p->bufsize = v; #ifdef HAVE_ZEROCOPY_BPF - if (!p->md.zerocopy) { + if (!pb->zerocopy) { #endif - p->buffer = (u_char *)malloc(p->bufsize); + p->buffer = malloc(p->bufsize); if (p->buffer == NULL) { - snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "malloc: %s", - pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "malloc"); status = PCAP_ERROR; goto bad; } @@ -2101,8 +2640,8 @@ pcap_activate_bpf(pcap_t *p) total_prog.bf_len = 1; total_prog.bf_insns = &total_insn; if (ioctl(p->fd, BIOCSETF, (caddr_t)&total_prog) < 0) { - snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCSETF: %s", - pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "BIOCSETF"); status = PCAP_ERROR; goto bad; } @@ -2160,28 +2699,325 @@ pcap_activate_bpf(pcap_t *p) p->setfilter_op = pcap_setfilter_bpf; p->setdirection_op = pcap_setdirection_bpf; p->set_datalink_op = pcap_set_datalink_bpf; - p->getnonblock_op = pcap_getnonblock_fd; - p->setnonblock_op = pcap_setnonblock_fd; + p->getnonblock_op = pcap_getnonblock_bpf; + p->setnonblock_op = pcap_setnonblock_bpf; p->stats_op = pcap_stats_bpf; p->cleanup_op = pcap_cleanup_bpf; return (status); bad: - pcap_cleanup_bpf(p); + pcap_cleanup_bpf(p); return (status); } -int -pcap_platform_finddevs(pcap_if_t **alldevsp, char *errbuf) +/* + * Not all interfaces can be bound to by BPF, so try to bind to + * the specified interface; return 0 if we fail with + * PCAP_ERROR_NO_SUCH_DEVICE (which means we got an ENXIO when we tried + * to bind, which means this interface isn't in the list of interfaces + * attached to BPF) and 1 otherwise. + */ +static int +check_bpf_bindable(const char *name) +{ + int fd; + char errbuf[PCAP_ERRBUF_SIZE]; + + /* + * On macOS, we don't do this check if the device name begins + * with "wlt"; at least some versions of macOS (actually, it + * was called "Mac OS X" then...) offer monitor mode capturing + * by having a separate "monitor mode" device for each wireless + * adapter, rather than by implementing the ioctls that + * {Free,Net,Open,DragonFly}BSD provide. Opening that device + * puts the adapter into monitor mode, which, at least for + * some adapters, causes them to deassociate from the network + * with which they're associated. + * + * Instead, we try to open the corresponding "en" device (so + * that we don't end up with, for users without sufficient + * privilege to open capture devices, a list of adapters that + * only includes the wlt devices). + */ +#ifdef __APPLE__ + if (strncmp(name, "wlt", 3) == 0) { + char *en_name; + size_t en_name_len; + + /* + * Try to allocate a buffer for the "en" + * device's name. + */ + en_name_len = strlen(name) - 1; + en_name = malloc(en_name_len + 1); + if (en_name == NULL) { + pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, + errno, "malloc"); + return (-1); + } + strcpy(en_name, "en"); + strcat(en_name, name + 3); + fd = bpf_open_and_bind(en_name, errbuf); + free(en_name); + } else +#endif /* __APPLE */ + fd = bpf_open_and_bind(name, errbuf); + if (fd < 0) { + /* + * Error - was it PCAP_ERROR_NO_SUCH_DEVICE? + */ + if (fd == PCAP_ERROR_NO_SUCH_DEVICE) { + /* + * Yes, so we can't bind to this because it's + * not something supported by BPF. + */ + return (0); + } + /* + * No, so we don't know whether it's supported or not; + * say it is, so that the user can at least try to + * open it and report the error (which is probably + * "you don't have permission to open BPF devices"; + * reporting those interfaces means users will ask + * "why am I getting a permissions error when I try + * to capture" rather than "why am I not seeing any + * interfaces", making the underlying problem clearer). + */ + return (1); + } + + /* + * Success. + */ + close(fd); + return (1); +} + +#if defined(__FreeBSD__) && defined(SIOCIFCREATE2) +static int +get_usb_if_flags(const char *name _U_, bpf_u_int32 *flags _U_, char *errbuf _U_) +{ + /* + * XXX - if there's a way to determine whether there's something + * plugged into a given USB bus, use that to determine whether + * this device is "connected" or not. + */ + return (0); +} + +static int +finddevs_usb(pcap_if_list_t *devlistp, char *errbuf) { -#ifdef HAVE_DAG_API - if (dag_platform_finddevs(alldevsp, errbuf) < 0) + DIR *usbdir; + struct dirent *usbitem; + size_t name_max; + char *name; + + /* + * We might have USB sniffing support, so try looking for USB + * interfaces. + * + * We want to report a usbusN device for each USB bus, but + * usbusN interfaces might, or might not, exist for them - + * we create one if there isn't already one. + * + * So, instead, we look in /dev/usb for all buses and create + * a "usbusN" device for each one. + */ + usbdir = opendir("/dev/usb"); + if (usbdir == NULL) { + /* + * Just punt. + */ + return (0); + } + + /* + * Leave enough room for a 32-bit (10-digit) bus number. + * Yes, that's overkill, but we won't be using + * the buffer very long. + */ + name_max = USBUS_PREFIX_LEN + 10 + 1; + name = malloc(name_max); + if (name == NULL) { + closedir(usbdir); + return (0); + } + while ((usbitem = readdir(usbdir)) != NULL) { + char *p; + size_t busnumlen; + + if (strcmp(usbitem->d_name, ".") == 0 || + strcmp(usbitem->d_name, "..") == 0) { + /* + * Ignore these. + */ + continue; + } + p = strchr(usbitem->d_name, '.'); + if (p == NULL) + continue; + busnumlen = p - usbitem->d_name; + memcpy(name, usbus_prefix, USBUS_PREFIX_LEN); + memcpy(name + USBUS_PREFIX_LEN, usbitem->d_name, busnumlen); + *(name + USBUS_PREFIX_LEN + busnumlen) = '\0'; + /* + * There's an entry in this directory for every USB device, + * not for every bus; if there's more than one device on + * the bus, there'll be more than one entry for that bus, + * so we need to avoid adding multiple capture devices + * for each bus. + */ + if (find_or_add_dev(devlistp, name, PCAP_IF_UP, + get_usb_if_flags, NULL, errbuf) == NULL) { + free(name); + closedir(usbdir); + return (PCAP_ERROR); + } + } + free(name); + closedir(usbdir); + return (0); +} +#endif + +/* + * Get additional flags for a device, using SIOCGIFMEDIA. + */ +#ifdef SIOCGIFMEDIA +static int +get_if_flags(const char *name, bpf_u_int32 *flags, char *errbuf) +{ + int sock; + struct ifmediareq req; + + sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock == -1) { + pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, errno, + "Can't create socket to get media information for %s", + name); + return (-1); + } + memset(&req, 0, sizeof(req)); + pcap_strlcpy(req.ifm_name, name, sizeof(req.ifm_name)); + if (ioctl(sock, SIOCGIFMEDIA, &req) < 0) { + if (errno == EOPNOTSUPP || errno == EINVAL || errno == ENOTTY || + errno == ENODEV || errno == EPERM +#ifdef EPWROFF + || errno == EPWROFF +#endif + ) { + /* + * Not supported, so we can't provide any + * additional information. Assume that + * this means that "connected" vs. + * "disconnected" doesn't apply. + * + * The ioctl routine for Apple's pktap devices, + * annoyingly, checks for "are you root?" before + * checking whether the ioctl is valid, so it + * returns EPERM, rather than ENOTSUP, for the + * invalid SIOCGIFMEDIA, unless you're root. + * So, just as we do for some ethtool ioctls + * on Linux, which makes the same mistake, we + * also treat EPERM as meaning "not supported". + * + * And it appears that Apple's llw0 device, which + * appears to be part of the Skywalk subsystem: + * + * http://newosxbook.com/bonus/vol1ch16.html + * + * can sometimes return EPWROFF ("Device power + * is off") for that ioctl, so we treat *that* + * as another indication that we can't get a + * connection status. (If it *isn't* "powered + * off", it's reported as a wireless device, + * complete with an active/inactive state.) + */ + *flags |= PCAP_IF_CONNECTION_STATUS_NOT_APPLICABLE; + close(sock); + return (0); + } + pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, errno, + "SIOCGIFMEDIA on %s failed", name); + close(sock); return (-1); -#endif /* HAVE_DAG_API */ -#ifdef HAVE_SNF_API - if (snf_platform_finddevs(alldevsp, errbuf) < 0) + } + close(sock); + + /* + * OK, what type of network is this? + */ + switch (IFM_TYPE(req.ifm_active)) { + + case IFM_IEEE80211: + /* + * Wireless. + */ + *flags |= PCAP_IF_WIRELESS; + break; + } + + /* + * Do we know whether it's connected? + */ + if (req.ifm_status & IFM_AVALID) { + /* + * Yes. + */ + if (req.ifm_status & IFM_ACTIVE) { + /* + * It's connected. + */ + *flags |= PCAP_IF_CONNECTION_STATUS_CONNECTED; + } else { + /* + * It's disconnected. + */ + *flags |= PCAP_IF_CONNECTION_STATUS_DISCONNECTED; + } + } + return (0); +} +#else +static int +get_if_flags(const char *name _U_, bpf_u_int32 *flags, char *errbuf _U_) +{ + /* + * Nothing we can do other than mark loopback devices as "the + * connected/disconnected status doesn't apply". + * + * XXX - on Solaris, can we do what the dladm command does, + * i.e. get a connected/disconnected indication from a kstat? + * (Note that you can also get the link speed, and possibly + * other information, from a kstat as well.) + */ + if (*flags & PCAP_IF_LOOPBACK) { + /* + * Loopback devices aren't wireless, and "connected"/ + * "disconnected" doesn't apply to them. + */ + *flags |= PCAP_IF_CONNECTION_STATUS_NOT_APPLICABLE; + return (0); + } + return (0); +} +#endif + +int +pcap_platform_finddevs(pcap_if_list_t *devlistp, char *errbuf) +{ + /* + * Get the list of regular interfaces first. + */ + if (pcap_findalldevs_interfaces(devlistp, errbuf, check_bpf_bindable, + get_if_flags) == -1) + return (-1); /* failure */ + +#if defined(__FreeBSD__) && defined(SIOCIFCREATE2) + if (finddevs_usb(devlistp, errbuf) == -1) return (-1); -#endif /* HAVE_SNF_API */ +#endif return (0); } @@ -2190,22 +3026,23 @@ pcap_platform_finddevs(pcap_if_t **alldevsp, char *errbuf) static int monitor_mode(pcap_t *p, int set) { + struct pcap_bpf *pb = p->priv; int sock; struct ifmediareq req; - int *media_list; + IFM_ULIST_TYPE *media_list; int i; int can_do; struct ifreq ifr; sock = socket(AF_INET, SOCK_DGRAM, 0); if (sock == -1) { - snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "can't open socket: %s", - pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "can't open socket"); return (PCAP_ERROR); } memset(&req, 0, sizeof req); - strncpy(req.ifm_name, p->opt.source, sizeof req.ifm_name); + pcap_strlcpy(req.ifm_name, p->opt.device, sizeof req.ifm_name); /* * Find out how many media types we have. @@ -2214,17 +3051,32 @@ monitor_mode(pcap_t *p, int set) /* * Can't get the media types. */ - if (errno == EINVAL) { + switch (errno) { + + case ENXIO: + /* + * There's no such device. + * + * There's nothing more to say, so clear the + * error message. + */ + p->errbuf[0] = '\0'; + close(sock); + return (PCAP_ERROR_NO_SUCH_DEVICE); + + case EINVAL: /* * Interface doesn't support SIOC{G,S}IFMEDIA. */ close(sock); return (PCAP_ERROR_RFMON_NOTSUP); + + default: + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "SIOCGIFMEDIA"); + close(sock); + return (PCAP_ERROR); } - snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "SIOCGIFMEDIA 1: %s", - pcap_strerror(errno)); - close(sock); - return (PCAP_ERROR); } if (req.ifm_count == 0) { /* @@ -2238,17 +3090,17 @@ monitor_mode(pcap_t *p, int set) * Allocate a buffer to hold all the media types, and * get the media types. */ - media_list = malloc(req.ifm_count * sizeof(int)); + media_list = malloc(req.ifm_count * sizeof(*media_list)); if (media_list == NULL) { - snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "malloc: %s", - pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "malloc"); close(sock); return (PCAP_ERROR); } req.ifm_ulist = media_list; if (ioctl(sock, SIOCGIFMEDIA, &req) < 0) { - snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "SIOCGIFMEDIA: %s", - pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "SIOCGIFMEDIA"); free(media_list); close(sock); return (PCAP_ERROR); @@ -2300,23 +3152,21 @@ monitor_mode(pcap_t *p, int set) * "atexit()" failed; don't put the interface * in monitor mode, just give up. */ - snprintf(p->errbuf, PCAP_ERRBUF_SIZE, - "atexit failed"); close(sock); return (PCAP_ERROR); } memset(&ifr, 0, sizeof(ifr)); - (void)strncpy(ifr.ifr_name, p->opt.source, + (void)pcap_strlcpy(ifr.ifr_name, p->opt.device, sizeof(ifr.ifr_name)); ifr.ifr_media = req.ifm_current | IFM_IEEE80211_MONITOR; if (ioctl(sock, SIOCSIFMEDIA, &ifr) == -1) { - snprintf(p->errbuf, PCAP_ERRBUF_SIZE, - "SIOCSIFMEDIA: %s", pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(p->errbuf, + PCAP_ERRBUF_SIZE, errno, "SIOCSIFMEDIA"); close(sock); return (PCAP_ERROR); } - p->md.must_do_on_close |= MUST_CLEAR_RFMON; + pb->must_do_on_close |= MUST_CLEAR_RFMON; /* * Add this to the list of pcaps to close when we exit. @@ -2343,7 +3193,7 @@ static int find_802_11(struct bpf_dltlist *bdlp) { int new_dlt; - int i; + u_int i; /* * Scan the list of DLT_ values, looking for 802.11 values, @@ -2365,8 +3215,12 @@ find_802_11(struct bpf_dltlist *bdlp) new_dlt = bdlp->bfl_list[i]; break; +#ifdef DLT_PRISM_HEADER case DLT_PRISM_HEADER: +#endif +#ifdef DLT_AIRONET_HEADER case DLT_AIRONET_HEADER: +#endif case DLT_IEEE802_11_RADIO_AVS: /* * 802.11 with radio, but not radiotap. @@ -2401,24 +3255,25 @@ find_802_11(struct bpf_dltlist *bdlp) #if defined(__APPLE__) && defined(BIOCGDLTLIST) /* - * Remove DLT_EN10MB from the list of DLT_ values, as we're in monitor mode, - * and DLT_EN10MB isn't supported in monitor mode. + * Remove non-802.11 header types from the list of DLT_ values, as we're in + * monitor mode, and those header types aren't supported in monitor mode. */ static void -remove_en(pcap_t *p) +remove_non_802_11(pcap_t *p) { int i, j; /* - * Scan the list of DLT_ values and discard DLT_EN10MB. + * Scan the list of DLT_ values and discard non-802.11 ones. */ j = 0; for (i = 0; i < p->dlt_count; i++) { switch (p->dlt_list[i]) { case DLT_EN10MB: + case DLT_RAW: /* - * Don't offer this one. + * Not 802.11. Don't offer this one. */ continue; @@ -2460,10 +3315,17 @@ remove_802_11(pcap_t *p) switch (p->dlt_list[i]) { case DLT_IEEE802_11: +#ifdef DLT_PRISM_HEADER case DLT_PRISM_HEADER: +#endif +#ifdef DLT_AIRONET_HEADER case DLT_AIRONET_HEADER: +#endif case DLT_IEEE802_11_RADIO: case DLT_IEEE802_11_RADIO_AVS: +#ifdef DLT_PPI + case DLT_PPI: +#endif /* * 802.11. Don't offer this one. */ @@ -2493,6 +3355,8 @@ remove_802_11(pcap_t *p) static int pcap_setfilter_bpf(pcap_t *p, struct bpf_program *fp) { + struct pcap_bpf *pb = p->priv; + /* * Free any user-mode filter we might happen to have installed. */ @@ -2505,7 +3369,7 @@ pcap_setfilter_bpf(pcap_t *p, struct bpf_program *fp) /* * It worked. */ - p->md.use_bpf = 1; /* filtering in the kernel */ + pb->filtering_in_kernel = 1; /* filtering in the kernel */ /* * Discard any previously-received packets, as they might @@ -2533,8 +3397,8 @@ pcap_setfilter_bpf(pcap_t *p, struct bpf_program *fp) * some kernels. */ if (errno != EINVAL) { - snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCSETF: %s", - pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "BIOCSETF"); return (-1); } @@ -2545,7 +3409,7 @@ pcap_setfilter_bpf(pcap_t *p, struct bpf_program *fp) */ if (install_bpf_program(p, fp) < 0) return (-1); - p->md.use_bpf = 0; /* filtering in userland */ + pb->filtering_in_kernel = 0; /* filtering in userland */ return (0); } @@ -2553,60 +3417,198 @@ pcap_setfilter_bpf(pcap_t *p, struct bpf_program *fp) * Set direction flag: Which packets do we accept on a forwarding * single device? IN, OUT or both? */ +#if defined(BIOCSDIRECTION) static int pcap_setdirection_bpf(pcap_t *p, pcap_direction_t d) { -#if defined(BIOCSDIRECTION) u_int direction; + const char *direction_name; + + /* + * FreeBSD and NetBSD. + */ + switch (d) { + + case PCAP_D_IN: + /* + * Incoming, but not outgoing, so accept only + * incoming packets. + */ + direction = BPF_D_IN; + direction_name = "\"incoming only\""; + break; + + case PCAP_D_OUT: + /* + * Outgoing, but not incoming, so accept only + * outgoing packets. + */ + direction = BPF_D_OUT; + direction_name = "\"outgoing only\""; + break; + + default: + /* + * Incoming and outgoing, so accept both + * incoming and outgoing packets. + * + * It's guaranteed, at this point, that d is a valid + * direction value, so we know that this is PCAP_D_INOUT + * if it's not PCAP_D_IN or PCAP_D_OUT. + */ + direction = BPF_D_INOUT; + direction_name = "\"incoming and outgoing\""; + break; + } - direction = (d == PCAP_D_IN) ? BPF_D_IN : - ((d == PCAP_D_OUT) ? BPF_D_OUT : BPF_D_INOUT); if (ioctl(p->fd, BIOCSDIRECTION, &direction) == -1) { - (void) snprintf(p->errbuf, sizeof(p->errbuf), - "Cannot set direction to %s: %s", - (d == PCAP_D_IN) ? "PCAP_D_IN" : - ((d == PCAP_D_OUT) ? "PCAP_D_OUT" : "PCAP_D_INOUT"), - strerror(errno)); + pcap_fmt_errmsg_for_errno(p->errbuf, sizeof(p->errbuf), + errno, "Cannot set direction to %s", direction_name); + return (-1); + } + return (0); +} +#elif defined(BIOCSDIRFILT) +static int +pcap_setdirection_bpf(pcap_t *p, pcap_direction_t d) +{ + u_int dirfilt; + const char *direction_name; + + /* + * OpenBSD; same functionality, different names, different + * semantics (the flags mean "*don't* capture packets in + * that direction", not "*capture only* packets in that + * direction"). + */ + switch (d) { + + case PCAP_D_IN: + /* + * Incoming, but not outgoing, so filter out + * outgoing packets. + */ + dirfilt = BPF_DIRECTION_OUT; + direction_name = "\"incoming only\""; + break; + + case PCAP_D_OUT: + /* + * Outgoing, but not incoming, so filter out + * incoming packets. + */ + dirfilt = BPF_DIRECTION_IN; + direction_name = "\"outgoing only\""; + break; + + default: + /* + * Incoming and outgoing, so don't filter out + * any packets based on direction. + * + * It's guaranteed, at this point, that d is a valid + * direction value, so we know that this is PCAP_D_INOUT + * if it's not PCAP_D_IN or PCAP_D_OUT. + */ + dirfilt = 0; + direction_name = "\"incoming and outgoing\""; + break; + } + if (ioctl(p->fd, BIOCSDIRFILT, &dirfilt) == -1) { + pcap_fmt_errmsg_for_errno(p->errbuf, sizeof(p->errbuf), + errno, "Cannot set direction to %s", direction_name); return (-1); } return (0); +} #elif defined(BIOCSSEESENT) +static int +pcap_setdirection_bpf(pcap_t *p, pcap_direction_t d) +{ u_int seesent; + const char *direction_name; /* - * We don't support PCAP_D_OUT. + * OS with just BIOCSSEESENT. */ - if (d == PCAP_D_OUT) { + switch (d) { + + case PCAP_D_IN: + /* + * Incoming, but not outgoing, so we don't want to + * see transmitted packets. + */ + seesent = 0; + direction_name = "\"incoming only\""; + break; + + case PCAP_D_OUT: + /* + * Outgoing, but not incoming; we can't specify that. + */ snprintf(p->errbuf, sizeof(p->errbuf), - "Setting direction to PCAP_D_OUT is not supported on BPF"); - return -1; + "Setting direction to \"outgoing only\" is not supported on this device"); + return (-1); + + default: + /* + * Incoming and outgoing, so we want to see transmitted + * packets. + * + * It's guaranteed, at this point, that d is a valid + * direction value, so we know that this is PCAP_D_INOUT + * if it's not PCAP_D_IN or PCAP_D_OUT. + */ + seesent = 1; + direction_name = "\"incoming and outgoing\""; + break; } - seesent = (d == PCAP_D_INOUT); if (ioctl(p->fd, BIOCSSEESENT, &seesent) == -1) { - (void) snprintf(p->errbuf, sizeof(p->errbuf), - "Cannot set direction to %s: %s", - (d == PCAP_D_INOUT) ? "PCAP_D_INOUT" : "PCAP_D_IN", - strerror(errno)); + pcap_fmt_errmsg_for_errno(p->errbuf, sizeof(p->errbuf), + errno, "Cannot set direction to %s", direction_name); return (-1); } return (0); +} #else +static int +pcap_setdirection_bpf(pcap_t *p, pcap_direction_t d _U_) +{ (void) snprintf(p->errbuf, sizeof(p->errbuf), - "This system doesn't support BIOCSSEESENT, so the direction can't be set"); + "Setting direction is not supported on this device"); return (-1); -#endif } +#endif +#ifdef BIOCSDLT static int pcap_set_datalink_bpf(pcap_t *p, int dlt) { -#ifdef BIOCSDLT if (ioctl(p->fd, BIOCSDLT, &dlt) == -1) { - (void) snprintf(p->errbuf, sizeof(p->errbuf), - "Cannot set DLT %d: %s", dlt, strerror(errno)); + pcap_fmt_errmsg_for_errno(p->errbuf, sizeof(p->errbuf), + errno, "Cannot set DLT %d", dlt); return (-1); } -#endif return (0); } +#else +static int +pcap_set_datalink_bpf(pcap_t *p _U_, int dlt _U_) +{ + return (0); +} +#endif + +/* + * Platform-specific information. + */ +const char * +pcap_lib_version(void) +{ +#ifdef HAVE_ZEROCOPY_BPF + return (PCAP_VERSION_STRING " (with zerocopy support)"); +#else + return (PCAP_VERSION_STRING); +#endif +}