tcpdump mailing list archives

Re: [PATCH] Re: Bug: Counting dropped packets in


From: Dustin Spicuzza <dustin () virtualroadside com>
Date: Mon, 31 Aug 2009 17:36:49 -0400

Stephen Donnelly wrote:
Dustin Spicuzza wrote:

So after reading the libpcap and kernel source, I see that this is
actually how its supposed to work. But it *seems* like it would be quite
nice if we could grab the interface driver drop statistics as well as
drops resulting from the capture buffer filling up.

I'm looking for a way to query the kernel about this number -- should I
bother submitting a patch if I can get it, or is the current behavior
more desired?

It appears that the only way to get this number is by looking at
/proc/stats/dev, so I added something that can parse it for the correct
interface, and get the drop stats from there. Since those are cumulative
over the boot time of the system, it stores a number and does
incremental updates of the number. It doesn't do this when not in
promiscuous mode however, since that doesn't make any sense.

It works for me, let me know what you think.

The current 'drop' count in libpcap is not intuitive, and frequently
arguably undercounts since it does not include 'rx buffer overflow' and
similar interface/OS specific packet loss. OTOH, the documentation is
quite clear about what it does count I think.

No, its not; its actually quite vague. From the man page:

"pcap_stats() fills in the pcap_stat structure pointed to by its second
argument.  The values represent packet statistics from the start of the
run to the time of the call."

The code has a few comments as to what it counts, and *that* is quite
clear. However, I would imagine not too many people will read that
unless they're using pcap from source.

And, after looking at it, there *is* support for drops by the interface
in pcap_stat(), its just currently not used by very many platforms. I
didn't see this previously, but looking at pcap.h:

/*
 * As returned by the pcap_stats()
 */
struct pcap_stat {
        u_int ps_recv;          /* number of packets received */
        u_int ps_drop;          /* number of packets dropped */
        u_int ps_ifdrop;        /* drops by interface XXX not yet
supported */
#ifdef WIN32
        u_int bs_capt;          /* number of packets that reach the
application */
#endif /* WIN32 */
};


So... I've changed my patch to populate ps_ifdrop instead, and it should
be good to go, without screwing with current applications. I suppose the
man page should be updated to mention that ps_ifdrop is only supported
on a few platforms (I noticed that grep shows one or two instances where
it is used).

Dustin






-- 
Innovation is just a problem away
diff --git a/pcap-int.h b/pcap-int.h
index a548220..5786ea5 100644
--- a/pcap-int.h
+++ b/pcap-int.h
@@ -139,6 +139,7 @@ struct pcap_md {
        u_int   tp_version;     /* version of tpacket_hdr for mmaped ring */
        u_int   tp_hdrlen;      /* hdrlen of tpacket_hdr for mmaped ring */
        u_char  *oneshot_buffer; /* buffer for copy of packet */
+       long    proc_dropped; /* packets reported dropped by /proc/net/dev */
 #endif /* linux */
 
 #ifdef HAVE_DAG_API
diff --git a/pcap-linux.c b/pcap-linux.c
index 25208f6..c529d56 100644
--- a/pcap-linux.c
+++ b/pcap-linux.c
@@ -891,6 +891,60 @@ pcap_can_set_rfmon_linux(pcap_t *handle)
        return 0;
 }
 
+/* grabs the number of dropped packets by the interface from /proc/net/dev */
+static
+long int linux_if_drops(const char * if_name)
+{
+       char buffer[512];
+       char * bufptr;
+       FILE * file;
+       int field_to_convert = 3, if_name_sz = strlen(if_name);
+       long int dropped_pkts = 0;
+       
+       file = fopen("/proc/net/dev", "r");
+       if (!file)
+               return 0;
+
+       while (!dropped_pkts && fgets( buffer, sizeof(buffer), file ))
+       {
+               /*      search for 'bytes' -- if its in there, then
+                       that means we need to grab the fourth field. otherwise
+                       grab the third field. */
+               if (field_to_convert != 4 && strstr(buffer, "bytes"))
+               {
+                       field_to_convert = 4;
+                       continue;
+               }
+       
+               /* find iface and make sure it actually matches -- space before the name and : after it */
+               if ((bufptr = strstr(buffer, if_name)) &&
+                       (bufptr == buffer || *(bufptr-1) == ' ') &&
+                       *(bufptr + if_name_sz) == ':')
+               {
+                       bufptr = bufptr + if_name_sz + 1;
+
+                       /* grab the nth field from it */
+                       while( --field_to_convert && *bufptr != '\0')
+                       {
+                               while (*bufptr != '\0' && *(bufptr++) == ' ');
+                               while (*bufptr != '\0' && *(bufptr++) != ' ');
+                       }
+                       
+                       /* get rid of any final spaces */
+                       while (*bufptr != '\0' && *bufptr == ' ') bufptr++;
+                       
+                       if (*bufptr != '\0')
+                               dropped_pkts = strtol(bufptr, NULL, 10);
+
+                       break;
+               }
+       }
+       
+       fclose(file);
+       return dropped_pkts;
+} 
+
+
 /*
  * With older kernels promiscuous mode is kind of interesting because we
  * have to reset the interface before exiting. The problem can't really
@@ -1066,6 +1120,14 @@ pcap_activate_linux(pcap_t *handle)
                         pcap_strerror(errno) );
                return PCAP_ERROR;
        }
+       
+       /*
+        * If we're in promiscuous mode, then we probably want 
+        * to see when the interface drops packets too, so get an
+        * initial count from /proc/net/dev
+        */
+       if (handle->opt.promisc)
+               handle->md.proc_dropped = linux_if_drops(handle->md.device);
 
        /*
         * Current Linux kernels use the protocol family PF_PACKET to
@@ -1564,6 +1626,18 @@ pcap_stats_linux(pcap_t *handle, struct pcap_stat *stats)
        socklen_t len = sizeof (struct tpacket_stats);
 #endif
 
+       long if_dropped = 0;
+       
+       /* 
+        *      To fill in ps_ifdrop, we parse /proc/net/dev for the number
+        */
+       if (handle->opt.promisc)
+       {
+               if_dropped = handle->md.proc_dropped;
+               handle->md.proc_dropped = linux_if_drops(handle->md.device);
+               handle->md.stat.ps_ifdrop += (handle->md.proc_dropped - if_dropped);
+       }
+
 #ifdef HAVE_TPACKET_STATS
        /*
         * Try to get the packet counts from the kernel.
@@ -1584,6 +1658,8 @@ pcap_stats_linux(pcap_t *handle, struct pcap_stat *stats)
                 *      dropped by the interface driver.  It counts only
                 *      packets that passed the filter.
                 *
+                *      See above for ps_ifdrop. 
+                *
                 *      Both statistics include packets not yet read from
                 *      the kernel by libpcap, and thus not yet seen by
                 *      the application.
@@ -1647,16 +1723,22 @@ pcap_stats_linux(pcap_t *handle, struct pcap_stat *stats)
         *
         *      "ps_drop" is not supported.
         *
+        *      "ps_ifdrop" is supported. It will return the number
+        *      of drops the interface reports in /proc/net/dev
+        *
         *      "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.
+        * packets were dropped by the kernel buffers -- but we know 
+        * how many the interface dropped, so we can return that.
         */
+        
        stats->ps_recv = handle->md.packets_read;
        stats->ps_drop = 0;
+       stats->ps_ifdrop = handle->md.stat.ps_drop;
        return 0;
 }
 
diff --git a/pcap/pcap.h b/pcap/pcap.h
index 94fb722..5886c49 100644
--- a/pcap/pcap.h
+++ b/pcap/pcap.h
@@ -163,7 +163,7 @@ struct pcap_pkthdr {
 struct pcap_stat {
        u_int ps_recv;          /* number of packets received */
        u_int ps_drop;          /* number of packets dropped */
-       u_int ps_ifdrop;        /* drops by interface XXX not yet supported */
+       u_int ps_ifdrop;        /* drops by interface -- only supported on some platforms */
 #ifdef WIN32
        u_int bs_capt;          /* number of packets that reach the application */
 #endif /* WIN32 */

Attachment: signature.asc
Description: OpenPGP digital signature


Current thread: