diff options
Diffstat (limited to 'pcap-linux.c')
-rw-r--r-- | pcap-linux.c | 488 |
1 files changed, 348 insertions, 140 deletions
diff --git a/pcap-linux.c b/pcap-linux.c index 5d42dfd0..4af9a90a 100644 --- a/pcap-linux.c +++ b/pcap-linux.c @@ -388,6 +388,9 @@ 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(pcap_t *handle, char *ebuf); +#endif static int iface_get_offload(pcap_t *handle); static int iface_bind_old(int fd, const char *device, char *ebuf); @@ -415,26 +418,15 @@ pcap_create_interface(const char *device, char *ebuf) 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) { + if (iface_ethtool_get_ts_info(handle, ebuf) == -1) { free(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) @@ -449,6 +441,8 @@ 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_strerror(errno)); if (handle->tstamp_type_list != NULL) free(handle->tstamp_type_list); free(handle); @@ -872,7 +866,7 @@ added: * Now configure the monitor interface up. */ memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_name, handlep->mondevice, sizeof(ifr.ifr_name)); + strlcpy(ifr.ifr_name, handlep->mondevice, sizeof(ifr.ifr_name)); if (ioctl(sock_fd, SIOCGIFFLAGS, &ifr) == -1) { snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "%s: Can't get flags for %s: %s", device, @@ -974,9 +968,8 @@ pcap_can_set_rfmon_linux(pcap_t *handle) /* * Attempt to get the current mode. */ - strncpy(ireq.ifr_ifrn.ifrn_name, handle->opt.source, + strlcpy(ireq.ifr_ifrn.ifrn_name, handle->opt.source, sizeof ireq.ifr_ifrn.ifrn_name); - ireq.ifr_ifrn.ifrn_name[sizeof ireq.ifr_ifrn.ifrn_name - 1] = 0; if (ioctl(sock_fd, SIOCGIWMODE, &ireq) != -1) { /* * Well, we got the mode; assume we can set it. @@ -1098,7 +1091,7 @@ static void pcap_cleanup_linux( pcap_t *handle ) * in 2.0[.x] kernels. */ memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_name, handlep->device, + strlcpy(ifr.ifr_name, handlep->device, sizeof(ifr.ifr_name)); if (ioctl(handle->fd, SIOCGIFFLAGS, &ifr) == -1) { fprintf(stderr, @@ -1162,7 +1155,7 @@ static void pcap_cleanup_linux( pcap_t *handle ) */ oldflags = 0; memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_name, handlep->device, + strlcpy(ifr.ifr_name, handlep->device, sizeof(ifr.ifr_name)); if (ioctl(handle->fd, SIOCGIFFLAGS, &ifr) != -1) { if (ifr.ifr_flags & IFF_UP) { @@ -1176,10 +1169,8 @@ static void pcap_cleanup_linux( pcap_t *handle ) /* * Now restore the mode. */ - strncpy(ireq.ifr_ifrn.ifrn_name, handlep->device, + strlcpy(ireq.ifr_ifrn.ifrn_name, handlep->device, sizeof ireq.ifr_ifrn.ifrn_name); - ireq.ifr_ifrn.ifrn_name[sizeof ireq.ifr_ifrn.ifrn_name - 1] - = 0; ireq.u.mode = handlep->oldmode; if (ioctl(handle->fd, SIOCSIWMODE, &ireq) == -1) { /* @@ -1238,10 +1229,28 @@ pcap_activate_linux(pcap_t *handle) { struct pcap_linux *handlep = handle->priv; const char *device; + struct ifreq ifr; int status = 0; + int ret; device = handle->opt.source; + /* + * Make sure the name we were handed will fit into the ioctls we + * might perform on the device; if not, return a "No such device" + * indication, as the Linux kernel shouldn't support creating + * a device whose name won't fit into those ioctls. + * + * "Will fit" means "will fit, complete with a null terminator", + * so if the length, which does *not* include the null terminator, + * is greater than *or equal to* the size of the field into which + * we'll be copying it, that won't fit. + */ + if (strlen(device) >= sizeof(ifr.ifr_name)) { + status = PCAP_ERROR_NO_SUCH_DEVICE; + goto fail; + } + handle->inject_op = pcap_inject_linux; handle->setfilter_op = pcap_setfilter_linux; handle->setdirection_op = pcap_setdirection_linux; @@ -1294,16 +1303,17 @@ pcap_activate_linux(pcap_t *handle) * to be compatible with older kernels for a while so we are * trying both methods with the newer method preferred. */ - status = activate_new(handle); - if (status < 0) { + ret = activate_new(handle); + if (ret < 0) { /* * Fatal error with the new way; just fail. - * status has the error return; if it's PCAP_ERROR, + * ret has the error return; if it's PCAP_ERROR, * handle->errbuf has been set appropriately. */ + status = ret; goto fail; } - if (status == 1) { + if (ret == 1) { /* * Success. * Try to use memory-mapped access. @@ -1330,21 +1340,23 @@ pcap_activate_linux(pcap_t *handle) /* * We failed to set up to use it, or the kernel * supports it, but we failed to enable it. - * status has been set to the error status to + * ret has been set to the error status to * return and, if it's PCAP_ERROR, handle->errbuf * contains the error message. */ + status = ret; goto fail; } } - else if (status == 0) { + else if (ret == 0) { /* Non-fatal error; try old way */ - if ((status = activate_old(handle)) != 1) { + if ((ret = activate_old(handle)) != 1) { /* * Both methods to open the packet socket failed. * Tidy up and report our failure (handle->errbuf * is expected to be set by the functions above). */ + status = ret; goto fail; } } @@ -1352,7 +1364,6 @@ pcap_activate_linux(pcap_t *handle) /* * We set up the socket, but not with memory-mapped access. */ - status = 0; if (handle->opt.buffer_size != 0) { /* * Set the socket buffer size to the specified value. @@ -1479,6 +1490,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 @@ -1662,6 +1674,11 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata) 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 packet_len += VLAN_TAG_LEN; } } @@ -1695,8 +1712,8 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata) * * We currently handle this by making a copy of the filter * program, fixing all "ret" instructions with non-zero - * operands to have an operand of 65535 so that the filter - * doesn't truncate the packet, and supplying that modified + * operands to have an operand of MAXIMUM_SNAPLEN so that the + * filter doesn't truncate the packet, and supplying that modified * filter to the kernel. */ @@ -1706,9 +1723,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; } @@ -2030,7 +2046,7 @@ add_linux_if(pcap_if_t **devlistp, const char *ifname, int fd, char *errbuf) /* * Get the flags for this interface. */ - strncpy(ifrflags.ifr_name, name, sizeof(ifrflags.ifr_name)); + strlcpy(ifrflags.ifr_name, name, sizeof(ifrflags.ifr_name)); if (ioctl(fd, SIOCGIFFLAGS, (char *)&ifrflags) < 0) { if (errno == ENXIO || errno == ENODEV) return (0); /* device doesn't actually exist - ignore it */ @@ -2304,7 +2320,8 @@ pcap_platform_finddevs(pcap_if_t **alldevsp, char *errbuf) /* * Add the "any" device. */ - if (pcap_add_if(alldevsp, "any", 0, any_descr, errbuf) < 0) + if (pcap_add_if(alldevsp, "any", IFF_UP|IFF_RUNNING, + any_descr, errbuf) < 0) return (-1); return (0); @@ -2327,7 +2344,7 @@ pcap_setfilter_linux_common(pcap_t *handle, struct bpf_program *filter, if (!handle) return -1; if (!filter) { - strncpy(handle->errbuf, "setfilter: No filter specified", + strlcpy(handle->errbuf, "setfilter: No filter specified", PCAP_ERRBUF_SIZE); return -1; } @@ -2370,12 +2387,13 @@ pcap_setfilter_linux_common(pcap_t *handle, struct bpf_program *filter, * of different size. Pointed out by Sebastian * * Oh, and we also need to fix it up so that all "ret" - * instructions with non-zero operands have 65535 as the - * operand if we're not capturing in memory-mapped modee, - * and so that, if we're in cooked mode, all memory-reference - * instructions use special magic offsets in references to - * the link-layer header and assume that the link-layer - * payload begins at 0; "fix_program()" will do that. + * instructions with non-zero operands have MAXIMUM_SNAPLEN + * as the operand if we're not capturing in memory-mapped + * mode, and so that, if we're in cooked mode, all memory- + * reference instructions use special magic offsets in + * references to the link-layer header and assume that the + * link-layer payload begins at 0; "fix_program()" will do + * that. */ switch (fix_program(handle, &fcode, is_mmapped)) { @@ -2461,8 +2479,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) { + 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()". @@ -2997,6 +3021,10 @@ activate_new(pcap_t *handle) #endif int err = 0; struct packet_mreq mr; +#ifdef SO_BPF_EXTENSIONS + int bpf_extensions; + socklen_t len = sizeof(bpf_extensions); +#endif /* * Open a socket with protocol family packet. If the @@ -3203,6 +3231,7 @@ activate_new(pcap_t *handle) /* * It doesn't support monitor mode. */ + close(sock_fd); return PCAP_ERROR_RFMON_NOTSUP; } @@ -3309,23 +3338,43 @@ activate_new(pcap_t *handle) break; } - /* Save the socket FD in the pcap structure */ - handle->fd = sock_fd; - #if defined(SIOCGSTAMPNS) && defined(SO_TIMESTAMPNS) if (handle->opt.tstamp_precision == PCAP_TSTAMP_PRECISION_NANO) { int nsec_tstamps = 1; - if (setsockopt(handle->fd, SOL_SOCKET, SO_TIMESTAMPNS, &nsec_tstamps, sizeof(nsec_tstamps)) < 0) { + 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"); + close(sock_fd); return PCAP_ERROR; } } #endif /* defined(SIOCGSTAMPNS) && defined(SO_TIMESTAMPNS) */ + /* + * We've succeeded. Save the socket FD in the pcap structure. + */ + handle->fd = sock_fd; + +#ifdef SO_BPF_EXTENSIONS + /* + * 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 /* SO_BPF_EXTENSIONS */ + return 1; #else /* HAVE_PF_PACKET_SOCKETS */ - strncpy(ebuf, + strlcpy(ebuf, "New packet capturing interface not supported by build " "environment", PCAP_ERRBUF_SIZE); return 0; @@ -3711,10 +3760,16 @@ create_ring(pcap_t *handle, int *status) * We pick a "frame" size of 128K to leave enough * room for at least one reasonably-sized packet * in the "frame". */ - req.tp_frame_size = 131072; + req.tp_frame_size = MAXIMUM_SNAPLEN; req.tp_frame_nr = handle->opt.buffer_size/req.tp_frame_size; break; #endif + default: + 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. @@ -3763,7 +3818,7 @@ create_ring(pcap_t *handle, int *status) hwconfig.rx_filter = HWTSTAMP_FILTER_ALL; memset(&ifr, 0, sizeof(ifr)); - strcpy(ifr.ifr_name, handle->opt.source); + strlcpy(ifr.ifr_name, handle->opt.source, sizeof(ifr.ifr_name)); ifr.ifr_data = (void *)&hwconfig; if (ioctl(handle->fd, SIOCSHWTSTAMP, &ifr) < 0) { @@ -3928,13 +3983,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; } } @@ -3998,6 +4054,13 @@ pcap_setnonblock_mmap(pcap_t *p, int nonblock, char *errbuf) struct pcap_linux *handlep = p->priv; /* + * Set the file descriptor to non-blocking mode, as we use + * it for sending packets. + */ + if (pcap_setnonblock_fd(p, nonblock, errbuf) == -1) + return -1; + + /* * Map each value to their corresponding negation to * preserve the timeout value provided with pcap_set_timeout. */ @@ -4186,7 +4249,7 @@ static int pcap_handle_packet_mmap( if (tp_mac + tp_snaplen > handle->bufsize) { 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; } @@ -4201,22 +4264,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; @@ -4229,7 +4279,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 @@ -4254,7 +4304,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; @@ -4461,6 +4534,7 @@ pcap_read_linux_mmap_v3(pcap_t *handle, int max_packets, pcap_handler callback, int pkts = 0; int ret; +again: if (handlep->current_packet == NULL) { /* wait for frames availability.*/ ret = pcap_wait_for_frames_mmap(handle); @@ -4469,8 +4543,13 @@ pcap_read_linux_mmap_v3(pcap_t *handle, int max_packets, pcap_handler callback, } } h.raw = pcap_get_ring_frame(handle, TP_STATUS_USER); - if (!h.raw) + if (!h.raw) { + if (pkts == 0 && handlep->timeout == 0) { + /* Block until we see a packet. */ + goto again; + } return pkts; + } /* non-positive values of max_packets are used to require all * packets currently available in the ring */ @@ -4552,6 +4631,10 @@ pcap_read_linux_mmap_v3(pcap_t *handle, int max_packets, pcap_handler callback, return PCAP_ERROR_BREAK; } } + if (pkts == 0 && handlep->timeout == 0) { + /* Block until we see a packet. */ + goto again; + } return pkts; } #endif /* HAVE_TPACKET3 */ @@ -4649,7 +4732,7 @@ iface_get_id(int fd, const char *device, char *ebuf) struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name)); + strlcpy(ifr.ifr_name, device, sizeof(ifr.ifr_name)); if (ioctl(fd, SIOCGIFINDEX, &ifr) == -1) { snprintf(ebuf, PCAP_ERRBUF_SIZE, @@ -4731,9 +4814,8 @@ has_wext(int sock_fd, const char *device, char *ebuf) { struct iwreq ireq; - strncpy(ireq.ifr_ifrn.ifrn_name, device, + strlcpy(ireq.ifr_ifrn.ifrn_name, device, sizeof ireq.ifr_ifrn.ifrn_name); - ireq.ifr_ifrn.ifrn_name[sizeof ireq.ifr_ifrn.ifrn_name - 1] = 0; if (ioctl(sock_fd, SIOCGIWNAME, &ireq) >= 0) return 1; /* yes */ snprintf(ebuf, PCAP_ERRBUF_SIZE, @@ -4869,9 +4951,8 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device) * return EOPNOTSUPP. */ memset(&ireq, 0, sizeof ireq); - strncpy(ireq.ifr_ifrn.ifrn_name, device, + strlcpy(ireq.ifr_ifrn.ifrn_name, device, sizeof ireq.ifr_ifrn.ifrn_name); - ireq.ifr_ifrn.ifrn_name[sizeof ireq.ifr_ifrn.ifrn_name - 1] = 0; ireq.u.data.pointer = (void *)args; ireq.u.data.length = 0; ireq.u.data.flags = 0; @@ -5072,9 +5153,8 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device) /* * Get the old mode. */ - strncpy(ireq.ifr_ifrn.ifrn_name, device, + strlcpy(ireq.ifr_ifrn.ifrn_name, device, sizeof ireq.ifr_ifrn.ifrn_name); - ireq.ifr_ifrn.ifrn_name[sizeof ireq.ifr_ifrn.ifrn_name - 1] = 0; if (ioctl(sock_fd, SIOCGIWMODE, &ireq) == -1) { /* * We probably won't be able to set the mode, either. @@ -5129,12 +5209,11 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device) * If it fails, just fall back on SIOCSIWMODE. */ memset(&ireq, 0, sizeof ireq); - strncpy(ireq.ifr_ifrn.ifrn_name, device, + strlcpy(ireq.ifr_ifrn.ifrn_name, device, sizeof ireq.ifr_ifrn.ifrn_name); - ireq.ifr_ifrn.ifrn_name[sizeof ireq.ifr_ifrn.ifrn_name - 1] = 0; ireq.u.data.length = 1; /* 1 argument */ args[0] = 3; /* request Prism header */ - memcpy(ireq.u.name, args, IFNAMSIZ); + memcpy(ireq.u.name, args, sizeof (int)); if (ioctl(sock_fd, cmd, &ireq) != -1) { /* * Success. @@ -5162,7 +5241,7 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device) * might get EBUSY. */ memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name)); + strlcpy(ifr.ifr_name, device, sizeof(ifr.ifr_name)); if (ioctl(sock_fd, SIOCGIFFLAGS, &ifr) == -1) { snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "%s: Can't get flags: %s", device, strerror(errno)); @@ -5182,9 +5261,8 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device) /* * Then turn monitor mode on. */ - strncpy(ireq.ifr_ifrn.ifrn_name, device, + strlcpy(ireq.ifr_ifrn.ifrn_name, device, sizeof ireq.ifr_ifrn.ifrn_name); - ireq.ifr_ifrn.ifrn_name[sizeof ireq.ifr_ifrn.ifrn_name - 1] = 0; ireq.u.mode = IW_MODE_MONITOR; if (ioctl(sock_fd, SIOCSIWMODE, &ireq) == -1) { /* @@ -5222,9 +5300,8 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device) * Try to select the radiotap header. */ memset(&ireq, 0, sizeof ireq); - strncpy(ireq.ifr_ifrn.ifrn_name, device, + strlcpy(ireq.ifr_ifrn.ifrn_name, device, sizeof ireq.ifr_ifrn.ifrn_name); - ireq.ifr_ifrn.ifrn_name[sizeof ireq.ifr_ifrn.ifrn_name - 1] = 0; args[0] = 3; /* request radiotap header */ memcpy(ireq.u.name, args, sizeof (int)); if (ioctl(sock_fd, cmd, &ireq) != -1) @@ -5234,9 +5311,8 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device) * That failed. Try to select the AVS header. */ memset(&ireq, 0, sizeof ireq); - strncpy(ireq.ifr_ifrn.ifrn_name, device, + strlcpy(ireq.ifr_ifrn.ifrn_name, device, sizeof ireq.ifr_ifrn.ifrn_name); - ireq.ifr_ifrn.ifrn_name[sizeof ireq.ifr_ifrn.ifrn_name - 1] = 0; args[0] = 2; /* request AVS header */ memcpy(ireq.u.name, args, sizeof (int)); if (ioctl(sock_fd, cmd, &ireq) != -1) @@ -5246,9 +5322,8 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device) * That failed. Try to select the Prism header. */ memset(&ireq, 0, sizeof ireq); - strncpy(ireq.ifr_ifrn.ifrn_name, device, + strlcpy(ireq.ifr_ifrn.ifrn_name, device, sizeof ireq.ifr_ifrn.ifrn_name); - ireq.ifr_ifrn.ifrn_name[sizeof ireq.ifr_ifrn.ifrn_name - 1] = 0; args[0] = 1; /* request Prism header */ memcpy(ireq.u.name, args, sizeof (int)); ioctl(sock_fd, cmd, &ireq); @@ -5265,9 +5340,8 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device) * Select the Prism header. */ memset(&ireq, 0, sizeof ireq); - strncpy(ireq.ifr_ifrn.ifrn_name, device, + strlcpy(ireq.ifr_ifrn.ifrn_name, device, sizeof ireq.ifr_ifrn.ifrn_name); - ireq.ifr_ifrn.ifrn_name[sizeof ireq.ifr_ifrn.ifrn_name - 1] = 0; args[0] = 3; /* request Prism header */ memcpy(ireq.u.name, args, sizeof (int)); ioctl(sock_fd, cmd, &ireq); @@ -5278,9 +5352,8 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device) * Get the current channel. */ memset(&ireq, 0, sizeof ireq); - strncpy(ireq.ifr_ifrn.ifrn_name, device, + strlcpy(ireq.ifr_ifrn.ifrn_name, device, sizeof ireq.ifr_ifrn.ifrn_name); - ireq.ifr_ifrn.ifrn_name[sizeof ireq.ifr_ifrn.ifrn_name - 1] = 0; if (ioctl(sock_fd, SIOCGIWFREQ, &ireq) == -1) { snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "%s: SIOCGIWFREQ: %s", device, @@ -5294,9 +5367,8 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device) * current value. */ memset(&ireq, 0, sizeof ireq); - strncpy(ireq.ifr_ifrn.ifrn_name, device, + strlcpy(ireq.ifr_ifrn.ifrn_name, device, sizeof ireq.ifr_ifrn.ifrn_name); - ireq.ifr_ifrn.ifrn_name[sizeof ireq.ifr_ifrn.ifrn_name - 1] = 0; args[0] = 1; /* request Prism header */ args[1] = channel; /* set channel */ memcpy(ireq.u.name, args, 2*sizeof (int)); @@ -5309,9 +5381,8 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device) * Prism header. */ memset(&ireq, 0, sizeof ireq); - strncpy(ireq.ifr_ifrn.ifrn_name, device, + strlcpy(ireq.ifr_ifrn.ifrn_name, device, sizeof ireq.ifr_ifrn.ifrn_name); - ireq.ifr_ifrn.ifrn_name[sizeof ireq.ifr_ifrn.ifrn_name - 1] = 0; args[0] = 0; /* disallow transmitting */ memcpy(ireq.u.name, args, sizeof (int)); ioctl(sock_fd, cmd, &ireq); @@ -5322,9 +5393,8 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device) * Force the Prism header. */ memset(&ireq, 0, sizeof ireq); - strncpy(ireq.ifr_ifrn.ifrn_name, device, + strlcpy(ireq.ifr_ifrn.ifrn_name, device, sizeof ireq.ifr_ifrn.ifrn_name); - ireq.ifr_ifrn.ifrn_name[sizeof ireq.ifr_ifrn.ifrn_name - 1] = 0; args[0] = 1; /* request Prism header */ memcpy(ireq.u.name, args, sizeof (int)); ioctl(sock_fd, cmd, &ireq); @@ -5335,9 +5405,8 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device) * Force the Prism header. */ memset(&ireq, 0, sizeof ireq); - strncpy(ireq.ifr_ifrn.ifrn_name, device, + strlcpy(ireq.ifr_ifrn.ifrn_name, device, sizeof ireq.ifr_ifrn.ifrn_name); - ireq.ifr_ifrn.ifrn_name[sizeof ireq.ifr_ifrn.ifrn_name - 1] = 0; ireq.u.data.length = 1; /* 1 argument */ ireq.u.data.pointer = "1"; ireq.u.data.flags = 0; @@ -5349,9 +5418,8 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device) * Force the Prism header. */ memset(&ireq, 0, sizeof ireq); - strncpy(ireq.ifr_ifrn.ifrn_name, device, + strlcpy(ireq.ifr_ifrn.ifrn_name, device, sizeof ireq.ifr_ifrn.ifrn_name); - ireq.ifr_ifrn.ifrn_name[sizeof ireq.ifr_ifrn.ifrn_name - 1] = 0; args[0] = 1; /* request Prism header */ memcpy(ireq.u.name, args, sizeof (int)); ioctl(sock_fd, cmd, &ireq); @@ -5433,6 +5501,120 @@ 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]) + +static void +iface_set_default_ts_types(pcap_t *handle) +{ + 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(pcap_t *handle, char *ebuf) +{ + int fd; + struct ifreq ifr; + struct ethtool_ts_info info; + int num_ts_types; + int i, j; + + /* + * This doesn't apply to the "any" device; you have to ask + * specific devices for their capabilities, so just default + * to saying we support all of them. + */ + if (strcmp(handle->opt.source, "any") == 0) { + iface_set_default_ts_types(handle); + return 0; + } + + /* + * Create a socket from which to fetch time stamping capabilities. + */ + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + (void)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, handle->opt.source, 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) { + close(fd); + if (errno == EOPNOTSUPP || errno == EINVAL) { + /* + * OK, let's just return all the possible time + * stamping types. + */ + iface_set_default_ts_types(handle); + return 0; + } + snprintf(ebuf, PCAP_ERRBUF_SIZE, + "%s: SIOCETHTOOL(ETHTOOL_GET_TS_INFO) ioctl failed: %s", handle->opt.source, + strerror(errno)); + return -1; + } + close(fd); + + 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(pcap_t *handle, char *ebuf _U_) +{ + /* + * We don't have an ioctl to use to ask what's supported, + * so say we support everything. + */ + iface_set_default_ts_types(handle); + return 0; +} +#endif /* ETHTOOL_GET_TS_INFO */ + +#endif /* defined(HAVE_LINUX_NET_TSTAMP_H) && defined(PACKET_TIMESTAMP) */ + /* * Find out if we have any form of fragmentation/reassembly offloading. * @@ -5443,13 +5625,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)); - strncpy(ifr.ifr_name, handle->opt.source, sizeof(ifr.ifr_name)); + strlcpy(ifr.ifr_name, handle->opt.source, sizeof(ifr.ifr_name)); eval.cmd = cmd; eval.data = 0; ifr.ifr_data = (caddr_t)&eval; @@ -5464,7 +5646,7 @@ 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, + "%s: SIOCETHTOOL(%s) ioctl failed: %s", handle->opt.source, cmdname, strerror(errno)); return -1; } @@ -5477,7 +5659,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) @@ -5485,7 +5667,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) @@ -5498,7 +5680,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) @@ -5506,7 +5688,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) @@ -5519,7 +5701,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) @@ -5587,7 +5769,7 @@ activate_old(pcap_t *handle) /* Bind to the given device */ if (strcmp(device, "any") == 0) { - strncpy(handle->errbuf, "pcap_activate: The \"any\" device isn't supported on 2.0[.x]-kernel systems", + strlcpy(handle->errbuf, "pcap_activate: The \"any\" device isn't supported on 2.0[.x]-kernel systems", PCAP_ERRBUF_SIZE); return PCAP_ERROR; } @@ -5616,7 +5798,7 @@ activate_old(pcap_t *handle) if (handle->opt.promisc) { memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name)); + strlcpy(ifr.ifr_name, device, sizeof(ifr.ifr_name)); if (ioctl(handle->fd, SIOCGIFFLAGS, &ifr) == -1) { snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "SIOCGIFFLAGS: %s", pcap_strerror(errno)); @@ -5754,7 +5936,7 @@ iface_bind_old(int fd, const char *device, char *ebuf) socklen_t errlen = sizeof(err); memset(&saddr, 0, sizeof(saddr)); - strncpy(saddr.sa_data, device, sizeof(saddr.sa_data)); + strlcpy(saddr.sa_data, device, sizeof(saddr.sa_data)); if (bind(fd, &saddr, sizeof(saddr)) == -1) { snprintf(ebuf, PCAP_ERRBUF_SIZE, "bind: %s", pcap_strerror(errno)); @@ -5793,7 +5975,7 @@ iface_get_mtu(int fd, const char *device, char *ebuf) return BIGGER_THAN_ALL_MTUS; memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name)); + strlcpy(ifr.ifr_name, device, sizeof(ifr.ifr_name)); if (ioctl(fd, SIOCGIFMTU, &ifr) == -1) { snprintf(ebuf, PCAP_ERRBUF_SIZE, @@ -5813,7 +5995,7 @@ iface_get_arptype(int fd, const char *device, char *ebuf) struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name)); + strlcpy(ifr.ifr_name, device, sizeof(ifr.ifr_name)); if (ioctl(fd, SIOCGIFHWADDR, &ifr) == -1) { snprintf(ebuf, PCAP_ERRBUF_SIZE, @@ -5880,18 +6062,19 @@ fix_program(pcap_t *handle, struct sock_fprog *fcode, int is_mmapped) * Yes - if the value to be returned, * i.e. the snapshot length, is * anything other than 0, make it - * 65535, so that the packet is - * truncated by "recvfrom()", + * MAXIMUM_SNAPLEN, so that the packet + * is truncated by "recvfrom()", * not by the filter. * * XXX - there's nothing we can * easily do if it's getting the * value from the accumulator; we'd * have to insert code to force - * non-zero values to be 65535. + * non-zero values to be + * MAXIMUM_SNAPLEN. */ if (p->k != 0) - p->k = 65535; + p->k = MAXIMUM_SNAPLEN; } } break; @@ -6024,20 +6207,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) { + 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) { + 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); + 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) { + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + "can't restore FD flags when changing filter: %s", + pcap_strerror(save_errno)); + return -2; } } @@ -6060,11 +6263,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) { + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + "can't remove kernel total filter: %s", + pcap_strerror(errno)); + return -2; /* fatal error */ + } errno = save_errno; } |