]> The Tcpdump Group git mirrors - libpcap/commitdiff
From Dustin Spicuzza: support ps_ifdrop on Linux, using /proc/net/dev.
authorGuy Harris <[email protected]>
Mon, 7 Sep 2009 23:23:15 +0000 (16:23 -0700)
committerGuy Harris <[email protected]>
Mon, 7 Sep 2009 23:23:15 +0000 (16:23 -0700)
Fix the title of the pcap_stats man page, and give more detail - and a
lot of caveats.

pcap-int.h
pcap-linux.c
pcap/pcap.h
pcap_stats.3pcap

index b127a00544c7e5cd58cb60fdfc8b74e5f8463156..e46a98885df354d83acd9f3e79ee26b6d8beec12 100644 (file)
@@ -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
index eebc3e0cba4f296729b0d08460bd4fcdbad97d54..d2f7c51e70c877efc52539d6e8ffe0425e8c6d7f 100644 (file)
@@ -890,6 +890,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
@@ -1065,6 +1119,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
@@ -1563,6 +1625,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.
@@ -1583,6 +1657,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.
@@ -1646,16 +1722,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;
 }
 
index 29d5a4e331f93fef107b7cc10503b82881564b6f..f2451adfb6d46fa527d8d33783f4d468e4ce8f3d 100644 (file)
@@ -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 */
index abc56f84dbefa4754642a653a0e2005bfdd110b2..a953e211d54c1f93469f4ccc87e0e82432ac4edd 100644 (file)
@@ -19,7 +19,7 @@
 .\" WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 .\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 .\"
-.TH PCAP_SNAPSHOT 3PCAP "5 April 2008"
+.TH PCAP_STATS 3PCAP "7 September 2009"
 .SH NAME
 pcap_stats \- get capture statistics
 .SH SYNOPSIS
@@ -35,17 +35,57 @@ int pcap_stats(pcap_t *p, struct pcap_stat *ps);
 .SH DESCRIPTION
 .B pcap_stats()
 fills in the
-.I pcap_stat
-structure pointed to by its second argument.  The values represent
+.B struct pcap_stat
+pointed to by its second argument.  The values represent
 packet statistics from the start of the run to the time of the call.
 .PP
 .B pcap_stats()
 is supported only on live captures, not on ``savefiles''; no statistics
 are stored in ``savefiles'', so no statistics are available when reading
 from a ``savefile''.
+.PP
+A
+.B struct pcap_stat
+has the following members:
+.RS
+.TP
+.B ps_recv
+number of packets received;
+.TP
+.B ps_drop
+number of packets dropped because there was no room in the operating
+system's buffer when they arrived, because packets weren't being read
+fast enough;
+.TP
+.B ps_ifdrop
+number of packets dropped by the network interface or its driver.
+.RE
+.PP
+The statistics do not behave the same way on all platforms.
+.B ps_recv
+might count packets whether they passed any filter set with
+.BR pcap_setfilter (3PCAP)
+or not, or it might count only packets that pass the filter.
+It also might, or might not, count packets dropped because there was no
+room in the operating system's buffer when they arrived.
+.B ps_drop
+is not available on all platforms; it is zero on platforms where it's
+not available.  If packet filtering is done in libpcap, rather than in
+the operating system, it would count packets that don't pass the filter.
+Both
+.B ps_recv
+and
+.B ps_drop
+might, or might not, count packets not yet read from the operating
+system and thus not yet seen by the application.
+.B ps_ifdrop
+might, or might not, be implemented; if it's zero, that might mean that
+no packets were dropped by the interface, or it might mean that the
+statistic is unavailable, so it should not be treated as an indication
+that the interface did not drop any packets.
 .SH RETURN VALUE
 .B pcap_stats()
-returns 0 on success and returns \-1 if there is an error or the
+returns 0 on success and returns \-1 if there is an error or if
 .I p
 doesn't support packet statistics.
 If \-1 is returned,