X-Git-Url: https://git.tcpdump.org/libpcap/blobdiff_plain/2a74cb1ec2057f959050c6d05b732be246a30f61..refs/heads/libpcap-1.3:/pcap-linux.c diff --git a/pcap-linux.c b/pcap-linux.c index 8d131cf1..e5c3afa7 100644 --- a/pcap-linux.c +++ b/pcap-linux.c @@ -127,11 +127,13 @@ static const char rcsid[] _U_ = #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -316,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 @@ -430,7 +432,7 @@ pcap_create(const char *device, char *ebuf) #ifdef PCAP_SUPPORT_CAN if ((strncmp(device, "can", 3) == 0 && isdigit(device[3])) || - (strncmp(device, "vcan", 4) == 0 && isigit(device[4]))) { + (strncmp(device, "vcan", 4) == 0 && isdigit(device[4]))) { return can_create(device, ebuf); } #endif @@ -1618,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; - tag = (struct vlan_tag *)(bp + 2 * ETH_ALEN); - tag->vlan_tpid = htons(ETH_P_8021Q); - tag->vlan_tci = htons(aux->tp_vlan_tci); + bp -= VLAN_TAG_LEN; + memmove(bp, bp + VLAN_TAG_LEN, handle->md.vlan_offset); - packet_len += VLAN_TAG_LEN; + 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; + } } #endif /* defined(HAVE_PACKET_AUXDATA) && defined(HAVE_LINUX_TPACKET_AUXDATA_TP_VLAN_TCI) */ #endif /* HAVE_PF_PACKET_SOCKETS */ @@ -1951,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; @@ -1995,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. @@ -2544,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 @@ -2562,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 @@ -2577,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 */ @@ -2992,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 || @@ -3181,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; @@ -3919,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; } @@ -4020,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); @@ -4935,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 @@ -5085,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); @@ -5213,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; }