[Midnightbsd-cvs] src [11955] trunk/tools/tools/netmap: update netmap
laffer1 at midnightbsd.org
laffer1 at midnightbsd.org
Sun Jul 22 15:23:20 EDT 2018
Revision: 11955
http://svnweb.midnightbsd.org/src/?rev=11955
Author: laffer1
Date: 2018-07-22 15:23:19 -0400 (Sun, 22 Jul 2018)
Log Message:
-----------
update netmap
Modified Paths:
--------------
trunk/tools/tools/netmap/Makefile
trunk/tools/tools/netmap/README
trunk/tools/tools/netmap/bridge.c
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 Paths:
-----------
trunk/tools/tools/netmap/vale-ctl.c
Property Changed:
----------------
trunk/tools/tools/netmap/README
trunk/tools/tools/netmap/click-test.cfg
Modified: trunk/tools/tools/netmap/Makefile
===================================================================
--- trunk/tools/tools/netmap/Makefile 2018-07-21 20:19:26 UTC (rev 11954)
+++ trunk/tools/tools/netmap/Makefile 2018-07-22 19:23:19 UTC (rev 11955)
@@ -1,16 +1,22 @@
+# $MidnightBSD$
#
-# $FreeBSD: release/9.2.0/tools/tools/netmap/Makefile 250458 2013-05-10 16:16:33Z luigi $
+# $FreeBSD: stable/10/tools/tools/netmap/Makefile 276486 2014-12-31 23:25:37Z ngie $
#
# 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
+PROGS = pkt-gen bridge vale-ctl
-CLEANFILES = $(PROGS) pcap.o nm_util.o
-NO_MAN=
-CFLAGS += -Werror -Wall -nostdinc -I/usr/include -I../../../sys
+CLEANFILES = $(PROGS) *.o
+MAN=
+CFLAGS += -Werror -Wall # -nostdinc -I/usr/include -I../../../sys
CFLAGS += -Wextra
-LDFLAGS += -lpthread -lpcap
+LDFLAGS += -lpthread
+.ifdef WITHOUT_PCAP
+CFLAGS += -DNO_PCAP
+.else
+LDFLAGS += -lpcap
+.endif
.include <bsd.prog.mk>
.include <bsd.lib.mk>
@@ -17,12 +23,11 @@
all: $(PROGS)
-pkt-gen bridge: nm_util.o
- $(CC) $(CFLAGS) -o ${.TARGET} ${.TARGET:=.c} nm_util.o $(LDFLAGS)
+pkt-gen: pkt-gen.o
+ $(CC) $(CFLAGS) -o pkt-gen pkt-gen.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}
+bridge: bridge.o
+ $(CC) $(CFLAGS) -o bridge bridge.o
+
+vale-ctl: vale-ctl.o
+ $(CC) $(CFLAGS) -o vale-ctl vale-ctl.o
Modified: trunk/tools/tools/netmap/README
===================================================================
--- trunk/tools/tools/netmap/README 2018-07-21 20:19:26 UTC (rev 11954)
+++ trunk/tools/tools/netmap/README 2018-07-22 19:23:19 UTC (rev 11955)
@@ -1,4 +1,4 @@
-$FreeBSD: release/9.2.0/tools/tools/netmap/README 250458 2013-05-10 16:16:33Z luigi $
+$MidnightBSD$
This directory contains examples that use netmap
@@ -6,19 +6,4 @@
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)
+ vale-ctl the program to control VALE bridges
Property changes on: trunk/tools/tools/netmap/README
___________________________________________________________________
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Modified: trunk/tools/tools/netmap/bridge.c
===================================================================
--- trunk/tools/tools/netmap/bridge.c 2018-07-21 20:19:26 UTC (rev 11954)
+++ trunk/tools/tools/netmap/bridge.c 2018-07-22 19:23:19 UTC (rev 11955)
@@ -1,5 +1,6 @@
+/* $MidnightBSD$ */
/*
- * (C) 2011 Luigi Rizzo, Matteo Landi
+ * (C) 2011-2014 Luigi Rizzo, Matteo Landi
*
* BSD license
*
@@ -6,17 +7,18 @@
* 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 $
+ * $FreeBSD: stable/10/tools/tools/netmap/bridge.c 262151 2014-02-18 05:01:04Z luigi $
*/
-#include "nm_util.h"
+#include <stdio.h>
+#define NETMAP_WITH_LIBS
+#include <net/netmap_user.h>
+#include <sys/poll.h>
-
int verbose = 0;
-char *version = "$Id: bridge.c 12016 2013-01-23 17:24:22Z luigi $";
-
static int do_abort = 0;
+static int zerocopy = 1; /* enable zerocopy if possible */
static void
sigint_h(int sig)
@@ -28,6 +30,26 @@
/*
+ * how many packets on this set of queues ?
+ */
+int
+pkt_queued(struct nm_desc *d, int tx)
+{
+ u_int i, tot = 0;
+
+ if (tx) {
+ for (i = d->first_tx_ring; i <= d->last_tx_ring; i++) {
+ tot += nm_ring_space(NETMAP_TXRING(d->nifp, i));
+ }
+ } else {
+ for (i = d->first_rx_ring; i <= d->last_rx_ring; i++) {
+ tot += nm_ring_space(NETMAP_RXRING(d->nifp, i));
+ }
+ }
+ return tot;
+}
+
+/*
* move up to 'limit' pkts from rxring to txring swapping buffers.
*/
static int
@@ -42,15 +64,16 @@
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 = nm_ring_space(rxring);
+ if (m < limit)
+ limit = m;
+ m = nm_ring_space(txring);
+ if (m < limit)
+ limit = m;
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) {
@@ -58,27 +81,31 @@
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)
+ if (rs->len > 2048) {
D("wrong len %d rx[%d] -> tx[%d]", rs->len, j, k);
- else if (verbose > 1)
+ rs->len = 0;
+ } 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);
+ if (zerocopy) {
+ uint32_t pkt = ts->buf_idx;
+ ts->buf_idx = rs->buf_idx;
+ rs->buf_idx = pkt;
+ /* report the buffer change. */
+ ts->flags |= NS_BUF_CHANGED;
+ rs->flags |= NS_BUF_CHANGED;
+ } else {
+ char *rxbuf = NETMAP_BUF(rxring, rs->buf_idx);
+ char *txbuf = NETMAP_BUF(txring, ts->buf_idx);
+ nm_pkt_copy(rxbuf, txbuf, ts->len);
+ }
+ j = nm_ring_next(rxring, j);
+ k = nm_ring_next(txring, k);
}
- rxring->avail -= m;
- txring->avail -= m;
- rxring->cur = j;
- txring->cur = k;
+ rxring->head = rxring->cur = j;
+ txring->head = txring->cur = k;
if (verbose && m > 0)
D("%s sent %d packets to %p", msg, m, txring);
@@ -87,22 +114,22 @@
/* move packts from src to destination */
static int
-move(struct my_ring *src, struct my_ring *dst, u_int limit)
+move(struct nm_desc *src, struct nm_desc *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) ?
+ u_int m = 0, si = src->first_rx_ring, di = dst->first_tx_ring;
+ const char *msg = (src->req.nr_ringid & NETMAP_SW_RING) ?
"host->net" : "net->host";
- while (si < src->end && di < dst->end) {
+ while (si <= src->last_rx_ring && di <= dst->last_tx_ring) {
rxring = NETMAP_RXRING(src->nifp, si);
txring = NETMAP_TXRING(dst->nifp, di);
ND("txring %p rxring %p", txring, rxring);
- if (rxring->avail == 0) {
+ if (nm_ring_empty(rxring)) {
si++;
continue;
}
- if (txring->avail == 0) {
+ if (nm_ring_empty(txring)) {
di++;
continue;
}
@@ -112,29 +139,7 @@
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)
{
@@ -154,17 +159,16 @@
main(int argc, char **argv)
{
struct pollfd pollfd[2];
- int i, ch;
+ int ch;
u_int burst = 1024, wait_link = 4;
- struct my_ring me[2];
+ struct nm_desc *pa = NULL, *pb = NULL;
char *ifa = NULL, *ifb = NULL;
+ char ifabuf[64] = { 0 };
- fprintf(stderr, "%s %s built %s %s\n",
- argv[0], version, __DATE__, __TIME__);
+ fprintf(stderr, "%s built %s %s\n",
+ argv[0], __DATE__, __TIME__);
- bzero(me, sizeof(me));
-
- while ( (ch = getopt(argc, argv, "b:i:vw:")) != -1) {
+ while ( (ch = getopt(argc, argv, "b:ci:vw:")) != -1) {
switch (ch) {
default:
D("bad option %c %s", ch, optarg);
@@ -182,6 +186,9 @@
D("%s ignored, already have 2 interfaces",
optarg);
break;
+ case 'c':
+ zerocopy = 0; /* do not zerocopy */
+ break;
case 'v':
verbose++;
break;
@@ -215,34 +222,38 @@
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;
+ snprintf(ifabuf, sizeof(ifabuf) - 1, "%s^", ifa);
+ ifa = ifabuf;
} else {
/* two different interfaces. Take all rings on if1 */
- i = 0; // all hw rings
}
- if (netmap_open(me, i, 1))
+ pa = nm_open(ifa, NULL, 0, NULL);
+ if (pa == NULL) {
+ D("cannot open %s", ifa);
return (1);
- me[1].mem = me[0].mem; /* copy the pointer, so only one mmap */
- if (netmap_open(me+1, 0, 1))
+ }
+ // XXX use a single mmap ?
+ pb = nm_open(ifb, NULL, NM_OPEN_NO_MMAP, pa);
+ if (pb == NULL) {
+ D("cannot open %s", ifb);
+ nm_close(pa);
return (1);
+ }
+ zerocopy = zerocopy && (pa->mem == pb->mem);
+ D("------- zerocopy %ssupported", zerocopy ? "" : "NOT ");
/* 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);
- }
+ pollfd[0].fd = pa->fd;
+ pollfd[1].fd = pb->fd;
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);
+ pa->req.nr_name, pa->first_rx_ring, pa->req.nr_rx_rings,
+ pb->req.nr_name, pb->first_rx_ring, pb->req.nr_rx_rings);
/* main loop */
signal(SIGINT, sigint_h);
@@ -250,8 +261,8 @@
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);
+ n0 = pkt_queued(pa, 0);
+ n1 = pkt_queued(pb, 0);
if (n0)
pollfd[1].events |= POLLOUT;
else
@@ -267,39 +278,41 @@
ret <= 0 ? "timeout" : "ok",
pollfd[0].events,
pollfd[0].revents,
- pkt_queued(me, 0),
- me[0].rx->cur,
- pkt_queued(me, 1),
+ pkt_queued(pa, 0),
+ NETMAP_RXRING(pa->nifp, pa->cur_rx_ring)->cur,
+ pkt_queued(pa, 1),
pollfd[1].events,
pollfd[1].revents,
- pkt_queued(me+1, 0),
- me[1].rx->cur,
- pkt_queued(me+1, 1)
+ pkt_queued(pb, 0),
+ NETMAP_RXRING(pb->nifp, pb->cur_rx_ring)->cur,
+ pkt_queued(pb, 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);
+ struct netmap_ring *rx = NETMAP_RXRING(pa->nifp, pa->cur_rx_ring);
+ D("error on fd0, rx [%d,%d,%d)",
+ rx->head, rx->cur, rx->tail);
}
if (pollfd[1].revents & POLLERR) {
- D("error on fd1, rxcur %d@%d",
- me[1].rx->avail, me[1].rx->cur);
+ struct netmap_ring *rx = NETMAP_RXRING(pb->nifp, pb->cur_rx_ring);
+ D("error on fd1, rx [%d,%d,%d)",
+ rx->head, rx->cur, rx->tail);
}
if (pollfd[0].revents & POLLOUT) {
- move(me + 1, me, burst);
+ move(pb, pa, burst);
// XXX we don't need the ioctl */
// ioctl(me[0].fd, NIOCTXSYNC, NULL);
}
if (pollfd[1].revents & POLLOUT) {
- move(me, me + 1, burst);
+ move(pa, pb, burst);
// XXX we don't need the ioctl */
// ioctl(me[1].fd, NIOCTXSYNC, NULL);
}
}
D("exiting");
- netmap_close(me + 1);
- netmap_close(me + 0);
+ nm_close(pb);
+ nm_close(pa);
return (0);
}
Index: trunk/tools/tools/netmap/click-test.cfg
===================================================================
--- trunk/tools/tools/netmap/click-test.cfg 2018-07-21 20:19:26 UTC (rev 11954)
+++ trunk/tools/tools/netmap/click-test.cfg 2018-07-22 19:23:19 UTC (rev 11955)
Property changes on: trunk/tools/tools/netmap/click-test.cfg
___________________________________________________________________
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Modified: trunk/tools/tools/netmap/nm_util.c
===================================================================
--- trunk/tools/tools/netmap/nm_util.c 2018-07-21 20:19:26 UTC (rev 11954)
+++ trunk/tools/tools/netmap/nm_util.c 2018-07-22 19:23:19 UTC (rev 11955)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
/*
* Copyright (C) 2012 Luigi Rizzo. All rights reserved.
*
Modified: trunk/tools/tools/netmap/nm_util.h
===================================================================
--- trunk/tools/tools/netmap/nm_util.h 2018-07-21 20:19:26 UTC (rev 11954)
+++ trunk/tools/tools/netmap/nm_util.h 2018-07-22 19:23:19 UTC (rev 11955)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
/*
* Copyright (C) 2012 Luigi Rizzo. All rights reserved.
*
Modified: trunk/tools/tools/netmap/pcap.c
===================================================================
--- trunk/tools/tools/netmap/pcap.c 2018-07-21 20:19:26 UTC (rev 11954)
+++ trunk/tools/tools/netmap/pcap.c 2018-07-22 19:23:19 UTC (rev 11955)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
/*
* (C) 2011-2012 Luigi Rizzo
*
Modified: trunk/tools/tools/netmap/pkt-gen.c
===================================================================
--- trunk/tools/tools/netmap/pkt-gen.c 2018-07-21 20:19:26 UTC (rev 11954)
+++ trunk/tools/tools/netmap/pkt-gen.c 2018-07-22 19:23:19 UTC (rev 11955)
@@ -1,5 +1,7 @@
+/* $MidnightBSD$ */
/*
- * Copyright (C) 2011-2012 Matteo Landi, Luigi Rizzo. All rights reserved.
+ * Copyright (C) 2011-2014 Matteo Landi, Luigi Rizzo. All rights reserved.
+ * Copyright (C) 2013-2014 Universita` di Pisa. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -24,8 +26,8 @@
*/
/*
- * $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 $
+ * $FreeBSD: stable/10/tools/tools/netmap/pkt-gen.c 274259 2014-11-08 00:42:11Z gnn $
+ * $Id: pkt-gen.c 12346 2013-06-12 17:36:25Z luigi $
*
* Example program to show how to build a multithreaded packet
* source/sink using the netmap device.
@@ -36,28 +38,108 @@
*
*/
-#include "nm_util.h"
+// #define TRASH_VHOST_HDR
-const char *default_payload="netmap pkt-gen payload\n"
+#define _GNU_SOURCE /* for CPU_SET() */
+#include <stdio.h>
+#define NETMAP_WITH_LIBS
+#include <net/netmap_user.h>
+
+
+#include <ctype.h> // isprint()
+#include <unistd.h> // sysconf()
+#include <sys/poll.h>
+#include <arpa/inet.h> /* ntohs */
+#include <sys/sysctl.h> /* sysctl */
+#include <ifaddrs.h> /* getifaddrs */
+#include <net/ethernet.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+
+#include <pthread.h>
+
+#ifndef NO_PCAP
+#include <pcap/pcap.h>
+#endif
+
+#ifdef linux
+
+#define cpuset_t cpu_set_t
+
+#define ifr_flagshigh ifr_flags /* only the low 16 bits here */
+#define IFF_PPROMISC IFF_PROMISC /* IFF_PPROMISC does not exist */
+#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 cpuset_t uint64_t // XXX
+static inline void CPU_ZERO(cpuset_t *p)
+{
+ *p = 0;
+}
+
+static inline void CPU_SET(uint32_t i, cpuset_t *p)
+{
+ *p |= 1<< (i & 0x3f);
+}
+
+#define pthread_setaffinity_np(a, b, c) ((void)a, 0)
+
+#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__ */
+
+const char *default_payload="netmap pkt-gen DIRECT payload\n"
"http://info.iet.unipi.it/~luigi/netmap/ ";
-int time_second; // support for RD() debugging macro
+const char *indirect_payload="netmap pkt-gen indirect payload\n"
+ "http://info.iet.unipi.it/~luigi/netmap/ ";
int verbose = 0;
-#define SKIP_PAYLOAD 1 /* do not check payload. */
+#define SKIP_PAYLOAD 1 /* do not check payload. XXX unused */
+
+#define VIRT_HDR_1 10 /* length of a base vnet-hdr */
+#define VIRT_HDR_2 12 /* length of the extenede vnet-hdr */
+#define VIRT_HDR_MAX VIRT_HDR_2
+struct virt_header {
+ uint8_t fields[VIRT_HDR_MAX];
+};
+
+#define MAX_BODYSIZE 16384
+
struct pkt {
+ struct virt_header vh;
struct ether_header eh;
struct ip ip;
struct udphdr udp;
- uint8_t body[2048]; // XXX hardwired
+ uint8_t body[MAX_BODYSIZE]; // XXX hardwired
} __attribute__((__packed__));
struct ip_range {
char *name;
- struct in_addr start, end, cur;
- uint16_t port0, port1, cur_p;
+ uint32_t start, end; /* same as struct in_addr */
+ uint16_t port0, port1;
};
struct mac_range {
@@ -65,6 +147,17 @@
struct ether_addr start, end;
};
+/* ifname can be netmap:foo-xxxx */
+#define MAX_IFNAMELEN 64 /* our buffer for ifname */
+//#define MAX_PKTSIZE 1536
+#define MAX_PKTSIZE MAX_BODYSIZE /* XXX: + IP_HDR + ETH_HDR */
+
+/* compact timestamp to fit into 60 byte packet. (enough to obtain RTT) */
+struct tstamp {
+ uint32_t sec;
+ uint32_t nsec;
+};
+
/*
* global arguments for all threads
*/
@@ -78,6 +171,7 @@
int burst;
int forever;
int npackets; /* total packets to send */
+ int frags; /* fragments per packet */
int nthreads;
int cpus;
int options; /* testing */
@@ -86,16 +180,30 @@
#define OPT_COPY 4
#define OPT_MEMCPY 8
#define OPT_TS 16 /* add a timestamp */
+#define OPT_INDIRECT 32 /* use indirect buffers, tx only */
+#define OPT_DUMP 64 /* dump rx/tx traffic */
+#define OPT_MONITOR_TX 128
+#define OPT_MONITOR_RX 256
int dev_type;
+#ifndef NO_PCAP
pcap_t *p;
+#endif
+ int tx_rate;
+ struct timespec tx_period;
+
int affinity;
int main_fd;
- int report_interval;
+ struct nm_desc *nmd;
+ int report_interval; /* milliseconds between prints */
void *(*td_body)(void *);
void *mmap_addr;
- int mmap_size;
- char *ifname;
+ char ifname[MAX_IFNAMELEN];
+ char *nmr_config;
+ int dummy_send;
+ int virt_header; /* send also the virt_header */
+ int extra_bufs; /* goes in nr_arg3 */
+ char *packet_file; /* -P option */
};
enum dev_type { DEV_NONE, DEV_NETMAP, DEV_PCAP, DEV_TAP };
@@ -110,16 +218,15 @@
int completed;
int cancel;
int fd;
- struct nmreq nmr;
- struct netmap_if *nifp;
- uint16_t qfirst, qlast; /* range of queues to scan */
+ struct nm_desc *nmd;
volatile uint64_t count;
- struct timeval tic, toc;
+ struct timespec tic, toc;
int me;
pthread_t thread;
int affinity;
struct pkt pkt;
+ void *frame;
};
@@ -130,51 +237,67 @@
static void
extract_ip_range(struct ip_range *r)
{
- char *p_lo, *p_hi;
- char buf1[16]; // one ip address
+ char *ap, *pp;
+ struct in_addr a;
- 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;
+ if (verbose)
+ D("extract IP range from %s", r->name);
+ r->port0 = r->port1 = 0;
+ r->start = r->end = 0;
+
+ /* the first - splits start/end of range */
+ ap = index(r->name, '-'); /* do we have ports ? */
+ if (ap) {
+ *ap++ = '\0';
+ }
+ /* grab the initial values (mandatory) */
+ pp = index(r->name, ':');
+ if (pp) {
+ *pp++ = '\0';
+ r->port0 = r->port1 = strtol(pp, NULL, 0);
+ };
+ inet_aton(r->name, &a);
+ r->start = r->end = ntohl(a.s_addr);
+ if (ap) {
+ pp = index(ap, ':');
+ if (pp) {
+ *pp++ = '\0';
+ if (*pp)
+ r->port1 = strtol(pp, NULL, 0);
}
- r->cur_p = r->port0;
- D("ports are %d to %d", r->port0, r->port1);
+ if (*ap) {
+ inet_aton(ap, &a);
+ r->end = ntohl(a.s_addr);
+ }
}
- 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;
+ if (r->port0 > r->port1) {
+ uint16_t tmp = r->port0;
+ r->port0 = r->port1;
+ r->port1 = tmp;
+ }
+ if (r->start > r->end) {
+ uint32_t tmp = r->start;
r->start = r->end;
- r->end = r->cur;
+ r->end = tmp;
}
- 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);
+ {
+ struct in_addr a;
+ char buf1[16]; // one ip address
+
+ a.s_addr = htonl(r->end);
+ strncpy(buf1, inet_ntoa(a), sizeof(buf1));
+ a.s_addr = htonl(r->start);
+ if (1)
+ D("range is %s:%d to %s:%d",
+ inet_ntoa(a), r->port0, buf1, r->port1);
+ }
}
static void
extract_mac_range(struct mac_range *r)
{
- D("extract MAC range from %s", r->name);
+ if (verbose)
+ 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
@@ -189,7 +312,8 @@
if (p)
targ->dst_mac_range = atoi(p+1);
#endif
- D("%s starts at %s", r->name, ether_ntoa(&r->start));
+ if (verbose)
+ D("%s starts at %s", r->name, ether_ntoa(&r->start));
}
static struct targ *targs;
@@ -202,6 +326,7 @@
int i;
(void)sig; /* UNUSED */
+ D("received control-C on thread %p", pthread_self());
for (i = 0; i < global_nthreads; i++) {
targs[i].cancel = 1;
}
@@ -212,19 +337,17 @@
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);
+ int ncpus;
+#if defined (__FreeBSD__)
+ int mib[2] = { CTL_HW, HW_NCPU };
+ size_t len = sizeof(mib);
sysctl(mib, 2, &ncpus, &len, NULL, 0);
-
+#elif defined(linux)
+ ncpus = sysconf(_SC_NPROCESSORS_ONLN);
+#else /* others */
+ ncpus = 1;
+#endif /* others */
return (ncpus);
-#else
- return 1;
-#endif /* !__FreeBSD__ */
}
#ifdef __linux__
@@ -249,6 +372,58 @@
/*
+ * parse the vale configuration in conf and put it in nmr.
+ * Return the flag set if necessary.
+ * The configuration may consist of 0 to 4 numbers separated
+ * by commas: #tx-slots,#rx-slots,#tx-rings,#rx-rings.
+ * Missing numbers or zeroes stand for default values.
+ * As an additional convenience, if exactly one number
+ * is specified, then this is assigned to both #tx-slots and #rx-slots.
+ * If there is no 4th number, then the 3rd is assigned to both #tx-rings
+ * and #rx-rings.
+ */
+int
+parse_nmr_config(const char* conf, struct nmreq *nmr)
+{
+ char *w, *tok;
+ int i, v;
+
+ nmr->nr_tx_rings = nmr->nr_rx_rings = 0;
+ nmr->nr_tx_slots = nmr->nr_rx_slots = 0;
+ if (conf == NULL || ! *conf)
+ return 0;
+ w = strdup(conf);
+ for (i = 0, tok = strtok(w, ","); tok; i++, tok = strtok(NULL, ",")) {
+ v = atoi(tok);
+ switch (i) {
+ case 0:
+ nmr->nr_tx_slots = nmr->nr_rx_slots = v;
+ break;
+ case 1:
+ nmr->nr_rx_slots = v;
+ break;
+ case 2:
+ nmr->nr_tx_rings = nmr->nr_rx_rings = v;
+ break;
+ case 3:
+ nmr->nr_rx_rings = v;
+ break;
+ default:
+ D("ignored config: %s", tok);
+ break;
+ }
+ }
+ D("txr %d txd %d rxr %d rxd %d",
+ nmr->nr_tx_rings, nmr->nr_tx_slots,
+ nmr->nr_rx_rings, nmr->nr_rx_slots);
+ free(w);
+ return (nmr->nr_tx_rings || nmr->nr_tx_slots ||
+ nmr->nr_rx_rings || nmr->nr_rx_slots) ?
+ NM_OPEN_RING_CFG : 0;
+}
+
+
+/*
* locate the src mac address for our interface, put it
* into the user-supplied buffer. return 0 if ok, -1 on error.
*/
@@ -289,7 +464,6 @@
static int
setaffinity(pthread_t me, int i)
{
-#ifdef __FreeBSD__
cpuset_t cpumask;
if (i == -1)
@@ -300,13 +474,9 @@
CPU_SET(i, &cpumask);
if (pthread_setaffinity_np(me, sizeof(cpuset_t), &cpumask) != 0) {
- D("Unable to set affinity");
+ D("Unable to set affinity: %s", strerror(errno));
return 1;
}
-#else
- (void)me; /* suppress 'unused' warnings */
- (void)i;
-#endif /* __FreeBSD__ */
return 0;
}
@@ -343,6 +513,35 @@
return (htons(sum));
}
+/* 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
+dump_payload(char *p, int len, struct netmap_ring *ring, int cur)
+{
+ char buf[128];
+ int i, j, i0;
+
+ /* get the length in ASCII of the length of the packet. */
+
+ printf("ring %p cur %5d [buf %6d flags 0x%04x len %5d]\n",
+ ring, cur, ring->slot[cur].buf_idx,
+ ring->slot[cur].flags, len);
+ /* hexdump routine */
+ for (i = 0; i < len; ) {
+ memset(buf, sizeof(buf), ' ');
+ sprintf(buf, "%5d: ", i);
+ i0 = i;
+ for (j=0; j < 16 && i < len; i++, j++)
+ sprintf(buf+7+j*3, "%02x ", (uint8_t)(p[i]));
+ i = i0;
+ for (j=0; j < 16 && i < len; i++, j++)
+ sprintf(buf+7+j + 48, "%c",
+ isprint(p[i]) ? p[i] : '.');
+ printf("%s\n", buf);
+ }
+}
+
/*
* Fill a packet with some payload.
* We create a UDP packet so the payload starts at
@@ -354,7 +553,58 @@
#define uh_ulen len
#define uh_sum check
#endif /* linux */
+
+/*
+ * increment the addressed in the packet,
+ * starting from the least significant field.
+ * DST_IP DST_PORT SRC_IP SRC_PORT
+ */
static void
+update_addresses(struct pkt *pkt, struct glob_arg *g)
+{
+ uint32_t a;
+ uint16_t p;
+ struct ip *ip = &pkt->ip;
+ struct udphdr *udp = &pkt->udp;
+
+ do {
+ p = ntohs(udp->uh_sport);
+ if (p < g->src_ip.port1) { /* just inc, no wrap */
+ udp->uh_sport = htons(p + 1);
+ break;
+ }
+ udp->uh_sport = htons(g->src_ip.port0);
+
+ a = ntohl(ip->ip_src.s_addr);
+ if (a < g->src_ip.end) { /* just inc, no wrap */
+ ip->ip_src.s_addr = htonl(a + 1);
+ break;
+ }
+ ip->ip_src.s_addr = htonl(g->src_ip.start);
+
+ udp->uh_sport = htons(g->src_ip.port0);
+ p = ntohs(udp->uh_dport);
+ if (p < g->dst_ip.port1) { /* just inc, no wrap */
+ udp->uh_dport = htons(p + 1);
+ break;
+ }
+ udp->uh_dport = htons(g->dst_ip.port0);
+
+ a = ntohl(ip->ip_dst.s_addr);
+ if (a < g->dst_ip.end) { /* just inc, no wrap */
+ ip->ip_dst.s_addr = htonl(a + 1);
+ break;
+ }
+ ip->ip_dst.s_addr = htonl(g->dst_ip.start);
+ } while (0);
+ // update checksum
+}
+
+/*
+ * initialize one packet and prepare for the next one.
+ * The copy could be done better instead of repeating it each time.
+ */
+static void
initialize_packet(struct targ *targ)
{
struct pkt *pkt = &targ->pkt;
@@ -362,16 +612,42 @@
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);
+ const char *payload = targ->g->options & OPT_INDIRECT ?
+ indirect_payload : default_payload;
+ int i, l0 = strlen(payload);
- for (i = 0; i < paylen;) {
- l = min(l0, paylen - i);
- bcopy(default_payload, pkt->body + i, l);
- i += l;
+ char errbuf[PCAP_ERRBUF_SIZE];
+ pcap_t *file;
+ struct pcap_pkthdr *header;
+ const unsigned char *packet;
+
+ /* Read a packet from a PCAP file if asked. */
+ if (targ->g->packet_file != NULL) {
+ if ((file = pcap_open_offline(targ->g->packet_file,
+ errbuf)) == NULL)
+ D("failed to open pcap file %s",
+ targ->g->packet_file);
+ if (pcap_next_ex(file, &header, &packet) < 0)
+ D("failed to read packet from %s",
+ targ->g->packet_file);
+ if ((targ->frame = malloc(header->caplen)) == NULL)
+ D("out of memory");
+ bcopy(packet, (unsigned char *)targ->frame, header->caplen);
+ targ->g->pkt_size = header->caplen;
+ pcap_close(file);
+ return;
}
+
+ /* create a nice NUL-terminated string */
+ for (i = 0; i < paylen; i += l0) {
+ if (l0 > paylen - i)
+ l0 = paylen - i; // last round
+ bcopy(payload, pkt->body + i, l0);
+ }
pkt->body[i-1] = '\0';
ip = &pkt->ip;
+ /* prepare the headers */
ip->ip_v = IPVERSION;
ip->ip_hl = 5;
ip->ip_id = 0;
@@ -381,22 +657,14 @@
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_dst.s_addr = htonl(targ->g->dst_ip.start);
+ ip->ip_src.s_addr = htonl(targ->g->src_ip.start);
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_sport = htons(targ->g->src_ip.port0);
+ udp->uh_dport = htons(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),
@@ -412,32 +680,37 @@
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);
+
+ bzero(&pkt->vh, sizeof(pkt->vh));
+#ifdef TRASH_VHOST_HDR
+ /* set bogus content */
+ pkt->vh.fields[0] = 0xff;
+ pkt->vh.fields[1] = 0xff;
+ pkt->vh.fields[2] = 0xff;
+ pkt->vh.fields[3] = 0xff;
+ pkt->vh.fields[4] = 0xff;
+ pkt->vh.fields[5] = 0xff;
+#endif /* TRASH_VHOST_HDR */
+ // dump_payload((void *)pkt, targ->g->pkt_size, NULL, 0);
}
-/* 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)
+set_vnet_hdr_len(struct targ *t)
{
- char temp[64];
- int n_read, size, sizelen;
+ int err, l = t->g->virt_header;
+ struct nmreq req;
- /* get the length in ASCII of the length of the packet. */
- sizelen = sprintf(temp, "%d", psize) + 1; // include a whitespace
+ if (l == 0)
+ return;
- /* 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;
+ memset(&req, 0, sizeof(req));
+ bcopy(t->nmd->req.nr_name, req.nr_name, sizeof(req.nr_name));
+ req.nr_version = NETMAP_API;
+ req.nr_cmd = NETMAP_BDG_VNET_HDR;
+ req.nr_arg1 = l;
+ err = ioctl(t->fd, NIOCREGIF, &req);
+ if (err) {
+ D("Unable to set vnet header length %d", l);
}
}
@@ -448,14 +721,20 @@
* an interrupt when done.
*/
static int
-send_packets(struct netmap_ring *ring, struct pkt *pkt,
- int size, u_int count, int options)
+send_packets(struct netmap_ring *ring, struct pkt *pkt, void *frame,
+ int size, struct glob_arg *g, u_int count, int options,
+ u_int nfrags)
{
- u_int sent, cur = ring->cur;
+ u_int n, sent, cur = ring->cur;
+ u_int fcnt;
- if (ring->avail < count)
- count = ring->avail;
-
+ n = nm_ring_space(ring);
+ if (n < count)
+ count = n;
+ if (count < nfrags) {
+ D("truncating packet, no room for frags %d %d",
+ count, nfrags);
+ }
#if 0
if (options & (OPT_COPY | OPT_PREFETCH) ) {
for (sent = 0; sent < count; sent++) {
@@ -462,29 +741,45 @@
struct netmap_slot *slot = &ring->slot[cur];
char *p = NETMAP_BUF(ring, slot->buf_idx);
- prefetch(p);
- cur = NETMAP_RING_NEXT(ring, cur);
+ __builtin_prefetch(p);
+ cur = nm_ring_next(ring, cur);
}
cur = ring->cur;
}
#endif
- for (sent = 0; sent < count; sent++) {
+ for (fcnt = nfrags, 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->flags = 0;
+ if (options & OPT_INDIRECT) {
+ slot->flags |= NS_INDIRECT;
+ slot->ptr = (uint64_t)frame;
+ } else if (options & OPT_COPY) {
+ nm_pkt_copy(frame, p, size);
+ if (fcnt == nfrags)
+ update_addresses(pkt, g);
+ } else if (options & OPT_MEMCPY) {
+ memcpy(p, frame, size);
+ if (fcnt == nfrags)
+ update_addresses(pkt, g);
+ } else if (options & OPT_PREFETCH) {
+ __builtin_prefetch(p);
+ }
+ if (options & OPT_DUMP)
+ dump_payload(p, size, ring, cur);
slot->len = size;
- if (sent == count - 1)
+ if (--fcnt > 0)
+ slot->flags |= NS_MOREFRAG;
+ else
+ fcnt = nfrags;
+ if (sent == count - 1) {
+ slot->flags &= ~NS_MOREFRAG;
slot->flags |= NS_REPORT;
- cur = NETMAP_RING_NEXT(ring, cur);
+ }
+ cur = nm_ring_next(ring, cur);
}
- ring->avail -= sent;
- ring->cur = cur;
+ ring->head = ring->cur = cur;
return (sent);
}
@@ -500,16 +795,19 @@
pinger_body(void *data)
{
struct targ *targ = (struct targ *) data;
- struct pollfd fds[1];
- struct netmap_if *nifp = targ->nifp;
+ struct pollfd pfd = { .fd = targ->fd, .events = POLLIN };
+ struct netmap_if *nifp = targ->nmd->nifp;
int i, rx = 0, n = targ->g->npackets;
-
- fds[0].fd = targ->fd;
- fds[0].events = (POLLIN);
- static uint32_t sent;
+ void *frame;
+ int size;
+ uint32_t sent = 0;
struct timespec ts, now, last_print;
uint32_t count = 0, min = 1000000000, av = 0;
+ frame = &targ->pkt;
+ frame += sizeof(targ->pkt.vh) - targ->g->virt_header;
+ size = targ->g->pkt_size + targ->g->virt_header;
+
if (targ->g->nthreads > 1) {
D("can only ping with 1 thread");
return NULL;
@@ -516,43 +814,51 @@
}
clock_gettime(CLOCK_REALTIME_PRECISE, &last_print);
+ now = 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++) {
+ for (i = 0; i < 1; i++) { /* XXX why the loop for 1 pkt ? */
slot = &ring->slot[ring->cur];
- slot->len = targ->g->pkt_size;
+ slot->len = size;
p = NETMAP_BUF(ring, slot->buf_idx);
- if (ring->avail == 0) {
+ if (nm_ring_empty(ring)) {
D("-- ouch, cannot send");
} else {
- pkt_copy(&targ->pkt, p, targ->g->pkt_size);
+ struct tstamp *tp;
+ nm_pkt_copy(frame, p, size);
clock_gettime(CLOCK_REALTIME_PRECISE, &ts);
bcopy(&sent, p+42, sizeof(sent));
- bcopy(&ts, p+46, sizeof(ts));
+ tp = (struct tstamp *)(p+46);
+ tp->sec = (uint32_t)ts.tv_sec;
+ tp->nsec = (uint32_t)ts.tv_nsec;
sent++;
- ring->cur = NETMAP_RING_NEXT(ring, ring->cur);
- ring->avail--;
+ ring->head = ring->cur = nm_ring_next(ring, ring->cur);
}
}
/* 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);
+ if (poll(&pfd, 1, 3000) <= 0) {
+ D("poll error/timeout on queue %d: %s", targ->me,
+ strerror(errno));
continue;
}
/* see what we got back */
- for (i = targ->qfirst; i < targ->qlast; i++) {
+ for (i = targ->nmd->first_tx_ring;
+ i <= targ->nmd->last_tx_ring; i++) {
ring = NETMAP_RXRING(nifp, i);
- while (ring->avail > 0) {
+ while (!nm_ring_empty(ring)) {
uint32_t seq;
+ struct tstamp *tp;
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));
+ tp = (struct tstamp *)(p+46);
+ ts.tv_sec = (time_t)tp->sec;
+ ts.tv_nsec = (long)tp->nsec;
ts.tv_sec = now.tv_sec - ts.tv_sec;
ts.tv_nsec = now.tv_nsec - ts.tv_nsec;
if (ts.tv_nsec < 0) {
@@ -565,8 +871,7 @@
min = ts.tv_nsec;
count ++;
av += ts.tv_nsec;
- ring->avail--;
- ring->cur = NETMAP_RING_NEXT(ring, ring->cur);
+ ring->head = ring->cur = nm_ring_next(ring, ring->cur);
rx++;
}
}
@@ -598,12 +903,10 @@
ponger_body(void *data)
{
struct targ *targ = (struct targ *) data;
- struct pollfd fds[1];
- struct netmap_if *nifp = targ->nifp;
+ struct pollfd pfd = { .fd = targ->fd, .events = POLLIN };
+ struct netmap_if *nifp = targ->nmd->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");
@@ -614,20 +917,21 @@
uint32_t txcur, txavail;
//#define BUSYWAIT
#ifdef BUSYWAIT
- ioctl(fds[0].fd, NIOCRXSYNC, NULL);
+ ioctl(pfd.fd, NIOCRXSYNC, NULL);
#else
- if (poll(fds, 1, 1000) <= 0) {
- D("poll error/timeout on queue %d", targ->me);
+ if (poll(&pfd, 1, 1000) <= 0) {
+ D("poll error/timeout on queue %d: %s", targ->me,
+ strerror(errno));
continue;
}
#endif
txring = NETMAP_TXRING(nifp, 0);
txcur = txring->cur;
- txavail = txring->avail;
+ txavail = nm_ring_space(txring);
/* see what we got back */
- for (i = targ->qfirst; i < targ->qlast; i++) {
+ for (i = targ->nmd->first_rx_ring; i <= targ->nmd->last_rx_ring; i++) {
rxring = NETMAP_RXRING(nifp, i);
- while (rxring->avail > 0) {
+ while (!nm_ring_empty(rxring)) {
uint16_t *spkt, *dpkt;
uint32_t cur = rxring->cur;
struct netmap_slot *slot = &rxring->slot[cur];
@@ -634,8 +938,7 @@
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);
+ rxring->head = rxring->cur = nm_ring_next(rxring, cur);
rx++;
if (txavail == 0)
continue;
@@ -644,7 +947,7 @@
/* copy... */
dpkt = (uint16_t *)dst;
spkt = (uint16_t *)src;
- pkt_copy(src, dst, slot->len);
+ nm_pkt_copy(src, dst, slot->len);
dpkt[0] = spkt[3];
dpkt[1] = spkt[4];
dpkt[2] = spkt[5];
@@ -653,16 +956,15 @@
dpkt[5] = spkt[2];
txring->slot[txcur].len = slot->len;
/* XXX swap src dst mac */
- txcur = NETMAP_RING_NEXT(txring, txcur);
+ txcur = nm_ring_next(txring, txcur);
txavail--;
sent++;
}
}
- txring->cur = txcur;
- txring->avail = txavail;
+ txring->head = txring->cur = txcur;
targ->count = sent;
#ifdef BUSYWAIT
- ioctl(fds[0].fd, NIOCTXSYNC, NULL);
+ ioctl(pfd.fd, NIOCTXSYNC, NULL);
#endif
//D("tx %d rx %d", sent, rx);
}
@@ -669,64 +971,169 @@
return NULL;
}
+static __inline int
+timespec_ge(const struct timespec *a, const struct timespec *b)
+{
+ if (a->tv_sec > b->tv_sec)
+ return (1);
+ if (a->tv_sec < b->tv_sec)
+ return (0);
+ if (a->tv_nsec >= b->tv_nsec)
+ return (1);
+ return (0);
+}
+
+static __inline struct timespec
+timeval2spec(const struct timeval *a)
+{
+ struct timespec ts = {
+ .tv_sec = a->tv_sec,
+ .tv_nsec = a->tv_usec * 1000
+ };
+ return ts;
+}
+
+static __inline struct timeval
+timespec2val(const struct timespec *a)
+{
+ struct timeval tv = {
+ .tv_sec = a->tv_sec,
+ .tv_usec = a->tv_nsec / 1000
+ };
+ return tv;
+}
+
+
+static __inline struct timespec
+timespec_add(struct timespec a, struct timespec b)
+{
+ struct timespec ret = { a.tv_sec + b.tv_sec, a.tv_nsec + b.tv_nsec };
+ if (ret.tv_nsec >= 1000000000) {
+ ret.tv_sec++;
+ ret.tv_nsec -= 1000000000;
+ }
+ return ret;
+}
+
+static __inline struct timespec
+timespec_sub(struct timespec a, struct timespec b)
+{
+ struct timespec ret = { a.tv_sec - b.tv_sec, a.tv_nsec - b.tv_nsec };
+ if (ret.tv_nsec < 0) {
+ ret.tv_sec--;
+ ret.tv_nsec += 1000000000;
+ }
+ return ret;
+}
+
+
+/*
+ * wait until ts, either busy or sleeping if more than 1ms.
+ * Return wakeup time.
+ */
+static struct timespec
+wait_time(struct timespec ts)
+{
+ for (;;) {
+ struct timespec w, cur;
+ clock_gettime(CLOCK_REALTIME_PRECISE, &cur);
+ w = timespec_sub(ts, cur);
+ if (w.tv_sec < 0)
+ return cur;
+ else if (w.tv_sec > 0 || w.tv_nsec > 1000000)
+ poll(NULL, 0, 1);
+ }
+}
+
static void *
sender_body(void *data)
{
struct targ *targ = (struct targ *) data;
-
- struct pollfd fds[1];
- struct netmap_if *nifp = targ->nifp;
+ struct pollfd pfd = { .fd = targ->fd, .events = POLLOUT };
+ struct netmap_if *nifp;
struct netmap_ring *txring;
- int i, n = targ->g->npackets / targ->g->nthreads, sent = 0;
+ int i, n = targ->g->npackets / targ->g->nthreads;
+ int64_t sent = 0;
int options = targ->g->options | OPT_COPY;
-D("start");
+ struct timespec nexttime = { 0, 0}; // XXX silence compiler
+ int rate_limit = targ->g->tx_rate;
+ struct pkt *pkt = &targ->pkt;
+ void *frame;
+ int size;
+
+ if (targ->frame == NULL) {
+ frame = pkt;
+ frame += sizeof(pkt->vh) - targ->g->virt_header;
+ size = targ->g->pkt_size + targ->g->virt_header;
+ } else {
+ frame = targ->frame;
+ size = targ->g->pkt_size;
+ }
+
+ D("start, fd %d main_fd %d", targ->fd, targ->g->main_fd);
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);
+ clock_gettime(CLOCK_REALTIME_PRECISE, &targ->tic);
+ if (rate_limit) {
+ targ->tic = timespec_add(targ->tic, (struct timespec){2,0});
+ targ->tic.tv_nsec = 0;
+ wait_time(targ->tic);
+ nexttime = targ->tic;
+ }
+ if (targ->g->dev_type == DEV_TAP) {
+ D("writing to file desc %d", targ->g->main_fd);
- 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)
+ if (write(targ->g->main_fd, frame, size) != -1)
sent++;
+ update_addresses(pkt, targ->g);
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);
+#ifndef NO_PCAP
+ } else if (targ->g->dev_type == DEV_PCAP) {
+ pcap_t *p = targ->g->p;
for (i = 0; !targ->cancel && (n == 0 || sent < n); i++) {
- if (write(targ->g->main_fd, pkt, size) != -1)
+ if (pcap_inject(p, frame, size) != -1)
sent++;
+ update_addresses(pkt, targ->g);
if (i > 10000) {
targ->count = sent;
i = 0;
}
}
+#endif /* NO_PCAP */
} else {
+ int tosend = 0;
+ int frags = targ->g->frags;
+
+ nifp = targ->nmd->nifp;
while (!targ->cancel && (n == 0 || sent < n)) {
+ if (rate_limit && tosend <= 0) {
+ tosend = targ->g->burst;
+ nexttime = timespec_add(nexttime, targ->g->tx_period);
+ wait_time(nexttime);
+ }
+
/*
* wait for available room in the send queue(s)
*/
- if (poll(fds, 1, 2000) <= 0) {
+ if (poll(&pfd, 1, 2000) <= 0) {
if (targ->cancel)
break;
- D("poll error/timeout on queue %d", targ->me);
+ D("poll error/timeout on queue %d: %s", targ->me,
+ strerror(errno));
+ // goto quit;
+ }
+ if (pfd.revents & POLLERR) {
+ D("poll error");
goto quit;
}
/*
@@ -736,33 +1143,48 @@
D("drop copy");
options &= ~OPT_COPY;
}
- for (i = targ->qfirst; i < targ->qlast; i++) {
- int m, limit = targ->g->burst;
+ for (i = targ->nmd->first_tx_ring; i <= targ->nmd->last_tx_ring; i++) {
+ int m, limit = rate_limit ? tosend : targ->g->burst;
if (n > 0 && n - sent < limit)
limit = n - sent;
txring = NETMAP_TXRING(nifp, i);
- if (txring->avail == 0)
+ if (nm_ring_empty(txring))
continue;
- m = send_packets(txring, &targ->pkt, targ->g->pkt_size,
- limit, options);
+ if (frags > 1)
+ limit = ((limit + frags - 1) / frags) * frags;
+
+ m = send_packets(txring, pkt, frame, size, targ->g,
+ limit, options, frags);
+ ND("limit %d tail %d frags %d m %d",
+ limit, txring->tail, frags, m);
sent += m;
targ->count = sent;
+ if (rate_limit) {
+ tosend -= m;
+ if (tosend <= 0)
+ break;
+ }
}
}
/* flush any remaining packets */
- ioctl(fds[0].fd, NIOCTXSYNC, NULL);
+ D("flush tail %d head %d on thread %p",
+ txring->tail, txring->head,
+ pthread_self());
+ ioctl(pfd.fd, NIOCTXSYNC, NULL);
/* final part: wait all the TX queues to be empty. */
- for (i = targ->qfirst; i < targ->qlast; i++) {
+ for (i = targ->nmd->first_tx_ring; i <= targ->nmd->last_tx_ring; i++) {
txring = NETMAP_TXRING(nifp, i);
- while (!NETMAP_TX_RING_EMPTY(txring)) {
- ioctl(fds[0].fd, NIOCTXSYNC, NULL);
+ while (nm_tx_pending(txring)) {
+ RD(5, "pending tx tail %d head %d on ring %d",
+ txring->tail, txring->head, i);
+ ioctl(pfd.fd, NIOCTXSYNC, NULL);
usleep(1); /* wait 1 tick */
}
}
- }
+ } /* end DEV_NETMAP */
- gettimeofday(&targ->toc, NULL);
+ clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc);
targ->completed = 1;
targ->count = sent;
@@ -774,6 +1196,7 @@
}
+#ifndef NO_PCAP
static void
receive_pcap(u_char *user, const struct pcap_pkthdr * h,
const u_char * bytes)
@@ -783,26 +1206,27 @@
(void)bytes; /* UNUSED */
(*count)++;
}
+#endif /* !NO_PCAP */
static int
-receive_packets(struct netmap_ring *ring, u_int limit, int skip_payload)
+receive_packets(struct netmap_ring *ring, u_int limit, int dump)
{
- u_int cur, rx;
+ u_int cur, rx, n;
cur = ring->cur;
- if (ring->avail < limit)
- limit = ring->avail;
+ n = nm_ring_space(ring);
+ if (n < limit)
+ limit = n;
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);
+ if (dump)
+ dump_payload(p, slot->len, ring, cur);
- cur = NETMAP_RING_NEXT(ring, cur);
+ cur = nm_ring_next(ring, cur);
}
- ring->avail -= rx;
- ring->cur = cur;
+ ring->head = ring->cur = cur;
return (rx);
}
@@ -811,8 +1235,8 @@
receiver_body(void *data)
{
struct targ *targ = (struct targ *) data;
- struct pollfd fds[1];
- struct netmap_if *nifp = targ->nifp;
+ struct pollfd pfd = { .fd = targ->fd, .events = POLLIN };
+ struct netmap_if *nifp;
struct netmap_ring *rxring;
int i;
uint64_t received = 0;
@@ -820,62 +1244,68 @@
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);
-
+ D("reading from %s fd %d main_fd %d",
+ targ->g->ifname, targ->fd, targ->g->main_fd);
/* unbounded wait for the first packet. */
- for (;;) {
- i = poll(fds, 1, 1000);
- if (i > 0 && !(fds[0].revents & POLLERR))
+ for (;!targ->cancel;) {
+ i = poll(&pfd, 1, 1000);
+ if (i > 0 && !(pfd.revents & POLLERR))
break;
- D("waiting for initial packets, poll returns %d %d", i, fds[0].revents);
+ RD(1, "waiting for initial packets, poll returns %d %d",
+ i, pfd.revents);
}
-
/* main loop, exit after 1s silence */
- gettimeofday(&targ->tic, NULL);
- if (targ->g->dev_type == DEV_PCAP) {
+ clock_gettime(CLOCK_REALTIME_PRECISE, &targ->tic);
+ if (targ->g->dev_type == DEV_TAP) {
while (!targ->cancel) {
+ char buf[MAX_BODYSIZE];
/* XXX should we poll ? */
- pcap_dispatch(targ->g->p, targ->g->burst, receive_pcap, NULL);
+ if (read(targ->g->main_fd, buf, sizeof(buf)) > 0)
+ targ->count++;
}
- } else if (targ->g->dev_type == DEV_TAP) {
- D("reading from %s fd %d", targ->g->ifname, targ->g->main_fd);
+#ifndef NO_PCAP
+ } else if (targ->g->dev_type == DEV_PCAP) {
while (!targ->cancel) {
- char buf[2048];
/* XXX should we poll ? */
- if (read(targ->g->main_fd, buf, sizeof(buf)) > 0)
- targ->count++;
+ pcap_dispatch(targ->g->p, targ->g->burst, receive_pcap,
+ (u_char *)&targ->count);
}
+#endif /* !NO_PCAP */
} else {
+ int dump = targ->g->options & OPT_DUMP;
+
+ nifp = targ->nmd->nifp;
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);
+ if (poll(&pfd, 1, 1 * 1000) <= 0 && !targ->g->forever) {
+ clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc);
targ->toc.tv_sec -= 1; /* Subtract timeout time. */
- break;
+ goto out;
}
- for (i = targ->qfirst; i < targ->qlast; i++) {
+ if (pfd.revents & POLLERR) {
+ D("poll err");
+ goto quit;
+ }
+
+ for (i = targ->nmd->first_rx_ring; i <= targ->nmd->last_rx_ring; i++) {
int m;
rxring = NETMAP_RXRING(nifp, i);
- if (rxring->avail == 0)
+ if (nm_ring_empty(rxring))
continue;
- m = receive_packets(rxring, targ->g->burst,
- SKIP_PAYLOAD);
+ m = receive_packets(rxring, targ->g->burst, dump);
received += m;
}
targ->count = received;
-
- // tell the card we have read the data
- //ioctl(fds[0].fd, NIOCRXSYNC, NULL);
}
}
+ clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc);
+
+out:
targ->completed = 1;
targ->count = received;
@@ -892,10 +1322,10 @@
static const char *
norm(char *buf, double val)
{
- char *units[] = { "", "K", "M", "G" };
+ char *units[] = { "", "K", "M", "G", "T" };
u_int i;
- for (i = 0; val >=1000 && i < sizeof(units)/sizeof(char *); i++)
+ for (i = 0; val >=1000 && i < sizeof(units)/sizeof(char *) - 1; i++)
val /= 1000;
sprintf(buf, "%.2f %s", val, units[i]);
return buf;
@@ -907,8 +1337,8 @@
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);
+ printf("Sent %llu packets, %d bytes each, in %.2f seconds.\n",
+ (unsigned long long)sent, size, delta);
if (delta == 0)
delta = 1e-6;
if (size < 60) /* correct for min packet size */
@@ -929,7 +1359,8 @@
double pps;
char b1[40];
- printf("Received %" PRIu64 " packets, in %.2f seconds.\n", received, delta);
+ printf("Received %llu packets, in %.2f seconds.\n",
+ (unsigned long long) received, delta);
if (delta == 0)
delta = 1e-6;
@@ -949,18 +1380,22 @@
"\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-l pkt_size in bytes excluding CRC\n"
+ "\t-d dst_ip[:port[-dst_ip:port]] single or range\n"
+ "\t-s src_ip[:port[-src_ip:port]] single or range\n"
+ "\t-D dst-mac\n"
+ "\t-S src-mac\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-P use libpcap instead of netmap\n"
"\t-w wait_for_link_time in seconds\n"
+ "\t-R rate in packets per second\n"
+ "\t-X dump payload\n"
+ "\t-H len add empty virtio-net-header with size 'len'\n"
+ "\t-P file load packet from pcap file"
"",
cmd);
@@ -978,65 +1413,64 @@
* 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;
+ struct targ *t = &targs[i];
+ bzero(t, sizeof(*t));
+ t->fd = -1; /* default, with pcap */
+ t->g = g;
+
if (g->dev_type == DEV_NETMAP) {
- struct nmreq tifreq;
- int tfd;
+ struct nm_desc nmd = *g->nmd; /* copy, we overwrite ringid */
+ uint64_t nmd_flags = 0;
+ nmd.self = &nmd;
- /* register interface. */
- tfd = open("/dev/netmap", O_RDWR);
- if (tfd == -1) {
- D("Unable to open /dev/netmap");
- continue;
+ if (g->nthreads > 1) {
+ if (nmd.req.nr_flags != NR_REG_ALL_NIC) {
+ D("invalid nthreads mode %d", nmd.req.nr_flags);
+ continue;
+ }
+ nmd.req.nr_flags = NR_REG_ONE_NIC;
+ nmd.req.nr_ringid = i;
}
- targs[i].fd = tfd;
+ /* Only touch one of the rings (rx is already ok) */
+ if (g->td_body == receiver_body)
+ nmd_flags |= NETMAP_NO_TX_POLL;
- 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;
+ /* register interface. Override ifname and ringid etc. */
+ if (g->options & OPT_MONITOR_TX)
+ nmd.req.nr_flags |= NR_MONITOR_TX;
+ if (g->options & OPT_MONITOR_RX)
+ nmd.req.nr_flags |= NR_MONITOR_RX;
- /*
- * 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;
+ t->nmd = nm_open(t->g->ifname, NULL, nmd_flags |
+ NM_OPEN_IFNAME | NM_OPEN_NO_MMAP, &nmd);
+ if (t->nmd == NULL) {
+ D("Unable to open %s: %s",
+ t->g->ifname, strerror(errno));
+ continue;
}
+ t->fd = t->nmd->fd;
+ set_vnet_hdr_len(t);
- 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;
+ t->used = 1;
+ t->me = i;
if (g->affinity >= 0) {
if (g->affinity < g->cpus)
- targs[i].affinity = g->affinity;
+ t->affinity = g->affinity;
else
- targs[i].affinity = i % g->cpus;
- } else
- targs[i].affinity = -1;
+ t->affinity = i % g->cpus;
+ } else {
+ t->affinity = -1;
+ }
/* default, init packets */
- initialize_packet(&targs[i]);
+ initialize_packet(t);
- if (pthread_create(&targs[i].thread, NULL, g->td_body,
- &targs[i]) == -1) {
- D("Unable to create thread %d", i);
- targs[i].used = 0;
+ if (pthread_create(&t->thread, NULL, g->td_body, t) == -1) {
+ D("Unable to create thread %d: %s", i, strerror(errno));
+ t->used = 0;
}
}
}
@@ -1061,7 +1495,6 @@
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++) {
@@ -1074,8 +1507,10 @@
continue;
npkts = my_count - prev;
pps = (npkts*1000000 + usec/2) / usec;
- D("%" PRIu64 " pps (%" PRIu64 " pkts in %" PRIu64 " usec)",
- pps, npkts, usec);
+ D("%llu pps (%llu pkts in %llu usec)",
+ (unsigned long long)pps,
+ (unsigned long long)npkts,
+ (unsigned long long)usec);
prev = my_count;
toc = now;
if (done == g->nthreads)
@@ -1085,11 +1520,13 @@
timerclear(&tic);
timerclear(&toc);
for (i = 0; i < g->nthreads; i++) {
+ struct timespec t_tic, t_toc;
/*
* Join active threads, unregister interfaces and close
* file descriptors.
*/
- pthread_join(targs[i].thread, NULL);
+ if (targs[i].used)
+ pthread_join(targs[i].thread, NULL);
close(targs[i].fd);
if (targs[i].completed == 0)
@@ -1100,10 +1537,12 @@
* 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;
+ t_tic = timeval2spec(&tic);
+ t_toc = timeval2spec(&toc);
+ if (!timerisset(&tic) || timespec_ge(&targs[i].tic, &t_tic))
+ tic = timespec2val(&targs[i].tic);
+ if (!timerisset(&toc) || timespec_ge(&targs[i].toc, &t_toc))
+ toc = timespec2val(&targs[i].toc);
}
/* print output. */
@@ -1115,8 +1554,7 @@
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);
+ munmap(g->nmd->mem, g->nmd->req.nr_memsize);
close(g->main_fd);
}
}
@@ -1179,7 +1617,7 @@
/* try to create the device */
if( (err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0 ) {
- D("failed to to a TUNSETIFF");
+ D("failed to to a TUNSETIFF: %s", strerror(errno));
close(fd);
return err;
}
@@ -1204,7 +1642,6 @@
struct glob_arg g;
- struct nmreq nmr;
int ch;
int wait_link = 2;
int devqueues = 1; /* how many device queues */
@@ -1224,9 +1661,14 @@
g.burst = 512; // default
g.nthreads = 1;
g.cpus = 1;
+ g.forever = 1;
+ g.tx_rate = 0;
+ g.frags = 1;
+ g.nmr_config = "";
+ g.virt_header = 0;
while ( (ch = getopt(arc, argv,
- "a:f:n:i:t:r:l:d:s:D:S:b:c:o:p:PT:w:Wv")) != -1) {
+ "a:f:F:n:i:Il:d:s:D:S:b:c:o:p:T:w:WvR:XC:H:e:m:P:")) != -1) {
struct sf *fn;
switch(ch) {
@@ -1239,6 +1681,15 @@
g.npackets = atoi(optarg);
break;
+ case 'F':
+ i = atoi(optarg);
+ if (i < 1 || i > 63) {
+ D("invalid frags %d [1..63], ignore", i);
+ break;
+ }
+ g.frags = i;
+ break;
+
case 'f':
for (fn = func; fn->key; fn++) {
if (!strcmp(fn->key, optarg))
@@ -1259,25 +1710,39 @@
break;
case 'i': /* interface */
- g.ifname = optarg;
- if (!strncmp(optarg, "tap", 3))
+ /* a prefix of tap: netmap: or pcap: forces the mode.
+ * otherwise we guess
+ */
+ D("interface is %s", optarg);
+ if (strlen(optarg) > MAX_IFNAMELEN - 8) {
+ D("ifname too long %s", optarg);
+ break;
+ }
+ strcpy(g.ifname, optarg);
+ if (!strcmp(optarg, "null")) {
+ g.dev_type = DEV_NETMAP;
+ g.dummy_send = 1;
+ } else if (!strncmp(optarg, "tap:", 4)) {
g.dev_type = DEV_TAP;
- else
+ strcpy(g.ifname, optarg + 4);
+ } else if (!strncmp(optarg, "pcap:", 5)) {
+ g.dev_type = DEV_PCAP;
+ strcpy(g.ifname, optarg + 5);
+ } else if (!strncmp(optarg, "netmap:", 7) ||
+ !strncmp(optarg, "vale", 4)) {
g.dev_type = DEV_NETMAP;
+ } else if (!strncmp(optarg, "tap", 3)) {
+ g.dev_type = DEV_TAP;
+ } else { /* prepend netmap: */
+ g.dev_type = DEV_NETMAP;
+ sprintf(g.ifname, "netmap:%s", optarg);
+ }
break;
- case 't': /* send, deprecated */
- D("-t deprecated, please use -f tx -n %s", optarg);
- g.td_body = sender_body;
- g.npackets = atoi(optarg);
+ case 'I':
+ g.options |= OPT_INDIRECT; /* XXX use indirect buffer */
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;
@@ -1298,8 +1763,8 @@
wait_link = atoi(optarg);
break;
- case 'W':
- g.forever = 1; /* do not exit rx even with no traffic */
+ case 'W': /* XXX changed default */
+ g.forever = 0; /* do not exit rx even with no traffic */
break;
case 'b': /* burst */
@@ -1312,10 +1777,6 @@
g.nthreads = atoi(optarg);
break;
- case 'P':
- g.dev_type = DEV_PCAP;
- break;
-
case 'D': /* destination mac */
g.dst_mac.name = optarg;
break;
@@ -1325,7 +1786,36 @@
break;
case 'v':
verbose++;
+ break;
+ case 'R':
+ g.tx_rate = atoi(optarg);
+ break;
+ case 'X':
+ g.options |= OPT_DUMP;
+ break;
+ case 'C':
+ g.nmr_config = strdup(optarg);
+ break;
+ case 'H':
+ g.virt_header = atoi(optarg);
+ break;
+ case 'e': /* extra bufs */
+ g.extra_bufs = atoi(optarg);
+ break;
+ case 'm':
+ if (strcmp(optarg, "tx") == 0) {
+ g.options |= OPT_MONITOR_TX;
+ } else if (strcmp(optarg, "rx") == 0) {
+ g.options |= OPT_MONITOR_RX;
+ } else {
+ D("unrecognized monitor mode %s", optarg);
+ }
+ break;
+ case 'P':
+ g.packet_file = strdup(optarg);
+ break;
}
+
}
if (g.ifname == NULL) {
@@ -1341,8 +1831,8 @@
if (g.cpus == 0)
g.cpus = i;
- if (g.pkt_size < 16 || g.pkt_size > 1536) {
- D("bad pktsize %d\n", g.pkt_size);
+ if (g.pkt_size < 16 || g.pkt_size > MAX_PKTSIZE) {
+ D("bad pktsize %d [16..%d]\n", g.pkt_size, MAX_PKTSIZE);
usage();
}
@@ -1361,6 +1851,18 @@
extract_mac_range(&g.src_mac);
extract_mac_range(&g.dst_mac);
+ if (g.src_ip.start != g.src_ip.end ||
+ g.src_ip.port0 != g.src_ip.port1 ||
+ g.dst_ip.start != g.dst_ip.end ||
+ g.dst_ip.port0 != g.dst_ip.port1)
+ g.options |= OPT_COPY;
+
+ if (g.virt_header != 0 && g.virt_header != VIRT_HDR_1
+ && g.virt_header != VIRT_HDR_2) {
+ D("bad virtio-net-header length");
+ usage();
+ }
+
if (g.dev_type == DEV_TAP) {
D("want to use tap %s", g.ifname);
g.main_fd = tap_alloc(g.ifname);
@@ -1368,47 +1870,52 @@
D("cannot open tap %s", g.ifname);
usage();
}
- } else if (g.dev_type > DEV_NETMAP) {
+#ifndef NO_PCAP
+ } else if (g.dev_type == DEV_PCAP) {
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);
+ g.p = pcap_open_live(g.ifname, 256 /* XXX */, 1, 100, pcap_errbuf);
if (g.p == NULL) {
D("cannot open pcap on %s", g.ifname);
usage();
}
+ g.main_fd = pcap_fileno(g.p);
+ D("using pcap on %s fileno %d", g.ifname, g.main_fd);
+#endif /* !NO_PCAP */
+ } else if (g.dummy_send) { /* but DEV_NETMAP */
+ D("using a dummy send routine");
} else {
- bzero(&nmr, sizeof(nmr));
- nmr.nr_version = NETMAP_API;
+ struct nmreq base_nmd;
+
+ bzero(&base_nmd, sizeof(base_nmd));
+
+ parse_nmr_config(g.nmr_config, &base_nmd);
+ if (g.extra_bufs) {
+ base_nmd.nr_arg3 = g.extra_bufs;
+ }
+
/*
- * Open the netmap device to fetch the number of queues of our
- * interface.
+ * Open the netmap device using nm_open().
*
- * 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.
+ * reconfigure. We do the open here to have time to reset.
*/
- 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;
+ g.nmd = nm_open(g.ifname, &base_nmd, 0, NULL);
+ if (g.nmd == NULL) {
+ D("Unable to open %s: %s", g.ifname, strerror(errno));
+ goto out;
}
+ g.main_fd = g.nmd->fd;
+ D("mapped %dKB at %p", g.nmd->req.nr_memsize>>10, g.nmd->mem);
+ /* get num of queues in tx or rx */
+ if (g.td_body == sender_body)
+ devqueues = g.nmd->req.nr_tx_rings;
+ else
+ devqueues = g.nmd->req.nr_rx_rings;
+
/* validate provided nthreads. */
if (g.nthreads < 1 || g.nthreads > devqueues) {
D("bad nthreads %d, have %d queues", g.nthreads, devqueues);
@@ -1415,36 +1922,25 @@
// 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
- }
+ if (verbose) {
+ struct netmap_if *nifp = g.nmd->nifp;
+ struct nmreq *req = &g.nmd->req;
- /*
- * 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
+ D("nifp at offset %d, %d tx %d rx region %d",
+ req->nr_offset, req->nr_tx_rings, req->nr_rx_rings,
+ req->nr_arg2);
+ for (i = 0; i <= req->nr_tx_rings; i++) {
+ struct netmap_ring *ring = NETMAP_TXRING(nifp, i);
+ D(" TX%d at 0x%lx slots %d", i,
+ (char *)ring - (char *)nifp, ring->num_slots);
+ }
+ for (i = 0; i <= req->nr_rx_rings; i++) {
+ struct netmap_ring *ring = NETMAP_RXRING(nifp, i);
+ D(" RX%d at 0x%lx slots %d", i,
+ (char *)ring - (char *)nifp, ring->num_slots);
+ }
}
-
/* Print some debug information. */
fprintf(stdout,
"%s %s: %d queues, %d threads and %d cpus.\n",
@@ -1458,7 +1954,8 @@
g.src_ip.name, g.dst_ip.name,
g.src_mac.name, g.dst_mac.name);
}
-
+
+out:
/* Exit if something went wrong. */
if (g.main_fd < 0) {
D("aborting");
@@ -1466,13 +1963,36 @@
}
}
+
if (g.options) {
- D("special options:%s%s%s%s\n",
+ D("--- SPECIAL OPTIONS:%s%s%s%s%s\n",
g.options & OPT_PREFETCH ? " prefetch" : "",
g.options & OPT_ACCESS ? " access" : "",
g.options & OPT_MEMCPY ? " memcpy" : "",
+ g.options & OPT_INDIRECT ? " indirect" : "",
g.options & OPT_COPY ? " copy" : "");
}
+
+ g.tx_period.tv_sec = g.tx_period.tv_nsec = 0;
+ if (g.tx_rate > 0) {
+ /* try to have at least something every second,
+ * reducing the burst size to some 0.01s worth of data
+ * (but no less than one full set of fragments)
+ */
+ uint64_t x;
+ int lim = (g.tx_rate)/300;
+ if (g.burst > lim)
+ g.burst = lim;
+ if (g.burst < g.frags)
+ g.burst = g.frags;
+ x = ((uint64_t)1000000000 * (uint64_t)g.burst) / (uint64_t) g.tx_rate;
+ g.tx_period.tv_nsec = x;
+ g.tx_period.tv_sec = g.tx_period.tv_nsec / 1000000000;
+ g.tx_period.tv_nsec = g.tx_period.tv_nsec % 1000000000;
+ }
+ if (g.td_body == sender_body)
+ D("Sending %d packets every %ld.%09ld s",
+ g.burst, g.tx_period.tv_sec, g.tx_period.tv_nsec);
/* Wait for PHY reset. */
D("Wait %d secs for phy reset", wait_link);
sleep(wait_link);
@@ -1482,16 +2002,6 @@
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;
Added: trunk/tools/tools/netmap/vale-ctl.c
===================================================================
--- trunk/tools/tools/netmap/vale-ctl.c (rev 0)
+++ trunk/tools/tools/netmap/vale-ctl.c 2018-07-22 19:23:19 UTC (rev 11955)
@@ -0,0 +1,236 @@
+/* $MidnightBSD$ */
+/*
+ * Copyright (C) 2013-2014 Michio Honda. 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: stable/10/tools/tools/netmap/vale-ctl.c 270252 2014-08-20 23:34:36Z luigi $ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <inttypes.h> /* PRI* macros */
+#include <string.h> /* strcmp */
+#include <fcntl.h> /* open */
+#include <unistd.h> /* close */
+#include <sys/ioctl.h> /* ioctl */
+#include <sys/param.h>
+#include <sys/socket.h> /* apple needs sockaddr */
+#include <net/if.h> /* ifreq */
+#include <net/netmap.h>
+#include <net/netmap_user.h>
+#include <libgen.h> /* basename */
+#include <stdlib.h> /* atoi, free */
+
+/* debug support */
+#define ND(format, ...) do {} while(0)
+#define D(format, ...) \
+ fprintf(stderr, "%s [%d] " format "\n", \
+ __FUNCTION__, __LINE__, ##__VA_ARGS__)
+
+/* XXX cut and paste from pkt-gen.c because I'm not sure whether this
+ * program may include nm_util.h
+ */
+void parse_nmr_config(const char* conf, struct nmreq *nmr)
+{
+ char *w, *tok;
+ int i, v;
+
+ nmr->nr_tx_rings = nmr->nr_rx_rings = 0;
+ nmr->nr_tx_slots = nmr->nr_rx_slots = 0;
+ if (conf == NULL || ! *conf)
+ return;
+ w = strdup(conf);
+ for (i = 0, tok = strtok(w, ","); tok; i++, tok = strtok(NULL, ",")) {
+ v = atoi(tok);
+ switch (i) {
+ case 0:
+ nmr->nr_tx_slots = nmr->nr_rx_slots = v;
+ break;
+ case 1:
+ nmr->nr_rx_slots = v;
+ break;
+ case 2:
+ nmr->nr_tx_rings = nmr->nr_rx_rings = v;
+ break;
+ case 3:
+ nmr->nr_rx_rings = v;
+ break;
+ default:
+ D("ignored config: %s", tok);
+ break;
+ }
+ }
+ D("txr %d txd %d rxr %d rxd %d",
+ nmr->nr_tx_rings, nmr->nr_tx_slots,
+ nmr->nr_rx_rings, nmr->nr_rx_slots);
+ free(w);
+}
+
+static int
+bdg_ctl(const char *name, int nr_cmd, int nr_arg, char *nmr_config)
+{
+ struct nmreq nmr;
+ int error = 0;
+ int fd = open("/dev/netmap", O_RDWR);
+
+ if (fd == -1) {
+ D("Unable to open /dev/netmap");
+ return -1;
+ }
+
+ bzero(&nmr, sizeof(nmr));
+ nmr.nr_version = NETMAP_API;
+ if (name != NULL) /* might be NULL */
+ strncpy(nmr.nr_name, name, sizeof(nmr.nr_name));
+ nmr.nr_cmd = nr_cmd;
+ parse_nmr_config(nmr_config, &nmr);
+
+ switch (nr_cmd) {
+ case NETMAP_BDG_DELIF:
+ case NETMAP_BDG_NEWIF:
+ error = ioctl(fd, NIOCREGIF, &nmr);
+ if (error == -1) {
+ ND("Unable to %s %s", nr_cmd == NETMAP_BDG_DELIF ? "delete":"create", name);
+ perror(name);
+ } else {
+ ND("Success to %s %s", nr_cmd == NETMAP_BDG_DELIF ? "delete":"create", name);
+ }
+ break;
+ case NETMAP_BDG_ATTACH:
+ case NETMAP_BDG_DETACH:
+ if (nr_arg && nr_arg != NETMAP_BDG_HOST)
+ nr_arg = 0;
+ nmr.nr_arg1 = nr_arg;
+ error = ioctl(fd, NIOCREGIF, &nmr);
+ if (error == -1) {
+ ND("Unable to %s %s to the bridge", nr_cmd ==
+ NETMAP_BDG_DETACH?"detach":"attach", name);
+ perror(name);
+ } else
+ ND("Success to %s %s to the bridge", nr_cmd ==
+ NETMAP_BDG_DETACH?"detach":"attach", name);
+ break;
+
+ case NETMAP_BDG_LIST:
+ if (strlen(nmr.nr_name)) { /* name to bridge/port info */
+ error = ioctl(fd, NIOCGINFO, &nmr);
+ if (error) {
+ ND("Unable to obtain info for %s", name);
+ perror(name);
+ } else
+ D("%s at bridge:%d port:%d", name, nmr.nr_arg1,
+ nmr.nr_arg2);
+ break;
+ }
+
+ /* scan all the bridges and ports */
+ nmr.nr_arg1 = nmr.nr_arg2 = 0;
+ for (; !ioctl(fd, NIOCGINFO, &nmr); nmr.nr_arg2++) {
+ D("bridge:%d port:%d %s", nmr.nr_arg1, nmr.nr_arg2,
+ nmr.nr_name);
+ nmr.nr_name[0] = '\0';
+ }
+
+ break;
+
+ default: /* GINFO */
+ nmr.nr_cmd = nmr.nr_arg1 = nmr.nr_arg2 = 0;
+ error = ioctl(fd, NIOCGINFO, &nmr);
+ if (error) {
+ ND("Unable to get if info for %s", name);
+ perror(name);
+ } else
+ D("%s: %d queues.", name, nmr.nr_rx_rings);
+ break;
+ }
+ close(fd);
+ return error;
+}
+
+int
+main(int argc, char *argv[])
+{
+ int ch, nr_cmd = 0, nr_arg = 0;
+ const char *command = basename(argv[0]);
+ char *name = NULL, *nmr_config = NULL;
+
+ if (argc > 3) {
+usage:
+ fprintf(stderr,
+ "Usage:\n"
+ "%s arguments\n"
+ "\t-g interface interface name to get info\n"
+ "\t-d interface interface name to be detached\n"
+ "\t-a interface interface name to be attached\n"
+ "\t-h interface interface name to be attached with the host stack\n"
+ "\t-n interface interface name to be created\n"
+ "\t-r interface interface name to be deleted\n"
+ "\t-l list all or specified bridge's interfaces (default)\n"
+ "\t-C string ring/slot setting of an interface creating by -n\n"
+ "", command);
+ return 0;
+ }
+
+ while ((ch = getopt(argc, argv, "d:a:h:g:l:n:r:C:")) != -1) {
+ name = optarg; /* default */
+ switch (ch) {
+ default:
+ fprintf(stderr, "bad option %c %s", ch, optarg);
+ goto usage;
+ case 'd':
+ nr_cmd = NETMAP_BDG_DETACH;
+ break;
+ case 'a':
+ nr_cmd = NETMAP_BDG_ATTACH;
+ break;
+ case 'h':
+ nr_cmd = NETMAP_BDG_ATTACH;
+ nr_arg = NETMAP_BDG_HOST;
+ break;
+ case 'n':
+ nr_cmd = NETMAP_BDG_NEWIF;
+ break;
+ case 'r':
+ nr_cmd = NETMAP_BDG_DELIF;
+ break;
+ case 'g':
+ nr_cmd = 0;
+ break;
+ case 'l':
+ nr_cmd = NETMAP_BDG_LIST;
+ if (optind < argc && argv[optind][0] == '-')
+ name = NULL;
+ break;
+ case 'C':
+ nmr_config = strdup(optarg);
+ break;
+ }
+ if (optind != argc) {
+ // fprintf(stderr, "optind %d argc %d\n", optind, argc);
+ goto usage;
+ }
+ }
+ if (argc == 1)
+ nr_cmd = NETMAP_BDG_LIST;
+ return bdg_ctl(name, nr_cmd, nr_arg, nmr_config) ? 1 : 0;
+}
Property changes on: trunk/tools/tools/netmap/vale-ctl.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