X-Git-Url: https://git.tcpdump.org/libpcap/blobdiff_plain/5f3f3fe2b07f444b33f3440cfa962b6185bcfdbe..refs/heads/libpcap-1.3:/pcap-linux.c diff --git a/pcap-linux.c b/pcap-linux.c index 5d291e95..e5c3afa7 100644 --- a/pcap-linux.c +++ b/pcap-linux.c @@ -127,42 +127,19 @@ static const char rcsid[] _U_ = #include #include #include +#include #include #include #include #include #include +#include #include #include #include #include #include -#ifdef HAVE_LINUX_NET_TSTAMP_H -#include -#include -#endif - -/* - * Got Wireless Extensions? - */ -#ifdef HAVE_LINUX_WIRELESS_H -#include -#endif /* HAVE_LINUX_WIRELESS_H */ - -/* - * Got libnl? - */ -#ifdef HAVE_LIBNL -#include - -#include -#include -#include -#include -#include -#endif /* HAVE_LIBNL */ - #include "pcap-int.h" #include "pcap/sll.h" #include "pcap/vlan.h" @@ -191,6 +168,14 @@ static const char rcsid[] _U_ = #include "pcap-can-linux.h" #endif +#if PCAP_SUPPORT_CANUSB +#include "pcap-canusb-linux.h" +#endif + +#ifdef PCAP_SUPPORT_NETFILTER +#include "pcap-netfilter-linux.h" +#endif + /* * If PF_PACKET is defined, we can use {SOCK_RAW,SOCK_DGRAM}/PF_PACKET * sockets rather than SOCK_PACKET sockets. @@ -255,6 +240,46 @@ static const char rcsid[] _U_ = #include #endif +/* + * We need linux/sockios.h if we have linux/net_tstamp.h (for time stamp + * specification) or linux/ethtool.h (for ethtool ioctls to get offloading + * information). + */ +#if defined(HAVE_LINUX_NET_TSTAMP_H) || defined(HAVE_LINUX_ETHTOOL_H) +#include +#endif + +#ifdef HAVE_LINUX_NET_TSTAMP_H +#include +#endif + +/* + * Got Wireless Extensions? + */ +#ifdef HAVE_LINUX_WIRELESS_H +#include +#endif /* HAVE_LINUX_WIRELESS_H */ + +/* + * Got libnl? + */ +#ifdef HAVE_LIBNL +#include + +#include +#include +#include +#include +#include +#endif /* HAVE_LIBNL */ + +/* + * Got ethtool support? + */ +#ifdef HAVE_LINUX_ETHTOOL_H +#include +#endif + #ifndef HAVE_SOCKLEN_T typedef int socklen_t; #endif @@ -293,7 +318,7 @@ typedef int socklen_t; /* * Prototypes for internal functions and methods. */ -static void map_arphrd_to_dlt(pcap_t *, int, 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 @@ -336,7 +361,7 @@ static void pcap_oneshot_mmap(u_char *user, const struct pcap_pkthdr *h, */ #ifdef HAVE_PF_PACKET_SOCKETS static int iface_get_id(int fd, const char *device, char *ebuf); -#endif +#endif /* HAVE_PF_PACKET_SOCKETS */ static int iface_get_mtu(int fd, const char *device, char *ebuf); static int iface_get_arptype(int fd, const char *device, char *ebuf); #ifdef HAVE_PF_PACKET_SOCKETS @@ -347,6 +372,7 @@ 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 */ +static int iface_get_offload(pcap_t *handle); static int iface_bind_old(int fd, const char *device, char *ebuf); #ifdef SO_ATTACH_FILTER @@ -360,7 +386,7 @@ static struct sock_filter total_insn = BPF_STMT(BPF_RET | BPF_K, 0); static struct sock_fprog total_fcode = { 1, &total_insn }; -#endif +#endif /* SO_ATTACH_FILTER */ pcap_t * pcap_create(const char *device, char *ebuf) @@ -398,8 +424,15 @@ pcap_create(const char *device, char *ebuf) } #endif +#if PCAP_SUPPORT_CANUSB + if (strstr(device, "canusb")) { + return canusb_create(device, ebuf); + } +#endif + #ifdef PCAP_SUPPORT_CAN - if (strstr(device, "can") || strstr(device, "vcan")) { + if ((strncmp(device, "can", 3) == 0 && isdigit(device[3])) || + (strncmp(device, "vcan", 4) == 0 && isdigit(device[4]))) { return can_create(device, ebuf); } #endif @@ -410,6 +443,12 @@ pcap_create(const char *device, char *ebuf) } #endif +#ifdef PCAP_SUPPORT_NETFILTER + if (strncmp(device, "nflog", strlen("nflog")) == 0) { + return nflog_create(device, ebuf); + } +#endif + handle = pcap_create_common(device, ebuf); if (handle == NULL) return NULL; @@ -835,6 +874,18 @@ added: nanosleep(&delay, NULL); #endif + /* + * If we haven't already done so, arrange to have + * "pcap_close_all()" called when we exit. + */ + if (!pcap_do_addexit(handle)) { + /* + * "atexit()" failed; don't put the interface + * in rfmon mode, just give up. + */ + return PCAP_ERROR_RFMON_NOTSUP; + } + /* * Now configure the monitor interface up. */ @@ -1044,6 +1095,7 @@ static void pcap_cleanup_linux( pcap_t *handle ) int ret; #endif /* HAVE_LIBNL */ #ifdef IW_MODE_MONITOR + int oldflags; struct iwreq ireq; #endif /* IW_MODE_MONITOR */ @@ -1067,10 +1119,10 @@ static void pcap_cleanup_linux( pcap_t *handle ) sizeof(ifr.ifr_name)); if (ioctl(handle->fd, SIOCGIFFLAGS, &ifr) == -1) { fprintf(stderr, - "Can't restore interface flags (SIOCGIFFLAGS failed: %s).\n" + "Can't restore interface %s flags (SIOCGIFFLAGS failed: %s).\n" "Please adjust manually.\n" "Hint: This can't happen with Linux >= 2.2.0.\n", - strerror(errno)); + handle->md.device, strerror(errno)); } else { if (ifr.ifr_flags & IFF_PROMISC) { /* @@ -1081,9 +1133,10 @@ static void pcap_cleanup_linux( pcap_t *handle ) if (ioctl(handle->fd, SIOCSIFFLAGS, &ifr) == -1) { fprintf(stderr, - "Can't restore interface flags (SIOCSIFFLAGS failed: %s).\n" + "Can't restore interface %s flags (SIOCSIFFLAGS failed: %s).\n" "Please adjust manually.\n" "Hint: This can't happen with Linux >= 2.2.0.\n", + handle->md.device, strerror(errno)); } } @@ -1117,6 +1170,29 @@ static void pcap_cleanup_linux( pcap_t *handle ) * mode, this code cannot know that, so it'll take * it out of rfmon mode. */ + + /* + * First, take the interface down if it's up; + * otherwise, we might get EBUSY. + * If we get errors, just drive on and print + * a warning if we can't restore the mode. + */ + oldflags = 0; + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, handle->md.device, + sizeof(ifr.ifr_name)); + if (ioctl(handle->fd, SIOCGIFFLAGS, &ifr) != -1) { + if (ifr.ifr_flags & IFF_UP) { + oldflags = ifr.ifr_flags; + ifr.ifr_flags &= ~IFF_UP; + if (ioctl(handle->fd, SIOCSIFFLAGS, &ifr) == -1) + oldflags = 0; /* didn't set, don't restore */ + } + } + + /* + * Now restore the mode. + */ strncpy(ireq.ifr_ifrn.ifrn_name, handle->md.device, sizeof ireq.ifr_ifrn.ifrn_name); ireq.ifr_ifrn.ifrn_name[sizeof ireq.ifr_ifrn.ifrn_name - 1] @@ -1127,9 +1203,23 @@ static void pcap_cleanup_linux( pcap_t *handle ) * Scientist, you've failed. */ fprintf(stderr, - "Can't restore interface wireless mode (SIOCSIWMODE failed: %s).\n" + "Can't restore interface %s wireless mode (SIOCSIWMODE failed: %s).\n" "Please adjust manually.\n", - strerror(errno)); + handle->md.device, strerror(errno)); + } + + /* + * Now bring the interface back up if we brought + * it down. + */ + if (oldflags != 0) { + ifr.ifr_flags = oldflags; + if (ioctl(handle->fd, SIOCSIFFLAGS, &ifr) == -1) { + fprintf(stderr, + "Can't bring interface %s back up (SIOCSIFFLAGS failed: %s).\n" + "Please adjust manually.\n", + handle->md.device, strerror(errno)); + } } } #endif /* IW_MODE_MONITOR */ @@ -1530,32 +1620,40 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata) } #if defined(HAVE_PACKET_AUXDATA) && defined(HAVE_LINUX_TPACKET_AUXDATA_TP_VLAN_TCI) - for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { - struct tpacket_auxdata *aux; - unsigned int len; - struct vlan_tag *tag; - - if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct tpacket_auxdata)) || - cmsg->cmsg_level != SOL_PACKET || - cmsg->cmsg_type != PACKET_AUXDATA) - continue; + if (handle->md.vlan_offset != -1) { + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + struct tpacket_auxdata *aux; + unsigned int len; + struct vlan_tag *tag; - aux = (struct tpacket_auxdata *)CMSG_DATA(cmsg); - if (aux->tp_vlan_tci == 0) - continue; + if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct tpacket_auxdata)) || + cmsg->cmsg_level != SOL_PACKET || + cmsg->cmsg_type != PACKET_AUXDATA) + continue; - len = packet_len > iov.iov_len ? iov.iov_len : packet_len; - if (len < 2 * ETH_ALEN) - break; + aux = (struct tpacket_auxdata *)CMSG_DATA(cmsg); +#if defined(TP_STATUS_VLAN_VALID) + if ((aux->tp_vlan_tci == 0) && !(aux->tp_status & TP_STATUS_VLAN_VALID)) +#else + if (aux->tp_vlan_tci == 0) /* this is ambigious but without the + TP_STATUS_VLAN_VALID flag, there is + nothing that we can do */ +#endif + continue; + + len = packet_len > iov.iov_len ? iov.iov_len : packet_len; + if (len < (unsigned int) handle->md.vlan_offset) + break; - bp -= VLAN_TAG_LEN; - memmove(bp, bp + VLAN_TAG_LEN, 2 * ETH_ALEN); + bp -= VLAN_TAG_LEN; + memmove(bp, bp + VLAN_TAG_LEN, handle->md.vlan_offset); - tag = (struct vlan_tag *)(bp + 2 * ETH_ALEN); - tag->vlan_tpid = htons(ETH_P_8021Q); - tag->vlan_tci = htons(aux->tp_vlan_tci); + tag = (struct vlan_tag *)(bp + handle->md.vlan_offset); + tag->vlan_tpid = htons(ETH_P_8021Q); + tag->vlan_tci = htons(aux->tp_vlan_tci); - packet_len += VLAN_TAG_LEN; + packet_len += VLAN_TAG_LEN; + } } #endif /* defined(HAVE_PACKET_AUXDATA) && defined(HAVE_LINUX_TPACKET_AUXDATA_TP_VLAN_TCI) */ #endif /* HAVE_PF_PACKET_SOCKETS */ @@ -1863,6 +1961,8 @@ scan_sys_class_net(pcap_if_t **devlistp, char *errbuf) DIR *sys_class_net_d; int fd; struct dirent *ent; + char subsystem_path[PATH_MAX+1]; + struct stat statb; char *p; char name[512]; /* XXX - pick a size */ char *q, *saveq; @@ -1870,8 +1970,20 @@ scan_sys_class_net(pcap_if_t **devlistp, char *errbuf) int ret = 1; sys_class_net_d = opendir("/sys/class/net"); - if (sys_class_net_d == NULL && errno == ENOENT) - return (0); + if (sys_class_net_d == NULL) { + /* + * Don't fail if it doesn't exist at all. + */ + if (errno == ENOENT) + return (0); + + /* + * Fail if we got some other error. + */ + (void)snprintf(errbuf, PCAP_ERRBUF_SIZE, + "Can't open /sys/class/net: %s", pcap_strerror(errno)); + return (-1); + } /* * Create a socket from which to fetch interface information. @@ -1880,6 +1992,7 @@ scan_sys_class_net(pcap_if_t **devlistp, char *errbuf) if (fd < 0) { (void)snprintf(errbuf, PCAP_ERRBUF_SIZE, "socket: %s", pcap_strerror(errno)); + (void)closedir(sys_class_net_d); return (-1); } @@ -1894,10 +2007,41 @@ scan_sys_class_net(pcap_if_t **devlistp, char *errbuf) } /* - * Ignore directories (".", "..", and any subdirectories). + * Ignore "." and "..". */ - if (ent->d_type == DT_DIR) + if (strcmp(ent->d_name, ".") == 0 || + strcmp(ent->d_name, "..") == 0) + continue; + + /* + * Ignore plain files; they do not have subdirectories + * and thus have no attributes. + */ + if (ent->d_type == DT_REG) + continue; + + /* + * Is there an "ifindex" file under that name? + * (We don't care whether it's a directory or + * a symlink; older kernels have directories + * for devices, newer kernels have symlinks to + * directories.) + */ + snprintf(subsystem_path, sizeof subsystem_path, + "/sys/class/net/%s/ifindex", ent->d_name); + if (lstat(subsystem_path, &statb) != 0) { + /* + * Stat failed. Either there was an error + * other than ENOENT, and we don't know if + * this is an interface, or it's ENOENT, + * and either some part of "/sys/class/net/{if}" + * disappeared, in which case it probably means + * the interface disappeared, or there's no + * "ifindex" file, which means it's not a + * network interface. + */ continue; + } /* * Get the interface name. @@ -2003,8 +2147,20 @@ scan_proc_net_dev(pcap_if_t **devlistp, char *errbuf) int ret = 0; proc_net_f = fopen("/proc/net/dev", "r"); - if (proc_net_f == NULL && errno == ENOENT) - return (0); + if (proc_net_f == NULL) { + /* + * Don't fail if it doesn't exist at all. + */ + if (errno == ENOENT) + return (0); + + /* + * Fail if we got some other error. + */ + (void)snprintf(errbuf, PCAP_ERRBUF_SIZE, + "Can't open /proc/net/dev: %s", pcap_strerror(errno)); + return (-1); + } /* * Create a socket from which to fetch interface information. @@ -2013,6 +2169,7 @@ scan_proc_net_dev(pcap_if_t **devlistp, char *errbuf) if (fd < 0) { (void)snprintf(errbuf, PCAP_ERRBUF_SIZE, "socket: %s", pcap_strerror(errno)); + (void)fclose(proc_net_f); return (-1); } @@ -2188,6 +2345,19 @@ pcap_platform_finddevs(pcap_if_t **alldevsp, char *errbuf) return (-1); #endif +#ifdef PCAP_SUPPORT_NETFILTER + /* + * Add netfilter devices. + */ + if (netfilter_platform_finddevs(alldevsp, errbuf) < 0) + return (-1); +#endif + +#if PCAP_SUPPORT_CANUSB + if (canusb_platform_finddevs(alldevsp, errbuf) < 0) + return (-1); +#endif + return (0); } @@ -2283,6 +2453,30 @@ pcap_setfilter_linux_common(pcap_t *handle, struct bpf_program *filter, } } + /* + * NOTE: at this point, we've set both the "len" and "filter" + * fields of "fcode". As of the 2.6.32.4 kernel, at least, + * those are the only members of the "sock_fprog" structure, + * so we initialize every member of that structure. + * + * If there is anything in "fcode" that is not initialized, + * it is either a field added in a later kernel, or it's + * padding. + * + * If a new field is added, this code needs to be updated + * to set it correctly. + * + * If there are no other fields, then: + * + * if the Linux kernel looks at the padding, it's + * buggy; + * + * if the Linux kernel doesn't look at the padding, + * then if some tool complains that we're passing + * uninitialized data to the kernel, then the tool + * is buggy and needs to understand that it's just + * padding. + */ if (can_filter_in_kernel) { if ((err = set_kernel_filter(handle, &fcode)) == 0) { @@ -2393,6 +2587,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 @@ -2411,13 +2650,33 @@ 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, 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"; + switch (arptype) { case ARPHRD_ETHER: /* - * This is (presumably) a real Ethernet capture; give it a + * 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 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. + * + * Detect those devices by checking the device name, and + * use DLT_RAW for them. + */ + if (strncmp(device, cdma_rmnet, sizeof cdma_rmnet - 1) == 0) { + handle->linktype = DLT_RAW; + return; + } + + /* + * 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 @@ -2426,21 +2685,27 @@ static void map_arphrd_to_dlt(pcap_t *handle, int arptype, int cooked_ok) * 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 */ @@ -2750,9 +3015,28 @@ activate_new(pcap_t *handle) socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if (sock_fd == -1) { + if (errno == EINVAL || errno == EAFNOSUPPORT) { + /* + * We don't support PF_PACKET/SOCK_whatever + * sockets; try the old mechanism. + */ + return 0; + } + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "socket: %s", pcap_strerror(errno) ); - return 0; /* try old mechanism */ + if (errno == EPERM || errno == EACCES) { + /* + * You don't have permission to open the + * socket. + */ + return PCAP_ERROR_PERM_DENIED; + } else { + /* + * Other error. + */ + return PCAP_ERROR; + } } /* It seems the kernel supports the new interface. */ @@ -2822,7 +3106,7 @@ activate_new(pcap_t *handle) close(sock_fd); return arptype; } - map_arphrd_to_dlt(handle, arptype, 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 || @@ -2849,7 +3133,18 @@ activate_new(pcap_t *handle) if (sock_fd == -1) { snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "socket: %s", pcap_strerror(errno)); - return PCAP_ERROR; + if (errno == EPERM || errno == EACCES) { + /* + * You don't have permission to + * open the socket. + */ + return PCAP_ERROR_PERM_DENIED; + } else { + /* + * Other error. + */ + return PCAP_ERROR; + } } handle->md.cooked = 1; @@ -3000,6 +3295,24 @@ activate_new(pcap_t *handle) } handle->bufsize = handle->snapshot; + /* + * Set the offset at which to insert VLAN tags. + */ + switch (handle->linktype) { + + case DLT_EN10MB: + handle->md.vlan_offset = 2 * ETH_ALEN; + break; + + case DLT_LINUX_SLL: + handle->md.vlan_offset = 14; + break; + + default: + handle->md.vlan_offset = -1; /* unknown */ + break; + } + /* Save the socket FD in the pcap structure */ handle->fd = sock_fd; @@ -3171,20 +3484,123 @@ create_ring(pcap_t *handle, int *status) { unsigned i, j, frames_per_block; struct tpacket_req req; + socklen_t len; + unsigned int sk_type, tp_reserve, maclen, tp_hdrlen, netoff, macoff; + unsigned int frame_size; /* * Start out assuming no warnings or errors. */ *status = 0; - /* Note that with large snapshot (say 64K) only a few frames - * will be available in the ring even with pretty large ring size - * (and a lot of memory will be unused). - * The snap len should be carefully chosen to achive best - * performance */ - req.tp_frame_size = TPACKET_ALIGN(handle->snapshot + - TPACKET_ALIGN(handle->md.tp_hdrlen) + - sizeof(struct sockaddr_ll)); + /* Note that with large snapshot length (say 64K, which is the default + * for recent versions of tcpdump, the value that "-s 0" has given + * for a long time with tcpdump, and the default in Wireshark/TShark), + * if we use the snapshot length to calculate the frame length, + * only a few frames will be available in the ring even with pretty + * large ring size (and a lot of memory will be unused). + * + * Ideally, we should choose a frame length based on the + * minimum of the specified snapshot length and the maximum + * packet size. That's not as easy as it sounds; consider, for + * example, an 802.11 interface in monitor mode, where the + * frame would include a radiotap header, where the maximum + * radiotap header length is device-dependent. + * + * So, for now, we just do this for Ethernet devices, where + * there's no metadata header, and the link-layer header is + * fixed length. We can get the maximum packet size by + * adding 18, the Ethernet header length plus the CRC length + * (just in case we happen to get the CRC in the packet), to + * the MTU of the interface; we fetch the MTU in the hopes + * that it reflects support for jumbo frames. (Even if the + * interface is just being used for passive snooping, the driver + * might set the size of buffers in the receive ring based on + * the MTU, so that the MTU limits the maximum size of packets + * that we can receive.) + * + * We don't do that if segmentation/fragmentation or receive + * offload are enabled, so we don't get rudely surprised by + * "packets" bigger than the MTU. */ + frame_size = handle->snapshot; + if (handle->linktype == DLT_EN10MB) { + int mtu; + int offload; + + offload = iface_get_offload(handle); + if (offload == -1) { + *status = PCAP_ERROR; + return -1; + } + if (!offload) { + mtu = iface_get_mtu(handle->fd, handle->opt.source, + handle->errbuf); + if (mtu == -1) { + *status = PCAP_ERROR; + return -1; + } + if (frame_size > mtu + 18) + frame_size = mtu + 18; + } + } + + /* NOTE: calculus matching those in tpacket_rcv() + * in linux-2.6/net/packet/af_packet.c + */ + len = sizeof(sk_type); + if (getsockopt(handle->fd, SOL_SOCKET, SO_TYPE, &sk_type, &len) < 0) { + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "getsockopt: %s", pcap_strerror(errno)); + *status = PCAP_ERROR; + return -1; + } +#ifdef PACKET_RESERVE + len = sizeof(tp_reserve); + if (getsockopt(handle->fd, SOL_PACKET, PACKET_RESERVE, &tp_reserve, &len) < 0) { + if (errno != ENOPROTOOPT) { + /* + * ENOPROTOOPT means "kernel doesn't support + * PACKET_RESERVE", in which case we fall back + * as best we can. + */ + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "getsockopt: %s", pcap_strerror(errno)); + *status = PCAP_ERROR; + return -1; + } + tp_reserve = 0; /* older kernel, reserve not supported */ + } +#else + tp_reserve = 0; /* older kernel, reserve not supported */ +#endif + maclen = (sk_type == SOCK_DGRAM) ? 0 : MAX_LINKHEADER_SIZE; + /* XXX: in the kernel maclen is calculated from + * LL_ALLOCATED_SPACE(dev) and vnet_hdr.hdr_len + * in: packet_snd() in linux-2.6/net/packet/af_packet.c + * then packet_alloc_skb() in linux-2.6/net/packet/af_packet.c + * then sock_alloc_send_pskb() in linux-2.6/net/core/sock.c + * but I see no way to get those sizes in userspace, + * like for instance with an ifreq ioctl(); + * the best thing I've found so far is MAX_HEADER in the kernel + * part of linux-2.6/include/linux/netdevice.h + * which goes up to 128+48=176; since pcap-linux.c defines + * a MAX_LINKHEADER_SIZE of 256 which is greater than that, + * let's use it.. maybe is it even large enough to directly + * replace macoff.. + */ + tp_hdrlen = TPACKET_ALIGN(handle->md.tp_hdrlen) + sizeof(struct sockaddr_ll) ; + netoff = TPACKET_ALIGN(tp_hdrlen + (maclen < 16 ? 16 : maclen)) + tp_reserve; + /* NOTE: AFAICS tp_reserve may break the TPACKET_ALIGN of + * netoff, which contradicts + * linux-2.6/Documentation/networking/packet_mmap.txt + * documenting that: + * "- Gap, chosen so that packet data (Start+tp_net) + * aligns to TPACKET_ALIGNMENT=16" + */ + /* NOTE: in linux-2.6/include/linux/skbuff.h: + * "CPUs often take a performance hit + * when accessing unaligned memory locations" + */ + macoff = netoff - maclen; + req.tp_frame_size = TPACKET_ALIGN(macoff + frame_size); req.tp_frame_nr = handle->opt.buffer_size/req.tp_frame_size; /* compute the minumum block size that will handle this frame. @@ -3635,7 +4051,7 @@ pcap_read_linux_mmap(pcap_t *handle, int max_packets, pcap_handler callback, 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; } @@ -3736,14 +4152,20 @@ pcap_read_linux_mmap(pcap_t *handle, int max_packets, pcap_handler callback, } #ifdef HAVE_TPACKET2 - if (handle->md.tp_version == TPACKET_V2 && h.h2->tp_vlan_tci && - tp_snaplen >= 2 * ETH_ALEN) { + if ((handle->md.tp_version == TPACKET_V2) && +#if defined(TP_STATUS_VLAN_VALID) + (h.h2->tp_vlan_tci || (h.h2->tp_status & TP_STATUS_VLAN_VALID)) && +#else + h.h2->tp_vlan_tci && +#endif + handle->md.vlan_offset != -1 && + tp_snaplen >= (unsigned int) handle->md.vlan_offset) { struct vlan_tag *tag; bp -= VLAN_TAG_LEN; - memmove(bp, bp + VLAN_TAG_LEN, 2 * ETH_ALEN); + memmove(bp, bp + VLAN_TAG_LEN, handle->md.vlan_offset); - tag = (struct vlan_tag *)(bp + 2 * ETH_ALEN); + tag = (struct vlan_tag *)(bp + handle->md.vlan_offset); tag->vlan_tpid = htons(ETH_P_8021Q); tag->vlan_tci = htons(h.h2->tp_vlan_tci); @@ -4038,6 +4460,8 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device) monitor_type montype; int i; __u32 cmd; + struct ifreq ifr; + int oldflags; int args[2]; int channel; @@ -4047,6 +4471,13 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device) err = has_wext(sock_fd, device, handle->errbuf); if (err <= 0) return err; /* either it doesn't or the device doesn't even exist */ + /* + * Start out assuming we have no private extensions to control + * radio metadata. + */ + montype = MONITOR_WEXT; + cmd = 0; + /* * Try to get all the Wireless Extensions private ioctls * supported by this device. @@ -4070,187 +4501,189 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device) device); return PCAP_ERROR; } - if (errno == EOPNOTSUPP) { + if (errno != EOPNOTSUPP) { /* - * No private ioctls, so we assume that there's only one - * DLT_ for monitor mode. + * OK, it's not as if there are no private ioctls. */ - return 0; - } - if (errno != E2BIG) { - /* - * Failed. - */ - snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, - "%s: SIOCGIWPRIV: %s", device, pcap_strerror(errno)); - return PCAP_ERROR; - } - priv = malloc(ireq.u.data.length * sizeof (struct iw_priv_args)); - if (priv == NULL) { - 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, - "%s: SIOCGIWPRIV: %s", device, pcap_strerror(errno)); - free(priv); - return PCAP_ERROR; - } - - /* - * Look for private ioctls to turn monitor mode on or, if - * monitor mode is on, to set the header type. - */ - montype = MONITOR_WEXT; - cmd = 0; - for (i = 0; i < ireq.u.data.length; i++) { - if (strcmp(priv[i].name, "monitor_type") == 0) { - /* - * Hostap driver, use this one. - * Set monitor mode first. - * You can set it to 0 to get DLT_IEEE80211, - * 1 to get DLT_PRISM, 2 to get - * DLT_IEEE80211_RADIO_AVS, and, with more - * recent versions of the driver, 3 to get - * DLT_IEEE80211_RADIO. - */ - if ((priv[i].set_args & IW_PRIV_TYPE_MASK) != IW_PRIV_TYPE_INT) - break; - if (!(priv[i].set_args & IW_PRIV_SIZE_FIXED)) - break; - if ((priv[i].set_args & IW_PRIV_SIZE_MASK) != 1) - break; - montype = MONITOR_HOSTAP; - cmd = priv[i].cmd; - break; - } - if (strcmp(priv[i].name, "set_prismhdr") == 0) { - /* - * Prism54 driver, use this one. - * Set monitor mode first. - * You can set it to 2 to get DLT_IEEE80211 - * or 3 or get DLT_PRISM. - */ - if ((priv[i].set_args & IW_PRIV_TYPE_MASK) != IW_PRIV_TYPE_INT) - break; - if (!(priv[i].set_args & IW_PRIV_SIZE_FIXED)) - break; - if ((priv[i].set_args & IW_PRIV_SIZE_MASK) != 1) - break; - montype = MONITOR_PRISM54; - cmd = priv[i].cmd; - break; - } - if (strcmp(priv[i].name, "forceprismheader") == 0) { + if (errno != E2BIG) { /* - * RT2570 driver, use this one. - * Do this after turning monitor mode on. - * You can set it to 1 to get DLT_PRISM or 2 - * to get DLT_IEEE80211. + * Failed. */ - if ((priv[i].set_args & IW_PRIV_TYPE_MASK) != IW_PRIV_TYPE_INT) - break; - if (!(priv[i].set_args & IW_PRIV_SIZE_FIXED)) - break; - if ((priv[i].set_args & IW_PRIV_SIZE_MASK) != 1) - break; - montype = MONITOR_RT2570; - cmd = priv[i].cmd; - break; + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + "%s: SIOCGIWPRIV: %s", device, + pcap_strerror(errno)); + return PCAP_ERROR; } - if (strcmp(priv[i].name, "forceprism") == 0) { - /* - * RT73 driver, use this one. - * Do this after turning monitor mode on. - * Its argument is a *string*; you can - * set it to "1" to get DLT_PRISM or "2" - * to get DLT_IEEE80211. - */ - if ((priv[i].set_args & IW_PRIV_TYPE_MASK) != IW_PRIV_TYPE_CHAR) - break; - if (priv[i].set_args & IW_PRIV_SIZE_FIXED) - break; - montype = MONITOR_RT73; - cmd = priv[i].cmd; - break; + + /* + * OK, try to get the list of private ioctls. + */ + priv = malloc(ireq.u.data.length * sizeof (struct iw_priv_args)); + if (priv == NULL) { + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + "malloc: %s", pcap_strerror(errno)); + return PCAP_ERROR; } - if (strcmp(priv[i].name, "prismhdr") == 0) { - /* - * One of the RTL8xxx drivers, use this one. - * It can only be done after monitor mode - * has been turned on. You can set it to 1 - * to get DLT_PRISM or 0 to get DLT_IEEE80211. - */ - if ((priv[i].set_args & IW_PRIV_TYPE_MASK) != IW_PRIV_TYPE_INT) - break; - if (!(priv[i].set_args & IW_PRIV_SIZE_FIXED)) - break; - if ((priv[i].set_args & IW_PRIV_SIZE_MASK) != 1) - break; - montype = MONITOR_RTL8XXX; - cmd = priv[i].cmd; - break; + ireq.u.data.pointer = (void *)priv; + if (ioctl(sock_fd, SIOCGIWPRIV, &ireq) == -1) { + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + "%s: SIOCGIWPRIV: %s", device, + pcap_strerror(errno)); + free(priv); + return PCAP_ERROR; } - if (strcmp(priv[i].name, "rfmontx") == 0) { - /* - * RT2500 or RT61 driver, use this one. - * It has one one-byte parameter; set - * u.data.length to 1 and u.data.pointer to - * point to the parameter. - * It doesn't itself turn monitor mode on. - * You can set it to 1 to allow transmitting - * in monitor mode(?) and get DLT_IEEE80211, - * or set it to 0 to disallow transmitting in - * monitor mode(?) and get DLT_PRISM. - */ - if ((priv[i].set_args & IW_PRIV_TYPE_MASK) != IW_PRIV_TYPE_INT) - break; - if ((priv[i].set_args & IW_PRIV_SIZE_MASK) != 2) + + /* + * Look for private ioctls to turn monitor mode on or, if + * monitor mode is on, to set the header type. + */ + for (i = 0; i < ireq.u.data.length; i++) { + if (strcmp(priv[i].name, "monitor_type") == 0) { + /* + * Hostap driver, use this one. + * Set monitor mode first. + * You can set it to 0 to get DLT_IEEE80211, + * 1 to get DLT_PRISM, 2 to get + * DLT_IEEE80211_RADIO_AVS, and, with more + * recent versions of the driver, 3 to get + * DLT_IEEE80211_RADIO. + */ + if ((priv[i].set_args & IW_PRIV_TYPE_MASK) != IW_PRIV_TYPE_INT) + break; + if (!(priv[i].set_args & IW_PRIV_SIZE_FIXED)) + break; + if ((priv[i].set_args & IW_PRIV_SIZE_MASK) != 1) + break; + montype = MONITOR_HOSTAP; + cmd = priv[i].cmd; break; - montype = MONITOR_RT2500; - cmd = priv[i].cmd; - break; - } - if (strcmp(priv[i].name, "monitor") == 0) { - /* - * Either ACX100 or hostap, use this one. - * It turns monitor mode on. - * If it takes two arguments, it's ACX100; - * the first argument is 1 for DLT_PRISM - * or 2 for DLT_IEEE80211, and the second - * argument is the channel on which to - * run. If it takes one argument, it's - * HostAP, and the argument is 2 for - * DLT_IEEE80211 and 3 for DLT_PRISM. - * - * If we see this, we don't quit, as this - * might be a version of the hostap driver - * that also supports "monitor_type". - */ - if ((priv[i].set_args & IW_PRIV_TYPE_MASK) != IW_PRIV_TYPE_INT) + } + if (strcmp(priv[i].name, "set_prismhdr") == 0) { + /* + * Prism54 driver, use this one. + * Set monitor mode first. + * You can set it to 2 to get DLT_IEEE80211 + * or 3 or get DLT_PRISM. + */ + if ((priv[i].set_args & IW_PRIV_TYPE_MASK) != IW_PRIV_TYPE_INT) + break; + if (!(priv[i].set_args & IW_PRIV_SIZE_FIXED)) + break; + if ((priv[i].set_args & IW_PRIV_SIZE_MASK) != 1) + break; + montype = MONITOR_PRISM54; + cmd = priv[i].cmd; break; - if (!(priv[i].set_args & IW_PRIV_SIZE_FIXED)) + } + if (strcmp(priv[i].name, "forceprismheader") == 0) { + /* + * RT2570 driver, use this one. + * Do this after turning monitor mode on. + * You can set it to 1 to get DLT_PRISM or 2 + * to get DLT_IEEE80211. + */ + if ((priv[i].set_args & IW_PRIV_TYPE_MASK) != IW_PRIV_TYPE_INT) + break; + if (!(priv[i].set_args & IW_PRIV_SIZE_FIXED)) + break; + if ((priv[i].set_args & IW_PRIV_SIZE_MASK) != 1) + break; + montype = MONITOR_RT2570; + cmd = priv[i].cmd; break; - switch (priv[i].set_args & IW_PRIV_SIZE_MASK) { - - case 1: - montype = MONITOR_PRISM; + } + if (strcmp(priv[i].name, "forceprism") == 0) { + /* + * RT73 driver, use this one. + * Do this after turning monitor mode on. + * Its argument is a *string*; you can + * set it to "1" to get DLT_PRISM or "2" + * to get DLT_IEEE80211. + */ + if ((priv[i].set_args & IW_PRIV_TYPE_MASK) != IW_PRIV_TYPE_CHAR) + break; + if (priv[i].set_args & IW_PRIV_SIZE_FIXED) + break; + montype = MONITOR_RT73; cmd = priv[i].cmd; break; - - case 2: - montype = MONITOR_ACX100; + } + if (strcmp(priv[i].name, "prismhdr") == 0) { + /* + * One of the RTL8xxx drivers, use this one. + * It can only be done after monitor mode + * has been turned on. You can set it to 1 + * to get DLT_PRISM or 0 to get DLT_IEEE80211. + */ + if ((priv[i].set_args & IW_PRIV_TYPE_MASK) != IW_PRIV_TYPE_INT) + break; + if (!(priv[i].set_args & IW_PRIV_SIZE_FIXED)) + break; + if ((priv[i].set_args & IW_PRIV_SIZE_MASK) != 1) + break; + montype = MONITOR_RTL8XXX; cmd = priv[i].cmd; break; - - default: + } + if (strcmp(priv[i].name, "rfmontx") == 0) { + /* + * RT2500 or RT61 driver, use this one. + * It has one one-byte parameter; set + * u.data.length to 1 and u.data.pointer to + * point to the parameter. + * It doesn't itself turn monitor mode on. + * You can set it to 1 to allow transmitting + * in monitor mode(?) and get DLT_IEEE80211, + * or set it to 0 to disallow transmitting in + * monitor mode(?) and get DLT_PRISM. + */ + if ((priv[i].set_args & IW_PRIV_TYPE_MASK) != IW_PRIV_TYPE_INT) + break; + if ((priv[i].set_args & IW_PRIV_SIZE_MASK) != 2) + break; + montype = MONITOR_RT2500; + cmd = priv[i].cmd; break; } + if (strcmp(priv[i].name, "monitor") == 0) { + /* + * Either ACX100 or hostap, use this one. + * It turns monitor mode on. + * If it takes two arguments, it's ACX100; + * the first argument is 1 for DLT_PRISM + * or 2 for DLT_IEEE80211, and the second + * argument is the channel on which to + * run. If it takes one argument, it's + * HostAP, and the argument is 2 for + * DLT_IEEE80211 and 3 for DLT_PRISM. + * + * If we see this, we don't quit, as this + * might be a version of the hostap driver + * that also supports "monitor_type". + */ + if ((priv[i].set_args & IW_PRIV_TYPE_MASK) != IW_PRIV_TYPE_INT) + break; + if (!(priv[i].set_args & IW_PRIV_SIZE_FIXED)) + break; + switch (priv[i].set_args & IW_PRIV_SIZE_MASK) { + + case 1: + montype = MONITOR_PRISM; + cmd = priv[i].cmd; + break; + + case 2: + montype = MONITOR_ACX100; + cmd = priv[i].cmd; + break; + + default: + break; + } + } } + free(priv); } - free(priv); /* * XXX - ipw3945? islism? @@ -4345,7 +4778,29 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device) } /* - * First, turn monitor mode on. + * First, take the interface down if it's up; otherwise, we + * might get EBUSY. + */ + memset(&ifr, 0, sizeof(ifr)); + strncpy(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)); + return PCAP_ERROR; + } + oldflags = 0; + if (ifr.ifr_flags & IFF_UP) { + oldflags = ifr.ifr_flags; + ifr.ifr_flags &= ~IFF_UP; + if (ioctl(sock_fd, SIOCSIFFLAGS, &ifr) == -1) { + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + "%s: Can't set flags: %s", device, strerror(errno)); + return PCAP_ERROR; + } + } + + /* + * Then turn monitor mode on. */ strncpy(ireq.ifr_ifrn.ifrn_name, device, sizeof ireq.ifr_ifrn.ifrn_name); @@ -4354,7 +4809,14 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device) if (ioctl(sock_fd, SIOCSIWMODE, &ireq) == -1) { /* * Scientist, you've failed. + * Bring the interface back up if we shut it down. */ + ifr.ifr_flags = oldflags; + if (ioctl(sock_fd, SIOCSIFFLAGS, &ifr) == -1) { + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + "%s: Can't set flags: %s", device, strerror(errno)); + return PCAP_ERROR; + } return PCAP_ERROR_RFMON_NOTSUP; } @@ -4516,6 +4978,32 @@ enter_rfmon_mode_wext(pcap_t *handle, int sock_fd, const char *device) break; } + /* + * Now bring the interface back up if we brought it down. + */ + if (oldflags != 0) { + ifr.ifr_flags = oldflags; + if (ioctl(sock_fd, SIOCSIFFLAGS, &ifr) == -1) { + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + "%s: Can't set flags: %s", device, strerror(errno)); + + /* + * At least try to restore the old mode on the + * interface. + */ + if (ioctl(handle->fd, SIOCSIWMODE, &ireq) == -1) { + /* + * Scientist, you've failed. + */ + fprintf(stderr, + "Can't restore interface wireless mode (SIOCSIWMODE failed: %s).\n" + "Please adjust manually.\n", + strerror(errno)); + } + return PCAP_ERROR; + } + } + /* * Note that we have to put the old mode back when we * close the device. @@ -4565,6 +5053,112 @@ enter_rfmon_mode(pcap_t *handle, int sock_fd, const char *device) return 0; } +/* + * Find out if we have any form of fragmentation/reassembly offloading. + * + * We do so using SIOCETHTOOL checking for various types of offloading; + * if SIOCETHTOOL isn't defined, or we don't have any #defines for any + * of the types of offloading, there's nothing we can do to check, so + * we just say "no, we don't". + */ +#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) +{ + struct ifreq ifr; + struct ethtool_value eval; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, handle->opt.source, sizeof(ifr.ifr_name)); + eval.cmd = cmd; + ifr.ifr_data = (caddr_t)&eval; + if (ioctl(handle->fd, SIOCETHTOOL, &ifr) == -1) { + if (errno == EOPNOTSUPP || errno == EINVAL) { + /* + * OK, let's just return 0, which, in our + * case, either means "no, what we're asking + * about is not enabled" or "all the flags + * are clear (i.e., nothing is enabled)". + */ + return 0; + } + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + "%s: SIOETHTOOL(%s) ioctl failed: %s", handle->opt.source, + cmdname, strerror(errno)); + return -1; + } + return eval.data; +} + +static int +iface_get_offload(pcap_t *handle) +{ + int ret; + +#ifdef ETHTOOL_GTSO + ret = iface_ethtool_ioctl(handle, ETHTOOL_GTSO, "ETHTOOL_GTSO"); + if (ret == -1) + return -1; + if (ret) + return 1; /* TCP segmentation offloading on */ +#endif + +#ifdef ETHTOOL_GUFO + ret = iface_ethtool_ioctl(handle, ETHTOOL_GUFO, "ETHTOOL_GUFO"); + if (ret == -1) + return -1; + if (ret) + return 1; /* UDP fragmentation offloading on */ +#endif + +#ifdef ETHTOOL_GGSO + /* + * XXX - will this cause large unsegmented packets to be + * handed to PF_PACKET sockets on transmission? If not, + * this need not be checked. + */ + ret = iface_ethtool_ioctl(handle, ETHTOOL_GGSO, "ETHTOOL_GGSO"); + if (ret == -1) + return -1; + if (ret) + return 1; /* generic segmentation offloading on */ +#endif + +#ifdef ETHTOOL_GFLAGS + ret = iface_ethtool_ioctl(handle, ETHTOOL_GFLAGS, "ETHTOOL_GFLAGS"); + if (ret == -1) + return -1; + if (ret & ETH_FLAG_LRO) + return 1; /* large receive offloading on */ +#endif + +#ifdef ETHTOOL_GGRO + /* + * XXX - will this cause large reassembled packets to be + * handed to PF_PACKET sockets on receipt? If not, + * this need not be checked. + */ + ret = iface_ethtool_ioctl(handle, ETHTOOL_GGRO, "ETHTOOL_GGRO"); + if (ret == -1) + return -1; + if (ret) + return 1; /* generic (large) receive offloading on */ +#endif + + return 0; +} +#else /* SIOCETHTOOL */ +static int +iface_get_offload(pcap_t *handle _U_) +{ + /* + * XXX - do we need to get this information if we don't + * have the ethtool ioctls? If so, how do we do that? + */ + return 0; +} +#endif /* SIOCETHTOOL */ + #endif /* HAVE_PF_PACKET_SOCKETS */ /* ===== Functions to interface to the older kernels ================== */ @@ -4588,7 +5182,18 @@ activate_old(pcap_t *handle) if (handle->fd == -1) { snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "socket: %s", pcap_strerror(errno)); - return PCAP_ERROR_PERM_DENIED; + if (errno == EPERM || errno == EACCES) { + /* + * You don't have permission to open the + * socket. + */ + return PCAP_ERROR_PERM_DENIED; + } else { + /* + * Other error. + */ + return PCAP_ERROR; + } } /* It worked - we are using the old interface */ @@ -4618,7 +5223,7 @@ activate_old(pcap_t *handle) * Try to find the DLT_ type corresponding to that * link-layer type. */ - map_arphrd_to_dlt(handle, arptype, 0); + map_arphrd_to_dlt(handle, handle->fd, arptype, device, 0); if (handle->linktype == -1) { snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "unknown arptype %d", arptype); @@ -4746,6 +5351,12 @@ activate_old(pcap_t *handle) */ handle->offset = 0; + /* + * SOCK_PACKET sockets don't supply information from + * stripped VLAN tags. + */ + handle->md.vlan_offset = -1; /* unknown */ + return 1; } @@ -4952,13 +5563,19 @@ fix_offset(struct bpf_insn *p) * header. */ p->k -= SLL_HDR_LEN; + } else if (p->k == 0) { + /* + * It's the packet type field; map it to the special magic + * kernel offset for that field. + */ + p->k = SKF_AD_OFF + SKF_AD_PKTTYPE; } else if (p->k == 14) { /* * It's the protocol field; map it to the special magic * kernel offset for that field. */ p->k = SKF_AD_OFF + SKF_AD_PROTOCOL; - } else { + } else if ((bpf_int32)(p->k) > 0) { /* * It's within the header, but it's not one of those * fields; we can't do that in the kernel, so punt