X-Git-Url: https://git.tcpdump.org/libpcap/blobdiff_plain/cc8520ff5294900d93509eaf843684c51af102a9..refs/heads/libpcap-1.3:/pcap-linux.c diff --git a/pcap-linux.c b/pcap-linux.c index a70d585f..e5c3afa7 100644 --- a/pcap-linux.c +++ b/pcap-linux.c @@ -127,50 +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 */ - -/* - * Got ethtool support? - */ -#ifdef HAVE_LINUX_ETHTOOL_H -#include -#include -#endif /* HAVE_LINUX_ETHTOOL_H */ - #include "pcap-int.h" #include "pcap/sll.h" #include "pcap/vlan.h" @@ -199,9 +168,12 @@ 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 -int netfilter_platform_finddevs(pcap_if_t **alldevsp, char *err_str); -pcap_t *nflog_create(const char *device, char *ebuf); +#include "pcap-netfilter-linux.h" #endif /* @@ -268,6 +240,46 @@ pcap_t *nflog_create(const char *device, char *ebuf); #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 @@ -306,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 @@ -412,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 @@ -855,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. */ @@ -1064,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 */ @@ -1087,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) { /* @@ -1101,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)); } } @@ -1137,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] @@ -1147,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 */ @@ -1550,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; - bp -= VLAN_TAG_LEN; - memmove(bp, bp + VLAN_TAG_LEN, 2 * ETH_ALEN); + 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, 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 */ @@ -1883,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; @@ -1890,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. @@ -1915,10 +2007,41 @@ scan_sys_class_net(pcap_if_t **devlistp, char *errbuf) } /* - * Ignore directories (".", "..", and any subdirectories). + * Ignore "." and "..". + */ + 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.) */ - if (ent->d_type == DT_DIR) + 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. @@ -2024,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. @@ -2218,6 +2353,11 @@ pcap_platform_finddevs(pcap_if_t **alldevsp, char *errbuf) return (-1); #endif +#if PCAP_SUPPORT_CANUSB + if (canusb_platform_finddevs(alldevsp, errbuf) < 0) + return (-1); +#endif + return (0); } @@ -2447,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 @@ -2465,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 @@ -2480,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 */ @@ -2804,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. */ @@ -2876,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 || @@ -2903,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; @@ -3054,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; @@ -3792,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; } @@ -3893,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); @@ -4195,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; @@ -4204,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. @@ -4227,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? @@ -4502,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); @@ -4511,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; } @@ -4673,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. @@ -4724,8 +5055,13 @@ enter_rfmon_mode(pcap_t *handle, int sock_fd, const char *device) /* * 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". */ -#ifdef SIOCETHTOOL +#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) { @@ -4737,7 +5073,7 @@ iface_ethtool_ioctl(pcap_t *handle, int cmd, const char *cmdname) eval.cmd = cmd; ifr.ifr_data = (caddr_t)&eval; if (ioctl(handle->fd, SIOCETHTOOL, &ifr) == -1) { - if (errno == EOPNOTSUPP) { + if (errno == EOPNOTSUPP || errno == EINVAL) { /* * OK, let's just return 0, which, in our * case, either means "no, what we're asking @@ -4759,18 +5095,23 @@ 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, @@ -4781,6 +5122,7 @@ iface_get_offload(pcap_t *handle) return -1; if (ret) return 1; /* generic segmentation offloading on */ +#endif #ifdef ETHTOOL_GFLAGS ret = iface_ethtool_ioctl(handle, ETHTOOL_GFLAGS, "ETHTOOL_GFLAGS"); @@ -4840,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 */ @@ -4870,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); @@ -4998,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; } @@ -5204,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