diff options
Diffstat (limited to 'contrib/libpcap/pcap-linux.c')
-rw-r--r-- | contrib/libpcap/pcap-linux.c | 117 |
1 files changed, 90 insertions, 27 deletions
diff --git a/contrib/libpcap/pcap-linux.c b/contrib/libpcap/pcap-linux.c index b8ed1f3..e9db010 100644 --- a/contrib/libpcap/pcap-linux.c +++ b/contrib/libpcap/pcap-linux.c @@ -27,7 +27,7 @@ #ifndef lint static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/pcap-linux.c,v 1.110.2.6 2005/08/16 04:25:26 guy Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/pcap-linux.c,v 1.110.2.14 2006/10/12 17:26:58 guy Exp $ (LBL)"; #endif /* @@ -394,7 +394,16 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, * * We can safely pass "recvfrom()" a byte count * based on the snapshot length. + * + * If we're in cooked mode, make the snapshot length + * large enough to hold a "cooked mode" header plus + * 1 byte of packet data (so we don't pass a byte + * count of 0 to "recvfrom()"). */ + if (handle->md.cooked) { + if (handle->snapshot < SLL_HDR_LEN + 1) + handle->snapshot = SLL_HDR_LEN + 1; + } handle->bufsize = handle->snapshot; } @@ -518,6 +527,22 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata) #ifdef HAVE_PF_PACKET_SOCKETS if (!handle->md.sock_packet) { /* + * Unfortunately, there is a window between socket() and + * bind() where the kernel may queue packets from any + * interface. If we're bound to a particular interface, + * discard packets not from that interface. + * + * (If socket filters are supported, we could do the + * same thing we do when changing the filter; however, + * that won't handle packet sockets without socket + * filter support, and it's a bit more complicated. + * It would save some instructions per packet, however.) + */ + if (handle->md.ifindex != -1 && + from.sll_ifindex != handle->md.ifindex) + return 0; + + /* * Do checks based on packet direction. * We can only do this if we're using PF_PACKET; the * address returned for SOCK_PACKET is a "sockaddr_pkt" @@ -657,7 +682,7 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata) if (ioctl(handle->fd, SIOCGSTAMP, &pcap_header.ts) == -1) { snprintf(handle->errbuf, sizeof(handle->errbuf), - "ioctl: %s", pcap_strerror(errno)); + "SIOCGSTAMP: %s", pcap_strerror(errno)); return -1; } pcap_header.caplen = caplen; @@ -695,8 +720,18 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata) * here, but it's not clear that always incrementing * the count is more expensive than always testing a flag * in memory. + * + * We keep the count in "md.packets_read", and use that for + * "ps_recv" if we can't get the statistics from the kernel. + * We do that because, if we *can* get the statistics from + * the kernel, we use "md.stat.ps_recv" and "md.stat.ps_drop" + * as running counts, as reading the statistics from the + * kernel resets the kernel statistics, and if we directly + * increment "md.stat.ps_recv" here, that means it will + * count packets *twice* on systems where we can get kernel + * statistics - once here, and once in pcap_stats_linux(). */ - handle->md.stat.ps_recv++; + handle->md.packets_read++; /* Call the user supplied callback function */ callback(userdata, &pcap_header, bp); @@ -770,6 +805,23 @@ pcap_stats_linux(pcap_t *handle, struct pcap_stat *stats) if (getsockopt(handle->fd, SOL_PACKET, PACKET_STATISTICS, &kstats, &len) > -1) { /* + * On systems where the PACKET_STATISTICS "getsockopt()" + * argument is supported on PF_PACKET sockets: + * + * "ps_recv" counts only packets that *passed* the + * filter, not packets that didn't pass the filter. + * This includes packets later dropped because we + * ran out of buffer space. + * + * "ps_drop" counts packets dropped because we ran + * out of buffer space. It doesn't count packets + * dropped by the interface driver. It counts only + * packets that passed the filter. + * + * Both statistics include packets not yet read from + * the kernel by libpcap, and thus not yet seen by + * the application. + * * In "linux/net/packet/af_packet.c", at least in the * 2.4.9 kernel, "tp_packets" is incremented for every * packet that passes the packet filter *and* is @@ -799,6 +851,8 @@ pcap_stats_linux(pcap_t *handle, struct pcap_stat *stats) */ handle->md.stat.ps_recv += kstats.tp_packets; handle->md.stat.ps_drop += kstats.tp_drops; + *stats = handle->md.stat; + return 0; } else { @@ -818,21 +872,6 @@ pcap_stats_linux(pcap_t *handle, struct pcap_stat *stats) #endif /* * On systems where the PACKET_STATISTICS "getsockopt()" argument - * is supported on PF_PACKET sockets: - * - * "ps_recv" counts only packets that *passed* the filter, - * not packets that didn't pass the filter. This includes - * packets later dropped because we ran out of buffer space. - * - * "ps_drop" counts packets dropped because we ran out of - * buffer space. It doesn't count packets dropped by the - * interface driver. It counts only packets that passed - * the filter. - * - * Both statistics include packets not yet read from the - * kernel by libpcap, and thus not yet seen by the application. - * - * On systems where the PACKET_STATISTICS "getsockopt()" argument * is not supported on PF_PACKET sockets: * * "ps_recv" counts only packets that *passed* the filter, @@ -844,8 +883,14 @@ pcap_stats_linux(pcap_t *handle, struct pcap_stat *stats) * * "ps_recv" doesn't include packets not yet read from * the kernel by libpcap. + * + * We maintain the count of packets processed by libpcap in + * "md.packets_read", for reasons described in the comment + * at the end of pcap_read_packet(). We have no idea how many + * packets were dropped. */ - *stats = handle->md.stat; + stats->ps_recv = handle->md.packets_read; + stats->ps_drop = 0; return 0; } @@ -917,6 +962,7 @@ pcap_setfilter_linux(pcap_t *handle, struct bpf_program *filter) * sake of correctness I added this check. */ fprintf(stderr, "Warning: Filter too complex for kernel\n"); + fcode.len = 0; fcode.filter = NULL; can_filter_in_kernel = 0; } else @@ -1315,6 +1361,16 @@ static void map_arphrd_to_dlt(pcap_t *handle, int arptype, int cooked_ok) //handle->md.cooked = 1; break; + /* ARPHRD_LAPD is unofficial and randomly allocated, if reallocation + * is needed, please report it to <daniele@orlandi.com> */ +#ifndef ARPHRD_LAPD +#define ARPHRD_LAPD 8445 +#endif + case ARPHRD_LAPD: + /* Don't expect IP packet out of this interfaces... */ + handle->linktype = DLT_LINUX_LAPD; + break; + default: handle->linktype = -1; break; @@ -1396,6 +1452,7 @@ live_open_new(pcap_t *handle, const char *device, int promisc, if (handle->linktype == -1 || handle->linktype == DLT_LINUX_SLL || handle->linktype == DLT_LINUX_IRDA || + handle->linktype == DLT_LINUX_LAPD || (handle->linktype == DLT_EN10MB && (strncmp("isdn", device, 4) == 0 || strncmp("isdY", device, 4) == 0))) { @@ -1449,7 +1506,8 @@ live_open_new(pcap_t *handle, const char *device, int promisc, } /* IrDA capture is not a real "cooked" capture, * it's IrLAP frames, not IP packets. */ - if (handle->linktype != DLT_LINUX_IRDA) + if (handle->linktype != DLT_LINUX_IRDA && + handle->linktype != DLT_LINUX_LAPD) handle->linktype = DLT_LINUX_SLL; } @@ -1557,7 +1615,7 @@ iface_get_id(int fd, const char *device, char *ebuf) if (ioctl(fd, SIOCGIFINDEX, &ifr) == -1) { snprintf(ebuf, PCAP_ERRBUF_SIZE, - "ioctl: %s", pcap_strerror(errno)); + "SIOCGIFINDEX: %s", pcap_strerror(errno)); return -1; } @@ -1774,7 +1832,7 @@ live_open_old(pcap_t *handle, const char *device, int promisc, strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name)); if (ioctl(handle->fd, SIOCGIFFLAGS, &ifr) == -1) { snprintf(ebuf, PCAP_ERRBUF_SIZE, - "ioctl: %s", pcap_strerror(errno)); + "SIOCGIFFLAGS: %s", pcap_strerror(errno)); break; } if ((ifr.ifr_flags & IFF_PROMISC) == 0) { @@ -1808,7 +1866,7 @@ live_open_old(pcap_t *handle, const char *device, int promisc, ifr.ifr_flags |= IFF_PROMISC; if (ioctl(handle->fd, SIOCSIFFLAGS, &ifr) == -1) { snprintf(ebuf, PCAP_ERRBUF_SIZE, - "ioctl: %s", + "SIOCSIFFLAGS: %s", pcap_strerror(errno)); break; } @@ -1892,7 +1950,7 @@ iface_get_mtu(int fd, const char *device, char *ebuf) if (ioctl(fd, SIOCGIFMTU, &ifr) == -1) { snprintf(ebuf, PCAP_ERRBUF_SIZE, - "ioctl: %s", pcap_strerror(errno)); + "SIOCGIFMTU: %s", pcap_strerror(errno)); return -1; } @@ -1912,7 +1970,7 @@ iface_get_arptype(int fd, const char *device, char *ebuf) if (ioctl(fd, SIOCGIFHWADDR, &ifr) == -1) { snprintf(ebuf, PCAP_ERRBUF_SIZE, - "ioctl: %s", pcap_strerror(errno)); + "SIOCGIFHWADDR: %s", pcap_strerror(errno)); return -1; } @@ -2149,8 +2207,13 @@ set_kernel_filter(pcap_t *handle, struct sock_fprog *fcode) static int reset_kernel_filter(pcap_t *handle) { - /* setsockopt() barfs unless it get a dummy parameter */ - int dummy; + /* + * setsockopt() barfs unless it get a dummy parameter. + * valgrind whines unless the value is initialized, + * as it has no idea that setsockopt() ignores its + * parameter. + */ + int dummy = 0; return setsockopt(handle->fd, SOL_SOCKET, SO_DETACH_FILTER, &dummy, sizeof(dummy)); |