tcpdump mailing list archives

Re: Exposing linux libpcap ring buffer usage information


From: Dustin Spicuzza <dustin () virtualroadside com>
Date: Wed, 15 Jul 2009 15:45:48 -0400

Guy Harris wrote:

On Jul 14, 2009, at 9:05 AM, Dustin Spicuzza wrote:

I'd like to get information on how much of the mmap'ed ring buffer is
being used so that I can deal with dropped packets more effectively. I
was going to add a patch for this, but its not clear to me the best way
to expose the information. I'm tempted to just add an additional
call/struct for the information, but it would be nice if the existing
API's could be extended instead.

What existing API were you thinking of extending?  Note that you can't
grow the pcap_stat structure without breaking binary compatibility.
-
This is the tcpdump-workers list.
Visit https://cod.sandelman.ca/ to unsubscribe.

I've attached a git patch to add a function, pcap_buffer_stats() to the
pcap API. I noticed some other platforms use the handle->opt.buffer_size
variable, but I don't have access to build any of those so I didn't try
an implementation of this API for them.

Dustin



-- 
Innovation is just a problem away
From 378010d47502051d6a3d46f41539c768d3cb8fd5 Mon Sep 17 00:00:00 2001
From: Dustin Spicuzza <dustin () virtualroadside com>
Date: Wed, 15 Jul 2009 15:17:12 -0400
Subject: [PATCH] Added pcap_buffer_stats API

- Added implementation to get buffer statistics for Linux mmap ring buffer
---
 pcap-int.h   |    2 ++
 pcap-linux.c |   55 +++++++++++++++++++++++++++++++++++++++++--------------
 pcap.c       |   12 ++++++++++++
 pcap/pcap.h  |   11 +++++++++++
 4 files changed, 66 insertions(+), 14 deletions(-)

diff --git a/pcap-int.h b/pcap-int.h
index bb52fb7..b7d633f 100644
--- a/pcap-int.h
+++ b/pcap-int.h
@@ -216,6 +216,7 @@ typedef int (*set_datalink_op_t)(pcap_t *, int);
 typedef int    (*getnonblock_op_t)(pcap_t *, char *);
 typedef int    (*setnonblock_op_t)(pcap_t *, int, char *);
 typedef int    (*stats_op_t)(pcap_t *, struct pcap_stat *);
+typedef int (*buf_stats_op_t)(pcap_t *, struct pcap_buffer_stat *);
 #ifdef WIN32
 typedef int    (*setbuff_op_t)(pcap_t *, int);
 typedef int    (*setmode_op_t)(pcap_t *, int);
