diff --git a/config.h.in b/config.h.in index 87a5a7a..3544f15 100644 --- a/config.h.in +++ b/config.h.in @@ -59,6 +59,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_LIMITS_H +/* Define to 1 if you have the header file. */ +#undef HAVE_LINUX_WIRELESS_H + /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H @@ -68,6 +71,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_NETINET_IF_ETHER_H +/* Define to 1 if you have the header file. */ +#undef HAVE_NET_IF_MEDIA_H + /* Define to 1 if you have the header file. */ #undef HAVE_NET_PFVAR_H diff --git a/configure.in b/configure.in index d947b25..d8f9354 100644 --- a/configure.in +++ b/configure.in @@ -1,4 +1,4 @@ -dnl @(#) $Header: /tcpdump/master/libpcap/configure.in,v 1.148 2008-03-14 09:12:49 guy Exp $ (LBL) +dnl @(#) $Header: /tcpdump/master/libpcap/configure.in,v 1.149 2008-04-04 19:37:44 guy Exp $ (LBL) dnl dnl Copyright (c) 1994, 1995, 1996, 1997 dnl The Regents of the University of California. All rights reserved. @@ -6,7 +6,7 @@ dnl dnl Process this file with autoconf to produce a configure script. dnl -AC_REVISION($Revision: 1.148 $) +AC_REVISION($Revision: 1.149 $) AC_PREREQ(2.50) AC_INIT(pcap.c) @@ -448,9 +448,23 @@ linux) if test $ac_cv_linux_vers -lt 2 ; then AC_MSG_ERROR(version 2 or higher required; see the INSTALL doc for more info) fi + AC_CHECK_HEADERS(linux/wireless.h, [], [], + [ +#include +#include +#include + ]) + AC_CHECK_HEADERS() AC_LBL_TPACKET_STATS ;; +bpf) + # + # Check whether we have the *BSD-style ioctls. + # + AC_CHECK_HEADERS(net/if_media.h) + ;; + dag) V_DEFS="$V_DEFS -DDAG_ONLY" ;; diff --git a/dlpisubs.c b/dlpisubs.c index beeee3e..14f64c0 100644 --- a/dlpisubs.c +++ b/dlpisubs.c @@ -12,7 +12,7 @@ */ #ifndef lint static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/dlpisubs.c,v 1.1 2008-03-13 18:13:57 guy Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/dlpisubs.c,v 1.2 2008-04-04 19:37:45 guy Exp $ (LBL)"; #endif #ifdef HAVE_CONFIG_H @@ -197,7 +197,7 @@ pcap_process_pkts(pcap_t *p, pcap_handler callback, u_char *user, * Process the mac type. Returns -1 if no matching mac type found, otherwise 0. */ int -pcap_process_mactype(pcap_t *p, u_int mactype, char *ebuf) +pcap_process_mactype(pcap_t *p, u_int mactype) { int retv = 0; @@ -247,7 +247,7 @@ pcap_process_mactype(pcap_t *p, u_int mactype, char *ebuf) #endif default: - snprintf(ebuf, PCAP_ERRBUF_SIZE, "unknown mactype %u", + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "unknown mactype %u", mactype); retv = -1; } @@ -260,7 +260,7 @@ pcap_process_mactype(pcap_t *p, u_int mactype, char *ebuf) * Push and configure the buffer module. Returns -1 for error, otherwise 0. */ int -pcap_conf_bufmod(pcap_t *p, int snaplen, int timeout, char *ebuf) +pcap_conf_bufmod(pcap_t *p, int snaplen, int timeout) { int retv = 0; @@ -268,14 +268,14 @@ pcap_conf_bufmod(pcap_t *p, int snaplen, int timeout, char *ebuf) /* Non-standard call to get the data nicely buffered. */ if (ioctl(p->fd, I_PUSH, "bufmod") != 0) { - pcap_stream_err("I_PUSH bufmod", errno, ebuf); + pcap_stream_err("I_PUSH bufmod", errno, p->errbuf); retv = -1; } ss = snaplen; if (ss > 0 && strioctl(p->fd, SBIOCSSNAP, sizeof(ss), (char *)&ss) != 0) { - pcap_stream_err("SBIOCSSNAP", errno, ebuf); + pcap_stream_err("SBIOCSSNAP", errno, p->errbuf); retv = -1; } @@ -286,7 +286,7 @@ pcap_conf_bufmod(pcap_t *p, int snaplen, int timeout, char *ebuf) to.tv_sec = timeout / 1000; to.tv_usec = (timeout * 1000) % 1000000; if (strioctl(p->fd, SBIOCSTIME, sizeof(to), (char *)&to) != 0) { - pcap_stream_err("SBIOCSTIME", errno, ebuf); + pcap_stream_err("SBIOCSTIME", errno, p->errbuf); retv = -1; } } @@ -295,7 +295,7 @@ pcap_conf_bufmod(pcap_t *p, int snaplen, int timeout, char *ebuf) chunksize = CHUNKSIZE; if (strioctl(p->fd, SBIOCSCHUNK, sizeof(chunksize), (char *)&chunksize) != 0) { - pcap_stream_err("SBIOCSCHUNKP", errno, ebuf); + pcap_stream_err("SBIOCSCHUNKP", errno, p->errbuf); retv = -1; } @@ -307,12 +307,12 @@ pcap_conf_bufmod(pcap_t *p, int snaplen, int timeout, char *ebuf) * Allocate data buffer. Returns -1 if memory allocation fails, else 0. */ int -pcap_alloc_databuf(pcap_t *p, char *ebuf) +pcap_alloc_databuf(pcap_t *p) { p->bufsize = PKTBUFSIZE; p->buffer = (u_char *)malloc(p->bufsize + p->offset); if (p->buffer == NULL) { - strlcpy(ebuf, pcap_strerror(errno), PCAP_ERRBUF_SIZE); + strlcpy(p->errbuf, pcap_strerror(errno), PCAP_ERRBUF_SIZE); return (-1); } diff --git a/dlpisubs.h b/dlpisubs.h index 62b9d73..1b57d4d 100644 --- a/dlpisubs.h +++ b/dlpisubs.h @@ -1,5 +1,5 @@ /* - * @(#) $Header: /tcpdump/master/libpcap/dlpisubs.h,v 1.1 2008-03-13 18:13:57 guy Exp $ + * @(#) $Header: /tcpdump/master/libpcap/dlpisubs.h,v 1.2 2008-04-04 19:37:45 guy Exp $ */ #ifndef dlpisubs_h @@ -14,11 +14,11 @@ extern "C" { */ int pcap_stats_dlpi(pcap_t *, struct pcap_stat *); int pcap_process_pkts(pcap_t *, pcap_handler, u_char *, int, u_char *, int); -int pcap_process_mactype(pcap_t *, u_int, char *); +int pcap_process_mactype(pcap_t *, u_int); #ifdef HAVE_SYS_BUFMOD_H -int pcap_conf_bufmod(pcap_t *, int, int, char *); +int pcap_conf_bufmod(pcap_t *, int, int); #endif -int pcap_alloc_databuf(pcap_t *, char *); +int pcap_alloc_databuf(pcap_t *); int strioctl(int, int, int, char *); #ifdef __cplusplus diff --git a/pcap-bpf.c b/pcap-bpf.c index f298b10..6e311f4 100644 --- a/pcap-bpf.c +++ b/pcap-bpf.c @@ -20,7 +20,7 @@ */ #ifndef lint static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/pcap-bpf.c,v 1.103 2008-01-29 10:12:55 guy Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/pcap-bpf.c,v 1.104 2008-04-04 19:37:45 guy Exp $ (LBL)"; #endif #ifdef HAVE_CONFIG_H @@ -92,6 +92,10 @@ static int odmlockid = 0; #include #include +#ifdef HAVE_NET_IF_MEDIA_H +# include +#endif + #include "pcap-int.h" #ifdef HAVE_DAG_API @@ -102,10 +106,346 @@ static int odmlockid = 0; #include "os-proto.h" #endif +#ifdef BIOCGDLTLIST +# if (defined(HAVE_NET_IF_MEDIA_H) && defined(IFM_IEEE80211)) && !defined(__APPLE__) +#define HAVE_BSD_IEEE80211 +# endif + +# if defined(__APPLE__) || defined(HAVE_BSD_IEEE80211) +static int find_802_11(struct bpf_dltlist *); + +# ifdef HAVE_BSD_IEEE80211 +static int monitor_mode(pcap_t *, int); +# endif + +# if defined(__APPLE__) +static void remove_en(pcap_t *); +static void remove_802_11(pcap_t *); +# endif + +# endif /* defined(__APPLE__) || defined(HAVE_BSD_IEEE80211) */ + +#endif /* BIOCGDLTLIST */ + +/* + * We include the OS's , not our "pcap/bpf.h", so we probably + * don't get DLT_DOCSIS defined. + */ +#ifndef DLT_DOCSIS +#define DLT_DOCSIS 143 +#endif + +/* + * On OS X, we don't even get any of the 802.11-plus-radio-header DLT_'s + * defined, even though some of them are used by various Airport drivers. + */ +#ifndef DLT_PRISM_HEADER +#define DLT_PRISM_HEADER 119 +#endif +#ifndef DLT_AIRONET_HEADER +#define DLT_AIRONET_HEADER 120 +#endif +#ifndef DLT_IEEE802_11_RADIO +#define DLT_IEEE802_11_RADIO 127 +#endif +#ifndef DLT_IEEE802_11_RADIO_AVS +#define DLT_IEEE802_11_RADIO_AVS 163 +#endif + +static int pcap_can_set_rfmon_bpf(pcap_t *p); +static int pcap_activate_bpf(pcap_t *p); static int pcap_setfilter_bpf(pcap_t *p, struct bpf_program *fp); static int pcap_setdirection_bpf(pcap_t *, pcap_direction_t); static int pcap_set_datalink_bpf(pcap_t *p, int dlt); +pcap_t * +pcap_create(const char *device, char *ebuf) +{ + pcap_t *p; + +#ifdef HAVE_DAG_API + if (strstr(device, "dag")) + return (dag_create(device, ebuf)); +#endif /* HAVE_DAG_API */ + + p = pcap_create_common(device, ebuf); + if (p == NULL) + return (NULL); + + p->activate_op = pcap_activate_bpf; + p->can_set_rfmon_op = pcap_can_set_rfmon_bpf; + return (p); +} + +static int +bpf_open(pcap_t *p) +{ + int fd; +#ifdef HAVE_CLONING_BPF + static const char device[] = "/dev/bpf"; +#else + int n = 0; + char device[sizeof "/dev/bpf0000000000"]; +#endif + +#ifdef _AIX + /* + * Load the bpf driver, if it isn't already loaded, + * and create the BPF device entries, if they don't + * already exist. + */ + if (bpf_load(p->errbuf) == -1) + return (-1); +#endif + +#ifdef HAVE_CLONING_BPF + if ((fd = open(device, O_RDWR)) == -1 && + (errno != EACCES || (fd = open(device, O_RDONLY)) == -1)) + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "(cannot open device) %s: %s", device, pcap_strerror(errno)); +#else + /* + * Go through all the minors and find one that isn't in use. + */ + do { + (void)snprintf(device, sizeof(device), "/dev/bpf%d", n++); + /* + * Initially try a read/write open (to allow the inject + * method to work). If that fails due to permission + * issues, fall back to read-only. This allows a + * non-root user to be granted specific access to pcap + * capabilities via file permissions. + * + * XXX - we should have an API that has a flag that + * controls whether to open read-only or read-write, + * so that denial of permission to send (or inability + * to send, if sending packets isn't supported on + * the device in question) can be indicated at open + * time. + */ + fd = open(device, O_RDWR); + if (fd == -1 && errno == EACCES) + fd = open(device, O_RDONLY); + } while (fd < 0 && errno == EBUSY); + + /* + * XXX better message for all minors used + */ + if (fd < 0) + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "(no devices found) %s: %s", + device, pcap_strerror(errno)); +#endif + + return (fd); +} + +#ifdef BIOCGDLTLIST +static int +get_dlt_list(int fd, int v, struct bpf_dltlist *bdlp, char *ebuf) +{ + memset(bdlp, 0, sizeof(*bdlp)); + if (ioctl(fd, BIOCGDLTLIST, (caddr_t)bdlp) == 0) { + u_int i; + int is_ethernet; + + bdlp->bfl_list = (u_int *) malloc(sizeof(u_int) * (bdlp->bfl_len + 1)); + if (bdlp->bfl_list == NULL) { + (void)snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s", + pcap_strerror(errno)); + return (-1); + } + + if (ioctl(fd, BIOCGDLTLIST, (caddr_t)bdlp) < 0) { + (void)snprintf(ebuf, PCAP_ERRBUF_SIZE, + "BIOCGDLTLIST: %s", pcap_strerror(errno)); + free(bdlp->bfl_list); + return (-1); + } + + /* + * OK, for real Ethernet devices, add DLT_DOCSIS to the + * list, so that an application can let you choose it, + * in case you're capturing DOCSIS traffic that a Cisco + * Cable Modem Termination System is putting out onto + * an Ethernet (it doesn't put an Ethernet header onto + * the wire, it puts raw DOCSIS frames out on the wire + * inside the low-level Ethernet framing). + * + * A "real Ethernet device" is defined here as a device + * that has a link-layer type of DLT_EN10MB and that has + * no alternate link-layer types; that's done to exclude + * 802.11 interfaces (which might or might not be the + * right thing to do, but I suspect it is - Ethernet <-> + * 802.11 bridges would probably badly mishandle frames + * that don't have Ethernet headers). + */ + if (v == DLT_EN10MB) { + is_ethernet = 1; + for (i = 0; i < bdlp->bfl_len; i++) { + if (bdlp->bfl_list[i] != DLT_EN10MB) { + is_ethernet = 0; + break; + } + } + if (is_ethernet) { + /* + * We reserved one more slot at the end of + * the list. + */ + bdlp->bfl_list[bdlp->bfl_len] = DLT_DOCSIS; + bdlp->bfl_len++; + } + } + } else { + /* + * EINVAL just means "we don't support this ioctl on + * this device"; don't treat it as an error. + */ + if (errno != EINVAL) { + (void)snprintf(ebuf, PCAP_ERRBUF_SIZE, + "BIOCGDLTLIST: %s", pcap_strerror(errno)); + return (-1); + } + } + return (0); +} +#endif + +static int +pcap_can_set_rfmon_bpf(pcap_t *p) +{ +#if defined(__APPLE__) + struct utsname osinfo; + struct ifreq ifr; + int fd; +#ifdef BIOCGDLTLIST + struct bpf_dltlist bdl; +#endif + + /* + * The joys of monitor mode on OS X. + * + * Prior to 10.4, it's not supported at all. + * + * In 10.4, if adapter enN supports monitor mode, there's a + * wltN adapter corresponding to it; you open it, instead of + * enN, to get monitor mode. You get whatever link-layer + * headers it supplies. + * + * In 10.5, and, we assume, later releases, if adapter enN + * supports monitor mode, it offers, among its selectable + * DLT_ values, values that let you get the 802.11 header; + * selecting one of those values puts the adapter into monitor + * mode (i.e., you can't get 802.11 headers except in monitor + * mode, and you can't get Ethernet headers in monitor mode). + */ + if (uname(&osinfo) == -1) { + /* + * Can't get the OS version; just say "no". + */ + return (0); + } + /* + * We assume osinfo.sysname is "Darwin", because + * __APPLE__ is defined. We just check the version. + */ + if (osinfo.release[0] < '8' && osinfo.release[1] == '.') { + /* + * 10.3 (Darwin 7.x) or earlier. + * Monitor mode not supported. + */ + return (0); + } + if (osinfo.release[0] == '8' && osinfo.release[1] == '.') { + /* + * 10.4 (Darwin 8.x). s/en/wlt/, and check + * whether the device exists. + */ + if (strncmp(p->opt.source, "en", 2) != 0) { + /* + * Not an enN device; no monitor mode. + */ + return (0); + } + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd == -1) { + (void)snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "socket: %s", pcap_strerror(errno)); + return (PCAP_ERROR); + } + strlcpy(ifr.ifr_name, "wlt", sizeof(ifr.ifr_name)); + strlcat(ifr.ifr_name, p->opt.source + 2, sizeof(ifr.ifr_name)); + if (ioctl(fd, SIOCGIFFLAGS, (char *)&ifr) < 0) { + /* + * No such device? + */ + close(fd); + return (0); + } + close(fd); + return (1); + } + +#ifdef BIOCGDLTLIST + /* + * Everything else is 10.5 or later; for those, + * we just open the enN device, and check whether + * we have any 802.11 devices. + * + * First, open a BPF device. + */ + fd = bpf_open(p); + if (fd < 0) + return (PCAP_ERROR); + + /* + * 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) { + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "BIOCSETIF: %s: %s", + p->opt.source, pcap_strerror(errno)); + close(fd); + return (PCAP_ERROR); + } + + /* + * We know the default link type -- now determine all the DLTs + * this interface supports. If this fails with EINVAL, it's + * not fatal; we just don't get to use the feature later. + * (We don't care about DLT_DOCSIS, so we pass DLT_NULL + * as the default DLT for this adapter.) + */ + if (get_dlt_list(fd, DLT_NULL, &bdl, p->errbuf) == -1) { + close(fd); + return (PCAP_ERROR); + } + if (find_802_11(&bdl) != -1) { + /* + * We have an 802.11 DLT, so we can set monitor mode. + */ + free(bdl.bfl_list); + close(fd); + return (1); + } + free(bdl.bfl_list); +#endif /* BIOCGDLTLIST */ + return (0); +#elif defined(HAVE_BSD_IEEE80211) + int ret; + + ret = monitor_mode(p, 0); + if (ret == PCAP_ERROR_RFMON_NOTSUP) + return (0); /* not an error, just a "can't do" */ + if (ret == 0) + return (1); /* success */ + return (ret); +#else + return (0); +#endif +} + static int pcap_stats_bpf(pcap_t *p, struct pcap_stat *ps) { @@ -516,169 +856,348 @@ bpf_load(char *errbuf) } #endif -static inline int -bpf_open(pcap_t *p, char *errbuf) +/* + * Turn off rfmon mode if necessary. + */ +static void +pcap_close_bpf(pcap_t *p) { - int fd; -#ifdef HAVE_CLONING_BPF - static const char device[] = "/dev/bpf"; -#else - int n = 0; - char device[sizeof "/dev/bpf0000000000"]; +#ifdef HAVE_BSD_IEEE80211 + int sock; + struct ifmediareq req; + struct ifreq ifr; #endif -#ifdef _AIX - /* - * Load the bpf driver, if it isn't already loaded, - * and create the BPF device entries, if they don't - * already exist. - */ - if (bpf_load(errbuf) == -1) - return (-1); -#endif - -#ifdef HAVE_CLONING_BPF - if ((fd = open(device, O_RDWR)) == -1 && - (errno != EACCES || (fd = open(device, O_RDONLY)) == -1)) - snprintf(errbuf, PCAP_ERRBUF_SIZE, - "(cannot open device) %s: %s", device, pcap_strerror(errno)); -#else - /* - * Go through all the minors and find one that isn't in use. - */ - do { - (void)snprintf(device, sizeof(device), "/dev/bpf%d", n++); + if (p->md.must_clear != 0) { /* - * 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. + * There's something we have to do when closing this + * pcap_t. */ - fd = open(device, O_RDWR); - if (fd == -1 && errno == EACCES) - fd = open(device, O_RDONLY); - } while (fd < 0 && errno == EBUSY); +#ifdef HAVE_BSD_IEEE80211 + if (p->md.must_clear & MUST_CLEAR_RFMON) { + /* + * We put the interface into rfmon mode; + * take it out of rfmon mode. + * + * XXX - if somebody else wants it in rfmon + * mode, this code cannot know that, so it'll take + * it out of rfmon mode. + */ + sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock == -1) { + fprintf(stderr, + "Can't restore interface flags (socket() failed: %s).\n" + "Please adjust manually.\n", + strerror(errno)); + } else { + memset(&req, 0, sizeof(req)); + strncpy(req.ifm_name, p->md.device, + sizeof(req.ifm_name)); + if (ioctl(sock, SIOCGIFMEDIA, &req) < 0) { + fprintf(stderr, + "Can't restore interface flags (SIOCGIFMEDIA failed: %s).\n" + "Please adjust manually.\n", + strerror(errno)); + } else { + if (req.ifm_current & IFM_IEEE80211_MONITOR) { + /* + * Rfmon mode is currently on; + * turn it off. + */ + memset(&ifr, 0, sizeof(ifr)); + (void)strncpy(ifr.ifr_name, + p->md.device, + sizeof(ifr.ifr_name)); + ifr.ifr_media = + req.ifm_current & ~IFM_IEEE80211_MONITOR; + if (ioctl(sock, SIOCSIFMEDIA, + &ifr) == -1) { + fprintf(stderr, + "Can't restore interface flags (SIOCSIFMEDIA failed: %s).\n" + "Please adjust manually.\n", + strerror(errno)); + } + } + } + close(sock); + } + } +#endif /* HAVE_BSD_IEEE80211 */ - /* - * XXX better message for all minors used - */ - if (fd < 0) - snprintf(errbuf, PCAP_ERRBUF_SIZE, "(no devices found) %s: %s", - device, pcap_strerror(errno)); -#endif + /* + * 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); + } - return (fd); + if (p->md.device != NULL) + free(p->md.device); + p->md.device = NULL; + pcap_close_common(p); } -/* - * We include the OS's , not our "pcap/bpf.h", so we probably - * don't get DLT_DOCSIS defined. - */ -#ifndef DLT_DOCSIS -#define DLT_DOCSIS 143 +static int +check_setif_failure(pcap_t *p, int error) +{ +#ifdef __APPLE__ + int fd; + struct ifreq ifr; + int err; #endif -pcap_t * -pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, - char *ebuf) + if (error == ENXIO) { + /* + * No such device exists. + */ +#ifdef __APPLE__ + if (p->opt.rfmon && strncmp(p->opt.source, "wlt", 3) == 0) { + /* + * Monitor mode was requested, and we're trying + * to open a "wltN" device. Assume that this + * is 10.4 and that we were asked to open an + * "enN" device; if that device exists, return + * "monitor mode not supported on the device". + */ + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd != -1) { + strlcpy(ifr.ifr_name, "en", + sizeof(ifr.ifr_name)); + strlcat(ifr.ifr_name, p->opt.source + 3, + sizeof(ifr.ifr_name)); + if (ioctl(fd, SIOCGIFFLAGS, (char *)&ifr) < 0) { + /* + * We assume this failed because + * the underlying device doesn't + * exist. + */ + err = PCAP_ERROR_NO_SUCH_DEVICE; + } else { + /* + * The underlying "enN" device + * exists, but there's no + * corresponding "wltN" device; + * that means that the "enN" + * device doesn't support + * monitor mode, probably because + * it's an Ethernet device rather + * than a wireless device. + */ + err = PCAP_ERROR_RFMON_NOTSUP; + } + close(fd); + } else { + /* + * We can't find out whether there's + * an underlying "enN" device, so + * just report "no such device". + */ + err = PCAP_ERROR_NO_SUCH_DEVICE; + } + return (err); + } +#endif + /* + * No such device. + */ + return (PCAP_ERROR_NO_SUCH_DEVICE); + } else { + /* + * Some other error; fill in the error string, and + * return PCAP_ERROR. + */ + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCSETIF: %s: %s", + p->opt.source, pcap_strerror(errno)); + return (PCAP_ERROR); + } +} + +static int +pcap_activate_bpf(pcap_t *p) { + int err; int fd; struct ifreq ifr; struct bpf_version bv; +#ifdef __APPLE__ + char *wltdev = NULL; +#endif #ifdef BIOCGDLTLIST struct bpf_dltlist bdl; +#if defined(__APPLE__) || defined(HAVE_BSD_IEEE80211) + int new_dlt; #endif +#endif /* BIOCGDLTLIST */ #if defined(BIOCGHDRCMPLT) && defined(BIOCSHDRCMPLT) u_int spoof_eth_src = 1; #endif u_int v; - pcap_t *p; struct bpf_insn total_insn; struct bpf_program total_prog; struct utsname osinfo; + int have_osinfo = 0; -#ifdef HAVE_DAG_API - if (strstr(device, "dag")) { - return dag_open_live(device, snaplen, promisc, to_ms, ebuf); - } -#endif /* HAVE_DAG_API */ - - p = (pcap_t *)malloc(sizeof(*p)); - if (p == NULL) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s", - pcap_strerror(errno)); - return (NULL); - } - memset(p, 0, sizeof(*p)); - fd = bpf_open(p, ebuf); - if (fd < 0) + fd = bpf_open(p); + if (fd < 0) { + err = PCAP_ERROR; goto bad; + } p->fd = fd; - p->snapshot = snaplen; if (ioctl(fd, BIOCVERSION, (caddr_t)&bv) < 0) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCVERSION: %s", + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCVERSION: %s", pcap_strerror(errno)); + err = PCAP_ERROR; goto bad; } if (bv.bv_major != BPF_MAJOR_VERSION || bv.bv_minor < BPF_MINOR_VERSION) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "kernel bpf filter out of date"); + err = PCAP_ERROR; goto bad; } + p->md.device = strdup(p->opt.source); + if (p->md.device == NULL) { + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "strdup: %s", + pcap_strerror(errno)); + return PCAP_ERROR; + } + /* - * Try finding a good size for the buffer; 32768 may be too - * big, so keep cutting it in half until we find a size - * that works, or run out of sizes to try. If the default - * is larger, don't make it smaller. - * - * XXX - there should be a user-accessible hook to set the - * initial buffer size. + * Attempt to find out the version of the OS on which we're running. */ - if ((ioctl(fd, BIOCGBLEN, (caddr_t)&v) < 0) || v < 32768) - v = 32768; - for ( ; v != 0; v >>= 1) { - /* Ignore the return value - this is because the call fails - * on BPF systems that don't have kernel malloc. And if - * the call fails, it's no big deal, we just continue to - * use the standard buffer size. + if (uname(&osinfo) == 0) + have_osinfo = 1; + +#ifdef __APPLE__ + /* + * See comment in pcap_can_set_rfmon_bpf() for an explanation + * of why we check the version number. + */ + if (p->opt.rfmon) { + if (have_osinfo) { + /* + * We assume osinfo.sysname is "Darwin", because + * __APPLE__ is defined. We just check the version. + */ + if (osinfo.release[0] < '8' && + osinfo.release[1] == '.') { + /* + * 10.3 (Darwin 7.x) or earlier. + */ + err = PCAP_ERROR_RFMON_NOTSUP; + goto bad; + } + if (osinfo.release[0] == '8' && + osinfo.release[1] == '.') { + /* + * 10.4 (Darwin 8.x). s/en/wlt/ + */ + if (strncmp(p->opt.source, "en", 2) != 0) { + /* + * Not an enN device; no monitor + * mode. + */ + err = PCAP_ERROR_RFMON_NOTSUP; + goto bad; + } + wltdev = malloc(strlen(p->opt.source) + 2); + if (wltdev == NULL) { + (void)snprintf(p->errbuf, + PCAP_ERRBUF_SIZE, "malloc: %s", + pcap_strerror(errno)); + err = PCAP_ERROR; + goto bad; + } + strcpy(wltdev, "wlt"); + strcat(wltdev, p->opt.source + 2); + free(p->opt.source); + p->opt.source = wltdev; + } + /* + * Everything else is 10.5 or later; for those, + * we just open the enN device, and set the DLT. + */ + } + } +#endif /* __APPLE__ */ + + /* + * Set the buffer size. + */ + if (p->opt.buffer_size != 0) { + /* + * A buffer size was explicitly specified; use it. */ - (void) ioctl(fd, BIOCSBLEN, (caddr_t)&v); + 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)); + err = PCAP_ERROR; + goto bad; + } - (void)strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name)); - if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) >= 0) - break; /* that size worked; we're done */ + /* + * 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) { + err = check_setif_failure(p, errno); + goto bad; + } + } else { + /* + * No buffer size was explicitly specified. + * + * Try finding a good size for the buffer; 32768 may + * be too big, so keep cutting it in half until we + * find a size that works, or run out of sizes to try. + * If the default is larger, don't make it smaller. + */ + if ((ioctl(fd, BIOCGBLEN, (caddr_t)&v) < 0) || v < 32768) + v = 32768; + for ( ; v != 0; v >>= 1) { + /* + * Ignore the return value - this is because the + * call fails on BPF systems that don't have + * kernel malloc. And if the call fails, it's + * no big deal, we just continue to use the + * standard buffer size. + */ + (void) ioctl(fd, BIOCSBLEN, (caddr_t)&v); - if (errno != ENOBUFS) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCSETIF: %s: %s", - device, pcap_strerror(errno)); + (void)strncpy(ifr.ifr_name, p->opt.source, + sizeof(ifr.ifr_name)); + if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) >= 0) + break; /* that size worked; we're done */ + + if (errno != ENOBUFS) { + err = check_setif_failure(p, errno); + goto bad; + } + } + + if (v == 0) { + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "BIOCSBLEN: %s: No buffer size worked", + p->opt.source); + err = PCAP_ERROR; goto bad; } } - if (v == 0) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, - "BIOCSBLEN: %s: No buffer size worked", device); + /* 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)); + err = PCAP_ERROR; goto bad; } - /* Get the data link layer type. */ - if (ioctl(fd, BIOCGDLT, (caddr_t)&v) < 0) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCGDLT: %s", - pcap_strerror(errno)); - goto bad; - } #ifdef _AIX /* * AIX's BPF returns IFF_ types, not DLT_ types, in BIOCGDLT. @@ -706,8 +1225,9 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, /* * We don't know what to map this to yet. */ - snprintf(ebuf, PCAP_ERRBUF_SIZE, "unknown interface type %u", + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "unknown interface type %u", v); + err = PCAP_ERROR; goto bad; } #endif @@ -732,13 +1252,6 @@ 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 /* @@ -746,69 +1259,144 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, * this interface supports. If this fails with EINVAL, it's * not fatal; we just don't get to use the feature later. */ - memset(&bdl, 0, sizeof(bdl)); - if (ioctl(fd, BIOCGDLTLIST, (caddr_t)&bdl) == 0) { - u_int i; - int is_ethernet; + if (get_dlt_list(fd, v, &bdl, p->errbuf) == -1) { + err = PCAP_ERROR; + goto bad; + } + p->dlt_count = bdl.bfl_len; + p->dlt_list = bdl.bfl_list; - bdl.bfl_list = (u_int *) malloc(sizeof(u_int) * (bdl.bfl_len + 1)); - if (bdl.bfl_list == NULL) { - (void)snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s", - pcap_strerror(errno)); - goto bad; +#ifdef __APPLE__ + /* + * Monitor mode fun, continued. + * + * For 10.5 and, we're assuming, later releases, as noted above, + * 802.1 adapters that support monitor mode offer both DLT_EN10MB, + * DLT_IEEE802_11, and possibly some 802.11-plus-radio-information + * DLT_ value. Choosing one of the 802.11 DLT_ values will turn + * monitor mode on. + * + * Therefore, if the user asked for monitor mode, we filter out + * the DLT_EN10MB value, as you can't get that in monitor mode, + * and, if the user didn't ask for monitor mode, we filter out + * the 802.11 DLT_ values, because selecting those will turn + * monitor mode on. Then, for monitor mode, if an 802.11-plus- + * radio DLT_ value is offered, we try to select that, otherwise + * we try to select DLT_IEEE802_11. + */ + if (have_osinfo) { + if (isdigit((unsigned)osinfo.release[0]) && + (osinfo.release[0] == '9' || + isdigit((unsigned)osinfo.release[1]))) { + /* + * 10.5 (Darwin 9.x), or later. + */ + new_dlt = find_802_11(&bdl); + if (new_dlt != -1) { + /* + * We have at least one 802.11 DLT_ value, + * so this is an 802.11 interface. + * new_dlt is the best of the 802.11 + * DLT_ values in the list. + */ + if (p->opt.rfmon) { + /* + * Our caller wants monitor mode. + * Purge DLT_EN10MB from the list + * of link-layer types, as selecting + * it will keep monitor mode off. + */ + remove_en(p); + + /* + * If the new mode we want isn't + * the default mode, attempt to + * select the new mode. + */ + if (new_dlt != v) { + if (ioctl(p->fd, BIOCSDLT, + &new_dlt) != -1) { + /* + * We succeeded; + * make this the + * new DLT_ value. + */ + v = new_dlt; + } + } + } else { + /* + * Our caller doesn't want + * monitor mode. Unless this + * is being done by pcap_open_live(), + * purge the 802.11 link-layer types + * from the list, as selecting + * one of them will turn monitor + * mode on. + */ + if (!p->oldstyle) + remove_802_11(p); + } + } else { + if (p->opt.rfmon) { + /* + * The caller requested monitor + * mode, but we have no 802.11 + * link-layer types, so they + * can't have it. + */ + err = PCAP_ERROR_RFMON_NOTSUP; + goto bad; + } + } } - - if (ioctl(fd, BIOCGDLTLIST, (caddr_t)&bdl) < 0) { - (void)snprintf(ebuf, PCAP_ERRBUF_SIZE, - "BIOCGDLTLIST: %s", pcap_strerror(errno)); - free(bdl.bfl_list); + } +#elif defined(HAVE_BSD_IEEE80211) + /* + * *BSD with the new 802.11 ioctls. + * Do we want monitor mode? + */ + if (p->opt.rfmon) { + /* + * Try to put the interface into monitor mode. + */ + err = monitor_mode(p, 1); + if (err != 0) { + /* + * We failed. + */ goto bad; } /* - * OK, for real Ethernet devices, add DLT_DOCSIS to the - * list, so that an application can let you choose it, - * in case you're capturing DOCSIS traffic that a Cisco - * Cable Modem Termination System is putting out onto - * an Ethernet (it doesn't put an Ethernet header onto - * the wire, it puts raw DOCSIS frames out on the wire - * inside the low-level Ethernet framing). - * - * A "real Ethernet device" is defined here as a device - * that has a link-layer type of DLT_EN10MB and that has - * no alternate link-layer types; that's done to exclude - * 802.11 interfaces (which might or might not be the - * right thing to do, but I suspect it is - Ethernet <-> - * 802.11 bridges would probably badly mishandle frames - * that don't have Ethernet headers). + * We're in monitor mode. + * Try to find the best 802.11 DLT_ value and, if we + * succeed, try to switch to that mode if we're not + * already in that mode. */ - if (p->linktype == DLT_EN10MB) { - is_ethernet = 1; - for (i = 0; i < bdl.bfl_len; i++) { - if (bdl.bfl_list[i] != DLT_EN10MB) { - is_ethernet = 0; - break; + new_dlt = find_802_11(&bdl); + if (new_dlt != -1) { + /* + * We have at least one 802.11 DLT_ value. + * new_dlt is the best of the 802.11 + * DLT_ values in the list. + * + * If the new mode we want isn't the default mode, + * attempt to select the new mode. + */ + if (new_dlt != v) { + if (ioctl(p->fd, BIOCSDLT, &new_dlt) != -1) { + /* + * We succeeded; make this the + * new DLT_ value. + */ + v = new_dlt; } } - if (is_ethernet) { - /* - * We reserved one more slot at the end of - * the list. - */ - bdl.bfl_list[bdl.bfl_len] = DLT_DOCSIS; - bdl.bfl_len++; - } - } - p->dlt_count = bdl.bfl_len; - p->dlt_list = bdl.bfl_list; - } else { - if (errno != EINVAL) { - (void)snprintf(ebuf, PCAP_ERRBUF_SIZE, - "BIOCGDLTLIST: %s", pcap_strerror(errno)); - goto bad; } } -#endif +#endif /* various platforms */ +#endif /* BIOCGDLTLIST */ /* * If this is an Ethernet device, and we don't have a DLT_ list, @@ -818,7 +1406,7 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, * some other way of determining whether it's an Ethernet or 802.11 * device.) */ - if (p->linktype == DLT_EN10MB && p->dlt_count == 0) { + if (v == DLT_EN10MB && p->dlt_count == 0) { p->dlt_list = (u_int *) malloc(sizeof(u_int) * 2); /* * If that fails, just leave the list empty. @@ -829,6 +1417,13 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, p->dlt_count = 2; } } +#ifdef PCAP_FDDIPAD + if (v == DLT_FDDI) + p->fddipad = PCAP_FDDIPAD; + else + p->fddipad = 0; +#endif + p->linktype = v; #if defined(BIOCGHDRCMPLT) && defined(BIOCSHDRCMPLT) /* @@ -841,24 +1436,26 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, * BSDs - check CVS log for "bpf.c"? */ if (ioctl(fd, BIOCSHDRCMPLT, &spoof_eth_src) == -1) { - (void)snprintf(ebuf, PCAP_ERRBUF_SIZE, + (void)snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCSHDRCMPLT: %s", pcap_strerror(errno)); + err = PCAP_ERROR; goto bad; } #endif /* set timeout */ - if (to_ms != 0) { + if (p->md.timeout != 0) { /* * XXX - is this seconds/nanoseconds in AIX? * (Treating it as such doesn't fix the timeout * problem described below.) */ struct timeval to; - to.tv_sec = to_ms / 1000; - to.tv_usec = (to_ms * 1000) % 1000000; + to.tv_sec = p->md.timeout / 1000; + to.tv_usec = (p->md.timeout * 1000) % 1000000; if (ioctl(p->fd, BIOCSRTIMEOUT, (caddr_t)&to) < 0) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCSRTIMEOUT: %s", + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCSRTIMEOUT: %s", pcap_strerror(errno)); + err = PCAP_ERROR; goto bad; } } @@ -913,31 +1510,34 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, */ v = 1; if (ioctl(p->fd, BIOCIMMEDIATE, &v) < 0) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCIMMEDIATE: %s", + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCIMMEDIATE: %s", pcap_strerror(errno)); + err = PCAP_ERROR; goto bad; } #endif /* BIOCIMMEDIATE */ #endif /* _AIX */ - if (promisc) { + if (p->opt.promisc) { /* set promiscuous mode, okay if it fails */ if (ioctl(p->fd, BIOCPROMISC, NULL) < 0) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCPROMISC: %s", + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCPROMISC: %s", pcap_strerror(errno)); } } if (ioctl(fd, BIOCGBLEN, (caddr_t)&v) < 0) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCGBLEN: %s", + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCGBLEN: %s", pcap_strerror(errno)); + err = PCAP_ERROR; goto bad; } p->bufsize = v; p->buffer = (u_char *)malloc(p->bufsize); if (p->buffer == NULL) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s", + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "malloc: %s", pcap_strerror(errno)); + err = PCAP_ERROR; goto bad; } #ifdef _AIX @@ -958,13 +1558,14 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, total_insn.code = (u_short)(BPF_RET | BPF_K); total_insn.jt = 0; total_insn.jf = 0; - total_insn.k = snaplen; + total_insn.k = p->snapshot; 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", + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCSETF: %s", pcap_strerror(errno)); + err = PCAP_ERROR; goto bad; } @@ -1005,7 +1606,7 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, * XXX - what about AIX? */ p->selectable_fd = p->fd; /* assume select() works until we know otherwise */ - if (uname(&osinfo) == 0) { + if (have_osinfo) { /* * We can check what OS this is. */ @@ -1024,15 +1625,16 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, p->getnonblock_op = pcap_getnonblock_fd; p->setnonblock_op = pcap_setnonblock_fd; p->stats_op = pcap_stats_bpf; - p->close_op = pcap_close_common; + p->close_op = pcap_close_bpf; - return (p); + return (0); bad: (void)close(fd); - if (p->dlt_list != NULL) - free(p->dlt_list); - free(p); - return (NULL); + if (p->buffer != NULL) { + free(p->buffer); + p->buffer = NULL; + } + return (err); } int @@ -1046,6 +1648,303 @@ pcap_platform_finddevs(pcap_if_t **alldevsp, char *errbuf) return (0); } +#ifdef HAVE_BSD_IEEE80211 +static int +monitor_mode(pcap_t *p, int set) +{ + int sock; + struct ifmediareq req; + int *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)); + return (PCAP_ERROR); + } + + memset(&req, 0, sizeof req); + strncpy(req.ifm_name, p->opt.source, sizeof req.ifm_name); + + /* + * Find out how many media types we have. + */ + if (ioctl(sock, SIOCGIFMEDIA, &req) < 0) { + /* + * Can't get the media types. + */ + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "SIOCGIFMEDIA: %s", + pcap_strerror(errno)); + close(sock); + return (PCAP_ERROR); + } + if (req.ifm_count == 0) { + /* + * No media types. + */ + close(sock); + return (PCAP_ERROR_RFMON_NOTSUP); + } + + /* + * Allocate a buffer to hold all the media types, and + * get the media types. + */ + media_list = malloc(req.ifm_count * sizeof(int)); + if (media_list == NULL) { + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "malloc: %s", + pcap_strerror(errno)); + 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)); + free(media_list); + close(sock); + return (PCAP_ERROR); + } + + /* + * Look for an 802.11 "automatic" media type. + * We assume that all 802.11 adapters have that media type, + * and that it will carry the monitor mode supported flag. + */ + can_do = 0; + for (i = 0; i < req.ifm_count; i++) { + if (IFM_TYPE(media_list[i]) == IFM_IEEE80211 + && IFM_SUBTYPE(media_list[i]) == IFM_AUTO) { + /* OK, does it do monitor mode? */ + if (media_list[i] & IFM_IEEE80211_MONITOR) { + can_do = 1; + break; + } + } + } + free(media_list); + if (!can_do) { + /* + * This adapter doesn't support monitor mode. + */ + close(sock); + return (PCAP_ERROR_RFMON_NOTSUP); + } + + if (set) { + /* + * Don't just check whether we can enable monitor mode, + * do so, if it's not already enabled. + */ + if ((req.ifm_current & IFM_IEEE80211_MONITOR) == 0) { + /* + * Monitor mode isn't currently on, so turn it on, + * and remember that we should turn it off when the + * pcap_t is closed. + */ + + /* + * 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 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, + 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)); + close(sock); + return (PCAP_ERROR); + } + + p->md.must_clear |= MUST_CLEAR_RFMON; + + /* + * Add this to the list of pcaps to close when we exit. + */ + pcap_add_to_pcaps_to_close(p); + } + } + return (0); +} +#endif /* HAVE_BSD_IEEE80211 */ + +#if defined(BIOCGDLTLIST) && (defined(__APPLE__) || defined(HAVE_BSD_IEEE80211)) +/* + * Check whether we have any 802.11 link-layer types; return the best + * of the 802.11 link-layer types if we find one, and return -1 + * otherwise. + * + * DLT_IEEE802_11_RADIO, with the radiotap header, is considered the + * best 802.11 link-layer type; any of the other 802.11-plus-radio + * headers are second-best; 802.11 with no radio information is + * the least good. + */ +static int +find_802_11(struct bpf_dltlist *bdlp) +{ + int new_dlt; + int i; + + /* + * Scan the list of DLT_ values, looking for 802.11 values, + * and, if we find any, choose the best of them. + */ + new_dlt = -1; + for (i = 0; i < bdlp->bfl_len; i++) { + switch (bdlp->bfl_list[i]) { + + case DLT_IEEE802_11: + /* + * 802.11, but no radio. + * + * Offer this, and select it as the new mode + * unless we've already found an 802.11 + * header with radio information. + */ + if (new_dlt == -1) + new_dlt = bdlp->bfl_list[i]; + break; + + case DLT_PRISM_HEADER: + case DLT_AIRONET_HEADER: + case DLT_IEEE802_11_RADIO_AVS: + /* + * 802.11 with radio, but not radiotap. + * + * Offer this, and select it as the new mode + * unless we've already found the radiotap DLT_. + */ + if (new_dlt != DLT_IEEE802_11_RADIO) + new_dlt = bdlp->bfl_list[i]; + break; + + case DLT_IEEE802_11_RADIO: + /* + * 802.11 with radiotap. + * + * Offer this, and select it as the new mode. + */ + new_dlt = bdlp->bfl_list[i]; + break; + + default: + /* + * Not 802.11. + */ + break; + } + } + + return (new_dlt); +} +#endif /* defined(BIOCGDLTLIST) && (defined(__APPLE__) || defined(HAVE_BSD_IEEE80211)) */ + +#if defined(__APPLE__) && defined(BIOCGDLTLIST) +/* + * Remove DLT_EN10MB from the list of DLT_ values. + */ +static void +remove_en(pcap_t *p) +{ + int i, j; + + /* + * Scan the list of DLT_ values and discard DLT_EN10MB. + */ + j = 0; + for (i = 0; i < p->dlt_count; i++) { + switch (p->dlt_list[i]) { + + case DLT_EN10MB: + /* + * Don't offer this one. + */ + continue; + + default: + /* + * Just copy this mode over. + */ + break; + } + + /* + * Copy this DLT_ value to its new position. + */ + p->dlt_list[j] = p->dlt_list[i]; + j++; + } + + /* + * Set the DLT_ count to the number of entries we copied. + */ + p->dlt_count = j; +} + +/* + * Remove DLT_EN10MB from the list of DLT_ values, and look for the + * best 802.11 link-layer type in that list and return it. + * Radiotap is better than anything else; 802.11 with any other radio + * header is better than 802.11 with no radio header. + */ +static void +remove_802_11(pcap_t *p) +{ + int i, j; + + /* + * Scan the list of DLT_ values and discard 802.11 values. + */ + j = 0; + for (i = 0; i < p->dlt_count; i++) { + switch (p->dlt_list[i]) { + + case DLT_IEEE802_11: + case DLT_PRISM_HEADER: + case DLT_AIRONET_HEADER: + case DLT_IEEE802_11_RADIO: + case DLT_IEEE802_11_RADIO_AVS: + /* + * 802.11. Don't offer this one. + */ + continue; + + default: + /* + * Just copy this mode over. + */ + break; + } + + /* + * Copy this DLT_ value to its new position. + */ + p->dlt_list[j] = p->dlt_list[i]; + j++; + } + + /* + * Set the DLT_ count to the number of entries we copied. + */ + p->dlt_count = j; +} +#endif /* defined(__APPLE__) && defined(BIOCGDLTLIST) */ + static int pcap_setfilter_bpf(pcap_t *p, struct bpf_program *fp) { diff --git a/pcap-bt-linux.c b/pcap-bt-linux.c index 2ca0338..0cb6624 100644 --- a/pcap-bt-linux.c +++ b/pcap-bt-linux.c @@ -33,7 +33,7 @@ */ #ifndef lint static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/pcap-bt-linux.c,v 1.10 2008-02-14 23:27:42 guy Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/pcap-bt-linux.c,v 1.11 2008-04-04 19:37:45 guy Exp $ (LBL)"; #endif #ifdef HAVE_CONFIG_H @@ -64,6 +64,7 @@ static const char rcsid[] _U_ = #define BT_CTRL_SIZE 128 /* forward declaration */ +static int bt_activate(pcap_t *); static int bt_read_linux(pcap_t *, int , pcap_handler , u_char *); static int bt_inject_linux(pcap_t *, const void *, size_t); static int bt_setfilter_linux(pcap_t *, struct bpf_program *); @@ -134,39 +135,41 @@ done: return ret; } -pcap_t* -bt_open_live(const char* bus, int snaplen, int promisc , int to_ms, char* errmsg) +pcap_t * +bt_create(const char *device, char *ebuf) +{ + pcap_t *p; + + p = pcap_create_common(device, ebuf); + if (p == NULL) + return (NULL); + + p->activate_op = bt_activate; + return (p); +} + +static int +bt_activate(pcap_t* handle) { struct sockaddr_hci addr; int opt; - pcap_t *handle; int dev_id; struct hci_filter flt; - + /* get bt interface id */ - if (sscanf(bus, BT_IFACE"%d", &dev_id) != 1) + if (sscanf(handle->opt.source, BT_IFACE"%d", &dev_id) != 1) { - snprintf(errmsg, PCAP_ERRBUF_SIZE, - "Can't get Bluetooth bus index from %s", bus); - return NULL; + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + "Can't get Bluetooth device index from %s", + handle->opt.source); + return -1; } - - /* Allocate a handle for this session. */ - handle = malloc(sizeof(*handle)); - if (handle == NULL) { - snprintf(errmsg, PCAP_ERRBUF_SIZE, "malloc: %s", - pcap_strerror(errno)); - return NULL; - } - + /* Initialize some components of the pcap structure. */ - memset(handle, 0, sizeof(*handle)); - handle->snapshot = snaplen; - handle->md.timeout = to_ms; - handle->bufsize = snaplen+BT_CTRL_SIZE+sizeof(pcap_bluetooth_h4_header); + handle->bufsize = handle->snapshot+BT_CTRL_SIZE+sizeof(pcap_bluetooth_h4_header); handle->offset = BT_CTRL_SIZE; handle->linktype = DLT_BLUETOOTH_HCI_H4_WITH_PHDR; - + handle->read_op = bt_read_linux; handle->inject_op = bt_inject_linux; handle->setfilter_op = bt_setfilter_linux; @@ -181,46 +184,41 @@ bt_open_live(const char* bus, int snaplen, int promisc , int to_ms, char* errmsg /* Create HCI socket */ handle->fd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI); if (handle->fd < 0) { - snprintf(errmsg, PCAP_ERRBUF_SIZE, "Can't create raw socket %d:%s", + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Can't create raw socket %d:%s", errno, strerror(errno)); - free(handle); - return NULL; + return -1; } handle->buffer = malloc(handle->bufsize); if (!handle->buffer) { - snprintf(errmsg, PCAP_ERRBUF_SIZE, "Can't allocate dump buffer: %s", + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Can't allocate dump buffer: %s", pcap_strerror(errno)); - pcap_close(handle); - return NULL; + goto close_fail; } opt = 1; if (setsockopt(handle->fd, SOL_HCI, HCI_DATA_DIR, &opt, sizeof(opt)) < 0) { - snprintf(errmsg, PCAP_ERRBUF_SIZE, "Can't enable data direction info %d:%s", + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Can't enable data direction info %d:%s", errno, strerror(errno)); - pcap_close(handle); - return NULL; + goto close_fail; } opt = 1; if (setsockopt(handle->fd, SOL_HCI, HCI_TIME_STAMP, &opt, sizeof(opt)) < 0) { - snprintf(errmsg, PCAP_ERRBUF_SIZE, "Can't enable time stamp %d:%s", + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Can't enable time stamp %d:%s", errno, strerror(errno)); - pcap_close(handle); - return NULL; + goto close_fail; } - + /* Setup filter, do not call hci function to avoid dependence on * external libs */ memset(&flt, 0, sizeof(flt)); memset((void *) &flt.type_mask, 0xff, sizeof(flt.type_mask)); memset((void *) &flt.event_mask, 0xff, sizeof(flt.event_mask)); if (setsockopt(handle->fd, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) { - snprintf(errmsg, PCAP_ERRBUF_SIZE, "Can't set filter %d:%s", + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Can't set filter %d:%s", errno, strerror(errno)); - pcap_close(handle); - return NULL; + goto close_fail; } @@ -228,14 +226,16 @@ bt_open_live(const char* bus, int snaplen, int promisc , int to_ms, char* errmsg addr.hci_family = AF_BLUETOOTH; addr.hci_dev = handle->md.ifindex; if (bind(handle->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - snprintf(errmsg, PCAP_ERRBUF_SIZE, "Can't attach to device %d %d:%s", + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Can't attach to device %d %d:%s", handle->md.ifindex, errno, strerror(errno)); - pcap_close(handle); - return NULL; + goto close_fail; } - handle->selectable_fd = handle->fd; - - return handle; + handle->selectable_fd = handle->fd; + return 0; + +close_fail: + close(handle->fd); + return -1; } static int diff --git a/pcap-bt-linux.h b/pcap-bt-linux.h index cc8be7d..ed01190 100644 --- a/pcap-bt-linux.h +++ b/pcap-bt-linux.h @@ -30,11 +30,11 @@ * Bluetooth sniffing API implementation for Linux platform * By Paolo Abeni * - * @(#) $Header: /tcpdump/master/libpcap/pcap-bt-linux.h,v 1.4 2007-09-14 01:55:49 guy Exp $ (LBL) + * @(#) $Header: /tcpdump/master/libpcap/pcap-bt-linux.h,v 1.5 2008-04-04 19:37:45 guy Exp $ (LBL) */ /* * Prototypes for Bluetooth-related functions */ int bt_platform_finddevs(pcap_if_t **alldevsp, char *err_str); -pcap_t* bt_open_live(const char* bus, int snaplen, int promisc , int to_ms, char* errmsg); +pcap_t *bt_create(const char *device, char *ebuf); diff --git a/pcap-dag.c b/pcap-dag.c index 207f5f3..a3ee2a2 100644 --- a/pcap-dag.c +++ b/pcap-dag.c @@ -17,7 +17,7 @@ #ifndef lint static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/pcap-dag.c,v 1.36 2008-02-02 20:42:35 guy Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/pcap-dag.c,v 1.37 2008-04-04 19:37:45 guy Exp $ (LBL)"; #endif #ifdef HAVE_CONFIG_H @@ -87,7 +87,7 @@ static const unsigned short endian_test_word = 0x0100; /* This code is required when compiling for a DAG device only. */ /* Replace dag function names with pcap equivalent. */ -#define dag_open_live pcap_open_live +#define dag_create pcap_create #define dag_platform_finddevs pcap_platform_finddevs #endif /* DAG_ONLY */ @@ -564,21 +564,20 @@ dag_inject(pcap_t *p, const void *buf _U_, size_t size _U_) * not supported in hardware. * * snaplen is now also ignored, until we get per-stream slen support. Set - * slen with approprite DAG tool BEFORE pcap_open_live(). + * slen with approprite DAG tool BEFORE pcap_activate(). * * See also pcap(3). */ -pcap_t * -dag_open_live(const char *device, int snaplen, int promisc, int to_ms, char *ebuf) +static int dag_activate(pcap_t* handle) { #if 0 char conf[30]; /* dag configure string */ #endif - pcap_t *handle; char *s; int n; daginf_t* daginf; char * newDev = NULL; + char * device = handle->opt.source; #ifdef HAVE_DAG_STREAMS_API uint32_t mindata; struct timeval maxwait; @@ -586,44 +585,35 @@ dag_open_live(const char *device, int snaplen, int promisc, int to_ms, char *ebu #endif if (device == NULL) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "device is NULL: %s", pcap_strerror(errno)); - return NULL; + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "device is NULL: %s", pcap_strerror(errno)); + return -1; } - /* Allocate a handle for this session. */ - handle = malloc(sizeof(*handle)); - if (handle == NULL) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc %s: %s", device, pcap_strerror(errno)); - return NULL; - } - /* Initialize some components of the pcap structure. */ - - memset(handle, 0, sizeof(*handle)); #ifdef HAVE_DAG_STREAMS_API newDev = (char *)malloc(strlen(device) + 16); if (newDev == NULL) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "Can't allocate string for device name: %s\n", pcap_strerror(errno)); + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Can't allocate string for device name: %s\n", pcap_strerror(errno)); goto fail; } /* Parse input name to get dag device and stream number if provided */ if (dag_parse_name(device, newDev, strlen(device) + 16, &handle->md.dag_stream) < 0) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "dag_parse_name: %s\n", pcap_strerror(errno)); + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "dag_parse_name: %s\n", pcap_strerror(errno)); goto fail; } device = newDev; if (handle->md.dag_stream%2) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "dag_parse_name: tx (even numbered) streams not supported for capture\n"); + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "dag_parse_name: tx (even numbered) streams not supported for capture\n"); goto fail; } #else if (strncmp(device, "/dev/", 5) != 0) { newDev = (char *)malloc(strlen(device) + 5); if (newDev == NULL) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "Can't allocate string for device name: %s\n", pcap_strerror(errno)); + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Can't allocate string for device name: %s\n", pcap_strerror(errno)); goto fail; } strcpy(newDev, "/dev/"); @@ -634,14 +624,14 @@ dag_open_live(const char *device, int snaplen, int promisc, int to_ms, char *ebu /* setup device parameters */ if((handle->fd = dag_open((char *)device)) < 0) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "dag_open %s: %s", device, pcap_strerror(errno)); + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "dag_open %s: %s", device, pcap_strerror(errno)); goto fail; } #ifdef HAVE_DAG_STREAMS_API /* Open requested stream. Can fail if already locked or on error */ if (dag_attach_stream(handle->fd, handle->md.dag_stream, 0, 0) < 0) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "dag_attach_stream: %s\n", pcap_strerror(errno)); + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "dag_attach_stream: %s\n", pcap_strerror(errno)); goto failclose; } @@ -650,7 +640,7 @@ dag_open_live(const char *device, int snaplen, int promisc, int to_ms, char *ebu */ if (dag_get_stream_poll(handle->fd, handle->md.dag_stream, &mindata, &maxwait, &poll) < 0) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "dag_get_stream_poll: %s\n", pcap_strerror(errno)); + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "dag_get_stream_poll: %s\n", pcap_strerror(errno)); goto faildetach; } @@ -668,13 +658,13 @@ dag_open_live(const char *device, int snaplen, int promisc, int to_ms, char *ebu if (dag_set_stream_poll(handle->fd, handle->md.dag_stream, mindata, &maxwait, &poll) < 0) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "dag_set_stream_poll: %s\n", pcap_strerror(errno)); + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "dag_set_stream_poll: %s\n", pcap_strerror(errno)); goto faildetach; } #else if((handle->md.dag_mem_base = dag_mmap(handle->fd)) == MAP_FAILED) { - snprintf(ebuf, PCAP_ERRBUF_SIZE,"dag_mmap %s: %s\n", device, pcap_strerror(errno)); + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,"dag_mmap %s: %s\n", device, pcap_strerror(errno)); goto failclose; } @@ -688,28 +678,28 @@ dag_open_live(const char *device, int snaplen, int promisc, int to_ms, char *ebu /* set the card snap length to the specified snaplen parameter */ /* This is a really bad idea, as different cards have different * valid slen ranges. Should fix in Config API. */ - if (snaplen == 0 || snaplen > MAX_DAG_SNAPLEN) { - snaplen = MAX_DAG_SNAPLEN; + if (handle->snapshot == 0 || handle->snapshot > MAX_DAG_SNAPLEN) { + handle->snapshot = MAX_DAG_SNAPLEN; } else if (snaplen < MIN_DAG_SNAPLEN) { - snaplen = MIN_DAG_SNAPLEN; + handle->snapshot = MIN_DAG_SNAPLEN; } /* snap len has to be a multiple of 4 */ snprintf(conf, 30, "varlen slen=%d", (snaplen + 3) & ~3); if(dag_configure(handle->fd, conf) < 0) { - snprintf(ebuf, PCAP_ERRBUF_SIZE,"dag_configure %s: %s\n", device, pcap_strerror(errno)); + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,"dag_configure %s: %s\n", device, pcap_strerror(errno)); goto faildetach; } #endif #ifdef HAVE_DAG_STREAMS_API if(dag_start_stream(handle->fd, handle->md.dag_stream) < 0) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "dag_start_stream %s: %s\n", device, pcap_strerror(errno)); + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "dag_start_stream %s: %s\n", device, pcap_strerror(errno)); goto faildetach; } #else if(dag_start(handle->fd) < 0) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "dag_start %s: %s\n", device, pcap_strerror(errno)); + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "dag_start %s: %s\n", device, pcap_strerror(errno)); goto failclose; } #endif /* HAVE_DAG_STREAMS_API */ @@ -745,7 +735,7 @@ dag_open_live(const char *device, int snaplen, int promisc, int to_ms, char *ebu handle->md.dag_fcs_bits = n; } else { snprintf(ebuf, PCAP_ERRBUF_SIZE, - "pcap_open_live %s: bad ERF_FCS_BITS value (%d) in environment\n", device, n); + "pcap_activate %s: bad ERF_FCS_BITS value (%d) in environment\n", device, n); goto failstop; } } @@ -763,19 +753,16 @@ dag_open_live(const char *device, int snaplen, int promisc, int to_ms, char *ebu } } - handle->snapshot = snaplen; - handle->md.dag_timeout = to_ms; + handle->md.dag_timeout = handle->md.timeout; handle->linktype = -1; - if (dag_get_datalink(handle) < 0) { - strcpy(ebuf, handle->errbuf); + if (dag_get_datalink(handle) < 0) goto failstop; - } handle->bufsize = 0; if (new_pcap_dag(handle) < 0) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "new_pcap_dag %s: %s\n", device, pcap_strerror(errno)); + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "new_pcap_dag %s: %s\n", device, pcap_strerror(errno)); goto failstop; } @@ -799,51 +786,46 @@ dag_open_live(const char *device, int snaplen, int promisc, int to_ms, char *ebu handle->close_op = dag_platform_close; handle->md.stat.ps_drop = 0; handle->md.stat.ps_recv = 0; - return handle; + return 0; #ifdef HAVE_DAG_STREAMS_API failstop: - if (handle != NULL) { - if (dag_stop_stream(handle->fd, handle->md.dag_stream) < 0) - fprintf(stderr,"dag_stop_stream: %s\n", strerror(errno)); + if (dag_stop_stream(handle->fd, handle->md.dag_stream) < 0) + fprintf(stderr,"dag_stop_stream: %s\n", strerror(errno)); } faildetach: - if (handle != NULL) { - if (dag_detach_stream(handle->fd, handle->md.dag_stream) < 0) - fprintf(stderr,"dag_detach_stream: %s\n", strerror(errno)); - } -#else + if (dag_detach_stream(handle->fd, handle->md.dag_stream) < 0) + fprintf(stderr,"dag_detach_stream: %s\n", strerror(errno)); +#else failstop: - if (handle != NULL) { - if (dag_stop(handle->fd) < 0) - fprintf(stderr,"dag_stop: %s\n", strerror(errno)); - } + if (dag_stop(handle->fd) < 0) + fprintf(stderr,"dag_stop: %s\n", strerror(errno)); #endif /* HAVE_DAG_STREAMS_API */ failclose: - if (handle != NULL) { - if (dag_close(handle->fd) < 0) - fprintf(stderr,"dag_close: %s\n", strerror(errno)); - } - if (handle != NULL) - delete_pcap_dag(handle); - + if (dag_close(handle->fd) < 0) + fprintf(stderr,"dag_close: %s\n", strerror(errno)); + delete_pcap_dag(handle); + fail: if (newDev != NULL) { free((char *)newDev); } - if (handle != NULL) { - /* - * Get rid of any link-layer type list we allocated. - */ - if (handle->dlt_list != NULL) { - free(handle->dlt_list); - } - free(handle); - } - return NULL; + return PCAP_ERROR; +} + +pcap_t *dag_create(const char *device, char *ebuf) +{ + pcap_t *p; + + p = pcap_create_common(device, ebuf); + if (p == NULL) + return NULL; + + p->activate_op = dag_activate; + return p; } static int diff --git a/pcap-dag.h b/pcap-dag.h index b8659f2..635eee4 100644 --- a/pcap-dag.h +++ b/pcap-dag.h @@ -7,10 +7,10 @@ * * Author: Richard Littin, Sean Irvine ({richard,sean}@reeltwo.com) * - * @(#) $Header: /tcpdump/master/libpcap/pcap-dag.h,v 1.6 2007-11-09 00:55:53 guy Exp $ (LBL) + * @(#) $Header: /tcpdump/master/libpcap/pcap-dag.h,v 1.7 2008-04-04 19:37:45 guy Exp $ (LBL) */ -pcap_t *dag_open_live(const char *device, int snaplen, int promisc, int to_ms, char *ebuf); +pcap_t *dag_create(const char *, char *); int dag_platform_finddevs(pcap_if_t **devlistp, char *errbuf); #ifndef TYPE_AAL5 diff --git a/pcap-dlpi.c b/pcap-dlpi.c index 21b0097..7537594 100644 --- a/pcap-dlpi.c +++ b/pcap-dlpi.c @@ -70,7 +70,7 @@ #ifndef lint static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/pcap-dlpi.c,v 1.120 2008-03-13 18:13:57 guy Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/pcap-dlpi.c,v 1.121 2008-04-04 19:37:45 guy Exp $ (LBL)"; #endif #ifdef HAVE_CONFIG_H @@ -321,12 +321,10 @@ pcap_close_dlpi(pcap_t *p) close(p->send_fd); } -pcap_t * -pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, - char *ebuf) +static int +pcap_activate_dlpi(pcap_t *p) { register char *cp; - register pcap_t *p; int ppa; #ifdef HAVE_SOLARIS int isatm = 0; @@ -345,12 +343,13 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, char dname2[100]; #endif - p = (pcap_t *)malloc(sizeof(*p)); - if (p == NULL) { - strlcpy(ebuf, pcap_strerror(errno), PCAP_ERRBUF_SIZE); - return (NULL); + if (p->opt.rfmon) { + /* + * No monitor mode on any platforms that support DLPI. + */ + return (PCAP_ERROR_RFMON_NOTSUP); } - memset(p, 0, sizeof(*p)); + p->fd = -1; /* indicate that it hasn't been opened yet */ p->send_fd = -1; @@ -358,9 +357,9 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, /* ** Remove any "/dev/" on the front of the device. */ - cp = strrchr(device, '/'); + cp = strrchr(p->opt.source, '/'); if (cp == NULL) - strlcpy(dname, device, sizeof(dname)); + strlcpy(dname, p->opt.source, sizeof(dname)); else strlcpy(dname, cp + 1, sizeof(dname)); @@ -368,7 +367,7 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, * Split the device name into a device type name and a unit number; * chop off the unit number, so "dname" is just a device type name. */ - cp = split_dname(dname, &ppa, ebuf); + cp = split_dname(dname, &ppa, p->errbuf); if (cp == NULL) goto bad; *cp = '\0'; @@ -386,7 +385,7 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, */ cp = "/dev/dlpi"; if ((p->fd = open(cp, O_RDWR)) < 0) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "%s: %s", cp, pcap_strerror(errno)); goto bad; } @@ -410,7 +409,7 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, * Get a table of all PPAs for that device, and search that * table for the specified device type name and unit number. */ - ppa = get_dlpi_ppa(p->fd, dname, ppa, ebuf); + ppa = get_dlpi_ppa(p->fd, dname, ppa, p->errbuf); if (ppa < 0) goto bad; #else @@ -420,17 +419,17 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, * otherwise, concatenate the device directory name and the * device name. */ - if (*device == '/') - strlcpy(dname, device, sizeof(dname)); + if (*p->opt.source == '/') + strlcpy(dname, p->opt.source, sizeof(dname)); else snprintf(dname, sizeof(dname), "%s/%s", PCAP_DEV_PREFIX, - device); + p->opt.source); /* * Get the unit number, and a pointer to the end of the device * type name. */ - cp = split_dname(dname, &ppa, ebuf); + cp = split_dname(dname, &ppa, p->errbuf); if (cp == NULL) goto bad; @@ -444,7 +443,7 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, /* Try device without unit number */ if ((p->fd = open(dname, O_RDWR)) < 0) { if (errno != ENOENT) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "%s: %s", dname, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "%s: %s", dname, pcap_strerror(errno)); goto bad; } @@ -467,10 +466,10 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, * for the loopback interface is just a * symptom of that inability. */ - snprintf(ebuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "%s: No DLPI device found", device); } else { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "%s: %s", + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "%s: %s", dname2, pcap_strerror(errno)); } goto bad; @@ -480,13 +479,11 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, } #endif - p->snapshot = snaplen; - /* ** Attach if "style 2" provider */ - if (dlinforeq(p->fd, ebuf) < 0 || - dlinfoack(p->fd, (char *)buf, ebuf) < 0) + if (dlinforeq(p->fd, p->errbuf) < 0 || + dlinfoack(p->fd, (char *)buf, p->errbuf) < 0) goto bad; infop = &((union DL_primitives *)buf)->info_ack; #ifdef HAVE_SOLARIS @@ -494,11 +491,11 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, isatm = 1; #endif if (infop->dl_provider_style == DL_STYLE2) { - if (dl_doattach(p->fd, ppa, ebuf) < 0) + if (dl_doattach(p->fd, ppa, p->errbuf) < 0) goto bad; #ifdef DL_HP_RAWDLS if (p->send_fd >= 0) { - if (dl_doattach(p->send_fd, ppa, ebuf) < 0) + if (dl_doattach(p->send_fd, ppa, p->errbuf) < 0) goto bad; } #endif @@ -535,15 +532,15 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, ** assume the SAP value in a DLPI bind is an LLC SAP for network ** types that use 802.2 LLC). */ - if ((dlbindreq(p->fd, 1537, ebuf) < 0 && - dlbindreq(p->fd, 2, ebuf) < 0) || - dlbindack(p->fd, (char *)buf, ebuf, NULL) < 0) + if ((dlbindreq(p->fd, 1537, p->errbuf) < 0 && + dlbindreq(p->fd, 2, p->errbuf) < 0) || + dlbindack(p->fd, (char *)buf, p->errbuf, NULL) < 0) goto bad; #elif defined(DL_HP_RAWDLS) /* ** HP-UX 10.0x and 10.1x. */ - if (dl_dohpuxbind(p->fd, ebuf) < 0) + if (dl_dohpuxbind(p->fd, p->errbuf) < 0) goto bad; if (p->send_fd >= 0) { /* @@ -551,7 +548,7 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, ** set it to -1, so that you can't send but can ** still receive? */ - if (dl_dohpuxbind(p->send_fd, ebuf) < 0) + if (dl_dohpuxbind(p->send_fd, p->errbuf) < 0) goto bad; } #else /* neither AIX nor HP-UX */ @@ -559,8 +556,8 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, ** Not Sinix, and neither AIX nor HP-UX - Solaris, and any other ** OS using DLPI. **/ - if (dlbindreq(p->fd, 0, ebuf) < 0 || - dlbindack(p->fd, (char *)buf, ebuf, NULL) < 0) + if (dlbindreq(p->fd, 0, p->errbuf) < 0 || + dlbindack(p->fd, (char *)buf, p->errbuf, NULL) < 0) goto bad; #endif /* AIX vs. HP-UX vs. other */ #endif /* !HP-UX 9 and !HP-UX 10.20 or later and !SINIX */ @@ -574,18 +571,18 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, ** help, and may break things. */ if (strioctl(p->fd, A_PROMISCON_REQ, 0, NULL) < 0) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "A_PROMISCON_REQ: %s", - pcap_strerror(errno)); + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "A_PROMISCON_REQ: %s", pcap_strerror(errno)); goto bad; } } else #endif - if (promisc) { + if (p->opt.promisc) { /* ** Enable promiscuous (not necessary on send FD) */ - if (dlpromisconreq(p->fd, DL_PROMISC_PHYS, ebuf) < 0 || - dlokack(p->fd, "promisc_phys", (char *)buf, ebuf) < 0) + if (dlpromisconreq(p->fd, DL_PROMISC_PHYS, p->errbuf) < 0 || + dlokack(p->fd, "promisc_phys", (char *)buf, p->errbuf) < 0) goto bad; /* @@ -594,10 +591,11 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, ** HP-UX or SINIX) (Not necessary on send FD) */ #if !defined(__hpux) && !defined(sinix) - if (dlpromisconreq(p->fd, DL_PROMISC_MULTI, ebuf) < 0 || - dlokack(p->fd, "promisc_multi", (char *)buf, ebuf) < 0) + if (dlpromisconreq(p->fd, DL_PROMISC_MULTI, p->errbuf) < 0 || + dlokack(p->fd, "promisc_multi", (char *)buf, p->errbuf) < 0) fprintf(stderr, - "WARNING: DL_PROMISC_MULTI failed (%s)\n", ebuf); + "WARNING: DL_PROMISC_MULTI failed (%s)\n", + p->errbuf); #endif } /* @@ -608,17 +606,17 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, #ifndef sinix if ( #ifdef __hpux - !promisc && + !p->opt.promisc && #endif #ifdef HAVE_SOLARIS !isatm && #endif - (dlpromisconreq(p->fd, DL_PROMISC_SAP, ebuf) < 0 || - dlokack(p->fd, "promisc_sap", (char *)buf, ebuf) < 0)) { + (dlpromisconreq(p->fd, DL_PROMISC_SAP, p->errbuf) < 0 || + dlokack(p->fd, "promisc_sap", (char *)buf, p->errbuf) < 0)) { /* Not fatal if promisc since the DL_PROMISC_PHYS worked */ - if (promisc) + if (p->opt.promisc) fprintf(stderr, - "WARNING: DL_PROMISC_SAP failed (%s)\n", ebuf); + "WARNING: DL_PROMISC_SAP failed (%s)\n", p->errbuf); else goto bad; } @@ -629,7 +627,7 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, ** promiscuous options. */ #if defined(HAVE_HPUX9) || defined(HAVE_HPUX10_20_OR_LATER) - if (dl_dohpuxbind(p->fd, ebuf) < 0) + if (dl_dohpuxbind(p->fd, p->errbuf) < 0) goto bad; /* ** We don't set promiscuous mode on the send FD, but we'll defer @@ -642,7 +640,7 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, ** set it to -1, so that you can't send but can ** still receive? */ - if (dl_dohpuxbind(p->send_fd, ebuf) < 0) + if (dl_dohpuxbind(p->send_fd, p->errbuf) < 0) goto bad; } #endif @@ -652,12 +650,12 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, ** XXX - get SAP length and address length as well, for use ** when sending packets. */ - if (dlinforeq(p->fd, ebuf) < 0 || - dlinfoack(p->fd, (char *)buf, ebuf) < 0) + if (dlinforeq(p->fd, p->errbuf) < 0 || + dlinfoack(p->fd, (char *)buf, p->errbuf) < 0) goto bad; infop = &((union DL_primitives *)buf)->info_ack; - if (pcap_process_mactype(p, infop->dl_mac_type, ebuf) != 0) + if (pcap_process_mactype(p, infop->dl_mac_type, p->errbuf) != 0) goto bad; #ifdef DLIOCRAW @@ -666,13 +664,13 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, ** header. */ if (strioctl(p->fd, DLIOCRAW, 0, NULL) < 0) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "DLIOCRAW: %s", + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "DLIOCRAW: %s", pcap_strerror(errno)); goto bad; } #endif - ss = snaplen; + ss = p->snapshot; /* ** There is a bug in bufmod(7). When dealing with messages of @@ -695,7 +693,7 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, #ifdef HAVE_SYS_BUFMOD_H /* Push and configure bufmod. */ - if (pcap_conf_bufmod(p, ss, to_ms, ebuf) != 0) + if (pcap_conf_bufmod(p, ss, p->md.timeout) != 0) goto bad; #endif @@ -703,13 +701,13 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, ** As the last operation flush the read side. */ if (ioctl(p->fd, I_FLUSH, FLUSHR) != 0) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "FLUSHR: %s", + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "FLUSHR: %s", pcap_strerror(errno)); goto bad; } /* Allocate data buffer. */ - if (pcap_alloc_databuf(p, ebuf) != 0) + if (pcap_alloc_databuf(p) != 0) goto bad; /* @@ -728,19 +726,13 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, p->stats_op = pcap_stats_dlpi; p->close_op = pcap_close_dlpi; - return (p); + return (0); bad: if (p->fd >= 0) close(p->fd); if (p->send_fd >= 0) close(p->send_fd); - /* - * Get rid of any link-layer type list we allocated. - */ - if (p->dlt_list != NULL) - free(p->dlt_list); - free(p); - return (NULL); + return (PCAP_ERROR); } /* @@ -1649,3 +1641,16 @@ dlpi_kread(register int fd, register off_t addr, return (cc); } #endif + +pcap_t * +pcap_create(const char *device, char *ebuf) +{ + pcap_t *p; + + p = pcap_create_common(device, ebuf); + if (p == NULL) + return (NULL); + + p->activate_op = pcap_activate_dlpi; + return (p); +} diff --git a/pcap-dos.c b/pcap-dos.c index 426a4fd..391f8cd 100644 --- a/pcap-dos.c +++ b/pcap-dos.c @@ -5,7 +5,7 @@ * pcap-dos.c: Interface to PKTDRVR, NDIS2 and 32-bit pmode * network drivers. * - * @(#) $Header: /tcpdump/master/libpcap/pcap-dos.c,v 1.3 2007-12-05 23:37:26 guy Exp $ (LBL) + * @(#) $Header: /tcpdump/master/libpcap/pcap-dos.c,v 1.4 2008-04-04 19:37:45 guy Exp $ (LBL) */ #include @@ -97,6 +97,7 @@ static volatile BOOL exc_occured = 0; static struct device *handle_to_device [20]; +static void pcap_activate_dos (pcap_t *p); static int pcap_read_dos (pcap_t *p, int cnt, pcap_handler callback, u_char *data); static void pcap_close_dos (pcap_t *p); @@ -142,59 +143,68 @@ static struct device *get_device (int fd) return handle_to_device [fd-1]; } +pcap_t *pcap_create (const char *device, char *ebuf) +{ + pcap_t *p; + + p = pcap_create_common(device, ebuf); + if (p == NULL) + return (NULL); + + p->activate_op = pcap_activate_dos; + return (p); +} + /* * Open MAC-driver with name 'device_name' for live capture of * network packets. */ -pcap_t *pcap_open_live (const char *device_name, int snaplen, int promisc, - int timeout_ms, char *errbuf) +static int pcap_activate_dos (pcap_t *pcap) { - struct pcap *pcap; + int err = 0; - if (snaplen < ETH_MIN) - snaplen = ETH_MIN; - - if (snaplen > ETH_MAX) /* silently accept and truncate large MTUs */ - snaplen = ETH_MAX; - - pcap = calloc (sizeof(*pcap), 1); - if (!pcap) - { - strcpy (errbuf, "Not enough memory (pcap)"); - return (NULL); + if (p->opt.rfmon) { + /* + * No monitor mode on DOS. + */ + return (PCAP_ERROR_RFMON_NOTSUP); } - pcap->snapshot = max (ETH_MIN+8, snaplen); + if (pcap->snapshot < ETH_MIN+8) + pcap->snapshot = ETH_MIN+8; + + if (pcap->snapshot > ETH_MAX) /* silently accept and truncate large MTUs */ + pcap->snapshot = ETH_MAX; + pcap->linktype = DLT_EN10MB; /* !! */ - pcap->inter_packet_wait = timeout_ms; pcap->close_op = pcap_close_dos; pcap->read_op = pcap_read_dos; pcap->stats_op = pcap_stats_dos; pcap->inject_op = pcap_sendpacket_dos; pcap->setfilter_op = pcap_setfilter_dos; - pcap->setdirection_op = NULL; /* Not implemented.*/ + pcap->setdirection_op = NULL; /* Not implemented.*/ pcap->fd = ++ref_count; if (pcap->fd == 1) /* first time we're called */ { - if (!init_watt32(pcap, device_name, errbuf) || - !first_init(device_name, errbuf, promisc)) + if (!init_watt32(pcap, pcap->md.device, pcap->errbuf) || + !first_init(pcap->md.device, pcap->errbuf, pcap->opt.promisc)) { free (pcap); - return (NULL); + return (PCAP_ERROR); } atexit (close_driver); } - else if (stricmp(active_dev->name,device_name)) + else if (stricmp(active_dev->name,pcap->md.device)) { - snprintf (errbuf, PCAP_ERRBUF_SIZE, + snprintf (pcap->errbuf, PCAP_ERRBUF_SIZE, "Cannot use different devices simultaneously " - "(`%s' vs. `%s')", active_dev->name, device_name); + "(`%s' vs. `%s')", active_dev->name, pcap->md.device); free (pcap); - pcap = NULL; + err = PCAP_ERROR; } handle_to_device [pcap->fd-1] = active_dev; - return (pcap); + return (err); } /* @@ -209,10 +219,10 @@ pcap_read_one (pcap_t *p, pcap_handler callback, u_char *data) BYTE *rx_buf; int rx_len = 0; - if (p->inter_packet_wait > 0) + if (p->md.timeout > 0) { gettimeofday2 (&now, NULL); - expiry.tv_usec = now.tv_usec + 1000UL * p->inter_packet_wait; + expiry.tv_usec = now.tv_usec + 1000UL * p->md.timeout; expiry.tv_sec = now.tv_sec; while (expiry.tv_usec >= 1000000L) { @@ -284,7 +294,7 @@ pcap_read_one (pcap_t *p, pcap_handler callback, u_char *data) /* If not to wait for a packet or pcap_close() called from * e.g. SIGINT handler, exit loop now. */ - if (p->inter_packet_wait <= 0 || (volatile int)p->fd <= 0) + if (p->md.timeout <= 0 || (volatile int)p->fd <= 0) break; gettimeofday2 (&now, NULL); @@ -476,7 +486,7 @@ int pcap_lookupnet (const char *device, bpf_u_int32 *localnet, { if (!_watt_is_init) { - strcpy (errbuf, "pcap_open_offline() or pcap_open_live() must be " + strcpy (errbuf, "pcap_open_offline() or pcap_activate() must be " "called first"); return (-1); } @@ -587,7 +597,7 @@ void pcap_set_wait (pcap_t *p, void (*yield)(void), int wait) if (p) { p->wait_proc = yield; - p->inter_packet_wait = wait; + p->md.timeout = wait; } } @@ -739,7 +749,7 @@ static void exc_handler (int sig) /* - * Open the pcap device for the first client calling pcap_open_live() + * Open the pcap device for the first client calling pcap_activate() */ static int first_init (const char *name, char *ebuf, int promisc) { diff --git a/pcap-int.h b/pcap-int.h index d973d52..463421f 100644 --- a/pcap-int.h +++ b/pcap-int.h @@ -30,7 +30,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#) $Header: /tcpdump/master/libpcap/pcap-int.h,v 1.88 2008-03-13 18:13:57 guy Exp $ (LBL) + * @(#) $Header: /tcpdump/master/libpcap/pcap-int.h,v 1.89 2008-04-04 19:37:45 guy Exp $ (LBL) */ #ifndef pcap_int_h @@ -96,6 +96,9 @@ typedef enum { MAYBE_SWAPPED } swapped_type_t; +/* + * Used when reading a savefile. + */ struct pcap_sf { FILE *rfile; int swapped; @@ -106,6 +109,9 @@ struct pcap_sf { u_char *base; }; +/* + * Used when doing a live capture. + */ struct pcap_md { struct pcap_stat stat; /*XXX*/ @@ -116,18 +122,17 @@ struct pcap_md { long TotMissed; /* missed by i/f during this run */ long OrigMissed; /* missed by i/f before this run */ char *device; /* device name */ + int timeout; /* timeout for buffering */ + int must_clear; /* stuff we must clear when we close */ + struct pcap *next; /* list of open pcaps that need stuff cleared on close */ #ifdef linux int sock_packet; /* using Linux 2.0 compatible interface */ int cooked; /* using SOCK_DGRAM rather than SOCK_RAW */ int ifindex; /* interface index of device we're bound to */ int lo_ifindex; /* interface index of the loopback device */ - struct pcap *next; /* list of open promiscuous sock_packet pcaps */ u_int packets_read; /* count of packets read with recvfrom() */ + bpf_u_int32 oldmode; /* mode to restore when turning monitor mode off */ #endif /* linux */ -#if defined(linux) || defined(SITA) - int timeout; /* timeout specified to pcap_open_live */ - int clear_promisc; /* must clear promiscuous mode when we close */ -#endif /* linux || SITA */ #ifdef HAVE_DAG_API #ifdef HAVE_DAG_STREAMS_API @@ -147,6 +152,19 @@ struct pcap_md { #endif /* HAVE_DAG_API */ }; +/* + * Stuff to clear when we close. + */ +#define MUST_CLEAR_PROMISC 0x00000001 /* promiscuous mode */ +#define MUST_CLEAR_RFMON 0x00000002 /* rfmon (monitor) mode */ + +struct pcap_opt { + int buffer_size; + char *source; + int promisc; + int rfmon; +}; + /* * Ultrix, DEC OSF/1^H^H^H^H^H^H^H^H^HDigital UNIX^H^H^H^H^H^H^H^H^H^H^H^H * Tru64 UNIX, and some versions of NetBSD pad FDDI packets to make everything @@ -160,11 +178,27 @@ struct pcap_md { #define PCAP_FDDIPAD 3 #endif +typedef int (*activate_op_t)(pcap_t *); +typedef int (*can_set_rfmon_op_t)(pcap_t *); +typedef int (*read_op_t)(pcap_t *, int cnt, pcap_handler, u_char *); +typedef int (*inject_op_t)(pcap_t *, const void *, size_t); +typedef int (*setfilter_op_t)(pcap_t *, struct bpf_program *); +typedef int (*setdirection_op_t)(pcap_t *, pcap_direction_t); +typedef int (*set_datalink_op_t)(pcap_t *, int); +typedef int (*getnonblock_op_t)(pcap_t *, char *); +typedef int (*setnonblock_op_t)(pcap_t *, int, char *); +typedef int (*stats_op_t)(pcap_t *, struct pcap_stat *); +#ifdef WIN32 +typedef int (*setbuff_op_t)(pcap_t *, int); +typedef int (*setmode_op_t)(pcap_t *, int); +typedef int (*setmintocopy_op_t)(pcap_t *, int); +#endif +typedef void (*close_op_t)(pcap_t *); + struct pcap { #ifdef WIN32 ADAPTER *adapter; LPPACKET Packet; - int timeout; int nonblock; #else int fd; @@ -180,6 +214,8 @@ struct pcap { int linktype_ext; /* Extended information stored in the linktype field of a file */ int tzoff; /* timezone offset */ int offset; /* offset for proper alignment */ + int activated; /* true if the capture is really started */ + int oldstyle; /* if we're opening with pcap_open_live() */ int break_loop; /* flag set to force break from packet-reading loop */ @@ -188,12 +224,12 @@ struct pcap { #endif #ifdef MSDOS - int inter_packet_wait; /* offline: wait between packets */ void (*wait_proc)(void); /* call proc while waiting */ #endif struct pcap_sf sf; struct pcap_md md; + struct pcap_opt opt; /* * Read buffer. @@ -214,30 +250,27 @@ struct pcap { /* * Methods. */ - int (*read_op)(pcap_t *, int cnt, pcap_handler, u_char *); - int (*inject_op)(pcap_t *, const void *, size_t); - int (*setfilter_op)(pcap_t *, struct bpf_program *); - int (*setdirection_op)(pcap_t *, pcap_direction_t); - int (*set_datalink_op)(pcap_t *, int); - int (*getnonblock_op)(pcap_t *, char *); - int (*setnonblock_op)(pcap_t *, int, char *); - int (*stats_op)(pcap_t *, struct pcap_stat *); -#ifdef WIN32 - /* - * Win32-only; given the way the buffer size is set with BPF, - * to make this cross-platform we'll have to set the buffer - * size at open time. - */ - int (*setbuff_op)(pcap_t *, int); + activate_op_t activate_op; + can_set_rfmon_op_t can_set_rfmon_op; + read_op_t read_op; + inject_op_t inject_op; + setfilter_op_t setfilter_op; + setdirection_op_t setdirection_op; + set_datalink_op_t set_datalink_op; + getnonblock_op_t getnonblock_op; + setnonblock_op_t setnonblock_op; + stats_op_t stats_op; +#ifdef WIN32 /* * These are, at least currently, specific to the Win32 NPF * driver. */ - int (*setmode_op)(pcap_t *, int); - int (*setmintocopy_op)(pcap_t *, int); + setbuff_op_t setbuff_op; + setmode_op_t setmode_op; + setmintocopy_op_t setmintocopy_op; #endif - void (*close_op)(pcap_t *); + close_op_t close_op; /* * Placeholder for filter code if bpf not in kernel. @@ -354,7 +387,13 @@ int pcap_getnonblock_fd(pcap_t *, char *); int pcap_setnonblock_fd(pcap_t *p, int, char *); #endif +pcap_t *pcap_create_common(const char *, char *); +int pcap_do_addexit(pcap_t *); +void pcap_add_to_pcaps_to_close(pcap_t *); +void pcap_remove_from_pcaps_to_close(pcap_t *); void pcap_close_common(pcap_t *); +int pcap_not_initialized(pcap_t *); +int pcap_check_activated(pcap_t *); /* * Internal interfaces for "pcap_findalldevs()". diff --git a/pcap-libdlpi.c b/pcap-libdlpi.c index a40281f..d1df5ba 100644 --- a/pcap-libdlpi.c +++ b/pcap-libdlpi.c @@ -26,7 +26,7 @@ #ifndef lint static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/pcap-libdlpi.c,v 1.3 2008-03-15 04:26:14 guy Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/pcap-libdlpi.c,v 1.4 2008-04-04 19:37:45 guy Exp $ (LBL)"; #endif #ifdef HAVE_CONFIG_H @@ -96,57 +96,54 @@ list_interfaces(const char *linkname, void *arg) return (B_FALSE); } -pcap_t * -pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, - char *ebuf) +static int +pcap_activate_libdlpi(pcap_t *p) { int retv; - pcap_t *p; dlpi_handle_t dh; dlpi_info_t dlinfo; - if ((p = (pcap_t *)malloc(sizeof(*p))) == NULL) { - strlcpy(ebuf, pcap_strerror(errno), PCAP_ERRBUF_SIZE); - return (NULL); + if (p->opt.rfmon) { + /* + * No monitor mode on any platforms that support DLPI. + */ + return (PCAP_ERROR_RFMON_NOTSUP); } - memset(p, 0, sizeof(*p)); + p->fd = -1; /* indicate that it hasn't been opened yet */ - p->send_fd = -1; /* * Enable Solaris raw and passive DLPI extensions; * dlpi_open() will not fail if the underlying link does not support * passive mode. See dlpi(7P) for details. */ - retv = dlpi_open(device, &dh, DLPI_RAW|DLPI_PASSIVE); + retv = dlpi_open(p->opt.source, &dh, DLPI_RAW|DLPI_PASSIVE); if (retv != DLPI_SUCCESS) { - pcap_libdlpi_err(device, "dlpi_open", retv, ebuf); + pcap_libdlpi_err(p->opt.source, "dlpi_open", retv, p->errbuf); goto bad; } p->dlpi_hd = dh; - p->snapshot = snaplen; - /* Bind with DLPI_ANY_SAP. */ if ((retv = dlpi_bind(p->dlpi_hd, DLPI_ANY_SAP, 0)) != DLPI_SUCCESS) { - pcap_libdlpi_err(device, "dlpi_bind", retv, ebuf); + pcap_libdlpi_err(p->opt.source, "dlpi_bind", retv, p->errbuf); goto bad; } /* Enable promiscuous mode. */ - if (promisc) { + if (p->opt.promisc) { retv = dlpi_promiscon(p->dlpi_hd, DL_PROMISC_PHYS); if (retv != DLPI_SUCCESS) { - pcap_libdlpi_err(device, "dlpi_promisc(PHYSICAL)", - retv, ebuf); + pcap_libdlpi_err(p->opt.source, + "dlpi_promisc(PHYSICAL)", retv, p->errbuf); goto bad; } } else { /* Try to enable multicast. */ retv = dlpi_promiscon(p->dlpi_hd, DL_PROMISC_MULTI); if (retv != DLPI_SUCCESS) { - pcap_libdlpi_err(device, "dlpi_promisc(MULTI)", - retv, ebuf); + pcap_libdlpi_err(p->opt.source, "dlpi_promisc(MULTI)", + retv, p->errbuf); goto bad; } } @@ -154,42 +151,42 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, /* Try to enable SAP promiscuity. */ if ((retv = dlpi_promiscon(p->dlpi_hd, DL_PROMISC_SAP)) != DLPI_SUCCESS) { if (!promisc) { - pcap_libdlpi_err(device, "dlpi_promisc(SAP)", - retv, ebuf); + pcap_libdlpi_err(p->opt.source, "dlpi_promisc(SAP)", + retv, p->errbuf); goto bad; } /* Not fatal, since the DL_PROMISC_PHYS mode worked. */ fprintf(stderr, "WARNING: dlpi_promisc(SAP) failed on" - " %s:(%s)\n", device, dlpi_strerror(retv)); + " %s:(%s)\n", p->opt.source, dlpi_strerror(retv)); } /* Determine link type. */ if ((retv = dlpi_info(p->dlpi_hd, &dlinfo, 0)) != DLPI_SUCCESS) { - pcap_libdlpi_err(device, "dlpi_info", retv, ebuf); + pcap_libdlpi_err(p->opt.source, "dlpi_info", retv, p->errbuf); goto bad; } - if (pcap_process_mactype(p, dlinfo.di_mactype, ebuf) != 0) + if (pcap_process_mactype(p, dlinfo.di_mactype) != 0) goto bad; p->fd = dlpi_fd(p->dlpi_hd); /* Push and configure bufmod. */ - if (pcap_conf_bufmod(p, snaplen, to_ms, ebuf) != 0) + if (pcap_conf_bufmod(p, snaplen, p->md.timeout) != 0) goto bad; /* * Flush the read side. */ if (ioctl(p->fd, I_FLUSH, FLUSHR) != 0) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "FLUSHR: %s", + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "FLUSHR: %s", pcap_strerror(errno)); goto bad; } /* Allocate data buffer. */ - if (pcap_alloc_databuf(p, ebuf) != 0) + if (pcap_alloc_databuf(p) != 0) goto bad; /* @@ -208,14 +205,13 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, p->stats_op = pcap_stats_dlpi; p->close_op = pcap_close_libdlpi; - return (p); + return (0); bad: /* Get rid of any link-layer type list we allocated. */ if (p->dlt_list != NULL) free(p->dlt_list); - pcap_close_libdlpi(p); - free(p); - return (NULL); + dlpi_close(p->dlpi_hd); + return (PCAP_ERROR); } /* @@ -352,3 +348,16 @@ pcap_libdlpi_err(const char *linkname, const char *func, int err, char *errbuf) snprintf(errbuf, PCAP_ERRBUF_SIZE, "libpcap: %s failed on %s: %s", func, linkname, dlpi_strerror(err)); } + +pcap_t * +pcap_create(const char *device, char *ebuf) +{ + pcap_t *p; + + p = pcap_create_common(device, ebuf); + if (p == NULL) + return (NULL); + + p->activate_op = pcap_activate_libdlpi; + return (p); +} diff --git a/pcap-linux.c b/pcap-linux.c index 77dee22..cb805c8 100644 --- a/pcap-linux.c +++ b/pcap-linux.c @@ -34,7 +34,7 @@ #ifndef lint static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/pcap-linux.c,v 1.139 2008-03-14 09:09:14 guy Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/pcap-linux.c,v 1.140 2008-04-04 19:37:45 guy Exp $ (LBL)"; #endif /* @@ -84,6 +84,28 @@ static const char rcsid[] _U_ = #include "config.h" #endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Got Wireless Extensions? + */ +#ifdef HAVE_LINUX_WIRELESS_H +#include +#endif + #include "pcap-int.h" #include "pcap/sll.h" @@ -103,21 +125,6 @@ static const char rcsid[] _U_ = #include "pcap-bt-linux.h" #endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - /* * If PF_PACKET is defined, we can use {SOCK_RAW,SOCK_DGRAM}/PF_PACKET * sockets rather than SOCK_PACKET sockets. @@ -210,15 +217,17 @@ typedef int socklen_t; #define BIGGER_THAN_ALL_MTUS (64*1024) /* - * Prototypes for internal functions + * Prototypes for internal functions and methods. */ static void map_arphrd_to_dlt(pcap_t *, int, int); #ifdef HAVE_PF_PACKET_SOCKETS static short int map_packet_type_to_sll_type(short int); #endif -static int live_open_old(pcap_t *, const char *, int, int, char *); -static int live_open_new(pcap_t *, const char *, int, int, char *); -static int live_open_mmap(pcap_t *, char *); +static int pcap_activate_linux(pcap_t *); +static int activate_old(pcap_t *); +static int activate_new(pcap_t *); +static int activate_mmap(pcap_t *); +static int pcap_can_set_rfmon_linux(pcap_t *); static int pcap_read_linux(pcap_t *, int, pcap_handler, u_char *); static int pcap_read_packet(pcap_t *, pcap_handler, u_char *); static int pcap_inject_linux(pcap_t *, const void *, size_t); @@ -231,7 +240,7 @@ static void pcap_close_linux(pcap_t *); #define RING_GET_FRAME(h) (((struct tpacket_hdr**)h->buffer)[h->offset]) static void destroy_ring(pcap_t *handle); -static int create_ring(pcap_t* handle, unsigned size, char* errmsg); +static int create_ring(pcap_t *handle); static void pcap_close_linux_mmap(pcap_t *); static int pcap_read_linux_mmap(pcap_t *, int, pcap_handler , u_char *); static int pcap_setfilter_linux_mmap(pcap_t *, struct bpf_program *); @@ -249,6 +258,9 @@ static int iface_get_mtu(int fd, const char *device, char *ebuf); static int iface_get_arptype(int fd, const char *device, char *ebuf); #ifdef HAVE_PF_PACKET_SOCKETS static int iface_bind(int fd, int ifindex, char *ebuf); +static int has_wext(int sock_fd, const char *device); +static int enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, + const char *device); #endif static int iface_bind_old(int fd, const char *device, char *ebuf); @@ -264,6 +276,204 @@ static struct sock_fprog total_fcode = { 1, &total_insn }; #endif +pcap_t * +pcap_create(const char *device, char *ebuf) +{ + pcap_t *handle; + +#ifdef HAVE_DAG_API + if (strstr(device, "dag")) { + return dag_create(device, ebuf); + } +#endif /* HAVE_DAG_API */ + +#ifdef HAVE_SEPTEL_API + if (strstr(device, "septel")) { + return septel_create(device, ebuf); + } +#endif /* HAVE_SEPTEL_API */ + +#ifdef PCAP_SUPPORT_BT + if (strstr(device, "bluetooth")) { + return bt_create(device, ebuf); + } +#endif + +#ifdef PCAP_SUPPORT_USB + if (strstr(device, "usb")) { + return usb_create(device, ebuf); + } +#endif + + handle = pcap_create_common(device, ebuf); + if (handle == NULL) + return NULL; + + handle->activate_op = pcap_activate_linux; + handle->can_set_rfmon_op = pcap_can_set_rfmon_linux; + return handle; +} + +static int +pcap_can_set_rfmon_linux(pcap_t *p) +{ +#ifdef IW_MODE_MONITOR + int sock_fd; + struct iwreq ireq; +#endif + + if (p->opt.source == NULL) { + /* + * This is equivalent to the "any" device, and we don't + * support monitor mode on it. + */ + return 0; + } + +#ifdef IW_MODE_MONITOR + /* + * Bleah. There doesn't appear to be an ioctl to use to ask + * whether a device supports monitor mode; we'll just do + * SIOCGIWMODE and, if it succeeds, assume the device supports + * monitor mode. + * + * Open a socket on which to attempt to get the mode. + * (We assume that if we have Wireless Extensions support + * we also have PF_PACKET support.) + */ + sock_fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); + if (sock_fd == -1) { + (void)snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "socket: %s", pcap_strerror(errno)); + return PCAP_ERROR; + } + + /* + * Attempt to get the current mode. + */ + strncpy(ireq.ifr_ifrn.ifrn_name, p->opt.source, + sizeof ireq.ifr_ifrn.ifrn_name); + ireq.ifr_ifrn.ifrn_name[sizeof ireq.ifr_ifrn.ifrn_name - 1] = 0; + if (ioctl(sock_fd, SIOCGIWMODE, &ireq) != -1) { + /* + * Well, we got the mode; assume we can set it. + */ + close(sock_fd); + return 1; + } + if (errno == ENODEV) { + /* The device doesn't even exist. */ + close(sock_fd); + return PCAP_ERROR_NO_SUCH_DEVICE; + } + close(sock_fd); +#endif + return 0; +} + +/* + * With older kernels promiscuous mode is kind of interesting because we + * have to reset the interface before exiting. The problem can't really + * be solved without some daemon taking care of managing usage counts. + * If we put the interface into promiscuous mode, we set a flag indicating + * that we must take it out of that mode when the interface is closed, + * and, when closing the interface, if that flag is set we take it out + * of promiscuous mode. + * + * Even with newer kernels, we have the same issue with rfmon mode. + */ + +static void pcap_close_linux( pcap_t *handle ) +{ + struct ifreq ifr; +#ifdef IW_MODE_MONITOR + struct iwreq ireq; +#endif + + if (handle->md.must_clear != 0) { + /* + * There's something we have to do when closing this + * pcap_t. + */ + if (handle->md.must_clear & MUST_CLEAR_PROMISC) { + /* + * We put the interface into promiscuous mode; + * take it out of promiscuous mode. + * + * XXX - if somebody else wants it in promiscuous + * mode, this code cannot know that, so it'll take + * it out of promiscuous mode. That's not fixable + * in 2.0[.x] kernels. + */ + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, handle->md.device, + sizeof(ifr.ifr_name)); + if (ioctl(handle->fd, SIOCGIFFLAGS, &ifr) == -1) { + fprintf(stderr, + "Can't restore interface flags (SIOCGIFFLAGS failed: %s).\n" + "Please adjust manually.\n" + "Hint: This can't happen with Linux >= 2.2.0.\n", + strerror(errno)); + } else { + if (ifr.ifr_flags & IFF_PROMISC) { + /* + * Promiscuous mode is currently on; + * turn it off. + */ + ifr.ifr_flags &= ~IFF_PROMISC; + if (ioctl(handle->fd, SIOCSIFFLAGS, + &ifr) == -1) { + fprintf(stderr, + "Can't restore interface flags (SIOCSIFFLAGS failed: %s).\n" + "Please adjust manually.\n" + "Hint: This can't happen with Linux >= 2.2.0.\n", + strerror(errno)); + } + } + } + } + +#ifdef IW_MODE_MONITOR + if (handle->md.must_clear & MUST_CLEAR_RFMON) { + /* + * We put the interface into rfmon mode; + * take it out of rfmon mode. + * + * XXX - if somebody else wants it in rfmon + * mode, this code cannot know that, so it'll take + * it out of rfmon mode. + */ + strncpy(ireq.ifr_ifrn.ifrn_name, handle->md.device, + sizeof ireq.ifr_ifrn.ifrn_name); + ireq.ifr_ifrn.ifrn_name[sizeof ireq.ifr_ifrn.ifrn_name - 1] + = 0; + ireq.u.mode = handle->md.oldmode; + if (ioctl(handle->fd, SIOCSIWMODE, &ireq) == -1) { + /* + * Scientist, you've failed. + */ + fprintf(stderr, + "Can't restore interface wireless mode (SIOCSIWMODE failed: %s).\n" + "Please adjust manually.\n", + strerror(errno)); + } + } +#endif + + /* + * 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(handle); + } + + if (handle->md.device != NULL) { + free(handle->md.device); + handle->md.device = NULL; + } + pcap_close_common(handle); +} + /* * Get a handle for a live capture from the given device. You can * pass NULL as device to get all packages (without link level @@ -271,55 +481,15 @@ static struct sock_fprog total_fcode * will be set to promiscous mode (XXX: I think this usage should * be deprecated and functions be added to select that later allow * modification of that values -- Torsten). - * - * See also pcap(3). */ -pcap_t * -pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, - char *ebuf) +static int +pcap_activate_linux(pcap_t *handle) { - pcap_t *handle; + const char *device; int err; - int live_open_ok = 0; + int activate_ok = 0; -#ifdef HAVE_DAG_API - if (strstr(device, "dag")) { - return dag_open_live(device, snaplen, promisc, to_ms, ebuf); - } -#endif /* HAVE_DAG_API */ - -#ifdef HAVE_SEPTEL_API - if (strstr(device, "septel")) { - return septel_open_live(device, snaplen, promisc, to_ms, ebuf); - } -#endif /* HAVE_SEPTEL_API */ - -#ifdef PCAP_SUPPORT_BT - if (strstr(device, "bluetooth")) { - return bt_open_live(device, snaplen, promisc, to_ms, ebuf); - } -#endif - -#ifdef PCAP_SUPPORT_USB - if (strstr(device, "usb")) { - return usb_open_live(device, snaplen, promisc, to_ms, ebuf); - } -#endif - - /* Allocate a handle for this session. */ - - handle = malloc(sizeof(*handle)); - if (handle == NULL) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s", - pcap_strerror(errno)); - return NULL; - } - - /* Initialize some components of the pcap structure. */ - - memset(handle, 0, sizeof(*handle)); - handle->snapshot = snaplen; - handle->md.timeout = to_ms; + device = handle->opt.source; handle->inject_op = pcap_inject_linux; handle->setfilter_op = pcap_setfilter_linux; @@ -338,10 +508,10 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, if (!device || strcmp(device, "any") == 0) { device = NULL; handle->md.device = strdup("any"); - if (promisc) { - promisc = 0; + if (handle->opt.promisc) { + handle->opt.promisc = 0; /* Just a warning. */ - snprintf(ebuf, PCAP_ERRBUF_SIZE, + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Promiscuous mode not supported on the \"any\" device"); } @@ -349,10 +519,9 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, handle->md.device = strdup(device); if (handle->md.device == NULL) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "strdup: %s", + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "strdup: %s", pcap_strerror(errno) ); - free(handle); - return NULL; + return PCAP_ERROR; } /* @@ -365,38 +534,40 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, * trying both methods with the newer method preferred. */ - if ((err = live_open_new(handle, device, promisc, to_ms, ebuf)) == 1) { - live_open_ok = 1; - if (live_open_mmap(handle, ebuf) == 1) - return handle; + if ((err = activate_new(handle)) == 1) { + activate_ok = 1; + /* + * Try to use memory-mapped access. + */ + if (activate_mmap(handle) == 1) + return 0; /* we succeeded; nothing more to do */ } else if (err == 0) { /* Non-fatal error; try old way */ - if (live_open_old(handle, device, promisc, to_ms, ebuf)) - live_open_ok = 1; + if ((err = activate_old(handle)) == 1) + activate_ok = 1; } - if (!live_open_ok) { + if (!activate_ok) { /* * Both methods to open the packet socket failed. Tidy * up and report our failure (ebuf is expected to be * set by the functions above). */ - if (handle->md.device != NULL) + if (handle->md.device != NULL) { free(handle->md.device); - free(handle); - return NULL; + handle->md.device = NULL; + } + return err; } /* Allocate the buffer */ handle->buffer = malloc(handle->bufsize + handle->offset); if (!handle->buffer) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "malloc: %s", pcap_strerror(errno)); - pcap_close_linux(handle); - free(handle); - return NULL; + return PCAP_ERROR; } /* @@ -405,7 +576,7 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, */ handle->selectable_fd = handle->fd; - return handle; + return 0; } /* @@ -489,7 +660,7 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata) if (errno == EAGAIN) return 0; /* no packet there */ else { - snprintf(handle->errbuf, sizeof(handle->errbuf), + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "recvfrom: %s", pcap_strerror(errno)); return -1; } @@ -617,7 +788,7 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata) /* Fill in our own header data */ if (ioctl(handle->fd, SIOCGSTAMP, &pcap_header.ts) == -1) { - snprintf(handle->errbuf, sizeof(handle->errbuf), + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "SIOCGSTAMP: %s", pcap_strerror(errno)); return -1; } @@ -880,7 +1051,7 @@ pcap_setfilter_linux(pcap_t *handle, struct bpf_program *filter) return -1; if (!filter) { strncpy(handle->errbuf, "setfilter: No filter specified", - sizeof(handle->errbuf)); + PCAP_ERRBUF_SIZE); return -1; } @@ -1018,7 +1189,7 @@ pcap_setdirection_linux(pcap_t *handle, pcap_direction_t d) * We're not using PF_PACKET sockets, so we can't determine * the direction of the packet. */ - snprintf(handle->errbuf, sizeof(handle->errbuf), + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Setting direction is not supported on SOCK_PACKET sockets"); return -1; } @@ -1095,7 +1266,7 @@ static void map_arphrd_to_dlt(pcap_t *handle, int arptype, int cooked_ok) * XXX - are there any sorts of "fake Ethernet" that have * ARPHRD_ETHER but that *shouldn't offer DLT_DOCSIS as * a Cisco CMTS won't put traffic onto it or get traffic - * bridged onto it? ISDN is handled in "live_open_new()", + * bridged onto it? ISDN is handled in "activate_new()", * as we fall back on cooked mode there; are there any * others? */ @@ -1362,238 +1533,251 @@ static void map_arphrd_to_dlt(pcap_t *handle, int arptype, int cooked_ok) /* ===== Functions to interface to the newer kernels ================== */ /* - * Try to open a packet socket using the new kernel interface. - * Returns 0 on failure. - * FIXME: 0 uses to mean success (Sebastian) + * Try to open a packet socket using the new kernel PF_PACKET interface. + * Returns 1 on success, 0 on an error that means the new interface isn't + * present (so the old SOCK_PACKET interface should be tried), and a + * PCAP_ERROR_ value on an error that means that the old mechanism won't + * work either (so it shouldn't be tried). */ static int -live_open_new(pcap_t *handle, const char *device, int promisc, - int to_ms, char *ebuf) +activate_new(pcap_t *handle) { #ifdef HAVE_PF_PACKET_SOCKETS int sock_fd = -1, arptype; - int err; - int fatal_err = 0; + int err = 0; struct packet_mreq mr; + const char* device = handle->opt.source; - /* One shot loop used for error handling - bail out with break */ + /* + * Open a socket with protocol family packet. If a device is + * given we try to open it in raw mode otherwise we use + * the cooked interface. + */ + sock_fd = device ? + socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)) + : socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_ALL)); - do { - /* - * Open a socket with protocol family packet. If a device is - * given we try to open it in raw mode otherwise we use - * the cooked interface. - */ - sock_fd = device ? - socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)) - : socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_ALL)); + if (sock_fd == -1) { + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "socket: %s", + pcap_strerror(errno) ); + return 0; /* try old mechanism */ + } - if (sock_fd == -1) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "socket: %s", - pcap_strerror(errno) ); - break; - } + /* It seems the kernel supports the new interface. */ + handle->md.sock_packet = 0; - /* It seems the kernel supports the new interface. */ - handle->md.sock_packet = 0; + /* + * Get the interface index of the loopback device. + * If the attempt fails, don't fail, just set the + * "md.lo_ifindex" to -1. + * + * XXX - can there be more than one device that loops + * packets back, i.e. devices other than "lo"? If so, + * we'd need to find them all, and have an array of + * indices for them, and check all of them in + * "pcap_read_packet()". + */ + handle->md.lo_ifindex = iface_get_id(sock_fd, "lo", handle->errbuf); - /* - * Get the interface index of the loopback device. - * If the attempt fails, don't fail, just set the - * "md.lo_ifindex" to -1. - * - * XXX - can there be more than one device that loops - * packets back, i.e. devices other than "lo"? If so, - * we'd need to find them all, and have an array of - * indices for them, and check all of them in - * "pcap_read_packet()". - */ - handle->md.lo_ifindex = iface_get_id(sock_fd, "lo", ebuf); + /* + * Default value for offset to align link-layer payload + * on a 4-byte boundary. + */ + handle->offset = 0; - /* - * Default value for offset to align link-layer payload - * on a 4-byte boundary. - */ - handle->offset = 0; + /* + * What kind of frames do we have to deal with? Fall back + * to cooked mode if we have an unknown interface type + * or a type we know doesn't work well in raw mode. + */ + if (device) { + /* Assume for now we don't need cooked mode. */ + handle->md.cooked = 0; - /* - * What kind of frames do we have to deal with? Fall back - * to cooked mode if we have an unknown interface type. - */ - - if (device) { - /* Assume for now we don't need cooked mode. */ - handle->md.cooked = 0; - - arptype = iface_get_arptype(sock_fd, device, ebuf); - if (arptype == -1) { - fatal_err = 1; - break; - } - map_arphrd_to_dlt(handle, arptype, 1); - if (handle->linktype == -1 || - handle->linktype == DLT_LINUX_SLL || - handle->linktype == DLT_LINUX_IRDA || - handle->linktype == DLT_LINUX_LAPD || - (handle->linktype == DLT_EN10MB && - (strncmp("isdn", device, 4) == 0 || - strncmp("isdY", device, 4) == 0))) { - /* - * Unknown interface type (-1), or a - * device we explicitly chose to run - * in cooked mode (e.g., PPP devices), - * or an ISDN device (whose link-layer - * type we can only determine by using - * APIs that may be different on different - * kernels) - reopen in cooked mode. - */ - if (close(sock_fd) == -1) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, - "close: %s", pcap_strerror(errno)); - break; - } - sock_fd = socket(PF_PACKET, SOCK_DGRAM, - htons(ETH_P_ALL)); - if (sock_fd == -1) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, - "socket: %s", pcap_strerror(errno)); - break; - } - handle->md.cooked = 1; - - /* - * Get rid of any link-layer type list - * we allocated - this only supports cooked - * capture. - */ - if (handle->dlt_list != NULL) { - free(handle->dlt_list); - handle->dlt_list = NULL; - handle->dlt_count = 0; - } - - if (handle->linktype == -1) { - /* - * Warn that we're falling back on - * cooked mode; we may want to - * update "map_arphrd_to_dlt()" - * to handle the new type. - */ - snprintf(ebuf, PCAP_ERRBUF_SIZE, - "arptype %d not " - "supported by libpcap - " - "falling back to cooked " - "socket", - arptype); - } - /* IrDA capture is not a real "cooked" capture, - * it's IrLAP frames, not IP packets. */ - if (handle->linktype != DLT_LINUX_IRDA && - handle->linktype != DLT_LINUX_LAPD) - handle->linktype = DLT_LINUX_SLL; - } - - handle->md.ifindex = iface_get_id(sock_fd, device, ebuf); - if (handle->md.ifindex == -1) - break; - - if ((err = iface_bind(sock_fd, handle->md.ifindex, - ebuf)) < 0) { - if (err == -2) - fatal_err = 1; - break; - } - } else { + if (handle->opt.rfmon) { /* - * This is cooked mode. + * We were asked to turn on monitor mode. + * Do so before we get the link-layer type, + * because entering monitor mode could change + * the link-layer type. */ + err = enter_rfmon_mode_wext(handle, sock_fd, device); + if (err < 0) { + /* Hard failure */ + close(sock_fd); + return err; + } + if (err == 0) { + /* + * Nothing worked for turning monitor mode + * on. + */ + close(sock_fd); + return PCAP_ERROR_RFMON_NOTSUP; + } + } + arptype = iface_get_arptype(sock_fd, device, handle->errbuf); + if (arptype < 0) { + close(sock_fd); + return arptype; + } + map_arphrd_to_dlt(handle, arptype, 1); + if (handle->linktype == -1 || + handle->linktype == DLT_LINUX_SLL || + handle->linktype == DLT_LINUX_IRDA || + handle->linktype == DLT_LINUX_LAPD || + (handle->linktype == DLT_EN10MB && + (strncmp("isdn", device, 4) == 0 || + strncmp("isdY", device, 4) == 0))) { + /* + * Unknown interface type (-1), or a + * device we explicitly chose to run + * in cooked mode (e.g., PPP devices), + * or an ISDN device (whose link-layer + * type we can only determine by using + * APIs that may be different on different + * kernels) - reopen in cooked mode. + */ + if (close(sock_fd) == -1) { + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + "close: %s", pcap_strerror(errno)); + return PCAP_ERROR; + } + sock_fd = socket(PF_PACKET, SOCK_DGRAM, + htons(ETH_P_ALL)); + if (sock_fd == -1) { + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + "socket: %s", pcap_strerror(errno)); + return PCAP_ERROR; + } handle->md.cooked = 1; - handle->linktype = DLT_LINUX_SLL; /* - * We're not bound to a device. - * XXX - true? Or true only if we're using - * the "any" device? - * For now, we're using this as an indication - * that we can't transmit; stop doing that only - * if we figure out how to transmit in cooked - * mode. + * Get rid of any link-layer type list + * we allocated - this only supports cooked + * capture. */ - handle->md.ifindex = -1; - } - - /* - * Select promiscuous mode on if "promisc" is set. - * - * Do not turn allmulti mode on if we don't select - * promiscuous mode - on some devices (e.g., Orinoco - * wireless interfaces), allmulti mode isn't supported - * and the driver implements it by turning promiscuous - * mode on, and that screws up the operation of the - * card as a normal networking interface, and on no - * other platform I know of does starting a non- - * promiscuous capture affect which multicast packets - * are received by the interface. - */ - - /* - * Hmm, how can we set promiscuous mode on all interfaces? - * I am not sure if that is possible at all. - */ - - if (device && promisc) { - memset(&mr, 0, sizeof(mr)); - mr.mr_ifindex = handle->md.ifindex; - mr.mr_type = PACKET_MR_PROMISC; - if (setsockopt(sock_fd, SOL_PACKET, - PACKET_ADD_MEMBERSHIP, &mr, sizeof(mr)) == -1) - { - snprintf(ebuf, PCAP_ERRBUF_SIZE, - "setsockopt: %s", pcap_strerror(errno)); - break; + if (handle->dlt_list != NULL) { + free(handle->dlt_list); + handle->dlt_list = NULL; + handle->dlt_count = 0; } + + if (handle->linktype == -1) { + /* + * Warn that we're falling back on + * cooked mode; we may want to + * update "map_arphrd_to_dlt()" + * to handle the new type. + */ + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + "arptype %d not " + "supported by libpcap - " + "falling back to cooked " + "socket", + arptype); + } + + /* + * IrDA capture is not a real "cooked" capture, + * it's IrLAP frames, not IP packets. The + * same applies to LAPD capture. + */ + if (handle->linktype != DLT_LINUX_IRDA && + handle->linktype != DLT_LINUX_LAPD) + handle->linktype = DLT_LINUX_SLL; } - /* - * This is a 2.2[.x] or later kernel (we know that - * because we're not using a SOCK_PACKET socket - - * PF_PACKET is supported only in 2.2 and later - * kernels). - * - * We can safely pass "recvfrom()" a byte count - * based on the snapshot length. - * - * If we're in cooked mode, make the snapshot length - * large enough to hold a "cooked mode" header plus - * 1 byte of packet data (so we don't pass a byte - * count of 0 to "recvfrom()"). - */ - if (handle->md.cooked) { - if (handle->snapshot < SLL_HDR_LEN + 1) - handle->snapshot = SLL_HDR_LEN + 1; + handle->md.ifindex = iface_get_id(sock_fd, device, + handle->errbuf); + if (handle->md.ifindex == -1) { + close(sock_fd); + return PCAP_ERROR; } - handle->bufsize = handle->snapshot; - /* Save the socket FD in the pcap structure */ - - handle->fd = sock_fd; - - return 1; - - } while(0); - - if (sock_fd != -1) - close(sock_fd); - - if (fatal_err) { + if ((err = iface_bind(sock_fd, handle->md.ifindex, + handle->errbuf)) < 0) { + close(sock_fd); + if (err == -2) + return PCAP_ERROR; + else + return 0; /* try old mechanism */ + } + } else { /* - * Get rid of any link-layer type list we allocated. + * This is cooked mode. */ - if (handle->dlt_list != NULL) - free(handle->dlt_list); - return -2; - } else - return 0; + handle->md.cooked = 1; + handle->linktype = DLT_LINUX_SLL; + + /* + * We're not bound to a device. + * XXX - true? Or true only if we're using + * the "any" device? + * For now, we're using this as an indication + * that we can't transmit; stop doing that only + * if we figure out how to transmit in cooked + * mode. + */ + handle->md.ifindex = -1; + } + + /* + * Select promiscuous mode on if "promisc" is set. + * + * Do not turn allmulti mode on if we don't select + * promiscuous mode - on some devices (e.g., Orinoco + * wireless interfaces), allmulti mode isn't supported + * and the driver implements it by turning promiscuous + * mode on, and that screws up the operation of the + * card as a normal networking interface, and on no + * other platform I know of does starting a non- + * promiscuous capture affect which multicast packets + * are received by the interface. + */ + + /* + * Hmm, how can we set promiscuous mode on all interfaces? + * I am not sure if that is possible at all. + */ + + if (device && handle->opt.promisc) { + memset(&mr, 0, sizeof(mr)); + mr.mr_ifindex = handle->md.ifindex; + mr.mr_type = PACKET_MR_PROMISC; + if (setsockopt(sock_fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, + &mr, sizeof(mr)) == -1) { + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + "setsockopt: %s", pcap_strerror(errno)); + close(sock_fd); + return PCAP_ERROR; + } + } + + /* + * This is a 2.2[.x] or later kernel (we know that + * because we're not using a SOCK_PACKET socket - + * PF_PACKET is supported only in 2.2 and later + * kernels). + * + * We can safely pass "recvfrom()" a byte count + * based on the snapshot length. + * + * If we're in cooked mode, make the snapshot length + * large enough to hold a "cooked mode" header plus + * 1 byte of packet data (so we don't pass a byte + * count of 0 to "recvfrom()"). + */ + if (handle->md.cooked) { + if (handle->snapshot < SLL_HDR_LEN + 1) + handle->snapshot = SLL_HDR_LEN + 1; + } + handle->bufsize = handle->snapshot; + + /* Save the socket FD in the pcap structure */ + handle->fd = sock_fd; + + return 1; #else strncpy(ebuf, "New packet capturing interface not supported by build " @@ -1603,16 +1787,21 @@ live_open_new(pcap_t *handle, const char *device, int promisc, } static int -live_open_mmap(pcap_t* handle, char* errmsg) +activate_mmap(pcap_t *handle) { #ifdef HAVE_PACKET_RING - /* by default request 4M for the ring buffer */ - int ret = create_ring(handle, 4*1024*1024, errmsg); + int ret; + + if (handle->opt.buffer_size == 0) { + /* by default request 2M for the ring buffer */ + handle->opt.buffer_size = 2*1024*1024; + } + ret = create_ring(handle); if (ret == 0) return ret; /* override some defaults and inherit the other fields from - * open_live_new + * activate_new * handle->offset is used to get the current position into the rx ring * handle->cc is used to store the ring size */ handle->read_op = pcap_read_linux_mmap; @@ -1644,7 +1833,7 @@ compute_ring_block(int frame_size, unsigned *block_size, unsigned *frames_per_bl } static int -create_ring(pcap_t* handle, unsigned size, char* errmsg) +create_ring(pcap_t *handle) { unsigned i, j, ringsize, frames_per_block; struct tpacket_req req; @@ -1655,7 +1844,7 @@ create_ring(pcap_t* handle, unsigned size, char* errmsg) * The snap len should be carefully chosen to achive best * performance */ req.tp_frame_size = TPACKET_ALIGN(handle->snapshot+TPACKET_HDRLEN); - req.tp_frame_nr = size/req.tp_frame_size; + req.tp_frame_nr = handle->opt.buffer_size/req.tp_frame_size; compute_ring_block(req.tp_frame_size, &req.tp_block_size, &frames_per_block); req.tp_block_nr = req.tp_frame_nr / frames_per_block; @@ -1672,7 +1861,7 @@ retry: req.tp_block_nr = req.tp_frame_nr/frames_per_block; goto retry; } - snprintf(errmsg, PCAP_ERRBUF_SIZE, "can't create rx ring on " + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "can't create rx ring on " "packet socket %d: %d-%s", handle->fd, errno, pcap_strerror(errno)); return 0; @@ -1683,7 +1872,7 @@ retry: handle->bp = mmap(0, ringsize, PROT_READ| PROT_WRITE, MAP_SHARED, handle->fd, 0); if (handle->bp == MAP_FAILED) { - snprintf(errmsg, PCAP_ERRBUF_SIZE, "can't mmap rx ring: %d-%s", + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "can't mmap rx ring: %d-%s", errno, pcap_strerror(errno)); /* clear the allocated ring on error*/ @@ -1761,7 +1950,7 @@ static int pcap_setnonblock_mmap(pcap_t *p, int nonblock, char *errbuf) { /* map each value to the corresponding 2's complement, to - * preserve the timeout value provided with pcap_open_live */ + * preserve the timeout value provided with pcap_set_timeout */ if (nonblock) { if (p->md.timeout > 0) p->md.timeout = p->md.timeout*-1 - 1; @@ -2010,307 +2199,738 @@ iface_bind(int fd, int ifindex, char *ebuf) return 0; } -#endif - - -/* ===== Functions to interface to the older kernels ================== */ - /* - * With older kernels promiscuous mode is kind of interesting because we - * have to reset the interface before exiting. The problem can't really - * be solved without some daemon taking care of managing usage counts. - * If we put the interface into promiscuous mode, we set a flag indicating - * that we must take it out of that mode when the interface is closed, - * and, when closing the interface, if that flag is set we take it out - * of promiscuous mode. + * Check whether the device supports the Wireless Extensions. + * Returns 1 if it does, 0 if it doesn't, PCAP_ERROR_NO_SUCH_DEVICE + * if the device doesn't even exist. */ - -/* - * List of pcaps for which we turned promiscuous mode on by hand. - * If there are any such pcaps, we arrange to call "pcap_close_all()" - * when we exit, and have it close all of them to turn promiscuous mode - * off. - */ -static struct pcap *pcaps_to_close; - -/* - * TRUE if we've already called "atexit()" to cause "pcap_close_all()" to - * be called on exit. - */ -static int did_atexit; - -static void pcap_close_all(void) +static int +has_wext(int sock_fd, const char *device) { - struct pcap *handle; +#ifdef IW_MODE_MONITOR + struct iwreq ireq; - while ((handle = pcaps_to_close) != NULL) - pcap_close(handle); + strncpy(ireq.ifr_ifrn.ifrn_name, device, + sizeof ireq.ifr_ifrn.ifrn_name); + ireq.ifr_ifrn.ifrn_name[sizeof ireq.ifr_ifrn.ifrn_name - 1] = 0; + if (ioctl(sock_fd, SIOCGIWNAME, &ireq) >= 0) + return 1; /* yes */ + if (errno == ENODEV) + return PCAP_ERROR_NO_SUCH_DEVICE; +#endif + return 0; } -static void pcap_close_linux( pcap_t *handle ) +/* + * Per me si va ne la citta dolente, + * Per me si va ne l'etterno dolore, + * ... + * Lasciate ogne speranza, voi ch'intrate. + */ +typedef enum { + MONITOR_WEXT, + MONITOR_HOSTAP, + MONITOR_PRISM, + MONITOR_PRISM54, + MONITOR_ACX100, + MONITOR_RT2500, + MONITOR_RT2570, + MONITOR_RT73, + MONITOR_RTL8XXX +} monitor_type; + +/* + * Use the Wireless Extensions, if we have them, to try to turn monitor mode + * on if it's not already on. + * + * Returns 1 on success, 0 if we don't support the Wireless Extensions + * on this device, or a PCAP_ERROR_ value if we do support them but + * we weren't able to turn monitor mode on. + */ +static int +enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device) { - struct pcap *p, *prevp; - struct ifreq ifr; +#ifdef IW_MODE_MONITOR + /* + * XXX - at least some adapters require non-Wireless Extensions + * mechanisms to turn monitor mode on. + * + * Atheros cards might require that a separate "monitor virtual access + * point" be created, with later versions of the madwifi driver. + * + * Some Intel Centrino adapters might require private ioctls to get + * radio headers; the ipw2200 and ipw3945 drivers allow you to + * configure a separate "rtapN" interface to capture in monitor + * mode without preventing the adapter from operating normally. + * + * It would be Truly Wonderful if mac80211 and nl80211 cleaned this + * up, and if all drivers were converted to mac80211 drivers. + */ + int err; + struct iwreq ireq; + struct iw_priv_args *priv; + monitor_type montype; + int i; + __u32 cmd; + int args[2]; + int channel; - if (handle->md.clear_promisc) { + /* + * Does this device *support* the Wireless Extensions? + */ + err = has_wext(sock_fd, device); + if (err <= 0) + return err; /* either it doesn't or the device doesn't even exist */ + /* + * Try to get all the Wireless Extensions private ioctls + * supported by this device. + * + * First, get the size of the buffer we need, by supplying no + * buffer and a length of 0. If the device supports private + * ioctls, it should return E2BIG, with ireq.u.data.length set + * to the length we need. If it doesn't support them, it should + * return EOPNOTSUPP. + */ + memset(&ireq, 0, sizeof ireq); + strncpy(ireq.ifr_ifrn.ifrn_name, device, + sizeof ireq.ifr_ifrn.ifrn_name); + ireq.ifr_ifrn.ifrn_name[sizeof ireq.ifr_ifrn.ifrn_name - 1] = 0; + ireq.u.data.pointer = args; + ireq.u.data.length = 0; + ireq.u.data.flags = 0; + if (ioctl(sock_fd, SIOCGIWPRIV, &ireq) != -1) { + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + "%s: SIOCGIWPRIV with a zero-length buffer didn't fail!", + device); + return PCAP_ERROR; + } + if (errno == EOPNOTSUPP) { /* - * We put the interface into promiscuous mode; take - * it out of promiscuous mode. - * - * XXX - if somebody else wants it in promiscuous mode, - * this code cannot know that, so it'll take it out - * of promiscuous mode. That's not fixable in 2.0[.x] - * kernels. + * No private ioctls, so we assume that there's only one + * DLT_ for monitor mode. */ - memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_name, handle->md.device, sizeof(ifr.ifr_name)); - if (ioctl(handle->fd, SIOCGIFFLAGS, &ifr) == -1) { - fprintf(stderr, - "Can't restore interface flags (SIOCGIFFLAGS failed: %s).\n" - "Please adjust manually.\n" - "Hint: This can't happen with Linux >= 2.2.0.\n", - strerror(errno)); - } else { - if (ifr.ifr_flags & IFF_PROMISC) { - /* - * Promiscuous mode is currently on; turn it - * off. - */ - ifr.ifr_flags &= ~IFF_PROMISC; - if (ioctl(handle->fd, SIOCSIFFLAGS, &ifr) == -1) { - fprintf(stderr, - "Can't restore interface flags (SIOCSIFFLAGS failed: %s).\n" - "Please adjust manually.\n" - "Hint: This can't happen with Linux >= 2.2.0.\n", - strerror(errno)); - } - } + return 0; + } + if (errno != E2BIG) { + /* + * Failed. + */ + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + "%s: SIOCGIWPRIV: %s", device, pcap_strerror(errno)); + return PCAP_ERROR; + } + priv = malloc(ireq.u.data.length); + if (priv == NULL) { + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + "malloc: %s", pcap_strerror(errno)); + return PCAP_ERROR; + } + ireq.u.data.pointer = priv; + if (ioctl(sock_fd, SIOCGIWPRIV, &ireq) == -1) { + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + "%s: SIOCGIWPRIV: %s", device, pcap_strerror(errno)); + return PCAP_ERROR; + } + + /* + * Look for private ioctls to turn monitor mode on or, if + * monitor mode is on, to set the header type. + */ + montype = MONITOR_WEXT; + cmd = 0; + for (i = 0; i < ireq.u.data.length; i++) { + if (strcmp(priv[i].name, "monitor_type") == 0) { + /* + * Hostap driver, use this one. + * Set monitor mode first. + * You can set it to 0 to get DLT_IEEE80211, + * 1 to get DLT_PRISM, or 2 to get + * DLT_IEEE80211_RADIO_AVS. + */ + if ((priv[i].set_args & IW_PRIV_TYPE_MASK) != IW_PRIV_TYPE_INT) + break; + if (!(priv[i].set_args & IW_PRIV_SIZE_FIXED)) + break; + if ((priv[i].set_args & IW_PRIV_SIZE_MASK) != 1) + break; + montype = MONITOR_HOSTAP; + cmd = priv[i].cmd; + break; } + if (strcmp(priv[i].name, "set_prismhdr") == 0) { + /* + * Prism54 driver, use this one. + * Set monitor mode first. + * You can set it to 2 to get DLT_IEEE80211 + * or 3 or get DLT_PRISM. + */ + if ((priv[i].set_args & IW_PRIV_TYPE_MASK) != IW_PRIV_TYPE_INT) + break; + if (!(priv[i].set_args & IW_PRIV_SIZE_FIXED)) + break; + if ((priv[i].set_args & IW_PRIV_SIZE_MASK) != 1) + break; + montype = MONITOR_PRISM54; + cmd = priv[i].cmd; + break; + } + if (strcmp(priv[i].name, "forceprismheader") == 0) { + /* + * RT2570 driver, use this one. + * Do this after turning monitor mode on. + * You can set it to 1 to get DLT_PRISM or 2 + * to get DLT_IEEE80211. + */ + if ((priv[i].set_args & IW_PRIV_TYPE_MASK) != IW_PRIV_TYPE_INT) + break; + if (!(priv[i].set_args & IW_PRIV_SIZE_FIXED)) + break; + if ((priv[i].set_args & IW_PRIV_SIZE_MASK) != 1) + break; + montype = MONITOR_RT2570; + cmd = priv[i].cmd; + break; + } + if (strcmp(priv[i].name, "forceprism") == 0) { + /* + * RT73 driver, use this one. + * Do this after turning monitor mode on. + * Its argument is a *string*; you can + * set it to "1" to get DLT_PRISM or "2" + * to get DLT_IEEE80211. + */ + if ((priv[i].set_args & IW_PRIV_TYPE_MASK) != IW_PRIV_TYPE_CHAR) + break; + if (priv[i].set_args & IW_PRIV_SIZE_FIXED) + break; + montype = MONITOR_RT73; + cmd = priv[i].cmd; + break; + } + if (strcmp(priv[i].name, "prismhdr") == 0) { + /* + * One of the RTL8xxx drivers, use this one. + * It can only be done after monitor mode + * has been turned on. You can set it to 1 + * to get DLT_PRISM or 0 to get DLT_IEEE80211. + */ + if ((priv[i].set_args & IW_PRIV_TYPE_MASK) != IW_PRIV_TYPE_INT) + break; + if (!(priv[i].set_args & IW_PRIV_SIZE_FIXED)) + break; + if ((priv[i].set_args & IW_PRIV_SIZE_MASK) != 1) + break; + montype = MONITOR_RTL8XXX; + cmd = priv[i].cmd; + break; + } + if (strcmp(priv[i].name, "rfmontx") == 0) { + /* + * RT2500 or RT61 driver, use this one. + * It has one one-byte parameter; set + * u.data.length to 1 and u.data.pointer to + * point to the parameter. + * It doesn't itself turn monitor mode on. + * You can set it to 1 to allow transmitting + * in monitor mode(?) and get DLT_IEEE80211, + * or set it to 0 to disallow transmitting in + * monitor mode(?) and get DLT_PRISM. + */ + if ((priv[i].set_args & IW_PRIV_TYPE_MASK) != IW_PRIV_TYPE_INT) + break; + if ((priv[i].set_args & IW_PRIV_SIZE_MASK) != 2) + break; + montype = MONITOR_RT2500; + cmd = priv[i].cmd; + break; + } + if (strcmp(priv[i].name, "monitor") == 0) { + /* + * Either ACX100 or hostap, use this one. + * It turns monitor mode on. + * If it takes two arguments, it's ACX100; + * the first argument is 1 for DLT_PRISM + * or 2 for DLT_IEEE80211, and the second + * argument is the channel on which to + * run. If it takes one argument, it's + * HostAP, and the argument is 2 for + * DLT_IEEE80211 and 3 for DLT_PRISM. + * + * If we see this, we don't quit, as this + * might be a version of the hostap driver + * that also supports "monitor_type". + */ + if ((priv[i].set_args & IW_PRIV_TYPE_MASK) != IW_PRIV_TYPE_INT) + break; + if (!(priv[i].set_args & IW_PRIV_SIZE_FIXED)) + break; + switch (priv[i].set_args & IW_PRIV_SIZE_MASK) { - /* - * Take this pcap out of the list of pcaps for which we - * have to take the interface out of promiscuous mode. - */ - for (p = pcaps_to_close, prevp = NULL; p != NULL; - prevp = p, p = p->md.next) { - if (p == handle) { - /* - * Found it. Remove it from the list. - */ - if (prevp == NULL) { - /* - * It was at the head of the list. - */ - pcaps_to_close = p->md.next; - } else { - /* - * It was in the middle of the list. - */ - prevp->md.next = p->md.next; - } + case 1: + montype = MONITOR_PRISM; + cmd = priv[i].cmd; + break; + + case 2: + montype = MONITOR_ACX100; + cmd = priv[i].cmd; + break; + + default: break; } } } - if (handle->md.device != NULL) - free(handle->md.device); - handle->md.device = NULL; - pcap_close_common(handle); + /* + * XXX - ipw3945? islism? + */ + + /* + * Get the old mode. + */ + strncpy(ireq.ifr_ifrn.ifrn_name, device, + sizeof ireq.ifr_ifrn.ifrn_name); + ireq.ifr_ifrn.ifrn_name[sizeof ireq.ifr_ifrn.ifrn_name - 1] = 0; + if (ioctl(sock_fd, SIOCGIWMODE, &ireq) == -1) { + /* + * We probably won't be able to set the mode, either. + */ + return PCAP_ERROR_RFMON_NOTSUP; + } + + /* + * Is it currently in monitor mode? + */ + if (ireq.u.mode == IW_MODE_MONITOR) { + /* + * Yes. Just leave things as they are. + * We don't offer multiple link-layer types, as + * changing the link-layer type out from under + * somebody else capturing in monitor mode would + * be considered rude. + */ + return 1; + } + /* + * No. We have to put the adapter into rfmon mode. + */ + + /* + * If we haven't already done so, arrange to have + * "pcap_close_all()" called when we exit. + */ + if (!pcap_do_addexit(handle)) { + /* + * "atexit()" failed; don't put the interface + * in rfmon mode, just give up. + */ + return PCAP_ERROR_RFMON_NOTSUP; + } + + /* + * Save the old mode. + */ + handle->md.oldmode = ireq.u.mode; + + /* + * Put the adapter in rfmon mode. How we do this depends + * on whether we have a special private ioctl or not. + */ + if (montype == MONITOR_PRISM) { + /* + * We have the "monitor" private ioctl, but none of + * the other private ioctls. Use this, and select + * the Prism header. + * + * If it fails, just fall back on SIOCSIWMODE. + */ + memset(&ireq, 0, sizeof ireq); + strncpy(ireq.ifr_ifrn.ifrn_name, device, + sizeof ireq.ifr_ifrn.ifrn_name); + ireq.ifr_ifrn.ifrn_name[sizeof ireq.ifr_ifrn.ifrn_name - 1] = 0; + ireq.u.data.length = 1; /* 1 argument */ + args[0] = 3; /* request Prism header */ + memcpy(ireq.u.name, args, IFNAMSIZ); + if (ioctl(sock_fd, cmd, &ireq) != -1) { + /* + * Success. + * Note that we have to put the old mode back + * when we close the device. + */ + handle->md.must_clear |= MUST_CLEAR_RFMON; + + /* + * Add this to the list of pcaps to close + * when we exit. + */ + pcap_add_to_pcaps_to_close(handle); + + return 1; + } + + /* + * Failure. Fall back on SIOCSIWMODE. + */ + } + + /* + * First, turn monitor mode on. + */ + strncpy(ireq.ifr_ifrn.ifrn_name, device, + sizeof ireq.ifr_ifrn.ifrn_name); + ireq.ifr_ifrn.ifrn_name[sizeof ireq.ifr_ifrn.ifrn_name - 1] = 0; + ireq.u.mode = IW_MODE_MONITOR; + if (ioctl(sock_fd, SIOCSIWMODE, &ireq) == -1) { + /* + * Scientist, you've failed. + */ + return PCAP_ERROR_RFMON_NOTSUP; + } + + /* + * Now select the appropriate radio header. + */ + switch (montype) { + + case MONITOR_WEXT: + /* + * We don't have any private ioctl to set the header. + */ + break; + + case MONITOR_HOSTAP: + /* + * Select the AVS header if we can, otherwise + * select the Prism header. + */ + memset(&ireq, 0, sizeof ireq); + strncpy(ireq.ifr_ifrn.ifrn_name, device, + sizeof ireq.ifr_ifrn.ifrn_name); + ireq.ifr_ifrn.ifrn_name[sizeof ireq.ifr_ifrn.ifrn_name - 1] = 0; + args[0] = 2; /* request AVS header */ + memcpy(ireq.u.name, args, sizeof (int)); + if (ioctl(sock_fd, cmd, &ireq) == -1) { + /* + * Failure - try the Prism header. + */ + memset(&ireq, 0, sizeof ireq); + strncpy(ireq.ifr_ifrn.ifrn_name, device, + sizeof ireq.ifr_ifrn.ifrn_name); + ireq.ifr_ifrn.ifrn_name[sizeof ireq.ifr_ifrn.ifrn_name - 1] = 0; + args[0] = 1; /* request Prism header */ + memcpy(ireq.u.name, args, sizeof (int)); + ioctl(sock_fd, cmd, &ireq); + } + break; + + case MONITOR_PRISM: + /* + * The private ioctl failed. + */ + break; + + case MONITOR_PRISM54: + /* + * Select the Prism header. + */ + memset(&ireq, 0, sizeof ireq); + strncpy(ireq.ifr_ifrn.ifrn_name, device, + sizeof ireq.ifr_ifrn.ifrn_name); + ireq.ifr_ifrn.ifrn_name[sizeof ireq.ifr_ifrn.ifrn_name - 1] = 0; + args[0] = 3; /* request Prism header */ + memcpy(ireq.u.name, args, sizeof (int)); + ioctl(sock_fd, cmd, &ireq); + break; + + case MONITOR_ACX100: + /* + * Get the current channel. + */ + memset(&ireq, 0, sizeof ireq); + strncpy(ireq.ifr_ifrn.ifrn_name, device, + sizeof ireq.ifr_ifrn.ifrn_name); + ireq.ifr_ifrn.ifrn_name[sizeof ireq.ifr_ifrn.ifrn_name - 1] = 0; + if (ioctl(sock_fd, SIOCGIWFREQ, &ireq) == -1) { + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + "%s: SIOCGIWFREQ: %s", device, + pcap_strerror(errno)); + return PCAP_ERROR; + } + channel = ireq.u.freq.m; + + /* + * Select the Prism header, and set the channel to the + * current value. + */ + memset(&ireq, 0, sizeof ireq); + strncpy(ireq.ifr_ifrn.ifrn_name, device, + sizeof ireq.ifr_ifrn.ifrn_name); + ireq.ifr_ifrn.ifrn_name[sizeof ireq.ifr_ifrn.ifrn_name - 1] = 0; + args[0] = 1; /* request Prism header */ + args[1] = channel; /* set channel */ + memcpy(ireq.u.name, args, 2*sizeof (int)); + ioctl(sock_fd, cmd, &ireq); + break; + + case MONITOR_RT2500: + /* + * Disallow transmission - that turns on the + * Prism header. + */ + memset(&ireq, 0, sizeof ireq); + strncpy(ireq.ifr_ifrn.ifrn_name, device, + sizeof ireq.ifr_ifrn.ifrn_name); + ireq.ifr_ifrn.ifrn_name[sizeof ireq.ifr_ifrn.ifrn_name - 1] = 0; + args[0] = 0; /* disallow transmitting */ + memcpy(ireq.u.name, args, sizeof (int)); + ioctl(sock_fd, cmd, &ireq); + break; + + case MONITOR_RT2570: + /* + * Force the Prism header. + */ + memset(&ireq, 0, sizeof ireq); + strncpy(ireq.ifr_ifrn.ifrn_name, device, + sizeof ireq.ifr_ifrn.ifrn_name); + ireq.ifr_ifrn.ifrn_name[sizeof ireq.ifr_ifrn.ifrn_name - 1] = 0; + args[0] = 1; /* request Prism header */ + memcpy(ireq.u.name, args, sizeof (int)); + ioctl(sock_fd, cmd, &ireq); + break; + + case MONITOR_RT73: + /* + * Force the Prism header. + */ + memset(&ireq, 0, sizeof ireq); + strncpy(ireq.ifr_ifrn.ifrn_name, device, + sizeof ireq.ifr_ifrn.ifrn_name); + ireq.ifr_ifrn.ifrn_name[sizeof ireq.ifr_ifrn.ifrn_name - 1] = 0; + ireq.u.data.length = 1; /* 1 argument */ + ireq.u.data.pointer = "1"; + ireq.u.data.flags = 0; + ioctl(sock_fd, cmd, &ireq); + break; + + case MONITOR_RTL8XXX: + /* + * Force the Prism header. + */ + memset(&ireq, 0, sizeof ireq); + strncpy(ireq.ifr_ifrn.ifrn_name, device, + sizeof ireq.ifr_ifrn.ifrn_name); + ireq.ifr_ifrn.ifrn_name[sizeof ireq.ifr_ifrn.ifrn_name - 1] = 0; + args[0] = 1; /* request Prism header */ + memcpy(ireq.u.name, args, sizeof (int)); + ioctl(sock_fd, cmd, &ireq); + break; + } + + /* + * Note that we have to put the old mode back when we + * close the device. + */ + handle->md.must_clear |= MUST_CLEAR_RFMON; + + /* + * Add this to the list of pcaps to close when we exit. + */ + pcap_add_to_pcaps_to_close(handle); + + return 1; +#else + /* + * We don't have the Wireless Extensions available, so we can't + * do monitor mode. + */ + return 0; +#endif } +#endif /* HAVE_PF_PACKET_SOCKETS */ + +/* ===== Functions to interface to the older kernels ================== */ + /* - * Try to open a packet socket using the old kernel interface. - * Returns 0 on failure. - * FIXME: 0 uses to mean success (Sebastian) + * Try to open a packet socket using the old kernel interface. + * Returns 1 on success and a PCAP_ERROR_ value on an error. */ static int -live_open_old(pcap_t *handle, const char *device, int promisc, - int to_ms, char *ebuf) +activate_old(pcap_t *handle) { int arptype; struct ifreq ifr; + const char *device = handle->opt.source; struct utsname utsname; int mtu; - do { - /* Open the socket */ + /* Open the socket */ - handle->fd = socket(PF_INET, SOCK_PACKET, htons(ETH_P_ALL)); - if (handle->fd == -1) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, - "socket: %s", pcap_strerror(errno)); - break; + handle->fd = socket(PF_INET, SOCK_PACKET, htons(ETH_P_ALL)); + if (handle->fd == -1) { + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + "socket: %s", pcap_strerror(errno)); + return PCAP_ERROR; + } + + /* It worked - we are using the old interface */ + handle->md.sock_packet = 1; + + /* ...which means we get the link-layer header. */ + handle->md.cooked = 0; + + /* Bind to the given device */ + + if (!device) { + strncpy(handle->errbuf, "pcap_activate: The \"any\" device isn't supported on 2.0[.x]-kernel systems", + PCAP_ERRBUF_SIZE); + return PCAP_ERROR; + } + if (iface_bind_old(handle->fd, device, handle->errbuf) == -1) + return PCAP_ERROR; + + /* + * Try to get the link-layer type. + */ + arptype = iface_get_arptype(handle->fd, device, handle->errbuf); + if (arptype < 0) + return PCAP_ERROR; + + /* + * Try to find the DLT_ type corresponding to that + * link-layer type. + */ + map_arphrd_to_dlt(handle, arptype, 0); + if (handle->linktype == -1) { + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + "unknown arptype %d", arptype); + return PCAP_ERROR; + } + + /* Go to promisc mode if requested */ + + if (handle->opt.promisc) { + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name)); + if (ioctl(handle->fd, SIOCGIFFLAGS, &ifr) == -1) { + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + "SIOCGIFFLAGS: %s", pcap_strerror(errno)); + return PCAP_ERROR; } + if ((ifr.ifr_flags & IFF_PROMISC) == 0) { + /* + * Promiscuous mode isn't currently on, + * so turn it on, and remember that + * we should turn it off when the + * pcap_t is closed. + */ - /* It worked - we are using the old interface */ - handle->md.sock_packet = 1; - - /* ...which means we get the link-layer header. */ - handle->md.cooked = 0; - - /* Bind to the given device */ - - if (!device) { - strncpy(ebuf, "pcap_open_live: The \"any\" device isn't supported on 2.0[.x]-kernel systems", - PCAP_ERRBUF_SIZE); - break; - } - if (iface_bind_old(handle->fd, device, ebuf) == -1) - break; - - /* - * Try to get the link-layer type. - */ - arptype = iface_get_arptype(handle->fd, device, ebuf); - if (arptype == -1) - break; - - /* - * Try to find the DLT_ type corresponding to that - * link-layer type. - */ - map_arphrd_to_dlt(handle, arptype, 0); - if (handle->linktype == -1) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, - "unknown arptype %d", arptype); - break; - } - - /* Go to promisc mode if requested */ - - if (promisc) { - memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name)); - if (ioctl(handle->fd, SIOCGIFFLAGS, &ifr) == -1) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, - "SIOCGIFFLAGS: %s", pcap_strerror(errno)); - break; + /* + * If we haven't already done so, arrange + * to have "pcap_close_all()" called when + * we exit. + */ + if (!pcap_do_addexit(handle)) { + /* + * "atexit()" failed; don't put + * the interface in promiscuous + * mode, just give up. + */ + return PCAP_ERROR; } - if ((ifr.ifr_flags & IFF_PROMISC) == 0) { - /* - * Promiscuous mode isn't currently on, - * so turn it on, and remember that - * we should turn it off when the - * pcap_t is closed. - */ - /* - * If we haven't already done so, arrange - * to have "pcap_close_all()" called when - * we exit. - */ - if (!did_atexit) { - if (atexit(pcap_close_all) == -1) { - /* - * "atexit()" failed; don't - * put the interface in - * promiscuous mode, just - * give up. - */ - strncpy(ebuf, "atexit failed", - PCAP_ERRBUF_SIZE); - break; - } - did_atexit = 1; - } - - ifr.ifr_flags |= IFF_PROMISC; - if (ioctl(handle->fd, SIOCSIFFLAGS, &ifr) == -1) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, - "SIOCSIFFLAGS: %s", - pcap_strerror(errno)); - break; - } - handle->md.clear_promisc = 1; - - /* - * Add this to the list of pcaps - * to close when we exit. - */ - handle->md.next = pcaps_to_close; - pcaps_to_close = handle; + ifr.ifr_flags |= IFF_PROMISC; + if (ioctl(handle->fd, SIOCSIFFLAGS, &ifr) == -1) { + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + "SIOCSIFFLAGS: %s", + pcap_strerror(errno)); + return PCAP_ERROR; } - } + handle->md.must_clear |= MUST_CLEAR_PROMISC; + /* + * Add this to the list of pcaps + * to close when we exit. + */ + pcap_add_to_pcaps_to_close(handle); + } + } + + /* + * Compute the buffer size. + * + * We're using SOCK_PACKET, so this might be a 2.0[.x] + * kernel, and might require special handling - check. + */ + if (uname(&utsname) < 0 || + strncmp(utsname.release, "2.0", 3) == 0) { /* - * Compute the buffer size. + * Either we couldn't find out what kernel release + * this is, or it's a 2.0[.x] kernel. * - * We're using SOCK_PACKET, so this might be a 2.0[.x] - * kernel, and might require special handling - check. + * In the 2.0[.x] kernel, a "recvfrom()" on + * a SOCK_PACKET socket, with MSG_TRUNC set, will + * return the number of bytes read, so if we pass + * a length based on the snapshot length, it'll + * return the number of bytes from the packet + * copied to userland, not the actual length + * of the packet. + * + * This means that, for example, the IP dissector + * in tcpdump will get handed a packet length less + * than the length in the IP header, and will + * complain about "truncated-ip". + * + * So we don't bother trying to copy from the + * kernel only the bytes in which we're interested, + * but instead copy them all, just as the older + * versions of libpcap for Linux did. + * + * The buffer therefore needs to be big enough to + * hold the largest packet we can get from this + * device. Unfortunately, we can't get the MRU + * of the network; we can only get the MTU. The + * MTU may be too small, in which case a packet larger + * than the buffer size will be truncated *and* we + * won't get the actual packet size. + * + * However, if the snapshot length is larger than + * the buffer size based on the MTU, we use the + * snapshot length as the buffer size, instead; + * this means that with a sufficiently large snapshot + * length we won't artificially truncate packets + * to the MTU-based size. + * + * This mess just one of many problems with packet + * capture on 2.0[.x] kernels; you really want a + * 2.2[.x] or later kernel if you want packet capture + * to work well. */ - if (uname(&utsname) < 0 || - strncmp(utsname.release, "2.0", 3) == 0) { - /* - * Either we couldn't find out what kernel release - * this is, or it's a 2.0[.x] kernel. - * - * In the 2.0[.x] kernel, a "recvfrom()" on - * a SOCK_PACKET socket, with MSG_TRUNC set, will - * return the number of bytes read, so if we pass - * a length based on the snapshot length, it'll - * return the number of bytes from the packet - * copied to userland, not the actual length - * of the packet. - * - * This means that, for example, the IP dissector - * in tcpdump will get handed a packet length less - * than the length in the IP header, and will - * complain about "truncated-ip". - * - * So we don't bother trying to copy from the - * kernel only the bytes in which we're interested, - * but instead copy them all, just as the older - * versions of libpcap for Linux did. - * - * The buffer therefore needs to be big enough to - * hold the largest packet we can get from this - * device. Unfortunately, we can't get the MRU - * of the network; we can only get the MTU. The - * MTU may be too small, in which case a packet larger - * than the buffer size will be truncated *and* we - * won't get the actual packet size. - * - * However, if the snapshot length is larger than - * the buffer size based on the MTU, we use the - * snapshot length as the buffer size, instead; - * this means that with a sufficiently large snapshot - * length we won't artificially truncate packets - * to the MTU-based size. - * - * This mess just one of many problems with packet - * capture on 2.0[.x] kernels; you really want a - * 2.2[.x] or later kernel if you want packet capture - * to work well. - */ - mtu = iface_get_mtu(handle->fd, device, ebuf); - if (mtu == -1) - break; - handle->bufsize = MAX_LINKHEADER_SIZE + mtu; - if (handle->bufsize < handle->snapshot) - handle->bufsize = handle->snapshot; - } else { - /* - * This is a 2.2[.x] or later kernel. - * - * We can safely pass "recvfrom()" a byte count - * based on the snapshot length. - */ + mtu = iface_get_mtu(handle->fd, device, handle->errbuf); + if (mtu == -1) + return PCAP_ERROR; + handle->bufsize = MAX_LINKHEADER_SIZE + mtu; + if (handle->bufsize < handle->snapshot) handle->bufsize = handle->snapshot; - } - + } else { /* - * Default value for offset to align link-layer payload - * on a 4-byte boundary. + * This is a 2.2[.x] or later kernel. + * + * We can safely pass "recvfrom()" a byte count + * based on the snapshot length. */ - handle->offset = 0; + handle->bufsize = handle->snapshot; + } - return 1; + /* + * Default value for offset to align link-layer payload + * on a 4-byte boundary. + */ + handle->offset = 0; - } while (0); - - pcap_close_linux(handle); - return 0; + return 1; } /* @@ -2387,9 +3007,15 @@ iface_get_arptype(int fd, const char *device, char *ebuf) strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name)); if (ioctl(fd, SIOCGIFHWADDR, &ifr) == -1) { + if (errno == ENODEV) { + /* + * No such device. + */ + return PCAP_ERROR_NO_SUCH_DEVICE; + } snprintf(ebuf, PCAP_ERRBUF_SIZE, "SIOCGIFHWADDR: %s", pcap_strerror(errno)); - return -1; + return PCAP_ERROR; } return ifr.ifr_hwaddr.sa_family; @@ -2413,7 +3039,7 @@ fix_program(pcap_t *handle, struct sock_fprog *fcode) len = handle->fcode.bf_len; f = (struct bpf_insn *)malloc(prog_size); if (f == NULL) { - snprintf(handle->errbuf, sizeof(handle->errbuf), + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "malloc: %s", pcap_strerror(errno)); return -1; } @@ -2585,7 +3211,7 @@ set_kernel_filter(pcap_t *handle, struct sock_fprog *fcode) if (save_errno != EAGAIN) { /* Fatal error */ reset_kernel_filter(handle); - snprintf(handle->errbuf, sizeof(handle->errbuf), + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "recv: %s", pcap_strerror(save_errno)); return -2; } diff --git a/pcap-nit.c b/pcap-nit.c index 4cc122e..7ae5309 100644 --- a/pcap-nit.c +++ b/pcap-nit.c @@ -20,7 +20,7 @@ */ #ifndef lint static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/pcap-nit.c,v 1.60 2008-02-02 20:58:18 guy Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/pcap-nit.c,v 1.61 2008-04-04 19:37:45 guy Exp $ (LBL)"; #endif #ifdef HAVE_CONFIG_H @@ -237,51 +237,43 @@ nit_setflags(int fd, int promisc, int to_ms, char *ebuf) return (0); } -static void -pcap_close_nit(pcap_t *p) -{ - pcap_close_common(p); - if (p->device != NULL) - free(p->device); -} - -pcap_t * -pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, - char *ebuf) +static int +pcap_activate_nit(pcap_t *p) { int fd; struct sockaddr_nit snit; - register pcap_t *p; - p = (pcap_t *)malloc(sizeof(*p)); - if (p == NULL) { - strlcpy(ebuf, pcap_strerror(errno), PCAP_ERRBUF_SIZE); - return (NULL); + if (p->opt.rfmon) { + /* + * No monitor mode on SunOS 3.x or earlier (no + * Wi-Fi *devices* for the hardware that supported + * them!). + */ + return (PCAP_ERROR_RFMON_NOTSUP); } - if (snaplen < 96) + if (p->snapshot < 96) /* * NIT requires a snapshot length of at least 96. */ - snaplen = 96; + p->snapshot = 96; memset(p, 0, sizeof(*p)); p->fd = fd = socket(AF_NIT, SOCK_RAW, NITPROTO_RAW); if (fd < 0) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "socket: %s", pcap_strerror(errno)); goto bad; } snit.snit_family = AF_NIT; - (void)strncpy(snit.snit_ifname, device, NITIFSIZ); + (void)strncpy(snit.snit_ifname, p->opt.source, NITIFSIZ); if (bind(fd, (struct sockaddr *)&snit, sizeof(snit))) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "bind: %s: %s", snit.snit_ifname, pcap_strerror(errno)); goto bad; } - p->snapshot = snaplen; - nit_setflags(p->fd, promisc, to_ms, ebuf); + nit_setflags(p->fd, p->opt.promisc, p->md.timeout, p->errbuf); /* * NIT supports only ethernets. @@ -291,17 +283,7 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, p->bufsize = BUFSPACE; p->buffer = (u_char *)malloc(p->bufsize); if (p->buffer == NULL) { - strlcpy(ebuf, pcap_strerror(errno), PCAP_ERRBUF_SIZE); - goto bad; - } - - /* - * We need the device name in order to send packets. - */ - p->device = strdup(device); - if (p->device == NULL) { - strlcpy(ebuf, pcap_strerror(errno), PCAP_ERRBUF_SIZE); - free(p->buffer); + strlcpy(p->errbuf, pcap_strerror(errno), PCAP_ERRBUF_SIZE); goto bad; } @@ -338,14 +320,26 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, p->getnonblock_op = pcap_getnonblock_fd; p->setnonblock_op = pcap_setnonblock_fd; p->stats_op = pcap_stats_nit; - p->close_op = pcap_close_nit; + p->close_op = pcap_close_common; - return (p); + return (0); bad: if (fd >= 0) close(fd); - free(p); - return (NULL); + return (PCAP_ERROR); +} + +pcap_t * +pcap_create(const char *device, char *ebuf) +{ + pcap_t *p; + + p = pcap_create_common(device, ebuf); + if (p == NULL) + return (NULL); + + p->activate_op = pcap_activate_nit; + return (p); } int diff --git a/pcap-null.c b/pcap-null.c index 9fa00a0..25a4aac 100644 --- a/pcap-null.c +++ b/pcap-null.c @@ -20,7 +20,7 @@ */ #ifndef lint static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/pcap-null.c,v 1.21 2003-11-15 23:24:03 guy Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/pcap-null.c,v 1.22 2008-04-04 19:37:45 guy Exp $ (LBL)"; #endif #ifdef HAVE_CONFIG_H @@ -40,10 +40,9 @@ static const char rcsid[] _U_ = static char nosup[] = "live packet capture not supported on this system"; pcap_t * -pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, - char *ebuf) +pcap_activate(pcap_t *p) { - (void)strlcpy(ebuf, nosup, PCAP_ERRBUF_SIZE); + (void)strlcpy(p->errbuf, nosup, PCAP_ERRBUF_SIZE); return (NULL); } diff --git a/pcap-pf.c b/pcap-pf.c index c85595f..5ba02a6 100644 --- a/pcap-pf.c +++ b/pcap-pf.c @@ -24,7 +24,7 @@ #ifndef lint static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/pcap-pf.c,v 1.95 2007-12-05 23:37:26 guy Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/pcap-pf.c,v 1.96 2008-04-04 19:37:45 guy Exp $ (LBL)"; #endif #ifdef HAVE_CONFIG_H @@ -291,23 +291,14 @@ pcap_stats_pf(pcap_t *p, struct pcap_stat *ps) #define DLT_DOCSIS 143 #endif -pcap_t * -pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, - char *ebuf) +static int +pcap_activate_pf(pcap_t *p) { - pcap_t *p; short enmode; int backlog = -1; /* request the most */ struct enfilter Filter; struct endevp devparams; - p = (pcap_t *)malloc(sizeof(*p)); - if (p == NULL) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, - "pcap_open_live: %s", pcap_strerror(errno)); - return (0); - } - memset(p, 0, sizeof(*p)); /* * Initially try a read/write open (to allow the inject * method to work). If that fails due to permission @@ -327,21 +318,21 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, * "const char *" as its first argument. That appears to be * the case, at least on Digital UNIX 4.0. */ - p->fd = pfopen(device, O_RDWR); + p->fd = pfopen(p->opt.source, O_RDWR); if (p->fd == -1 && errno == EACCES) - p->fd = pfopen(device, O_RDONLY); + p->fd = pfopen(p->opt.source, O_RDONLY); if (p->fd < 0) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "pf open: %s: %s\n\ + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "pf open: %s: %s\n\ your system may not be properly configured; see the packetfilter(4) man page\n", - device, pcap_strerror(errno)); + p->opt.source, pcap_strerror(errno)); goto bad; } p->md.OrigMissed = -1; enmode = ENTSTAMP|ENBATCH|ENNONEXCL; - if (promisc) + if (p->opt.promisc) enmode |= ENPROMISC; if (ioctl(p->fd, EIOCMBIS, (caddr_t)&enmode) < 0) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "EIOCMBIS: %s", + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "EIOCMBIS: %s", pcap_strerror(errno)); goto bad; } @@ -352,13 +343,13 @@ your system may not be properly configured; see the packetfilter(4) man page\n", #endif /* set the backlog */ if (ioctl(p->fd, EIOCSETW, (caddr_t)&backlog) < 0) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "EIOCSETW: %s", + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "EIOCSETW: %s", pcap_strerror(errno)); goto bad; } /* discover interface type */ if (ioctl(p->fd, EIOCDEVP, (caddr_t)&devparams) < 0) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "EIOCDEVP: %s", + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "EIOCDEVP: %s", pcap_strerror(errno)); goto bad; } @@ -440,8 +431,8 @@ your system may not be properly configured; see the packetfilter(4) man page\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 %u", - devparams.end_dev_type); + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "unknown data-link type %u", devparams.end_dev_type); goto bad; } /* set truncation */ @@ -450,32 +441,31 @@ your system may not be properly configured; see the packetfilter(4) man page\n", p->fddipad = PCAP_FDDIPAD; /* packetfilter includes the padding in the snapshot */ - snaplen += PCAP_FDDIPAD; + p->snapshot += PCAP_FDDIPAD; } else p->fddipad = 0; #endif - if (ioctl(p->fd, EIOCTRUNCATE, (caddr_t)&snaplen) < 0) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "EIOCTRUNCATE: %s", + if (ioctl(p->fd, EIOCTRUNCATE, (caddr_t)&p->snapshot) < 0) { + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "EIOCTRUNCATE: %s", pcap_strerror(errno)); goto bad; } - p->snapshot = snaplen; /* accept all packets */ memset(&Filter, 0, sizeof(Filter)); Filter.enf_Priority = 37; /* anything > 2 */ Filter.enf_FilterLen = 0; /* means "always true" */ if (ioctl(p->fd, EIOCSETF, (caddr_t)&Filter) < 0) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "EIOCSETF: %s", + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "EIOCSETF: %s", pcap_strerror(errno)); goto bad; } - if (to_ms != 0) { + if (p->md.timeout != 0) { struct timeval timeout; - timeout.tv_sec = to_ms / 1000; - timeout.tv_usec = (to_ms * 1000) % 1000000; + timeout.tv_sec = p->md.timeout / 1000; + timeout.tv_usec = (p->md.timeout * 1000) % 1000000; if (ioctl(p->fd, EIOCSRTIMEOUT, (caddr_t)&timeout) < 0) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "EIOCSRTIMEOUT: %s", + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "EIOCSRTIMEOUT: %s", pcap_strerror(errno)); goto bad; } @@ -484,7 +474,7 @@ your system may not be properly configured; see the packetfilter(4) man page\n", p->bufsize = BUFSPACE; p->buffer = (u_char*)malloc(p->bufsize + p->offset); if (p->buffer == NULL) { - strlcpy(ebuf, pcap_strerror(errno), PCAP_ERRBUF_SIZE); + strlcpy(p->errbuf, pcap_strerror(errno), PCAP_ERRBUF_SIZE); goto bad; } @@ -503,17 +493,24 @@ your system may not be properly configured; see the packetfilter(4) man page\n", p->stats_op = pcap_stats_pf; p->close_op = pcap_close_common; - return (p); + return (0); bad: if (p->fd >= 0) close(p->fd); - /* - * Get rid of any link-layer type list we allocated. - */ - if (p->dlt_list != NULL) - free(p->dlt_list); - free(p); - return (NULL); + return (PCAP_ERROR); +} + +pcap_t * +pcap_create(const char *device, char *ebuf) +{ + pcap_t *p; + + p = pcap_create_common(device, ebuf); + if (p == NULL) + return (NULL); + + p->activate_op = pcap_activate_pf; + return (p); } int diff --git a/pcap-septel.c b/pcap-septel.c index 9f0233e..32786d4 100644 --- a/pcap-septel.c +++ b/pcap-septel.c @@ -16,7 +16,7 @@ #ifndef lint static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/pcap-septel.c,v 1.2 2005-06-21 01:03:03 guy Exp $"; + "@(#) $Header: /tcpdump/master/libpcap/pcap-septel.c,v 1.3 2008-04-04 19:37:45 guy Exp $"; #endif #ifdef HAVE_CONFIG_H @@ -50,8 +50,8 @@ static const char rcsid[] _U_ = /* This code is required when compiling for a Septel device only. */ #include "pcap-septel.h" -/* Replace dag function names with pcap equivalent. */ -#define septel_open_live pcap_open_live +/* Replace septel function names with pcap equivalent. */ +#define septel_create pcap_create #define septel_platform_finddevs pcap_platform_finddevs #endif /* SEPTEL_ONLY */ @@ -199,28 +199,14 @@ septel_inject(pcap_t *handle, const void *buf _U_, size_t size _U_) } /* - * Get a handle for a live capture from the given Septel device. Always pass a NULL device + * Activate a handle for a live capture from the given Septel device. Always pass a NULL device * The promisc flag is ignored because Septel cards have built-in tracing. - * The to_ms parameter is also ignored as it is - * not supported in hardware. + * The timeout is also ignored as it is not supported in hardware. * * See also pcap(3). */ -pcap_t *septel_open_live(const char *device, int snaplen, int promisc, int to_ms, char *ebuf) { - pcap_t *handle; - - handle = malloc(sizeof(*handle)); - if (handle == NULL) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc %s: %s", device, pcap_strerror(errno)); - return NULL; - } - - /* Initialize some components of the pcap structure. */ - - memset(handle, 0, sizeof(*handle)); - - handle->snapshot = snaplen; - +static pcap_t *septel_activate(pcap_t* handle) { + /* Initialize some components of the pcap structure. */ handle->linktype = DLT_MTP2; handle->bufsize = 0; @@ -239,14 +225,18 @@ pcap_t *septel_open_live(const char *device, int snaplen, int promisc, int to_ms handle->stats_op = septel_stats; handle->close_op = septel_platform_close; - return handle; + return 0; +} -fail: - if (handle != NULL) { - free(handle); - } +pcap_t *septel_create(const char *device, char *ebuf) { + pcap_t *p; - return NULL; + p = pcap_create_common(device, ebuf); + if (p == NULL) + return NULL; + + p->activate_op = septel_activate; + return p; } static int septel_stats(pcap_t *p, struct pcap_stat *ps) { diff --git a/pcap-septel.h b/pcap-septel.h index a9526b5..227d056 100644 --- a/pcap-septel.h +++ b/pcap-septel.h @@ -8,8 +8,8 @@ * Authors: Gilbert HOYEK (gil_hoyek@hotmail.com), Elias M. KHOURY * (+961 3 485343); * - * @(#) $Header: /tcpdump/master/libpcap/pcap-septel.h,v 1.1 2005-06-20 21:27:10 guy Exp $ + * @(#) $Header: /tcpdump/master/libpcap/pcap-septel.h,v 1.2 2008-04-04 19:37:45 guy Exp $ */ -pcap_t *septel_open_live(const char *device, int snaplen, int promisc, int to_ms, char *ebuf); +pcap_t *septel_create(const char *device, char *ebuf); diff --git a/pcap-sita.c b/pcap-sita.c index 658362c..8634922 100644 --- a/pcap-sita.c +++ b/pcap-sita.c @@ -917,25 +917,19 @@ static int pcap_read_acn(pcap_t *handle, int max_packets, pcap_handler callback, return 1; } -pcap_t *pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, char *ebuf) { - pcap_t *handle; +static int pcap_activate_sita(pcap_t *handle) { int fd; - /* Allocate a handle for this session. */ - - handle = malloc(sizeof(*handle)); - if (handle == NULL) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s", - pcap_strerror(errno)); - return NULL; + if (handle->opt.rfmon) { + /* + * No monitor mode on SITA devices (they're not Wi-Fi + * devices). + */ + return PCAP_ERROR_RFMON_NOTSUP; } /* Initialize some components of the pcap structure. */ - memset(handle, 0, sizeof(*handle)); - handle->snapshot = snaplen; - handle->md.timeout = to_ms; - handle->inject_op = pcap_inject_acn; handle->setfilter_op = pcap_setfilter_acn; handle->setdirection_op = pcap_setdirection_acn; @@ -946,12 +940,11 @@ pcap_t *pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, handle->read_op = pcap_read_acn; handle->stats_op = pcap_stats_acn; - fd = acn_open_live(device, ebuf, &handle->linktype); - if (fd == -1) { - free(handle); - return NULL; - } - handle->md.clear_promisc = promisc; + fd = acn_open_live(handle->opt.source, handle->errbuf, + &handle->linktype); + if (fd == -1) + return PCAP_ERROR; + handle->md.clear_promisc = handle->md.promisc; handle->fd = fd; handle->bufsize = handle->snapshot; @@ -959,11 +952,10 @@ pcap_t *pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, handle->buffer = malloc(handle->bufsize + handle->offset); if (!handle->buffer) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "malloc: %s", pcap_strerror(errno)); pcap_close_acn(handle); - free(handle); - return NULL; + return PCAP_ERROR; } /* @@ -972,5 +964,16 @@ pcap_t *pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, */ handle->selectable_fd = handle->fd; - return handle; + return 0; +} + +pcap_t *pcap_create(const char *device, char *ebuf) { + pcap_t *p; + + p = pcap_create_common(device, ebuf); + if (p == NULL) + return (NULL); + + p->activate_op = pcap_activate_sita; + return (p); } diff --git a/pcap-snit.c b/pcap-snit.c index a14a2c3..13c7e9b 100644 --- a/pcap-snit.c +++ b/pcap-snit.c @@ -25,7 +25,7 @@ #ifndef lint static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/pcap-snit.c,v 1.75 2008-02-02 20:58:18 guy Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/pcap-snit.c,v 1.76 2008-04-04 19:37:45 guy Exp $ (LBL)"; #endif #ifdef HAVE_CONFIG_H @@ -260,30 +260,29 @@ nit_setflags(int fd, int promisc, int to_ms, char *ebuf) return (0); } -pcap_t * -pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, - char *ebuf) +static int +pcap_activate_snit(pcap_t *p) { struct strioctl si; /* struct for ioctl() */ struct ifreq ifr; /* interface request struct */ int chunksize = CHUNKSIZE; int fd; static char dev[] = "/dev/nit"; - register pcap_t *p; - p = (pcap_t *)malloc(sizeof(*p)); - if (p == NULL) { - strlcpy(ebuf, pcap_strerror(errno), PCAP_ERRBUF_SIZE); - return (NULL); + if (p->opt.rfmon) { + /* + * No monitor mode on SunOS 4.x (no Wi-Fi devices on + * hardware supported by SunOS 4.x). + */ + return (PCAP_ERROR_RFMON_NOTSUP); } - if (snaplen < 96) + if (p->snapshot < 96) /* * NIT requires a snapshot length of at least 96. */ - snaplen = 96; + p->snapshot = 96; - memset(p, 0, sizeof(*p)); /* * Initially try a read/write open (to allow the inject * method to work). If that fails due to permission @@ -302,19 +301,19 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, if (fd < 0 && errno == EACCES) p->fd = fd = open(dev, O_RDONLY); if (fd < 0) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "%s: %s", dev, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "%s: %s", dev, pcap_strerror(errno)); goto bad; } /* arrange to get discrete messages from the STREAM and use NIT_BUF */ if (ioctl(fd, I_SRDOPT, (char *)RMSGD) < 0) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "I_SRDOPT: %s", + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "I_SRDOPT: %s", pcap_strerror(errno)); goto bad; } if (ioctl(fd, I_PUSH, "nbuf") < 0) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "push nbuf: %s", + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "push nbuf: %s", pcap_strerror(errno)); goto bad; } @@ -324,34 +323,33 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, si.ic_len = sizeof(chunksize); si.ic_dp = (char *)&chunksize; if (ioctl(fd, I_STR, (char *)&si) < 0) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "NIOCSCHUNK: %s", + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "NIOCSCHUNK: %s", pcap_strerror(errno)); goto bad; } /* request the interface */ - strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name)); + strncpy(ifr.ifr_name, p->opt.source, sizeof(ifr.ifr_name)); ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = '\0'; si.ic_cmd = NIOCBIND; si.ic_len = sizeof(ifr); si.ic_dp = (char *)𝔦 if (ioctl(fd, I_STR, (char *)&si) < 0) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "NIOCBIND: %s: %s", + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "NIOCBIND: %s: %s", ifr.ifr_name, pcap_strerror(errno)); goto bad; } /* set the snapshot length */ si.ic_cmd = NIOCSSNAP; - si.ic_len = sizeof(snaplen); - si.ic_dp = (char *)&snaplen; + si.ic_len = sizeof(p->snapshot); + si.ic_dp = (char *)&p->snapshot; if (ioctl(fd, I_STR, (char *)&si) < 0) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "NIOCSSNAP: %s", + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "NIOCSSNAP: %s", pcap_strerror(errno)); goto bad; } - p->snapshot = snaplen; - if (nit_setflags(p->fd, promisc, to_ms, ebuf) < 0) + if (nit_setflags(p->fd, p->opt.promisc, p->md.timeout, p->errbuf) < 0) goto bad; (void)ioctl(fd, I_FLUSH, (char *)FLUSHR); @@ -363,7 +361,7 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, p->bufsize = BUFSPACE; p->buffer = (u_char *)malloc(p->bufsize); if (p->buffer == NULL) { - strlcpy(ebuf, pcap_strerror(errno), PCAP_ERRBUF_SIZE); + strlcpy(p->errbuf, pcap_strerror(errno), PCAP_ERRBUF_SIZE); goto bad; } @@ -403,12 +401,24 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, p->stats_op = pcap_stats_snit; p->close_op = pcap_close_common; - return (p); + return (0); bad: if (fd >= 0) close(fd); - free(p); - return (NULL); + return (PCAP_ERROR); +} + +pcap_t * +pcap_create(const char *device, char *ebuf) +{ + pcap_t *p; + + p = pcap_create_common(device, ebuf); + if (p == NULL) + return (NULL); + + p->activate_op = pcap_activate_snit; + return (p); } int diff --git a/pcap-snoop.c b/pcap-snoop.c index e4545c4..bbcf140 100644 --- a/pcap-snoop.c +++ b/pcap-snoop.c @@ -20,7 +20,7 @@ */ #ifndef lint static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/pcap-snoop.c,v 1.55 2005-05-03 18:54:00 guy Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/pcap-snoop.c,v 1.56 2008-04-04 19:37:45 guy Exp $ (LBL)"; #endif #ifdef HAVE_CONFIG_H @@ -194,9 +194,8 @@ pcap_stats_snoop(pcap_t *p, struct pcap_stat *ps) } /* XXX can't disable promiscuous */ -pcap_t * -pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, - char *ebuf) +static int +pcap_activate_snoop(pcap_t *p) { int fd; struct sockaddr_raw sr; @@ -204,34 +203,34 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, u_int v; int ll_hdrlen; int snooplen; - pcap_t *p; struct ifreq ifr; - p = (pcap_t *)malloc(sizeof(*p)); - if (p == NULL) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s", - pcap_strerror(errno)); - return (NULL); + if (p->opt.rfmon) { + /* + * No monitor mode on Irix (no Wi-Fi devices on + * hardware supported by Irix). + */ + return (PCAP_ERROR_RFMON_NOTSUP); } - memset(p, 0, sizeof(*p)); + fd = socket(PF_RAW, SOCK_RAW, RAWPROTO_SNOOP); if (fd < 0) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "snoop socket: %s", + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "snoop socket: %s", pcap_strerror(errno)); goto bad; } p->fd = fd; memset(&sr, 0, sizeof(sr)); sr.sr_family = AF_RAW; - (void)strncpy(sr.sr_ifname, device, sizeof(sr.sr_ifname)); + (void)strncpy(sr.sr_ifname, p->opt.source, sizeof(sr.sr_ifname)); if (bind(fd, (struct sockaddr *)&sr, sizeof(sr))) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "snoop bind: %s", + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "snoop bind: %s", pcap_strerror(errno)); goto bad; } memset(&sf, 0, sizeof(sf)); if (ioctl(fd, SIOCADDSNOOP, &sf) < 0) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "SIOCADDSNOOP: %s", + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "SIOCADDSNOOP: %s", pcap_strerror(errno)); goto bad; } @@ -240,19 +239,19 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, /* * XXX hack - map device name to link layer type */ - if (strncmp("et", device, 2) == 0 || /* Challenge 10 Mbit */ - strncmp("ec", device, 2) == 0 || /* Indigo/Indy 10 Mbit, - O2 10/100 */ - strncmp("ef", device, 2) == 0 || /* O200/2000 10/100 Mbit */ - strncmp("eg", device, 2) == 0 || /* Octane/O2xxx/O3xxx Gigabit */ - strncmp("gfe", device, 3) == 0 || /* GIO 100 Mbit */ - strncmp("fxp", device, 3) == 0 || /* Challenge VME Enet */ - strncmp("ep", device, 2) == 0 || /* Challenge 8x10 Mbit EPLEX */ - strncmp("vfe", device, 3) == 0 || /* Challenge VME 100Mbit */ - strncmp("fa", device, 2) == 0 || - strncmp("qaa", device, 3) == 0 || - strncmp("cip", device, 3) == 0 || - strncmp("el", device, 2) == 0) { + if (strncmp("et", p->opt.source, 2) == 0 || /* Challenge 10 Mbit */ + strncmp("ec", p->opt.source, 2) == 0 || /* Indigo/Indy 10 Mbit, + O2 10/100 */ + strncmp("ef", p->opt.source, 2) == 0 || /* O200/2000 10/100 Mbit */ + strncmp("eg", p->opt.source, 2) == 0 || /* Octane/O2xxx/O3xxx Gigabit */ + strncmp("gfe", p->opt.source, 3) == 0 || /* GIO 100 Mbit */ + strncmp("fxp", p->opt.source, 3) == 0 || /* Challenge VME Enet */ + strncmp("ep", p->opt.source, 2) == 0 || /* Challenge 8x10 Mbit EPLEX */ + strncmp("vfe", p->opt.source, 3) == 0 || /* Challenge VME 100Mbit */ + strncmp("fa", p->opt.source, 2) == 0 || + strncmp("qaa", p->opt.source, 3) == 0 || + strncmp("cip", p->opt.source, 3) == 0 || + strncmp("el", p->opt.source, 2) == 0) { p->linktype = DLT_EN10MB; p->offset = RAW_HDRPAD(sizeof(struct ether_header)); ll_hdrlen = sizeof(struct ether_header); @@ -285,26 +284,26 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, p->dlt_list[1] = DLT_DOCSIS; p->dlt_count = 2; } - } else if (strncmp("ipg", device, 3) == 0 || - strncmp("rns", device, 3) == 0 || /* O2/200/2000 FDDI */ - strncmp("xpi", device, 3) == 0) { + } else if (strncmp("ipg", p->opt.source, 3) == 0 || + strncmp("rns", p->opt.source, 3) == 0 || /* O2/200/2000 FDDI */ + strncmp("xpi", p->opt.source, 3) == 0) { p->linktype = DLT_FDDI; p->offset = 3; /* XXX yeah? */ ll_hdrlen = 13; - } else if (strncmp("ppp", device, 3) == 0) { + } else if (strncmp("ppp", p->opt.source, 3) == 0) { p->linktype = DLT_RAW; ll_hdrlen = 0; /* DLT_RAW meaning "no PPP header, just the IP packet"? */ - } else if (strncmp("qfa", device, 3) == 0) { + } else if (strncmp("qfa", p->opt.source, 3) == 0) { p->linktype = DLT_IP_OVER_FC; ll_hdrlen = 24; - } else if (strncmp("pl", device, 2) == 0) { + } else if (strncmp("pl", p->opt.source, 2) == 0) { p->linktype = DLT_RAW; ll_hdrlen = 0; /* Cray UNICOS/mp pseudo link */ - } else if (strncmp("lo", device, 2) == 0) { + } else if (strncmp("lo", p->opt.source, 2) == 0) { p->linktype = DLT_NULL; ll_hdrlen = 4; } else { - snprintf(ebuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "snoop: unknown physical layer type"); goto bad; } @@ -315,9 +314,9 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, * the MTU first and, if that succeeds, trim the snap length * to be no greater than the MTU. */ - (void)strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name)); + (void)strncpy(ifr.ifr_name, p->opt.source, sizeof(ifr.ifr_name)); if (ioctl(fd, SIOCGIFMTU, (char *)&ifr) < 0) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "SIOCGIFMTU: %s", + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "SIOCGIFMTU: %s", pcap_strerror(errno)); goto bad; } @@ -338,8 +337,8 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, #ifndef ifr_mtu #define ifr_mtu ifr_metric #endif - if (snaplen > ifr.ifr_mtu + ll_hdrlen) - snaplen = ifr.ifr_mtu + ll_hdrlen; + if (p->snapshot > ifr.ifr_mtu + ll_hdrlen) + p->snapshot = ifr.ifr_mtu + ll_hdrlen; #endif /* @@ -347,18 +346,17 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, * payload bytes to capture - it doesn't count link-layer * header bytes. */ - snooplen = snaplen - ll_hdrlen; + snooplen = p->snapshot - ll_hdrlen; if (snooplen < 0) snooplen = 0; if (ioctl(fd, SIOCSNOOPLEN, &snooplen) < 0) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "SIOCSNOOPLEN: %s", + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "SIOCSNOOPLEN: %s", pcap_strerror(errno)); goto bad; } - p->snapshot = snaplen; v = 1; if (ioctl(fd, SIOCSNOOPING, &v) < 0) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "SIOCSNOOPING: %s", + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "SIOCSNOOPING: %s", pcap_strerror(errno)); goto bad; } @@ -366,7 +364,7 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, p->bufsize = 4096; /* XXX */ p->buffer = (u_char *)malloc(p->bufsize); if (p->buffer == NULL) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s", + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "malloc: %s", pcap_strerror(errno)); goto bad; } @@ -386,16 +384,23 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, p->stats_op = pcap_stats_snoop; p->close_op = pcap_close_common; - return (p); + return (0); bad: (void)close(fd); - /* - * Get rid of any link-layer type list we allocated. - */ - if (p->dlt_list != NULL) - free(p->dlt_list); - free(p); - return (NULL); + return (PCAP_ERROR); +} + +pcap_t * +pcap_create(const char *device, char *ebuf) +{ + pcap_t *p; + + p = pcap_create_common(device, ebuf); + if (p == NULL) + return (NULL); + + p->activate_op = pcap_activate_snoop; + return (p); } int diff --git a/pcap-usb-linux.c b/pcap-usb-linux.c index 2c5a254..7b447bc 100644 --- a/pcap-usb-linux.c +++ b/pcap-usb-linux.c @@ -34,7 +34,7 @@ */ #ifndef lint static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/pcap-usb-linux.c,v 1.21 2008-02-02 20:50:31 guy Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/pcap-usb-linux.c,v 1.22 2008-04-04 19:37:45 guy Exp $ (LBL)"; #endif #ifdef HAVE_CONFIG_H @@ -117,6 +117,7 @@ struct mon_bin_mfetch { #define MON_BIN_ERROR 0x8 /* forward declaration */ +static int usb_activate(pcap_t *); static int usb_stats_linux(pcap_t *, struct pcap_stat *); static int usb_stats_linux_bin(pcap_t *, struct pcap_stat *); static int usb_read_linux(pcap_t *, int , pcap_handler , u_char *); @@ -183,25 +184,26 @@ int usb_mmap(pcap_t* handle) return handle->buffer != MAP_FAILED; } -pcap_t* -usb_open_live(const char* bus, int snaplen, int promisc , int to_ms, char* errmsg) +pcap_t * +usb_create(const char *device, char *ebuf) +{ + pcap_t *p; + + p = pcap_create_common(device, ebuf); + if (p == NULL) + return (NULL); + + p->activate_op = usb_activate; + return (p); +} + +static int +usb_activate(pcap_t* handle) { char full_path[USB_LINE_LEN]; - pcap_t *handle; - - /* Allocate a handle for this session. */ - handle = malloc(sizeof(*handle)); - if (handle == NULL) { - snprintf(errmsg, PCAP_ERRBUF_SIZE, "malloc: %s", - pcap_strerror(errno)); - return NULL; - } /* Initialize some components of the pcap structure. */ - memset(handle, 0, sizeof(*handle)); - handle->snapshot = snaplen; - handle->md.timeout = to_ms; - handle->bufsize = snaplen; + handle->bufsize = handle->snapshot; handle->offset = 0; handle->linktype = DLT_USB_LINUX; @@ -214,12 +216,11 @@ usb_open_live(const char* bus, int snaplen, int promisc , int to_ms, char* errms handle->close_op = usb_close_linux; /*get usb bus index from device name */ - if (sscanf(bus, USB_IFACE"%d", &handle->md.ifindex) != 1) + if (sscanf(handle->opt.source, USB_IFACE"%d", &handle->md.ifindex) != 1) { - snprintf(errmsg, PCAP_ERRBUF_SIZE, - "Can't get USB bus index from %s", bus); - free(handle); - return NULL; + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + "Can't get USB bus index from %s", handle->opt.source); + return -1; } /*now select the read method: try to open binary interface */ @@ -238,7 +239,7 @@ usb_open_live(const char* bus, int snaplen, int promisc , int to_ms, char* errms * "poll()" work on it. */ handle->selectable_fd = handle->fd; - return handle; + return 0; } /* can't mmap, use plain binary interface access */ @@ -252,10 +253,9 @@ usb_open_live(const char* bus, int snaplen, int promisc , int to_ms, char* errms if (handle->fd < 0) { /* no more fallback, give it up*/ - snprintf(errmsg, PCAP_ERRBUF_SIZE, + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Can't open USB bus file %s: %s", full_path, strerror(errno)); - free(handle); - return NULL; + return -1; } handle->stats_op = usb_stats_linux; handle->read_op = usb_read_linux; @@ -271,12 +271,12 @@ usb_open_live(const char* bus, int snaplen, int promisc , int to_ms, char* errms * buffer */ handle->buffer = malloc(handle->bufsize); if (!handle->buffer) { - snprintf(errmsg, PCAP_ERRBUF_SIZE, + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "malloc: %s", pcap_strerror(errno)); - usb_close_linux(handle); - return NULL; + close(handle->fd); + return -1; } - return handle; + return 0; } static inline int diff --git a/pcap-usb-linux.h b/pcap-usb-linux.h index ed7f2aa..2d9638c 100644 --- a/pcap-usb-linux.h +++ b/pcap-usb-linux.h @@ -30,11 +30,11 @@ * USB sniffing API implementation for Linux platform * By Paolo Abeni * - * @(#) $Header: /tcpdump/master/libpcap/pcap-usb-linux.h,v 1.4 2007-09-14 01:55:49 guy Exp $ (LBL) + * @(#) $Header: /tcpdump/master/libpcap/pcap-usb-linux.h,v 1.5 2008-04-04 19:37:45 guy Exp $ (LBL) */ /* * Prototypes for USB-related functions */ int usb_platform_finddevs(pcap_if_t **alldevsp, char *err_str); -pcap_t* usb_open_live(const char* bus, int snaplen, int promisc , int to_ms, char* errmsg); +pcap_t *usb_create(const char *device, char *ebuf); diff --git a/pcap-win32.c b/pcap-win32.c index 0faa96e..09a2895 100644 --- a/pcap-win32.c +++ b/pcap-win32.c @@ -33,7 +33,7 @@ #ifndef lint static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/pcap-win32.c,v 1.36 2007-11-13 21:55:51 gianluca Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/pcap-win32.c,v 1.37 2008-04-04 19:37:45 guy Exp $ (LBL)"; #endif #include @@ -430,39 +430,36 @@ pcap_close_win32(pcap_t *p) } } -pcap_t * -pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, - char *ebuf) +static int +pcap_activate_win32(pcap_t *p) { - register pcap_t *p; NetType type; + if (p->opt.rfmon) { + /* + * No monitor mode on Windows. It could be done on + * Vista with drivers that support the native 802.11 + * mechanism and monitor mode. + */ + return (PCAP_ERROR_RFMON_NOTSUP); + } + /* Init WinSock */ wsockinit(); - p = (pcap_t *)malloc(sizeof(*p)); - if (p == NULL) - { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s", pcap_strerror(errno)); - return (NULL); - } - memset(p, 0, sizeof(*p)); - p->adapter=NULL; - - p->adapter = PacketOpenAdapter((char*)device); + p->adapter = PacketOpenAdapter(p->opt.source); if (p->adapter == NULL) { - free(p); /* Adapter detected but we are not able to open it. Return failure. */ - snprintf(ebuf, PCAP_ERRBUF_SIZE, "Error opening adapter: %s", pcap_win32strerror()); - return NULL; + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Error opening adapter: %s", pcap_win32strerror()); + return PCAP_ERROR; } /*get network type*/ if(PacketGetNetType (p->adapter,&type) == FALSE) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "Cannot determine the network type: %s", pcap_win32strerror()); + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Cannot determine the network type: %s", pcap_win32strerror()); goto bad; } @@ -546,12 +543,12 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, } /* Set promiscuous mode */ - if (promisc) + if (p->opt.promisc) { if (PacketSetHwFilter(p->adapter,NDIS_PACKET_TYPE_PROMISCUOUS) == FALSE) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "failed to set hardware filter to promiscuous mode"); + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "failed to set hardware filter to promiscuous mode"); goto bad; } } @@ -559,7 +556,7 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, { if (PacketSetHwFilter(p->adapter,NDIS_PACKET_TYPE_ALL_LOCAL) == FALSE) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "failed to set hardware filter to non-promiscuous mode"); + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "failed to set hardware filter to non-promiscuous mode"); goto bad; } } @@ -568,12 +565,12 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, p->bufsize = PcapBufSize; /* Store the timeout. Used by pcap_setnonblock() */ - p->timeout= to_ms; + p->md.timeout= to_ms; /* allocate Packet structure used during the capture */ if((p->Packet = PacketAllocatePacket())==NULL) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "failed to allocate the PACKET structure"); + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "failed to allocate the PACKET structure"); goto bad; } @@ -582,11 +579,22 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, /* * Traditional Adapter */ + /* + * If the buffer size wasn't explicitly set, default to + * SIZE_BUF. + */ + if (p->opt.buffer_size == 0) + p->opt.buffer_size = SIZE_BUF; + if(PacketSetBuff(p->adapter,p->opt.buffer_size)==FALSE) + { + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "driver error: not enough memory to allocate the kernel buffer"); + goto bad; + } p->buffer = (u_char *)malloc(PcapBufSize); if (p->buffer == NULL) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s", pcap_strerror(errno)); + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "malloc: %s", pcap_strerror(errno)); goto bad; } @@ -597,14 +605,14 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, /* allocate the standard buffer in the driver */ if(PacketSetBuff( p->adapter, SIZE_BUF)==FALSE) { - snprintf(ebuf, PCAP_ERRBUF_SIZE,"driver error: not enough memory to allocate the kernel buffer\n"); + snprintf(p->errbuf, PCAP_ERRBUF_SIZE,"driver error: not enough memory to allocate the kernel buffer\n"); goto bad; } /* tell the driver to copy the buffer only if it contains at least 16K */ if(PacketSetMinToCopy(p->adapter,16000)==FALSE) { - snprintf(ebuf, PCAP_ERRBUF_SIZE,"Error calling PacketSetMinToCopy: %s\n", pcap_win32strerror()); + snprintf(p->errbuf, PCAP_ERRBUF_SIZE,"Error calling PacketSetMinToCopy: %s\n", pcap_win32strerror()); goto bad; } } @@ -623,7 +631,7 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, snprintf(keyname, sizeof(keyname), "%s\\CardParams\\%s", "SYSTEM\\CurrentControlSet\\Services\\DAG", - strstr(_strlwr((char*)device), "dag")); + strstr(_strlwr(p->opt.source), "dag")); do { status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyname, 0, KEY_READ, &dagkey); @@ -687,23 +695,31 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, p->setmintocopy_op = pcap_setmintocopy_win32; p->close_op = pcap_close_win32; - return (p); + return (0); bad: if (p->adapter) PacketCloseAdapter(p->adapter); - if (p->buffer != NULL) + if (p->buffer != NULL) { free(p->buffer); + p->buffer = NULL; + } if(p->Packet) PacketFreePacket(p->Packet); - /* - * Get rid of any link-layer type list we allocated. - */ - if (p->dlt_list != NULL) - free(p->dlt_list); - free(p); - return (NULL); + return (PCAP_ERROR); } +pcap_t * +pcap_create(const char *device, char *ebuf) +{ + pcap_t *p; + + p = pcap_create_common(device, ebuf); + if (p == NULL) + return (NULL); + + p->activate_op = pcap_activate_win32; + return (p); +} static int pcap_setfilter_win32_npf(pcap_t *p, struct bpf_program *fp) @@ -780,7 +796,7 @@ pcap_setnonblock_win32(pcap_t *p, int nonblock, char *errbuf) * (Note that this may be -1, in which case we're not * really leaving non-blocking mode.) */ - newtimeout = p->timeout; + newtimeout = p->md.timeout; } if (!PacketSetReadTimeout(p->adapter, newtimeout)) { snprintf(p->errbuf, PCAP_ERRBUF_SIZE, diff --git a/pcap.c b/pcap.c index e8a4cab..270e3a9 100644 --- a/pcap.c +++ b/pcap.c @@ -33,7 +33,7 @@ #ifndef lint static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/pcap.c,v 1.114 2007-11-06 16:20:53 gianluca Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/pcap.c,v 1.115 2008-04-04 19:37:45 guy Exp $ (LBL)"; #endif #ifdef HAVE_CONFIG_H @@ -70,10 +70,192 @@ static const char rcsid[] _U_ = #include #endif +int +pcap_not_initialized(pcap_t *pcap) +{ + /* this means 'not initialized' */ + return PCAP_ERROR_NOT_ACTIVATED; +} + +/* + * Returns 1 if rfmon mode can be set on the pcap_t, 0 if it can't, + * a PCAP_ERROR value on an error. + */ +int +pcap_can_set_rfmon(pcap_t *p) +{ + return (p->can_set_rfmon_op(p)); +} + +/* + * For systems where rfmon mode is never supported. + */ +static int +pcap_cant_set_rfmon(pcap_t *p _U_) +{ + return (0); +} + +pcap_t * +pcap_create_common(const char *source, char *ebuf) +{ + pcap_t *p; + + p = malloc(sizeof(*p)); + if (p == NULL) { + snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s", + pcap_strerror(errno)); + return (NULL); + } + memset(p, 0, sizeof(*p)); + + p->opt.source = strdup(source); + if (p->opt.source == NULL) { + snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s", + pcap_strerror(errno)); + free(p); + return (NULL); + } + + /* + * Default to "can't set rfmon mode"; if it's supported by + * a platform, it can set the op to its routine to check + * whether a particular device supports it. + */ + p->can_set_rfmon_op = pcap_cant_set_rfmon; + + /* + * Some operations can be performed only on activated pcap_t's; + * have those operations handled by a "not supported" handler + * until the pcap_t is activated. + */ + p->read_op = (read_op_t)pcap_not_initialized; + p->inject_op = (inject_op_t)pcap_not_initialized; + p->setfilter_op = (setfilter_op_t)pcap_not_initialized; + p->setdirection_op = (setdirection_op_t)pcap_not_initialized; + p->set_datalink_op = (set_datalink_op_t)pcap_not_initialized; + p->getnonblock_op = (getnonblock_op_t)pcap_not_initialized; + p->setnonblock_op = (setnonblock_op_t)pcap_not_initialized; + p->stats_op = (stats_op_t)pcap_not_initialized; +#ifdef WIN32 + p->setbuff_op = (setbuff_op_t)pcap_not_initialized; + p->setmode_op = (setmode_op_t)pcap_not_initialized; + p->setmintocopy_op = (setmintocopy_op_t)pcap_not_initialized; +#endif + p->close_op = (close_op_t)pcap_close_common; + + /* put in some defaults*/ + pcap_set_timeout(p, 0); + pcap_set_snaplen(p, 65535); /* max packet size */ + p->opt.promisc = 0; + p->opt.buffer_size = 0; + return (p); +} + +int +pcap_check_activated(pcap_t *p) +{ + if (p->activated) { + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "can't perform " + " operation on activated capture"); + return -1; + } + return 0; +} + +int +pcap_set_snaplen(pcap_t *p, int snaplen) +{ + if (pcap_check_activated(p)) + return PCAP_ERROR_ACTIVATED; + p->snapshot = snaplen; + return 0; +} + +int +pcap_set_promisc(pcap_t *p, int promisc) +{ + if (pcap_check_activated(p)) + return PCAP_ERROR_ACTIVATED; + p->opt.promisc = promisc; + return 0; +} + +int +pcap_set_rfmon(pcap_t *p, int rfmon) +{ + if (pcap_check_activated(p)) + return PCAP_ERROR_ACTIVATED; + p->opt.rfmon = rfmon; + return 0; +} + +int +pcap_set_timeout(pcap_t *p, int timeout_ms) +{ + if (pcap_check_activated(p)) + return PCAP_ERROR_ACTIVATED; + p->md.timeout = timeout_ms; + return 0; +} + +int +pcap_set_buffer_size(pcap_t *p, int buffer_size) +{ + if (pcap_check_activated(p)) + return PCAP_ERROR_ACTIVATED; + p->opt.buffer_size = buffer_size; + return 0; +} + +int +pcap_activate(pcap_t *p) +{ + int err; + + err = p->activate_op(p); + if (err == 0) + p->activated = 1; + return (err); +} + +pcap_t * +pcap_open_live(const char *source, int snaplen, int promisc, int to_ms, char *errbuf) +{ + pcap_t *p; + + p = pcap_create(source, errbuf); + if (p == NULL) + return (NULL); + if (pcap_set_snaplen(p, snaplen)) + goto fail; + if (pcap_set_promisc(p, promisc)) + goto fail; + if (pcap_set_timeout(p, to_ms)) + goto fail; + /* + * Mark this as opened with pcap_open_live(), so that, for + * example, we show the full list of DLT_ values, rather + * than just the ones that are compatible with capturing + * when not in monitor mode. That allows existing applications + * to work the way they used to work, but allows new applications + * that know about the new open API to, for example, find out the + * DLT_ values that they can select without changing whether + * the adapter is in monitor mode or not. + */ + p->oldstyle = 1; + if (pcap_activate(p)) + goto fail; + return (p); +fail: + strncpy(errbuf, p->errbuf, PCAP_ERRBUF_SIZE); + pcap_close(p); + return (NULL); +} + int pcap_dispatch(pcap_t *p, int cnt, pcap_handler callback, u_char *user) { - return p->read_op(p, cnt, callback, user); } @@ -702,22 +884,57 @@ pcap_win32strerror(void) /* * Not all systems have strerror(). + * We also use this to generate error strings for PCAP_ERROR_ values. */ const char * pcap_strerror(int errnum) { -#ifdef HAVE_STRERROR - return (strerror(errnum)); -#else + static char ebuf[128+1]; +#ifndef HAVE_STRERROR extern int sys_nerr; extern const char *const sys_errlist[]; - static char ebuf[20]; +#endif - if ((unsigned int)errnum < sys_nerr) - return ((char *)sys_errlist[errnum]); + switch (errnum) { + + case PCAP_ERROR: + (void)snprintf(ebuf, sizeof ebuf, "Generic error"); + return(ebuf); + + case PCAP_ERROR_BREAK: + (void)snprintf(ebuf, sizeof ebuf, "Loop terminated by pcap_breakloop"); + return(ebuf); + + case PCAP_ERROR_NOT_ACTIVATED: + (void)snprintf(ebuf, sizeof ebuf, "The pcap_t has not been activated"); + return(ebuf); + + case PCAP_ERROR_ACTIVATED: + (void)snprintf(ebuf, sizeof ebuf, "The setting can't be changed after the pcap_t is activated"); + return(ebuf); + + case PCAP_ERROR_NO_SUCH_DEVICE: + (void)snprintf(ebuf, sizeof ebuf, "No such device exists"); + return(ebuf); + + case PCAP_ERROR_RFMON_NOTSUP: + (void)snprintf(ebuf, sizeof ebuf, "That device doesn't support monitor mode"); + return(ebuf); + + case PCAP_ERROR_NOT_RFMON: + (void)snprintf(ebuf, sizeof ebuf, "That operation is supported only in monitor mode"); + return(ebuf); + } + if (errnum >= 0) { +#ifdef HAVE_STRERROR + return (strerror(errnum)); +#else + if ((unsigned int)errnum < sys_nerr) + return ((char *)sys_errlist[errnum]); +#endif + } (void)snprintf(ebuf, sizeof ebuf, "Unknown error: %d", errnum); return(ebuf); -#endif } int @@ -801,11 +1018,102 @@ pcap_setmintocopy_dead(pcap_t *p, int size) } #endif +/* + * On some platforms, we need to clean up promiscuous or monitor mode + * when we close a device - and we want that to happen even if the + * application just exits without explicitl closing devices. + * On those platforms, we need to register a "close all the pcaps" + * routine to be called when we exit, and need to maintain a list of + * pcaps that need to be closed to clean up modes. + * + * XXX - not thread-safe. + */ + +/* + * List of pcaps on which we've done something that needs to be + * cleaned up. + * If there are any such pcaps, we arrange to call "pcap_close_all()" + * when we exit, and have it close all of them. + */ +static struct pcap *pcaps_to_close; + +/* + * TRUE if we've already called "atexit()" to cause "pcap_close_all()" to + * be called on exit. + */ +static int did_atexit; + +static void +pcap_close_all(void) +{ + struct pcap *handle; + + while ((handle = pcaps_to_close) != NULL) + pcap_close(handle); +} + +int +pcap_do_addexit(pcap_t *p) +{ + /* + * If we haven't already done so, arrange to have + * "pcap_close_all()" called when we exit. + */ + if (!did_atexit) { + if (atexit(pcap_close_all) == -1) { + /* + * "atexit()" failed; let our caller know. + */ + strncpy(p->errbuf, "atexit failed", + PCAP_ERRBUF_SIZE); + return (0); + } + did_atexit = 1; + } + return (1); +} + +void +pcap_add_to_pcaps_to_close(pcap_t *p) +{ + p->md.next = pcaps_to_close; + pcaps_to_close = p; +} + +void +pcap_remove_from_pcaps_to_close(pcap_t *p) +{ + pcap_t *pc, *prevpc; + + for (pc = pcaps_to_close, prevpc = NULL; pc != NULL; + prevpc = pc, pc = pc->md.next) { + if (pc == p) { + /* + * Found it. Remove it from the list. + */ + if (prevpc == NULL) { + /* + * It was at the head of the list. + */ + pcaps_to_close = pc->md.next; + } else { + /* + * It was in the middle of the list. + */ + prevpc->md.next = pc->md.next; + } + break; + } + } +} + void pcap_close_common(pcap_t *p) { if (p->buffer != NULL) free(p->buffer); + if (p->opt.source != NULL); + free(p->opt.source); #if !defined(WIN32) && !defined(MSDOS) if (p->fd >= 0) close(p->fd); @@ -836,6 +1144,7 @@ pcap_open_dead(int linktype, int snaplen) p->setmintocopy_op = pcap_setmintocopy_dead; #endif p->close_op = pcap_close_dead; + p->activated = 1; return p; } diff --git a/pcap/pcap.h b/pcap/pcap.h index cdbac4a..e9d5daa 100644 --- a/pcap/pcap.h +++ b/pcap/pcap.h @@ -31,7 +31,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#) $Header: /tcpdump/master/libpcap/pcap/pcap.h,v 1.5 2008-01-02 04:16:46 guy Exp $ (LBL) + * @(#) $Header: /tcpdump/master/libpcap/pcap/pcap.h,v 1.6 2008-04-04 19:37:45 guy Exp $ (LBL) */ #ifndef lib_pcap_pcap_h @@ -226,8 +226,27 @@ struct pcap_addr { typedef void (*pcap_handler)(u_char *, const struct pcap_pkthdr *, const u_char *); +/* list of known error code for pcap API */ +#define PCAP_ERROR -1 /* generic error code */ +#define PCAP_ERROR_BREAK -2 /* loop terminated by pcap_breakloop */ +#define PCAP_ERROR_NOT_ACTIVATED -3 /* the capture needs to be activated */ +#define PCAP_ERROR_ACTIVATED -4 /* the operation can't be performed on already activated captures */ +#define PCAP_ERROR_NO_SUCH_DEVICE -5 /* no such device exists */ +#define PCAP_ERROR_RFMON_NOTSUP -6 /* this device doesn't support rfmon (monitor) mode */ +#define PCAP_ERROR_NOT_RFMON -7 /* operation supported only in monitor mode */ + char *pcap_lookupdev(char *); int pcap_lookupnet(const char *, bpf_u_int32 *, bpf_u_int32 *, char *); + +pcap_t *pcap_create(const char *, char *); +int pcap_set_snaplen(pcap_t *, int); +int pcap_set_promisc(pcap_t *, int); +int pcap_can_set_rfmon(pcap_t *); +int pcap_set_rfmon(pcap_t *, int); +int pcap_set_timeout(pcap_t *, int); +int pcap_set_buffer_size(pcap_t *, int); +int pcap_activate(pcap_t *); + pcap_t *pcap_open_live(const char *, int, int, int, char *); pcap_t *pcap_open_dead(int, int); pcap_t *pcap_open_offline(const char *, char *); diff --git a/savefile.c b/savefile.c index f62502e..142458c 100644 --- a/savefile.c +++ b/savefile.c @@ -30,7 +30,7 @@ #ifndef lint static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/savefile.c,v 1.172 2008-02-18 20:21:00 guy Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/savefile.c,v 1.173 2008-04-04 19:37:45 guy Exp $ (LBL)"; #endif #ifdef HAVE_CONFIG_H @@ -1310,6 +1310,7 @@ pcap_fopen_offline(FILE *fp, char *errbuf) p->setmintocopy_op = sf_setmintocopy; #endif p->close_op = sf_close; + p->activated = 1; return (p); bad: