[Midnightbsd-cvs] src [8415] trunk/tools/tools/netmap: add netmap util

laffer1 at midnightbsd.org laffer1 at midnightbsd.org
Sun Sep 18 17:05:32 EDT 2016


Revision: 8415
          http://svnweb.midnightbsd.org/src/?rev=8415
Author:   laffer1
Date:     2016-09-18 17:05:32 -0400 (Sun, 18 Sep 2016)
Log Message:
-----------
add netmap util

Added Paths:
-----------
    trunk/tools/tools/netmap/
    trunk/tools/tools/netmap/Makefile
    trunk/tools/tools/netmap/README
    trunk/tools/tools/netmap/bridge.c
    trunk/tools/tools/netmap/click-test.cfg
    trunk/tools/tools/netmap/nm_util.c
    trunk/tools/tools/netmap/nm_util.h
    trunk/tools/tools/netmap/pcap.c
    trunk/tools/tools/netmap/pkt-gen.c

Added: trunk/tools/tools/netmap/Makefile
===================================================================
--- trunk/tools/tools/netmap/Makefile	                        (rev 0)
+++ trunk/tools/tools/netmap/Makefile	2016-09-18 21:05:32 UTC (rev 8415)
@@ -0,0 +1,28 @@
+#
+# $FreeBSD: release/9.2.0/tools/tools/netmap/Makefile 250458 2013-05-10 16:16:33Z luigi $
+#
+# For multiple programs using a single source file each,
+# we can just define 'progs' and create custom targets.
+PROGS	=	pkt-gen bridge testpcap libnetmap.so
+
+CLEANFILES = $(PROGS) pcap.o nm_util.o
+NO_MAN=
+CFLAGS += -Werror -Wall -nostdinc -I/usr/include -I../../../sys
+CFLAGS += -Wextra
+
+LDFLAGS += -lpthread -lpcap
+
+.include <bsd.prog.mk>
+.include <bsd.lib.mk>
+
+all: $(PROGS)
+
+pkt-gen bridge: nm_util.o
+	$(CC) $(CFLAGS) -o ${.TARGET} ${.TARGET:=.c} nm_util.o $(LDFLAGS)
+
+testpcap: pcap.c libnetmap.so
+	$(CC) $(CFLAGS) -DTEST -L. -lnetmap -o ${.TARGET} pcap.c
+	
+libnetmap.so:	pcap.c nm_util.c
+	$(CC) $(CFLAGS) -fpic -c ${.ALLSRC}
+	$(CC) -shared -o ${.TARGET} ${.ALLSRC:.c=.o}


Property changes on: trunk/tools/tools/netmap/Makefile
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/tools/tools/netmap/README
===================================================================
--- trunk/tools/tools/netmap/README	                        (rev 0)
+++ trunk/tools/tools/netmap/README	2016-09-18 21:05:32 UTC (rev 8415)
@@ -0,0 +1,24 @@
+$FreeBSD: release/9.2.0/tools/tools/netmap/README 250458 2013-05-10 16:16:33Z luigi $
+
+This directory contains examples that use netmap
+
+	pkt-gen		a packet sink/source using the netmap API
+
+	bridge		a two-port jumper wire, also using the native API
+
+	testpcap	a jumper wire using libnetmap (or libpcap)
+
+	click*		various click examples
+
+------------------------------------------------------------
+Some performance data as of may 2012 for applications using libpcap.
+Throughput is generally in Mpps computed with the 64-byte frames,
+using 1 core on a 2.9GHz CPU and 10Gbit/s interface
+
+Libpcap version	-- Application ---------------------
+BSD	netmap
+---------------------------------------------------
+ 0.77	 3.82	ports/trafshow (version 5)
+ 0.94	 7.7	net-mgmt/ipcad (ip accounting daemon)
+ 0.9	 5.0	net-mgmt/darkstat (ip accounting + graphing)
+ 0.83	 2.45	net-mgmt/iftop (curses traffic display)

Added: trunk/tools/tools/netmap/bridge.c
===================================================================
--- trunk/tools/tools/netmap/bridge.c	                        (rev 0)
+++ trunk/tools/tools/netmap/bridge.c	2016-09-18 21:05:32 UTC (rev 8415)
@@ -0,0 +1,305 @@
+/*
+ * (C) 2011 Luigi Rizzo, Matteo Landi
+ *
+ * BSD license
+ *
+ * A netmap client to bridge two network interfaces
+ * (or one interface and the host stack).
+ *
+ * $FreeBSD: release/9.2.0/tools/tools/netmap/bridge.c 250458 2013-05-10 16:16:33Z luigi $
+ */
+
+#include "nm_util.h"
+
+
+int verbose = 0;
+
+char *version = "$Id: bridge.c 12016 2013-01-23 17:24:22Z luigi $";
+
+static int do_abort = 0;
+
+static void
+sigint_h(int sig)
+{
+	(void)sig;	/* UNUSED */
+	do_abort = 1;
+	signal(SIGINT, SIG_DFL);
+}
+
+
+/*
+ * move up to 'limit' pkts from rxring to txring swapping buffers.
+ */
+static int
+process_rings(struct netmap_ring *rxring, struct netmap_ring *txring,
+	      u_int limit, const char *msg)
+{
+	u_int j, k, m = 0;
+
+	/* print a warning if any of the ring flags is set (e.g. NM_REINIT) */
+	if (rxring->flags || txring->flags)
+		D("%s rxflags %x txflags %x",
+			msg, rxring->flags, txring->flags);
+	j = rxring->cur; /* RX */
+	k = txring->cur; /* TX */
+	if (rxring->avail < limit)
+		limit = rxring->avail;
+	if (txring->avail < limit)
+		limit = txring->avail;
+	m = limit;
+	while (limit-- > 0) {
+		struct netmap_slot *rs = &rxring->slot[j];
+		struct netmap_slot *ts = &txring->slot[k];
+		uint32_t pkt;
+
+		/* swap packets */
+		if (ts->buf_idx < 2 || rs->buf_idx < 2) {
+			D("wrong index rx[%d] = %d  -> tx[%d] = %d",
+				j, rs->buf_idx, k, ts->buf_idx);
+			sleep(2);
+		}
+		pkt = ts->buf_idx;
+		ts->buf_idx = rs->buf_idx;
+		rs->buf_idx = pkt;
+
+		/* copy the packet length. */
+		if (rs->len < 14 || rs->len > 2048)
+			D("wrong len %d rx[%d] -> tx[%d]", rs->len, j, k);
+		else if (verbose > 1)
+			D("%s send len %d rx[%d] -> tx[%d]", msg, rs->len, j, k);
+		ts->len = rs->len;
+
+		/* report the buffer change. */
+		ts->flags |= NS_BUF_CHANGED;
+		rs->flags |= NS_BUF_CHANGED;
+		j = NETMAP_RING_NEXT(rxring, j);
+		k = NETMAP_RING_NEXT(txring, k);
+	}
+	rxring->avail -= m;
+	txring->avail -= m;
+	rxring->cur = j;
+	txring->cur = k;
+	if (verbose && m > 0)
+		D("%s sent %d packets to %p", msg, m, txring);
+
+	return (m);
+}
+
+/* move packts from src to destination */
+static int
+move(struct my_ring *src, struct my_ring *dst, u_int limit)
+{
+	struct netmap_ring *txring, *rxring;
+	u_int m = 0, si = src->begin, di = dst->begin;
+	const char *msg = (src->queueid & NETMAP_SW_RING) ?
+		"host->net" : "net->host";
+
+	while (si < src->end && di < dst->end) {
+		rxring = NETMAP_RXRING(src->nifp, si);
+		txring = NETMAP_TXRING(dst->nifp, di);
+		ND("txring %p rxring %p", txring, rxring);
+		if (rxring->avail == 0) {
+			si++;
+			continue;
+		}
+		if (txring->avail == 0) {
+			di++;
+			continue;
+		}
+		m += process_rings(rxring, txring, limit, msg);
+	}
+
+	return (m);
+}
+
+/*
+ * how many packets on this set of queues ?
+ */
+static int
+pkt_queued(struct my_ring *me, int tx)
+{
+	u_int i, tot = 0;
+
+	ND("me %p begin %d end %d", me, me->begin, me->end);
+	for (i = me->begin; i < me->end; i++) {
+		struct netmap_ring *ring = tx ?
+			NETMAP_TXRING(me->nifp, i) : NETMAP_RXRING(me->nifp, i);
+		tot += ring->avail;
+	}
+	if (0 && verbose && tot && !tx)
+		D("ring %s %s %s has %d avail at %d",
+			me->ifname, tx ? "tx": "rx",
+			me->end >= me->nifp->ni_tx_rings ? // XXX who comes first ?
+				"host":"net",
+			tot, NETMAP_TXRING(me->nifp, me->begin)->cur);
+	return tot;
+}
+
+static void
+usage(void)
+{
+	fprintf(stderr,
+	    "usage: bridge [-v] [-i ifa] [-i ifb] [-b burst] [-w wait_time] [iface]\n");
+	exit(1);
+}
+
+/*
+ * bridge [-v] if1 [if2]
+ *
+ * If only one name, or the two interfaces are the same,
+ * bridges userland and the adapter. Otherwise bridge
+ * two intefaces.
+ */
+int
+main(int argc, char **argv)
+{
+	struct pollfd pollfd[2];
+	int i, ch;
+	u_int burst = 1024, wait_link = 4;
+	struct my_ring me[2];
+	char *ifa = NULL, *ifb = NULL;
+
+	fprintf(stderr, "%s %s built %s %s\n",
+		argv[0], version, __DATE__, __TIME__);
+
+	bzero(me, sizeof(me));
+
+	while ( (ch = getopt(argc, argv, "b:i:vw:")) != -1) {
+		switch (ch) {
+		default:
+			D("bad option %c %s", ch, optarg);
+			usage();
+			break;
+		case 'b':	/* burst */
+			burst = atoi(optarg);
+			break;
+		case 'i':	/* interface */
+			if (ifa == NULL)
+				ifa = optarg;
+			else if (ifb == NULL)
+				ifb = optarg;
+			else
+				D("%s ignored, already have 2 interfaces",
+					optarg);
+			break;
+		case 'v':
+			verbose++;
+			break;
+		case 'w':
+			wait_link = atoi(optarg);
+			break;
+		}
+
+	}
+
+	argc -= optind;
+	argv += optind;
+
+	if (argc > 1)
+		ifa = argv[1];
+	if (argc > 2)
+		ifb = argv[2];
+	if (argc > 3)
+		burst = atoi(argv[3]);
+	if (!ifb)
+		ifb = ifa;
+	if (!ifa) {
+		D("missing interface");
+		usage();
+	}
+	if (burst < 1 || burst > 8192) {
+		D("invalid burst %d, set to 1024", burst);
+		burst = 1024;
+	}
+	if (wait_link > 100) {
+		D("invalid wait_link %d, set to 4", wait_link);
+		wait_link = 4;
+	}
+	/* setup netmap interface #1. */
+	me[0].ifname = ifa;
+	me[1].ifname = ifb;
+	if (!strcmp(ifa, ifb)) {
+		D("same interface, endpoint 0 goes to host");
+		i = NETMAP_SW_RING;
+	} else {
+		/* two different interfaces. Take all rings on if1 */
+		i = 0;	// all hw rings
+	}
+	if (netmap_open(me, i, 1))
+		return (1);
+	me[1].mem = me[0].mem; /* copy the pointer, so only one mmap */
+	if (netmap_open(me+1, 0, 1))
+		return (1);
+
+	/* setup poll(2) variables. */
+	memset(pollfd, 0, sizeof(pollfd));
+	for (i = 0; i < 2; i++) {
+		pollfd[i].fd = me[i].fd;
+		pollfd[i].events = (POLLIN);
+	}
+
+	D("Wait %d secs for link to come up...", wait_link);
+	sleep(wait_link);
+	D("Ready to go, %s 0x%x/%d <-> %s 0x%x/%d.",
+		me[0].ifname, me[0].queueid, me[0].nifp->ni_rx_rings,
+		me[1].ifname, me[1].queueid, me[1].nifp->ni_rx_rings);
+
+	/* main loop */
+	signal(SIGINT, sigint_h);
+	while (!do_abort) {
+		int n0, n1, ret;
+		pollfd[0].events = pollfd[1].events = 0;
+		pollfd[0].revents = pollfd[1].revents = 0;
+		n0 = pkt_queued(me, 0);
+		n1 = pkt_queued(me + 1, 0);
+		if (n0)
+			pollfd[1].events |= POLLOUT;
+		else
+			pollfd[0].events |= POLLIN;
+		if (n1)
+			pollfd[0].events |= POLLOUT;
+		else
+			pollfd[1].events |= POLLIN;
+		ret = poll(pollfd, 2, 2500);
+		if (ret <= 0 || verbose)
+		    D("poll %s [0] ev %x %x rx %d@%d tx %d,"
+			     " [1] ev %x %x rx %d@%d tx %d",
+				ret <= 0 ? "timeout" : "ok",
+				pollfd[0].events,
+				pollfd[0].revents,
+				pkt_queued(me, 0),
+				me[0].rx->cur,
+				pkt_queued(me, 1),
+				pollfd[1].events,
+				pollfd[1].revents,
+				pkt_queued(me+1, 0),
+				me[1].rx->cur,
+				pkt_queued(me+1, 1)
+			);
+		if (ret < 0)
+			continue;
+		if (pollfd[0].revents & POLLERR) {
+			D("error on fd0, rxcur %d@%d",
+				me[0].rx->avail, me[0].rx->cur);
+		}
+		if (pollfd[1].revents & POLLERR) {
+			D("error on fd1, rxcur %d@%d",
+				me[1].rx->avail, me[1].rx->cur);
+		}
+		if (pollfd[0].revents & POLLOUT) {
+			move(me + 1, me, burst);
+			// XXX we don't need the ioctl */
+			// ioctl(me[0].fd, NIOCTXSYNC, NULL);
+		}
+		if (pollfd[1].revents & POLLOUT) {
+			move(me, me + 1, burst);
+			// XXX we don't need the ioctl */
+			// ioctl(me[1].fd, NIOCTXSYNC, NULL);
+		}
+	}
+	D("exiting");
+	netmap_close(me + 1);
+	netmap_close(me + 0);
+
+	return (0);
+}


Property changes on: trunk/tools/tools/netmap/bridge.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/tools/tools/netmap/click-test.cfg
===================================================================
--- trunk/tools/tools/netmap/click-test.cfg	                        (rev 0)
+++ trunk/tools/tools/netmap/click-test.cfg	2016-09-18 21:05:32 UTC (rev 8415)
@@ -0,0 +1,19 @@
+//
+// $FreeBSD: release/9.2.0/tools/tools/netmap/click-test.cfg 231650 2012-02-14 09:42:02Z luigi $
+//
+// A sample test configuration for click
+//
+//
+// create a switch
+
+myswitch :: EtherSwitch;
+
+// two input devices
+
+c0 :: FromDevice(ix0, PROMISC true);
+c1 :: FromDevice(ix1, PROMISC true);
+
+// and now pass packets around
+
+c0[0] -> [0]sw[0] -> Queue(10000) -> ToDevice(ix0);
+c1[0] -> [1]sw[1] -> Queue(10000) -> ToDevice(ix1);

Added: trunk/tools/tools/netmap/nm_util.c
===================================================================
--- trunk/tools/tools/netmap/nm_util.c	                        (rev 0)
+++ trunk/tools/tools/netmap/nm_util.c	2016-09-18 21:05:32 UTC (rev 8415)
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+/*
+ * $FreeBSD: release/9.2.0/tools/tools/netmap/nm_util.c 250458 2013-05-10 16:16:33Z luigi $
+ * $Id$
+ *
+ * utilities to use netmap devices.
+ * This does the basic functions of opening a device and issuing
+ * ioctls()
+ */
+
+#include "nm_util.h"
+
+extern int verbose;
+
+int
+nm_do_ioctl(struct my_ring *me, u_long what, int subcmd)
+{
+	struct ifreq ifr;
+	int error;
+#if defined( __FreeBSD__ ) || defined (__APPLE__)
+	int fd = me->fd;
+#endif
+#ifdef linux 
+	struct ethtool_value eval;
+	int fd;
+	fd = socket(AF_INET, SOCK_DGRAM, 0);
+	if (fd < 0) {
+		printf("Error: cannot get device control socket.\n");
+		return -1;
+	}
+#endif /* linux */
+
+	(void)subcmd;	// unused
+	bzero(&ifr, sizeof(ifr));
+	strncpy(ifr.ifr_name, me->ifname, sizeof(ifr.ifr_name));
+	switch (what) {
+	case SIOCSIFFLAGS:
+#ifndef __APPLE__
+		ifr.ifr_flagshigh = me->if_flags >> 16;
+#endif
+		ifr.ifr_flags = me->if_flags & 0xffff;
+		break;
+
+#if defined( __FreeBSD__ )
+	case SIOCSIFCAP:
+		ifr.ifr_reqcap = me->if_reqcap;
+		ifr.ifr_curcap = me->if_curcap;
+		break;
+#endif
+#ifdef linux
+	case SIOCETHTOOL:
+		eval.cmd = subcmd;
+		eval.data = 0;
+		ifr.ifr_data = (caddr_t)&eval;
+		break;
+#endif /* linux */
+	}
+	error = ioctl(fd, what, &ifr);
+	if (error)
+		goto done;
+	switch (what) {
+	case SIOCGIFFLAGS:
+#ifndef __APPLE__
+		me->if_flags = (ifr.ifr_flagshigh << 16) |
+			(0xffff & ifr.ifr_flags);
+#endif
+		if (verbose)
+			D("flags are 0x%x", me->if_flags);
+		break;
+
+#if defined( __FreeBSD__ )
+	case SIOCGIFCAP:
+		me->if_reqcap = ifr.ifr_reqcap;
+		me->if_curcap = ifr.ifr_curcap;
+		if (verbose)
+			D("curcap are 0x%x", me->if_curcap);
+		break;
+#endif /* __FreeBSD__ */
+	}
+done:
+#ifdef linux
+	close(fd);
+#endif
+	if (error)
+		D("ioctl error %d %lu", error, what);
+	return error;
+}
+
+/*
+ * open a device. if me->mem is null then do an mmap.
+ * Returns the file descriptor.
+ * The extra flag checks configures promisc mode.
+ */
+int
+netmap_open(struct my_ring *me, int ringid, int promisc)
+{
+	int fd, err, l;
+	struct nmreq req;
+
+	me->fd = fd = open("/dev/netmap", O_RDWR);
+	if (fd < 0) {
+		D("Unable to open /dev/netmap");
+		return (-1);
+	}
+	bzero(&req, sizeof(req));
+	req.nr_version = NETMAP_API;
+	strncpy(req.nr_name, me->ifname, sizeof(req.nr_name));
+	req.nr_ringid = ringid;
+	err = ioctl(fd, NIOCGINFO, &req);
+	if (err) {
+		D("cannot get info on %s, errno %d ver %d",
+			me->ifname, errno, req.nr_version);
+		goto error;
+	}
+	me->memsize = l = req.nr_memsize;
+	if (verbose)
+		D("memsize is %d MB", l>>20);
+	err = ioctl(fd, NIOCREGIF, &req);
+	if (err) {
+		D("Unable to register %s", me->ifname);
+		goto error;
+	}
+
+	if (me->mem == NULL) {
+		me->mem = mmap(0, l, PROT_WRITE | PROT_READ, MAP_SHARED, fd, 0);
+		if (me->mem == MAP_FAILED) {
+			D("Unable to mmap");
+			me->mem = NULL;
+			goto error;
+		}
+	}
+
+
+	/* Set the operating mode. */
+	if (ringid != NETMAP_SW_RING) {
+		nm_do_ioctl(me, SIOCGIFFLAGS, 0);
+		if ((me[0].if_flags & IFF_UP) == 0) {
+			D("%s is down, bringing up...", me[0].ifname);
+			me[0].if_flags |= IFF_UP;
+		}
+		if (promisc) {
+			me[0].if_flags |= IFF_PPROMISC;
+			nm_do_ioctl(me, SIOCSIFFLAGS, 0);
+		}
+
+#ifdef __FreeBSD__
+		/* also disable checksums etc. */
+		nm_do_ioctl(me, SIOCGIFCAP, 0);
+		me[0].if_reqcap = me[0].if_curcap;
+		me[0].if_reqcap &= ~(IFCAP_HWCSUM | IFCAP_TSO | IFCAP_TOE);
+		nm_do_ioctl(me+0, SIOCSIFCAP, 0);
+#endif
+#ifdef linux
+		/* disable:
+		 * - generic-segmentation-offload
+		 * - tcp-segmentation-offload
+		 * - rx-checksumming
+		 * - tx-checksumming
+		 * XXX check how to set back the caps.
+		 */
+		nm_do_ioctl(me, SIOCETHTOOL, ETHTOOL_SGSO);
+		nm_do_ioctl(me, SIOCETHTOOL, ETHTOOL_STSO);
+		nm_do_ioctl(me, SIOCETHTOOL, ETHTOOL_SRXCSUM);
+		nm_do_ioctl(me, SIOCETHTOOL, ETHTOOL_STXCSUM);
+#endif /* linux */
+	}
+
+	me->nifp = NETMAP_IF(me->mem, req.nr_offset);
+	me->queueid = ringid;
+	if (ringid & NETMAP_SW_RING) {
+		me->begin = req.nr_rx_rings;
+		me->end = me->begin + 1;
+		me->tx = NETMAP_TXRING(me->nifp, req.nr_tx_rings);
+		me->rx = NETMAP_RXRING(me->nifp, req.nr_rx_rings);
+	} else if (ringid & NETMAP_HW_RING) {
+		D("XXX check multiple threads");
+		me->begin = ringid & NETMAP_RING_MASK;
+		me->end = me->begin + 1;
+		me->tx = NETMAP_TXRING(me->nifp, me->begin);
+		me->rx = NETMAP_RXRING(me->nifp, me->begin);
+	} else {
+		me->begin = 0;
+		me->end = req.nr_rx_rings; // XXX max of the two
+		me->tx = NETMAP_TXRING(me->nifp, 0);
+		me->rx = NETMAP_RXRING(me->nifp, 0);
+	}
+	return (0);
+error:
+	close(me->fd);
+	return -1;
+}
+
+
+int
+netmap_close(struct my_ring *me)
+{
+	D("");
+	if (me->mem)
+		munmap(me->mem, me->memsize);
+	ioctl(me->fd, NIOCUNREGIF, NULL);
+	close(me->fd);
+	return (0);
+}
+
+
+/*
+ * how many packets on this set of queues ?
+ */
+int
+pkt_queued(struct my_ring *me, int tx)
+{
+	u_int i, tot = 0;
+
+	ND("me %p begin %d end %d", me, me->begin, me->end);
+	for (i = me->begin; i < me->end; i++) {
+		struct netmap_ring *ring = tx ?
+			NETMAP_TXRING(me->nifp, i) : NETMAP_RXRING(me->nifp, i);
+		tot += ring->avail;
+	}
+	if (0 && verbose && tot && !tx)
+		D("ring %s %s %s has %d avail at %d",
+			me->ifname, tx ? "tx": "rx",
+			me->end >= me->nifp->ni_tx_rings ? // XXX who comes first ?
+				"host":"net",
+			tot, NETMAP_TXRING(me->nifp, me->begin)->cur);
+	return tot;
+}


Property changes on: trunk/tools/tools/netmap/nm_util.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/tools/tools/netmap/nm_util.h
===================================================================
--- trunk/tools/tools/netmap/nm_util.h	                        (rev 0)
+++ trunk/tools/tools/netmap/nm_util.h	2016-09-18 21:05:32 UTC (rev 8415)
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+/*
+ * $FreeBSD: release/9.2.0/tools/tools/netmap/nm_util.h 250458 2013-05-10 16:16:33Z luigi $
+ * $Id$
+ *
+ * Some utilities to build netmap-based programs.
+ */
+
+#ifndef _NM_UTIL_H
+#define _NM_UTIL_H
+#include <errno.h>
+#include <signal.h>	/* signal */
+#include <stdlib.h>
+#include <stdio.h>
+#include <inttypes.h>	/* PRI* macros */
+#include <string.h>	/* strcmp */
+#include <fcntl.h>	/* open */
+#include <unistd.h>	/* close */
+#include <ifaddrs.h>	/* getifaddrs */
+
+#include <sys/mman.h>	/* PROT_* */
+#include <sys/ioctl.h>	/* ioctl */
+#include <sys/poll.h>
+#include <sys/socket.h>	/* sockaddr.. */
+#include <arpa/inet.h>	/* ntohs */
+#include <sys/param.h>
+#include <sys/sysctl.h>	/* sysctl */
+#include <sys/time.h>	/* timersub */
+
+#include <net/ethernet.h>
+#include <net/if.h>	/* ifreq */
+
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+
+#include <net/netmap.h>
+#include <net/netmap_user.h>
+
+#ifndef MY_PCAP		/* use the system's pcap if available */
+
+#ifdef NO_PCAP
+#define	PCAP_ERRBUF_SIZE	512
+typedef void pcap_t;
+struct pcap_pkthdr;
+#define	pcap_inject(a,b,c)	((void)a, (void)b, (void)c, -1)
+#define	pcap_dispatch(a, b, c, d)	(void)c
+#define	pcap_open_live(a, b, c, d, e)	((void)e, NULL)
+#else /* !NO_PCAP */
+#include <pcap/pcap.h> // XXX do we need it ?
+#endif /* !NO_PCAP */
+
+#endif // XXX hack
+
+#include <pthread.h>	/* pthread_* */
+
+#ifdef linux
+#define ifr_flagshigh  ifr_flags
+#define ifr_curcap     ifr_flags
+#define ifr_reqcap     ifr_flags
+#define IFF_PPROMISC   IFF_PROMISC
+#include <linux/ethtool.h>
+#include <linux/sockios.h>
+
+#define CLOCK_REALTIME_PRECISE CLOCK_REALTIME
+#include <netinet/ether.h>      /* ether_aton */
+#include <linux/if_packet.h>    /* sockaddr_ll */
+#endif	/* linux */
+
+#ifdef __FreeBSD__
+#include <sys/endian.h> /* le64toh */
+#include <machine/param.h>
+
+#include <pthread_np.h> /* pthread w/ affinity */
+#include <sys/cpuset.h> /* cpu_set */
+#include <net/if_dl.h>  /* LLADDR */
+#endif	/* __FreeBSD__ */
+
+#ifdef __APPLE__
+#define ifr_flagshigh  ifr_flags	// XXX
+#define IFF_PPROMISC   IFF_PROMISC
+#include <net/if_dl.h>  /* LLADDR */
+#define clock_gettime(a,b)	\
+	do {struct timespec t0 = {0,0};	*(b) = t0; } while (0)
+#endif	/* __APPLE__ */
+
+static inline int min(int a, int b) { return a < b ? a : b; }
+extern int time_second;
+
+/* debug support */
+#define ND(format, ...)	do {} while(0)
+#define D(format, ...)					\
+	fprintf(stderr, "%s [%d] " format "\n",		\
+	__FUNCTION__, __LINE__, ##__VA_ARGS__)
+
+#define RD(lps, format, ...)				\
+	do {						\
+		static int t0, cnt;			\
+		if (t0 != time_second) {		\
+			t0 = time_second;		\
+			cnt = 0;			\
+		}					\
+		if (cnt++ < lps)			\
+			D(format, ##__VA_ARGS__);	\
+	} while (0)
+
+
+
+// XXX does it work on 32-bit machines ?
+static inline void prefetch (const void *x)
+{
+	__asm volatile("prefetcht0 %0" :: "m" (*(const unsigned long *)x));
+}
+
+// XXX only for multiples of 64 bytes, non overlapped.
+static inline void
+pkt_copy(const void *_src, void *_dst, int l)
+{
+	const uint64_t *src = _src;
+	uint64_t *dst = _dst;
+#define likely(x)       __builtin_expect(!!(x), 1)
+#define unlikely(x)       __builtin_expect(!!(x), 0)
+	if (unlikely(l >= 1024)) {
+		bcopy(src, dst, l);
+		return;
+	}
+	for (; l > 0; l-=64) {
+		*dst++ = *src++;
+		*dst++ = *src++;
+		*dst++ = *src++;
+		*dst++ = *src++;
+		*dst++ = *src++;
+		*dst++ = *src++;
+		*dst++ = *src++;
+		*dst++ = *src++;
+	}
+}
+
+/*
+ * info on a ring we handle
+ */
+struct my_ring {
+	const char *ifname;
+	int fd;
+	char *mem;                      /* userspace mmap address */
+	u_int memsize;
+	u_int queueid;
+	u_int begin, end;               /* first..last+1 rings to check */
+	struct netmap_if *nifp;
+	struct netmap_ring *tx, *rx;    /* shortcuts */
+
+	uint32_t if_flags;
+	uint32_t if_reqcap;
+	uint32_t if_curcap;
+};
+int netmap_open(struct my_ring *me, int ringid, int promisc);
+int netmap_close(struct my_ring *me);
+int nm_do_ioctl(struct my_ring *me, u_long what, int subcmd);
+#endif /* _NM_UTIL_H */


Property changes on: trunk/tools/tools/netmap/nm_util.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/tools/tools/netmap/pcap.c
===================================================================
--- trunk/tools/tools/netmap/pcap.c	                        (rev 0)
+++ trunk/tools/tools/netmap/pcap.c	2016-09-18 21:05:32 UTC (rev 8415)
@@ -0,0 +1,655 @@
+/*
+ * (C) 2011-2012 Luigi Rizzo
+ *
+ * BSD license
+ *
+ * A simple library that maps some pcap functions onto netmap
+ * This is not 100% complete but enough to let tcpdump, trafshow
+ * and other apps work.
+ *
+ * $FreeBSD: release/9.2.0/tools/tools/netmap/pcap.c 250458 2013-05-10 16:16:33Z luigi $
+ */
+
+#define MY_PCAP
+#include "nm_util.h"
+
+char *version = "$Id: pcap.c 11463 2012-07-30 15:26:02Z luigi $";
+int verbose = 0;
+
+/*
+ * We redefine here a number of structures that are in pcap.h
+ * so we can compile this file without the system header.
+ */
+#ifndef PCAP_ERRBUF_SIZE
+#define PCAP_ERRBUF_SIZE 128
+/*
+ * Each packet is accompanied by a header including the timestamp,
+ * captured size and actual size.
+ */
+struct pcap_pkthdr {
+	struct timeval ts;	/* time stamp */
+	uint32_t caplen;	/* length of portion present */
+	uint32_t len;		/* length this packet (off wire) */
+};
+
+typedef struct pcap_if pcap_if_t;
+
+/*
+ * Representation of an interface address.
+ */
+struct pcap_addr {
+	struct pcap_addr *next;
+	struct sockaddr *addr;		/* address */
+	struct sockaddr *netmask;	/* netmask for the above */
+	struct sockaddr *broadaddr;	/* broadcast addr for the above */
+	struct sockaddr *dstaddr;	/* P2P dest. address for the above */
+};
+
+struct pcap_if {
+	struct pcap_if *next;
+	char *name;		/* name to hand to "pcap_open_live()" */
+	char *description;	/* textual description of interface, or NULL */
+	struct pcap_addr *addresses;
+	uint32_t flags;      /* PCAP_IF_ interface flags */
+};
+
+/*
+ * We do not support stats (yet)
+ */
+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 */
+#ifdef WIN32
+	u_int bs_capt;		/* number of packets that reach the app. */
+#endif /* WIN32 */
+};
+
+typedef void	pcap_t;
+typedef enum {
+	PCAP_D_INOUT = 0,
+	PCAP_D_IN,
+	PCAP_D_OUT
+} pcap_direction_t;
+ 
+
+
+typedef void (*pcap_handler)(u_char *user,
+		const struct pcap_pkthdr *h, const u_char *bytes);
+
+char errbuf[PCAP_ERRBUF_SIZE];
+
+pcap_t *pcap_open_live(const char *device, int snaplen,
+               int promisc, int to_ms, char *errbuf);
+
+int pcap_findalldevs(pcap_if_t **alldevsp, char *errbuf);
+void pcap_close(pcap_t *p);
+int pcap_get_selectable_fd(pcap_t *p);
+int pcap_dispatch(pcap_t *p, int cnt, pcap_handler callback, u_char *user);
+int pcap_setnonblock(pcap_t *p, int nonblock, char *errbuf);
+int pcap_setdirection(pcap_t *p, pcap_direction_t d);
+char *pcap_lookupdev(char *errbuf);
+int pcap_inject(pcap_t *p, const void *buf, size_t size);
+int pcap_fileno(pcap_t *p);
+const char *pcap_lib_version(void);
+
+
+struct eproto {
+	const char *s;
+	u_short p;
+};
+#endif /* !PCAP_ERRBUF_SIZE */
+
+#ifndef TEST
+/*
+ * build as a shared library
+ */
+
+char pcap_version[] = "libnetmap version 0.3";
+
+/*
+ * Our equivalent of pcap_t
+ */
+struct pcap_ring {
+	struct my_ring me;
+#if 0
+	const char *ifname;
+
+	//struct nmreq nmr;
+
+	int fd;
+	char *mem;			/* userspace mmap address */
+	u_int memsize;
+	u_int queueid;
+	u_int begin, end;		/* first..last+1 rings to check */
+	struct netmap_if *nifp;
+
+	uint32_t if_flags;
+	uint32_t if_reqcap;
+	uint32_t if_curcap;
+#endif
+	int snaplen;
+	char *errbuf;
+	int promisc;
+	int to_ms;
+
+	struct pcap_pkthdr hdr;
+
+
+	struct pcap_stat st;
+
+	char msg[PCAP_ERRBUF_SIZE];
+};
+
+
+
+/*
+ * There is a set of functions that tcpdump expects even if probably
+ * not used
+ */
+struct eproto eproto_db[] = {
+	{ "ip", ETHERTYPE_IP },
+	{ "arp", ETHERTYPE_ARP },
+	{ (char *)0, 0 }
+};
+
+
+const char *pcap_lib_version(void)
+{
+	return pcap_version;
+}
+
+int
+pcap_findalldevs(pcap_if_t **alldevsp, char *errbuf)
+{
+	pcap_if_t *top = NULL;
+#ifndef linux
+	struct ifaddrs *i_head, *i;
+	pcap_if_t *cur;
+	struct pcap_addr *tail = NULL;
+	int l;
+
+	D("listing all devs");
+	*alldevsp = NULL;
+	i_head = NULL;
+
+	if (getifaddrs(&i_head)) {
+		D("cannot get if addresses");
+		return -1;
+	}
+	for (i = i_head; i; i = i->ifa_next) {
+		//struct ifaddrs   *ifa;
+		struct pcap_addr *pca;
+		//struct sockaddr *sa;
+
+		D("got interface %s", i->ifa_name);
+		if (!top || strcmp(top->name, i->ifa_name)) {
+			/* new interface */
+			l = sizeof(*top) + strlen(i->ifa_name) + 1;
+			cur = calloc(1, l);
+			if (cur == NULL) {
+				D("no space for if descriptor");
+				continue;
+			}
+			cur->name = (char *)(cur + 1);
+			//cur->flags = i->ifa_flags;
+			strcpy(cur->name, i->ifa_name);
+			cur->description = NULL;
+			cur->next = top;
+			top = cur;
+			tail = NULL;
+		}
+		/* now deal with addresses */
+		D("%s addr family %d len %d %s %s",
+			top->name,
+			i->ifa_addr->sa_family, i->ifa_addr->sa_len,
+			i->ifa_netmask ? "Netmask" : "",
+			i->ifa_broadaddr ? "Broadcast" : "");
+		l = sizeof(struct pcap_addr) +
+			(i->ifa_addr ? i->ifa_addr->sa_len:0) +
+			(i->ifa_netmask ? i->ifa_netmask->sa_len:0) +
+			(i->ifa_broadaddr? i->ifa_broadaddr->sa_len:0);
+		pca = calloc(1, l);
+		if (pca == NULL) {
+			D("no space for if addr");
+			continue;
+		}
+#define SA_NEXT(x) ((struct sockaddr *)((char *)(x) + (x)->sa_len))
+		pca->addr = (struct sockaddr *)(pca + 1);
+		pkt_copy(i->ifa_addr, pca->addr, i->ifa_addr->sa_len);
+		if (i->ifa_netmask) {
+			pca->netmask = SA_NEXT(pca->addr);
+			bcopy(i->ifa_netmask, pca->netmask, i->ifa_netmask->sa_len);
+			if (i->ifa_broadaddr) {
+				pca->broadaddr = SA_NEXT(pca->netmask);
+				bcopy(i->ifa_broadaddr, pca->broadaddr, i->ifa_broadaddr->sa_len);
+			}
+		}
+		if (tail == NULL) {
+			top->addresses = pca;
+		} else {
+			tail->next = pca;
+		}
+		tail = pca;
+
+	}
+	freeifaddrs(i_head);
+#endif /* !linux */
+	(void)errbuf;	/* UNUSED */
+	*alldevsp = top;
+	return 0;
+}
+
+void pcap_freealldevs(pcap_if_t *alldevs)
+{
+	(void)alldevs;	/* UNUSED */
+	D("unimplemented");
+}
+
+char *
+pcap_lookupdev(char *buf)
+{
+	D("%s", buf);
+	strcpy(buf, "/dev/netmap");
+	return buf;
+}
+
+pcap_t *
+pcap_create(const char *source, char *errbuf)
+{
+	D("src %s (call open liveted)", source);
+	return pcap_open_live(source, 0, 1, 100, errbuf);
+}
+
+int
+pcap_activate(pcap_t *p)
+{
+	D("pcap %p running", p);
+	return 0;
+}
+
+int
+pcap_can_set_rfmon(pcap_t *p)
+{
+	(void)p;	/* UNUSED */
+	D("");
+	return 0;	/* no we can't */
+}
+
+int
+pcap_set_snaplen(pcap_t *p, int snaplen)
+{
+	struct pcap_ring *me = p;
+
+	D("len %d", snaplen);
+	me->snaplen = snaplen;
+	return 0;
+}
+
+int
+pcap_snapshot(pcap_t *p)
+{
+	struct pcap_ring *me = p;
+
+	D("len %d", me->snaplen);
+	return me->snaplen;
+}
+
+int
+pcap_lookupnet(const char *device, uint32_t *netp,
+	uint32_t *maskp, char *errbuf)
+{
+
+	(void)errbuf;	/* UNUSED */
+	D("device %s", device);
+	inet_aton("10.0.0.255", (struct in_addr *)netp);
+	inet_aton("255.255.255.0",(struct in_addr *) maskp);
+	return 0;
+}
+
+int
+pcap_set_promisc(pcap_t *p, int promisc)
+{
+	struct pcap_ring *me = p;
+
+	D("promisc %d", promisc);
+        if (nm_do_ioctl(&me->me, SIOCGIFFLAGS, 0))
+		D("SIOCGIFFLAGS failed");
+	if (promisc) {
+		me->me.if_flags |= IFF_PPROMISC;
+	} else {
+		me->me.if_flags &= ~IFF_PPROMISC;
+	}
+	if (nm_do_ioctl(&me->me, SIOCSIFFLAGS, 0))
+		D("SIOCSIFFLAGS failed");
+	return 0;
+}
+
+int
+pcap_set_timeout(pcap_t *p, int to_ms)
+{
+	struct pcap_ring *me = p;
+
+	D("%d ms", to_ms);
+	me->to_ms = to_ms;
+	return 0;
+}
+
+struct bpf_program;
+
+int
+pcap_compile(pcap_t *p, struct bpf_program *fp,
+	const char *str, int optimize, uint32_t netmask)
+{
+	(void)p;	/* UNUSED */
+	(void)fp;	/* UNUSED */
+	(void)optimize;	/* UNUSED */
+	(void)netmask;	/* UNUSED */
+	D("%s", str);
+	return 0;
+}
+
+int
+pcap_setfilter(pcap_t *p, struct bpf_program *fp)
+{
+	(void)p;	/* UNUSED */
+	(void)fp;	/* UNUSED */
+	D("");
+	return 0;
+}
+
+int
+pcap_datalink(pcap_t *p)
+{
+	(void)p;	/* UNUSED */
+	D("returns 1");
+	return 1;	// ethernet
+}
+
+const char *
+pcap_datalink_val_to_name(int dlt)
+{
+	D("%d returns DLT_EN10MB", dlt);
+	return "DLT_EN10MB";
+}
+
+const char *
+pcap_datalink_val_to_description(int dlt)
+{
+	D("%d returns Ethernet link", dlt);
+	return "Ethernet link";
+}
+
+struct pcap_stat;
+int
+pcap_stats(pcap_t *p, struct pcap_stat *ps)
+{
+	struct pcap_ring *me = p;
+	ND("");
+
+	*ps = me->st;
+	return 0;	/* accumulate from pcap_dispatch() */
+};
+
+char *
+pcap_geterr(pcap_t *p)
+{
+	struct pcap_ring *me = p;
+
+	D("");
+	return me->msg;
+}
+
+pcap_t *
+pcap_open_live(const char *device, int snaplen,
+               int promisc, int to_ms, char *errbuf)
+{
+	struct pcap_ring *me;
+	int l;
+
+	(void)snaplen;	/* UNUSED */
+	(void)errbuf;	/* UNUSED */
+	if (!device) {
+		D("missing device name");
+		return NULL;
+	}
+
+	l = strlen(device) + 1;
+	D("request to open %s snaplen %d promisc %d timeout %dms",
+		device, snaplen, promisc, to_ms);
+	me = calloc(1, sizeof(*me) + l);
+	if (me == NULL) {
+		D("failed to allocate struct for %s", device);
+		return NULL;
+	}
+	me->me.ifname = (char *)(me + 1);
+	strcpy((char *)me->me.ifname, device);
+	if (netmap_open(&me->me, 0, promisc)) {
+		D("error opening %s", device);
+		free(me);
+		return NULL;
+	}
+	me->to_ms = to_ms;
+
+	return (pcap_t *)me;
+}
+
+void
+pcap_close(pcap_t *p)
+{
+	struct my_ring *me = p;
+
+	D("");
+	if (!me)
+		return;
+	if (me->mem)
+		munmap(me->mem, me->memsize);
+	/* restore original flags ? */
+	ioctl(me->fd, NIOCUNREGIF, NULL);
+	close(me->fd);
+	bzero(me, sizeof(*me));
+	free(me);
+}
+
+int
+pcap_fileno(pcap_t *p)
+{
+	struct my_ring *me = p;
+	D("returns %d", me->fd);
+	return me->fd;
+}
+
+int
+pcap_get_selectable_fd(pcap_t *p)
+{
+	struct my_ring *me = p;
+
+	ND("");
+	return me->fd;
+}
+
+int
+pcap_setnonblock(pcap_t *p, int nonblock, char *errbuf)
+{
+	(void)p;	/* UNUSED */
+	(void)errbuf;	/* UNUSED */
+	D("mode is %d", nonblock);
+	return 0;	/* ignore */
+}
+
+int
+pcap_setdirection(pcap_t *p, pcap_direction_t d)
+{
+	(void)p;	/* UNUSED */
+	(void)d;	/* UNUSED */
+	D("");
+	return 0;	/* ignore */
+};
+
+int
+pcap_dispatch(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
+{
+	struct pcap_ring *pme = p;
+	struct my_ring *me = &pme->me;
+	int got = 0;
+	u_int si;
+
+	ND("cnt %d", cnt);
+	if (cnt == 0)
+		cnt = -1;
+	/* scan all rings */
+	for (si = me->begin; si < me->end; si++) {
+		struct netmap_ring *ring = NETMAP_RXRING(me->nifp, si);
+		ND("ring has %d pkts", ring->avail);
+		if (ring->avail == 0)
+			continue;
+		pme->hdr.ts = ring->ts;
+		/*
+		 * XXX a proper prefetch should be done as
+		 *	prefetch(i); callback(i-1); ...
+		 */
+		while ((cnt == -1 || cnt != got) && ring->avail > 0) {
+			u_int i = ring->cur;
+			u_int idx = ring->slot[i].buf_idx;
+			if (idx < 2) {
+				D("%s bogus RX index %d at offset %d",
+					me->nifp->ni_name, idx, i);
+				sleep(2);
+			}
+			u_char *buf = (u_char *)NETMAP_BUF(ring, idx);
+			prefetch(buf);
+			pme->hdr.len = pme->hdr.caplen = ring->slot[i].len;
+			// D("call %p len %d", p, me->hdr.len);
+			callback(user, &pme->hdr, buf);
+			ring->cur = NETMAP_RING_NEXT(ring, i);
+			ring->avail--;
+			got++;
+		}
+	}
+	pme->st.ps_recv += got;
+	return got;
+}
+
+int
+pcap_inject(pcap_t *p, const void *buf, size_t size)
+{
+        struct my_ring *me = p;
+        u_int si;
+ 
+        ND("cnt %d", cnt);
+        /* scan all rings */
+        for (si = me->begin; si < me->end; si++) {
+                struct netmap_ring *ring = NETMAP_TXRING(me->nifp, si);
+ 
+                ND("ring has %d pkts", ring->avail);
+                if (ring->avail == 0)
+                        continue;
+		u_int i = ring->cur;
+		u_int idx = ring->slot[i].buf_idx;
+		if (idx < 2) {
+			D("%s bogus TX index %d at offset %d",
+				me->nifp->ni_name, idx, i);
+			sleep(2);
+		}
+		u_char *dst = (u_char *)NETMAP_BUF(ring, idx);
+		ring->slot[i].len = size;
+		pkt_copy(buf, dst, size);
+		ring->cur = NETMAP_RING_NEXT(ring, i);
+		ring->avail--;
+		// if (ring->avail == 0) ioctl(me->fd, NIOCTXSYNC, NULL);
+		return size;
+        }
+	errno = ENOBUFS;
+	return -1;
+}
+
+int
+pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
+{
+	struct pcap_ring *me = p;
+	struct pollfd fds[1];
+	int i;
+
+	ND("cnt %d", cnt);
+	memset(fds, 0, sizeof(fds));
+	fds[0].fd = me->me.fd;
+	fds[0].events = (POLLIN);
+
+	while (cnt == -1 || cnt > 0) {
+                if (poll(fds, 1, me->to_ms) <= 0) {
+                        D("poll error/timeout");
+			continue;
+                }
+		i = pcap_dispatch(p, cnt, callback, user);
+		if (cnt > 0)
+			cnt -= i;
+	}
+	return 0;
+}
+
+#endif /* !TEST */
+
+#ifdef TEST	/* build test code */
+void do_send(u_char *user, const struct pcap_pkthdr *h, const u_char *buf)
+{
+	pcap_inject((pcap_t *)user, buf, h->caplen);
+}
+
+/*
+ * a simple pcap test program, bridge between two interfaces.
+ */
+int
+main(int argc, char **argv)
+{
+	pcap_t *p0, *p1;
+	int burst = 1024;
+	struct pollfd pollfd[2];
+
+	fprintf(stderr, "%s %s built %s %s\n",
+		argv[0], version, __DATE__, __TIME__);
+		
+	while (argc > 1 && !strcmp(argv[1], "-v")) {
+		verbose++;
+		argv++;
+		argc--;
+	}
+
+	if (argc < 3 || argc > 4 || !strcmp(argv[1], argv[2])) {
+		D("Usage: %s IFNAME1 IFNAME2 [BURST]", argv[0]);
+		return (1);
+	}
+	if (argc > 3)
+		burst = atoi(argv[3]);
+
+	p0 = pcap_open_live(argv[1], 0, 1, 100, NULL);
+	p1 = pcap_open_live(argv[2], 0, 1, 100, NULL);
+	D("%s", version);
+	D("open returns %p %p", p0, p1);
+	if (!p0 || !p1)
+		return(1);
+	bzero(pollfd, sizeof(pollfd));
+	pollfd[0].fd = pcap_fileno(p0);
+	pollfd[1].fd = pcap_fileno(p1);
+	pollfd[0].events = pollfd[1].events = POLLIN;
+	for (;;) {
+		/* do i need to reset ? */
+		pollfd[0].revents = pollfd[1].revents = 0;
+		int ret = poll(pollfd, 2, 1000);
+		if (ret <= 0 || verbose)
+                   D("poll %s [0] ev %x %x [1] ev %x %x",
+                        ret <= 0 ? "timeout" : "ok",
+                                pollfd[0].events,
+                                pollfd[0].revents,
+                                pollfd[1].events,
+                                pollfd[1].revents);
+		if (ret < 0)
+			continue;
+		if (pollfd[0].revents & POLLIN)
+			pcap_dispatch(p0, burst, do_send, p1);
+		if (pollfd[1].revents & POLLIN)
+			pcap_dispatch(p1, burst, do_send, p0);
+	}
+
+	return (0);
+}
+#endif /* TEST */


Property changes on: trunk/tools/tools/netmap/pcap.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/tools/tools/netmap/pkt-gen.c
===================================================================
--- trunk/tools/tools/netmap/pkt-gen.c	                        (rev 0)
+++ trunk/tools/tools/netmap/pkt-gen.c	2016-09-18 21:05:32 UTC (rev 8415)
@@ -0,0 +1,1500 @@
+/*
+ * Copyright (C) 2011-2012 Matteo Landi, 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.
+ */
+
+/*
+ * $FreeBSD: release/9.2.0/tools/tools/netmap/pkt-gen.c 250458 2013-05-10 16:16:33Z luigi $
+ * $Id: pkt-gen.c 12024 2013-01-25 05:41:51Z luigi $
+ *
+ * Example program to show how to build a multithreaded packet
+ * source/sink using the netmap device.
+ *
+ * In this example we create a programmable number of threads
+ * to take care of all the queues of the interface used to
+ * send or receive traffic.
+ *
+ */
+
+#include "nm_util.h"
+
+const char *default_payload="netmap pkt-gen payload\n"
+	"http://info.iet.unipi.it/~luigi/netmap/ ";
+
+int time_second;	// support for RD() debugging macro
+
+int verbose = 0;
+
+#define SKIP_PAYLOAD 1 /* do not check payload. */
+
+struct pkt {
+	struct ether_header eh;
+	struct ip ip;
+	struct udphdr udp;
+	uint8_t body[2048];	// XXX hardwired
+} __attribute__((__packed__));
+
+struct ip_range {
+	char *name;
+	struct in_addr start, end, cur;
+	uint16_t port0, port1, cur_p;
+};
+
+struct mac_range {
+	char *name;
+	struct ether_addr start, end;
+};
+
+/*
+ * global arguments for all threads
+ */
+
+struct glob_arg {
+	struct ip_range src_ip;
+	struct ip_range dst_ip;
+	struct mac_range dst_mac;
+	struct mac_range src_mac;
+	int pkt_size;
+	int burst;
+	int forever;
+	int npackets;	/* total packets to send */
+	int nthreads;
+	int cpus;
+	int options;	/* testing */
+#define OPT_PREFETCH	1
+#define OPT_ACCESS	2
+#define OPT_COPY	4
+#define OPT_MEMCPY	8
+#define OPT_TS		16	/* add a timestamp */
+	int dev_type;
+	pcap_t *p;
+
+	int affinity;
+	int main_fd;
+	int report_interval;
+	void *(*td_body)(void *);
+	void *mmap_addr;
+	int mmap_size;
+	char *ifname;
+};
+enum dev_type { DEV_NONE, DEV_NETMAP, DEV_PCAP, DEV_TAP };
+
+
+/*
+ * Arguments for a new thread. The same structure is used by
+ * the source and the sink
+ */
+struct targ {
+	struct glob_arg *g;
+	int used;
+	int completed;
+	int cancel;
+	int fd;
+	struct nmreq nmr;
+	struct netmap_if *nifp;
+	uint16_t	qfirst, qlast; /* range of queues to scan */
+	volatile uint64_t count;
+	struct timeval tic, toc;
+	int me;
+	pthread_t thread;
+	int affinity;
+
+	struct pkt pkt;
+};
+
+
+/*
+ * extract the extremes from a range of ipv4 addresses.
+ * addr_lo[-addr_hi][:port_lo[-port_hi]]
+ */
+static void
+extract_ip_range(struct ip_range *r)
+{
+	char *p_lo, *p_hi;
+	char buf1[16]; // one ip address
+
+	D("extract IP range from %s", r->name);
+	p_lo = index(r->name, ':');	/* do we have ports ? */
+	if (p_lo) {
+		D(" found ports at %s", p_lo);
+		*p_lo++ = '\0';
+		p_hi = index(p_lo, '-');
+		if (p_hi)
+			*p_hi++ = '\0';
+		else
+			p_hi = p_lo;
+		r->port0 = strtol(p_lo, NULL, 0);
+		r->port1 = strtol(p_hi, NULL, 0);
+		if (r->port1 < r->port0) {
+			r->cur_p = r->port0;
+			r->port0 = r->port1;
+			r->port1 = r->cur_p;
+		}
+		r->cur_p = r->port0;
+		D("ports are %d to %d", r->port0, r->port1);
+	}
+	p_hi = index(r->name, '-');	/* do we have upper ip ? */
+	if (p_hi) {
+		*p_hi++ = '\0';
+	} else
+		p_hi = r->name;
+	inet_aton(r->name, &r->start);
+	inet_aton(p_hi, &r->end);
+	if (r->start.s_addr > r->end.s_addr) {
+		r->cur = r->start;
+		r->start = r->end;
+		r->end = r->cur;
+	}
+	r->cur = r->start;
+	strncpy(buf1, inet_ntoa(r->end), sizeof(buf1));
+	D("range is %s %d to %s %d", inet_ntoa(r->start), r->port0,
+		buf1, r->port1);
+}
+
+static void
+extract_mac_range(struct mac_range *r)
+{
+	D("extract MAC range from %s", r->name);
+	bcopy(ether_aton(r->name), &r->start, 6);
+	bcopy(ether_aton(r->name), &r->end, 6);
+#if 0
+	bcopy(targ->src_mac, eh->ether_shost, 6);
+	p = index(targ->g->src_mac, '-');
+	if (p)
+		targ->src_mac_range = atoi(p+1);
+
+	bcopy(ether_aton(targ->g->dst_mac), targ->dst_mac, 6);
+	bcopy(targ->dst_mac, eh->ether_dhost, 6);
+	p = index(targ->g->dst_mac, '-');
+	if (p)
+		targ->dst_mac_range = atoi(p+1);
+#endif
+	D("%s starts at %s", r->name, ether_ntoa(&r->start));
+}
+
+static struct targ *targs;
+static int global_nthreads;
+
+/* control-C handler */
+static void
+sigint_h(int sig)
+{
+	int i;
+
+	(void)sig;	/* UNUSED */
+	for (i = 0; i < global_nthreads; i++) {
+		targs[i].cancel = 1;
+	}
+	signal(SIGINT, SIG_DFL);
+}
+
+/* sysctl wrapper to return the number of active CPUs */
+static int
+system_ncpus(void)
+{
+#ifdef __FreeBSD__
+	int mib[2], ncpus;
+	size_t len;
+
+	mib[0] = CTL_HW;
+	mib[1] = HW_NCPU;
+	len = sizeof(mib);
+	sysctl(mib, 2, &ncpus, &len, NULL, 0);
+
+	return (ncpus);
+#else
+	return 1;
+#endif /* !__FreeBSD__ */
+}
+
+#ifdef __linux__
+#define sockaddr_dl    sockaddr_ll
+#define sdl_family     sll_family
+#define AF_LINK        AF_PACKET
+#define LLADDR(s)      s->sll_addr;
+#include <linux/if_tun.h>
+#define TAP_CLONEDEV	"/dev/net/tun"
+#endif /* __linux__ */
+
+#ifdef __FreeBSD__
+#include <net/if_tun.h>
+#define TAP_CLONEDEV	"/dev/tap"
+#endif /* __FreeBSD */
+
+#ifdef __APPLE__
+// #warning TAP not supported on apple ?
+#include <net/if_utun.h>
+#define TAP_CLONEDEV	"/dev/tap"
+#endif /* __APPLE__ */
+
+
+/*
+ * locate the src mac address for our interface, put it
+ * into the user-supplied buffer. return 0 if ok, -1 on error.
+ */
+static int
+source_hwaddr(const char *ifname, char *buf)
+{
+	struct ifaddrs *ifaphead, *ifap;
+	int l = sizeof(ifap->ifa_name);
+
+	if (getifaddrs(&ifaphead) != 0) {
+		D("getifaddrs %s failed", ifname);
+		return (-1);
+	}
+
+	for (ifap = ifaphead; ifap; ifap = ifap->ifa_next) {
+		struct sockaddr_dl *sdl =
+			(struct sockaddr_dl *)ifap->ifa_addr;
+		uint8_t *mac;
+
+		if (!sdl || sdl->sdl_family != AF_LINK)
+			continue;
+		if (strncmp(ifap->ifa_name, ifname, l) != 0)
+			continue;
+		mac = (uint8_t *)LLADDR(sdl);
+		sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x",
+			mac[0], mac[1], mac[2],
+			mac[3], mac[4], mac[5]);
+		if (verbose)
+			D("source hwaddr %s", buf);
+		break;
+	}
+	freeifaddrs(ifaphead);
+	return ifap ? 0 : 1;
+}
+
+
+/* set the thread affinity. */
+static int
+setaffinity(pthread_t me, int i)
+{
+#ifdef __FreeBSD__
+	cpuset_t cpumask;
+
+	if (i == -1)
+		return 0;
+
+	/* Set thread affinity affinity.*/
+	CPU_ZERO(&cpumask);
+	CPU_SET(i, &cpumask);
+
+	if (pthread_setaffinity_np(me, sizeof(cpuset_t), &cpumask) != 0) {
+		D("Unable to set affinity");
+		return 1;
+	}
+#else
+	(void)me; /* suppress 'unused' warnings */
+	(void)i;
+#endif /* __FreeBSD__ */
+	return 0;
+}
+
+/* Compute the checksum of the given ip header. */
+static uint16_t
+checksum(const void *data, uint16_t len, uint32_t sum)
+{
+        const uint8_t *addr = data;
+	uint32_t i;
+
+        /* Checksum all the pairs of bytes first... */
+        for (i = 0; i < (len & ~1U); i += 2) {
+                sum += (u_int16_t)ntohs(*((u_int16_t *)(addr + i)));
+                if (sum > 0xFFFF)
+                        sum -= 0xFFFF;
+        }
+	/*
+	 * If there's a single byte left over, checksum it, too.
+	 * Network byte order is big-endian, so the remaining byte is
+	 * the high byte.
+	 */
+	if (i < len) {
+		sum += addr[i] << 8;
+		if (sum > 0xFFFF)
+			sum -= 0xFFFF;
+	}
+	return sum;
+}
+
+static u_int16_t
+wrapsum(u_int32_t sum)
+{
+	sum = ~sum & 0xFFFF;
+	return (htons(sum));
+}
+
+/*
+ * Fill a packet with some payload.
+ * We create a UDP packet so the payload starts at
+ *	14+20+8 = 42 bytes.
+ */
+#ifdef __linux__
+#define uh_sport source
+#define uh_dport dest
+#define uh_ulen len
+#define uh_sum check
+#endif /* linux */
+static void
+initialize_packet(struct targ *targ)
+{
+	struct pkt *pkt = &targ->pkt;
+	struct ether_header *eh;
+	struct ip *ip;
+	struct udphdr *udp;
+	uint16_t paylen = targ->g->pkt_size - sizeof(*eh) - sizeof(struct ip);
+	int i, l, l0 = strlen(default_payload);
+
+	for (i = 0; i < paylen;) {
+		l = min(l0, paylen - i);
+		bcopy(default_payload, pkt->body + i, l);
+		i += l;
+	}
+	pkt->body[i-1] = '\0';
+	ip = &pkt->ip;
+
+        ip->ip_v = IPVERSION;
+        ip->ip_hl = 5;
+        ip->ip_id = 0;
+        ip->ip_tos = IPTOS_LOWDELAY;
+	ip->ip_len = ntohs(targ->g->pkt_size - sizeof(*eh));
+        ip->ip_id = 0;
+        ip->ip_off = htons(IP_DF); /* Don't fragment */
+        ip->ip_ttl = IPDEFTTL;
+	ip->ip_p = IPPROTO_UDP;
+	ip->ip_dst.s_addr = targ->g->dst_ip.cur.s_addr;
+	if (++targ->g->dst_ip.cur.s_addr > targ->g->dst_ip.end.s_addr)
+		targ->g->dst_ip.cur.s_addr = targ->g->dst_ip.start.s_addr;
+	ip->ip_src.s_addr = targ->g->src_ip.cur.s_addr;
+	if (++targ->g->src_ip.cur.s_addr > targ->g->src_ip.end.s_addr)
+		targ->g->src_ip.cur.s_addr = targ->g->src_ip.start.s_addr;
+	ip->ip_sum = wrapsum(checksum(ip, sizeof(*ip), 0));
+
+
+	udp = &pkt->udp;
+        udp->uh_sport = htons(targ->g->src_ip.cur_p);
+	if (++targ->g->src_ip.cur_p > targ->g->src_ip.port1)
+		targ->g->src_ip.cur_p = targ->g->src_ip.port0;
+        udp->uh_dport = htons(targ->g->dst_ip.cur_p);
+	if (++targ->g->dst_ip.cur_p > targ->g->dst_ip.port1)
+		targ->g->dst_ip.cur_p = targ->g->dst_ip.port0;
+	udp->uh_ulen = htons(paylen);
+	/* Magic: taken from sbin/dhclient/packet.c */
+	udp->uh_sum = wrapsum(checksum(udp, sizeof(*udp),
+                    checksum(pkt->body,
+                        paylen - sizeof(*udp),
+                        checksum(&ip->ip_src, 2 * sizeof(ip->ip_src),
+                            IPPROTO_UDP + (u_int32_t)ntohs(udp->uh_ulen)
+                        )
+                    )
+                ));
+
+	eh = &pkt->eh;
+	bcopy(&targ->g->src_mac.start, eh->ether_shost, 6);
+	bcopy(&targ->g->dst_mac.start, eh->ether_dhost, 6);
+	eh->ether_type = htons(ETHERTYPE_IP);
+}
+
+/* Check the payload of the packet for errors (use it for debug).
+ * Look for consecutive ascii representations of the size of the packet.
+ */
+static void
+check_payload(char *p, int psize)
+{
+	char temp[64];
+	int n_read, size, sizelen;
+
+	/* get the length in ASCII of the length of the packet. */
+	sizelen = sprintf(temp, "%d", psize) + 1; // include a whitespace
+
+	/* dummy payload. */
+	p += 14; /* skip packet header. */
+	n_read = 14;
+	while (psize - n_read >= sizelen) {
+		sscanf(p, "%d", &size);
+		if (size != psize) {
+			D("Read %d instead of %d", size, psize);
+			break;
+		}
+
+		p += sizelen;
+		n_read += sizelen;
+	}
+}
+
+
+/*
+ * create and enqueue a batch of packets on a ring.
+ * On the last one set NS_REPORT to tell the driver to generate
+ * an interrupt when done.
+ */
+static int
+send_packets(struct netmap_ring *ring, struct pkt *pkt, 
+		int size, u_int count, int options)
+{
+	u_int sent, cur = ring->cur;
+
+	if (ring->avail < count)
+		count = ring->avail;
+
+#if 0
+	if (options & (OPT_COPY | OPT_PREFETCH) ) {
+		for (sent = 0; sent < count; sent++) {
+			struct netmap_slot *slot = &ring->slot[cur];
+			char *p = NETMAP_BUF(ring, slot->buf_idx);
+
+			prefetch(p);
+			cur = NETMAP_RING_NEXT(ring, cur);
+		}
+		cur = ring->cur;
+	}
+#endif
+	for (sent = 0; sent < count; sent++) {
+		struct netmap_slot *slot = &ring->slot[cur];
+		char *p = NETMAP_BUF(ring, slot->buf_idx);
+
+		if (options & OPT_COPY)
+			pkt_copy(pkt, p, size);
+		else if (options & OPT_MEMCPY)
+			memcpy(p, pkt, size);
+		else if (options & OPT_PREFETCH)
+			prefetch(p);
+		slot->len = size;
+		if (sent == count - 1)
+			slot->flags |= NS_REPORT;
+		cur = NETMAP_RING_NEXT(ring, cur);
+	}
+	ring->avail -= sent;
+	ring->cur = cur;
+
+	return (sent);
+}
+
+/*
+ * Send a packet, and wait for a response.
+ * The payload (after UDP header, ofs 42) has a 4-byte sequence
+ * followed by a struct timeval (or bintime?)
+ */
+#define	PAY_OFS	42	/* where in the pkt... */
+
+static void *
+pinger_body(void *data)
+{
+	struct targ *targ = (struct targ *) data;
+	struct pollfd fds[1];
+	struct netmap_if *nifp = targ->nifp;
+	int i, rx = 0, n = targ->g->npackets;
+
+	fds[0].fd = targ->fd;
+	fds[0].events = (POLLIN);
+	static uint32_t sent;
+	struct timespec ts, now, last_print;
+	uint32_t count = 0, min = 1000000000, av = 0;
+
+	if (targ->g->nthreads > 1) {
+		D("can only ping with 1 thread");
+		return NULL;
+	}
+
+	clock_gettime(CLOCK_REALTIME_PRECISE, &last_print);
+	while (n == 0 || (int)sent < n) {
+		struct netmap_ring *ring = NETMAP_TXRING(nifp, 0);
+		struct netmap_slot *slot;
+		char *p;
+	    for (i = 0; i < 1; i++) {
+		slot = &ring->slot[ring->cur];
+		slot->len = targ->g->pkt_size;
+		p = NETMAP_BUF(ring, slot->buf_idx);
+
+		if (ring->avail == 0) {
+			D("-- ouch, cannot send");
+		} else {
+			pkt_copy(&targ->pkt, p, targ->g->pkt_size);
+			clock_gettime(CLOCK_REALTIME_PRECISE, &ts);
+			bcopy(&sent, p+42, sizeof(sent));
+			bcopy(&ts, p+46, sizeof(ts));
+			sent++;
+			ring->cur = NETMAP_RING_NEXT(ring, ring->cur);
+			ring->avail--;
+		}
+	    }
+		/* should use a parameter to decide how often to send */
+		if (poll(fds, 1, 3000) <= 0) {
+			D("poll error/timeout on queue %d", targ->me);
+			continue;
+		}
+		/* see what we got back */
+		for (i = targ->qfirst; i < targ->qlast; i++) {
+			ring = NETMAP_RXRING(nifp, i);
+			while (ring->avail > 0) {
+				uint32_t seq;
+				slot = &ring->slot[ring->cur];
+				p = NETMAP_BUF(ring, slot->buf_idx);
+
+				clock_gettime(CLOCK_REALTIME_PRECISE, &now);
+				bcopy(p+42, &seq, sizeof(seq));
+				bcopy(p+46, &ts, sizeof(ts));
+				ts.tv_sec = now.tv_sec - ts.tv_sec;
+				ts.tv_nsec = now.tv_nsec - ts.tv_nsec;
+				if (ts.tv_nsec < 0) {
+					ts.tv_nsec += 1000000000;
+					ts.tv_sec--;
+				}
+				if (1) D("seq %d/%d delta %d.%09d", seq, sent,
+					(int)ts.tv_sec, (int)ts.tv_nsec);
+				if (ts.tv_nsec < (int)min)
+					min = ts.tv_nsec;
+				count ++;
+				av += ts.tv_nsec;
+				ring->avail--;
+				ring->cur = NETMAP_RING_NEXT(ring, ring->cur);
+				rx++;
+			}
+		}
+		//D("tx %d rx %d", sent, rx);
+		//usleep(100000);
+		ts.tv_sec = now.tv_sec - last_print.tv_sec;
+		ts.tv_nsec = now.tv_nsec - last_print.tv_nsec;
+		if (ts.tv_nsec < 0) {
+			ts.tv_nsec += 1000000000;
+			ts.tv_sec--;
+		}
+		if (ts.tv_sec >= 1) {
+			D("count %d min %d av %d",
+				count, min, av/count);
+			count = 0;
+			av = 0;
+			min = 100000000;
+			last_print = now;
+		}
+	}
+	return NULL;
+}
+
+
+/*
+ * reply to ping requests
+ */
+static void *
+ponger_body(void *data)
+{
+	struct targ *targ = (struct targ *) data;
+	struct pollfd fds[1];
+	struct netmap_if *nifp = targ->nifp;
+	struct netmap_ring *txring, *rxring;
+	int i, rx = 0, sent = 0, n = targ->g->npackets;
+	fds[0].fd = targ->fd;
+	fds[0].events = (POLLIN);
+
+	if (targ->g->nthreads > 1) {
+		D("can only reply ping with 1 thread");
+		return NULL;
+	}
+	D("understood ponger %d but don't know how to do it", n);
+	while (n == 0 || sent < n) {
+		uint32_t txcur, txavail;
+//#define BUSYWAIT
+#ifdef BUSYWAIT
+		ioctl(fds[0].fd, NIOCRXSYNC, NULL);
+#else
+		if (poll(fds, 1, 1000) <= 0) {
+			D("poll error/timeout on queue %d", targ->me);
+			continue;
+		}
+#endif
+		txring = NETMAP_TXRING(nifp, 0);
+		txcur = txring->cur;
+		txavail = txring->avail;
+		/* see what we got back */
+		for (i = targ->qfirst; i < targ->qlast; i++) {
+			rxring = NETMAP_RXRING(nifp, i);
+			while (rxring->avail > 0) {
+				uint16_t *spkt, *dpkt;
+				uint32_t cur = rxring->cur;
+				struct netmap_slot *slot = &rxring->slot[cur];
+				char *src, *dst;
+				src = NETMAP_BUF(rxring, slot->buf_idx);
+				//D("got pkt %p of size %d", src, slot->len);
+				rxring->avail--;
+				rxring->cur = NETMAP_RING_NEXT(rxring, cur);
+				rx++;
+				if (txavail == 0)
+					continue;
+				dst = NETMAP_BUF(txring,
+				    txring->slot[txcur].buf_idx);
+				/* copy... */
+				dpkt = (uint16_t *)dst;
+				spkt = (uint16_t *)src;
+				pkt_copy(src, dst, slot->len);
+				dpkt[0] = spkt[3];
+				dpkt[1] = spkt[4];
+				dpkt[2] = spkt[5];
+				dpkt[3] = spkt[0];
+				dpkt[4] = spkt[1];
+				dpkt[5] = spkt[2];
+				txring->slot[txcur].len = slot->len;
+				/* XXX swap src dst mac */
+				txcur = NETMAP_RING_NEXT(txring, txcur);
+				txavail--;
+				sent++;
+			}
+		}
+		txring->cur = txcur;
+		txring->avail = txavail;
+		targ->count = sent;
+#ifdef BUSYWAIT
+		ioctl(fds[0].fd, NIOCTXSYNC, NULL);
+#endif
+		//D("tx %d rx %d", sent, rx);
+	}
+	return NULL;
+}
+
+
+static void *
+sender_body(void *data)
+{
+	struct targ *targ = (struct targ *) data;
+
+	struct pollfd fds[1];
+	struct netmap_if *nifp = targ->nifp;
+	struct netmap_ring *txring;
+	int i, n = targ->g->npackets / targ->g->nthreads, sent = 0;
+	int options = targ->g->options | OPT_COPY;
+D("start");
+	if (setaffinity(targ->thread, targ->affinity))
+		goto quit;
+	/* setup poll(2) mechanism. */
+	memset(fds, 0, sizeof(fds));
+	fds[0].fd = targ->fd;
+	fds[0].events = (POLLOUT);
+
+	/* main loop.*/
+	gettimeofday(&targ->tic, NULL);
+
+    if (targ->g->dev_type == DEV_PCAP) {
+	    int size = targ->g->pkt_size;
+	    void *pkt = &targ->pkt;
+	    pcap_t *p = targ->g->p;
+
+	    for (i = 0; !targ->cancel && (n == 0 || sent < n); i++) {
+		if (pcap_inject(p, pkt, size) != -1)
+			sent++;
+		if (i > 10000) {
+			targ->count = sent;
+			i = 0;
+		}
+	    }
+    } else if (targ->g->dev_type == DEV_TAP) { /* tap */
+	    int size = targ->g->pkt_size;
+	    void *pkt = &targ->pkt;
+	    D("writing to file desc %d", targ->g->main_fd);
+
+	    for (i = 0; !targ->cancel && (n == 0 || sent < n); i++) {
+		if (write(targ->g->main_fd, pkt, size) != -1)
+			sent++;
+		if (i > 10000) {
+			targ->count = sent;
+			i = 0;
+		}
+	    }
+    } else {
+	while (!targ->cancel && (n == 0 || sent < n)) {
+
+		/*
+		 * wait for available room in the send queue(s)
+		 */
+		if (poll(fds, 1, 2000) <= 0) {
+			if (targ->cancel)
+				break;
+			D("poll error/timeout on queue %d", targ->me);
+			goto quit;
+		}
+		/*
+		 * scan our queues and send on those with room
+		 */
+		if (options & OPT_COPY && sent > 100000 && !(targ->g->options & OPT_COPY) ) {
+			D("drop copy");
+			options &= ~OPT_COPY;
+		}
+		for (i = targ->qfirst; i < targ->qlast; i++) {
+			int m, limit = targ->g->burst;
+			if (n > 0 && n - sent < limit)
+				limit = n - sent;
+			txring = NETMAP_TXRING(nifp, i);
+			if (txring->avail == 0)
+				continue;
+			m = send_packets(txring, &targ->pkt, targ->g->pkt_size,
+					 limit, options);
+			sent += m;
+			targ->count = sent;
+		}
+	}
+	/* flush any remaining packets */
+	ioctl(fds[0].fd, NIOCTXSYNC, NULL);
+
+	/* final part: wait all the TX queues to be empty. */
+	for (i = targ->qfirst; i < targ->qlast; i++) {
+		txring = NETMAP_TXRING(nifp, i);
+		while (!NETMAP_TX_RING_EMPTY(txring)) {
+			ioctl(fds[0].fd, NIOCTXSYNC, NULL);
+			usleep(1); /* wait 1 tick */
+		}
+	}
+    }
+
+	gettimeofday(&targ->toc, NULL);
+	targ->completed = 1;
+	targ->count = sent;
+
+quit:
+	/* reset the ``used`` flag. */
+	targ->used = 0;
+
+	return (NULL);
+}
+
+
+static void
+receive_pcap(u_char *user, const struct pcap_pkthdr * h,
+	const u_char * bytes)
+{
+	int *count = (int *)user;
+	(void)h;	/* UNUSED */
+	(void)bytes;	/* UNUSED */
+	(*count)++;
+}
+
+static int
+receive_packets(struct netmap_ring *ring, u_int limit, int skip_payload)
+{
+	u_int cur, rx;
+
+	cur = ring->cur;
+	if (ring->avail < limit)
+		limit = ring->avail;
+	for (rx = 0; rx < limit; rx++) {
+		struct netmap_slot *slot = &ring->slot[cur];
+		char *p = NETMAP_BUF(ring, slot->buf_idx);
+
+		if (!skip_payload)
+			check_payload(p, slot->len);
+
+		cur = NETMAP_RING_NEXT(ring, cur);
+	}
+	ring->avail -= rx;
+	ring->cur = cur;
+
+	return (rx);
+}
+
+static void *
+receiver_body(void *data)
+{
+	struct targ *targ = (struct targ *) data;
+	struct pollfd fds[1];
+	struct netmap_if *nifp = targ->nifp;
+	struct netmap_ring *rxring;
+	int i;
+	uint64_t received = 0;
+
+	if (setaffinity(targ->thread, targ->affinity))
+		goto quit;
+
+	/* setup poll(2) mechanism. */
+	memset(fds, 0, sizeof(fds));
+	fds[0].fd = targ->fd;
+	fds[0].events = (POLLIN);
+
+	/* unbounded wait for the first packet. */
+	for (;;) {
+		i = poll(fds, 1, 1000);
+		if (i > 0 && !(fds[0].revents & POLLERR))
+			break;
+		D("waiting for initial packets, poll returns %d %d", i, fds[0].revents);
+	}
+
+	/* main loop, exit after 1s silence */
+	gettimeofday(&targ->tic, NULL);
+    if (targ->g->dev_type == DEV_PCAP) {
+	while (!targ->cancel) {
+		/* XXX should we poll ? */
+		pcap_dispatch(targ->g->p, targ->g->burst, receive_pcap, NULL);
+	}
+    } else if (targ->g->dev_type == DEV_TAP) {
+	D("reading from %s fd %d", targ->g->ifname, targ->g->main_fd);
+	while (!targ->cancel) {
+		char buf[2048];
+		/* XXX should we poll ? */
+		if (read(targ->g->main_fd, buf, sizeof(buf)) > 0)
+			targ->count++;
+	}
+    } else {
+	while (!targ->cancel) {
+		/* Once we started to receive packets, wait at most 1 seconds
+		   before quitting. */
+		if (poll(fds, 1, 1 * 1000) <= 0 && targ->g->forever == 0) {
+			gettimeofday(&targ->toc, NULL);
+			targ->toc.tv_sec -= 1; /* Subtract timeout time. */
+			break;
+		}
+
+		for (i = targ->qfirst; i < targ->qlast; i++) {
+			int m;
+
+			rxring = NETMAP_RXRING(nifp, i);
+			if (rxring->avail == 0)
+				continue;
+
+			m = receive_packets(rxring, targ->g->burst,
+					SKIP_PAYLOAD);
+			received += m;
+		}
+		targ->count = received;
+
+		// tell the card we have read the data
+		//ioctl(fds[0].fd, NIOCRXSYNC, NULL);
+	}
+    }
+
+	targ->completed = 1;
+	targ->count = received;
+
+quit:
+	/* reset the ``used`` flag. */
+	targ->used = 0;
+
+	return (NULL);
+}
+
+/* very crude code to print a number in normalized form.
+ * Caller has to make sure that the buffer is large enough.
+ */
+static const char *
+norm(char *buf, double val)
+{
+	char *units[] = { "", "K", "M", "G" };
+	u_int i;
+
+	for (i = 0; val >=1000 && i < sizeof(units)/sizeof(char *); i++)
+		val /= 1000;
+	sprintf(buf, "%.2f %s", val, units[i]);
+	return buf;
+}
+
+static void
+tx_output(uint64_t sent, int size, double delta)
+{
+	double bw, raw_bw, pps;
+	char b1[40], b2[80], b3[80];
+
+	printf("Sent %" PRIu64 " packets, %d bytes each, in %.2f seconds.\n",
+	       sent, size, delta);
+	if (delta == 0)
+		delta = 1e-6;
+	if (size < 60)		/* correct for min packet size */
+		size = 60;
+	pps = sent / delta;
+	bw = (8.0 * size * sent) / delta;
+	/* raw packets have4 bytes crc + 20 bytes framing */
+	raw_bw = (8.0 * (size + 24) * sent) / delta;
+
+	printf("Speed: %spps Bandwidth: %sbps (raw %sbps)\n",
+		norm(b1, pps), norm(b2, bw), norm(b3, raw_bw) );
+}
+
+
+static void
+rx_output(uint64_t received, double delta)
+{
+	double pps;
+	char b1[40];
+
+	printf("Received %" PRIu64 " packets, in %.2f seconds.\n", received, delta);
+
+	if (delta == 0)
+		delta = 1e-6;
+	pps = received / delta;
+	printf("Speed: %spps\n", norm(b1, pps));
+}
+
+static void
+usage(void)
+{
+	const char *cmd = "pkt-gen";
+	fprintf(stderr,
+		"Usage:\n"
+		"%s arguments\n"
+		"\t-i interface		interface name\n"
+		"\t-f function		tx rx ping pong\n"
+		"\t-n count		number of iterations (can be 0)\n"
+		"\t-t pkts_to_send		also forces tx mode\n"
+		"\t-r pkts_to_receive	also forces rx mode\n"
+		"\t-l pkts_size		in bytes excluding CRC\n"
+		"\t-d dst-ip		end with %%n to sweep n addresses\n"
+		"\t-s src-ip		end with %%n to sweep n addresses\n"
+		"\t-D dst-mac		end with %%n to sweep n addresses\n"
+		"\t-S src-mac		end with %%n to sweep n addresses\n"
+		"\t-a cpu_id		use setaffinity\n"
+		"\t-b burst size		testing, mostly\n"
+		"\t-c cores		cores to use\n"
+		"\t-p threads		processes/threads to use\n"
+		"\t-T report_ms		milliseconds between reports\n"
+		"\t-P				use libpcap instead of netmap\n"
+		"\t-w wait_for_link_time	in seconds\n"
+		"",
+		cmd);
+
+	exit(0);
+}
+
+static void
+start_threads(struct glob_arg *g)
+{
+	int i;
+
+	targs = calloc(g->nthreads, sizeof(*targs));
+	/*
+	 * Now create the desired number of threads, each one
+	 * using a single descriptor.
+ 	 */
+	for (i = 0; i < g->nthreads; i++) {
+		bzero(&targs[i], sizeof(targs[i]));
+		targs[i].fd = -1; /* default, with pcap */
+		targs[i].g = g;
+
+	    if (g->dev_type == DEV_NETMAP) {
+		struct nmreq tifreq;
+		int tfd;
+
+		/* register interface. */
+		tfd = open("/dev/netmap", O_RDWR);
+		if (tfd == -1) {
+			D("Unable to open /dev/netmap");
+			continue;
+		}
+		targs[i].fd = tfd;
+
+		bzero(&tifreq, sizeof(tifreq));
+		strncpy(tifreq.nr_name, g->ifname, sizeof(tifreq.nr_name));
+		tifreq.nr_version = NETMAP_API;
+		tifreq.nr_ringid = (g->nthreads > 1) ? (i | NETMAP_HW_RING) : 0;
+
+		/*
+		 * if we are acting as a receiver only, do not touch the transmit ring.
+		 * This is not the default because many apps may use the interface
+		 * in both directions, but a pure receiver does not.
+		 */
+		if (g->td_body == receiver_body) {
+			tifreq.nr_ringid |= NETMAP_NO_TX_POLL;
+		}
+
+		if ((ioctl(tfd, NIOCREGIF, &tifreq)) == -1) {
+			D("Unable to register %s", g->ifname);
+			continue;
+		}
+		targs[i].nmr = tifreq;
+		targs[i].nifp = NETMAP_IF(g->mmap_addr, tifreq.nr_offset);
+		/* start threads. */
+		targs[i].qfirst = (g->nthreads > 1) ? i : 0;
+		targs[i].qlast = (g->nthreads > 1) ? i+1 :
+			(g->td_body == receiver_body ? tifreq.nr_rx_rings : tifreq.nr_tx_rings);
+	    } else {
+		targs[i].fd = g->main_fd;
+	    }
+		targs[i].used = 1;
+		targs[i].me = i;
+		if (g->affinity >= 0) {
+			if (g->affinity < g->cpus)
+				targs[i].affinity = g->affinity;
+			else
+				targs[i].affinity = i % g->cpus;
+		} else
+			targs[i].affinity = -1;
+		/* default, init packets */
+		initialize_packet(&targs[i]);
+
+		if (pthread_create(&targs[i].thread, NULL, g->td_body,
+				   &targs[i]) == -1) {
+			D("Unable to create thread %d", i);
+			targs[i].used = 0;
+		}
+	}
+}
+
+static void
+main_thread(struct glob_arg *g)
+{
+	int i;
+
+	uint64_t prev = 0;
+	uint64_t count = 0;
+	double delta_t;
+	struct timeval tic, toc;
+
+	gettimeofday(&toc, NULL);
+	for (;;) {
+		struct timeval now, delta;
+		uint64_t pps, usec, my_count, npkts;
+		int done = 0;
+
+		delta.tv_sec = g->report_interval/1000;
+		delta.tv_usec = (g->report_interval%1000)*1000;
+		select(0, NULL, NULL, NULL, &delta);
+		gettimeofday(&now, NULL);
+		time_second = now.tv_sec;
+		timersub(&now, &toc, &toc);
+		my_count = 0;
+		for (i = 0; i < g->nthreads; i++) {
+			my_count += targs[i].count;
+			if (targs[i].used == 0)
+				done++;
+		}
+		usec = toc.tv_sec* 1000000 + toc.tv_usec;
+		if (usec < 10000)
+			continue;
+		npkts = my_count - prev;
+		pps = (npkts*1000000 + usec/2) / usec;
+		D("%" PRIu64 " pps (%" PRIu64 " pkts in %" PRIu64 " usec)",
+			pps, npkts, usec);
+		prev = my_count;
+		toc = now;
+		if (done == g->nthreads)
+			break;
+	}
+
+	timerclear(&tic);
+	timerclear(&toc);
+	for (i = 0; i < g->nthreads; i++) {
+		/*
+		 * Join active threads, unregister interfaces and close
+		 * file descriptors.
+		 */
+		pthread_join(targs[i].thread, NULL);
+		close(targs[i].fd);
+
+		if (targs[i].completed == 0)
+			D("ouch, thread %d exited with error", i);
+
+		/*
+		 * Collect threads output and extract information about
+		 * how long it took to send all the packets.
+		 */
+		count += targs[i].count;
+		if (!timerisset(&tic) || timercmp(&targs[i].tic, &tic, <))
+			tic = targs[i].tic;
+		if (!timerisset(&toc) || timercmp(&targs[i].toc, &toc, >))
+			toc = targs[i].toc;
+	}
+
+	/* print output. */
+	timersub(&toc, &tic, &toc);
+	delta_t = toc.tv_sec + 1e-6* toc.tv_usec;
+	if (g->td_body == sender_body)
+		tx_output(count, g->pkt_size, delta_t);
+	else
+		rx_output(count, delta_t);
+
+	if (g->dev_type == DEV_NETMAP) {
+		ioctl(g->main_fd, NIOCUNREGIF, NULL); // XXX deprecated
+		munmap(g->mmap_addr, g->mmap_size);
+		close(g->main_fd);
+	}
+}
+
+
+struct sf {
+	char *key;
+	void *f;
+};
+
+static struct sf func[] = {
+	{ "tx",	sender_body },
+	{ "rx",	receiver_body },
+	{ "ping",	pinger_body },
+	{ "pong",	ponger_body },
+	{ NULL, NULL }
+};
+
+static int
+tap_alloc(char *dev)
+{
+	struct ifreq ifr;
+	int fd, err;
+	char *clonedev = TAP_CLONEDEV;
+
+	(void)err;
+	(void)dev;
+	/* Arguments taken by the function:
+	 *
+	 * char *dev: the name of an interface (or '\0'). MUST have enough
+	 *   space to hold the interface name if '\0' is passed
+	 * int flags: interface flags (eg, IFF_TUN etc.)
+	 */
+
+#ifdef __FreeBSD__
+	if (dev[3]) { /* tapSomething */
+		static char buf[128];
+		snprintf(buf, sizeof(buf), "/dev/%s", dev);
+		clonedev = buf;
+	}
+#endif
+	/* open the device */
+	if( (fd = open(clonedev, O_RDWR)) < 0 ) {
+		return fd;
+	}
+	D("%s open successful", clonedev);
+
+	/* preparation of the struct ifr, of type "struct ifreq" */
+	memset(&ifr, 0, sizeof(ifr));
+
+#ifdef linux
+	ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
+
+	if (*dev) {
+		/* if a device name was specified, put it in the structure; otherwise,
+		* the kernel will try to allocate the "next" device of the
+		* specified type */
+		strncpy(ifr.ifr_name, dev, IFNAMSIZ);
+	}
+
+	/* try to create the device */
+	if( (err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0 ) {
+		D("failed to to a TUNSETIFF");
+		close(fd);
+		return err;
+	}
+
+	/* if the operation was successful, write back the name of the
+	* interface to the variable "dev", so the caller can know
+	* it. Note that the caller MUST reserve space in *dev (see calling
+	* code below) */
+	strcpy(dev, ifr.ifr_name);
+	D("new name is %s", dev);
+#endif /* linux */
+
+        /* this is the special file descriptor that the caller will use to talk
+         * with the virtual interface */
+        return fd;
+}
+
+int
+main(int arc, char **argv)
+{
+	int i;
+
+	struct glob_arg g;
+
+	struct nmreq nmr;
+	int ch;
+	int wait_link = 2;
+	int devqueues = 1;	/* how many device queues */
+
+	bzero(&g, sizeof(g));
+
+	g.main_fd = -1;
+	g.td_body = receiver_body;
+	g.report_interval = 1000;	/* report interval */
+	g.affinity = -1;
+	/* ip addresses can also be a range x.x.x.x-x.x.x.y */
+	g.src_ip.name = "10.0.0.1";
+	g.dst_ip.name = "10.1.0.1";
+	g.dst_mac.name = "ff:ff:ff:ff:ff:ff";
+	g.src_mac.name = NULL;
+	g.pkt_size = 60;
+	g.burst = 512;		// default
+	g.nthreads = 1;
+	g.cpus = 1;
+
+	while ( (ch = getopt(arc, argv,
+			"a:f:n:i:t:r:l:d:s:D:S:b:c:o:p:PT:w:Wv")) != -1) {
+		struct sf *fn;
+
+		switch(ch) {
+		default:
+			D("bad option %c %s", ch, optarg);
+			usage();
+			break;
+
+		case 'n':
+			g.npackets = atoi(optarg);
+			break;
+
+		case 'f':
+			for (fn = func; fn->key; fn++) {
+				if (!strcmp(fn->key, optarg))
+					break;
+			}
+			if (fn->key)
+				g.td_body = fn->f;
+			else
+				D("unrecognised function %s", optarg);
+			break;
+
+		case 'o':	/* data generation options */
+			g.options = atoi(optarg);
+			break;
+
+		case 'a':       /* force affinity */
+			g.affinity = atoi(optarg);
+			break;
+
+		case 'i':	/* interface */
+			g.ifname = optarg;
+			if (!strncmp(optarg, "tap", 3))
+				g.dev_type = DEV_TAP;
+			else
+				g.dev_type = DEV_NETMAP;
+			break;
+
+		case 't':	/* send, deprecated */
+			D("-t deprecated, please use -f tx -n %s", optarg);
+			g.td_body = sender_body;
+			g.npackets = atoi(optarg);
+			break;
+
+		case 'r':	/* receive */
+			D("-r deprecated, please use -f rx -n %s", optarg);
+			g.td_body = receiver_body;
+			g.npackets = atoi(optarg);
+			break;
+
+		case 'l':	/* pkt_size */
+			g.pkt_size = atoi(optarg);
+			break;
+
+		case 'd':
+			g.dst_ip.name = optarg;
+			break;
+
+		case 's':
+			g.src_ip.name = optarg;
+			break;
+
+		case 'T':	/* report interval */
+			g.report_interval = atoi(optarg);
+			break;
+
+		case 'w':
+			wait_link = atoi(optarg);
+			break;
+
+		case 'W':
+			g.forever = 1; /* do not exit rx even with no traffic */
+			break;
+
+		case 'b':	/* burst */
+			g.burst = atoi(optarg);
+			break;
+		case 'c':
+			g.cpus = atoi(optarg);
+			break;
+		case 'p':
+			g.nthreads = atoi(optarg);
+			break;
+
+		case 'P':
+			g.dev_type = DEV_PCAP;
+			break;
+
+		case 'D': /* destination mac */
+			g.dst_mac.name = optarg;
+			break;
+
+		case 'S': /* source mac */
+			g.src_mac.name = optarg;
+			break;
+		case 'v':
+			verbose++;
+		}
+	}
+
+	if (g.ifname == NULL) {
+		D("missing ifname");
+		usage();
+	}
+
+	i = system_ncpus();
+	if (g.cpus < 0 || g.cpus > i) {
+		D("%d cpus is too high, have only %d cpus", g.cpus, i);
+		usage();
+	}
+	if (g.cpus == 0)
+		g.cpus = i;
+
+	if (g.pkt_size < 16 || g.pkt_size > 1536) {
+		D("bad pktsize %d\n", g.pkt_size);
+		usage();
+	}
+
+	if (g.src_mac.name == NULL) {
+		static char mybuf[20] = "00:00:00:00:00:00";
+		/* retrieve source mac address. */
+		if (source_hwaddr(g.ifname, mybuf) == -1) {
+			D("Unable to retrieve source mac");
+			// continue, fail later
+		}
+		g.src_mac.name = mybuf;
+	}
+	/* extract address ranges */
+	extract_ip_range(&g.src_ip);
+	extract_ip_range(&g.dst_ip);
+	extract_mac_range(&g.src_mac);
+	extract_mac_range(&g.dst_mac);
+
+    if (g.dev_type == DEV_TAP) {
+	D("want to use tap %s", g.ifname);
+	g.main_fd = tap_alloc(g.ifname);
+	if (g.main_fd < 0) {
+		D("cannot open tap %s", g.ifname);
+		usage();
+	}
+    } else if (g.dev_type > DEV_NETMAP) {
+	char pcap_errbuf[PCAP_ERRBUF_SIZE];
+
+	D("using pcap on %s", g.ifname);
+	pcap_errbuf[0] = '\0'; // init the buffer
+	g.p = pcap_open_live(g.ifname, 0, 1, 100, pcap_errbuf);
+	if (g.p == NULL) {
+		D("cannot open pcap on %s", g.ifname);
+		usage();
+	}
+    } else {
+	bzero(&nmr, sizeof(nmr));
+	nmr.nr_version = NETMAP_API;
+	/*
+	 * Open the netmap device to fetch the number of queues of our
+	 * interface.
+	 *
+	 * The first NIOCREGIF also detaches the card from the
+	 * protocol stack and may cause a reset of the card,
+	 * which in turn may take some time for the PHY to
+	 * reconfigure.
+	 */
+	g.main_fd = open("/dev/netmap", O_RDWR);
+	if (g.main_fd == -1) {
+		D("Unable to open /dev/netmap");
+		// fail later
+	} else {
+		if ((ioctl(g.main_fd, NIOCGINFO, &nmr)) == -1) {
+			D("Unable to get if info without name");
+		} else {
+			D("map size is %d Kb", nmr.nr_memsize >> 10);
+		}
+		bzero(&nmr, sizeof(nmr));
+		nmr.nr_version = NETMAP_API;
+		strncpy(nmr.nr_name, g.ifname, sizeof(nmr.nr_name));
+		if ((ioctl(g.main_fd, NIOCGINFO, &nmr)) == -1) {
+			D("Unable to get if info for %s", g.ifname);
+		}
+		devqueues = nmr.nr_rx_rings;
+	}
+
+	/* validate provided nthreads. */
+	if (g.nthreads < 1 || g.nthreads > devqueues) {
+		D("bad nthreads %d, have %d queues", g.nthreads, devqueues);
+		// continue, fail later
+	}
+
+	/*
+	 * Map the netmap shared memory: instead of issuing mmap()
+	 * inside the body of the threads, we prefer to keep this
+	 * operation here to simplify the thread logic.
+	 */
+	D("mapping %d Kbytes", nmr.nr_memsize>>10);
+	g.mmap_size = nmr.nr_memsize;
+	g.mmap_addr = (struct netmap_d *) mmap(0, nmr.nr_memsize,
+					    PROT_WRITE | PROT_READ,
+					    MAP_SHARED, g.main_fd, 0);
+	if (g.mmap_addr == MAP_FAILED) {
+		D("Unable to mmap %d KB", nmr.nr_memsize >> 10);
+		// continue, fail later
+	}
+
+	/*
+	 * Register the interface on the netmap device: from now on,
+	 * we can operate on the network interface without any
+	 * interference from the legacy network stack.
+	 *
+	 * We decide to put the first interface registration here to
+	 * give time to cards that take a long time to reset the PHY.
+	 */
+	nmr.nr_version = NETMAP_API;
+	if (ioctl(g.main_fd, NIOCREGIF, &nmr) == -1) {
+		D("Unable to register interface %s", g.ifname);
+		//continue, fail later
+	}
+
+
+	/* Print some debug information. */
+	fprintf(stdout,
+		"%s %s: %d queues, %d threads and %d cpus.\n",
+		(g.td_body == sender_body) ? "Sending on" : "Receiving from",
+		g.ifname,
+		devqueues,
+		g.nthreads,
+		g.cpus);
+	if (g.td_body == sender_body) {
+		fprintf(stdout, "%s -> %s (%s -> %s)\n",
+			g.src_ip.name, g.dst_ip.name,
+			g.src_mac.name, g.dst_mac.name);
+	}
+			
+	/* Exit if something went wrong. */
+	if (g.main_fd < 0) {
+		D("aborting");
+		usage();
+	}
+    }
+
+	if (g.options) {
+		D("special options:%s%s%s%s\n",
+			g.options & OPT_PREFETCH ? " prefetch" : "",
+			g.options & OPT_ACCESS ? " access" : "",
+			g.options & OPT_MEMCPY ? " memcpy" : "",
+			g.options & OPT_COPY ? " copy" : "");
+	}
+	/* Wait for PHY reset. */
+	D("Wait %d secs for phy reset", wait_link);
+	sleep(wait_link);
+	D("Ready...");
+
+	/* Install ^C handler. */
+	global_nthreads = g.nthreads;
+	signal(SIGINT, sigint_h);
+
+#if 0 // XXX this is not needed, i believe
+	if (g.dev_type > DEV_NETMAP) {
+		g.p = pcap_open_live(g.ifname, 0, 1, 100, NULL);
+		if (g.p == NULL) {
+			D("cannot open pcap on %s", g.ifname);
+			usage();
+		} else
+			D("using pcap %p on %s", g.p, g.ifname);
+	}
+#endif // XXX
+	start_threads(&g);
+	main_thread(&g);
+	return 0;
+}
+
+/* end of file */


Property changes on: trunk/tools/tools/netmap/pkt-gen.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property


More information about the Midnightbsd-cvs mailing list