From: Guy Harris Date: Wed, 31 May 2017 19:45:20 +0000 (-0700) Subject: Add netmap support. X-Git-Tag: libpcap-1.9-bp~813 X-Git-Url: https://git.tcpdump.org/libpcap/commitdiff_plain/f6685854a93e4ba9e0842001ccdc19410d4e424c Add netmap support. Based on https://github.com/luigirizzo/netmap-libpcap. --- diff --git a/Makefile.in b/Makefile.in index 320ed043..bbe470c4 100644 --- a/Makefile.in +++ b/Makefile.in @@ -83,7 +83,13 @@ YACC = @YACC@ @rm -f $@ $(CC) $(FULL_CFLAGS) -c $(srcdir)/$*.c -PSRC = pcap-@V_PCAP@.c @USB_SRC@ @BT_SRC@ @BT_MONITOR_SRC@ @NETFILTER_SRC@ @DBUS_SRC@ +PSRC = pcap-@V_PCAP@.c \ + @USB_SRC@ \ + @BT_SRC@ \ + @BT_MONITOR_SRC@ \ + @NETFILTER_SRC@ \ + @DBUS_SRC@ \ + @NETMAP_SRC@ FSRC = @V_FINDALLDEVS@ SSRC = @SSRC@ CSRC = pcap.c inet.c gencode.c optimize.c nametoaddr.c \ @@ -345,6 +351,8 @@ EXTRA_DIST = \ pcap-new.c \ pcap-netfilter-linux.c \ pcap-netfilter-linux.h \ + pcap-netmap.c \ + pcap-netmap.h \ pcap-nit.c \ pcap-null.c \ pcap-pf.c \ diff --git a/config.h.in b/config.h.in index d901c750..51bdf13b 100644 --- a/config.h.in +++ b/config.h.in @@ -277,6 +277,9 @@ /* target host supports netfilter sniffing */ #undef PCAP_SUPPORT_NETFILTER +/* target host supports netmap */ +#undef PCAP_SUPPORT_NETMAP + /* use Linux packet ring capture if available */ #undef PCAP_SUPPORT_PACKET_RING diff --git a/configure b/configure index a4904572..9b68b37b 100755 --- a/configure +++ b/configure @@ -630,6 +630,8 @@ PKGCONFIG BT_MONITOR_SRC BT_SRC PCAP_SUPPORT_BT +NETMAP_SRC +PCAP_SUPPORT_NETMAP NETFILTER_SRC PCAP_SUPPORT_NETFILTER USB_SRC @@ -752,6 +754,7 @@ enable_remote enable_universal enable_shared enable_usb +enable_netmap enable_bluetooth enable_dbus enable_packet_ring @@ -1392,6 +1395,8 @@ Optional Features: available] --enable-usb enable nusb support [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-dbus enable D-Bus capture support [default=yes, if @@ -8628,6 +8633,36 @@ fi +# 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.ac b/configure.ac index 4ae1f0bb..af0e857b 100644 --- a/configure.ac +++ b/configure.ac @@ -1640,6 +1640,25 @@ fi 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 76abcb8f..34da782f 100644 --- a/inet.c +++ b/inet.c @@ -150,6 +150,10 @@ pcap_lookupnet(device, netp, maskp, errbuf) #endif #ifdef HAVE_SNF_API || strstr(device, "snf") != NULL +#endif +#ifdef PCAP_SUPPORT_NETMAP + || strncmp(device, "netmap:", 7) == 0 + || strncmp(device, "vale", 4) == 0 #endif ) { *netp = *maskp = 0; diff --git a/pcap-netmap.c b/pcap-netmap.c new file mode 100644 index 00000000..6dde821b --- /dev/null +++ b/pcap-netmap.c @@ -0,0 +1,254 @@ +/* + * 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 ``AS 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 +#include +#include +#include +#include +#include +#include +#include + +#define NETMAP_WITH_LIBS +#include + +#include "pcap-int.h" +#include "pcap-netmap.h" + +#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 +#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 = p->priv; + + 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 = p->priv; + const struct bpf_insn *pc = p->fcode.bf_insns; + + ++pn->rx_pkts; + if (pc == NULL || bpf_filter(pc, 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 = p->priv; + 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; + errno = 0; + ret = poll(&pfd, 1, p->opt.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 pcap_netmap *pn = p->priv; + struct nm_desc *d = pn->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 = p->priv; + 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; +#ifdef __FreeBSD__ + ifr.ifr_flagshigh = *if_flags >> 16; +#endif /* __FreeBSD__ */ + break; + } + error = ioctl(fd, what, &ifr); + if (!error) { + switch (what) { + case SIOCGIFFLAGS: + *if_flags = ifr.ifr_flags; +#ifdef __FreeBSD__ + *if_flags |= (ifr.ifr_flagshigh << 16); +#endif /* __FreeBSD__ */ + } + } +#ifdef linux + close(fd); +#endif /* linux */ + return error ? -1 : 0; +} + + +static void +pcap_netmap_close(pcap_t *p) +{ + struct pcap_netmap *pn = p->priv; + 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); + pcap_cleanup_live_common(p); +} + + +static int +pcap_netmap_activate(pcap_t *p) +{ + struct pcap_netmap *pn = p->priv; + 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)); + pcap_cleanup_live_common(p); + return (PCAP_ERROR); + } + if (0) + 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; + p = pcap_create_common(ebuf, sizeof (struct pcap_netmap)); + if (p == NULL) + return (NULL); + p->activate_op = pcap_netmap_activate; + return (p); +} + +/* + * XXX - is there a way to enumerate the netmap devices? + */ +int +pcap_netmap_findalldevs(pcap_if_list_t *devlistp, char *err_str) +{ + return 0; +} + diff --git a/pcap-netmap.h b/pcap-netmap.h new file mode 100644 index 00000000..6a414fca --- /dev/null +++ b/pcap-netmap.h @@ -0,0 +1,2 @@ +pcap_t *pcap_netmap_create(const char *, char *, int *); +int pcap_netmap_findalldevs(pcap_if_list_t *devlistp, char *errbuf); diff --git a/pcap.c b/pcap.c index 488846d5..b12ce091 100644 --- a/pcap.c +++ b/pcap.c @@ -120,6 +120,10 @@ struct rtentry; /* declarations in */ #include "pcap-netfilter-linux.h" #endif +#ifdef PCAP_SUPPORT_NETMAP +#include "pcap-netmap.h" +#endif + #ifdef PCAP_SUPPORT_DBUS #include "pcap-dbus.h" #endif @@ -350,6 +354,9 @@ static struct capture_source_type { #ifdef PCAP_SUPPORT_NETFILTER { netfilter_findalldevs, netfilter_create }, #endif +#ifdef PCAP_SUPPORT_NETFILTER + { pcap_netmap_findalldevs, pcap_netmap_create }, +#endif #ifdef PCAP_SUPPORT_DBUS { dbus_findalldevs, dbus_create }, #endif