@@ -288,6 +289,7 @@ struct pcap {
        getnonblock_op_t getnonblock_op;
        setnonblock_op_t setnonblock_op;
        stats_op_t stats_op;
+       buf_stats_op_t buf_stats_op;
 
 #ifdef WIN32
        /*
diff --git a/pcap-linux.c b/pcap-linux.c
index c91cd6e..809a79f 100644
--- a/pcap-linux.c
+++ b/pcap-linux.c
@@ -312,6 +312,7 @@ static int pcap_read_linux_mmap(pcap_t *, int, pcap_handler , u_char *);
 static int pcap_setfilter_linux_mmap(pcap_t *, struct bpf_program *);
 static int pcap_setnonblock_mmap(pcap_t *p, int nonblock, char *errbuf);
 static int pcap_getnonblock_mmap(pcap_t *p, char *errbuf);
+static int pcap_buffer_stats_linux_mmap(pcap_t *, struct pcap_buffer_stat *);
 #endif
 
 /*
@@ -2516,6 +2517,7 @@ activate_mmap(pcap_t *handle)
        handle->setnonblock_op = pcap_setnonblock_mmap;
        handle->getnonblock_op = pcap_getnonblock_mmap;
        handle->selectable_fd = handle->fd;
+       handle->buf_stats_op = pcap_buffer_stats_linux_mmap;
        return 1;
 #else /* HAVE_PACKET_RING */
        return 0;
@@ -2588,8 +2590,9 @@ create_ring(pcap_t *handle)
        req.tp_frame_size = TPACKET_ALIGN(handle->snapshot +
                                          TPACKET_ALIGN(handle->md.tp_hdrlen) +
                                          sizeof(struct sockaddr_ll));
+                                         
        req.tp_frame_nr = handle->opt.buffer_size/req.tp_frame_size;
-
+       
        /* compute the minumum block size that will handle this frame. 
         * The block has to be page size aligned. 
         * The max block size allowed by the kernel is arch-dependent and 
@@ -2983,19 +2986,11 @@ skip:
        return pkts;
 }
 
-static int 
-pcap_setfilter_linux_mmap(pcap_t *handle, struct bpf_program *filter)
+/* returns the number of packets waiting in the ring buffer */
+static int
+pcap_get_ring_count(pcap_t *handle)
 {
        int n, offset;
-       int ret = pcap_setfilter_linux(handle, filter);
-       if (ret < 0)
-               return ret;
-
-       /* if the kernel filter is enabled, we need to apply the filter on
-        * all packets present into the ring. Get an upper bound of their number
-        */
-       if (!handle->md.use_bpf)
-               return ret;
 
        /* walk the ring backward and count the free slot */
        offset = handle->offset;
@@ -3010,9 +3005,41 @@ pcap_setfilter_linux_mmap(pcap_t *handle, struct bpf_program *filter)
 
        /* be careful to not change current ring position */
        handle->offset = offset;
+       
+       return (handle->cc - n);
+}
+
+static int
+pcap_buffer_stats_linux_mmap(pcap_t * handle, struct pcap_buffer_stat *stats)
+{
+       stats->buffer_size              = handle->md.mmapbuflen;
+       stats->packets_queued   = pcap_get_ring_count(handle);
+       
+       /* used_bytes is reasonably close to correct, the padding is averaged out */
+       u_int padding                   = (stats->buffer_size - ( handle->bufsize * handle->cc )) / handle->cc;
+       stats->used_bytes               = stats->packets_queued * (handle->bufsize + padding);
+       
+       stats->max_packets              = handle->cc;
+       
+       return 0;
+}
+
+static int 
+pcap_setfilter_linux_mmap(pcap_t *handle, struct bpf_program *filter)
+{
+       
+       int ret = pcap_setfilter_linux(handle, filter);
+       if (ret < 0)
+               return ret;
+
+       /* if the kernel filter is enabled, we need to apply the filter on
+        * all packets present into the ring. Get an upper bound of their number
+        */
+       if (!handle->md.use_bpf)
+               return ret;
 
-       /* store the number of packets currently present in the ring */
-       handle->md.use_bpf = 1 + (handle->cc - n);
+       /* store the number of packets currently present in the ring +1 */
+       handle->md.use_bpf = pcap_get_ring_count(handle) + 1;
        return ret;
 }
 
diff --git a/pcap.c b/pcap.c
index 0adbe34..47a9b06 100644
--- a/pcap.c
+++ b/pcap.c
@@ -1011,6 +1011,18 @@ pcap_stats(pcap_t *p, struct pcap_stat *ps)
        return p->stats_op(p, ps);
 }
 
+int
+pcap_buffer_stats(pcap_t *p, struct pcap_buffer_stat *pbs)
+{
+       if (p->buf_stats_op == NULL) {
+               snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+                   "No internal buffer stats available on this platform");
+               return -1;
+       }
+       
+       return p->buf_stats_op(p, pbs);
+}
+
 static int
 pcap_stats_dead(pcap_t *p, struct pcap_stat *ps _U_)
 {
diff --git a/pcap/pcap.h b/pcap/pcap.h
index 29d5a4e..94fb722 100644
--- a/pcap/pcap.h
+++ b/pcap/pcap.h
@@ -203,6 +203,16 @@ struct pcap_stat_ex {
 #endif
 
 /*
+ * As returned by the pcap_buffer_stats()
+ */
+struct pcap_buffer_stat {
+       u_int buffer_size;              /* total size (in bytes) of internal buffer */
+       u_int used_bytes;               /* bytes currently occupied */
+       u_int packets_queued;   /* packets queued in buffer */
+       u_int max_packets;              /* max number of packets buffer can hold */
+};
+
+/*
  * Item in a list of interfaces.
  */
 struct pcap_if {
@@ -288,6 +298,7 @@ const u_char*
 int    pcap_next_ex(pcap_t *, struct pcap_pkthdr **, const u_char **);
 void   pcap_breakloop(pcap_t *);
 int    pcap_stats(pcap_t *, struct pcap_stat *);
+int pcap_buffer_stats(pcap_t *, struct pcap_buffer_stat *);
 int    pcap_setfilter(pcap_t *, struct bpf_program *);
 int    pcap_setdirection(pcap_t *, pcap_direction_t);
 int    pcap_getnonblock(pcap_t *, char *);
-- 
1.6.0.4

Attachment: signature.asc
Description: OpenPGP digital signature


Current thread: