tcpdump mailing list archives

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


From: Dustin Spicuzza <dustin () virtualroadside com>
Date: Fri, 28 Aug 2009 16:24:43 -0400

Dustin Spicuzza wrote:
Dustin Spicuzza wrote:
Hey,

I've been trying to determine the performance of my application (ie,
when does it start dropping packets). I am using the current version of
libpcap in git, with Ubuntu 9.04 server (kernel 2.6.28-15-server). I am
using the new pcap API calls to set the ring buffer to something like
2GB, and I'm reasonably sure that pcap compiles with mmap support enabled.

I noticed that when looking at the packet counts as reported by
ifconfig, it agrees with the counts reported by my generator box, and in
this particular instance it reports a handful of dropped packets.
However, the number of packets my application processed is the number
reported by ifconfig, but pcap_get_stats() does *not* report the number
of dropped packets correctly.


Traffic generator ifconfig:

  41843935 inbound, 0 drops
  27987584 outbound, 0 drops

Application box ifconfig:

  41843935 inbound, 1382 drops
  27987584 outbound, 0 drops

Application report via pcap_get_stats()

  41842553 inbound, 0 drops
  27987584 outbound, 0 drops

 Any thoughts on why this might be the case, and where I can find the bug?

Dustin

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.

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..1d80437 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,16 @@ pcap_stats_linux(pcap_t *handle, struct pcap_stat *stats)
        socklen_t len = sizeof (struct tpacket_stats);
 #endif
 
+       long if_dropped = 0;
+       
+       /* grab drops by the interface from /proc/net/dev */
+       if (handle->opt.promisc)
+       {
+               if_dropped = handle->md.proc_dropped;
+               handle->md.proc_dropped = linux_if_drops(handle->md.device);
+               handle->md.stat.ps_drop += (handle->md.proc_dropped - if_dropped);
+       }
+
 #ifdef HAVE_TPACKET_STATS
        /*
         * Try to get the packet counts from the kernel.
@@ -1645,7 +1717,8 @@ pcap_stats_linux(pcap_t *handle, struct pcap_stat *stats)
         *      count packets dropped because we ran out of buffer
         *      space.
         *
-        *      "ps_drop" is not supported.
+        *      "ps_drop" is partially 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.
@@ -1653,10 +1726,12 @@ pcap_stats_linux(pcap_t *handle, struct pcap_stat *stats)
         * 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 -- 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_drop = handle->md.stat.ps_drop;
        return 0;
 }
 

Attachment: signature.asc
Description: OpenPGP digital signature


Current thread: