tcpdump mailing list archives

code available: netmap support for libpcap


From: Luigi Rizzo <rizzo () iet unipi it>
Date: Sat, 15 Feb 2014 21:17:27 +0100


I have created a repo with a cloned copy of the pcap repository
with netmap support:

        https://code.google.com/p/netmap-libpcap

With that, you can easily have tcpdump (and any libpcap application)
process 10-15Mpps with a single thread through NICs in netmap mode
(see http://info.iet.unipi.it/~luigi/netmap/ ) ports of the VALE
software switch (part of netmap), or "netmap pipes".

To try it, fetch the netmap code (for Linux and FreeBSD) is at

        https://code.google.com/p/netmap

You don't need a 10G interface, you can simply test across VALE
ports or netmap pipes using the pkt-gen traffic generator in the
netmap distribution, and tcpdump using the new libpcap:

        # send on the master side of a netmap pipe called vale:p0
        # pkt-gen produces udp traffic
        pkt-gen -i vale:p}0 -f tx
        # receive from the slave side of the pipe with tcpdump
        # tcpdump -nlei vale:p{0 tcp

and enjoy the counters on the sender side (the pipe is blocking,
so the numbers on the sender side reflect the speed of the receiver).
        
I would like to understand if there is interest for this stuff,
obviously I'd rather see this integrated in the main distribution
than have to point people to our repo.

Diff attached for the curious.

        cheers
        luigi

diff --git a/Makefile.in b/Makefile.in
index 47f5a06..5e90533 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -83,7 +83,7 @@ YACC = @V_YACC@
        @rm -f $@
        $(CC) $(FULL_CFLAGS) -c $(srcdir)/$*.c
 
-PSRC = pcap-@V_PCAP@.c @USB_SRC@ @BT_SRC@ @CAN_SRC@ @NETFILTER_SRC@ @CANUSB_SRC@ @DBUS_SRC@
+PSRC = pcap-@V_PCAP@.c @USB_SRC@ @BT_SRC@ @CAN_SRC@ @NETFILTER_SRC@ @CANUSB_SRC@ @DBUS_SRC@ @NETMAP_SRC@
 FSRC =  fad-@V_FINDALLDEVS@.c
 SSRC =  @SSRC@
 CSRC = pcap.c inet.c gencode.c optimize.c nametoaddr.c etherent.c \
@@ -314,6 +314,7 @@ EXTRA_DIST = \
        pcap-namedb.h \
        pcap-netfilter-linux.c \
        pcap-netfilter-linux.h \
+       pcap-netmap.c \
        pcap-nit.c \
        pcap-null.c \
        pcap-pf.c \
diff --git a/config.h.in b/config.h.in
index c6bc68e..09c8557 100644
--- a/config.h.in
+++ b/config.h.in
@@ -268,6 +268,9 @@
 /* target host supports netfilter sniffing */
 #undef PCAP_SUPPORT_NETFILTER
 
+/* target host supports netmap */
+#undef PCAP_SUPPORT_NETMAP
+
 /* target host supports USB sniffing */
 #undef PCAP_SUPPORT_USB
 
diff --git a/configure b/configure
index 07c2d33..a602df5 100755
--- a/configure
+++ b/configure
@@ -632,6 +632,8 @@ CANUSB_SRC
 PCAP_SUPPORT_CANUSB
 BT_SRC
 PCAP_SUPPORT_BT
+NETMAP_SRC
+PCAP_SUPPORT_NETMAP
 NETFILTER_SRC
 PCAP_SUPPORT_NETFILTER
 USB_SRC
@@ -744,6 +746,7 @@ with_flex
 with_bison
 enable_universal
 enable_shared
+enable_netmap
 enable_bluetooth
 enable_canusb
 enable_can
@@ -1379,6 +1382,8 @@ Optional Features:
   --disable-universal     don't build universal on OS X
   --enable-shared         build shared libraries [default=yes, if support
                           available]
+  --enable-netmap         enable netmap support [default=yes, if support
+                          available]
   --enable-bluetooth      enable Bluetooth support [default=yes, if support
                           available]
   --enable-canusb         enable canusb support [default=yes, if support
@@ -7995,6 +8000,35 @@ esac
 
 
 
+# Check whether --enable-netmap was given.
+if test "${enable_netmap+set}" = set; then :
+  enableval=$enable_netmap;
+else
+  enable_netmap=yes
+fi
+
+
+if test "x$enable_netmap" != "xno" ; then
+               ac_fn_c_check_header_compile "$LINENO" "net/netmap_user.h" "ac_cv_header_net_netmap_user_h" 
"$ac_includes_default
+
+"
+if test "x$ac_cv_header_net_netmap_user_h" = xyes; then :
+
+$as_echo "#define PCAP_SUPPORT_NETMAP 1" >>confdefs.h
+
+               NETMAP_SRC=pcap-netmap.c
+               { $as_echo "$as_me:${as_lineno-$LINENO}: netmap is supported" >&5
+$as_echo "$as_me: netmap is supported" >&6;}
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: netmap is not supported" >&5
+$as_echo "$as_me: netmap is not supported" >&6;}
+fi
+
+
+
+
+fi
+
 # Check whether --enable-bluetooth was given.
 if test "${enable_bluetooth+set}" = set; then :
   enableval=$enable_bluetooth;
diff --git a/configure.in b/configure.in
index 42cbe9b..86b7a74 100644
--- a/configure.in
+++ b/configure.in
@@ -1452,6 +1452,24 @@ esac
 AC_SUBST(PCAP_SUPPORT_NETFILTER)
 AC_SUBST(NETFILTER_SRC)
 
