diff options
author | Xin LI <delphij@FreeBSD.org> | 2017-02-12 07:04:44 +0000 |
---|---|---|
committer | Xin LI <delphij@FreeBSD.org> | 2017-02-12 07:04:44 +0000 |
commit | c8c6d70e300aa9261c4766502c179fc4cd2b22a0 (patch) | |
tree | 14c068bb8b49db9f9ae540e8e2be80a9035e403a /pcap-linux.c | |
parent | 50224b10fa4e157f09ee1cd03c790c9e61e76c5e (diff) |
Vendor import of libpcap 1.8.1.vendor/libpcap/1.8.1
Notes
Notes:
svn path=/vendor/libpcap/dist/; revision=313676
svn path=/vendor/libpcap/1.8.1/; revision=313677; tag=vendor/libpcap/1.8.1
Diffstat (limited to 'pcap-linux.c')
-rw-r--r-- | pcap-linux.c | 1571 |
1 files changed, 1171 insertions, 400 deletions
diff --git a/pcap-linux.c b/pcap-linux.c index 900ebbc0dd56..924df42af061 100644 --- a/pcap-linux.c +++ b/pcap-linux.c @@ -25,10 +25,10 @@ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * Modifications: Added PACKET_MMAP support - * Paolo Abeni <paolo.abeni@email.it> + * Paolo Abeni <paolo.abeni@email.it> * Added TPACKET_V3 support * Gabor Tatarka <gabor.tatarka@ericsson.com> - * + * * based on previous works of: * Simon Patarin <patarin@cs.unibo.it> * Phil Wood <cpw@lanl.gov> @@ -47,7 +47,7 @@ * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. + * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. @@ -56,12 +56,12 @@ * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. @@ -188,9 +188,10 @@ # endif /* PACKET_HOST */ - /* check for memory mapped access avaibility. We assume every needed + /* check for memory mapped access avaibility. We assume every needed * struct is defined if the macro TPACKET_HDRLEN is defined, because it * uses many ring related structs and macros */ +# ifdef PCAP_SUPPORT_PACKET_RING # ifdef TPACKET_HDRLEN # define HAVE_PACKET_RING # ifdef TPACKET3_HDRLEN @@ -202,6 +203,7 @@ # define TPACKET_V1 0 /* Old kernel with only V1, so no TPACKET_Vn defined */ # endif /* TPACKET2_HDRLEN */ # endif /* TPACKET_HDRLEN */ +# endif /* PCAP_SUPPORT_PACKET_RING */ #endif /* PF_PACKET */ #ifdef SO_ATTACH_FILTER @@ -213,6 +215,23 @@ #include <linux/net_tstamp.h> #endif +#ifdef HAVE_LINUX_SOCKIOS_H +#include <linux/sockios.h> +#endif + +#ifdef HAVE_LINUX_IF_BONDING_H +#include <linux/if_bonding.h> + +/* + * The ioctl code to use to check whether a device is a bonding device. + */ +#if defined(SIOCBONDINFOQUERY) + #define BOND_INFO_QUERY_IOCTL SIOCBONDINFOQUERY +#elif defined(BOND_INFO_QUERY_OLD) + #define BOND_INFO_QUERY_IOCTL BOND_INFO_QUERY_OLD +#endif +#endif /* HAVE_LINUX_IF_BONDING_H */ + /* * Got Wireless Extensions? */ @@ -300,6 +319,7 @@ struct pcap_linux { u_int tp_version; /* version of tpacket_hdr for mmaped ring */ u_int tp_hdrlen; /* hdrlen of tpacket_hdr for mmaped ring */ u_char *oneshot_buffer; /* buffer for copy of packet */ + int poll_timeout; /* timeout to use in poll() */ #ifdef HAVE_TPACKET3 unsigned char *current_packet; /* Current packet within the TPACKET_V3 block. Move to next block if NULL. */ int packets_left; /* Unhandled packets left within the block from previous call to pcap_read_linux_mmap_v3 in case of TPACKET_V3. */ @@ -316,7 +336,7 @@ struct pcap_linux { /* * Prototypes for internal functions and methods. */ -static void map_arphrd_to_dlt(pcap_t *, int, const char *, int); +static void map_arphrd_to_dlt(pcap_t *, int, int, const char *, int); #ifdef HAVE_PF_PACKET_SOCKETS static short int map_packet_type_to_sll_type(short int); #endif @@ -334,8 +354,30 @@ static int pcap_setdirection_linux(pcap_t *, pcap_direction_t); static int pcap_set_datalink_linux(pcap_t *, int); static void pcap_cleanup_linux(pcap_t *); +/* + * This is what the header structure looks like in a 64-bit kernel; + * we use this, rather than struct tpacket_hdr, if we're using + * TPACKET_V1 in 32-bit code running on a 64-bit kernel. + */ +struct tpacket_hdr_64 { + uint64_t tp_status; + unsigned int tp_len; + unsigned int tp_snaplen; + unsigned short tp_mac; + unsigned short tp_net; + unsigned int tp_sec; + unsigned int tp_usec; +}; + +/* + * We use this internally as the tpacket version for TPACKET_V1 in + * 32-bit code on a 64-bit kernel. + */ +#define TPACKET_V1_64 99 + union thdr { struct tpacket_hdr *h1; + struct tpacket_hdr_64 *h1_64; #ifdef HAVE_TPACKET2 struct tpacket2_hdr *h2; #endif @@ -346,13 +388,15 @@ union thdr { }; #ifdef HAVE_PACKET_RING -#define RING_GET_FRAME(h) (((union thdr **)h->buffer)[h->offset]) +#define RING_GET_FRAME_AT(h, offset) (((union thdr **)h->buffer)[(offset)]) +#define RING_GET_CURRENT_FRAME(h) RING_GET_FRAME_AT(h, h->offset) static void destroy_ring(pcap_t *handle); static int create_ring(pcap_t *handle, int *status); static int prepare_tpacket_socket(pcap_t *handle); static void pcap_cleanup_linux_mmap(pcap_t *); static int pcap_read_linux_mmap_v1(pcap_t *, int, pcap_handler , u_char *); +static int pcap_read_linux_mmap_v1_64(pcap_t *, int, pcap_handler , u_char *); #ifdef HAVE_TPACKET2 static int pcap_read_linux_mmap_v2(pcap_t *, int, pcap_handler , u_char *); #endif @@ -366,6 +410,12 @@ static void pcap_oneshot_mmap(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes); #endif +#ifdef TP_STATUS_VLAN_TPID_VALID +# define VLAN_TPID(hdr, hv) (((hv)->tp_vlan_tpid || ((hdr)->tp_status & TP_STATUS_VLAN_TPID_VALID)) ? (hv)->tp_vlan_tpid : ETH_P_8021Q) +#else +# define VLAN_TPID(hdr, hv) ETH_P_8021Q +#endif + /* * Wrap some ioctl calls */ @@ -382,7 +432,13 @@ static int has_wext(int sock_fd, const char *device, char *ebuf); static int enter_rfmon_mode(pcap_t *handle, int sock_fd, const char *device); #endif /* HAVE_PF_PACKET_SOCKETS */ +#if defined(HAVE_LINUX_NET_TSTAMP_H) && defined(PACKET_TIMESTAMP) +static int iface_ethtool_get_ts_info(const char *device, pcap_t *handle, + char *ebuf); +#endif +#ifdef HAVE_PACKET_RING static int iface_get_offload(pcap_t *handle); +#endif static int iface_bind_old(int fd, const char *device, char *ebuf); #ifdef SO_ATTACH_FILTER @@ -403,34 +459,21 @@ pcap_create_interface(const char *device, char *ebuf) { pcap_t *handle; - handle = pcap_create_common(device, ebuf, sizeof (struct pcap_linux)); + handle = pcap_create_common(ebuf, sizeof (struct pcap_linux)); if (handle == NULL) return NULL; handle->activate_op = pcap_activate_linux; handle->can_set_rfmon_op = pcap_can_set_rfmon_linux; + #if defined(HAVE_LINUX_NET_TSTAMP_H) && defined(PACKET_TIMESTAMP) /* - * We claim that we support: - * - * software time stamps, with no details about their precision; - * hardware time stamps, synced to the host time; - * hardware time stamps, not synced to the host time. - * - * XXX - we can't ask a device whether it supports - * hardware time stamps, so we just claim all devices do. + * See what time stamp types we support. */ - handle->tstamp_type_count = 3; - handle->tstamp_type_list = malloc(3 * sizeof(u_int)); - if (handle->tstamp_type_list == NULL) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s", - pcap_strerror(errno)); - free(handle); + if (iface_ethtool_get_ts_info(device, handle, ebuf) == -1) { + pcap_close(handle); return NULL; } - handle->tstamp_type_list[0] = PCAP_TSTAMP_HOST; - handle->tstamp_type_list[1] = PCAP_TSTAMP_ADAPTER; - handle->tstamp_type_list[2] = PCAP_TSTAMP_ADAPTER_UNSYNCED; #endif #if defined(SIOCGSTAMPNS) && defined(SO_TIMESTAMPNS) @@ -445,11 +488,9 @@ pcap_create_interface(const char *device, char *ebuf) handle->tstamp_precision_count = 2; handle->tstamp_precision_list = malloc(2 * sizeof(u_int)); if (handle->tstamp_precision_list == NULL) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s", + pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s", pcap_strerror(errno)); - if (handle->tstamp_type_list != NULL) - free(handle->tstamp_type_list); - free(handle); + pcap_close(handle); return NULL; } handle->tstamp_precision_list[0] = PCAP_TSTAMP_PRECISION_MICRO; @@ -501,7 +542,7 @@ pcap_create_interface(const char *device, char *ebuf) * * Yes, you can have multiple monitor devices for a given * physical device. -*/ + */ /* * Is this a mac80211 device? If so, fill in the physical device path and @@ -519,7 +560,7 @@ get_mac80211_phydev(pcap_t *handle, const char *device, char *phydev_path, * Generate the path string for the symlink to the physical device. */ if (asprintf(&pathstr, "/sys/class/net/%s/phy80211", device) == -1) { - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "%s: Can't generate path name string for /sys/class/net device", device); return PCAP_ERROR; @@ -534,7 +575,7 @@ get_mac80211_phydev(pcap_t *handle, const char *device, char *phydev_path, free(pathstr); return 0; } - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "%s: Can't readlink %s: %s", device, pathstr, strerror(errno)); free(pathstr); @@ -591,20 +632,20 @@ nl80211_init(pcap_t *handle, struct nl80211_state *state, const char *device) state->nl_sock = nl_socket_alloc(); if (!state->nl_sock) { - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "%s: failed to allocate netlink handle", device); return PCAP_ERROR; } if (genl_connect(state->nl_sock)) { - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "%s: failed to connect to generic netlink", device); goto out_handle_destroy; } err = genl_ctrl_alloc_cache(state->nl_sock, &state->nl_cache); if (err < 0) { - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "%s: failed to allocate generic netlink cache: %s", device, get_nl_errmsg(-err)); goto out_handle_destroy; @@ -612,7 +653,7 @@ nl80211_init(pcap_t *handle, struct nl80211_state *state, const char *device) state->nl80211 = genl_ctrl_search_by_name(state->nl_cache, "nl80211"); if (!state->nl80211) { - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "%s: nl80211 not found", device); goto out_cache_free; } @@ -635,9 +676,14 @@ nl80211_cleanup(struct nl80211_state *state) } static int +del_mon_if(pcap_t *handle, int sock_fd, struct nl80211_state *state, + const char *device, const char *mondevice); + +static int add_mon_if(pcap_t *handle, int sock_fd, struct nl80211_state *state, const char *device, const char *mondevice) { + struct pcap_linux *handlep = handle->priv; int ifindex; struct nl_msg *msg; int err; @@ -648,7 +694,7 @@ add_mon_if(pcap_t *handle, int sock_fd, struct nl80211_state *state, msg = nlmsg_alloc(); if (!msg) { - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "%s: failed to allocate netlink msg", device); return PCAP_ERROR; } @@ -680,7 +726,7 @@ add_mon_if(pcap_t *handle, int sock_fd, struct nl80211_state *state, * Real failure, not just "that device is not * available. */ - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "%s: nl_send_auto_complete failed adding %s interface: %s", device, mondevice, get_nl_errmsg(-err)); nlmsg_free(msg); @@ -708,7 +754,7 @@ add_mon_if(pcap_t *handle, int sock_fd, struct nl80211_state *state, * Real failure, not just "that device is not * available. */ - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "%s: nl_wait_for_ack failed adding %s interface: %s", device, mondevice, get_nl_errmsg(-err)); nlmsg_free(msg); @@ -720,10 +766,24 @@ add_mon_if(pcap_t *handle, int sock_fd, struct nl80211_state *state, * Success. */ nlmsg_free(msg); + + /* + * Try to remember the monitor device. + */ + handlep->mondevice = strdup(mondevice); + if (handlep->mondevice == NULL) { + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "strdup: %s", + pcap_strerror(errno)); + /* + * Get rid of the monitor device. + */ + del_mon_if(handle, sock_fd, state, device, mondevice); + return PCAP_ERROR; + } return 1; nla_put_failure: - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "%s: nl_put failed adding %s interface", device, mondevice); nlmsg_free(msg); @@ -744,7 +804,7 @@ del_mon_if(pcap_t *handle, int sock_fd, struct nl80211_state *state, msg = nlmsg_alloc(); if (!msg) { - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "%s: failed to allocate netlink msg", device); return PCAP_ERROR; } @@ -755,7 +815,7 @@ del_mon_if(pcap_t *handle, int sock_fd, struct nl80211_state *state, err = nl_send_auto_complete(state->nl_sock, msg); if (err < 0) { - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "%s: nl_send_auto_complete failed deleting %s interface: %s", device, mondevice, get_nl_errmsg(-err)); nlmsg_free(msg); @@ -763,7 +823,7 @@ del_mon_if(pcap_t *handle, int sock_fd, struct nl80211_state *state, } err = nl_wait_for_ack(state->nl_sock); if (err < 0) { - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "%s: nl_wait_for_ack failed adding %s interface: %s", device, mondevice, get_nl_errmsg(-err)); nlmsg_free(msg); @@ -777,7 +837,7 @@ del_mon_if(pcap_t *handle, int sock_fd, struct nl80211_state *state, return 1; nla_put_failure: - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "%s: nl_put failed deleting %s interface", device, mondevice); nlmsg_free(msg); @@ -822,10 +882,13 @@ enter_rfmon_mode_mac80211(pcap_t *handle, int sock_fd, const char *device) */ char mondevice[3+10+1]; /* mon{UINT_MAX}\0 */ - snprintf(mondevice, sizeof mondevice, "mon%u", n); + pcap_snprintf(mondevice, sizeof mondevice, "mon%u", n); ret = add_mon_if(handle, sock_fd, &nlstate, device, mondevice); if (ret == 1) { - handlep->mondevice = strdup(mondevice); + /* + * Success. We don't clean up the libnl state + * yet, as we'll be using it later. + */ goto added; } if (ret < 0) { @@ -838,7 +901,7 @@ enter_rfmon_mode_mac80211(pcap_t *handle, int sock_fd, const char *device) } } - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "%s: No free monN interfaces", device); nl80211_cleanup(&nlstate); return PCAP_ERROR; @@ -863,7 +926,10 @@ added: * "atexit()" failed; don't put the interface * in rfmon mode, just give up. */ - return PCAP_ERROR_RFMON_NOTSUP; + del_mon_if(handle, sock_fd, &nlstate, device, + handlep->mondevice); + nl80211_cleanup(&nlstate); + return PCAP_ERROR; } /* @@ -872,7 +938,7 @@ added: memset(&ifr, 0, sizeof(ifr)); strlcpy(ifr.ifr_name, handlep->mondevice, sizeof(ifr.ifr_name)); if (ioctl(sock_fd, SIOCGIFFLAGS, &ifr) == -1) { - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "%s: Can't get flags for %s: %s", device, handlep->mondevice, strerror(errno)); del_mon_if(handle, sock_fd, &nlstate, device, @@ -882,7 +948,7 @@ added: } ifr.ifr_flags |= IFF_UP|IFF_RUNNING; if (ioctl(sock_fd, SIOCSIFFLAGS, &ifr) == -1) { - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "%s: Can't set flags for %s: %s", device, handlep->mondevice, strerror(errno)); del_mon_if(handle, sock_fd, &nlstate, device, @@ -911,6 +977,37 @@ added: } #endif /* HAVE_LIBNL */ +#ifdef IW_MODE_MONITOR +/* + * Bonding devices mishandle unknown ioctls; they fail with ENODEV + * rather than ENOTSUP, EOPNOTSUPP, or ENOTTY, so Wireless Extensions + * will fail with ENODEV if we try to do them on a bonding device, + * making us return a "no such device" indication rather than just + * saying "no Wireless Extensions". + * + * So we check for bonding devices, if we can, before trying those + * ioctls, by trying a bonding device information query ioctl to see + * whether it succeeds. + */ +static int +is_bonding_device(int fd, const char *device) +{ +#ifdef BOND_INFO_QUERY_IOCTL + struct ifreq ifr; + ifbond ifb; + + memset(&ifr, 0, sizeof ifr); + strlcpy(ifr.ifr_name, device, sizeof ifr.ifr_name); + memset(&ifb, 0, sizeof ifb); + ifr.ifr_data = (caddr_t)&ifb; + if (ioctl(fd, BOND_INFO_QUERY_IOCTL, &ifr) == 0) + return 1; /* success, so it's a bonding device */ +#endif /* BOND_INFO_QUERY_IOCTL */ + + return 0; /* no, it's not a bonding device */ +} +#endif /* IW_MODE_MONITOR */ + static int pcap_can_set_rfmon_linux(pcap_t *handle) { @@ -923,7 +1020,7 @@ pcap_can_set_rfmon_linux(pcap_t *handle) struct iwreq ireq; #endif - if (strcmp(handle->opt.source, "any") == 0) { + if (strcmp(handle->opt.device, "any") == 0) { /* * Monitor mode makes no sense on the "any" device. */ @@ -943,7 +1040,7 @@ pcap_can_set_rfmon_linux(pcap_t *handle) * wmaster device, so we don't bother checking whether * a mac80211 device supports the Wireless Extensions. */ - ret = get_mac80211_phydev(handle, handle->opt.source, phydev_path, + ret = get_mac80211_phydev(handle, handle->opt.device, phydev_path, PATH_MAX); if (ret < 0) return ret; /* error */ @@ -964,15 +1061,21 @@ pcap_can_set_rfmon_linux(pcap_t *handle) */ sock_fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if (sock_fd == -1) { - (void)snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + (void)pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "socket: %s", pcap_strerror(errno)); return PCAP_ERROR; } + if (is_bonding_device(sock_fd, handle->opt.device)) { + /* It's a bonding device, so don't even try. */ + close(sock_fd); + return 0; + } + /* * Attempt to get the current mode. */ - strlcpy(ireq.ifr_ifrn.ifrn_name, handle->opt.source, + strlcpy(ireq.ifr_ifrn.ifrn_name, handle->opt.device, sizeof ireq.ifr_ifrn.ifrn_name); if (ioctl(sock_fd, SIOCGIWMODE, &ireq) != -1) { /* @@ -983,7 +1086,7 @@ pcap_can_set_rfmon_linux(pcap_t *handle) } if (errno == ENODEV) { /* The device doesn't even exist. */ - (void)snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + (void)pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "SIOCGIWMODE failed: %s", pcap_strerror(errno)); close(sock_fd); return PCAP_ERROR_NO_SUCH_DEVICE; @@ -1009,7 +1112,7 @@ linux_if_drops(const char * if_name) FILE * file; int field_to_convert = 3, if_name_sz = strlen(if_name); long int dropped_pkts = 0; - + file = fopen("/proc/net/dev", "r"); if (!file) return 0; @@ -1024,7 +1127,7 @@ linux_if_drops(const char * if_name) field_to_convert = 4; continue; } - + /* find iface and make sure it actually matches -- space before the name and : after it */ if ((bufptr = strstr(buffer, if_name)) && (bufptr == buffer || *(bufptr-1) == ' ') && @@ -1038,20 +1141,20 @@ linux_if_drops(const char * if_name) while (*bufptr != '\0' && *(bufptr++) == ' '); while (*bufptr != '\0' && *(bufptr++) != ' '); } - + /* get rid of any final spaces */ while (*bufptr != '\0' && *bufptr == ' ') bufptr++; - + if (*bufptr != '\0') dropped_pkts = strtol(bufptr, NULL, 10); break; } } - + fclose(file); return dropped_pkts; -} +} /* @@ -1221,6 +1324,94 @@ static void pcap_cleanup_linux( pcap_t *handle ) } /* + * Set the timeout to be used in poll() with memory-mapped packet capture. + */ +static void +set_poll_timeout(struct pcap_linux *handlep) +{ +#ifdef HAVE_TPACKET3 + struct utsname utsname; + char *version_component, *endp; + int major, minor; + int broken_tpacket_v3 = 1; + + /* + * Some versions of TPACKET_V3 have annoying bugs/misfeatures + * around which we have to work. Determine if we have those + * problems or not. + */ + if (uname(&utsname) == 0) { + /* + * 3.19 is the first release with a fixed version of + * TPACKET_V3. We treat anything before that as + * not haveing a fixed version; that may really mean + * it has *no* version. + */ + version_component = utsname.release; + major = strtol(version_component, &endp, 10); + if (endp != version_component && *endp == '.') { + /* + * OK, that was a valid major version. + * Get the minor version. + */ + version_component = endp + 1; + minor = strtol(version_component, &endp, 10); + if (endp != version_component && + (*endp == '.' || *endp == '\0')) { + /* + * OK, that was a valid minor version. + * Is this 3.19 or newer? + */ + if (major >= 4 || (major == 3 && minor >= 19)) { + /* Yes. TPACKET_V3 works correctly. */ + broken_tpacket_v3 = 0; + } + } + } + } +#endif + if (handlep->timeout == 0) { +#ifdef HAVE_TPACKET3 + /* + * XXX - due to a set of (mis)features in the TPACKET_V3 + * kernel code prior to the 3.19 kernel, blocking forever + * with a TPACKET_V3 socket can, if few packets are + * arriving and passing the socket filter, cause most + * packets to be dropped. See libpcap issue #335 for the + * full painful story. + * + * The workaround is to have poll() time out very quickly, + * so we grab the frames handed to us, and return them to + * the kernel, ASAP. + */ + if (handlep->tp_version == TPACKET_V3 && broken_tpacket_v3) + handlep->poll_timeout = 1; /* don't block for very long */ + else +#endif + handlep->poll_timeout = -1; /* block forever */ + } else if (handlep->timeout > 0) { +#ifdef HAVE_TPACKET3 + /* + * For TPACKET_V3, the timeout is handled by the kernel, + * so block forever; that way, we don't get extra timeouts. + * Don't do that if we have a broken TPACKET_V3, though. + */ + if (handlep->tp_version == TPACKET_V3 && !broken_tpacket_v3) + handlep->poll_timeout = -1; /* block forever, let TPACKET_V3 wake us up */ + else +#endif + handlep->poll_timeout = handlep->timeout; /* block for that amount of time */ + } else { + /* + * Non-blocking mode; we call poll() to pick up error + * indications, but we don't want it to wait for + * anything. + */ + handlep->poll_timeout = 0; + } +} + +/* * Get a handle for a live capture from the given device. You can * pass NULL as device to get all packages (without link level * information of course). If you pass 1 as promisc the interface @@ -1237,7 +1428,7 @@ pcap_activate_linux(pcap_t *handle) int status = 0; int ret; - device = handle->opt.source; + device = handle->opt.device; /* * Make sure the name we were handed will fit into the ioctls we @@ -1274,7 +1465,7 @@ pcap_activate_linux(pcap_t *handle) if (handle->opt.promisc) { handle->opt.promisc = 0; /* Just a warning. */ - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Promiscuous mode not supported on the \"any\" device"); status = PCAP_WARNING_PROMISC_NOTSUP; } @@ -1282,16 +1473,16 @@ pcap_activate_linux(pcap_t *handle) handlep->device = strdup(device); if (handlep->device == NULL) { - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "strdup: %s", + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "strdup: %s", pcap_strerror(errno) ); return PCAP_ERROR; } - + /* copy timeout value */ handlep->timeout = handle->opt.timeout; /* - * If we're in promiscuous mode, then we probably want + * If we're in promiscuous mode, then we probably want * to see when the interface drops packets too, so get an * initial count from /proc/net/dev */ @@ -1330,7 +1521,11 @@ pcap_activate_linux(pcap_t *handle) * set to the status to return, * which might be 0, or might be * a PCAP_WARNING_ value. + * + * Set the timeout to use in poll() before + * returning. */ + set_poll_timeout(handlep); return status; case 0: @@ -1375,7 +1570,7 @@ pcap_activate_linux(pcap_t *handle) if (setsockopt(handle->fd, SOL_SOCKET, SO_RCVBUF, &handle->opt.buffer_size, sizeof(handle->opt.buffer_size)) == -1) { - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "SO_RCVBUF: %s", pcap_strerror(errno)); status = PCAP_ERROR; goto fail; @@ -1386,7 +1581,7 @@ pcap_activate_linux(pcap_t *handle) handle->buffer = malloc(handle->bufsize + handle->offset); if (!handle->buffer) { - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "malloc: %s", pcap_strerror(errno)); status = PCAP_ERROR; goto fail; @@ -1448,6 +1643,22 @@ linux_check_direction(const pcap_t *handle, const struct sockaddr_ll *sll) return 0; /* + * If this is an outgoing CAN or CAN FD frame, and + * the user doesn't only want outgoing packets, + * reject it; CAN devices and drivers, and the CAN + * stack, always arrange to loop back transmitted + * packets, so they also appear as incoming packets. + * We don't want duplicate packets, and we can't + * easily distinguish packets looped back by the CAN + * layer than those received by the CAN layer, so we + * eliminate this packet instead. + */ + if ((sll->sll_protocol == LINUX_SLL_P_CAN || + sll->sll_protocol == LINUX_SLL_P_CANFD) && + handle->direction != PCAP_D_OUT) + return 0; + + /* * If the user only wants incoming packets, reject it. */ if (handle->direction == PCAP_D_IN) @@ -1494,6 +1705,7 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata) int packet_len, caplen; struct pcap_pkthdr pcap_header; + struct bpf_aux_data aux_data; #ifdef HAVE_PF_PACKET_SOCKETS /* * If this is a cooked device, leave extra room for a @@ -1526,7 +1738,7 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata) * if we're using a memory-mapped buffer, we won't even * get notified of "network down" events. */ - bp = handle->buffer + handle->offset; + bp = (u_char *)handle->buffer + handle->offset; #if defined(HAVE_PACKET_AUXDATA) && defined(HAVE_LINUX_TPACKET_AUXDATA_TP_VLAN_TCI) msg.msg_name = &from; @@ -1582,12 +1794,12 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata) * PCAP_ERROR_IFACE_NOT_UP, but pcap_dispatch() * etc. aren't defined to return that. */ - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "The interface went down"); return PCAP_ERROR; default: - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "recvfrom: %s", pcap_strerror(errno)); return PCAP_ERROR; } @@ -1666,17 +1878,35 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata) #endif continue; - len = packet_len > iov.iov_len ? iov.iov_len : packet_len; - if (len < (unsigned int) handlep->vlan_offset) + len = (u_int)packet_len > iov.iov_len ? iov.iov_len : (u_int)packet_len; + if (len < (u_int)handlep->vlan_offset) break; + /* + * Move everything in the header, except the + * type field, down VLAN_TAG_LEN bytes, to + * allow us to insert the VLAN tag between + * that stuff and the type field. + */ bp -= VLAN_TAG_LEN; memmove(bp, bp + VLAN_TAG_LEN, handlep->vlan_offset); + /* + * Now insert the tag. + */ tag = (struct vlan_tag *)(bp + handlep->vlan_offset); - tag->vlan_tpid = htons(ETH_P_8021Q); + tag->vlan_tpid = htons(VLAN_TPID(aux, aux)); tag->vlan_tci = htons(aux->tp_vlan_tci); + /* store vlan tci to bpf_aux_data struct for userland bpf filter */ +#if defined(TP_STATUS_VLAN_VALID) + aux_data.vlan_tag = htons(aux->tp_vlan_tci) & 0x0fff; + aux_data.vlan_tag_present = (aux->tp_status & TP_STATUS_VLAN_VALID); +#endif + + /* + * Add the tag to the packet lengths. + */ packet_len += VLAN_TAG_LEN; } } @@ -1721,9 +1951,8 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata) /* Run the packet filter if not using kernel filter */ if (handlep->filter_in_userland && handle->fcode.bf_insns) { - if (bpf_filter(handle->fcode.bf_insns, bp, - packet_len, caplen) == 0) - { + if (bpf_filter_with_aux_data(handle->fcode.bf_insns, bp, + packet_len, caplen, &aux_data) == 0) { /* rejected by filter */ return 0; } @@ -1735,7 +1964,7 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata) #if defined(SIOCGSTAMPNS) && defined(SO_TIMESTAMPNS) if (handle->opt.tstamp_precision == PCAP_TSTAMP_PRECISION_NANO) { if (ioctl(handle->fd, SIOCGSTAMPNS, &pcap_header.ts) == -1) { - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "SIOCGSTAMPNS: %s", pcap_strerror(errno)); return PCAP_ERROR; } @@ -1743,7 +1972,7 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata) #endif { if (ioctl(handle->fd, SIOCGSTAMP, &pcap_header.ts) == -1) { - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "SIOCGSTAMP: %s", pcap_strerror(errno)); return PCAP_ERROR; } @@ -1841,12 +2070,12 @@ pcap_inject_linux(pcap_t *handle, const void *buf, size_t size) ret = send(handle->fd, buf, size, 0); if (ret == -1) { - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "send: %s", + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "send: %s", pcap_strerror(errno)); return (-1); } return (ret); -} +} /* * Get the statistics for the given packet capture handle. @@ -1884,8 +2113,8 @@ pcap_stats_linux(pcap_t *handle, struct pcap_stat *stats) #endif /* HAVE_TPACKET_STATS */ long if_dropped = 0; - - /* + + /* * To fill in ps_ifdrop, we parse /proc/net/dev for the number */ if (handle->opt.promisc) @@ -1915,7 +2144,7 @@ pcap_stats_linux(pcap_t *handle, struct pcap_stat *stats) * dropped by the interface driver. It counts only * packets that passed the filter. * - * See above for ps_ifdrop. + * See above for ps_ifdrop. * * Both statistics include packets not yet read from * the kernel by libpcap, and thus not yet seen by @@ -1944,7 +2173,7 @@ pcap_stats_linux(pcap_t *handle, struct pcap_stat *stats) * "tp_packets" as the count of packets and "tp_drops" * as the count of drops. * - * Keep a running total because each call to + * Keep a running total because each call to * getsockopt(handle->fd, SOL_PACKET, PACKET_STATISTICS, .... * resets the counters to zero. */ @@ -1963,7 +2192,7 @@ pcap_stats_linux(pcap_t *handle, struct pcap_stat *stats) * is built on a system without "struct tpacket_stats". */ if (errno != EOPNOTSUPP) { - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "pcap_stats: %s", pcap_strerror(errno)); return -1; } @@ -1990,10 +2219,10 @@ pcap_stats_linux(pcap_t *handle, struct pcap_stat *stats) * We maintain the count of packets processed by libpcap in * "handlep->packets_read", for reasons described in the comment * at the end of pcap_read_packet(). We have no idea how many - * packets were dropped by the kernel buffers -- but we know + * packets were dropped by the kernel buffers -- but we know * how many the interface dropped, so we can return that. */ - + stats->ps_recv = handlep->packets_read; stats->ps_drop = 0; stats->ps_ifdrop = handlep->stat.ps_ifdrop; @@ -2049,7 +2278,7 @@ add_linux_if(pcap_if_t **devlistp, const char *ifname, int fd, char *errbuf) if (ioctl(fd, SIOCGIFFLAGS, (char *)&ifrflags) < 0) { if (errno == ENXIO || errno == ENODEV) return (0); /* device doesn't actually exist - ignore it */ - (void)snprintf(errbuf, PCAP_ERRBUF_SIZE, + (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "SIOCGIFFLAGS: %.*s: %s", (int)sizeof(ifrflags.ifr_name), ifrflags.ifr_name, @@ -2060,7 +2289,8 @@ add_linux_if(pcap_if_t **devlistp, const char *ifname, int fd, char *errbuf) /* * Add an entry for this interface, with no addresses. */ - if (pcap_add_if(devlistp, name, ifrflags.ifr_flags, NULL, + if (pcap_add_if(devlistp, name, + if_flags_to_pcap_flags(name, ifrflags.ifr_flags), NULL, errbuf) == -1) { /* * Failure. @@ -2108,7 +2338,7 @@ scan_sys_class_net(pcap_if_t **devlistp, char *errbuf) /* * Fail if we got some other error. */ - (void)snprintf(errbuf, PCAP_ERRBUF_SIZE, + (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Can't open /sys/class/net: %s", pcap_strerror(errno)); return (-1); } @@ -2116,9 +2346,9 @@ scan_sys_class_net(pcap_if_t **devlistp, char *errbuf) /* * Create a socket from which to fetch interface information. */ - fd = socket(AF_INET, SOCK_DGRAM, 0); + fd = socket(PF_UNIX, SOCK_RAW, 0); if (fd < 0) { - (void)snprintf(errbuf, PCAP_ERRBUF_SIZE, + (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "socket: %s", pcap_strerror(errno)); (void)closedir(sys_class_net_d); return (-1); @@ -2155,7 +2385,7 @@ scan_sys_class_net(pcap_if_t **devlistp, char *errbuf) * for devices, newer kernels have symlinks to * directories.) */ - snprintf(subsystem_path, sizeof subsystem_path, + pcap_snprintf(subsystem_path, sizeof subsystem_path, "/sys/class/net/%s/ifindex", ent->d_name); if (lstat(subsystem_path, &statb) != 0) { /* @@ -2186,7 +2416,7 @@ scan_sys_class_net(pcap_if_t **devlistp, char *errbuf) * fail due to an error reading the directory? */ if (errno != 0) { - (void)snprintf(errbuf, PCAP_ERRBUF_SIZE, + (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Error reading /sys/class/net: %s", pcap_strerror(errno)); ret = -1; @@ -2226,7 +2456,7 @@ scan_proc_net_dev(pcap_if_t **devlistp, char *errbuf) /* * Fail if we got some other error. */ - (void)snprintf(errbuf, PCAP_ERRBUF_SIZE, + (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Can't open /proc/net/dev: %s", pcap_strerror(errno)); return (-1); } @@ -2234,9 +2464,9 @@ scan_proc_net_dev(pcap_if_t **devlistp, char *errbuf) /* * Create a socket from which to fetch interface information. */ - fd = socket(AF_INET, SOCK_DGRAM, 0); + fd = socket(PF_UNIX, SOCK_RAW, 0); if (fd < 0) { - (void)snprintf(errbuf, PCAP_ERRBUF_SIZE, + (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "socket: %s", pcap_strerror(errno)); (void)fclose(proc_net_f); return (-1); @@ -2275,7 +2505,7 @@ scan_proc_net_dev(pcap_if_t **devlistp, char *errbuf) * fail due to an error reading the file? */ if (ferror(proc_net_f)) { - (void)snprintf(errbuf, PCAP_ERRBUF_SIZE, + (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Error reading /proc/net/dev: %s", pcap_strerror(errno)); ret = -1; @@ -2292,12 +2522,27 @@ scan_proc_net_dev(pcap_if_t **devlistp, char *errbuf) */ static const char any_descr[] = "Pseudo-device that captures on all interfaces"; +/* + * A SOCK_PACKET or PF_PACKET socket can be bound to any network interface. + */ +static int +can_be_bound(const char *name _U_) +{ + return (1); +} + int pcap_platform_finddevs(pcap_if_t **alldevsp, char *errbuf) { int ret; /* + * Get the list of regular interfaces first. + */ + if (pcap_findalldevs_interfaces(alldevsp, errbuf, can_be_bound) == -1) + return (-1); /* failure */ + + /* * Read "/sys/class/net", and add to the list of interfaces all * interfaces listed there that we don't already have, because, * on Linux, SIOCGIFCONF reports only interfaces with IPv4 addresses, @@ -2319,7 +2564,7 @@ pcap_platform_finddevs(pcap_if_t **alldevsp, char *errbuf) /* * Add the "any" device. */ - if (pcap_add_if(alldevsp, "any", IFF_UP|IFF_RUNNING, + if (pcap_add_if(alldevsp, "any", PCAP_IF_UP|PCAP_IF_RUNNING, any_descr, errbuf) < 0) return (-1); @@ -2478,8 +2723,14 @@ pcap_setfilter_linux_common(pcap_t *handle, struct bpf_program *filter, * calling "pcap_setfilter()". Otherwise, the kernel filter may * filter out packets that would pass the new userland filter. */ - if (handlep->filter_in_userland) - reset_kernel_filter(handle); + if (handlep->filter_in_userland) { + if (reset_kernel_filter(handle) == -1) { + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + "can't remove kernel filter: %s", + pcap_strerror(errno)); + err = -2; /* fatal error */ + } + } /* * Free up the copy of the filter that was made by "fix_program()". @@ -2521,7 +2772,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, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "Setting direction is not supported on SOCK_PACKET sockets"); return -1; } @@ -2561,6 +2812,51 @@ map_packet_type_to_sll_type(short int sll_pkttype) } #endif +static int +is_wifi(int sock_fd +#ifndef IW_MODE_MONITOR +_U_ +#endif +, const char *device) +{ + char *pathstr; + struct stat statb; +#ifdef IW_MODE_MONITOR + char errbuf[PCAP_ERRBUF_SIZE]; +#endif + + /* + * See if there's a sysfs wireless directory for it. + * If so, it's a wireless interface. + */ + if (asprintf(&pathstr, "/sys/class/net/%s/wireless", device) == -1) { + /* + * Just give up here. + */ + return 0; + } + if (stat(pathstr, &statb) == 0) { + free(pathstr); + return 1; + } + free(pathstr); + +#ifdef IW_MODE_MONITOR + /* + * OK, maybe it's not wireless, or maybe this kernel doesn't + * support sysfs. Try the wireless extensions. + */ + if (has_wext(sock_fd, device, errbuf) == 1) { + /* + * It supports the wireless extensions, so it's a Wi-Fi + * device. + */ + return 1; + } +#endif + return 0; +} + /* * Linux uses the ARP hardware type to identify the type of an * interface. pcap uses the DLT_xxx constants for this. This @@ -2579,8 +2875,8 @@ map_packet_type_to_sll_type(short int sll_pkttype) * * Sets the link type to -1 if unable to map the type. */ -static void map_arphrd_to_dlt(pcap_t *handle, int arptype, const char *device, - int cooked_ok) +static void map_arphrd_to_dlt(pcap_t *handle, int sock_fd, int arptype, + const char *device, int cooked_ok) { static const char cdma_rmnet[] = "cdma_rmnet"; @@ -2591,7 +2887,7 @@ static void map_arphrd_to_dlt(pcap_t *handle, int arptype, const char *device, * For various annoying reasons having to do with DHCP * software, some versions of Android give the mobile- * phone-network interface an ARPHRD_ value of - * ARPHRD_ETHER, even though the packet supplied by + * ARPHRD_ETHER, even though the packets supplied by * that interface have no link-layer header, and begin * with an IP header, so that the ARPHRD_ value should * be ARPHRD_NONE. @@ -2603,9 +2899,9 @@ static void map_arphrd_to_dlt(pcap_t *handle, int arptype, const char *device, handle->linktype = DLT_RAW; return; } - + /* - * This is (presumably) a real Ethernet capture; give it a + * Is this a real Ethernet device? If so, give it a * link-layer-type list with DLT_EN10MB and DLT_DOCSIS, so * that an application can let you choose it, in case you're * capturing DOCSIS traffic that a Cisco Cable Modem @@ -2614,21 +2910,27 @@ static void map_arphrd_to_dlt(pcap_t *handle, int arptype, const char *device, * DOCSIS frames out on the wire inside the low-level * Ethernet framing). * - * XXX - are there any sorts of "fake Ethernet" that have - * ARPHRD_ETHER but that *shouldn't offer DLT_DOCSIS as + * XXX - are there any other 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 "activate_new()", - * as we fall back on cooked mode there; are there any + * as we fall back on cooked mode there, and we use + * is_wifi() to check for 802.11 devices; are there any * others? */ - handle->dlt_list = (u_int *) malloc(sizeof(u_int) * 2); - /* - * If that fails, just leave the list empty. - */ - if (handle->dlt_list != NULL) { - handle->dlt_list[0] = DLT_EN10MB; - handle->dlt_list[1] = DLT_DOCSIS; - handle->dlt_count = 2; + if (!is_wifi(sock_fd, device)) { + /* + * It's not a Wi-Fi device; offer DOCSIS. + */ + handle->dlt_list = (u_int *) malloc(sizeof(u_int) * 2); + /* + * If that fails, just leave the list empty. + */ + if (handle->dlt_list != NULL) { + handle->dlt_list[0] = DLT_EN10MB; + handle->dlt_list[1] = DLT_DOCSIS; + handle->dlt_count = 2; + } } /* FALLTHROUGH */ @@ -2657,7 +2959,14 @@ static void map_arphrd_to_dlt(pcap_t *handle, int arptype, const char *device, #define ARPHRD_CAN 280 #endif case ARPHRD_CAN: - handle->linktype = DLT_CAN_SOCKETCAN; + /* + * Map this to DLT_LINUX_SLL; that way, CAN frames will + * have ETH_P_CAN/LINUX_SLL_P_CAN as the protocol and + * CAN FD frames will have ETH_P_CANFD/LINUX_SLL_P_CANFD + * as the protocol, so they can be distinguished by the + * protocol in the SLL header. + */ + handle->linktype = DLT_LINUX_SLL; break; #ifndef ARPHRD_IEEE802_TR @@ -2940,7 +3249,7 @@ static void map_arphrd_to_dlt(pcap_t *handle, int arptype, const char *device, * so let's use "Linux-cooked" mode. Jean II * * XXX - this is handled in activate_new(). */ - //handlep->cooked = 1; + /* handlep->cooked = 1; */ break; /* ARPHRD_LAPD is unofficial and randomly allocated, if reallocation @@ -2983,7 +3292,7 @@ static void map_arphrd_to_dlt(pcap_t *handle, int arptype, const char *device, * * XXX - this is handled in activate_new(). */ - //handlep->cooked = 1; + /* handlep->cooked = 1; */ break; default: @@ -3006,7 +3315,7 @@ activate_new(pcap_t *handle) { #ifdef HAVE_PF_PACKET_SOCKETS struct pcap_linux *handlep = handle->priv; - const char *device = handle->opt.source; + const char *device = handle->opt.device; int is_any_device = (strcmp(device, "any") == 0); int sock_fd = -1, arptype; #ifdef HAVE_PACKET_AUXDATA @@ -3014,6 +3323,10 @@ activate_new(pcap_t *handle) #endif int err = 0; struct packet_mreq mr; +#if defined(SO_BPF_EXTENSIONS) && defined(SKF_AD_VLAN_TAG_PRESENT) + int bpf_extensions; + socklen_t len = sizeof(bpf_extensions); +#endif /* * Open a socket with protocol family packet. If the @@ -3034,7 +3347,7 @@ activate_new(pcap_t *handle) return 0; } - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "socket: %s", + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "socket: %s", pcap_strerror(errno) ); if (errno == EPERM || errno == EACCES) { /* @@ -3117,7 +3430,7 @@ activate_new(pcap_t *handle) close(sock_fd); return arptype; } - map_arphrd_to_dlt(handle, arptype, device, 1); + map_arphrd_to_dlt(handle, sock_fd, arptype, device, 1); if (handle->linktype == -1 || handle->linktype == DLT_LINUX_SLL || handle->linktype == DLT_LINUX_IRDA || @@ -3136,14 +3449,14 @@ activate_new(pcap_t *handle) * kernels) - reopen in cooked mode. */ if (close(sock_fd) == -1) { - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_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, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "socket: %s", pcap_strerror(errno)); if (errno == EPERM || errno == EACCES) { /* @@ -3178,7 +3491,7 @@ activate_new(pcap_t *handle) * update "map_arphrd_to_dlt()" * to handle the new type. */ - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "arptype %d not " "supported by libpcap - " "falling back to cooked " @@ -3268,7 +3581,7 @@ activate_new(pcap_t *handle) 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, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "setsockopt: %s", pcap_strerror(errno)); close(sock_fd); return PCAP_ERROR; @@ -3281,7 +3594,7 @@ activate_new(pcap_t *handle) val = 1; if (setsockopt(sock_fd, SOL_PACKET, PACKET_AUXDATA, &val, sizeof(val)) == -1 && errno != ENOPROTOOPT) { - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "setsockopt: %s", pcap_strerror(errno)); close(sock_fd); return PCAP_ERROR; @@ -3311,15 +3624,24 @@ activate_new(pcap_t *handle) /* * Set the offset at which to insert VLAN tags. + * That should be the offset of the type field. */ switch (handle->linktype) { case DLT_EN10MB: + /* + * The type field is after the destination and source + * MAC address. + */ handlep->vlan_offset = 2 * ETH_ALEN; break; case DLT_LINUX_SLL: - handlep->vlan_offset = 14; + /* + * The type field is in the last 2 bytes of the + * DLT_LINUX_SLL header. + */ + handlep->vlan_offset = SLL_HDR_LEN - 2; break; default: @@ -3332,7 +3654,7 @@ activate_new(pcap_t *handle) int nsec_tstamps = 1; if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMPNS, &nsec_tstamps, sizeof(nsec_tstamps)) < 0) { - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "setsockopt: unable to set SO_TIMESTAMPNS"); + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "setsockopt: unable to set SO_TIMESTAMPNS"); close(sock_fd); return PCAP_ERROR; } @@ -3344,6 +3666,23 @@ activate_new(pcap_t *handle) */ handle->fd = sock_fd; +#if defined(SO_BPF_EXTENSIONS) && defined(SKF_AD_VLAN_TAG_PRESENT) + /* + * Can we generate special code for VLAN checks? + * (XXX - what if we need the special code but it's not supported + * by the OS? Is that possible?) + */ + if (getsockopt(sock_fd, SOL_SOCKET, SO_BPF_EXTENSIONS, + &bpf_extensions, &len) == 0) { + if (bpf_extensions >= SKF_AD_VLAN_TAG_PRESENT) { + /* + * Yes, we can. Request that we do so. + */ + handle->bpf_codegen_flags |= BPF_SPECIAL_VLAN_HANDLING; + } + } +#endif /* defined(SO_BPF_EXTENSIONS) && defined(SKF_AD_VLAN_TAG_PRESENT) */ + return 1; #else /* HAVE_PF_PACKET_SOCKETS */ strlcpy(ebuf, @@ -3366,7 +3705,7 @@ activate_new(pcap_t *handle) * On error, returns -1, and sets *status to the appropriate error code; * if that is PCAP_ERROR, sets handle->errbuf to the appropriate message. */ -static int +static int activate_mmap(pcap_t *handle, int *status) { struct pcap_linux *handlep = handle->priv; @@ -3378,7 +3717,7 @@ activate_mmap(pcap_t *handle, int *status) */ handlep->oneshot_buffer = malloc(handle->snapshot); if (handlep->oneshot_buffer == NULL) { - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "can't allocate oneshot buffer: %s", pcap_strerror(errno)); *status = PCAP_ERROR; @@ -3427,6 +3766,9 @@ activate_mmap(pcap_t *handle, int *status) case TPACKET_V1: handle->read_op = pcap_read_linux_mmap_v1; break; + case TPACKET_V1_64: + handle->read_op = pcap_read_linux_mmap_v1_64; + break; #ifdef HAVE_TPACKET2 case TPACKET_V2: handle->read_op = pcap_read_linux_mmap_v2; @@ -3447,7 +3789,7 @@ activate_mmap(pcap_t *handle, int *status) return 1; } #else /* HAVE_PACKET_RING */ -static int +static int activate_mmap(pcap_t *handle _U_, int *status _U_) { return 0; @@ -3471,13 +3813,16 @@ init_tpacket(pcap_t *handle, int version, const char *version_str) int val = version; socklen_t len = sizeof(val); - /* Probe whether kernel supports the specified TPACKET version */ + /* + * Probe whether kernel supports the specified TPACKET version; + * this also gets the length of the header for that version. + */ if (getsockopt(handle->fd, SOL_PACKET, PACKET_HDRLEN, &val, &len) < 0) { if (errno == ENOPROTOOPT || errno == EINVAL) return 1; /* no */ /* Failed to even find out; this is a fatal error. */ - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "can't get %s header len on packet socket: %s", version_str, pcap_strerror(errno)); @@ -3488,7 +3833,7 @@ init_tpacket(pcap_t *handle, int version, const char *version_str) val = version; if (setsockopt(handle->fd, SOL_PACKET, PACKET_VERSION, &val, sizeof(val)) < 0) { - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "can't activate %s on packet socket: %s", version_str, pcap_strerror(errno)); @@ -3500,7 +3845,7 @@ init_tpacket(pcap_t *handle, int version, const char *version_str) val = VLAN_TAG_LEN; if (setsockopt(handle->fd, SOL_PACKET, PACKET_RESERVE, &val, sizeof(val)) < 0) { - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "can't set up reserve on packet socket: %s", pcap_strerror(errno)); return -1; @@ -3511,6 +3856,36 @@ init_tpacket(pcap_t *handle, int version, const char *version_str) #endif /* defined HAVE_TPACKET2 || defined HAVE_TPACKET3 */ /* + * If the instruction set for which we're compiling has both 32-bit + * and 64-bit versions, and Linux support for the 64-bit version + * predates TPACKET_V2, define ISA_64_BIT as the .machine value + * you get from uname() for the 64-bit version. Otherwise, leave + * it undefined. (This includes ARM, which has a 64-bit version, + * but Linux support for it appeared well after TPACKET_V2 support + * did, so there should never be a case where 32-bit ARM code is + * running o a 64-bit kernel that only supports TPACKET_V1.) + * + * If we've omitted your favorite such architecture, please contribute + * a patch. (No patch is needed for architectures that are 32-bit-only + * or for which Linux has no support for 32-bit userland - or for which, + * as noted, 64-bit support appeared in Linux after TPACKET_V2 support + * did.) + */ +#if defined(__i386__) +#define ISA_64_BIT "x86_64" +#elif defined(__ppc__) +#define ISA_64_BIT "ppc64" +#elif defined(__sparc__) +#define ISA_64_BIT "sparc64" +#elif defined(__s390__) +#define ISA_64_BIT "s390x" +#elif defined(__mips__) +#define ISA_64_BIT "mips64" +#elif defined(__hppa__) +#define ISA_64_BIT "parisc64" +#endif + +/* * Attempt to set the socket to version 3 of the memory-mapped header and, * if that fails because version 3 isn't supported, attempt to fall * back to version 2. If version 2 isn't supported, just leave it at @@ -3527,11 +3902,10 @@ prepare_tpacket_socket(pcap_t *handle) int ret; #endif - handlep->tp_version = TPACKET_V1; - handlep->tp_hdrlen = sizeof(struct tpacket_hdr); - #ifdef HAVE_TPACKET3 /* + * Try setting the version to TPACKET_V3. + * * The only mode in which buffering is done on PF_PACKET * sockets, so that packets might not be delivered * immediately, is TPACKET_V3 mode. @@ -3540,28 +3914,87 @@ prepare_tpacket_socket(pcap_t *handle) * if the user has requested immediate mode, we don't * use TPACKET_V3. */ - if (handle->opt.immediate) - ret = 1; /* pretend TPACKET_V3 couldn't be set */ - else + if (!handle->opt.immediate) { ret = init_tpacket(handle, TPACKET_V3, "TPACKET_V3"); - if (-1 == ret) { - /* Error during setting up TPACKET_V3. */ - return -1; - } else if (1 == ret) { - /* TPACKET_V3 not supported - fall back to TPACKET_V2. */ + if (ret == 0) { + /* + * Success. + */ + return 1; + } + if (ret == -1) { + /* + * We failed for some reason other than "the + * kernel doesn't support TPACKET_V3". + */ + return -1; + } + } #endif /* HAVE_TPACKET3 */ #ifdef HAVE_TPACKET2 - ret = init_tpacket(handle, TPACKET_V2, "TPACKET_V2"); - if (-1 == ret) { - /* Error during setting up TPACKET_V2. */ - return -1; - } + /* + * Try setting the version to TPACKET_V2. + */ + ret = init_tpacket(handle, TPACKET_V2, "TPACKET_V2"); + if (ret == 0) { + /* + * Success. + */ + return 1; + } + if (ret == -1) { + /* + * We failed for some reason other than "the + * kernel doesn't support TPACKET_V2". + */ + return -1; + } #endif /* HAVE_TPACKET2 */ -#ifdef HAVE_TPACKET3 + /* + * OK, we're using TPACKET_V1, as that's all the kernel supports. + */ + handlep->tp_version = TPACKET_V1; + handlep->tp_hdrlen = sizeof(struct tpacket_hdr); + +#ifdef ISA_64_BIT + /* + * 32-bit userspace + 64-bit kernel + TPACKET_V1 are not compatible with + * each other due to platform-dependent data type size differences. + * + * If we have a 32-bit userland and a 64-bit kernel, use an + * internally-defined TPACKET_V1_64, with which we use a 64-bit + * version of the data structures. + */ + if (sizeof(long) == 4) { + /* + * This is 32-bit code. + */ + struct utsname utsname; + + if (uname(&utsname) == -1) { + /* + * Failed. + */ + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + "uname failed: %s", pcap_strerror(errno)); + return -1; + } + if (strcmp(utsname.machine, ISA_64_BIT) == 0) { + /* + * uname() tells us the machine is 64-bit, + * so we presumably have a 64-bit kernel. + * + * XXX - this presumes that uname() won't lie + * in 32-bit code and claim that the machine + * has the 32-bit version of the ISA. + */ + handlep->tp_version = TPACKET_V1_64; + handlep->tp_hdrlen = sizeof(struct tpacket_hdr_64); + } } -#endif /* HAVE_TPACKET3 */ +#endif return 1; } @@ -3605,6 +4038,7 @@ create_ring(pcap_t *handle, int *status) switch (handlep->tp_version) { case TPACKET_V1: + case TPACKET_V1_64: #ifdef HAVE_TPACKET2 case TPACKET_V2: #endif @@ -3649,14 +4083,14 @@ create_ring(pcap_t *handle, int *status) return -1; } if (!offload) { - mtu = iface_get_mtu(handle->fd, handle->opt.source, + mtu = iface_get_mtu(handle->fd, handle->opt.device, handle->errbuf); if (mtu == -1) { *status = PCAP_ERROR; return -1; } - if (frame_size > mtu + 18) - frame_size = mtu + 18; + if (frame_size > (unsigned int)mtu + 18) + frame_size = (unsigned int)mtu + 18; } } @@ -3666,7 +4100,7 @@ create_ring(pcap_t *handle, int *status) len = sizeof(sk_type); if (getsockopt(handle->fd, SOL_SOCKET, SO_TYPE, &sk_type, &len) < 0) { - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "getsockopt: %s", pcap_strerror(errno)); *status = PCAP_ERROR; return -1; @@ -3681,7 +4115,7 @@ create_ring(pcap_t *handle, int *status) * PACKET_RESERVE", in which case we fall back * as best we can. */ - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "getsockopt: %s", pcap_strerror(errno)); *status = PCAP_ERROR; return -1; @@ -3736,14 +4170,20 @@ create_ring(pcap_t *handle, int *status) req.tp_frame_nr = handle->opt.buffer_size/req.tp_frame_size; break; #endif + default: + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + "Internal error: unknown TPACKET_ value %u", + handlep->tp_version); + *status = PCAP_ERROR; + return -1; } - /* compute the minumum block size that will handle this frame. - * The block has to be page size aligned. - * The max block size allowed by the kernel is arch-dependent and + /* compute the minumum block size that will handle this frame. + * The block has to be page size aligned. + * The max block size allowed by the kernel is arch-dependent and * it's not explicitly checked here. */ req.tp_block_size = getpagesize(); - while (req.tp_block_size < req.tp_frame_size) + while (req.tp_block_size < req.tp_frame_size) req.tp_block_size <<= 1; frames_per_block = req.tp_block_size/req.tp_frame_size; @@ -3784,7 +4224,7 @@ create_ring(pcap_t *handle, int *status) hwconfig.rx_filter = HWTSTAMP_FILTER_ALL; memset(&ifr, 0, sizeof(ifr)); - strlcpy(ifr.ifr_name, handle->opt.source, sizeof(ifr.ifr_name)); + strlcpy(ifr.ifr_name, handle->opt.device, sizeof(ifr.ifr_name)); ifr.ifr_data = (void *)&hwconfig; if (ioctl(handle->fd, SIOCSHWTSTAMP, &ifr) < 0) { @@ -3802,18 +4242,26 @@ create_ring(pcap_t *handle, int *status) return -1; case EOPNOTSUPP: + case ERANGE: /* * Treat this as a warning, as the * only way to fix the warning is to * get an adapter that supports hardware - * time stamps. We'll just fall back - * on the standard host time stamps. + * time stamps for *all* packets. + * (ERANGE means "we support hardware + * time stamps, but for packets matching + * that particular filter", so it means + * "we don't support hardware time stamps + * for all incoming packets" here.) + * + * We'll just fall back on the standard + * host time stamps. */ *status = PCAP_WARNING_TSTAMP_TYPE_NOTSUP; break; default: - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "SIOCSHWTSTAMP failed: %s", pcap_strerror(errno)); *status = PCAP_ERROR; @@ -3841,8 +4289,8 @@ create_ring(pcap_t *handle, int *status) } if (setsockopt(handle->fd, SOL_PACKET, PACKET_TIMESTAMP, (void *)×ource, sizeof(timesource))) { - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, - "can't set PACKET_TIMESTAMP: %s", + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + "can't set PACKET_TIMESTAMP: %s", pcap_strerror(errno)); *status = PCAP_ERROR; return -1; @@ -3857,7 +4305,7 @@ retry: /* req.tp_frame_nr is requested to match frames_per_block*req.tp_block_nr */ req.tp_frame_nr = req.tp_block_nr * frames_per_block; - + #ifdef HAVE_TPACKET3 /* timeout value to retire block - use the configured buffering timeout, or default if <0. */ req.tp_retire_blk_tov = (handlep->timeout>=0)?handlep->timeout:0; @@ -3891,7 +4339,7 @@ retry: */ return 0; } - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "can't create rx ring on packet socket: %s", pcap_strerror(errno)); *status = PCAP_ERROR; @@ -3903,7 +4351,7 @@ retry: handlep->mmapbuf = mmap(0, handlep->mmapbuflen, PROT_READ|PROT_WRITE, MAP_SHARED, handle->fd, 0); if (handlep->mmapbuf == MAP_FAILED) { - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "can't mmap rx ring: %s", pcap_strerror(errno)); /* clear the allocated ring on error*/ @@ -3916,7 +4364,7 @@ retry: handle->cc = req.tp_frame_nr; handle->buffer = malloc(handle->cc * sizeof(union thdr *)); if (!handle->buffer) { - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "can't allocate ring of frame headers: %s", pcap_strerror(errno)); @@ -3930,7 +4378,7 @@ retry: for (i=0; i<req.tp_block_nr; ++i) { void *base = &handlep->mmapbuf[i*req.tp_block_size]; for (j=0; j<frames_per_block; ++j, ++handle->offset) { - RING_GET_FRAME(handle) = base; + RING_GET_CURRENT_FRAME(handle) = base; base += req.tp_frame_size; } } @@ -3949,13 +4397,14 @@ destroy_ring(pcap_t *handle) /* tell the kernel to destroy the ring*/ struct tpacket_req req; memset(&req, 0, sizeof(req)); - setsockopt(handle->fd, SOL_PACKET, PACKET_RX_RING, + /* do not test for setsockopt failure, as we can't recover from any error */ + (void)setsockopt(handle->fd, SOL_PACKET, PACKET_RX_RING, (void *) &req, sizeof(req)); /* if ring is mapped, unmap it*/ if (handlep->mmapbuf) { /* do not test for mmap failure, as we can't recover from any error */ - munmap(handlep->mmapbuf, handlep->mmapbuflen); + (void)munmap(handlep->mmapbuf, handlep->mmapbuflen); handlep->mmapbuf = NULL; } } @@ -3989,7 +4438,7 @@ pcap_oneshot_mmap(u_char *user, const struct pcap_pkthdr *h, memcpy(handlep->oneshot_buffer, bytes, h->caplen); *sp->pkt = handlep->oneshot_buffer; } - + static void pcap_cleanup_linux_mmap( pcap_t *handle ) { @@ -4042,151 +4491,131 @@ pcap_setnonblock_mmap(pcap_t *p, int nonblock, char *errbuf) handlep->timeout = ~handlep->timeout; } } + /* Update the timeout to use in poll(). */ + set_poll_timeout(handlep); return 0; } -static inline union thdr * -pcap_get_ring_frame(pcap_t *handle, int status) +/* + * Get the status field of the ring buffer frame at a specified offset. + */ +static inline int +pcap_get_ring_frame_status(pcap_t *handle, int offset) { struct pcap_linux *handlep = handle->priv; union thdr h; - h.raw = RING_GET_FRAME(handle); + h.raw = RING_GET_FRAME_AT(handle, offset); switch (handlep->tp_version) { case TPACKET_V1: - if (status != (h.h1->tp_status ? TP_STATUS_USER : - TP_STATUS_KERNEL)) - return NULL; + return (h.h1->tp_status); + break; + case TPACKET_V1_64: + return (h.h1_64->tp_status); break; #ifdef HAVE_TPACKET2 case TPACKET_V2: - if (status != (h.h2->tp_status ? TP_STATUS_USER : - TP_STATUS_KERNEL)) - return NULL; + return (h.h2->tp_status); break; #endif #ifdef HAVE_TPACKET3 case TPACKET_V3: - if (status != (h.h3->hdr.bh1.block_status ? TP_STATUS_USER : - TP_STATUS_KERNEL)) - return NULL; + return (h.h3->hdr.bh1.block_status); break; #endif } - return h.raw; + /* This should not happen. */ + return 0; } #ifndef POLLRDHUP #define POLLRDHUP 0 #endif -/* wait for frames availability.*/ +/* + * Block waiting for frames to be available. + */ static int pcap_wait_for_frames_mmap(pcap_t *handle) { - if (!pcap_get_ring_frame(handle, TP_STATUS_USER)) { - struct pcap_linux *handlep = handle->priv; - int timeout; - char c; - struct pollfd pollinfo; - int ret; + struct pcap_linux *handlep = handle->priv; + char c; + struct pollfd pollinfo; + int ret; - pollinfo.fd = handle->fd; - pollinfo.events = POLLIN; + pollinfo.fd = handle->fd; + pollinfo.events = POLLIN; - if (handlep->timeout == 0) { -#ifdef HAVE_TPACKET3 + do { + /* + * Yes, we do this even in non-blocking mode, as it's + * the only way to get error indications from a + * tpacket socket. + * + * The timeout is 0 in non-blocking mode, so poll() + * returns immediately. + */ + ret = poll(&pollinfo, 1, handlep->poll_timeout); + if (ret < 0 && errno != EINTR) { + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + "can't poll on packet socket: %s", + pcap_strerror(errno)); + return PCAP_ERROR; + } else if (ret > 0 && + (pollinfo.revents & (POLLHUP|POLLRDHUP|POLLERR|POLLNVAL))) { /* - * XXX - due to a set of (mis)features in the - * TPACKET_V3 kernel code, blocking forever with - * a TPACKET_V3 socket can, if few packets - * are arriving and passing the socket filter, - * cause most packets to be dropped. See - * libpcap issue #335 for the full painful - * story. The workaround is to have poll() - * time out very quickly, so we grab the - * frames handed to us, and return them to - * the kernel, ASAP. - * - * If those issues are ever fixed, we might - * want to check the kernel version and block - * forever with TPACKET_V3 if we're running - * with a kernel that has the fix. + * There's some indication other than + * "you can read on this descriptor" on + * the descriptor. */ - if (handlep->tp_version == TPACKET_V3) - timeout = 1; /* don't block for very long */ - else -#endif - timeout = -1; /* block forever */ - } else if (handlep->timeout > 0) - timeout = handlep->timeout; /* block for that amount of time */ - else - timeout = 0; /* non-blocking mode - poll to pick up errors */ - do { - ret = poll(&pollinfo, 1, timeout); - if (ret < 0 && errno != EINTR) { - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, - "can't poll on packet socket: %s", - pcap_strerror(errno)); + if (pollinfo.revents & (POLLHUP | POLLRDHUP)) { + pcap_snprintf(handle->errbuf, + PCAP_ERRBUF_SIZE, + "Hangup on packet socket"); return PCAP_ERROR; - } else if (ret > 0 && - (pollinfo.revents & (POLLHUP|POLLRDHUP|POLLERR|POLLNVAL))) { + } + if (pollinfo.revents & POLLERR) { /* - * There's some indication other than - * "you can read on this descriptor" on - * the descriptor. + * A recv() will give us the actual error code. + * + * XXX - make the socket non-blocking? */ - if (pollinfo.revents & (POLLHUP | POLLRDHUP)) { - snprintf(handle->errbuf, - PCAP_ERRBUF_SIZE, - "Hangup on packet socket"); - return PCAP_ERROR; - } - if (pollinfo.revents & POLLERR) { + if (recv(handle->fd, &c, sizeof c, + MSG_PEEK) != -1) + continue; /* what, no error? */ + if (errno == ENETDOWN) { /* - * A recv() will give us the - * actual error code. + * The device on which we're + * capturing went away. * - * XXX - make the socket non-blocking? + * XXX - we should really return + * PCAP_ERROR_IFACE_NOT_UP, but + * pcap_dispatch() etc. aren't + * defined to return that. */ - if (recv(handle->fd, &c, sizeof c, - MSG_PEEK) != -1) - continue; /* what, no error? */ - if (errno == ENETDOWN) { - /* - * 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(handle->errbuf, - PCAP_ERRBUF_SIZE, - "The interface went down"); - } else { - snprintf(handle->errbuf, - PCAP_ERRBUF_SIZE, - "Error condition on packet socket: %s", - strerror(errno)); - } - return PCAP_ERROR; - } - if (pollinfo.revents & POLLNVAL) { - snprintf(handle->errbuf, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, - "Invalid polling request on packet socket"); - return PCAP_ERROR; + "The interface went down"); + } else { + pcap_snprintf(handle->errbuf, + PCAP_ERRBUF_SIZE, + "Error condition on packet socket: %s", + strerror(errno)); } + return PCAP_ERROR; } - /* check for break loop condition on interrupted syscall*/ - if (handle->break_loop) { - handle->break_loop = 0; - return PCAP_ERROR_BREAK; + if (pollinfo.revents & POLLNVAL) { + pcap_snprintf(handle->errbuf, + PCAP_ERRBUF_SIZE, + "Invalid polling request on packet socket"); + return PCAP_ERROR; } - } while (ret < 0); - } + } + /* check for break loop condition on interrupted syscall*/ + if (handle->break_loop) { + handle->break_loop = 0; + return PCAP_ERROR_BREAK; + } + } while (ret < 0); return 0; } @@ -4202,7 +4631,8 @@ static int pcap_handle_packet_mmap( unsigned int tp_sec, unsigned int tp_usec, int tp_vlan_tci_valid, - __u16 tp_vlan_tci) + __u16 tp_vlan_tci, + __u16 tp_vlan_tpid) { struct pcap_linux *handlep = handle->priv; unsigned char *bp; @@ -4211,9 +4641,9 @@ static int pcap_handle_packet_mmap( /* perform sanity check on internal offset. */ if (tp_mac + tp_snaplen > handle->bufsize) { - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "corrupted frame on kernel ring mac " - "offset %d + caplen %d > frame len %d", + "offset %u + caplen %u > frame len %d", tp_mac, tp_snaplen, handle->bufsize); return -1; } @@ -4228,22 +4658,9 @@ static int pcap_handle_packet_mmap( * the filter when the ring became empty, but it can possibly * happen a lot later... */ bp = frame + tp_mac; - if (handlep->filter_in_userland && handle->fcode.bf_insns && - (bpf_filter(handle->fcode.bf_insns, bp, - tp_len, tp_snaplen) == 0)) - return 0; - - sll = (void *)frame + TPACKET_ALIGN(handlep->tp_hdrlen); - if (!linux_check_direction(handle, sll)) - return 0; - - /* get required packet info from ring header */ - pcaphdr.ts.tv_sec = tp_sec; - pcaphdr.ts.tv_usec = tp_usec; - pcaphdr.caplen = tp_snaplen; - pcaphdr.len = tp_len; /* if required build in place the sll header*/ + sll = (void *)frame + TPACKET_ALIGN(handlep->tp_hdrlen); if (handlep->cooked) { struct sll_header *hdrp; @@ -4256,7 +4673,7 @@ static int pcap_handle_packet_mmap( */ bp -= SLL_HDR_LEN; - /*/* + /* * Let's make sure that's past the end of * the tpacket header, i.e. >= * ((u_char *)thdr + TPACKET_HDRLEN), so we @@ -4266,7 +4683,7 @@ static int pcap_handle_packet_mmap( if (bp < (u_char *)frame + TPACKET_ALIGN(handlep->tp_hdrlen) + sizeof(struct sockaddr_ll)) { - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "cooked-mode frame doesn't have room for sll header"); return -1; } @@ -4281,7 +4698,30 @@ static int pcap_handle_packet_mmap( hdrp->sll_halen = htons(sll->sll_halen); memcpy(hdrp->sll_addr, sll->sll_addr, SLL_ADDRLEN); hdrp->sll_protocol = sll->sll_protocol; + } + + if (handlep->filter_in_userland && handle->fcode.bf_insns) { + struct bpf_aux_data aux_data; + + aux_data.vlan_tag = tp_vlan_tci & 0x0fff; + aux_data.vlan_tag_present = tp_vlan_tci_valid; + if (bpf_filter_with_aux_data(handle->fcode.bf_insns, bp, + tp_len, tp_snaplen, &aux_data) == 0) + return 0; + } + + if (!linux_check_direction(handle, sll)) + return 0; + + /* get required packet info from ring header */ + pcaphdr.ts.tv_sec = tp_sec; + pcaphdr.ts.tv_usec = tp_usec; + pcaphdr.caplen = tp_snaplen; + pcaphdr.len = tp_len; + + /* if required build in place the sll header*/ + if (handlep->cooked) { /* update packet len */ pcaphdr.caplen += SLL_HDR_LEN; pcaphdr.len += SLL_HDR_LEN; @@ -4294,13 +4734,24 @@ static int pcap_handle_packet_mmap( { struct vlan_tag *tag; + /* + * Move everything in the header, except the type field, + * down VLAN_TAG_LEN bytes, to allow us to insert the + * VLAN tag between that stuff and the type field. + */ bp -= VLAN_TAG_LEN; memmove(bp, bp + VLAN_TAG_LEN, handlep->vlan_offset); + /* + * Now insert the tag. + */ tag = (struct vlan_tag *)(bp + handlep->vlan_offset); - tag->vlan_tpid = htons(ETH_P_8021Q); + tag->vlan_tpid = htons(tp_vlan_tpid); tag->vlan_tci = htons(tp_vlan_tci); + /* + * Add the tag to the packet lengths. + */ pcaphdr.caplen += VLAN_TAG_LEN; pcaphdr.len += VLAN_TAG_LEN; } @@ -4315,7 +4766,7 @@ static int pcap_handle_packet_mmap( * Trim the snapshot length to be no longer than the * specified snapshot length. */ - if (pcaphdr.caplen > handle->snapshot) + if (pcaphdr.caplen > (bpf_u_int32)handle->snapshot) pcaphdr.caplen = handle->snapshot; /* pass the packet to the user */ @@ -4329,22 +4780,32 @@ pcap_read_linux_mmap_v1(pcap_t *handle, int max_packets, pcap_handler callback, u_char *user) { struct pcap_linux *handlep = handle->priv; + union thdr h; int pkts = 0; int ret; /* wait for frames availability.*/ - ret = pcap_wait_for_frames_mmap(handle); - if (ret) { - return ret; + h.raw = RING_GET_CURRENT_FRAME(handle); + if (h.h1->tp_status == TP_STATUS_KERNEL) { + /* + * The current frame is owned by the kernel; wait for + * a frame to be handed to us. + */ + ret = pcap_wait_for_frames_mmap(handle); + if (ret) { + return ret; + } } /* non-positive values of max_packets are used to require all * packets currently available in the ring */ while ((pkts < max_packets) || PACKET_COUNT_IS_UNLIMITED(max_packets)) { - union thdr h; - - h.raw = pcap_get_ring_frame(handle, TP_STATUS_USER); - if (!h.raw) + /* + * Get the current ring buffer frame, and break if + * it's still owned by the kernel. + */ + h.raw = RING_GET_CURRENT_FRAME(handle); + if (h.h1->tp_status == TP_STATUS_KERNEL) break; ret = pcap_handle_packet_mmap( @@ -4358,6 +4819,7 @@ pcap_read_linux_mmap_v1(pcap_t *handle, int max_packets, pcap_handler callback, h.h1->tp_sec, h.h1->tp_usec, 0, + 0, 0); if (ret == 1) { pkts++; @@ -4397,28 +4859,122 @@ pcap_read_linux_mmap_v1(pcap_t *handle, int max_packets, pcap_handler callback, return pkts; } +static int +pcap_read_linux_mmap_v1_64(pcap_t *handle, int max_packets, pcap_handler callback, + u_char *user) +{ + struct pcap_linux *handlep = handle->priv; + union thdr h; + int pkts = 0; + int ret; + + /* wait for frames availability.*/ + h.raw = RING_GET_CURRENT_FRAME(handle); + if (h.h1_64->tp_status == TP_STATUS_KERNEL) { + /* + * The current frame is owned by the kernel; wait for + * a frame to be handed to us. + */ + ret = pcap_wait_for_frames_mmap(handle); + if (ret) { + return ret; + } + } + + /* non-positive values of max_packets are used to require all + * packets currently available in the ring */ + while ((pkts < max_packets) || PACKET_COUNT_IS_UNLIMITED(max_packets)) { + /* + * Get the current ring buffer frame, and break if + * it's still owned by the kernel. + */ + h.raw = RING_GET_CURRENT_FRAME(handle); + if (h.h1_64->tp_status == TP_STATUS_KERNEL) + break; + + ret = pcap_handle_packet_mmap( + handle, + callback, + user, + h.raw, + h.h1_64->tp_len, + h.h1_64->tp_mac, + h.h1_64->tp_snaplen, + h.h1_64->tp_sec, + h.h1_64->tp_usec, + 0, + 0, + 0); + if (ret == 1) { + pkts++; + handlep->packets_read++; + } else if (ret < 0) { + return ret; + } + + /* + * Hand this block back to the kernel, and, if we're + * counting blocks that need to be filtered in userland + * after having been filtered by the kernel, count + * the one we've just processed. + */ + h.h1_64->tp_status = TP_STATUS_KERNEL; + if (handlep->blocks_to_filter_in_userland > 0) { + handlep->blocks_to_filter_in_userland--; + if (handlep->blocks_to_filter_in_userland == 0) { + /* + * No more blocks need to be filtered + * in userland. + */ + handlep->filter_in_userland = 0; + } + } + + /* next block */ + if (++handle->offset >= handle->cc) + handle->offset = 0; + + /* check for break loop condition*/ + if (handle->break_loop) { + handle->break_loop = 0; + return PCAP_ERROR_BREAK; + } + } + return pkts; +} + #ifdef HAVE_TPACKET2 static int pcap_read_linux_mmap_v2(pcap_t *handle, int max_packets, pcap_handler callback, u_char *user) { struct pcap_linux *handlep = handle->priv; + union thdr h; int pkts = 0; int ret; /* wait for frames availability.*/ - ret = pcap_wait_for_frames_mmap(handle); - if (ret) { - return ret; + h.raw = RING_GET_CURRENT_FRAME(handle); + if (h.h2->tp_status == TP_STATUS_KERNEL) { + /* + * The current frame is owned by the kernel; wait for + * a frame to be handed to us. + */ + ret = pcap_wait_for_frames_mmap(handle); + if (ret) { + return ret; + } } /* non-positive values of max_packets are used to require all * packets currently available in the ring */ while ((pkts < max_packets) || PACKET_COUNT_IS_UNLIMITED(max_packets)) { - union thdr h; - - h.raw = pcap_get_ring_frame(handle, TP_STATUS_USER); - if (!h.raw) + /* + * Get the current ring buffer frame, and break if + * it's still owned by the kernel. + */ + h.raw = RING_GET_CURRENT_FRAME(handle); + if (h.h2->tp_status == TP_STATUS_KERNEL) break; ret = pcap_handle_packet_mmap( @@ -4436,7 +4992,8 @@ pcap_read_linux_mmap_v2(pcap_t *handle, int max_packets, pcap_handler callback, #else h.h2->tp_vlan_tci != 0, #endif - h.h2->tp_vlan_tci); + h.h2->tp_vlan_tci, + VLAN_TPID(h.h2, h.h2)); if (ret == 1) { pkts++; handlep->packets_read++; @@ -4489,13 +5046,20 @@ pcap_read_linux_mmap_v3(pcap_t *handle, int max_packets, pcap_handler callback, again: if (handlep->current_packet == NULL) { /* wait for frames availability.*/ - ret = pcap_wait_for_frames_mmap(handle); - if (ret) { - return ret; + h.raw = RING_GET_CURRENT_FRAME(handle); + if (h.h3->hdr.bh1.block_status == TP_STATUS_KERNEL) { + /* + * The current frame is owned by the kernel; wait + * for a frame to be handed to us. + */ + ret = pcap_wait_for_frames_mmap(handle); + if (ret) { + return ret; + } } } - h.raw = pcap_get_ring_frame(handle, TP_STATUS_USER); - if (!h.raw) { + h.raw = RING_GET_CURRENT_FRAME(handle); + if (h.h3->hdr.bh1.block_status == TP_STATUS_KERNEL) { if (pkts == 0 && handlep->timeout == 0) { /* Block until we see a packet. */ goto again; @@ -4506,21 +5070,30 @@ again: /* non-positive values of max_packets are used to require all * packets currently available in the ring */ while ((pkts < max_packets) || PACKET_COUNT_IS_UNLIMITED(max_packets)) { + int packets_to_read; + if (handlep->current_packet == NULL) { - h.raw = pcap_get_ring_frame(handle, TP_STATUS_USER); - if (!h.raw) + h.raw = RING_GET_CURRENT_FRAME(handle); + if (h.h3->hdr.bh1.block_status == TP_STATUS_KERNEL) break; handlep->current_packet = h.raw + h.h3->hdr.bh1.offset_to_first_pkt; handlep->packets_left = h.h3->hdr.bh1.num_pkts; } - int packets_to_read = handlep->packets_left; + packets_to_read = handlep->packets_left; - if (!PACKET_COUNT_IS_UNLIMITED(max_packets) && packets_to_read > max_packets) { - packets_to_read = max_packets; + if (!PACKET_COUNT_IS_UNLIMITED(max_packets) && + packets_to_read > (max_packets - pkts)) { + /* + * We've been given a maximum number of packets + * to process, and there are more packets in + * this buffer than that. Only process enough + * of them to get us up to that maximum. + */ + packets_to_read = max_packets - pkts; } - while(packets_to_read--) { + while (packets_to_read-- && !handle->break_loop) { struct tpacket3_hdr* tp3_hdr = (struct tpacket3_hdr*) handlep->current_packet; ret = pcap_handle_packet_mmap( handle, @@ -4537,7 +5110,8 @@ again: #else tp3_hdr->hv1.tp_vlan_tci != 0, #endif - tp3_hdr->hv1.tp_vlan_tci); + tp3_hdr->hv1.tp_vlan_tci, + VLAN_TPID(tp3_hdr, &tp3_hdr->hv1)); if (ret == 1) { pkts++; handlep->packets_read++; @@ -4590,7 +5164,7 @@ again: } #endif /* HAVE_TPACKET3 */ -static int +static int pcap_setfilter_linux_mmap(pcap_t *handle, struct bpf_program *filter) { struct pcap_linux *handlep = handle->priv; @@ -4624,12 +5198,12 @@ pcap_setfilter_linux_mmap(pcap_t *handle, struct bpf_program *filter) * walk the ring backward and count the free blocks. */ offset = handle->offset; - if (--handle->offset < 0) - handle->offset = handle->cc - 1; + if (--offset < 0) + offset = handle->cc - 1; for (n=0; n < handle->cc; ++n) { - if (--handle->offset < 0) - handle->offset = handle->cc - 1; - if (!pcap_get_ring_frame(handle, TP_STATUS_KERNEL)) + if (--offset < 0) + offset = handle->cc - 1; + if (pcap_get_ring_frame_status(handle, offset) != TP_STATUS_KERNEL) break; } @@ -4650,9 +5224,6 @@ pcap_setfilter_linux_mmap(pcap_t *handle, struct bpf_program *filter) if (n != 0) n--; - /* be careful to not change current ring position */ - handle->offset = offset; - /* * Set the count of blocks worth of packets to filter * in userland to the total number of blocks in the @@ -4686,7 +5257,7 @@ iface_get_id(int fd, const char *device, char *ebuf) strlcpy(ifr.ifr_name, device, sizeof(ifr.ifr_name)); if (ioctl(fd, SIOCGIFINDEX, &ifr) == -1) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE, "SIOCGIFINDEX: %s", pcap_strerror(errno)); return -1; } @@ -4722,7 +5293,7 @@ iface_bind(int fd, int ifindex, char *ebuf) */ return PCAP_ERROR_IFACE_NOT_UP; } else { - snprintf(ebuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE, "bind: %s", pcap_strerror(errno)); return PCAP_ERROR; } @@ -4731,7 +5302,7 @@ iface_bind(int fd, int ifindex, char *ebuf) /* Any pending errors, e.g., network is down? */ if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE, "getsockopt: %s", pcap_strerror(errno)); return 0; } @@ -4746,7 +5317,7 @@ iface_bind(int fd, int ifindex, char *ebuf) */ return PCAP_ERROR_IFACE_NOT_UP; } else if (err > 0) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE, "bind: %s", pcap_strerror(err)); return 0; } @@ -4765,12 +5336,15 @@ has_wext(int sock_fd, const char *device, char *ebuf) { struct iwreq ireq; + if (is_bonding_device(sock_fd, device)) + return 0; /* bonding device, so don't even try */ + strlcpy(ireq.ifr_ifrn.ifrn_name, device, sizeof ireq.ifr_ifrn.ifrn_name); if (ioctl(sock_fd, SIOCGIWNAME, &ireq) >= 0) return 1; /* yes */ - snprintf(ebuf, PCAP_ERRBUF_SIZE, - "%s: SIOCGIWPRIV: %s", device, pcap_strerror(errno)); + pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE, + "%s: SIOCGIWNAME: %s", device, pcap_strerror(errno)); if (errno == ENODEV) return PCAP_ERROR_NO_SUCH_DEVICE; return 0; @@ -4908,7 +5482,7 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device) ireq.u.data.length = 0; ireq.u.data.flags = 0; if (ioctl(sock_fd, SIOCGIWPRIV, &ireq) != -1) { - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "%s: SIOCGIWPRIV with a zero-length buffer didn't fail!", device); return PCAP_ERROR; @@ -4921,7 +5495,7 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device) /* * Failed. */ - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "%s: SIOCGIWPRIV: %s", device, pcap_strerror(errno)); return PCAP_ERROR; @@ -4932,13 +5506,13 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device) */ priv = malloc(ireq.u.data.length * sizeof (struct iw_priv_args)); if (priv == NULL) { - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "malloc: %s", pcap_strerror(errno)); return PCAP_ERROR; } ireq.u.data.pointer = (void *)priv; if (ioctl(sock_fd, SIOCGIWPRIV, &ireq) == -1) { - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "%s: SIOCGIWPRIV: %s", device, pcap_strerror(errno)); free(priv); @@ -5194,7 +5768,7 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device) memset(&ifr, 0, sizeof(ifr)); strlcpy(ifr.ifr_name, device, sizeof(ifr.ifr_name)); if (ioctl(sock_fd, SIOCGIFFLAGS, &ifr) == -1) { - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "%s: Can't get flags: %s", device, strerror(errno)); return PCAP_ERROR; } @@ -5203,7 +5777,7 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device) oldflags = ifr.ifr_flags; ifr.ifr_flags &= ~IFF_UP; if (ioctl(sock_fd, SIOCSIFFLAGS, &ifr) == -1) { - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "%s: Can't set flags: %s", device, strerror(errno)); return PCAP_ERROR; } @@ -5222,7 +5796,7 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device) */ ifr.ifr_flags = oldflags; if (ioctl(sock_fd, SIOCSIFFLAGS, &ifr) == -1) { - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "%s: Can't set flags: %s", device, strerror(errno)); return PCAP_ERROR; } @@ -5306,7 +5880,7 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device) strlcpy(ireq.ifr_ifrn.ifrn_name, device, sizeof ireq.ifr_ifrn.ifrn_name); if (ioctl(sock_fd, SIOCGIWFREQ, &ireq) == -1) { - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "%s: SIOCGIWFREQ: %s", device, pcap_strerror(errno)); return PCAP_ERROR; @@ -5383,7 +5957,7 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device) if (oldflags != 0) { ifr.ifr_flags = oldflags; if (ioctl(sock_fd, SIOCSIFFLAGS, &ifr) == -1) { - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "%s: Can't set flags: %s", device, strerror(errno)); /* @@ -5452,6 +6026,176 @@ enter_rfmon_mode(pcap_t *handle, int sock_fd, const char *device) return 0; } +#if defined(HAVE_LINUX_NET_TSTAMP_H) && defined(PACKET_TIMESTAMP) +/* + * Map SOF_TIMESTAMPING_ values to PCAP_TSTAMP_ values. + */ +static const struct { + int soft_timestamping_val; + int pcap_tstamp_val; +} sof_ts_type_map[3] = { + { SOF_TIMESTAMPING_SOFTWARE, PCAP_TSTAMP_HOST }, + { SOF_TIMESTAMPING_SYS_HARDWARE, PCAP_TSTAMP_ADAPTER }, + { SOF_TIMESTAMPING_RAW_HARDWARE, PCAP_TSTAMP_ADAPTER_UNSYNCED } +}; +#define NUM_SOF_TIMESTAMPING_TYPES (sizeof sof_ts_type_map / sizeof sof_ts_type_map[0]) + +/* + * Set the list of time stamping types to include all types. + */ +static void +iface_set_all_ts_types(pcap_t *handle) +{ + u_int i; + + handle->tstamp_type_count = NUM_SOF_TIMESTAMPING_TYPES; + handle->tstamp_type_list = malloc(NUM_SOF_TIMESTAMPING_TYPES * sizeof(u_int)); + for (i = 0; i < NUM_SOF_TIMESTAMPING_TYPES; i++) + handle->tstamp_type_list[i] = sof_ts_type_map[i].pcap_tstamp_val; +} + +#ifdef ETHTOOL_GET_TS_INFO +/* + * Get a list of time stamping capabilities. + */ +static int +iface_ethtool_get_ts_info(const char *device, pcap_t *handle, char *ebuf) +{ + int fd; + struct ifreq ifr; + struct ethtool_ts_info info; + int num_ts_types; + u_int i, j; + + /* + * This doesn't apply to the "any" device; you can't say "turn on + * hardware time stamping for all devices that exist now and arrange + * that it be turned on for any device that appears in the future", + * and not all devices even necessarily *support* hardware time + * stamping, so don't report any time stamp types. + */ + if (strcmp(device, "any") == 0) { + handle->tstamp_type_list = NULL; + return 0; + } + + /* + * Create a socket from which to fetch time stamping capabilities. + */ + fd = socket(PF_UNIX, SOCK_RAW, 0); + if (fd < 0) { + (void)pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE, + "socket for SIOCETHTOOL(ETHTOOL_GET_TS_INFO): %s", pcap_strerror(errno)); + return -1; + } + + memset(&ifr, 0, sizeof(ifr)); + strlcpy(ifr.ifr_name, device, sizeof(ifr.ifr_name)); + memset(&info, 0, sizeof(info)); + info.cmd = ETHTOOL_GET_TS_INFO; + ifr.ifr_data = (caddr_t)&info; + if (ioctl(fd, SIOCETHTOOL, &ifr) == -1) { + int save_errno = errno; + + close(fd); + switch (save_errno) { + + case EOPNOTSUPP: + case EINVAL: + /* + * OK, this OS version or driver doesn't support + * asking for the time stamping types, so let's + * just return all the possible types. + */ + iface_set_all_ts_types(handle); + return 0; + + case ENODEV: + /* + * OK, no such device. + * The user will find that out when they try to + * activate the device; just return an empty + * list of time stamp types. + */ + handle->tstamp_type_list = NULL; + return 0; + + default: + /* + * Other error. + */ + pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE, + "%s: SIOCETHTOOL(ETHTOOL_GET_TS_INFO) ioctl failed: %s", device, + strerror(save_errno)); + return -1; + } + } + close(fd); + + /* + * Do we support hardware time stamping of *all* packets? + */ + if (!(info.rx_filters & (1 << HWTSTAMP_FILTER_ALL))) { + /* + * No, so don't report any time stamp types. + * + * XXX - some devices either don't report + * HWTSTAMP_FILTER_ALL when they do support it, or + * report HWTSTAMP_FILTER_ALL but map it to only + * time stamping a few PTP packets. See + * http://marc.info/?l=linux-netdev&m=146318183529571&w=2 + */ + handle->tstamp_type_list = NULL; + return 0; + } + + num_ts_types = 0; + for (i = 0; i < NUM_SOF_TIMESTAMPING_TYPES; i++) { + if (info.so_timestamping & sof_ts_type_map[i].soft_timestamping_val) + num_ts_types++; + } + handle->tstamp_type_count = num_ts_types; + if (num_ts_types != 0) { + handle->tstamp_type_list = malloc(num_ts_types * sizeof(u_int)); + for (i = 0, j = 0; i < NUM_SOF_TIMESTAMPING_TYPES; i++) { + if (info.so_timestamping & sof_ts_type_map[i].soft_timestamping_val) { + handle->tstamp_type_list[j] = sof_ts_type_map[i].pcap_tstamp_val; + j++; + } + } + } else + handle->tstamp_type_list = NULL; + + return 0; +} +#else /* ETHTOOL_GET_TS_INFO */ +static int +iface_ethtool_get_ts_info(const char *device, pcap_t *handle, char *ebuf _U_) +{ + /* + * This doesn't apply to the "any" device; you can't say "turn on + * hardware time stamping for all devices that exist now and arrange + * that it be turned on for any device that appears in the future", + * and not all devices even necessarily *support* hardware time + * stamping, so don't report any time stamp types. + */ + if (strcmp(device, "any") == 0) { + handle->tstamp_type_list = NULL; + return 0; + } + + /* + * We don't have an ioctl to use to ask what's supported, + * so say we support everything. + */ + iface_set_all_ts_types(handle); + return 0; +} +#endif /* ETHTOOL_GET_TS_INFO */ + +#endif /* defined(HAVE_LINUX_NET_TSTAMP_H) && defined(PACKET_TIMESTAMP) */ + +#ifdef HAVE_PACKET_RING /* * Find out if we have any form of fragmentation/reassembly offloading. * @@ -5462,13 +6206,13 @@ enter_rfmon_mode(pcap_t *handle, int sock_fd, const char *device) */ #if defined(SIOCETHTOOL) && (defined(ETHTOOL_GTSO) || defined(ETHTOOL_GUFO) || defined(ETHTOOL_GGSO) || defined(ETHTOOL_GFLAGS) || defined(ETHTOOL_GGRO)) static int -iface_ethtool_ioctl(pcap_t *handle, int cmd, const char *cmdname) +iface_ethtool_flag_ioctl(pcap_t *handle, int cmd, const char *cmdname) { struct ifreq ifr; struct ethtool_value eval; memset(&ifr, 0, sizeof(ifr)); - strlcpy(ifr.ifr_name, handle->opt.source, sizeof(ifr.ifr_name)); + strlcpy(ifr.ifr_name, handle->opt.device, sizeof(ifr.ifr_name)); eval.cmd = cmd; eval.data = 0; ifr.ifr_data = (caddr_t)&eval; @@ -5482,12 +6226,12 @@ iface_ethtool_ioctl(pcap_t *handle, int cmd, const char *cmdname) */ return 0; } - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, - "%s: SIOETHTOOL(%s) ioctl failed: %s", handle->opt.source, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + "%s: SIOCETHTOOL(%s) ioctl failed: %s", handle->opt.device, cmdname, strerror(errno)); return -1; } - return eval.data; + return eval.data; } static int @@ -5496,7 +6240,7 @@ iface_get_offload(pcap_t *handle) int ret; #ifdef ETHTOOL_GTSO - ret = iface_ethtool_ioctl(handle, ETHTOOL_GTSO, "ETHTOOL_GTSO"); + ret = iface_ethtool_flag_ioctl(handle, ETHTOOL_GTSO, "ETHTOOL_GTSO"); if (ret == -1) return -1; if (ret) @@ -5504,7 +6248,7 @@ iface_get_offload(pcap_t *handle) #endif #ifdef ETHTOOL_GUFO - ret = iface_ethtool_ioctl(handle, ETHTOOL_GUFO, "ETHTOOL_GUFO"); + ret = iface_ethtool_flag_ioctl(handle, ETHTOOL_GUFO, "ETHTOOL_GUFO"); if (ret == -1) return -1; if (ret) @@ -5517,7 +6261,7 @@ iface_get_offload(pcap_t *handle) * handed to PF_PACKET sockets on transmission? If not, * this need not be checked. */ - ret = iface_ethtool_ioctl(handle, ETHTOOL_GGSO, "ETHTOOL_GGSO"); + ret = iface_ethtool_flag_ioctl(handle, ETHTOOL_GGSO, "ETHTOOL_GGSO"); if (ret == -1) return -1; if (ret) @@ -5525,7 +6269,7 @@ iface_get_offload(pcap_t *handle) #endif #ifdef ETHTOOL_GFLAGS - ret = iface_ethtool_ioctl(handle, ETHTOOL_GFLAGS, "ETHTOOL_GFLAGS"); + ret = iface_ethtool_flag_ioctl(handle, ETHTOOL_GFLAGS, "ETHTOOL_GFLAGS"); if (ret == -1) return -1; if (ret & ETH_FLAG_LRO) @@ -5538,7 +6282,7 @@ iface_get_offload(pcap_t *handle) * handed to PF_PACKET sockets on receipt? If not, * this need not be checked. */ - ret = iface_ethtool_ioctl(handle, ETHTOOL_GGRO, "ETHTOOL_GGRO"); + ret = iface_ethtool_flag_ioctl(handle, ETHTOOL_GGRO, "ETHTOOL_GGRO"); if (ret == -1) return -1; if (ret) @@ -5559,6 +6303,8 @@ iface_get_offload(pcap_t *handle _U_) } #endif /* SIOCETHTOOL */ +#endif /* HAVE_PACKET_RING */ + #endif /* HAVE_PF_PACKET_SOCKETS */ /* ===== Functions to interface to the older kernels ================== */ @@ -5573,7 +6319,7 @@ activate_old(pcap_t *handle) struct pcap_linux *handlep = handle->priv; int arptype; struct ifreq ifr; - const char *device = handle->opt.source; + const char *device = handle->opt.device; struct utsname utsname; int mtu; @@ -5581,7 +6327,7 @@ activate_old(pcap_t *handle) handle->fd = socket(PF_INET, SOCK_PACKET, htons(ETH_P_ALL)); if (handle->fd == -1) { - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "socket: %s", pcap_strerror(errno)); if (errno == EPERM || errno == EACCES) { /* @@ -5624,9 +6370,9 @@ activate_old(pcap_t *handle) * Try to find the DLT_ type corresponding to that * link-layer type. */ - map_arphrd_to_dlt(handle, arptype, device, 0); + map_arphrd_to_dlt(handle, handle->fd, arptype, device, 0); if (handle->linktype == -1) { - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "unknown arptype %d", arptype); return PCAP_ERROR; } @@ -5637,7 +6383,7 @@ activate_old(pcap_t *handle) memset(&ifr, 0, sizeof(ifr)); strlcpy(ifr.ifr_name, device, sizeof(ifr.ifr_name)); if (ioctl(handle->fd, SIOCGIFFLAGS, &ifr) == -1) { - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "SIOCGIFFLAGS: %s", pcap_strerror(errno)); return PCAP_ERROR; } @@ -5665,7 +6411,7 @@ activate_old(pcap_t *handle) ifr.ifr_flags |= IFF_PROMISC; if (ioctl(handle->fd, SIOCSIFFLAGS, &ifr) == -1) { - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "SIOCSIFFLAGS: %s", pcap_strerror(errno)); return PCAP_ERROR; @@ -5734,8 +6480,8 @@ activate_old(pcap_t *handle) if (mtu == -1) return PCAP_ERROR; handle->bufsize = MAX_LINKHEADER_SIZE + mtu; - if (handle->bufsize < handle->snapshot) - handle->bufsize = handle->snapshot; + if (handle->bufsize < (u_int)handle->snapshot) + handle->bufsize = (u_int)handle->snapshot; } else { /* * This is a 2.2[.x] or later kernel. @@ -5743,7 +6489,7 @@ activate_old(pcap_t *handle) * We can safely pass "recvfrom()" a byte count * based on the snapshot length. */ - handle->bufsize = handle->snapshot; + handle->bufsize = (u_int)handle->snapshot; } /* @@ -5775,7 +6521,7 @@ iface_bind_old(int fd, const char *device, char *ebuf) memset(&saddr, 0, sizeof(saddr)); strlcpy(saddr.sa_data, device, sizeof(saddr.sa_data)); if (bind(fd, &saddr, sizeof(saddr)) == -1) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE, "bind: %s", pcap_strerror(errno)); return -1; } @@ -5783,13 +6529,13 @@ iface_bind_old(int fd, const char *device, char *ebuf) /* Any pending errors, e.g., network is down? */ if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE, "getsockopt: %s", pcap_strerror(errno)); return -1; } if (err > 0) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE, "bind: %s", pcap_strerror(err)); return -1; } @@ -5815,7 +6561,7 @@ iface_get_mtu(int fd, const char *device, char *ebuf) strlcpy(ifr.ifr_name, device, sizeof(ifr.ifr_name)); if (ioctl(fd, SIOCGIFMTU, &ifr) == -1) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE, "SIOCGIFMTU: %s", pcap_strerror(errno)); return -1; } @@ -5835,7 +6581,7 @@ iface_get_arptype(int fd, const char *device, char *ebuf) strlcpy(ifr.ifr_name, device, sizeof(ifr.ifr_name)); if (ioctl(fd, SIOCGIFHWADDR, &ifr) == -1) { - snprintf(ebuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE, "SIOCGIFHWADDR: %s", pcap_strerror(errno)); if (errno == ENODEV) { /* @@ -5868,7 +6614,7 @@ fix_program(pcap_t *handle, struct sock_fprog *fcode, int is_mmapped) len = handle->fcode.bf_len; f = (struct bpf_insn *)malloc(prog_size); if (f == NULL) { - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "malloc: %s", pcap_strerror(errno)); return -1; } @@ -6044,20 +6790,40 @@ set_kernel_filter(pcap_t *handle, struct sock_fprog *fcode) * "nothing more to be read" error). */ save_mode = fcntl(handle->fd, F_GETFL, 0); - if (save_mode != -1 && - fcntl(handle->fd, F_SETFL, save_mode | O_NONBLOCK) >= 0) { - while (recv(handle->fd, &drain, sizeof drain, - MSG_TRUNC) >= 0) - ; - save_errno = errno; - fcntl(handle->fd, F_SETFL, save_mode); - if (save_errno != EAGAIN) { - /* Fatal error */ - reset_kernel_filter(handle); - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, - "recv: %s", pcap_strerror(save_errno)); - return -2; - } + if (save_mode == -1) { + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + "can't get FD flags when changing filter: %s", + pcap_strerror(errno)); + return -2; + } + if (fcntl(handle->fd, F_SETFL, save_mode | O_NONBLOCK) < 0) { + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + "can't set nonblocking mode when changing filter: %s", + pcap_strerror(errno)); + return -2; + } + while (recv(handle->fd, &drain, sizeof drain, MSG_TRUNC) >= 0) + ; + save_errno = errno; + if (save_errno != EAGAIN) { + /* + * Fatal error. + * + * If we can't restore the mode or reset the + * kernel filter, there's nothing we can do. + */ + (void)fcntl(handle->fd, F_SETFL, save_mode); + (void)reset_kernel_filter(handle); + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + "recv failed when changing filter: %s", + pcap_strerror(save_errno)); + return -2; + } + if (fcntl(handle->fd, F_SETFL, save_mode) == -1) { + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + "can't restore FD flags when changing filter: %s", + pcap_strerror(save_errno)); + return -2; } } @@ -6080,11 +6846,16 @@ set_kernel_filter(pcap_t *handle, struct sock_fprog *fcode) save_errno = errno; /* - * XXX - if this fails, we're really screwed; - * we have the total filter on the socket, - * and it won't come off. What do we do then? + * If this fails, we're really screwed; we have the + * total filter on the socket, and it won't come off. + * Report it as a fatal error. */ - reset_kernel_filter(handle); + if (reset_kernel_filter(handle) == -1) { + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + "can't remove kernel total filter: %s", + pcap_strerror(errno)); + return -2; /* fatal error */ + } errno = save_errno; } |