From 8093f5e607f42cbabe8359e77f8982ec908b2bac Mon Sep 17 00:00:00 2001 From: Lawrence Teo Date: Fri, 25 May 2012 01:58:09 +0000 Subject: Import a number of core functions from libpcap-1.2.0 while preserving local changes: strncpy() -> strlcpy(), malloc(x * y) -> calloc(x, y), exclude cross-platform cruft, etc. The new functions are pcap_create(), pcap_set_snaplen(), pcap_set_promisc(), pcap_can_set_rfmon(), pcap_set_rfmon(), pcap_set_timeout(), pcap_set_buffer_size(), pcap_activate(), and pcap_statustostr(). This diff was tested on amd64, i386, macppc, and sparc64, where regression tests were done on various pcap-based ports (especially amd64 and i386 where regression tests were run on all pcap-based ports). Testers also tried running pcap-based ports that they are familiar with to ensure that there is no behavioral change. tcpdump and pflogd in base were also tested by different testers. The new pcap_* functions were tested with a proof-of-concept Snort 2.9 port for many months. Thank you to everyone who helped test this diff and provided feedback: haesbaert@, sthen@, matthew@, gonzalo@, brett@, Rodolfo Gouveia, Aaron Bieber, Markus Lude, and Ray Percival. ok haesbaert sthen henning --- lib/libpcap/Makefile | 10 +- lib/libpcap/pcap-bpf.c | 777 +++++++++++++++++++++++++++++++++++++++++----- lib/libpcap/pcap-int.h | 32 +- lib/libpcap/pcap.3 | 73 ++++- lib/libpcap/pcap.c | 225 ++++++++++++-- lib/libpcap/pcap.h | 42 ++- lib/libpcap/savefile.c | 3 +- lib/libpcap/shlib_version | 2 +- 8 files changed, 1050 insertions(+), 114 deletions(-) diff --git a/lib/libpcap/Makefile b/lib/libpcap/Makefile index 3519f88efa5..3673d2997ce 100644 --- a/lib/libpcap/Makefile +++ b/lib/libpcap/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.20 2009/08/13 19:54:58 jmc Exp $ +# $OpenBSD: Makefile,v 1.21 2012/05/25 01:58:08 lteo Exp $ # $NetBSD: Makefile,v 1.3 1996/05/10 21:54:24 cgd Exp $ LIB= pcap @@ -25,11 +25,15 @@ MLINKS= pcap.3 pcap_open_live.3 pcap.3 pcap_open_offline.3 \ pcap.3 pcap_sendpacket.3 pcap.3 pcap_next_ex.3 \ pcap.3 pcap_setdirection.3 pcap.3 pcap_dump_file.3 \ pcap.3 pcap_dump_ftell.3 pcap.3 pcap_fopen_offline.3 \ - pcap.3 pcap_dump_flush.3 + pcap.3 pcap_dump_flush.3 pcap.3 pcap_create.3 \ + pcap.3 pcap_set_snaplen.3 pcap.3 pcap_set_promisc.3 \ + pcap.3 pcap_can_set_rfmon.3 pcap.3 pcap_set_rfmon.3 \ + pcap.3 pcap_set_timeout.3 pcap.3 pcap_set_buffer_size.3 \ + pcap.3 pcap_activate.3 pcap.3 pcap_statustostr.3 DEFS= -DHAVE_SYS_IOCCOM_H -DHAVE_SYS_SOCKIO_H -DHAVE_ETHER_HOSTTON \ -DHAVE_STRERROR -DHAVE_SOCKADDR_SA_LEN -DLBL_ALIGN -DHAVE_IFADDRS_H \ - -DINET6 + -DINET6 -DHAVE_BSD_IEEE80211 CFLAGS+=-I. -I${.CURDIR} -Dyylval=pcap_yylval ${DEFS} diff --git a/lib/libpcap/pcap-bpf.c b/lib/libpcap/pcap-bpf.c index bc988b1325f..afd2de20e0f 100644 --- a/lib/libpcap/pcap-bpf.c +++ b/lib/libpcap/pcap-bpf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pcap-bpf.c,v 1.20 2006/03/26 20:58:51 djm Exp $ */ +/* $OpenBSD: pcap-bpf.c,v 1.21 2012/05/25 01:58:08 lteo Exp $ */ /* * Copyright (c) 1993, 1994, 1995, 1996, 1998 @@ -38,6 +38,8 @@ #include #include +#include + #include "pcap-int.h" #ifdef HAVE_OS_PROTO_H @@ -46,6 +48,9 @@ #include "gencode.h" +static int find_802_11(struct bpf_dltlist *); +static int monitor_mode(pcap_t *, int); + int pcap_stats(pcap_t *p, struct pcap_stat *ps) { @@ -54,7 +59,7 @@ pcap_stats(pcap_t *p, struct pcap_stat *ps) if (ioctl(p->fd, BIOCGSTATS, (caddr_t)&s) < 0) { snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCGSTATS: %s", pcap_strerror(errno)); - return (-1); + return (PCAP_ERROR); } ps->ps_recv = s.bs_recv; @@ -76,11 +81,11 @@ pcap_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) if (p->break_loop) { /* * Yes - clear the flag that indicates that it - * has, and return -2 to indicate that we were - * told to break out of the loop. + * has, and return PCAP_ERROR_BREAK to indicate + * that we were told to break out of the loop. */ p->break_loop = 0; - return (-2); + return (PCAP_ERROR_BREAK); } cc = p->cc; @@ -95,6 +100,21 @@ pcap_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) case EWOULDBLOCK: return (0); + + case ENXIO: + /* + * The device on which we're capturing + * went away. + * + * XXX - we should really return + * PCAP_ERROR_IFACE_NOT_UP, but + * pcap_dispatch() etc. aren't + * defined to return that. + */ + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "The interface went down"); + return (PCAP_ERROR); + #if defined(sun) && !defined(BSD) /* * Due to a SunOS bug, after 2^31 bytes, the kernel @@ -112,7 +132,7 @@ pcap_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) } snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "read: %s", pcap_strerror(errno)); - return (-1); + return (PCAP_ERROR); } bp = p->buffer; } else @@ -129,21 +149,36 @@ pcap_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) /* * Has "pcap_breakloop()" been called? * If so, return immediately - if we haven't read any - * packets, clear the flag and return -2 to indicate - * that we were told to break out of the loop, otherwise - * leave the flag set, so that the *next* call will break - * out of the loop without having read any packets, and - * return the number of packets we've processed so far. + * packets, clear the flag and return PCAP_ERROR_BREAK + * to indicate that we were told to break out of the loop, + * otherwise leave the flag set, so that the *next* call + * will break out of the loop without having read any + * packets, and return the number of packets we've + * processed so far. */ if (p->break_loop) { + p->bp = bp; + p->cc = ep - bp; + /* + * ep is set based on the return value of read(), + * but read() from a BPF device doesn't necessarily + * return a value that's a multiple of the alignment + * value for BPF_WORDALIGN(). However, whenever we + * increment bp, we round up the increment value by + * a value rounded up by BPF_WORDALIGN(), so we + * could increment bp past ep after processing the + * last packet in the buffer. + * + * We treat ep < bp as an indication that this + * happened, and just set p->cc to 0. + */ + if (p->cc < 0) + p->cc = 0; if (n == 0) { p->break_loop = 0; - return (-2); - } else { - p->bp = bp; - p->cc = ep - bp; + return (PCAP_ERROR_BREAK); + } else return (n); - } } caplen = bhp->bh_caplen; @@ -178,7 +213,7 @@ pcap_sendpacket(pcap_t *p, const u_char *buf, int size) /* ARGSUSED */ static __inline int -bpf_open(pcap_t *p, char *errbuf) +bpf_open(pcap_t *p) { int fd; int n = 0; @@ -197,50 +232,276 @@ bpf_open(pcap_t *p, char *errbuf) /* * XXX better message for all minors used */ - if (fd < 0) - snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: %s", - device, pcap_strerror(errno)); + if (fd < 0) { + switch (errno) { + + case ENOENT: + fd = PCAP_ERROR; + if (n == 1) { + /* + * /dev/bpf0 doesn't exist, which + * means we probably have no BPF + * devices. + */ + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "(there are no BPF devices)"); + } else { + /* + * We got EBUSY on at least one + * BPF device, so we have BPF + * devices, but all the ones + * that exist are busy. + */ + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "(all BPF devices are busy)"); + } + break; + + case EACCES: + /* + * Got EACCES on the last device we tried, + * and EBUSY on all devices before that, + * if any. + */ + fd = PCAP_ERROR_PERM_DENIED; + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "(cannot open BPF device) %s: %s", device, + pcap_strerror(errno)); + break; + + default: + /* + * Some other problem. + */ + fd = PCAP_ERROR; + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "(cannot open BPF device) %s: %s", device, + pcap_strerror(errno)); + break; + } + } return (fd); } -pcap_t * -pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, - char *ebuf) +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) { + bdlp->bfl_list = (u_int *) calloc(bdlp->bfl_len + 1, sizeof(u_int)); + if (bdlp->bfl_list == NULL) { + (void)snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s", + pcap_strerror(errno)); + return (PCAP_ERROR); + } + + if (ioctl(fd, BIOCGDLTLIST, (caddr_t)bdlp) < 0) { + (void)snprintf(ebuf, PCAP_ERRBUF_SIZE, + "BIOCGDLTLIST: %s", pcap_strerror(errno)); + free(bdlp->bfl_list); + return (PCAP_ERROR); + } + } 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 (PCAP_ERROR); + } + } + return (0); +} + +/* + * 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) { +#if 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 void +pcap_cleanup_bpf(pcap_t *p) +{ +#ifdef HAVE_BSD_IEEE80211 + int sock; + struct ifmediareq req; + struct ifreq ifr; +#endif + + if (p->md.must_do_on_close != 0) { + /* + * There's something we have to do when closing this + * pcap_t. + */ +#ifdef HAVE_BSD_IEEE80211 + if (p->md.must_do_on_close & MUST_CLEAR_RFMON) { + /* + * 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)); + (void)strlcpy(req.ifm_name, p->opt.source, + 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)strlcpy(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) { + fprintf(stderr, + "Can't restore interface flags " + "(SIOCSIFMEDIA failed: %s).\n" + "Please adjust manually.\n", + strerror(errno)); + } + } + close(sock); + } + } +#endif /* HAVE_BSD_IEEE80211 */ + + /* + * Take this pcap out of the list of pcaps for which we + * have to take the interface out of some mode. + */ + pcap_remove_from_pcaps_to_close(p); + p->md.must_do_on_close = 0; + } + + /*XXX*/ + if (p->fd >= 0) { + close(p->fd); + p->fd = -1; + } + if (p->sf.rfile != NULL) { + (void)fclose(p->sf.rfile); + if (p->sf.base != NULL) + free(p->sf.base); + } else if (p->buffer != NULL) + free(p->buffer); + pcap_freecode(&p->fcode); + if (p->dlt_list != NULL) { + free(p->dlt_list); + p->dlt_list = NULL; + p->dlt_count = 0; + } +} + +void +pcap_close(pcap_t *p) +{ + if (p->opt.source != NULL) + free(p->opt.source); + pcap_cleanup_bpf(p); + free(p); +} + + +static int +check_setif_failure(pcap_t *p, int error) +{ + if (error == ENXIO) { + /* + * No such device. + */ + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCSETIF failed: %s", + pcap_strerror(errno)); + return (PCAP_ERROR_NO_SUCH_DEVICE); + } else if (errno == ENETDOWN) { + /* + * Return a "network down" indication, so that + * the application can report that rather than + * saying we had a mysterious failure and + * suggest that they report a problem to the + * libpcap developers. + */ + return (PCAP_ERROR_IFACE_NOT_UP); + } else { + /* + * Some other error; fill in the error string, and + * return PCAP_ERROR. + */ + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCSETIF: %s: %s", + p->opt.source, pcap_strerror(errno)); + return (PCAP_ERROR); + } +} + +int +pcap_activate(pcap_t *p) +{ + int status = 0; int fd; struct ifreq ifr; struct bpf_version bv; - u_int v; - pcap_t *p; struct bpf_dltlist bdl; + int new_dlt; + u_int v; - bzero(&bdl, sizeof(bdl)); - p = (pcap_t *)malloc(sizeof(*p)); - if (p == NULL) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s", - pcap_strerror(errno)); - return (NULL); - } - bzero(p, sizeof(*p)); - fd = bpf_open(p, ebuf); - if (fd < 0) + fd = bpf_open(p); + if (fd < 0) { + status = fd; 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)); + status = 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"); + status = PCAP_ERROR; goto bad; } + #if 0 /* Just use the kernel default */ v = 32768; /* XXX this should be a user-accessible hook */ @@ -252,16 +513,36 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, (void) ioctl(fd, BIOCSBLEN, (caddr_t)&v); #endif - (void)strlcpy(ifr.ifr_name, device, sizeof(ifr.ifr_name)); + /* + * Set the buffer size. + */ + if (p->opt.buffer_size != 0) { + /* + * A buffer size was explicitly specified; use it. + */ + 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)); + status = PCAP_ERROR; + goto bad; + } + } + + /* + * Now bind to the device. + */ + (void)strlcpy(ifr.ifr_name, p->opt.source, sizeof(ifr.ifr_name)); if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) < 0) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "%s: %s", - device, pcap_strerror(errno)); + status = check_setif_failure(p, errno); goto bad; } /* Get the data link layer type. */ if (ioctl(fd, BIOCGDLT, (caddr_t)&v) < 0) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCGDLT: %s", + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCGDLT: %s", pcap_strerror(errno)); + status = PCAP_ERROR; goto bad; } #if _BSDI_VERSION - 0 >= 199510 @@ -275,72 +556,418 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, case DLT_PPP: v = DLT_PPP_BSDOS; break; + + case 11: /*DLT_FR*/ + v = DLT_FRELAY; + break; + + case 12: /*DLT_C_HDLC*/ + v = DLT_CHDLC; + break; } #endif - p->linktype = v; /* * 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. */ - if (ioctl(fd, BIOCGDLTLIST, (caddr_t)&bdl) == 0) { - bdl.bfl_list = (u_int *) calloc(bdl.bfl_len + 1, sizeof(u_int)); - if (bdl.bfl_list == NULL) { - (void)snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s", - pcap_strerror(errno)); - goto bad; - } + if (get_dlt_list(fd, v, &bdl, p->errbuf) == -1) { + status = PCAP_ERROR; + goto bad; + } + p->dlt_count = bdl.bfl_len; + p->dlt_list = bdl.bfl_list; - if (ioctl(fd, BIOCGDLTLIST, (caddr_t)&bdl) < 0) { - (void)snprintf(ebuf, PCAP_ERRBUF_SIZE, - "BIOCGDLTLIST: %s", pcap_strerror(errno)); + /* + * *BSD with the new 802.11 ioctls. + * Do we want monitor mode? + */ + if (p->opt.rfmon) { + /* + * Try to put the interface into monitor mode. + */ + status = monitor_mode(p, 1); + if (status != 0) { + /* + * We failed. + */ goto bad; } - 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; + + /* + * 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. + */ + 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; + } + } } } + p->linktype = v; /* set timeout */ - if (to_ms != 0) { + if (p->md.timeout) { 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", - pcap_strerror(errno)); + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "BIOCSRTIMEOUT: %s", pcap_strerror(errno)); + status = PCAP_ERROR; goto bad; } } - if (promisc) - /* set promiscuous mode, okay if it fails */ - (void)ioctl(p->fd, BIOCPROMISC, NULL); + + if (p->opt.promisc) { + /* set promiscuous mode, just warn if it fails */ + if (ioctl(p->fd, BIOCPROMISC, NULL) < 0) { + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "BIOCPROMISC: %s", + pcap_strerror(errno)); + status = PCAP_WARNING_PROMISC_NOTSUP; + } + } 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)); + status = 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)); + status = PCAP_ERROR; goto bad; } - return (p); + if (status < 0) + goto bad; + + p->activated = 1; + + return (status); bad: - if (fd >= 0) - (void)close(fd); - free(bdl.bfl_list); - free(p); + pcap_cleanup_bpf(p); + + if (p->errbuf[0] == '\0') { + /* + * No error message supplied by the activate routine; + * for the benefit of programs that don't specially + * handle errors other than PCAP_ERROR, return the + * error message corresponding to the status. + */ + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "%s", + pcap_statustostr(status)); + } + + return (status); +} + +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); + (void)strlcpy(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. + */ + switch (errno) { + + case ENXIO: + /* + * There's no such device. + */ + close(sock); + return (PCAP_ERROR_NO_SUCH_DEVICE); + + case EINVAL: + /* + * Interface doesn't support SIOC{G,S}IFMEDIA. + */ + close(sock); + return (PCAP_ERROR_RFMON_NOTSUP); + + default: + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "SIOCGIFMEDIA 1: %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 = (int *) calloc(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)strlcpy(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_do_on_close |= MUST_CLEAR_RFMON; + + /* + * Add this to the list of pcaps to close when we exit. + */ + pcap_add_to_pcaps_to_close(p); + } + } + return (0); +} + +/* + * 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_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); +} + +pcap_t * +pcap_create(const char *device, 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->fd = -1; /* not opened yet */ + + p->opt.source = strdup(device); + if (p->opt.source == NULL) { + snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s", + pcap_strerror(errno)); + free(p); + return (NULL); + } + + /* 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); +} + +pcap_t * +pcap_open_live(const char *source, int snaplen, int promisc, int to_ms, + char *errbuf) +{ + pcap_t *p; + int status; + + p = pcap_create(source, errbuf); + if (p == NULL) + return (NULL); + status = pcap_set_snaplen(p, snaplen); + if (status < 0) + goto fail; + status = pcap_set_promisc(p, promisc); + if (status < 0) + goto fail; + status = pcap_set_timeout(p, to_ms); + if (status < 0) + 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; + status = pcap_activate(p); + if (status < 0) + goto fail; + return (p); +fail: + if (status == PCAP_ERROR) + snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: %s", source, + p->errbuf); + else if (status == PCAP_ERROR_NO_SUCH_DEVICE || + status == PCAP_ERROR_PERM_DENIED || + status == PCAP_ERROR_PROMISC_PERM_DENIED) + snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: %s (%s)", source, + pcap_statustostr(status), p->errbuf); + else + snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: %s", source, + pcap_statustostr(status)); + pcap_close(p); return (NULL); } diff --git a/lib/libpcap/pcap-int.h b/lib/libpcap/pcap-int.h index d870d326bca..4c0eac9dfe4 100644 --- a/lib/libpcap/pcap-int.h +++ b/lib/libpcap/pcap-int.h @@ -1,4 +1,4 @@ -/* $OpenBSD: pcap-int.h,v 1.11 2006/03/26 20:58:51 djm Exp $ */ +/* $OpenBSD: pcap-int.h,v 1.12 2012/05/25 01:58:08 lteo Exp $ */ /* * Copyright (c) 1994, 1995, 1996 @@ -32,7 +32,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#) $Header: /cvs/OpenBSD/src/lib/libpcap/pcap-int.h,v 1.11 2006/03/26 20:58:51 djm Exp $ (LBL) + * @(#) $Header: /cvs/OpenBSD/src/lib/libpcap/pcap-int.h,v 1.12 2012/05/25 01:58:08 lteo Exp $ (LBL) */ #ifndef pcap_int_h @@ -40,6 +40,18 @@ #include +/* + * Stuff to do when we close. + */ +#define MUST_CLEAR_RFMON 0x00000002 /* clear rfmon (monitor) mode */ + +struct pcap_opt { + int buffer_size; + char *source; + int promisc; + int rfmon; +}; + /* * Savefile */ @@ -60,11 +72,9 @@ struct pcap_md { u_long TotDrops; /* count of dropped packets */ long TotMissed; /* missed by i/f during this run */ long OrigMissed; /* missed by i/f before this run */ -#ifdef linux - int pad; - int skip; - char *device; -#endif + int timeout; /* timeout for buffering */ + int must_do_on_close; /* stuff we must do when we close */ + struct pcap *next; /* list of open pcaps that need stuff cleared on close */ }; struct pcap { @@ -73,10 +83,13 @@ struct pcap { int linktype; 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; /* force break from packet-reading loop */ struct pcap_sf sf; struct pcap_md md; + struct pcap_opt opt; /* * Read buffer. @@ -136,6 +149,11 @@ int yylex(void); int pcap_offline_read(pcap_t *, int, pcap_handler, u_char *); int pcap_read(pcap_t *, int cnt, pcap_handler, u_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 *); +int pcap_check_activated(pcap_t *); + /* Ultrix pads to make everything line up on a nice boundary */ #if defined(ultrix) || defined(__alpha) #define PCAP_FDDIPAD 3 diff --git a/lib/libpcap/pcap.3 b/lib/libpcap/pcap.3 index dc4cf4b461d..dac4215ecb5 100644 --- a/lib/libpcap/pcap.3 +++ b/lib/libpcap/pcap.3 @@ -1,4 +1,4 @@ -.\" $OpenBSD: pcap.3,v 1.30 2007/05/31 19:19:36 jmc Exp $ +.\" $OpenBSD: pcap.3,v 1.31 2012/05/25 01:58:08 lteo Exp $ .\" .\" Copyright (c) 1994, 1996, 1997 .\" The Regents of the University of California. All rights reserved. @@ -19,7 +19,7 @@ .\" WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. .\" -.Dd $Mdocdate: May 31 2007 $ +.Dd $Mdocdate: May 25 2012 $ .Dt PCAP 3 .Os .Sh NAME @@ -121,6 +121,24 @@ .Fn pcap_datalink_val_to_description "int" .Ft int .Fn pcap_datalink_name_to_val "const char *" +.Ft "pcap_t *" +.Fn pcap_create "const char *source" "char *errbuf" +.Ft int +.Fn pcap_set_snaplen "pcap_t *p" "int snaplen" +.Ft int +.Fn pcap_set_promisc "pcap_t *p" "int promisc" +.Ft int +.Fn pcap_can_set_rfmon "pcap_t *p" +.Ft int +.Fn pcap_set_rfmon "pcap_t *p" "int rfmon" +.Ft int +.Fn pcap_set_timeout "pcap_t *p" "int timeout" +.Ft int +.Fn pcap_set_buffer_size "pcap_t *p" "int buffer_size" +.Ft int +.Fn pcap_activate "pcap_t *p" +.Ft const char * +.Fn pcap_statustostr "int" .Sh DESCRIPTION .Nm provides a high level interface to packet capture systems. @@ -486,6 +504,57 @@ if the specified datalink type is not known. .Fn pcap_datalink_name_to_val finds the datalink number for a given datalink name. Returns \-1 if the name is not known. +.Pp +.Fn pcap_create +is used to create a packet capture handle to look at +packets on the network. +The returned handle must be activated with +.Fn pcap_activate +before packets can be captured with it; options for the +capture, such as promiscuous mode, can be set on the handle +before activating it. +.Pp +.Fn pcap_set_snaplen +sets the snapshot length to be used on a capture handle when the +handle is activated to +.Fa snaplen . +.Pp +.Fn pcap_set_promisc +sets whether promiscuous mode should be set on a capture handle +when the handle is activated. If +.Fa promisc +is non-zero, promiscuous mode will be set, otherwise it will not be set. +.Pp +.Fn pcap_can_set_rfmon +checks whether monitor mode could be set on a capture handle when the +handle is activated. +.Pp +.Fn pcap_set_rfmon +sets whether monitor mode should be set on a capture handle +when the handle is activated. If +.Fa rfmon +is non-zero, monitor mode will be set, otherwise it will not be set. +.Pp +.Fn pcap_set_timeout +sets the read timeout that will be used on a capture handle when the +handle is activated to +.Fa to_ms , +which is in units of milliseconds. +.Pp +.Fn pcap_set_buffer_size +sets the buffer size that will be used on a capture handle when the +handle is activated to +.Fa buffer_size , +which is in units of bytes. +.Pp +.Fn pcap_activate +is used to activate a packet capture handle to look at +packets on the network, with the options that were set on the handle +being in effect. +.Pp +.Fn pcap_statustostr +converts a PCAP_ERROR_ or PCAP_WARNING_ value returned by a libpcap +routine to an error string. .Sh SEE ALSO .Xr tcpdump 8 .\" , tcpslice(1) diff --git a/lib/libpcap/pcap.c b/lib/libpcap/pcap.c index 190c37305a9..591094fd2e7 100644 --- a/lib/libpcap/pcap.c +++ b/lib/libpcap/pcap.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pcap.c,v 1.12 2010/06/26 16:47:07 henning Exp $ */ +/* $OpenBSD: pcap.c,v 1.13 2012/05/25 01:58:08 lteo Exp $ */ /* * Copyright (c) 1993, 1994, 1995, 1996, 1997, 1998 @@ -175,6 +175,62 @@ pcap_next_ex(pcap_t *p, struct pcap_pkthdr **pkt_header, return (pcap_read(p, 1, pcap_fakecallback, (u_char *)&s)); } +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; +} + /* * Force the loop in "pcap_read()" or "pcap_read_offline()" to terminate. */ @@ -386,6 +442,62 @@ pcap_setnonblock(pcap_t *p, int nonblock, char *errbuf) return (0); } +/* + * Generate error strings for PCAP_ERROR_ and PCAP_WARNING_ values. + */ +const char * +pcap_statustostr(int errnum) +{ + static char ebuf[15+10+1]; + + switch (errnum) { + + case PCAP_WARNING: + return("Generic warning"); + + case PCAP_WARNING_TSTAMP_TYPE_NOTSUP: + return ("That type of time stamp is not supported by that device"); + + case PCAP_WARNING_PROMISC_NOTSUP: + return ("That device doesn't support promiscuous mode"); + + case PCAP_ERROR: + return("Generic error"); + + case PCAP_ERROR_BREAK: + return("Loop terminated by pcap_breakloop"); + + case PCAP_ERROR_NOT_ACTIVATED: + return("The pcap_t has not been activated"); + + case PCAP_ERROR_ACTIVATED: + return ("The setting can't be changed after the pcap_t is activated"); + + case PCAP_ERROR_NO_SUCH_DEVICE: + return ("No such device exists"); + + case PCAP_ERROR_RFMON_NOTSUP: + return ("That device doesn't support monitor mode"); + + case PCAP_ERROR_NOT_RFMON: + return ("That operation is supported only in monitor mode"); + + case PCAP_ERROR_PERM_DENIED: + return ("You don't have permission to capture on that device"); + + case PCAP_ERROR_IFACE_NOT_UP: + return ("That device is not up"); + + case PCAP_ERROR_CANTSET_TSTAMP_TYPE: + return ("That device doesn't support setting the time stamp type"); + + case PCAP_ERROR_PROMISC_PERM_DENIED: + return ("You don't have permission to capture in promiscuous mode on that device"); + } + (void)snprintf(ebuf, sizeof ebuf, "Unknown error: %d", errnum); + return(ebuf); +} + /* * Not all systems have strerror(). */ @@ -406,6 +518,95 @@ pcap_strerror(int errnum) #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. + */ + (void)strlcpy(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; + } + } +} + pcap_t * pcap_open_dead(int linktype, int snaplen) { @@ -421,28 +622,6 @@ pcap_open_dead(int linktype, int snaplen) return p; } -void -pcap_close(pcap_t *p) -{ - /*XXX*/ - if (p->fd >= 0) - close(p->fd); - if (p->sf.rfile != NULL) { - (void)fclose(p->sf.rfile); - if (p->sf.base != NULL) - free(p->sf.base); - } else if (p->buffer != NULL) - free(p->buffer); -#ifdef linux - if (p->md.device != NULL) - free(p->md.device); -#endif - pcap_freecode(&p->fcode); - if (p->dlt_list != NULL) - free(p->dlt_list); - free(p); -} - const char * pcap_lib_version(void) { diff --git a/lib/libpcap/pcap.h b/lib/libpcap/pcap.h index bb71365e32a..77ff89c0132 100644 --- a/lib/libpcap/pcap.h +++ b/lib/libpcap/pcap.h @@ -1,4 +1,4 @@ -/* $OpenBSD: pcap.h,v 1.14 2006/03/26 20:58:51 djm Exp $ */ +/* $OpenBSD: pcap.h,v 1.15 2012/05/25 01:58:08 lteo Exp $ */ /* * Copyright (c) 1993, 1994, 1995, 1996, 1997 @@ -32,7 +32,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#) $Header: /cvs/OpenBSD/src/lib/libpcap/pcap.h,v 1.14 2006/03/26 20:58:51 djm Exp $ (LBL) + * @(#) $Header: /cvs/OpenBSD/src/lib/libpcap/pcap.h,v 1.15 2012/05/25 01:58:08 lteo Exp $ (LBL) */ #ifndef lib_pcap_h @@ -130,12 +130,49 @@ struct pcap_addr { struct sockaddr *dstaddr; /* P2P destination address for that address */ }; +/* + * Error codes for the pcap API. + * These will all be negative, so you can check for the success or + * failure of a call that returns these codes by checking for a + * negative value. + */ +#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 */ +#define PCAP_ERROR_PERM_DENIED -8 /* no permission to open the device */ +#define PCAP_ERROR_IFACE_NOT_UP -9 /* interface isn't up */ +#define PCAP_ERROR_CANTSET_TSTAMP_TYPE -10 /* this device doesn't support setting the time stamp type */ +#define PCAP_ERROR_PROMISC_PERM_DENIED -11 /* you don't have permission to capture in promiscuous mode */ + +/* + * Warning codes for the pcap API. + * These will all be positive and non-zero, so they won't look like + * errors. + */ +#define PCAP_WARNING 1 /* generic warning code */ +#define PCAP_WARNING_PROMISC_NOTSUP 2 /* this device doesn't support promiscuous mode */ +#define PCAP_WARNING_TSTAMP_TYPE_NOTSUP 3 /* the requested time stamp type is not supported */ + typedef void (*pcap_handler)(u_char *, const struct pcap_pkthdr *, const u_char *); __BEGIN_DECLS 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 *); @@ -155,6 +192,7 @@ int pcap_setnonblock(pcap_t *, int, char *); void pcap_perror(pcap_t *, char *); int pcap_inject(pcap_t *, const void *, size_t); int pcap_sendpacket(pcap_t *, const u_char *, int); +const char *pcap_statustostr(int); char *pcap_strerror(int); char *pcap_geterr(pcap_t *); int pcap_compile(pcap_t *, struct bpf_program *, char *, int, diff --git a/lib/libpcap/savefile.c b/lib/libpcap/savefile.c index e9ccefff3c0..c0527bacf3b 100644 --- a/lib/libpcap/savefile.c +++ b/lib/libpcap/savefile.c @@ -1,4 +1,4 @@ -/* $OpenBSD: savefile.c,v 1.9 2006/03/26 20:58:51 djm Exp $ */ +/* $OpenBSD: savefile.c,v 1.10 2012/05/25 01:58:08 lteo Exp $ */ /* * Copyright (c) 1993, 1994, 1995, 1996, 1997 @@ -395,6 +395,7 @@ pcap_dump_ftell(pcap_dumper_t *p) return (ftell((FILE *)p)); } +int pcap_dump_flush(pcap_dumper_t *p) { diff --git a/lib/libpcap/shlib_version b/lib/libpcap/shlib_version index 9c1551636c5..5b844bbf422 100644 --- a/lib/libpcap/shlib_version +++ b/lib/libpcap/shlib_version @@ -1,2 +1,2 @@ -major=6 +major=7 minor=0 -- cgit v1.2.3