+AC_ARG_ENABLE([netmap],
+[AC_HELP_STRING([--enable-netmap],[enable netmap support @<:@default=yes, if support available@:>@])],
+    [],
+    [enable_netmap=yes])
+
+if test "x$enable_netmap" != "xno" ; then
+       dnl check for netmap support
+       AC_CHECK_HEADER(net/netmap_user.h,
+               [ AC_DEFINE(PCAP_SUPPORT_NETMAP, 1, [target host supports netmap])
+               NETMAP_SRC=pcap-netmap.c
+               AC_MSG_NOTICE(netmap is supported)],
+               AC_MSG_NOTICE(netmap is not supported),
+               AC_INCLUDES_DEFAULT
+          )
+       AC_SUBST(PCAP_SUPPORT_NETMAP)
+       AC_SUBST(NETMAP_SRC)
+fi
+
 AC_ARG_ENABLE([bluetooth],
 [AC_HELP_STRING([--enable-bluetooth],[enable Bluetooth support @<:@default=yes, if support available@:>@])],
     [],
diff --git a/inet.c b/inet.c
index c699658..d132507 100644
--- a/inet.c
+++ b/inet.c
@@ -883,6 +883,10 @@ pcap_lookupnet(device, netp, maskp, errbuf)
 #ifdef PCAP_SUPPORT_USB
            || strstr(device, "usbmon") != NULL
 #endif
+#ifdef PCAP_SUPPORT_NETMAP
+           || !strncmp(device, "netmap:", 7)
+           || !strncmp(device, "vale", 4)
+#endif
 #ifdef HAVE_SNF_API
            || strstr(device, "snf") != NULL
 #endif
diff --git a/pcap-netmap.c b/pcap-netmap.c
new file mode 100644
index 0000000..df2d01c
--- /dev/null
+++ b/pcap-netmap.c
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2014 Luigi Rizzo. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``S IS''AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <poll.h>
+#include <ctype.h>
+#include <errno.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define NETMAP_WITH_LIBS
+#include <net/netmap_user.h>
+
+#include "pcap-int.h"
+
+/*
+ * This code is meant to build also on older versions of libpcap.
+ *
+ * older libpcap miss p->priv, use p->md.device instead (and allocate).
+ * Also opt.timeout was in md.timeout before.
+ * Use #define PCAP_IF_UP to discriminate
+ */
+#ifdef PCAP_IF_UP
+#define NM_PRIV(p)     ((struct pcap_netmap *)(p->priv))
+#define the_timeout    opt.timeout
+#else
+#define HAVE_NO_PRIV
+#define        NM_PRIV(p)      ((struct pcap_netmap *)(p->md.device))
+#define SET_PRIV(p, x) p->md.device = (void *)x
+#define the_timeout    md.timeout
+#endif
+
+#if defined (linux)
+/* On FreeBSD we use IFF_PPROMISC which is in ifr_flagshigh.
+ * remap to IFF_PROMISC on linux
+ */
+#define IFF_PPROMISC   IFF_PROMISC
+#define ifr_flagshigh  ifr_flags
+#endif /* linux */
+
+struct pcap_netmap {
+       struct nm_desc *d;      /* pointer returned by nm_open() */
+       pcap_handler cb;        /* callback and argument */
+       u_char *cb_arg;
+       int must_clear_promisc; /* flag */
+       uint64_t rx_pkts;       /* # of pkts received before the filter */
+};
+
+static int
+pcap_netmap_stats(pcap_t *p, struct pcap_stat *ps)
+{
+       struct pcap_netmap *pn = NM_PRIV(p);
+
+       ps->ps_recv = pn->rx_pkts;
+       ps->ps_drop = 0;
+       ps->ps_ifdrop = 0;
+       return 0;
+}
+
+static void
+pcap_netmap_filter(u_char *arg, struct pcap_pkthdr *h, const u_char *buf)
+{
+       pcap_t *p = (pcap_t *)arg;
+       struct pcap_netmap *pn = NM_PRIV(p);
+
+       ++pn->rx_pkts;
+       if (bpf_filter(p->fcode.bf_insns, buf, h->len, h->caplen))
+               pn->cb(pn->cb_arg, h, buf);
+}
+
+static int
+pcap_netmap_dispatch(pcap_t *p, int cnt, pcap_handler cb, u_char *user)
+{
+       int ret;
+       struct pcap_netmap *pn = NM_PRIV(p);
+       struct nm_desc *d = pn->d;
+       struct pollfd pfd = { .fd = p->fd, .events = POLLIN, .revents = 0 };
+
+       pn->cb = cb;
+       pn->cb_arg = user;
+
+       for (;;) {
+               if (p->break_loop) {
+                       p->break_loop = 0;
+                       return PCAP_ERROR_BREAK;
+               }
+               /* nm_dispatch won't run forever */
+               ret = nm_dispatch((void *)d, cnt, (void *)pcap_netmap_filter, (void *)p);
+               if (ret != 0)
+                       break;
+               poll(&pfd, 1, p->the_timeout);
+       }
+       return ret;
+}
+
+/* XXX need to check the NIOCTXSYNC/poll */
+static int
+pcap_netmap_inject(pcap_t *p, const void *buf, size_t size)
+{
+       struct nm_desc *d = NM_PRIV(p)->d;
+
+       return nm_inject(d, buf, size);
+}
+
+static int
+pcap_netmap_ioctl(pcap_t *p, u_long what, uint32_t *if_flags)
+{
+       struct pcap_netmap *pn = NM_PRIV(p);
+       struct nm_desc *d = pn->d;
+       struct ifreq ifr;
+       int error, fd = d->fd;
+
+#ifdef linux
+       fd = socket(AF_INET, SOCK_DGRAM, 0);
+       if (fd < 0) {
+               fprintf(stderr, "Error: cannot get device control socket.\n");
+               return -1;
+       }
+#endif /* linux */
+       bzero(&ifr, sizeof(ifr));
+       strncpy(ifr.ifr_name, d->req.nr_name, sizeof(ifr.ifr_name));
+       switch (what) {
+       case SIOCSIFFLAGS:
+               ifr.ifr_flags = *if_flags;
+               ifr.ifr_flagshigh = *if_flags >> 16;
+               break;
+       }
+       error = ioctl(fd, what, &ifr);
+       fprintf(stderr, "%s %s ioctl 0x%lx returns %d\n", __FUNCTION__,
+               d->req.nr_name, what, error);
+       if (error)
+               return -1;
+       switch (what) {
+       case SIOCGIFFLAGS:
+               *if_flags = ifr.ifr_flags | (ifr.ifr_flagshigh << 16);
+       }
+       return 0;
+}
+
+static void
+pcap_netmap_close(pcap_t *p)
+{
+       struct pcap_netmap *pn = NM_PRIV(p);
+       struct nm_desc *d = pn->d;
+       uint32_t if_flags = 0;
+
+       if (pn->must_clear_promisc) {
+               pcap_netmap_ioctl(p, SIOCGIFFLAGS, &if_flags); /* fetch flags */
+               if (if_flags & IFF_PPROMISC) {
+                       if_flags &= ~IFF_PPROMISC;
+                       pcap_netmap_ioctl(p, SIOCSIFFLAGS, &if_flags);
+               }
+       }
+       nm_close(d);
+#ifdef HAVE_NO_PRIV
+       free(pn);
+       SET_PRIV(p, NULL); // unnecessary
+#endif
+       pcap_cleanup_live_common(p);
+}
+
+static int
+pcap_netmap_activate(pcap_t *p)
+{
+       struct pcap_netmap *pn = NM_PRIV(p);
+       struct nm_desc *d = nm_open(p->opt.source, NULL, 0, NULL);
+       uint32_t if_flags = 0;
+
+       if (d == NULL) {
+               snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+                       "netmap open: cannot access %s: %s\n",
+                       p->opt.source, pcap_strerror(errno));
+#ifdef HAVE_NO_PRIV
+               free(pn);
+               SET_PRIV(p, NULL); // unnecessary
+#endif
+               pcap_cleanup_live_common(p);
+               return (PCAP_ERROR);
+       }
+       fprintf(stderr, "%s device %s priv %p fd %d ports %d..%d\n",
+               __FUNCTION__, p->opt.source, d, d->fd,
+               d->first_rx_ring, d->last_rx_ring);
+       pn->d = d;
+       p->fd = d->fd;
+       if (p->opt.promisc && !(d->req.nr_ringid & NETMAP_SW_RING)) {
+               pcap_netmap_ioctl(p, SIOCGIFFLAGS, &if_flags); /* fetch flags */
+               if (!(if_flags & IFF_PPROMISC)) {
+                       pn->must_clear_promisc = 1;
+                       if_flags |= IFF_PPROMISC;
+                       pcap_netmap_ioctl(p, SIOCSIFFLAGS, &if_flags);
+               }
+       }
+       p->linktype = DLT_EN10MB;
+       p->selectable_fd = p->fd;
+       p->read_op = pcap_netmap_dispatch;
+       p->inject_op = pcap_netmap_inject,
+       p->setfilter_op = install_bpf_program;
+       p->setdirection_op = NULL;
+       p->set_datalink_op = NULL;
+       p->getnonblock_op = pcap_getnonblock_fd;
+       p->setnonblock_op = pcap_setnonblock_fd;
+       p->stats_op = pcap_netmap_stats;
+       p->cleanup_op = pcap_netmap_close;
+       return (0);
+}
+
+pcap_t *
+pcap_netmap_create(const char *device, char *ebuf, int *is_ours)
+{
+       pcap_t *p;
+
+       *is_ours = (!strncmp(device, "netmap:", 7) || !strncmp(device, "vale", 4));
+       if (! *is_ours)
+               return NULL;
+#ifdef HAVE_NO_PRIV
+       {
+               void *pn = calloc(1, sizeof(struct pcap_netmap));
+               if (pn == NULL)
+                       return NULL;
+               p = pcap_create_common(device, ebuf);
+               if (p == NULL) {
+                       free(pn);
+                       return NULL;
+               }
+               SET_PRIV(p, pn);
+       }
+#else
+       p = pcap_create_common(device, ebuf, sizeof (struct pcap_netmap));
+       if (p == NULL)
+               return (NULL);
+#endif
+       p->activate_op = pcap_netmap_activate;
+       return (p);
+}
diff --git a/pcap.c b/pcap.c
index b2b5da6..beda714 100644
--- a/pcap.c
+++ b/pcap.c
@@ -104,6 +104,10 @@
 #include "pcap-dbus.h"
 #endif
 
+#ifdef PCAP_SUPPORT_NETMAP
+pcap_t* pcap_netmap_create(const char *device, char *ebuf, int *is_ours);
+#endif
+
 int
 pcap_not_initialized(pcap_t *pcap _U_)
 {
@@ -307,6 +311,9 @@ struct capture_source_type {
        int (*findalldevs_op)(pcap_if_t **, char *);
        pcap_t *(*create_op)(const char *, char *, int *);
 } capture_source_types[] = {
+#ifdef PCAP_SUPPORT_NETMAP
+       { NULL, pcap_netmap_create },
+#endif
 #ifdef HAVE_DAG_API
        { dag_findalldevs, dag_create },
 #endif
_______________________________________________
tcpdump-workers mailing list
tcpdump-workers () lists tcpdump org
https://lists.sandelman.ca/mailman/listinfo/tcpdump-workers


Current thread: