xref: /freebsd/tools/tools/netmap/pkt-gen.c (revision b797f66c)
168b8534bSLuigi Rizzo /*
217885a7bSLuigi Rizzo  * Copyright (C) 2011-2014 Matteo Landi, Luigi Rizzo. All rights reserved.
337e3a6d3SLuigi Rizzo  * Copyright (C) 2013-2015 Universita` di Pisa. All rights reserved.
468b8534bSLuigi Rizzo  *
568b8534bSLuigi Rizzo  * Redistribution and use in source and binary forms, with or without
668b8534bSLuigi Rizzo  * modification, are permitted provided that the following conditions
768b8534bSLuigi Rizzo  * are met:
868b8534bSLuigi Rizzo  *   1. Redistributions of source code must retain the above copyright
968b8534bSLuigi Rizzo  *      notice, this list of conditions and the following disclaimer.
1068b8534bSLuigi Rizzo  *   2. Redistributions in binary form must reproduce the above copyright
1168b8534bSLuigi Rizzo  *      notice, this list of conditions and the following disclaimer in the
1268b8534bSLuigi Rizzo  *    documentation and/or other materials provided with the distribution.
1368b8534bSLuigi Rizzo  *
1468b8534bSLuigi Rizzo  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1568b8534bSLuigi Rizzo  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1668b8534bSLuigi Rizzo  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1768b8534bSLuigi Rizzo  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1868b8534bSLuigi Rizzo  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1968b8534bSLuigi Rizzo  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2068b8534bSLuigi Rizzo  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2168b8534bSLuigi Rizzo  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2268b8534bSLuigi Rizzo  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2368b8534bSLuigi Rizzo  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2468b8534bSLuigi Rizzo  * SUCH DAMAGE.
2568b8534bSLuigi Rizzo  */
2668b8534bSLuigi Rizzo 
2768b8534bSLuigi Rizzo /*
2868b8534bSLuigi Rizzo  * $FreeBSD$
29ce3ee1e7SLuigi Rizzo  * $Id: pkt-gen.c 12346 2013-06-12 17:36:25Z luigi $
3068b8534bSLuigi Rizzo  *
3168b8534bSLuigi Rizzo  * Example program to show how to build a multithreaded packet
3268b8534bSLuigi Rizzo  * source/sink using the netmap device.
3368b8534bSLuigi Rizzo  *
3468b8534bSLuigi Rizzo  * In this example we create a programmable number of threads
3568b8534bSLuigi Rizzo  * to take care of all the queues of the interface used to
3668b8534bSLuigi Rizzo  * send or receive traffic.
3768b8534bSLuigi Rizzo  *
3868b8534bSLuigi Rizzo  */
3968b8534bSLuigi Rizzo 
40f0ea3689SLuigi Rizzo #define _GNU_SOURCE	/* for CPU_SET() */
41f0ea3689SLuigi Rizzo #include <stdio.h>
42f0ea3689SLuigi Rizzo #define NETMAP_WITH_LIBS
43f0ea3689SLuigi Rizzo #include <net/netmap_user.h>
44f0ea3689SLuigi Rizzo 
45f8e4e36aSLuigi Rizzo 
46b303f675SLuigi Rizzo #include <ctype.h>	// isprint()
47f0ea3689SLuigi Rizzo #include <unistd.h>	// sysconf()
48f0ea3689SLuigi Rizzo #include <sys/poll.h>
49f0ea3689SLuigi Rizzo #include <arpa/inet.h>	/* ntohs */
5037e3a6d3SLuigi Rizzo #ifndef _WIN32
51f0ea3689SLuigi Rizzo #include <sys/sysctl.h>	/* sysctl */
5237e3a6d3SLuigi Rizzo #endif
53f0ea3689SLuigi Rizzo #include <ifaddrs.h>	/* getifaddrs */
54f0ea3689SLuigi Rizzo #include <net/ethernet.h>
55f0ea3689SLuigi Rizzo #include <netinet/in.h>
56f0ea3689SLuigi Rizzo #include <netinet/ip.h>
57f0ea3689SLuigi Rizzo #include <netinet/udp.h>
5837e3a6d3SLuigi Rizzo #include <assert.h>
5937e3a6d3SLuigi Rizzo #include <math.h>
60f0ea3689SLuigi Rizzo 
61f0ea3689SLuigi Rizzo #include <pthread.h>
62b303f675SLuigi Rizzo 
63f2637526SLuigi Rizzo #ifndef NO_PCAP
64f2637526SLuigi Rizzo #include <pcap/pcap.h>
65f2637526SLuigi Rizzo #endif
66f0ea3689SLuigi Rizzo 
6737e3a6d3SLuigi Rizzo #include "ctrs.h"
6837e3a6d3SLuigi Rizzo 
6937e3a6d3SLuigi Rizzo #ifdef _WIN32
7037e3a6d3SLuigi Rizzo #define cpuset_t        DWORD_PTR   //uint64_t
7137e3a6d3SLuigi Rizzo static inline void CPU_ZERO(cpuset_t *p)
7237e3a6d3SLuigi Rizzo {
7337e3a6d3SLuigi Rizzo         *p = 0;
7437e3a6d3SLuigi Rizzo }
7537e3a6d3SLuigi Rizzo 
7637e3a6d3SLuigi Rizzo static inline void CPU_SET(uint32_t i, cpuset_t *p)
7737e3a6d3SLuigi Rizzo {
7837e3a6d3SLuigi Rizzo         *p |= 1<< (i & 0x3f);
7937e3a6d3SLuigi Rizzo }
8037e3a6d3SLuigi Rizzo 
8137e3a6d3SLuigi Rizzo #define pthread_setaffinity_np(a, b, c) !SetThreadAffinityMask(a, *c)    //((void)a, 0)
8237e3a6d3SLuigi Rizzo #define TAP_CLONEDEV	"/dev/tap"
8337e3a6d3SLuigi Rizzo #define AF_LINK	18	//defined in winsocks.h
8437e3a6d3SLuigi Rizzo #define CLOCK_REALTIME_PRECISE CLOCK_REALTIME
8537e3a6d3SLuigi Rizzo #include <net/if_dl.h>
8637e3a6d3SLuigi Rizzo 
8737e3a6d3SLuigi Rizzo /*
8837e3a6d3SLuigi Rizzo  * Convert an ASCII representation of an ethernet address to
8937e3a6d3SLuigi Rizzo  * binary form.
9037e3a6d3SLuigi Rizzo  */
9137e3a6d3SLuigi Rizzo struct ether_addr *
9237e3a6d3SLuigi Rizzo ether_aton(const char *a)
9337e3a6d3SLuigi Rizzo {
9437e3a6d3SLuigi Rizzo 	int i;
9537e3a6d3SLuigi Rizzo 	static struct ether_addr o;
9637e3a6d3SLuigi Rizzo 	unsigned int o0, o1, o2, o3, o4, o5;
9737e3a6d3SLuigi Rizzo 
9837e3a6d3SLuigi Rizzo 	i = sscanf(a, "%x:%x:%x:%x:%x:%x", &o0, &o1, &o2, &o3, &o4, &o5);
9937e3a6d3SLuigi Rizzo 
10037e3a6d3SLuigi Rizzo 	if (i != 6)
10137e3a6d3SLuigi Rizzo 		return (NULL);
10237e3a6d3SLuigi Rizzo 
10337e3a6d3SLuigi Rizzo 	o.octet[0]=o0;
10437e3a6d3SLuigi Rizzo 	o.octet[1]=o1;
10537e3a6d3SLuigi Rizzo 	o.octet[2]=o2;
10637e3a6d3SLuigi Rizzo 	o.octet[3]=o3;
10737e3a6d3SLuigi Rizzo 	o.octet[4]=o4;
10837e3a6d3SLuigi Rizzo 	o.octet[5]=o5;
10937e3a6d3SLuigi Rizzo 
11037e3a6d3SLuigi Rizzo 	return ((struct ether_addr *)&o);
11137e3a6d3SLuigi Rizzo }
11237e3a6d3SLuigi Rizzo 
11337e3a6d3SLuigi Rizzo /*
11437e3a6d3SLuigi Rizzo  * Convert a binary representation of an ethernet address to
11537e3a6d3SLuigi Rizzo  * an ASCII string.
11637e3a6d3SLuigi Rizzo  */
11737e3a6d3SLuigi Rizzo char *
11837e3a6d3SLuigi Rizzo ether_ntoa(const struct ether_addr *n)
11937e3a6d3SLuigi Rizzo {
12037e3a6d3SLuigi Rizzo 	int i;
12137e3a6d3SLuigi Rizzo 	static char a[18];
12237e3a6d3SLuigi Rizzo 
12337e3a6d3SLuigi Rizzo 	i = sprintf(a, "%02x:%02x:%02x:%02x:%02x:%02x",
12437e3a6d3SLuigi Rizzo 	    n->octet[0], n->octet[1], n->octet[2],
12537e3a6d3SLuigi Rizzo 	    n->octet[3], n->octet[4], n->octet[5]);
12637e3a6d3SLuigi Rizzo 	return (i < 17 ? NULL : (char *)&a);
12737e3a6d3SLuigi Rizzo }
12837e3a6d3SLuigi Rizzo #endif /* _WIN32 */
12937e3a6d3SLuigi Rizzo 
130f0ea3689SLuigi Rizzo #ifdef linux
131f0ea3689SLuigi Rizzo 
132f0ea3689SLuigi Rizzo #define cpuset_t        cpu_set_t
133f0ea3689SLuigi Rizzo 
134f0ea3689SLuigi Rizzo #define ifr_flagshigh  ifr_flags        /* only the low 16 bits here */
135f0ea3689SLuigi Rizzo #define IFF_PPROMISC   IFF_PROMISC      /* IFF_PPROMISC does not exist */
136f0ea3689SLuigi Rizzo #include <linux/ethtool.h>
137f0ea3689SLuigi Rizzo #include <linux/sockios.h>
138f0ea3689SLuigi Rizzo 
139f0ea3689SLuigi Rizzo #define CLOCK_REALTIME_PRECISE CLOCK_REALTIME
140f0ea3689SLuigi Rizzo #include <netinet/ether.h>      /* ether_aton */
141f0ea3689SLuigi Rizzo #include <linux/if_packet.h>    /* sockaddr_ll */
142f0ea3689SLuigi Rizzo #endif  /* linux */
143f0ea3689SLuigi Rizzo 
144f0ea3689SLuigi Rizzo #ifdef __FreeBSD__
145f0ea3689SLuigi Rizzo #include <sys/endian.h> /* le64toh */
146f0ea3689SLuigi Rizzo #include <machine/param.h>
147f0ea3689SLuigi Rizzo 
148f0ea3689SLuigi Rizzo #include <pthread_np.h> /* pthread w/ affinity */
149f0ea3689SLuigi Rizzo #include <sys/cpuset.h> /* cpu_set */
150f0ea3689SLuigi Rizzo #include <net/if_dl.h>  /* LLADDR */
151f0ea3689SLuigi Rizzo #endif  /* __FreeBSD__ */
152f0ea3689SLuigi Rizzo 
153f0ea3689SLuigi Rizzo #ifdef __APPLE__
154f0ea3689SLuigi Rizzo 
155f0ea3689SLuigi Rizzo #define cpuset_t        uint64_t        // XXX
156f0ea3689SLuigi Rizzo static inline void CPU_ZERO(cpuset_t *p)
157f0ea3689SLuigi Rizzo {
158f0ea3689SLuigi Rizzo         *p = 0;
159f0ea3689SLuigi Rizzo }
160f0ea3689SLuigi Rizzo 
161f0ea3689SLuigi Rizzo static inline void CPU_SET(uint32_t i, cpuset_t *p)
162f0ea3689SLuigi Rizzo {
163f0ea3689SLuigi Rizzo         *p |= 1<< (i & 0x3f);
164f0ea3689SLuigi Rizzo }
165f0ea3689SLuigi Rizzo 
166f0ea3689SLuigi Rizzo #define pthread_setaffinity_np(a, b, c) ((void)a, 0)
167f0ea3689SLuigi Rizzo 
168f0ea3689SLuigi Rizzo #define ifr_flagshigh  ifr_flags        // XXX
169f0ea3689SLuigi Rizzo #define IFF_PPROMISC   IFF_PROMISC
170f0ea3689SLuigi Rizzo #include <net/if_dl.h>  /* LLADDR */
171f0ea3689SLuigi Rizzo #define clock_gettime(a,b)      \
172f0ea3689SLuigi Rizzo         do {struct timespec t0 = {0,0}; *(b) = t0; } while (0)
173f0ea3689SLuigi Rizzo #endif  /* __APPLE__ */
174f0ea3689SLuigi Rizzo 
175ce3ee1e7SLuigi Rizzo const char *default_payload="netmap pkt-gen DIRECT payload\n"
176ce3ee1e7SLuigi Rizzo 	"http://info.iet.unipi.it/~luigi/netmap/ ";
177ce3ee1e7SLuigi Rizzo 
178ce3ee1e7SLuigi Rizzo const char *indirect_payload="netmap pkt-gen indirect payload\n"
17968b8534bSLuigi Rizzo 	"http://info.iet.unipi.it/~luigi/netmap/ ";
18068b8534bSLuigi Rizzo 
18168b8534bSLuigi Rizzo int verbose = 0;
18268b8534bSLuigi Rizzo 
183f0ea3689SLuigi Rizzo #define SKIP_PAYLOAD 1 /* do not check payload. XXX unused */
18468b8534bSLuigi Rizzo 
18517885a7bSLuigi Rizzo 
18617885a7bSLuigi Rizzo #define VIRT_HDR_1	10	/* length of a base vnet-hdr */
18717885a7bSLuigi Rizzo #define VIRT_HDR_2	12	/* length of the extenede vnet-hdr */
18817885a7bSLuigi Rizzo #define VIRT_HDR_MAX	VIRT_HDR_2
18917885a7bSLuigi Rizzo struct virt_header {
19017885a7bSLuigi Rizzo 	uint8_t fields[VIRT_HDR_MAX];
19117885a7bSLuigi Rizzo };
19217885a7bSLuigi Rizzo 
1934bf50f18SLuigi Rizzo #define MAX_BODYSIZE	16384
1944bf50f18SLuigi Rizzo 
19568b8534bSLuigi Rizzo struct pkt {
19617885a7bSLuigi Rizzo 	struct virt_header vh;
19768b8534bSLuigi Rizzo 	struct ether_header eh;
19868b8534bSLuigi Rizzo 	struct ip ip;
19968b8534bSLuigi Rizzo 	struct udphdr udp;
2004bf50f18SLuigi Rizzo 	uint8_t body[MAX_BODYSIZE];	// XXX hardwired
20168b8534bSLuigi Rizzo } __attribute__((__packed__));
20268b8534bSLuigi Rizzo 
203f8e4e36aSLuigi Rizzo struct ip_range {
204f8e4e36aSLuigi Rizzo 	char *name;
205ce3ee1e7SLuigi Rizzo 	uint32_t start, end; /* same as struct in_addr */
206ce3ee1e7SLuigi Rizzo 	uint16_t port0, port1;
207f8e4e36aSLuigi Rizzo };
208f8e4e36aSLuigi Rizzo 
209f8e4e36aSLuigi Rizzo struct mac_range {
210f8e4e36aSLuigi Rizzo 	char *name;
211f8e4e36aSLuigi Rizzo 	struct ether_addr start, end;
212f8e4e36aSLuigi Rizzo };
213f8e4e36aSLuigi Rizzo 
214f0ea3689SLuigi Rizzo /* ifname can be netmap:foo-xxxx */
215f0ea3689SLuigi Rizzo #define MAX_IFNAMELEN	64	/* our buffer for ifname */
2164bf50f18SLuigi Rizzo //#define MAX_PKTSIZE	1536
2174bf50f18SLuigi Rizzo #define MAX_PKTSIZE	MAX_BODYSIZE	/* XXX: + IP_HDR + ETH_HDR */
2184bf50f18SLuigi Rizzo 
2194bf50f18SLuigi Rizzo /* compact timestamp to fit into 60 byte packet. (enough to obtain RTT) */
2204bf50f18SLuigi Rizzo struct tstamp {
2214bf50f18SLuigi Rizzo 	uint32_t sec;
2224bf50f18SLuigi Rizzo 	uint32_t nsec;
2234bf50f18SLuigi Rizzo };
2244bf50f18SLuigi Rizzo 
22568b8534bSLuigi Rizzo /*
22668b8534bSLuigi Rizzo  * global arguments for all threads
22768b8534bSLuigi Rizzo  */
228f8e4e36aSLuigi Rizzo 
22968b8534bSLuigi Rizzo struct glob_arg {
230f8e4e36aSLuigi Rizzo 	struct ip_range src_ip;
231f8e4e36aSLuigi Rizzo 	struct ip_range dst_ip;
232f8e4e36aSLuigi Rizzo 	struct mac_range dst_mac;
233f8e4e36aSLuigi Rizzo 	struct mac_range src_mac;
23468b8534bSLuigi Rizzo 	int pkt_size;
23568b8534bSLuigi Rizzo 	int burst;
236f8e4e36aSLuigi Rizzo 	int forever;
23737e3a6d3SLuigi Rizzo 	uint64_t npackets;	/* total packets to send */
238ce3ee1e7SLuigi Rizzo 	int frags;	/* fragments per packet */
23968b8534bSLuigi Rizzo 	int nthreads;
24037e3a6d3SLuigi Rizzo 	int cpus;	/* cpus used for running */
24137e3a6d3SLuigi Rizzo 	int system_cpus;	/* cpus on the system */
24237e3a6d3SLuigi Rizzo 
24399fb123fSLuigi Rizzo 	int options;	/* testing */
24499fb123fSLuigi Rizzo #define OPT_PREFETCH	1
24599fb123fSLuigi Rizzo #define OPT_ACCESS	2
24699fb123fSLuigi Rizzo #define OPT_COPY	4
24799fb123fSLuigi Rizzo #define OPT_MEMCPY	8
248f8e4e36aSLuigi Rizzo #define OPT_TS		16	/* add a timestamp */
249b303f675SLuigi Rizzo #define OPT_INDIRECT	32	/* use indirect buffers, tx only */
250b303f675SLuigi Rizzo #define OPT_DUMP	64	/* dump rx/tx traffic */
25137e3a6d3SLuigi Rizzo #define OPT_RUBBISH	256	/* send wathever the buffers contain */
25256717743SAdrian Chadd #define OPT_RANDOM_SRC  512
25356717743SAdrian Chadd #define OPT_RANDOM_DST  1024
25437e3a6d3SLuigi Rizzo #define OPT_PPS_STATS   2048
255f8e4e36aSLuigi Rizzo 	int dev_type;
256f2637526SLuigi Rizzo #ifndef NO_PCAP
25768b8534bSLuigi Rizzo 	pcap_t *p;
258f2637526SLuigi Rizzo #endif
25968b8534bSLuigi Rizzo 
2601cb4c501SLuigi Rizzo 	int tx_rate;
2611cb4c501SLuigi Rizzo 	struct timespec tx_period;
2621cb4c501SLuigi Rizzo 
263f8e4e36aSLuigi Rizzo 	int affinity;
264f8e4e36aSLuigi Rizzo 	int main_fd;
265f0ea3689SLuigi Rizzo 	struct nm_desc *nmd;
266f2637526SLuigi Rizzo 	int report_interval;		/* milliseconds between prints */
267f8e4e36aSLuigi Rizzo 	void *(*td_body)(void *);
26837e3a6d3SLuigi Rizzo 	int td_type;
269f8e4e36aSLuigi Rizzo 	void *mmap_addr;
270f0ea3689SLuigi Rizzo 	char ifname[MAX_IFNAMELEN];
271ce3ee1e7SLuigi Rizzo 	char *nmr_config;
272ce3ee1e7SLuigi Rizzo 	int dummy_send;
27317885a7bSLuigi Rizzo 	int virt_header;	/* send also the virt_header */
274f0ea3689SLuigi Rizzo 	int extra_bufs;		/* goes in nr_arg3 */
27537e3a6d3SLuigi Rizzo 	int extra_pipes;	/* goes in nr_arg1 */
276f284c737SGeorge V. Neville-Neil 	char *packet_file;	/* -P option */
27737e3a6d3SLuigi Rizzo #define	STATS_WIN	15
27837e3a6d3SLuigi Rizzo 	int win_idx;
27937e3a6d3SLuigi Rizzo 	int64_t win[STATS_WIN];
28068b8534bSLuigi Rizzo };
281f8e4e36aSLuigi Rizzo enum dev_type { DEV_NONE, DEV_NETMAP, DEV_PCAP, DEV_TAP };
282f8e4e36aSLuigi Rizzo 
28368b8534bSLuigi Rizzo 
28468b8534bSLuigi Rizzo /*
28568b8534bSLuigi Rizzo  * Arguments for a new thread. The same structure is used by
28668b8534bSLuigi Rizzo  * the source and the sink
28768b8534bSLuigi Rizzo  */
28868b8534bSLuigi Rizzo struct targ {
28968b8534bSLuigi Rizzo 	struct glob_arg *g;
29068b8534bSLuigi Rizzo 	int used;
29168b8534bSLuigi Rizzo 	int completed;
2923fe77e68SEd Maste 	int cancel;
29368b8534bSLuigi Rizzo 	int fd;
294f0ea3689SLuigi Rizzo 	struct nm_desc *nmd;
29537e3a6d3SLuigi Rizzo 	/* these ought to be volatile, but they are
29637e3a6d3SLuigi Rizzo 	 * only sampled and errors should not accumulate
29737e3a6d3SLuigi Rizzo 	 */
29837e3a6d3SLuigi Rizzo 	struct my_ctrs ctr;
29937e3a6d3SLuigi Rizzo 
3001cb4c501SLuigi Rizzo 	struct timespec tic, toc;
30168b8534bSLuigi Rizzo 	int me;
30268b8534bSLuigi Rizzo 	pthread_t thread;
30368b8534bSLuigi Rizzo 	int affinity;
30468b8534bSLuigi Rizzo 
30568b8534bSLuigi Rizzo 	struct pkt pkt;
306f284c737SGeorge V. Neville-Neil 	void *frame;
30768b8534bSLuigi Rizzo };
30868b8534bSLuigi Rizzo 
30968b8534bSLuigi Rizzo 
310f8e4e36aSLuigi Rizzo /*
311f8e4e36aSLuigi Rizzo  * extract the extremes from a range of ipv4 addresses.
312f8e4e36aSLuigi Rizzo  * addr_lo[-addr_hi][:port_lo[-port_hi]]
313f8e4e36aSLuigi Rizzo  */
314f8e4e36aSLuigi Rizzo static void
315f8e4e36aSLuigi Rizzo extract_ip_range(struct ip_range *r)
316f8e4e36aSLuigi Rizzo {
317ce3ee1e7SLuigi Rizzo 	char *ap, *pp;
318ce3ee1e7SLuigi Rizzo 	struct in_addr a;
319f8e4e36aSLuigi Rizzo 
32017885a7bSLuigi Rizzo 	if (verbose)
321f8e4e36aSLuigi Rizzo 		D("extract IP range from %s", r->name);
322ce3ee1e7SLuigi Rizzo 	r->port0 = r->port1 = 0;
323ce3ee1e7SLuigi Rizzo 	r->start = r->end = 0;
324ce3ee1e7SLuigi Rizzo 
325ce3ee1e7SLuigi Rizzo 	/* the first - splits start/end of range */
326ce3ee1e7SLuigi Rizzo 	ap = index(r->name, '-');	/* do we have ports ? */
327ce3ee1e7SLuigi Rizzo 	if (ap) {
328ce3ee1e7SLuigi Rizzo 		*ap++ = '\0';
329ce3ee1e7SLuigi Rizzo 	}
330ce3ee1e7SLuigi Rizzo 	/* grab the initial values (mandatory) */
331ce3ee1e7SLuigi Rizzo 	pp = index(r->name, ':');
332ce3ee1e7SLuigi Rizzo 	if (pp) {
333ce3ee1e7SLuigi Rizzo 		*pp++ = '\0';
334ce3ee1e7SLuigi Rizzo 		r->port0 = r->port1 = strtol(pp, NULL, 0);
335ce3ee1e7SLuigi Rizzo 	};
336ce3ee1e7SLuigi Rizzo 	inet_aton(r->name, &a);
337ce3ee1e7SLuigi Rizzo 	r->start = r->end = ntohl(a.s_addr);
338ce3ee1e7SLuigi Rizzo 	if (ap) {
339ce3ee1e7SLuigi Rizzo 		pp = index(ap, ':');
340ce3ee1e7SLuigi Rizzo 		if (pp) {
341ce3ee1e7SLuigi Rizzo 			*pp++ = '\0';
342ce3ee1e7SLuigi Rizzo 			if (*pp)
343ce3ee1e7SLuigi Rizzo 				r->port1 = strtol(pp, NULL, 0);
344ce3ee1e7SLuigi Rizzo 		}
345ce3ee1e7SLuigi Rizzo 		if (*ap) {
346ce3ee1e7SLuigi Rizzo 			inet_aton(ap, &a);
347ce3ee1e7SLuigi Rizzo 			r->end = ntohl(a.s_addr);
348ce3ee1e7SLuigi Rizzo 		}
349ce3ee1e7SLuigi Rizzo 	}
350ce3ee1e7SLuigi Rizzo 	if (r->port0 > r->port1) {
351ce3ee1e7SLuigi Rizzo 		uint16_t tmp = r->port0;
352f8e4e36aSLuigi Rizzo 		r->port0 = r->port1;
353ce3ee1e7SLuigi Rizzo 		r->port1 = tmp;
354f8e4e36aSLuigi Rizzo 	}
355ce3ee1e7SLuigi Rizzo 	if (r->start > r->end) {
356ce3ee1e7SLuigi Rizzo 		uint32_t tmp = r->start;
357f8e4e36aSLuigi Rizzo 		r->start = r->end;
358ce3ee1e7SLuigi Rizzo 		r->end = tmp;
359f8e4e36aSLuigi Rizzo 	}
360ce3ee1e7SLuigi Rizzo 	{
361ce3ee1e7SLuigi Rizzo 		struct in_addr a;
362ce3ee1e7SLuigi Rizzo 		char buf1[16]; // one ip address
363ce3ee1e7SLuigi Rizzo 
364ce3ee1e7SLuigi Rizzo 		a.s_addr = htonl(r->end);
365ce3ee1e7SLuigi Rizzo 		strncpy(buf1, inet_ntoa(a), sizeof(buf1));
366ce3ee1e7SLuigi Rizzo 		a.s_addr = htonl(r->start);
36717885a7bSLuigi Rizzo 		if (1)
368ce3ee1e7SLuigi Rizzo 		    D("range is %s:%d to %s:%d",
369ce3ee1e7SLuigi Rizzo 			inet_ntoa(a), r->port0, buf1, r->port1);
370ce3ee1e7SLuigi Rizzo 	}
371f8e4e36aSLuigi Rizzo }
372f8e4e36aSLuigi Rizzo 
373f8e4e36aSLuigi Rizzo static void
374f8e4e36aSLuigi Rizzo extract_mac_range(struct mac_range *r)
375f8e4e36aSLuigi Rizzo {
37617885a7bSLuigi Rizzo 	if (verbose)
377f8e4e36aSLuigi Rizzo 	    D("extract MAC range from %s", r->name);
378f8e4e36aSLuigi Rizzo 	bcopy(ether_aton(r->name), &r->start, 6);
379f8e4e36aSLuigi Rizzo 	bcopy(ether_aton(r->name), &r->end, 6);
380f8e4e36aSLuigi Rizzo #if 0
381f8e4e36aSLuigi Rizzo 	bcopy(targ->src_mac, eh->ether_shost, 6);
382f8e4e36aSLuigi Rizzo 	p = index(targ->g->src_mac, '-');
383f8e4e36aSLuigi Rizzo 	if (p)
384f8e4e36aSLuigi Rizzo 		targ->src_mac_range = atoi(p+1);
385f8e4e36aSLuigi Rizzo 
386f8e4e36aSLuigi Rizzo 	bcopy(ether_aton(targ->g->dst_mac), targ->dst_mac, 6);
387f8e4e36aSLuigi Rizzo 	bcopy(targ->dst_mac, eh->ether_dhost, 6);
388f8e4e36aSLuigi Rizzo 	p = index(targ->g->dst_mac, '-');
389f8e4e36aSLuigi Rizzo 	if (p)
390f8e4e36aSLuigi Rizzo 		targ->dst_mac_range = atoi(p+1);
391f8e4e36aSLuigi Rizzo #endif
39217885a7bSLuigi Rizzo 	if (verbose)
393f8e4e36aSLuigi Rizzo 		D("%s starts at %s", r->name, ether_ntoa(&r->start));
394f8e4e36aSLuigi Rizzo }
395f8e4e36aSLuigi Rizzo 
39668b8534bSLuigi Rizzo static struct targ *targs;
39768b8534bSLuigi Rizzo static int global_nthreads;
39868b8534bSLuigi Rizzo 
39968b8534bSLuigi Rizzo /* control-C handler */
40068b8534bSLuigi Rizzo static void
401f8e4e36aSLuigi Rizzo sigint_h(int sig)
40268b8534bSLuigi Rizzo {
403f8e4e36aSLuigi Rizzo 	int i;
40468b8534bSLuigi Rizzo 
405f8e4e36aSLuigi Rizzo 	(void)sig;	/* UNUSED */
40637e3a6d3SLuigi Rizzo 	D("received control-C on thread %p", (void *)pthread_self());
407f8e4e36aSLuigi Rizzo 	for (i = 0; i < global_nthreads; i++) {
408f8e4e36aSLuigi Rizzo 		targs[i].cancel = 1;
409f8e4e36aSLuigi Rizzo 	}
41068b8534bSLuigi Rizzo }
41168b8534bSLuigi Rizzo 
41268b8534bSLuigi Rizzo /* sysctl wrapper to return the number of active CPUs */
41368b8534bSLuigi Rizzo static int
41468b8534bSLuigi Rizzo system_ncpus(void)
41568b8534bSLuigi Rizzo {
416f0ea3689SLuigi Rizzo 	int ncpus;
417f0ea3689SLuigi Rizzo #if defined (__FreeBSD__)
418f0ea3689SLuigi Rizzo 	int mib[2] = { CTL_HW, HW_NCPU };
419f0ea3689SLuigi Rizzo 	size_t len = sizeof(mib);
42068b8534bSLuigi Rizzo 	sysctl(mib, 2, &ncpus, &len, NULL, 0);
421f0ea3689SLuigi Rizzo #elif defined(linux)
422f0ea3689SLuigi Rizzo 	ncpus = sysconf(_SC_NPROCESSORS_ONLN);
42337e3a6d3SLuigi Rizzo #elif defined(_WIN32)
42437e3a6d3SLuigi Rizzo 	{
42537e3a6d3SLuigi Rizzo 		SYSTEM_INFO sysinfo;
42637e3a6d3SLuigi Rizzo 		GetSystemInfo(&sysinfo);
42737e3a6d3SLuigi Rizzo 		ncpus = sysinfo.dwNumberOfProcessors;
42837e3a6d3SLuigi Rizzo 	}
429f0ea3689SLuigi Rizzo #else /* others */
430f0ea3689SLuigi Rizzo 	ncpus = 1;
431f0ea3689SLuigi Rizzo #endif /* others */
43268b8534bSLuigi Rizzo 	return (ncpus);
43368b8534bSLuigi Rizzo }
43468b8534bSLuigi Rizzo 
435f8e4e36aSLuigi Rizzo #ifdef __linux__
436f8e4e36aSLuigi Rizzo #define sockaddr_dl    sockaddr_ll
437f8e4e36aSLuigi Rizzo #define sdl_family     sll_family
438f8e4e36aSLuigi Rizzo #define AF_LINK        AF_PACKET
439f8e4e36aSLuigi Rizzo #define LLADDR(s)      s->sll_addr;
440f8e4e36aSLuigi Rizzo #include <linux/if_tun.h>
441f8e4e36aSLuigi Rizzo #define TAP_CLONEDEV	"/dev/net/tun"
442f8e4e36aSLuigi Rizzo #endif /* __linux__ */
443f8e4e36aSLuigi Rizzo 
444f8e4e36aSLuigi Rizzo #ifdef __FreeBSD__
445f8e4e36aSLuigi Rizzo #include <net/if_tun.h>
446f8e4e36aSLuigi Rizzo #define TAP_CLONEDEV	"/dev/tap"
447f8e4e36aSLuigi Rizzo #endif /* __FreeBSD */
448f8e4e36aSLuigi Rizzo 
449f8e4e36aSLuigi Rizzo #ifdef __APPLE__
450f8e4e36aSLuigi Rizzo // #warning TAP not supported on apple ?
451f8e4e36aSLuigi Rizzo #include <net/if_utun.h>
452f8e4e36aSLuigi Rizzo #define TAP_CLONEDEV	"/dev/tap"
453f8e4e36aSLuigi Rizzo #endif /* __APPLE__ */
454f8e4e36aSLuigi Rizzo 
455f8e4e36aSLuigi Rizzo 
45668b8534bSLuigi Rizzo /*
457ce3ee1e7SLuigi Rizzo  * parse the vale configuration in conf and put it in nmr.
458f0ea3689SLuigi Rizzo  * Return the flag set if necessary.
459ce3ee1e7SLuigi Rizzo  * The configuration may consist of 0 to 4 numbers separated
460fc6eb28bSHiren Panchasara  * by commas: #tx-slots,#rx-slots,#tx-rings,#rx-rings.
461ce3ee1e7SLuigi Rizzo  * Missing numbers or zeroes stand for default values.
462ce3ee1e7SLuigi Rizzo  * As an additional convenience, if exactly one number
463fc6eb28bSHiren Panchasara  * is specified, then this is assigned to both #tx-slots and #rx-slots.
464fc6eb28bSHiren Panchasara  * If there is no 4th number, then the 3rd is assigned to both #tx-rings
465ce3ee1e7SLuigi Rizzo  * and #rx-rings.
466ce3ee1e7SLuigi Rizzo  */
467f0ea3689SLuigi Rizzo int
468f0ea3689SLuigi Rizzo parse_nmr_config(const char* conf, struct nmreq *nmr)
469ce3ee1e7SLuigi Rizzo {
470ce3ee1e7SLuigi Rizzo 	char *w, *tok;
471ce3ee1e7SLuigi Rizzo 	int i, v;
472ce3ee1e7SLuigi Rizzo 
473ce3ee1e7SLuigi Rizzo 	nmr->nr_tx_rings = nmr->nr_rx_rings = 0;
474ce3ee1e7SLuigi Rizzo 	nmr->nr_tx_slots = nmr->nr_rx_slots = 0;
475ce3ee1e7SLuigi Rizzo 	if (conf == NULL || ! *conf)
476f0ea3689SLuigi Rizzo 		return 0;
477ce3ee1e7SLuigi Rizzo 	w = strdup(conf);
478ce3ee1e7SLuigi Rizzo 	for (i = 0, tok = strtok(w, ","); tok; i++, tok = strtok(NULL, ",")) {
479ce3ee1e7SLuigi Rizzo 		v = atoi(tok);
480ce3ee1e7SLuigi Rizzo 		switch (i) {
481ce3ee1e7SLuigi Rizzo 		case 0:
482ce3ee1e7SLuigi Rizzo 			nmr->nr_tx_slots = nmr->nr_rx_slots = v;
483ce3ee1e7SLuigi Rizzo 			break;
484ce3ee1e7SLuigi Rizzo 		case 1:
485ce3ee1e7SLuigi Rizzo 			nmr->nr_rx_slots = v;
486ce3ee1e7SLuigi Rizzo 			break;
487ce3ee1e7SLuigi Rizzo 		case 2:
488ce3ee1e7SLuigi Rizzo 			nmr->nr_tx_rings = nmr->nr_rx_rings = v;
489ce3ee1e7SLuigi Rizzo 			break;
490ce3ee1e7SLuigi Rizzo 		case 3:
491ce3ee1e7SLuigi Rizzo 			nmr->nr_rx_rings = v;
492ce3ee1e7SLuigi Rizzo 			break;
493ce3ee1e7SLuigi Rizzo 		default:
494ce3ee1e7SLuigi Rizzo 			D("ignored config: %s", tok);
495ce3ee1e7SLuigi Rizzo 			break;
496ce3ee1e7SLuigi Rizzo 		}
497ce3ee1e7SLuigi Rizzo 	}
498ce3ee1e7SLuigi Rizzo 	D("txr %d txd %d rxr %d rxd %d",
499ce3ee1e7SLuigi Rizzo 			nmr->nr_tx_rings, nmr->nr_tx_slots,
500ce3ee1e7SLuigi Rizzo 			nmr->nr_rx_rings, nmr->nr_rx_slots);
501ce3ee1e7SLuigi Rizzo 	free(w);
502f0ea3689SLuigi Rizzo 	return (nmr->nr_tx_rings || nmr->nr_tx_slots ||
503f0ea3689SLuigi Rizzo                         nmr->nr_rx_rings || nmr->nr_rx_slots) ?
504f0ea3689SLuigi Rizzo 		NM_OPEN_RING_CFG : 0;
505ce3ee1e7SLuigi Rizzo }
506ce3ee1e7SLuigi Rizzo 
507ce3ee1e7SLuigi Rizzo 
508ce3ee1e7SLuigi Rizzo /*
50968b8534bSLuigi Rizzo  * locate the src mac address for our interface, put it
51068b8534bSLuigi Rizzo  * into the user-supplied buffer. return 0 if ok, -1 on error.
51168b8534bSLuigi Rizzo  */
51268b8534bSLuigi Rizzo static int
51368b8534bSLuigi Rizzo source_hwaddr(const char *ifname, char *buf)
51468b8534bSLuigi Rizzo {
51568b8534bSLuigi Rizzo 	struct ifaddrs *ifaphead, *ifap;
51668b8534bSLuigi Rizzo 	int l = sizeof(ifap->ifa_name);
51768b8534bSLuigi Rizzo 
51868b8534bSLuigi Rizzo 	if (getifaddrs(&ifaphead) != 0) {
51968b8534bSLuigi Rizzo 		D("getifaddrs %s failed", ifname);
52068b8534bSLuigi Rizzo 		return (-1);
52168b8534bSLuigi Rizzo 	}
52268b8534bSLuigi Rizzo 
52368b8534bSLuigi Rizzo 	for (ifap = ifaphead; ifap; ifap = ifap->ifa_next) {
52468b8534bSLuigi Rizzo 		struct sockaddr_dl *sdl =
52568b8534bSLuigi Rizzo 			(struct sockaddr_dl *)ifap->ifa_addr;
52668b8534bSLuigi Rizzo 		uint8_t *mac;
52768b8534bSLuigi Rizzo 
52868b8534bSLuigi Rizzo 		if (!sdl || sdl->sdl_family != AF_LINK)
52968b8534bSLuigi Rizzo 			continue;
53068b8534bSLuigi Rizzo 		if (strncmp(ifap->ifa_name, ifname, l) != 0)
53168b8534bSLuigi Rizzo 			continue;
53268b8534bSLuigi Rizzo 		mac = (uint8_t *)LLADDR(sdl);
53368b8534bSLuigi Rizzo 		sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x",
53468b8534bSLuigi Rizzo 			mac[0], mac[1], mac[2],
53568b8534bSLuigi Rizzo 			mac[3], mac[4], mac[5]);
53668b8534bSLuigi Rizzo 		if (verbose)
53768b8534bSLuigi Rizzo 			D("source hwaddr %s", buf);
53868b8534bSLuigi Rizzo 		break;
53968b8534bSLuigi Rizzo 	}
54068b8534bSLuigi Rizzo 	freeifaddrs(ifaphead);
54168b8534bSLuigi Rizzo 	return ifap ? 0 : 1;
54268b8534bSLuigi Rizzo }
54368b8534bSLuigi Rizzo 
54468b8534bSLuigi Rizzo 
54568b8534bSLuigi Rizzo /* set the thread affinity. */
54668b8534bSLuigi Rizzo static int
54768b8534bSLuigi Rizzo setaffinity(pthread_t me, int i)
54868b8534bSLuigi Rizzo {
54968b8534bSLuigi Rizzo 	cpuset_t cpumask;
55068b8534bSLuigi Rizzo 
55168b8534bSLuigi Rizzo 	if (i == -1)
55268b8534bSLuigi Rizzo 		return 0;
55368b8534bSLuigi Rizzo 
55468b8534bSLuigi Rizzo 	/* Set thread affinity affinity.*/
55568b8534bSLuigi Rizzo 	CPU_ZERO(&cpumask);
55668b8534bSLuigi Rizzo 	CPU_SET(i, &cpumask);
55768b8534bSLuigi Rizzo 
55868b8534bSLuigi Rizzo 	if (pthread_setaffinity_np(me, sizeof(cpuset_t), &cpumask) != 0) {
55917885a7bSLuigi Rizzo 		D("Unable to set affinity: %s", strerror(errno));
56068b8534bSLuigi Rizzo 		return 1;
56168b8534bSLuigi Rizzo 	}
56268b8534bSLuigi Rizzo 	return 0;
56368b8534bSLuigi Rizzo }
56468b8534bSLuigi Rizzo 
56568b8534bSLuigi Rizzo /* Compute the checksum of the given ip header. */
56668b8534bSLuigi Rizzo static uint16_t
567f8e4e36aSLuigi Rizzo checksum(const void *data, uint16_t len, uint32_t sum)
56868b8534bSLuigi Rizzo {
56968b8534bSLuigi Rizzo         const uint8_t *addr = data;
570f8e4e36aSLuigi Rizzo 	uint32_t i;
57168b8534bSLuigi Rizzo 
572f8e4e36aSLuigi Rizzo         /* Checksum all the pairs of bytes first... */
573f8e4e36aSLuigi Rizzo         for (i = 0; i < (len & ~1U); i += 2) {
574f8e4e36aSLuigi Rizzo                 sum += (u_int16_t)ntohs(*((u_int16_t *)(addr + i)));
575f8e4e36aSLuigi Rizzo                 if (sum > 0xFFFF)
576f8e4e36aSLuigi Rizzo                         sum -= 0xFFFF;
577f8e4e36aSLuigi Rizzo         }
578f8e4e36aSLuigi Rizzo 	/*
579f8e4e36aSLuigi Rizzo 	 * If there's a single byte left over, checksum it, too.
580f8e4e36aSLuigi Rizzo 	 * Network byte order is big-endian, so the remaining byte is
581f8e4e36aSLuigi Rizzo 	 * the high byte.
582f8e4e36aSLuigi Rizzo 	 */
583f8e4e36aSLuigi Rizzo 	if (i < len) {
584f8e4e36aSLuigi Rizzo 		sum += addr[i] << 8;
585f8e4e36aSLuigi Rizzo 		if (sum > 0xFFFF)
586f8e4e36aSLuigi Rizzo 			sum -= 0xFFFF;
587f8e4e36aSLuigi Rizzo 	}
588f8e4e36aSLuigi Rizzo 	return sum;
58968b8534bSLuigi Rizzo }
59068b8534bSLuigi Rizzo 
591f8e4e36aSLuigi Rizzo static u_int16_t
592f8e4e36aSLuigi Rizzo wrapsum(u_int32_t sum)
593f8e4e36aSLuigi Rizzo {
594f8e4e36aSLuigi Rizzo 	sum = ~sum & 0xFFFF;
595f8e4e36aSLuigi Rizzo 	return (htons(sum));
59668b8534bSLuigi Rizzo }
59768b8534bSLuigi Rizzo 
598b303f675SLuigi Rizzo /* Check the payload of the packet for errors (use it for debug).
599b303f675SLuigi Rizzo  * Look for consecutive ascii representations of the size of the packet.
600b303f675SLuigi Rizzo  */
601b303f675SLuigi Rizzo static void
60237e3a6d3SLuigi Rizzo dump_payload(const char *_p, int len, struct netmap_ring *ring, int cur)
603b303f675SLuigi Rizzo {
604b303f675SLuigi Rizzo 	char buf[128];
605b303f675SLuigi Rizzo 	int i, j, i0;
60637e3a6d3SLuigi Rizzo 	const unsigned char *p = (const unsigned char *)_p;
607b303f675SLuigi Rizzo 
608b303f675SLuigi Rizzo 	/* get the length in ASCII of the length of the packet. */
609b303f675SLuigi Rizzo 
610ce3ee1e7SLuigi Rizzo 	printf("ring %p cur %5d [buf %6d flags 0x%04x len %5d]\n",
611ce3ee1e7SLuigi Rizzo 		ring, cur, ring->slot[cur].buf_idx,
612ce3ee1e7SLuigi Rizzo 		ring->slot[cur].flags, len);
613b303f675SLuigi Rizzo 	/* hexdump routine */
614b303f675SLuigi Rizzo 	for (i = 0; i < len; ) {
615b797f66cSConrad Meyer 		memset(buf, ' ', sizeof(buf));
616b303f675SLuigi Rizzo 		sprintf(buf, "%5d: ", i);
617b303f675SLuigi Rizzo 		i0 = i;
618b303f675SLuigi Rizzo 		for (j=0; j < 16 && i < len; i++, j++)
619b303f675SLuigi Rizzo 			sprintf(buf+7+j*3, "%02x ", (uint8_t)(p[i]));
620b303f675SLuigi Rizzo 		i = i0;
621b303f675SLuigi Rizzo 		for (j=0; j < 16 && i < len; i++, j++)
622b303f675SLuigi Rizzo 			sprintf(buf+7+j + 48, "%c",
623b303f675SLuigi Rizzo 				isprint(p[i]) ? p[i] : '.');
624b303f675SLuigi Rizzo 		printf("%s\n", buf);
625b303f675SLuigi Rizzo 	}
626b303f675SLuigi Rizzo }
627b303f675SLuigi Rizzo 
62868b8534bSLuigi Rizzo /*
62968b8534bSLuigi Rizzo  * Fill a packet with some payload.
630f8e4e36aSLuigi Rizzo  * We create a UDP packet so the payload starts at
631f8e4e36aSLuigi Rizzo  *	14+20+8 = 42 bytes.
63268b8534bSLuigi Rizzo  */
633f8e4e36aSLuigi Rizzo #ifdef __linux__
634f8e4e36aSLuigi Rizzo #define uh_sport source
635f8e4e36aSLuigi Rizzo #define uh_dport dest
636f8e4e36aSLuigi Rizzo #define uh_ulen len
637f8e4e36aSLuigi Rizzo #define uh_sum check
638f8e4e36aSLuigi Rizzo #endif /* linux */
639b303f675SLuigi Rizzo 
640ce3ee1e7SLuigi Rizzo /*
641ce3ee1e7SLuigi Rizzo  * increment the addressed in the packet,
642ce3ee1e7SLuigi Rizzo  * starting from the least significant field.
643ce3ee1e7SLuigi Rizzo  *	DST_IP DST_PORT SRC_IP SRC_PORT
644ce3ee1e7SLuigi Rizzo  */
645ce3ee1e7SLuigi Rizzo static void
646ce3ee1e7SLuigi Rizzo update_addresses(struct pkt *pkt, struct glob_arg *g)
647ce3ee1e7SLuigi Rizzo {
648ce3ee1e7SLuigi Rizzo 	uint32_t a;
649ce3ee1e7SLuigi Rizzo 	uint16_t p;
650ce3ee1e7SLuigi Rizzo 	struct ip *ip = &pkt->ip;
651ce3ee1e7SLuigi Rizzo 	struct udphdr *udp = &pkt->udp;
652ce3ee1e7SLuigi Rizzo 
653f2637526SLuigi Rizzo     do {
65456717743SAdrian Chadd     	/* XXX for now it doesn't handle non-random src, random dst */
65556717743SAdrian Chadd 	if (g->options & OPT_RANDOM_SRC) {
65656717743SAdrian Chadd 		udp->uh_sport = random();
65756717743SAdrian Chadd 		ip->ip_src.s_addr = random();
65856717743SAdrian Chadd 	} else {
659ce3ee1e7SLuigi Rizzo 		p = ntohs(udp->uh_sport);
660ce3ee1e7SLuigi Rizzo 		if (p < g->src_ip.port1) { /* just inc, no wrap */
661ce3ee1e7SLuigi Rizzo 			udp->uh_sport = htons(p + 1);
662f2637526SLuigi Rizzo 			break;
663ce3ee1e7SLuigi Rizzo 		}
664ce3ee1e7SLuigi Rizzo 		udp->uh_sport = htons(g->src_ip.port0);
665ce3ee1e7SLuigi Rizzo 
666ce3ee1e7SLuigi Rizzo 		a = ntohl(ip->ip_src.s_addr);
667ce3ee1e7SLuigi Rizzo 		if (a < g->src_ip.end) { /* just inc, no wrap */
668ce3ee1e7SLuigi Rizzo 			ip->ip_src.s_addr = htonl(a + 1);
669f2637526SLuigi Rizzo 			break;
670ce3ee1e7SLuigi Rizzo 		}
671ce3ee1e7SLuigi Rizzo 		ip->ip_src.s_addr = htonl(g->src_ip.start);
672ce3ee1e7SLuigi Rizzo 
673ce3ee1e7SLuigi Rizzo 		udp->uh_sport = htons(g->src_ip.port0);
67456717743SAdrian Chadd 	}
67556717743SAdrian Chadd 
67656717743SAdrian Chadd 	if (g->options & OPT_RANDOM_DST) {
67756717743SAdrian Chadd 		udp->uh_dport = random();
67856717743SAdrian Chadd 		ip->ip_dst.s_addr = random();
67956717743SAdrian Chadd 	} else {
680ce3ee1e7SLuigi Rizzo 		p = ntohs(udp->uh_dport);
681ce3ee1e7SLuigi Rizzo 		if (p < g->dst_ip.port1) { /* just inc, no wrap */
682ce3ee1e7SLuigi Rizzo 			udp->uh_dport = htons(p + 1);
683f2637526SLuigi Rizzo 			break;
684ce3ee1e7SLuigi Rizzo 		}
685ce3ee1e7SLuigi Rizzo 		udp->uh_dport = htons(g->dst_ip.port0);
686ce3ee1e7SLuigi Rizzo 
687ce3ee1e7SLuigi Rizzo 		a = ntohl(ip->ip_dst.s_addr);
688ce3ee1e7SLuigi Rizzo 		if (a < g->dst_ip.end) { /* just inc, no wrap */
689ce3ee1e7SLuigi Rizzo 			ip->ip_dst.s_addr = htonl(a + 1);
690f2637526SLuigi Rizzo 			break;
691ce3ee1e7SLuigi Rizzo 		}
69256717743SAdrian Chadd 	}
693ce3ee1e7SLuigi Rizzo 	ip->ip_dst.s_addr = htonl(g->dst_ip.start);
694f2637526SLuigi Rizzo     } while (0);
695f2637526SLuigi Rizzo     // update checksum
696ce3ee1e7SLuigi Rizzo }
697ce3ee1e7SLuigi Rizzo 
698ce3ee1e7SLuigi Rizzo /*
699ce3ee1e7SLuigi Rizzo  * initialize one packet and prepare for the next one.
700ce3ee1e7SLuigi Rizzo  * The copy could be done better instead of repeating it each time.
701ce3ee1e7SLuigi Rizzo  */
70268b8534bSLuigi Rizzo static void
70368b8534bSLuigi Rizzo initialize_packet(struct targ *targ)
70468b8534bSLuigi Rizzo {
70568b8534bSLuigi Rizzo 	struct pkt *pkt = &targ->pkt;
70668b8534bSLuigi Rizzo 	struct ether_header *eh;
70768b8534bSLuigi Rizzo 	struct ip *ip;
70868b8534bSLuigi Rizzo 	struct udphdr *udp;
709f8e4e36aSLuigi Rizzo 	uint16_t paylen = targ->g->pkt_size - sizeof(*eh) - sizeof(struct ip);
710b303f675SLuigi Rizzo 	const char *payload = targ->g->options & OPT_INDIRECT ?
711ce3ee1e7SLuigi Rizzo 		indirect_payload : default_payload;
712f2637526SLuigi Rizzo 	int i, l0 = strlen(payload);
71368b8534bSLuigi Rizzo 
71437e3a6d3SLuigi Rizzo #ifndef NO_PCAP
715f284c737SGeorge V. Neville-Neil 	char errbuf[PCAP_ERRBUF_SIZE];
716f284c737SGeorge V. Neville-Neil 	pcap_t *file;
717f284c737SGeorge V. Neville-Neil 	struct pcap_pkthdr *header;
718f284c737SGeorge V. Neville-Neil 	const unsigned char *packet;
719f284c737SGeorge V. Neville-Neil 
720f284c737SGeorge V. Neville-Neil 	/* Read a packet from a PCAP file if asked. */
721f284c737SGeorge V. Neville-Neil 	if (targ->g->packet_file != NULL) {
722f284c737SGeorge V. Neville-Neil 		if ((file = pcap_open_offline(targ->g->packet_file,
723f284c737SGeorge V. Neville-Neil 			    errbuf)) == NULL)
724f284c737SGeorge V. Neville-Neil 			D("failed to open pcap file %s",
725f284c737SGeorge V. Neville-Neil 			    targ->g->packet_file);
726f284c737SGeorge V. Neville-Neil 		if (pcap_next_ex(file, &header, &packet) < 0)
727f284c737SGeorge V. Neville-Neil 			D("failed to read packet from %s",
728f284c737SGeorge V. Neville-Neil 			    targ->g->packet_file);
729f284c737SGeorge V. Neville-Neil 		if ((targ->frame = malloc(header->caplen)) == NULL)
730f284c737SGeorge V. Neville-Neil 			D("out of memory");
731f284c737SGeorge V. Neville-Neil 		bcopy(packet, (unsigned char *)targ->frame, header->caplen);
732f284c737SGeorge V. Neville-Neil 		targ->g->pkt_size = header->caplen;
733f284c737SGeorge V. Neville-Neil 		pcap_close(file);
734f284c737SGeorge V. Neville-Neil 		return;
735f284c737SGeorge V. Neville-Neil 	}
73637e3a6d3SLuigi Rizzo #endif
737f284c737SGeorge V. Neville-Neil 
738ce3ee1e7SLuigi Rizzo 	/* create a nice NUL-terminated string */
739f2637526SLuigi Rizzo 	for (i = 0; i < paylen; i += l0) {
740f2637526SLuigi Rizzo 		if (l0 > paylen - i)
741f2637526SLuigi Rizzo 			l0 = paylen - i; // last round
742f2637526SLuigi Rizzo 		bcopy(payload, pkt->body + i, l0);
74368b8534bSLuigi Rizzo 	}
74468b8534bSLuigi Rizzo 	pkt->body[i-1] = '\0';
74568b8534bSLuigi Rizzo 	ip = &pkt->ip;
746f8e4e36aSLuigi Rizzo 
747ce3ee1e7SLuigi Rizzo 	/* prepare the headers */
74868b8534bSLuigi Rizzo         ip->ip_v = IPVERSION;
74968b8534bSLuigi Rizzo         ip->ip_hl = 5;
75068b8534bSLuigi Rizzo         ip->ip_id = 0;
75168b8534bSLuigi Rizzo         ip->ip_tos = IPTOS_LOWDELAY;
75268b8534bSLuigi Rizzo 	ip->ip_len = ntohs(targ->g->pkt_size - sizeof(*eh));
75368b8534bSLuigi Rizzo         ip->ip_id = 0;
75468b8534bSLuigi Rizzo         ip->ip_off = htons(IP_DF); /* Don't fragment */
75568b8534bSLuigi Rizzo         ip->ip_ttl = IPDEFTTL;
75668b8534bSLuigi Rizzo 	ip->ip_p = IPPROTO_UDP;
757ce3ee1e7SLuigi Rizzo 	ip->ip_dst.s_addr = htonl(targ->g->dst_ip.start);
758ce3ee1e7SLuigi Rizzo 	ip->ip_src.s_addr = htonl(targ->g->src_ip.start);
759f8e4e36aSLuigi Rizzo 	ip->ip_sum = wrapsum(checksum(ip, sizeof(*ip), 0));
760f8e4e36aSLuigi Rizzo 
761f8e4e36aSLuigi Rizzo 
762f8e4e36aSLuigi Rizzo 	udp = &pkt->udp;
763ce3ee1e7SLuigi Rizzo         udp->uh_sport = htons(targ->g->src_ip.port0);
764ce3ee1e7SLuigi Rizzo         udp->uh_dport = htons(targ->g->dst_ip.port0);
765f8e4e36aSLuigi Rizzo 	udp->uh_ulen = htons(paylen);
766f8e4e36aSLuigi Rizzo 	/* Magic: taken from sbin/dhclient/packet.c */
767f8e4e36aSLuigi Rizzo 	udp->uh_sum = wrapsum(checksum(udp, sizeof(*udp),
768f8e4e36aSLuigi Rizzo                     checksum(pkt->body,
769f8e4e36aSLuigi Rizzo                         paylen - sizeof(*udp),
770f8e4e36aSLuigi Rizzo                         checksum(&ip->ip_src, 2 * sizeof(ip->ip_src),
771f8e4e36aSLuigi Rizzo                             IPPROTO_UDP + (u_int32_t)ntohs(udp->uh_ulen)
772f8e4e36aSLuigi Rizzo                         )
773f8e4e36aSLuigi Rizzo                     )
774f8e4e36aSLuigi Rizzo                 ));
77568b8534bSLuigi Rizzo 
77668b8534bSLuigi Rizzo 	eh = &pkt->eh;
777f8e4e36aSLuigi Rizzo 	bcopy(&targ->g->src_mac.start, eh->ether_shost, 6);
778f8e4e36aSLuigi Rizzo 	bcopy(&targ->g->dst_mac.start, eh->ether_dhost, 6);
77968b8534bSLuigi Rizzo 	eh->ether_type = htons(ETHERTYPE_IP);
78017885a7bSLuigi Rizzo 
78117885a7bSLuigi Rizzo 	bzero(&pkt->vh, sizeof(pkt->vh));
782b303f675SLuigi Rizzo 	// dump_payload((void *)pkt, targ->g->pkt_size, NULL, 0);
78368b8534bSLuigi Rizzo }
78468b8534bSLuigi Rizzo 
7854bf50f18SLuigi Rizzo static void
78637e3a6d3SLuigi Rizzo get_vnet_hdr_len(struct glob_arg *g)
7874bf50f18SLuigi Rizzo {
78837e3a6d3SLuigi Rizzo 	struct nmreq req;
78937e3a6d3SLuigi Rizzo 	int err;
79037e3a6d3SLuigi Rizzo 
79137e3a6d3SLuigi Rizzo 	memset(&req, 0, sizeof(req));
79237e3a6d3SLuigi Rizzo 	bcopy(g->nmd->req.nr_name, req.nr_name, sizeof(req.nr_name));
79337e3a6d3SLuigi Rizzo 	req.nr_version = NETMAP_API;
79437e3a6d3SLuigi Rizzo 	req.nr_cmd = NETMAP_VNET_HDR_GET;
79537e3a6d3SLuigi Rizzo 	err = ioctl(g->main_fd, NIOCREGIF, &req);
79637e3a6d3SLuigi Rizzo 	if (err) {
79737e3a6d3SLuigi Rizzo 		D("Unable to get virtio-net header length");
79837e3a6d3SLuigi Rizzo 		return;
79937e3a6d3SLuigi Rizzo 	}
80037e3a6d3SLuigi Rizzo 
80137e3a6d3SLuigi Rizzo 	g->virt_header = req.nr_arg1;
80237e3a6d3SLuigi Rizzo 	if (g->virt_header) {
80337e3a6d3SLuigi Rizzo 		D("Port requires virtio-net header, length = %d",
80437e3a6d3SLuigi Rizzo 		  g->virt_header);
80537e3a6d3SLuigi Rizzo 	}
80637e3a6d3SLuigi Rizzo }
80737e3a6d3SLuigi Rizzo 
80837e3a6d3SLuigi Rizzo static void
80937e3a6d3SLuigi Rizzo set_vnet_hdr_len(struct glob_arg *g)
81037e3a6d3SLuigi Rizzo {
81137e3a6d3SLuigi Rizzo 	int err, l = g->virt_header;
8124bf50f18SLuigi Rizzo 	struct nmreq req;
8134bf50f18SLuigi Rizzo 
8144bf50f18SLuigi Rizzo 	if (l == 0)
8154bf50f18SLuigi Rizzo 		return;
8164bf50f18SLuigi Rizzo 
8174bf50f18SLuigi Rizzo 	memset(&req, 0, sizeof(req));
81837e3a6d3SLuigi Rizzo 	bcopy(g->nmd->req.nr_name, req.nr_name, sizeof(req.nr_name));
8194bf50f18SLuigi Rizzo 	req.nr_version = NETMAP_API;
8204bf50f18SLuigi Rizzo 	req.nr_cmd = NETMAP_BDG_VNET_HDR;
8214bf50f18SLuigi Rizzo 	req.nr_arg1 = l;
82237e3a6d3SLuigi Rizzo 	err = ioctl(g->main_fd, NIOCREGIF, &req);
8234bf50f18SLuigi Rizzo 	if (err) {
82437e3a6d3SLuigi Rizzo 		D("Unable to set virtio-net header length %d", l);
8254bf50f18SLuigi Rizzo 	}
8264bf50f18SLuigi Rizzo }
82768b8534bSLuigi Rizzo 
82868b8534bSLuigi Rizzo 
82968b8534bSLuigi Rizzo /*
83068b8534bSLuigi Rizzo  * create and enqueue a batch of packets on a ring.
83168b8534bSLuigi Rizzo  * On the last one set NS_REPORT to tell the driver to generate
83268b8534bSLuigi Rizzo  * an interrupt when done.
83368b8534bSLuigi Rizzo  */
83468b8534bSLuigi Rizzo static int
83517885a7bSLuigi Rizzo send_packets(struct netmap_ring *ring, struct pkt *pkt, void *frame,
83617885a7bSLuigi Rizzo 		int size, struct glob_arg *g, u_int count, int options,
83717885a7bSLuigi Rizzo 		u_int nfrags)
83868b8534bSLuigi Rizzo {
83917885a7bSLuigi Rizzo 	u_int n, sent, cur = ring->cur;
840f2637526SLuigi Rizzo 	u_int fcnt;
84168b8534bSLuigi Rizzo 
84217885a7bSLuigi Rizzo 	n = nm_ring_space(ring);
84317885a7bSLuigi Rizzo 	if (n < count)
84417885a7bSLuigi Rizzo 		count = n;
845ce3ee1e7SLuigi Rizzo 	if (count < nfrags) {
846ce3ee1e7SLuigi Rizzo 		D("truncating packet, no room for frags %d %d",
847ce3ee1e7SLuigi Rizzo 				count, nfrags);
848ce3ee1e7SLuigi Rizzo 	}
84999fb123fSLuigi Rizzo #if 0
85099fb123fSLuigi Rizzo 	if (options & (OPT_COPY | OPT_PREFETCH) ) {
85168b8534bSLuigi Rizzo 		for (sent = 0; sent < count; sent++) {
85268b8534bSLuigi Rizzo 			struct netmap_slot *slot = &ring->slot[cur];
85368b8534bSLuigi Rizzo 			char *p = NETMAP_BUF(ring, slot->buf_idx);
85468b8534bSLuigi Rizzo 
855f2637526SLuigi Rizzo 			__builtin_prefetch(p);
85617885a7bSLuigi Rizzo 			cur = nm_ring_next(ring, cur);
85799fb123fSLuigi Rizzo 		}
85899fb123fSLuigi Rizzo 		cur = ring->cur;
85999fb123fSLuigi Rizzo 	}
86099fb123fSLuigi Rizzo #endif
861ce3ee1e7SLuigi Rizzo 	for (fcnt = nfrags, sent = 0; sent < count; sent++) {
86299fb123fSLuigi Rizzo 		struct netmap_slot *slot = &ring->slot[cur];
86399fb123fSLuigi Rizzo 		char *p = NETMAP_BUF(ring, slot->buf_idx);
86437e3a6d3SLuigi Rizzo 		int buf_changed = slot->flags & NS_BUF_CHANGED;
86599fb123fSLuigi Rizzo 
866b303f675SLuigi Rizzo 		slot->flags = 0;
86737e3a6d3SLuigi Rizzo 		if (options & OPT_RUBBISH) {
86837e3a6d3SLuigi Rizzo 			/* do nothing */
86937e3a6d3SLuigi Rizzo 		} else if (options & OPT_INDIRECT) {
870b303f675SLuigi Rizzo 			slot->flags |= NS_INDIRECT;
87137e3a6d3SLuigi Rizzo 			slot->ptr = (uint64_t)((uintptr_t)frame);
87237e3a6d3SLuigi Rizzo 		} else if ((options & OPT_COPY) || buf_changed) {
873f0ea3689SLuigi Rizzo 			nm_pkt_copy(frame, p, size);
874f2637526SLuigi Rizzo 			if (fcnt == nfrags)
875ce3ee1e7SLuigi Rizzo 				update_addresses(pkt, g);
876ce3ee1e7SLuigi Rizzo 		} else if (options & OPT_MEMCPY) {
87717885a7bSLuigi Rizzo 			memcpy(p, frame, size);
878f2637526SLuigi Rizzo 			if (fcnt == nfrags)
879ce3ee1e7SLuigi Rizzo 				update_addresses(pkt, g);
880ce3ee1e7SLuigi Rizzo 		} else if (options & OPT_PREFETCH) {
881f2637526SLuigi Rizzo 			__builtin_prefetch(p);
882ce3ee1e7SLuigi Rizzo 		}
883ce3ee1e7SLuigi Rizzo 		if (options & OPT_DUMP)
884ce3ee1e7SLuigi Rizzo 			dump_payload(p, size, ring, cur);
88568b8534bSLuigi Rizzo 		slot->len = size;
886ce3ee1e7SLuigi Rizzo 		if (--fcnt > 0)
887ce3ee1e7SLuigi Rizzo 			slot->flags |= NS_MOREFRAG;
888ce3ee1e7SLuigi Rizzo 		else
889ce3ee1e7SLuigi Rizzo 			fcnt = nfrags;
890ce3ee1e7SLuigi Rizzo 		if (sent == count - 1) {
891ce3ee1e7SLuigi Rizzo 			slot->flags &= ~NS_MOREFRAG;
89268b8534bSLuigi Rizzo 			slot->flags |= NS_REPORT;
893ce3ee1e7SLuigi Rizzo 		}
89417885a7bSLuigi Rizzo 		cur = nm_ring_next(ring, cur);
89568b8534bSLuigi Rizzo 	}
89617885a7bSLuigi Rizzo 	ring->head = ring->cur = cur;
89768b8534bSLuigi Rizzo 
89868b8534bSLuigi Rizzo 	return (sent);
89968b8534bSLuigi Rizzo }
90068b8534bSLuigi Rizzo 
901f8e4e36aSLuigi Rizzo /*
90237e3a6d3SLuigi Rizzo  * Index of the highest bit set
90337e3a6d3SLuigi Rizzo  */
90437e3a6d3SLuigi Rizzo uint32_t
90537e3a6d3SLuigi Rizzo msb64(uint64_t x)
90637e3a6d3SLuigi Rizzo {
90737e3a6d3SLuigi Rizzo 	uint64_t m = 1ULL << 63;
90837e3a6d3SLuigi Rizzo 	int i;
90937e3a6d3SLuigi Rizzo 
91037e3a6d3SLuigi Rizzo 	for (i = 63; i >= 0; i--, m >>=1)
91137e3a6d3SLuigi Rizzo 		if (m & x)
91237e3a6d3SLuigi Rizzo 			return i;
91337e3a6d3SLuigi Rizzo 	return 0;
91437e3a6d3SLuigi Rizzo }
91537e3a6d3SLuigi Rizzo 
91637e3a6d3SLuigi Rizzo /*
917f8e4e36aSLuigi Rizzo  * Send a packet, and wait for a response.
918f8e4e36aSLuigi Rizzo  * The payload (after UDP header, ofs 42) has a 4-byte sequence
919f8e4e36aSLuigi Rizzo  * followed by a struct timeval (or bintime?)
920f8e4e36aSLuigi Rizzo  */
921f8e4e36aSLuigi Rizzo #define	PAY_OFS	42	/* where in the pkt... */
922f8e4e36aSLuigi Rizzo 
92368b8534bSLuigi Rizzo static void *
924f8e4e36aSLuigi Rizzo pinger_body(void *data)
92568b8534bSLuigi Rizzo {
92668b8534bSLuigi Rizzo 	struct targ *targ = (struct targ *) data;
927f0ea3689SLuigi Rizzo 	struct pollfd pfd = { .fd = targ->fd, .events = POLLIN };
928f0ea3689SLuigi Rizzo 	struct netmap_if *nifp = targ->nmd->nifp;
92937e3a6d3SLuigi Rizzo 	int i, rx = 0;
93017885a7bSLuigi Rizzo 	void *frame;
93117885a7bSLuigi Rizzo 	int size;
932f0ea3689SLuigi Rizzo 	struct timespec ts, now, last_print;
93337e3a6d3SLuigi Rizzo 	uint64_t sent = 0, n = targ->g->npackets;
93437e3a6d3SLuigi Rizzo 	uint64_t count = 0, t_cur, t_min = ~0, av = 0;
93537e3a6d3SLuigi Rizzo 	uint64_t buckets[64];	/* bins for delays, ns */
93617885a7bSLuigi Rizzo 
93717885a7bSLuigi Rizzo 	frame = &targ->pkt;
93817885a7bSLuigi Rizzo 	frame += sizeof(targ->pkt.vh) - targ->g->virt_header;
93917885a7bSLuigi Rizzo 	size = targ->g->pkt_size + targ->g->virt_header;
940e5ecae38SEd Maste 
94137e3a6d3SLuigi Rizzo 
942f8e4e36aSLuigi Rizzo 	if (targ->g->nthreads > 1) {
943f8e4e36aSLuigi Rizzo 		D("can only ping with 1 thread");
944f8e4e36aSLuigi Rizzo 		return NULL;
945f95a30bdSEd Maste 	}
946f8e4e36aSLuigi Rizzo 
94737e3a6d3SLuigi Rizzo 	bzero(&buckets, sizeof(buckets));
948f8e4e36aSLuigi Rizzo 	clock_gettime(CLOCK_REALTIME_PRECISE, &last_print);
94917885a7bSLuigi Rizzo 	now = last_print;
95037e3a6d3SLuigi Rizzo 	while (!targ->cancel && (n == 0 || sent < n)) {
951f8e4e36aSLuigi Rizzo 		struct netmap_ring *ring = NETMAP_TXRING(nifp, 0);
952f8e4e36aSLuigi Rizzo 		struct netmap_slot *slot;
953f8e4e36aSLuigi Rizzo 		char *p;
95417885a7bSLuigi Rizzo 	    for (i = 0; i < 1; i++) { /* XXX why the loop for 1 pkt ? */
955f8e4e36aSLuigi Rizzo 		slot = &ring->slot[ring->cur];
95617885a7bSLuigi Rizzo 		slot->len = size;
957f8e4e36aSLuigi Rizzo 		p = NETMAP_BUF(ring, slot->buf_idx);
958f8e4e36aSLuigi Rizzo 
95917885a7bSLuigi Rizzo 		if (nm_ring_empty(ring)) {
960f8e4e36aSLuigi Rizzo 			D("-- ouch, cannot send");
961f8e4e36aSLuigi Rizzo 		} else {
9624bf50f18SLuigi Rizzo 			struct tstamp *tp;
963f0ea3689SLuigi Rizzo 			nm_pkt_copy(frame, p, size);
964f8e4e36aSLuigi Rizzo 			clock_gettime(CLOCK_REALTIME_PRECISE, &ts);
965f8e4e36aSLuigi Rizzo 			bcopy(&sent, p+42, sizeof(sent));
9664bf50f18SLuigi Rizzo 			tp = (struct tstamp *)(p+46);
9674bf50f18SLuigi Rizzo 			tp->sec = (uint32_t)ts.tv_sec;
9684bf50f18SLuigi Rizzo 			tp->nsec = (uint32_t)ts.tv_nsec;
969f8e4e36aSLuigi Rizzo 			sent++;
97017885a7bSLuigi Rizzo 			ring->head = ring->cur = nm_ring_next(ring, ring->cur);
971f8e4e36aSLuigi Rizzo 		}
972f8e4e36aSLuigi Rizzo 	    }
973f8e4e36aSLuigi Rizzo 		/* should use a parameter to decide how often to send */
974f0ea3689SLuigi Rizzo 		if (poll(&pfd, 1, 3000) <= 0) {
97517885a7bSLuigi Rizzo 			D("poll error/timeout on queue %d: %s", targ->me,
97617885a7bSLuigi Rizzo 				strerror(errno));
977f8e4e36aSLuigi Rizzo 			continue;
978f8e4e36aSLuigi Rizzo 		}
979f8e4e36aSLuigi Rizzo 		/* see what we got back */
980f0ea3689SLuigi Rizzo 		for (i = targ->nmd->first_tx_ring;
981f0ea3689SLuigi Rizzo 			i <= targ->nmd->last_tx_ring; i++) {
982f8e4e36aSLuigi Rizzo 			ring = NETMAP_RXRING(nifp, i);
98317885a7bSLuigi Rizzo 			while (!nm_ring_empty(ring)) {
984f8e4e36aSLuigi Rizzo 				uint32_t seq;
9854bf50f18SLuigi Rizzo 				struct tstamp *tp;
98637e3a6d3SLuigi Rizzo 				int pos;
98737e3a6d3SLuigi Rizzo 
988f8e4e36aSLuigi Rizzo 				slot = &ring->slot[ring->cur];
989f8e4e36aSLuigi Rizzo 				p = NETMAP_BUF(ring, slot->buf_idx);
990f8e4e36aSLuigi Rizzo 
991f8e4e36aSLuigi Rizzo 				clock_gettime(CLOCK_REALTIME_PRECISE, &now);
992f8e4e36aSLuigi Rizzo 				bcopy(p+42, &seq, sizeof(seq));
9934bf50f18SLuigi Rizzo 				tp = (struct tstamp *)(p+46);
9944bf50f18SLuigi Rizzo 				ts.tv_sec = (time_t)tp->sec;
9954bf50f18SLuigi Rizzo 				ts.tv_nsec = (long)tp->nsec;
996f8e4e36aSLuigi Rizzo 				ts.tv_sec = now.tv_sec - ts.tv_sec;
997f8e4e36aSLuigi Rizzo 				ts.tv_nsec = now.tv_nsec - ts.tv_nsec;
998f8e4e36aSLuigi Rizzo 				if (ts.tv_nsec < 0) {
999f8e4e36aSLuigi Rizzo 					ts.tv_nsec += 1000000000;
1000f8e4e36aSLuigi Rizzo 					ts.tv_sec--;
1001f8e4e36aSLuigi Rizzo 				}
100237e3a6d3SLuigi Rizzo 				if (0) D("seq %d/%lu delta %d.%09d", seq, sent,
1003f8e4e36aSLuigi Rizzo 					(int)ts.tv_sec, (int)ts.tv_nsec);
100437e3a6d3SLuigi Rizzo 				t_cur = ts.tv_sec * 1000000000UL + ts.tv_nsec;
100537e3a6d3SLuigi Rizzo 				if (t_cur < t_min)
100637e3a6d3SLuigi Rizzo 					t_min = t_cur;
1007f8e4e36aSLuigi Rizzo 				count ++;
100837e3a6d3SLuigi Rizzo 				av += t_cur;
100937e3a6d3SLuigi Rizzo 				pos = msb64(t_cur);
101037e3a6d3SLuigi Rizzo 				buckets[pos]++;
101137e3a6d3SLuigi Rizzo 				/* now store it in a bucket */
101217885a7bSLuigi Rizzo 				ring->head = ring->cur = nm_ring_next(ring, ring->cur);
1013f8e4e36aSLuigi Rizzo 				rx++;
1014f8e4e36aSLuigi Rizzo 			}
1015f8e4e36aSLuigi Rizzo 		}
1016f8e4e36aSLuigi Rizzo 		//D("tx %d rx %d", sent, rx);
1017f8e4e36aSLuigi Rizzo 		//usleep(100000);
1018f8e4e36aSLuigi Rizzo 		ts.tv_sec = now.tv_sec - last_print.tv_sec;
1019f8e4e36aSLuigi Rizzo 		ts.tv_nsec = now.tv_nsec - last_print.tv_nsec;
1020f8e4e36aSLuigi Rizzo 		if (ts.tv_nsec < 0) {
1021f8e4e36aSLuigi Rizzo 			ts.tv_nsec += 1000000000;
1022f8e4e36aSLuigi Rizzo 			ts.tv_sec--;
1023f8e4e36aSLuigi Rizzo 		}
1024f8e4e36aSLuigi Rizzo 		if (ts.tv_sec >= 1) {
102537e3a6d3SLuigi Rizzo 			D("count %d RTT: min %d av %d ns",
102637e3a6d3SLuigi Rizzo 				(int)count, (int)t_min, (int)(av/count));
102737e3a6d3SLuigi Rizzo 			int k, j, kmin;
102837e3a6d3SLuigi Rizzo 			char buf[512];
102937e3a6d3SLuigi Rizzo 
103037e3a6d3SLuigi Rizzo 			for (kmin = 0; kmin < 64; kmin ++)
103137e3a6d3SLuigi Rizzo 				if (buckets[kmin])
103237e3a6d3SLuigi Rizzo 					break;
103337e3a6d3SLuigi Rizzo 			for (k = 63; k >= kmin; k--)
103437e3a6d3SLuigi Rizzo 				if (buckets[k])
103537e3a6d3SLuigi Rizzo 					break;
103637e3a6d3SLuigi Rizzo 			buf[0] = '\0';
103737e3a6d3SLuigi Rizzo 			for (j = kmin; j <= k; j++)
103837e3a6d3SLuigi Rizzo 				sprintf(buf, "%s %5d", buf, (int)buckets[j]);
103937e3a6d3SLuigi Rizzo 			D("k: %d .. %d\n\t%s", 1<<kmin, 1<<k, buf);
104037e3a6d3SLuigi Rizzo 			bzero(&buckets, sizeof(buckets));
1041f8e4e36aSLuigi Rizzo 			count = 0;
1042f8e4e36aSLuigi Rizzo 			av = 0;
104337e3a6d3SLuigi Rizzo 			t_min = ~0;
1044f8e4e36aSLuigi Rizzo 			last_print = now;
1045f8e4e36aSLuigi Rizzo 		}
1046f8e4e36aSLuigi Rizzo 	}
104737e3a6d3SLuigi Rizzo 
104837e3a6d3SLuigi Rizzo 	/* reset the ``used`` flag. */
104937e3a6d3SLuigi Rizzo 	targ->used = 0;
105037e3a6d3SLuigi Rizzo 
1051f8e4e36aSLuigi Rizzo 	return NULL;
1052f8e4e36aSLuigi Rizzo }
1053f8e4e36aSLuigi Rizzo 
1054f8e4e36aSLuigi Rizzo 
1055f8e4e36aSLuigi Rizzo /*
1056f8e4e36aSLuigi Rizzo  * reply to ping requests
1057f8e4e36aSLuigi Rizzo  */
1058f8e4e36aSLuigi Rizzo static void *
1059f8e4e36aSLuigi Rizzo ponger_body(void *data)
1060f8e4e36aSLuigi Rizzo {
1061f8e4e36aSLuigi Rizzo 	struct targ *targ = (struct targ *) data;
1062f0ea3689SLuigi Rizzo 	struct pollfd pfd = { .fd = targ->fd, .events = POLLIN };
1063f0ea3689SLuigi Rizzo 	struct netmap_if *nifp = targ->nmd->nifp;
1064f8e4e36aSLuigi Rizzo 	struct netmap_ring *txring, *rxring;
106537e3a6d3SLuigi Rizzo 	int i, rx = 0;
106637e3a6d3SLuigi Rizzo 	uint64_t sent = 0, n = targ->g->npackets;
1067f8e4e36aSLuigi Rizzo 
1068f8e4e36aSLuigi Rizzo 	if (targ->g->nthreads > 1) {
1069f8e4e36aSLuigi Rizzo 		D("can only reply ping with 1 thread");
1070f8e4e36aSLuigi Rizzo 		return NULL;
1071f8e4e36aSLuigi Rizzo 	}
107237e3a6d3SLuigi Rizzo 	D("understood ponger %lu but don't know how to do it", n);
107337e3a6d3SLuigi Rizzo 	while (!targ->cancel && (n == 0 || sent < n)) {
1074f8e4e36aSLuigi Rizzo 		uint32_t txcur, txavail;
1075f8e4e36aSLuigi Rizzo //#define BUSYWAIT
1076f8e4e36aSLuigi Rizzo #ifdef BUSYWAIT
1077f0ea3689SLuigi Rizzo 		ioctl(pfd.fd, NIOCRXSYNC, NULL);
1078f8e4e36aSLuigi Rizzo #else
1079f0ea3689SLuigi Rizzo 		if (poll(&pfd, 1, 1000) <= 0) {
108017885a7bSLuigi Rizzo 			D("poll error/timeout on queue %d: %s", targ->me,
108117885a7bSLuigi Rizzo 				strerror(errno));
1082f8e4e36aSLuigi Rizzo 			continue;
1083f8e4e36aSLuigi Rizzo 		}
1084f8e4e36aSLuigi Rizzo #endif
1085f8e4e36aSLuigi Rizzo 		txring = NETMAP_TXRING(nifp, 0);
1086f8e4e36aSLuigi Rizzo 		txcur = txring->cur;
108717885a7bSLuigi Rizzo 		txavail = nm_ring_space(txring);
1088f8e4e36aSLuigi Rizzo 		/* see what we got back */
1089f0ea3689SLuigi Rizzo 		for (i = targ->nmd->first_rx_ring; i <= targ->nmd->last_rx_ring; i++) {
1090f8e4e36aSLuigi Rizzo 			rxring = NETMAP_RXRING(nifp, i);
109117885a7bSLuigi Rizzo 			while (!nm_ring_empty(rxring)) {
1092f8e4e36aSLuigi Rizzo 				uint16_t *spkt, *dpkt;
1093f8e4e36aSLuigi Rizzo 				uint32_t cur = rxring->cur;
1094f8e4e36aSLuigi Rizzo 				struct netmap_slot *slot = &rxring->slot[cur];
1095f8e4e36aSLuigi Rizzo 				char *src, *dst;
1096f8e4e36aSLuigi Rizzo 				src = NETMAP_BUF(rxring, slot->buf_idx);
1097f8e4e36aSLuigi Rizzo 				//D("got pkt %p of size %d", src, slot->len);
109817885a7bSLuigi Rizzo 				rxring->head = rxring->cur = nm_ring_next(rxring, cur);
1099f8e4e36aSLuigi Rizzo 				rx++;
1100f8e4e36aSLuigi Rizzo 				if (txavail == 0)
1101f8e4e36aSLuigi Rizzo 					continue;
1102f8e4e36aSLuigi Rizzo 				dst = NETMAP_BUF(txring,
1103f8e4e36aSLuigi Rizzo 				    txring->slot[txcur].buf_idx);
1104f8e4e36aSLuigi Rizzo 				/* copy... */
1105f8e4e36aSLuigi Rizzo 				dpkt = (uint16_t *)dst;
1106f8e4e36aSLuigi Rizzo 				spkt = (uint16_t *)src;
1107f0ea3689SLuigi Rizzo 				nm_pkt_copy(src, dst, slot->len);
1108f8e4e36aSLuigi Rizzo 				dpkt[0] = spkt[3];
1109f8e4e36aSLuigi Rizzo 				dpkt[1] = spkt[4];
1110f8e4e36aSLuigi Rizzo 				dpkt[2] = spkt[5];
1111f8e4e36aSLuigi Rizzo 				dpkt[3] = spkt[0];
1112f8e4e36aSLuigi Rizzo 				dpkt[4] = spkt[1];
1113f8e4e36aSLuigi Rizzo 				dpkt[5] = spkt[2];
1114f8e4e36aSLuigi Rizzo 				txring->slot[txcur].len = slot->len;
1115f8e4e36aSLuigi Rizzo 				/* XXX swap src dst mac */
111617885a7bSLuigi Rizzo 				txcur = nm_ring_next(txring, txcur);
1117f8e4e36aSLuigi Rizzo 				txavail--;
1118f8e4e36aSLuigi Rizzo 				sent++;
1119f8e4e36aSLuigi Rizzo 			}
1120f8e4e36aSLuigi Rizzo 		}
112117885a7bSLuigi Rizzo 		txring->head = txring->cur = txcur;
112237e3a6d3SLuigi Rizzo 		targ->ctr.pkts = sent;
1123f8e4e36aSLuigi Rizzo #ifdef BUSYWAIT
1124f0ea3689SLuigi Rizzo 		ioctl(pfd.fd, NIOCTXSYNC, NULL);
1125f8e4e36aSLuigi Rizzo #endif
1126f8e4e36aSLuigi Rizzo 		//D("tx %d rx %d", sent, rx);
1127f8e4e36aSLuigi Rizzo 	}
112837e3a6d3SLuigi Rizzo 
112937e3a6d3SLuigi Rizzo 	/* reset the ``used`` flag. */
113037e3a6d3SLuigi Rizzo 	targ->used = 0;
113137e3a6d3SLuigi Rizzo 
1132f8e4e36aSLuigi Rizzo 	return NULL;
1133f8e4e36aSLuigi Rizzo }
1134f8e4e36aSLuigi Rizzo 
1135f8e4e36aSLuigi Rizzo 
113617885a7bSLuigi Rizzo /*
113717885a7bSLuigi Rizzo  * wait until ts, either busy or sleeping if more than 1ms.
113817885a7bSLuigi Rizzo  * Return wakeup time.
113917885a7bSLuigi Rizzo  */
114017885a7bSLuigi Rizzo static struct timespec
114117885a7bSLuigi Rizzo wait_time(struct timespec ts)
114217885a7bSLuigi Rizzo {
114317885a7bSLuigi Rizzo 	for (;;) {
114417885a7bSLuigi Rizzo 		struct timespec w, cur;
114517885a7bSLuigi Rizzo 		clock_gettime(CLOCK_REALTIME_PRECISE, &cur);
114617885a7bSLuigi Rizzo 		w = timespec_sub(ts, cur);
114717885a7bSLuigi Rizzo 		if (w.tv_sec < 0)
114817885a7bSLuigi Rizzo 			return cur;
114917885a7bSLuigi Rizzo 		else if (w.tv_sec > 0 || w.tv_nsec > 1000000)
115017885a7bSLuigi Rizzo 			poll(NULL, 0, 1);
115117885a7bSLuigi Rizzo 	}
115217885a7bSLuigi Rizzo }
115317885a7bSLuigi Rizzo 
1154f8e4e36aSLuigi Rizzo static void *
1155f8e4e36aSLuigi Rizzo sender_body(void *data)
1156f8e4e36aSLuigi Rizzo {
1157f8e4e36aSLuigi Rizzo 	struct targ *targ = (struct targ *) data;
1158f0ea3689SLuigi Rizzo 	struct pollfd pfd = { .fd = targ->fd, .events = POLLOUT };
11594bf50f18SLuigi Rizzo 	struct netmap_if *nifp;
116037e3a6d3SLuigi Rizzo 	struct netmap_ring *txring = NULL;
116137e3a6d3SLuigi Rizzo 	int i;
116237e3a6d3SLuigi Rizzo 	uint64_t n = targ->g->npackets / targ->g->nthreads;
116337e3a6d3SLuigi Rizzo 	uint64_t sent = 0;
116437e3a6d3SLuigi Rizzo 	uint64_t event = 0;
1165f8e4e36aSLuigi Rizzo 	int options = targ->g->options | OPT_COPY;
116617885a7bSLuigi Rizzo 	struct timespec nexttime = { 0, 0}; // XXX silence compiler
11671cb4c501SLuigi Rizzo 	int rate_limit = targ->g->tx_rate;
116817885a7bSLuigi Rizzo 	struct pkt *pkt = &targ->pkt;
116917885a7bSLuigi Rizzo 	void *frame;
117017885a7bSLuigi Rizzo 	int size;
117117885a7bSLuigi Rizzo 
1172f284c737SGeorge V. Neville-Neil 	if (targ->frame == NULL) {
117317885a7bSLuigi Rizzo 		frame = pkt;
117417885a7bSLuigi Rizzo 		frame += sizeof(pkt->vh) - targ->g->virt_header;
117517885a7bSLuigi Rizzo 		size = targ->g->pkt_size + targ->g->virt_header;
1176f284c737SGeorge V. Neville-Neil 	} else {
1177f284c737SGeorge V. Neville-Neil 		frame = targ->frame;
1178f284c737SGeorge V. Neville-Neil 		size = targ->g->pkt_size;
1179f284c737SGeorge V. Neville-Neil 	}
1180b303f675SLuigi Rizzo 
11814bf50f18SLuigi Rizzo 	D("start, fd %d main_fd %d", targ->fd, targ->g->main_fd);
118268b8534bSLuigi Rizzo 	if (setaffinity(targ->thread, targ->affinity))
118368b8534bSLuigi Rizzo 		goto quit;
118468b8534bSLuigi Rizzo 
118568b8534bSLuigi Rizzo 	/* main loop.*/
11861cb4c501SLuigi Rizzo 	clock_gettime(CLOCK_REALTIME_PRECISE, &targ->tic);
11871cb4c501SLuigi Rizzo 	if (rate_limit) {
118817885a7bSLuigi Rizzo 		targ->tic = timespec_add(targ->tic, (struct timespec){2,0});
11891cb4c501SLuigi Rizzo 		targ->tic.tv_nsec = 0;
119017885a7bSLuigi Rizzo 		wait_time(targ->tic);
11911cb4c501SLuigi Rizzo 		nexttime = targ->tic;
11921cb4c501SLuigi Rizzo 	}
1193f2637526SLuigi Rizzo         if (targ->g->dev_type == DEV_TAP) {
1194f8e4e36aSLuigi Rizzo 	    D("writing to file desc %d", targ->g->main_fd);
1195f8e4e36aSLuigi Rizzo 
1196f8e4e36aSLuigi Rizzo 	    for (i = 0; !targ->cancel && (n == 0 || sent < n); i++) {
119717885a7bSLuigi Rizzo 		if (write(targ->g->main_fd, frame, size) != -1)
1198f8e4e36aSLuigi Rizzo 			sent++;
1199ce3ee1e7SLuigi Rizzo 		update_addresses(pkt, targ->g);
1200f8e4e36aSLuigi Rizzo 		if (i > 10000) {
120137e3a6d3SLuigi Rizzo 			targ->ctr.pkts = sent;
120237e3a6d3SLuigi Rizzo 			targ->ctr.bytes = sent*size;
120337e3a6d3SLuigi Rizzo 			targ->ctr.events = sent;
1204f8e4e36aSLuigi Rizzo 			i = 0;
1205f8e4e36aSLuigi Rizzo 		}
1206f8e4e36aSLuigi Rizzo 	    }
1207f2637526SLuigi Rizzo #ifndef NO_PCAP
1208f2637526SLuigi Rizzo     } else if (targ->g->dev_type == DEV_PCAP) {
1209f2637526SLuigi Rizzo 	    pcap_t *p = targ->g->p;
1210f2637526SLuigi Rizzo 
1211f2637526SLuigi Rizzo 	    for (i = 0; !targ->cancel && (n == 0 || sent < n); i++) {
1212f2637526SLuigi Rizzo 		if (pcap_inject(p, frame, size) != -1)
1213f2637526SLuigi Rizzo 			sent++;
1214f2637526SLuigi Rizzo 		update_addresses(pkt, targ->g);
1215f2637526SLuigi Rizzo 		if (i > 10000) {
121637e3a6d3SLuigi Rizzo 			targ->ctr.pkts = sent;
121737e3a6d3SLuigi Rizzo 			targ->ctr.bytes = sent*size;
121837e3a6d3SLuigi Rizzo 			targ->ctr.events = sent;
1219f2637526SLuigi Rizzo 			i = 0;
1220f2637526SLuigi Rizzo 		}
1221f2637526SLuigi Rizzo 	    }
1222f2637526SLuigi Rizzo #endif /* NO_PCAP */
122368b8534bSLuigi Rizzo     } else {
12241cb4c501SLuigi Rizzo 	int tosend = 0;
1225ce3ee1e7SLuigi Rizzo 	int frags = targ->g->frags;
1226ce3ee1e7SLuigi Rizzo 
12274bf50f18SLuigi Rizzo 	nifp = targ->nmd->nifp;
1228f8e4e36aSLuigi Rizzo 	while (!targ->cancel && (n == 0 || sent < n)) {
122968b8534bSLuigi Rizzo 
12301cb4c501SLuigi Rizzo 		if (rate_limit && tosend <= 0) {
12311cb4c501SLuigi Rizzo 			tosend = targ->g->burst;
123217885a7bSLuigi Rizzo 			nexttime = timespec_add(nexttime, targ->g->tx_period);
123317885a7bSLuigi Rizzo 			wait_time(nexttime);
12341cb4c501SLuigi Rizzo 		}
12351cb4c501SLuigi Rizzo 
123668b8534bSLuigi Rizzo 		/*
123768b8534bSLuigi Rizzo 		 * wait for available room in the send queue(s)
123868b8534bSLuigi Rizzo 		 */
123937e3a6d3SLuigi Rizzo #ifdef BUSYWAIT
124037e3a6d3SLuigi Rizzo 		if (ioctl(pfd.fd, NIOCTXSYNC, NULL) < 0) {
124137e3a6d3SLuigi Rizzo 			D("ioctl error on queue %d: %s", targ->me,
124237e3a6d3SLuigi Rizzo 					strerror(errno));
124337e3a6d3SLuigi Rizzo 			goto quit;
124437e3a6d3SLuigi Rizzo 		}
124537e3a6d3SLuigi Rizzo #else /* !BUSYWAIT */
1246f0ea3689SLuigi Rizzo 		if (poll(&pfd, 1, 2000) <= 0) {
12473fe77e68SEd Maste 			if (targ->cancel)
12483fe77e68SEd Maste 				break;
124917885a7bSLuigi Rizzo 			D("poll error/timeout on queue %d: %s", targ->me,
125017885a7bSLuigi Rizzo 				strerror(errno));
1251f0ea3689SLuigi Rizzo 			// goto quit;
125217885a7bSLuigi Rizzo 		}
1253f0ea3689SLuigi Rizzo 		if (pfd.revents & POLLERR) {
125437e3a6d3SLuigi Rizzo 			D("poll error on %d ring %d-%d", pfd.fd,
125537e3a6d3SLuigi Rizzo 				targ->nmd->first_tx_ring, targ->nmd->last_tx_ring);
125668b8534bSLuigi Rizzo 			goto quit;
125768b8534bSLuigi Rizzo 		}
125837e3a6d3SLuigi Rizzo #endif /* !BUSYWAIT */
125968b8534bSLuigi Rizzo 		/*
126068b8534bSLuigi Rizzo 		 * scan our queues and send on those with room
126168b8534bSLuigi Rizzo 		 */
1262f8e4e36aSLuigi Rizzo 		if (options & OPT_COPY && sent > 100000 && !(targ->g->options & OPT_COPY) ) {
1263f8e4e36aSLuigi Rizzo 			D("drop copy");
126499fb123fSLuigi Rizzo 			options &= ~OPT_COPY;
1265f8e4e36aSLuigi Rizzo 		}
1266f0ea3689SLuigi Rizzo 		for (i = targ->nmd->first_tx_ring; i <= targ->nmd->last_tx_ring; i++) {
126737e3a6d3SLuigi Rizzo 			int m;
126837e3a6d3SLuigi Rizzo 			uint64_t limit = rate_limit ?  tosend : targ->g->burst;
1269f8e4e36aSLuigi Rizzo 			if (n > 0 && n - sent < limit)
1270f8e4e36aSLuigi Rizzo 				limit = n - sent;
127168b8534bSLuigi Rizzo 			txring = NETMAP_TXRING(nifp, i);
127217885a7bSLuigi Rizzo 			if (nm_ring_empty(txring))
127368b8534bSLuigi Rizzo 				continue;
1274ce3ee1e7SLuigi Rizzo 			if (frags > 1)
1275ce3ee1e7SLuigi Rizzo 				limit = ((limit + frags - 1) / frags) * frags;
1276ce3ee1e7SLuigi Rizzo 
127717885a7bSLuigi Rizzo 			m = send_packets(txring, pkt, frame, size, targ->g,
1278ce3ee1e7SLuigi Rizzo 					 limit, options, frags);
1279f2637526SLuigi Rizzo 			ND("limit %d tail %d frags %d m %d",
1280f2637526SLuigi Rizzo 				limit, txring->tail, frags, m);
128168b8534bSLuigi Rizzo 			sent += m;
128237e3a6d3SLuigi Rizzo 			if (m > 0) //XXX-ste: can m be 0?
128337e3a6d3SLuigi Rizzo 				event++;
128437e3a6d3SLuigi Rizzo 			targ->ctr.pkts = sent;
128537e3a6d3SLuigi Rizzo 			targ->ctr.bytes = sent*size;
128637e3a6d3SLuigi Rizzo 			targ->ctr.events = event;
1287ce3ee1e7SLuigi Rizzo 			if (rate_limit) {
1288ce3ee1e7SLuigi Rizzo 				tosend -= m;
1289ce3ee1e7SLuigi Rizzo 				if (tosend <= 0)
1290ce3ee1e7SLuigi Rizzo 					break;
1291ce3ee1e7SLuigi Rizzo 			}
129268b8534bSLuigi Rizzo 		}
129368b8534bSLuigi Rizzo 	}
129499fb123fSLuigi Rizzo 	/* flush any remaining packets */
12954bf50f18SLuigi Rizzo 	D("flush tail %d head %d on thread %p",
12964bf50f18SLuigi Rizzo 		txring->tail, txring->head,
129737e3a6d3SLuigi Rizzo 		(void *)pthread_self());
1298f0ea3689SLuigi Rizzo 	ioctl(pfd.fd, NIOCTXSYNC, NULL);
129968b8534bSLuigi Rizzo 
130068b8534bSLuigi Rizzo 	/* final part: wait all the TX queues to be empty. */
1301f0ea3689SLuigi Rizzo 	for (i = targ->nmd->first_tx_ring; i <= targ->nmd->last_tx_ring; i++) {
130268b8534bSLuigi Rizzo 		txring = NETMAP_TXRING(nifp, i);
130337e3a6d3SLuigi Rizzo 		while (!targ->cancel && nm_tx_pending(txring)) {
13044bf50f18SLuigi Rizzo 			RD(5, "pending tx tail %d head %d on ring %d",
13054bf50f18SLuigi Rizzo 				txring->tail, txring->head, i);
1306f0ea3689SLuigi Rizzo 			ioctl(pfd.fd, NIOCTXSYNC, NULL);
130768b8534bSLuigi Rizzo 			usleep(1); /* wait 1 tick */
130868b8534bSLuigi Rizzo 		}
130968b8534bSLuigi Rizzo 	}
1310f2637526SLuigi Rizzo     } /* end DEV_NETMAP */
131168b8534bSLuigi Rizzo 
13121cb4c501SLuigi Rizzo 	clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc);
131368b8534bSLuigi Rizzo 	targ->completed = 1;
131437e3a6d3SLuigi Rizzo 	targ->ctr.pkts = sent;
131537e3a6d3SLuigi Rizzo 	targ->ctr.bytes = sent*size;
131637e3a6d3SLuigi Rizzo 	targ->ctr.events = event;
131768b8534bSLuigi Rizzo quit:
131868b8534bSLuigi Rizzo 	/* reset the ``used`` flag. */
131968b8534bSLuigi Rizzo 	targ->used = 0;
132068b8534bSLuigi Rizzo 
132168b8534bSLuigi Rizzo 	return (NULL);
132268b8534bSLuigi Rizzo }
132368b8534bSLuigi Rizzo 
132468b8534bSLuigi Rizzo 
1325f2637526SLuigi Rizzo #ifndef NO_PCAP
132668b8534bSLuigi Rizzo static void
1327f8e4e36aSLuigi Rizzo receive_pcap(u_char *user, const struct pcap_pkthdr * h,
1328f8e4e36aSLuigi Rizzo 	const u_char * bytes)
132968b8534bSLuigi Rizzo {
133037e3a6d3SLuigi Rizzo 	struct my_ctrs *ctr = (struct my_ctrs *)user;
1331f8e4e36aSLuigi Rizzo 	(void)bytes;	/* UNUSED */
133237e3a6d3SLuigi Rizzo 	ctr->bytes += h->len;
133337e3a6d3SLuigi Rizzo 	ctr->pkts++;
133468b8534bSLuigi Rizzo }
1335f2637526SLuigi Rizzo #endif /* !NO_PCAP */
133668b8534bSLuigi Rizzo 
133737e3a6d3SLuigi Rizzo 
133868b8534bSLuigi Rizzo static int
133937e3a6d3SLuigi Rizzo receive_packets(struct netmap_ring *ring, u_int limit, int dump, uint64_t *bytes)
134068b8534bSLuigi Rizzo {
134117885a7bSLuigi Rizzo 	u_int cur, rx, n;
134237e3a6d3SLuigi Rizzo 	uint64_t b = 0;
134337e3a6d3SLuigi Rizzo 
134437e3a6d3SLuigi Rizzo 	if (bytes == NULL)
134537e3a6d3SLuigi Rizzo 		bytes = &b;
134668b8534bSLuigi Rizzo 
134768b8534bSLuigi Rizzo 	cur = ring->cur;
134817885a7bSLuigi Rizzo 	n = nm_ring_space(ring);
134917885a7bSLuigi Rizzo 	if (n < limit)
135017885a7bSLuigi Rizzo 		limit = n;
135168b8534bSLuigi Rizzo 	for (rx = 0; rx < limit; rx++) {
135268b8534bSLuigi Rizzo 		struct netmap_slot *slot = &ring->slot[cur];
135368b8534bSLuigi Rizzo 		char *p = NETMAP_BUF(ring, slot->buf_idx);
135468b8534bSLuigi Rizzo 
135537e3a6d3SLuigi Rizzo 		*bytes += slot->len;
1356b303f675SLuigi Rizzo 		if (dump)
1357b303f675SLuigi Rizzo 			dump_payload(p, slot->len, ring, cur);
135868b8534bSLuigi Rizzo 
135917885a7bSLuigi Rizzo 		cur = nm_ring_next(ring, cur);
136068b8534bSLuigi Rizzo 	}
136117885a7bSLuigi Rizzo 	ring->head = ring->cur = cur;
136268b8534bSLuigi Rizzo 
136368b8534bSLuigi Rizzo 	return (rx);
136468b8534bSLuigi Rizzo }
136568b8534bSLuigi Rizzo 
136668b8534bSLuigi Rizzo static void *
136768b8534bSLuigi Rizzo receiver_body(void *data)
136868b8534bSLuigi Rizzo {
136968b8534bSLuigi Rizzo 	struct targ *targ = (struct targ *) data;
1370f0ea3689SLuigi Rizzo 	struct pollfd pfd = { .fd = targ->fd, .events = POLLIN };
13714bf50f18SLuigi Rizzo 	struct netmap_if *nifp;
137268b8534bSLuigi Rizzo 	struct netmap_ring *rxring;
1373f8e4e36aSLuigi Rizzo 	int i;
137437e3a6d3SLuigi Rizzo 	struct my_ctrs cur;
137537e3a6d3SLuigi Rizzo 
137637e3a6d3SLuigi Rizzo 	cur.pkts = cur.bytes = cur.events = cur.min_space = 0;
137737e3a6d3SLuigi Rizzo 	cur.t.tv_usec = cur.t.tv_sec = 0; //  unused, just silence the compiler
137868b8534bSLuigi Rizzo 
137968b8534bSLuigi Rizzo 	if (setaffinity(targ->thread, targ->affinity))
138068b8534bSLuigi Rizzo 		goto quit;
138168b8534bSLuigi Rizzo 
13824bf50f18SLuigi Rizzo 	D("reading from %s fd %d main_fd %d",
13834bf50f18SLuigi Rizzo 		targ->g->ifname, targ->fd, targ->g->main_fd);
138468b8534bSLuigi Rizzo 	/* unbounded wait for the first packet. */
13854bf50f18SLuigi Rizzo 	for (;!targ->cancel;) {
1386f0ea3689SLuigi Rizzo 		i = poll(&pfd, 1, 1000);
1387f0ea3689SLuigi Rizzo 		if (i > 0 && !(pfd.revents & POLLERR))
138868b8534bSLuigi Rizzo 			break;
1389f0ea3689SLuigi Rizzo 		RD(1, "waiting for initial packets, poll returns %d %d",
1390f0ea3689SLuigi Rizzo 			i, pfd.revents);
139168b8534bSLuigi Rizzo 	}
139268b8534bSLuigi Rizzo 	/* main loop, exit after 1s silence */
13931cb4c501SLuigi Rizzo 	clock_gettime(CLOCK_REALTIME_PRECISE, &targ->tic);
1394f2637526SLuigi Rizzo     if (targ->g->dev_type == DEV_TAP) {
1395f8e4e36aSLuigi Rizzo 	while (!targ->cancel) {
13964bf50f18SLuigi Rizzo 		char buf[MAX_BODYSIZE];
1397f8e4e36aSLuigi Rizzo 		/* XXX should we poll ? */
139837e3a6d3SLuigi Rizzo 		i = read(targ->g->main_fd, buf, sizeof(buf));
139937e3a6d3SLuigi Rizzo 		if (i > 0) {
140037e3a6d3SLuigi Rizzo 			targ->ctr.pkts++;
140137e3a6d3SLuigi Rizzo 			targ->ctr.bytes += i;
140237e3a6d3SLuigi Rizzo 			targ->ctr.events++;
140337e3a6d3SLuigi Rizzo 		}
1404f8e4e36aSLuigi Rizzo 	}
1405f2637526SLuigi Rizzo #ifndef NO_PCAP
1406f2637526SLuigi Rizzo     } else if (targ->g->dev_type == DEV_PCAP) {
1407f2637526SLuigi Rizzo 	while (!targ->cancel) {
1408f2637526SLuigi Rizzo 		/* XXX should we poll ? */
14094bf50f18SLuigi Rizzo 		pcap_dispatch(targ->g->p, targ->g->burst, receive_pcap,
141037e3a6d3SLuigi Rizzo 			(u_char *)&targ->ctr);
141137e3a6d3SLuigi Rizzo                 targ->ctr.events++;
1412f2637526SLuigi Rizzo 	}
1413f2637526SLuigi Rizzo #endif /* !NO_PCAP */
141468b8534bSLuigi Rizzo     } else {
1415b303f675SLuigi Rizzo 	int dump = targ->g->options & OPT_DUMP;
14164bf50f18SLuigi Rizzo 
14174bf50f18SLuigi Rizzo 	nifp = targ->nmd->nifp;
14183fe77e68SEd Maste 	while (!targ->cancel) {
141968b8534bSLuigi Rizzo 		/* Once we started to receive packets, wait at most 1 seconds
142068b8534bSLuigi Rizzo 		   before quitting. */
142137e3a6d3SLuigi Rizzo #ifdef BUSYWAIT
142237e3a6d3SLuigi Rizzo 		if (ioctl(pfd.fd, NIOCRXSYNC, NULL) < 0) {
142337e3a6d3SLuigi Rizzo 			D("ioctl error on queue %d: %s", targ->me,
142437e3a6d3SLuigi Rizzo 					strerror(errno));
142537e3a6d3SLuigi Rizzo 			goto quit;
142637e3a6d3SLuigi Rizzo 		}
142737e3a6d3SLuigi Rizzo #else /* !BUSYWAIT */
142837e3a6d3SLuigi Rizzo 		if (poll(&pfd, 1, 1 * 1000) <= 0 && !targ->g->forever) {
142937e3a6d3SLuigi Rizzo 			clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc);
143037e3a6d3SLuigi Rizzo 			targ->toc.tv_sec -= 1; /* Subtract timeout time. */
143137e3a6d3SLuigi Rizzo 			goto out;
143237e3a6d3SLuigi Rizzo 		}
143337e3a6d3SLuigi Rizzo 
143437e3a6d3SLuigi Rizzo 		if (pfd.revents & POLLERR) {
143537e3a6d3SLuigi Rizzo 			D("poll err");
143637e3a6d3SLuigi Rizzo 			goto quit;
143737e3a6d3SLuigi Rizzo 		}
143837e3a6d3SLuigi Rizzo #endif /* !BUSYWAIT */
143937e3a6d3SLuigi Rizzo 		uint64_t cur_space = 0;
144037e3a6d3SLuigi Rizzo 		for (i = targ->nmd->first_rx_ring; i <= targ->nmd->last_rx_ring; i++) {
144137e3a6d3SLuigi Rizzo 			int m;
144237e3a6d3SLuigi Rizzo 
144337e3a6d3SLuigi Rizzo 			rxring = NETMAP_RXRING(nifp, i);
144437e3a6d3SLuigi Rizzo 			/* compute free space in the ring */
144537e3a6d3SLuigi Rizzo 			m = rxring->head + rxring->num_slots - rxring->tail;
144637e3a6d3SLuigi Rizzo 			if (m >= (int) rxring->num_slots)
144737e3a6d3SLuigi Rizzo 				m -= rxring->num_slots;
144837e3a6d3SLuigi Rizzo 			cur_space += m;
144937e3a6d3SLuigi Rizzo 			if (nm_ring_empty(rxring))
145037e3a6d3SLuigi Rizzo 				continue;
145137e3a6d3SLuigi Rizzo 
145237e3a6d3SLuigi Rizzo 			m = receive_packets(rxring, targ->g->burst, dump, &cur.bytes);
145337e3a6d3SLuigi Rizzo 			cur.pkts += m;
145437e3a6d3SLuigi Rizzo 			if (m > 0) //XXX-ste: can m be 0?
145537e3a6d3SLuigi Rizzo 				cur.events++;
145637e3a6d3SLuigi Rizzo 		}
145737e3a6d3SLuigi Rizzo 		cur.min_space = targ->ctr.min_space;
145837e3a6d3SLuigi Rizzo 		if (cur_space < cur.min_space)
145937e3a6d3SLuigi Rizzo 			cur.min_space = cur_space;
146037e3a6d3SLuigi Rizzo 		targ->ctr = cur;
146137e3a6d3SLuigi Rizzo 	}
146237e3a6d3SLuigi Rizzo     }
146337e3a6d3SLuigi Rizzo 
146437e3a6d3SLuigi Rizzo 	clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc);
146537e3a6d3SLuigi Rizzo 
146637e3a6d3SLuigi Rizzo #if !defined(BUSYWAIT)
146737e3a6d3SLuigi Rizzo out:
146837e3a6d3SLuigi Rizzo #endif
146937e3a6d3SLuigi Rizzo 	targ->completed = 1;
147037e3a6d3SLuigi Rizzo 	targ->ctr = cur;
147137e3a6d3SLuigi Rizzo 
147237e3a6d3SLuigi Rizzo quit:
147337e3a6d3SLuigi Rizzo 	/* reset the ``used`` flag. */
147437e3a6d3SLuigi Rizzo 	targ->used = 0;
147537e3a6d3SLuigi Rizzo 
147637e3a6d3SLuigi Rizzo 	return (NULL);
147737e3a6d3SLuigi Rizzo }
147837e3a6d3SLuigi Rizzo 
147937e3a6d3SLuigi Rizzo static void *
148037e3a6d3SLuigi Rizzo txseq_body(void *data)
148137e3a6d3SLuigi Rizzo {
148237e3a6d3SLuigi Rizzo 	struct targ *targ = (struct targ *) data;
148337e3a6d3SLuigi Rizzo 	struct pollfd pfd = { .fd = targ->fd, .events = POLLOUT };
148437e3a6d3SLuigi Rizzo 	struct netmap_ring *ring;
148537e3a6d3SLuigi Rizzo 	int64_t sent = 0;
148637e3a6d3SLuigi Rizzo 	uint64_t event = 0;
148737e3a6d3SLuigi Rizzo 	int options = targ->g->options | OPT_COPY;
148837e3a6d3SLuigi Rizzo 	struct timespec nexttime = {0, 0};
148937e3a6d3SLuigi Rizzo 	int rate_limit = targ->g->tx_rate;
149037e3a6d3SLuigi Rizzo 	struct pkt *pkt = &targ->pkt;
149137e3a6d3SLuigi Rizzo 	int frags = targ->g->frags;
149237e3a6d3SLuigi Rizzo 	uint32_t sequence = 0;
149337e3a6d3SLuigi Rizzo 	int budget = 0;
149437e3a6d3SLuigi Rizzo 	void *frame;
149537e3a6d3SLuigi Rizzo 	int size;
149637e3a6d3SLuigi Rizzo 
149737e3a6d3SLuigi Rizzo 	if (targ->g->nthreads > 1) {
149837e3a6d3SLuigi Rizzo 		D("can only txseq ping with 1 thread");
149937e3a6d3SLuigi Rizzo 		return NULL;
150037e3a6d3SLuigi Rizzo 	}
150137e3a6d3SLuigi Rizzo 
150237e3a6d3SLuigi Rizzo 	if (targ->g->npackets > 0) {
150337e3a6d3SLuigi Rizzo 		D("Ignoring -n argument");
150437e3a6d3SLuigi Rizzo 	}
150537e3a6d3SLuigi Rizzo 
150637e3a6d3SLuigi Rizzo 	frame = pkt;
150737e3a6d3SLuigi Rizzo 	frame += sizeof(pkt->vh) - targ->g->virt_header;
150837e3a6d3SLuigi Rizzo 	size = targ->g->pkt_size + targ->g->virt_header;
150937e3a6d3SLuigi Rizzo 
151037e3a6d3SLuigi Rizzo 	D("start, fd %d main_fd %d", targ->fd, targ->g->main_fd);
151137e3a6d3SLuigi Rizzo 	if (setaffinity(targ->thread, targ->affinity))
151237e3a6d3SLuigi Rizzo 		goto quit;
151337e3a6d3SLuigi Rizzo 
151437e3a6d3SLuigi Rizzo 	clock_gettime(CLOCK_REALTIME_PRECISE, &targ->tic);
151537e3a6d3SLuigi Rizzo 	if (rate_limit) {
151637e3a6d3SLuigi Rizzo 		targ->tic = timespec_add(targ->tic, (struct timespec){2,0});
151737e3a6d3SLuigi Rizzo 		targ->tic.tv_nsec = 0;
151837e3a6d3SLuigi Rizzo 		wait_time(targ->tic);
151937e3a6d3SLuigi Rizzo 		nexttime = targ->tic;
152037e3a6d3SLuigi Rizzo 	}
152137e3a6d3SLuigi Rizzo 
152237e3a6d3SLuigi Rizzo 	/* Only use the first queue. */
152337e3a6d3SLuigi Rizzo 	ring = NETMAP_TXRING(targ->nmd->nifp, targ->nmd->first_tx_ring);
152437e3a6d3SLuigi Rizzo 
152537e3a6d3SLuigi Rizzo 	while (!targ->cancel) {
152637e3a6d3SLuigi Rizzo 		int64_t limit;
152737e3a6d3SLuigi Rizzo 		unsigned int space;
152837e3a6d3SLuigi Rizzo 		unsigned int head;
152937e3a6d3SLuigi Rizzo 		int fcnt;
153037e3a6d3SLuigi Rizzo 
153137e3a6d3SLuigi Rizzo 		if (!rate_limit) {
153237e3a6d3SLuigi Rizzo 			budget = targ->g->burst;
153337e3a6d3SLuigi Rizzo 
153437e3a6d3SLuigi Rizzo 		} else if (budget <= 0) {
153537e3a6d3SLuigi Rizzo 			budget = targ->g->burst;
153637e3a6d3SLuigi Rizzo 			nexttime = timespec_add(nexttime, targ->g->tx_period);
153737e3a6d3SLuigi Rizzo 			wait_time(nexttime);
153837e3a6d3SLuigi Rizzo 		}
153937e3a6d3SLuigi Rizzo 
154037e3a6d3SLuigi Rizzo 		/* wait for available room in the send queue */
154137e3a6d3SLuigi Rizzo 		if (poll(&pfd, 1, 2000) <= 0) {
154237e3a6d3SLuigi Rizzo 			if (targ->cancel)
154337e3a6d3SLuigi Rizzo 				break;
154437e3a6d3SLuigi Rizzo 			D("poll error/timeout on queue %d: %s", targ->me,
154537e3a6d3SLuigi Rizzo 				strerror(errno));
154637e3a6d3SLuigi Rizzo 		}
154737e3a6d3SLuigi Rizzo 		if (pfd.revents & POLLERR) {
154837e3a6d3SLuigi Rizzo 			D("poll error on %d ring %d-%d", pfd.fd,
154937e3a6d3SLuigi Rizzo 				targ->nmd->first_tx_ring, targ->nmd->last_tx_ring);
155037e3a6d3SLuigi Rizzo 			goto quit;
155137e3a6d3SLuigi Rizzo 		}
155237e3a6d3SLuigi Rizzo 
155337e3a6d3SLuigi Rizzo 		/* If no room poll() again. */
155437e3a6d3SLuigi Rizzo 		space = nm_ring_space(ring);
155537e3a6d3SLuigi Rizzo 		if (!space) {
155637e3a6d3SLuigi Rizzo 			continue;
155737e3a6d3SLuigi Rizzo 		}
155837e3a6d3SLuigi Rizzo 
155937e3a6d3SLuigi Rizzo 		limit = budget;
156037e3a6d3SLuigi Rizzo 
156137e3a6d3SLuigi Rizzo 		if (space < limit) {
156237e3a6d3SLuigi Rizzo 			limit = space;
156337e3a6d3SLuigi Rizzo 		}
156437e3a6d3SLuigi Rizzo 
156537e3a6d3SLuigi Rizzo 		/* Cut off ``limit`` to make sure is multiple of ``frags``. */
156637e3a6d3SLuigi Rizzo 		if (frags > 1) {
156737e3a6d3SLuigi Rizzo 			limit = (limit / frags) * frags;
156837e3a6d3SLuigi Rizzo 		}
156937e3a6d3SLuigi Rizzo 
157037e3a6d3SLuigi Rizzo 		limit = sent + limit; /* Convert to absolute. */
157137e3a6d3SLuigi Rizzo 
157237e3a6d3SLuigi Rizzo 		for (fcnt = frags, head = ring->head;
157337e3a6d3SLuigi Rizzo 				sent < limit; sent++, sequence++) {
157437e3a6d3SLuigi Rizzo 			struct netmap_slot *slot = &ring->slot[head];
157537e3a6d3SLuigi Rizzo 			char *p = NETMAP_BUF(ring, slot->buf_idx);
157637e3a6d3SLuigi Rizzo 
157737e3a6d3SLuigi Rizzo 			slot->flags = 0;
157837e3a6d3SLuigi Rizzo 			pkt->body[0] = sequence >> 24;
157937e3a6d3SLuigi Rizzo 			pkt->body[1] = (sequence >> 16) & 0xff;
158037e3a6d3SLuigi Rizzo 			pkt->body[2] = (sequence >> 8) & 0xff;
158137e3a6d3SLuigi Rizzo 			pkt->body[3] = sequence & 0xff;
158237e3a6d3SLuigi Rizzo 			nm_pkt_copy(frame, p, size);
158337e3a6d3SLuigi Rizzo 			if (fcnt == frags) {
158437e3a6d3SLuigi Rizzo 				update_addresses(pkt, targ->g);
158537e3a6d3SLuigi Rizzo 			}
158637e3a6d3SLuigi Rizzo 
158737e3a6d3SLuigi Rizzo 			if (options & OPT_DUMP) {
158837e3a6d3SLuigi Rizzo 				dump_payload(p, size, ring, head);
158937e3a6d3SLuigi Rizzo 			}
159037e3a6d3SLuigi Rizzo 
159137e3a6d3SLuigi Rizzo 			slot->len = size;
159237e3a6d3SLuigi Rizzo 
159337e3a6d3SLuigi Rizzo 			if (--fcnt > 0) {
159437e3a6d3SLuigi Rizzo 				slot->flags |= NS_MOREFRAG;
159537e3a6d3SLuigi Rizzo 			} else {
159637e3a6d3SLuigi Rizzo 				fcnt = frags;
159737e3a6d3SLuigi Rizzo 			}
159837e3a6d3SLuigi Rizzo 
159937e3a6d3SLuigi Rizzo 			if (sent == limit - 1) {
160037e3a6d3SLuigi Rizzo 				/* Make sure we don't push an incomplete
160137e3a6d3SLuigi Rizzo 				 * packet. */
160237e3a6d3SLuigi Rizzo 				assert(!(slot->flags & NS_MOREFRAG));
160337e3a6d3SLuigi Rizzo 				slot->flags |= NS_REPORT;
160437e3a6d3SLuigi Rizzo 			}
160537e3a6d3SLuigi Rizzo 
160637e3a6d3SLuigi Rizzo 			head = nm_ring_next(ring, head);
160737e3a6d3SLuigi Rizzo 			if (rate_limit) {
160837e3a6d3SLuigi Rizzo 				budget--;
160937e3a6d3SLuigi Rizzo 			}
161037e3a6d3SLuigi Rizzo 		}
161137e3a6d3SLuigi Rizzo 
161237e3a6d3SLuigi Rizzo 		ring->cur = ring->head = head;
161337e3a6d3SLuigi Rizzo 
161437e3a6d3SLuigi Rizzo 		event ++;
161537e3a6d3SLuigi Rizzo 		targ->ctr.pkts = sent;
161637e3a6d3SLuigi Rizzo 		targ->ctr.bytes = sent * size;
161737e3a6d3SLuigi Rizzo 		targ->ctr.events = event;
161837e3a6d3SLuigi Rizzo 	}
161937e3a6d3SLuigi Rizzo 
162037e3a6d3SLuigi Rizzo 	/* flush any remaining packets */
162137e3a6d3SLuigi Rizzo 	D("flush tail %d head %d on thread %p",
162237e3a6d3SLuigi Rizzo 		ring->tail, ring->head,
162337e3a6d3SLuigi Rizzo 		(void *)pthread_self());
162437e3a6d3SLuigi Rizzo 	ioctl(pfd.fd, NIOCTXSYNC, NULL);
162537e3a6d3SLuigi Rizzo 
162637e3a6d3SLuigi Rizzo 	/* final part: wait the TX queues to become empty. */
162737e3a6d3SLuigi Rizzo 	while (!targ->cancel && nm_tx_pending(ring)) {
162837e3a6d3SLuigi Rizzo 		RD(5, "pending tx tail %d head %d on ring %d",
162937e3a6d3SLuigi Rizzo 				ring->tail, ring->head, targ->nmd->first_tx_ring);
163037e3a6d3SLuigi Rizzo 		ioctl(pfd.fd, NIOCTXSYNC, NULL);
163137e3a6d3SLuigi Rizzo 		usleep(1); /* wait 1 tick */
163237e3a6d3SLuigi Rizzo 	}
163337e3a6d3SLuigi Rizzo 
163437e3a6d3SLuigi Rizzo 	clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc);
163537e3a6d3SLuigi Rizzo 	targ->completed = 1;
163637e3a6d3SLuigi Rizzo 	targ->ctr.pkts = sent;
163737e3a6d3SLuigi Rizzo 	targ->ctr.bytes = sent * size;
163837e3a6d3SLuigi Rizzo 	targ->ctr.events = event;
163937e3a6d3SLuigi Rizzo quit:
164037e3a6d3SLuigi Rizzo 	/* reset the ``used`` flag. */
164137e3a6d3SLuigi Rizzo 	targ->used = 0;
164237e3a6d3SLuigi Rizzo 
164337e3a6d3SLuigi Rizzo 	return (NULL);
164437e3a6d3SLuigi Rizzo }
164537e3a6d3SLuigi Rizzo 
164637e3a6d3SLuigi Rizzo 
164737e3a6d3SLuigi Rizzo static char *
164837e3a6d3SLuigi Rizzo multi_slot_to_string(struct netmap_ring *ring, unsigned int head,
164937e3a6d3SLuigi Rizzo 		     unsigned int nfrags, char *strbuf, size_t strbuflen)
165037e3a6d3SLuigi Rizzo {
165137e3a6d3SLuigi Rizzo 	unsigned int f;
165237e3a6d3SLuigi Rizzo 	char *ret = strbuf;
165337e3a6d3SLuigi Rizzo 
165437e3a6d3SLuigi Rizzo 	for (f = 0; f < nfrags; f++) {
165537e3a6d3SLuigi Rizzo 		struct netmap_slot *slot = &ring->slot[head];
165637e3a6d3SLuigi Rizzo 		int m = snprintf(strbuf, strbuflen, "|%u,%x|", slot->len,
165737e3a6d3SLuigi Rizzo 				 slot->flags);
165837e3a6d3SLuigi Rizzo 		if (m >= (int)strbuflen) {
165937e3a6d3SLuigi Rizzo 			break;
166037e3a6d3SLuigi Rizzo 		}
166137e3a6d3SLuigi Rizzo 		strbuf += m;
166237e3a6d3SLuigi Rizzo 		strbuflen -= m;
166337e3a6d3SLuigi Rizzo 
166437e3a6d3SLuigi Rizzo 		head = nm_ring_next(ring, head);
166537e3a6d3SLuigi Rizzo 	}
166637e3a6d3SLuigi Rizzo 
166737e3a6d3SLuigi Rizzo 	return ret;
166837e3a6d3SLuigi Rizzo }
166937e3a6d3SLuigi Rizzo 
167037e3a6d3SLuigi Rizzo static void *
167137e3a6d3SLuigi Rizzo rxseq_body(void *data)
167237e3a6d3SLuigi Rizzo {
167337e3a6d3SLuigi Rizzo 	struct targ *targ = (struct targ *) data;
167437e3a6d3SLuigi Rizzo 	struct pollfd pfd = { .fd = targ->fd, .events = POLLIN };
167537e3a6d3SLuigi Rizzo 	int dump = targ->g->options & OPT_DUMP;
167637e3a6d3SLuigi Rizzo 	struct netmap_ring *ring;
167737e3a6d3SLuigi Rizzo 	unsigned int frags_exp = 1;
167837e3a6d3SLuigi Rizzo 	uint32_t seq_exp = 0;
167937e3a6d3SLuigi Rizzo 	struct my_ctrs cur;
168037e3a6d3SLuigi Rizzo 	unsigned int frags = 0;
168137e3a6d3SLuigi Rizzo 	int first_packet = 1;
168237e3a6d3SLuigi Rizzo 	int first_slot = 1;
168337e3a6d3SLuigi Rizzo 	int i;
168437e3a6d3SLuigi Rizzo 
168537e3a6d3SLuigi Rizzo 	cur.pkts = cur.bytes = cur.events = cur.min_space = 0;
168637e3a6d3SLuigi Rizzo 	cur.t.tv_usec = cur.t.tv_sec = 0; //  unused, just silence the compiler
168737e3a6d3SLuigi Rizzo 
168837e3a6d3SLuigi Rizzo 	if (setaffinity(targ->thread, targ->affinity))
168937e3a6d3SLuigi Rizzo 		goto quit;
169037e3a6d3SLuigi Rizzo 
169137e3a6d3SLuigi Rizzo 	D("reading from %s fd %d main_fd %d",
169237e3a6d3SLuigi Rizzo 		targ->g->ifname, targ->fd, targ->g->main_fd);
169337e3a6d3SLuigi Rizzo 	/* unbounded wait for the first packet. */
169437e3a6d3SLuigi Rizzo 	for (;!targ->cancel;) {
169537e3a6d3SLuigi Rizzo 		i = poll(&pfd, 1, 1000);
169637e3a6d3SLuigi Rizzo 		if (i > 0 && !(pfd.revents & POLLERR))
169737e3a6d3SLuigi Rizzo 			break;
169837e3a6d3SLuigi Rizzo 		RD(1, "waiting for initial packets, poll returns %d %d",
169937e3a6d3SLuigi Rizzo 			i, pfd.revents);
170037e3a6d3SLuigi Rizzo 	}
170137e3a6d3SLuigi Rizzo 
170237e3a6d3SLuigi Rizzo 	clock_gettime(CLOCK_REALTIME_PRECISE, &targ->tic);
170337e3a6d3SLuigi Rizzo 
170437e3a6d3SLuigi Rizzo 	ring = NETMAP_RXRING(targ->nmd->nifp, targ->nmd->first_rx_ring);
170537e3a6d3SLuigi Rizzo 
170637e3a6d3SLuigi Rizzo 	while (!targ->cancel) {
170737e3a6d3SLuigi Rizzo 		unsigned int head;
170837e3a6d3SLuigi Rizzo 		uint32_t seq;
170937e3a6d3SLuigi Rizzo 		int limit;
171037e3a6d3SLuigi Rizzo 
171137e3a6d3SLuigi Rizzo 		/* Once we started to receive packets, wait at most 1 seconds
171237e3a6d3SLuigi Rizzo 		   before quitting. */
1713f0ea3689SLuigi Rizzo 		if (poll(&pfd, 1, 1 * 1000) <= 0 && !targ->g->forever) {
17141cb4c501SLuigi Rizzo 			clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc);
17158ce070c1SUlrich Spörlein 			targ->toc.tv_sec -= 1; /* Subtract timeout time. */
1716f0ea3689SLuigi Rizzo 			goto out;
171768b8534bSLuigi Rizzo 		}
171868b8534bSLuigi Rizzo 
1719f0ea3689SLuigi Rizzo 		if (pfd.revents & POLLERR) {
172017885a7bSLuigi Rizzo 			D("poll err");
172117885a7bSLuigi Rizzo 			goto quit;
172217885a7bSLuigi Rizzo 		}
172317885a7bSLuigi Rizzo 
172437e3a6d3SLuigi Rizzo 		if (nm_ring_empty(ring))
172568b8534bSLuigi Rizzo 			continue;
172668b8534bSLuigi Rizzo 
172737e3a6d3SLuigi Rizzo 		limit = nm_ring_space(ring);
172837e3a6d3SLuigi Rizzo 		if (limit > targ->g->burst)
172937e3a6d3SLuigi Rizzo 			limit = targ->g->burst;
173037e3a6d3SLuigi Rizzo 
173137e3a6d3SLuigi Rizzo #if 0
173237e3a6d3SLuigi Rizzo 		/* Enable this if
173337e3a6d3SLuigi Rizzo 		 *     1) we remove the early-return optimization from
173437e3a6d3SLuigi Rizzo 		 *        the netmap poll implementation, or
173537e3a6d3SLuigi Rizzo 		 *     2) pipes get NS_MOREFRAG support.
173637e3a6d3SLuigi Rizzo 		 * With the current netmap implementation, an experiment like
173737e3a6d3SLuigi Rizzo 		 *    pkt-gen -i vale:1{1 -f txseq -F 9
173837e3a6d3SLuigi Rizzo 		 *    pkt-gen -i vale:1}1 -f rxseq
173937e3a6d3SLuigi Rizzo 		 * would get stuck as soon as we find nm_ring_space(ring) < 9,
174037e3a6d3SLuigi Rizzo 		 * since here limit is rounded to 0 and
174137e3a6d3SLuigi Rizzo 		 * pipe rxsync is not called anymore by the poll() of this loop.
174237e3a6d3SLuigi Rizzo 		 */
174337e3a6d3SLuigi Rizzo 		if (frags_exp > 1) {
174437e3a6d3SLuigi Rizzo 			int o = limit;
174537e3a6d3SLuigi Rizzo 			/* Cut off to the closest smaller multiple. */
174637e3a6d3SLuigi Rizzo 			limit = (limit / frags_exp) * frags_exp;
174737e3a6d3SLuigi Rizzo 			RD(2, "LIMIT %d --> %d", o, limit);
174868b8534bSLuigi Rizzo 		}
174937e3a6d3SLuigi Rizzo #endif
175037e3a6d3SLuigi Rizzo 
175137e3a6d3SLuigi Rizzo 		for (head = ring->head, i = 0; i < limit; i++) {
175237e3a6d3SLuigi Rizzo 			struct netmap_slot *slot = &ring->slot[head];
175337e3a6d3SLuigi Rizzo 			char *p = NETMAP_BUF(ring, slot->buf_idx);
175437e3a6d3SLuigi Rizzo 			int len = slot->len;
175537e3a6d3SLuigi Rizzo 			struct pkt *pkt;
175637e3a6d3SLuigi Rizzo 
175737e3a6d3SLuigi Rizzo 			if (dump) {
175837e3a6d3SLuigi Rizzo 				dump_payload(p, slot->len, ring, head);
175968b8534bSLuigi Rizzo 			}
176037e3a6d3SLuigi Rizzo 
176137e3a6d3SLuigi Rizzo 			frags++;
176237e3a6d3SLuigi Rizzo 			if (!(slot->flags & NS_MOREFRAG)) {
176337e3a6d3SLuigi Rizzo 				if (first_packet) {
176437e3a6d3SLuigi Rizzo 					first_packet = 0;
176537e3a6d3SLuigi Rizzo 				} else if (frags != frags_exp) {
176637e3a6d3SLuigi Rizzo 					char prbuf[512];
176737e3a6d3SLuigi Rizzo 					RD(1, "Received packets with %u frags, "
176837e3a6d3SLuigi Rizzo 					      "expected %u, '%s'", frags, frags_exp,
176937e3a6d3SLuigi Rizzo 					      multi_slot_to_string(ring, head-frags+1, frags,
177037e3a6d3SLuigi Rizzo 								   prbuf, sizeof(prbuf)));
177137e3a6d3SLuigi Rizzo 				}
177237e3a6d3SLuigi Rizzo 				first_packet = 0;
177337e3a6d3SLuigi Rizzo 				frags_exp = frags;
177437e3a6d3SLuigi Rizzo 				frags = 0;
177537e3a6d3SLuigi Rizzo 			}
177637e3a6d3SLuigi Rizzo 
177737e3a6d3SLuigi Rizzo 			p -= sizeof(pkt->vh) - targ->g->virt_header;
177837e3a6d3SLuigi Rizzo 			len += sizeof(pkt->vh) - targ->g->virt_header;
177937e3a6d3SLuigi Rizzo 			pkt = (struct pkt *)p;
178037e3a6d3SLuigi Rizzo 
178137e3a6d3SLuigi Rizzo 			if ((char *)pkt + len < ((char *)pkt->body) + sizeof(seq)) {
178237e3a6d3SLuigi Rizzo 				RD(1, "%s: packet too small (len=%u)", __func__,
178337e3a6d3SLuigi Rizzo 				      slot->len);
178437e3a6d3SLuigi Rizzo 			} else {
178537e3a6d3SLuigi Rizzo 				seq = (pkt->body[0] << 24) | (pkt->body[1] << 16)
178637e3a6d3SLuigi Rizzo 				      | (pkt->body[2] << 8) | pkt->body[3];
178737e3a6d3SLuigi Rizzo 				if (first_slot) {
178837e3a6d3SLuigi Rizzo 					/* Grab the first one, whatever it
178937e3a6d3SLuigi Rizzo 					   is. */
179037e3a6d3SLuigi Rizzo 					seq_exp = seq;
179137e3a6d3SLuigi Rizzo 					first_slot = 0;
179237e3a6d3SLuigi Rizzo 				} else if (seq != seq_exp) {
179337e3a6d3SLuigi Rizzo 					uint32_t delta = seq - seq_exp;
179437e3a6d3SLuigi Rizzo 
179537e3a6d3SLuigi Rizzo 					if (delta < (0xFFFFFFFF >> 1)) {
179637e3a6d3SLuigi Rizzo 						RD(2, "Sequence GAP: exp %u found %u",
179737e3a6d3SLuigi Rizzo 						      seq_exp, seq);
179837e3a6d3SLuigi Rizzo 					} else {
179937e3a6d3SLuigi Rizzo 						RD(2, "Sequence OUT OF ORDER: "
180037e3a6d3SLuigi Rizzo 						      "exp %u found %u", seq_exp, seq);
180137e3a6d3SLuigi Rizzo 					}
180237e3a6d3SLuigi Rizzo 					seq_exp = seq;
180337e3a6d3SLuigi Rizzo 				}
180437e3a6d3SLuigi Rizzo 				seq_exp++;
180537e3a6d3SLuigi Rizzo 			}
180637e3a6d3SLuigi Rizzo 
180737e3a6d3SLuigi Rizzo 			cur.bytes += slot->len;
180837e3a6d3SLuigi Rizzo 			head = nm_ring_next(ring, head);
180937e3a6d3SLuigi Rizzo 			cur.pkts++;
181037e3a6d3SLuigi Rizzo 		}
181137e3a6d3SLuigi Rizzo 
181237e3a6d3SLuigi Rizzo 		ring->cur = ring->head = head;
181337e3a6d3SLuigi Rizzo 
181437e3a6d3SLuigi Rizzo 		cur.events++;
181537e3a6d3SLuigi Rizzo 		targ->ctr = cur;
181668b8534bSLuigi Rizzo 	}
181768b8534bSLuigi Rizzo 
1818f0ea3689SLuigi Rizzo 	clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc);
1819f0ea3689SLuigi Rizzo 
1820f0ea3689SLuigi Rizzo out:
182168b8534bSLuigi Rizzo 	targ->completed = 1;
182237e3a6d3SLuigi Rizzo 	targ->ctr = cur;
182368b8534bSLuigi Rizzo 
182468b8534bSLuigi Rizzo quit:
182568b8534bSLuigi Rizzo 	/* reset the ``used`` flag. */
182668b8534bSLuigi Rizzo 	targ->used = 0;
182768b8534bSLuigi Rizzo 
182868b8534bSLuigi Rizzo 	return (NULL);
182968b8534bSLuigi Rizzo }
183068b8534bSLuigi Rizzo 
183166a698c9SEd Maste 
183268b8534bSLuigi Rizzo static void
183337e3a6d3SLuigi Rizzo tx_output(struct my_ctrs *cur, double delta, const char *msg)
183468b8534bSLuigi Rizzo {
183537e3a6d3SLuigi Rizzo 	double bw, raw_bw, pps, abs;
1836f8e4e36aSLuigi Rizzo 	char b1[40], b2[80], b3[80];
183737e3a6d3SLuigi Rizzo 	int size;
183868b8534bSLuigi Rizzo 
183937e3a6d3SLuigi Rizzo 	if (cur->pkts == 0) {
184037e3a6d3SLuigi Rizzo 		printf("%s nothing.\n", msg);
184137e3a6d3SLuigi Rizzo 		return;
184237e3a6d3SLuigi Rizzo 	}
184337e3a6d3SLuigi Rizzo 
184437e3a6d3SLuigi Rizzo 	size = (int)(cur->bytes / cur->pkts);
184537e3a6d3SLuigi Rizzo 
184637e3a6d3SLuigi Rizzo 	printf("%s %llu packets %llu bytes %llu events %d bytes each in %.2f seconds.\n",
184737e3a6d3SLuigi Rizzo 		msg,
184837e3a6d3SLuigi Rizzo 		(unsigned long long)cur->pkts,
184937e3a6d3SLuigi Rizzo 		(unsigned long long)cur->bytes,
185037e3a6d3SLuigi Rizzo 		(unsigned long long)cur->events, size, delta);
1851f8e4e36aSLuigi Rizzo 	if (delta == 0)
1852f8e4e36aSLuigi Rizzo 		delta = 1e-6;
1853f8e4e36aSLuigi Rizzo 	if (size < 60)		/* correct for min packet size */
1854f8e4e36aSLuigi Rizzo 		size = 60;
185537e3a6d3SLuigi Rizzo 	pps = cur->pkts / delta;
185637e3a6d3SLuigi Rizzo 	bw = (8.0 * cur->bytes) / delta;
1857f8e4e36aSLuigi Rizzo 	/* raw packets have4 bytes crc + 20 bytes framing */
185837e3a6d3SLuigi Rizzo 	raw_bw = (8.0 * (cur->pkts * 24 + cur->bytes)) / delta;
185937e3a6d3SLuigi Rizzo 	abs = cur->pkts / (double)(cur->events);
186066a698c9SEd Maste 
186137e3a6d3SLuigi Rizzo 	printf("Speed: %spps Bandwidth: %sbps (raw %sbps). Average batch: %.2f pkts\n",
186237e3a6d3SLuigi Rizzo 		norm(b1, pps), norm(b2, bw), norm(b3, raw_bw), abs);
186368b8534bSLuigi Rizzo }
186468b8534bSLuigi Rizzo 
186568b8534bSLuigi Rizzo static void
186668b8534bSLuigi Rizzo usage(void)
186768b8534bSLuigi Rizzo {
186868b8534bSLuigi Rizzo 	const char *cmd = "pkt-gen";
186968b8534bSLuigi Rizzo 	fprintf(stderr,
187068b8534bSLuigi Rizzo 		"Usage:\n"
187168b8534bSLuigi Rizzo 		"%s arguments\n"
187268b8534bSLuigi Rizzo 		"\t-i interface		interface name\n"
187337e3a6d3SLuigi Rizzo 		"\t-f function		tx rx ping pong txseq rxseq\n"
1874f8e4e36aSLuigi Rizzo 		"\t-n count		number of iterations (can be 0)\n"
1875f8e4e36aSLuigi Rizzo 		"\t-t pkts_to_send	also forces tx mode\n"
1876f8e4e36aSLuigi Rizzo 		"\t-r pkts_to_receive	also forces rx mode\n"
1877ce3ee1e7SLuigi Rizzo 		"\t-l pkt_size		in bytes excluding CRC\n"
1878ce3ee1e7SLuigi Rizzo 		"\t-d dst_ip[:port[-dst_ip:port]]   single or range\n"
1879ce3ee1e7SLuigi Rizzo 		"\t-s src_ip[:port[-src_ip:port]]   single or range\n"
1880ce3ee1e7SLuigi Rizzo 		"\t-D dst-mac\n"
1881ce3ee1e7SLuigi Rizzo 		"\t-S src-mac\n"
1882f8e4e36aSLuigi Rizzo 		"\t-a cpu_id		use setaffinity\n"
188368b8534bSLuigi Rizzo 		"\t-b burst size		testing, mostly\n"
188468b8534bSLuigi Rizzo 		"\t-c cores		cores to use\n"
188568b8534bSLuigi Rizzo 		"\t-p threads		processes/threads to use\n"
188668b8534bSLuigi Rizzo 		"\t-T report_ms		milliseconds between reports\n"
188768b8534bSLuigi Rizzo 		"\t-w wait_for_link_time	in seconds\n"
1888ce3ee1e7SLuigi Rizzo 		"\t-R rate		in packets per second\n"
1889ce3ee1e7SLuigi Rizzo 		"\t-X			dump payload\n"
189017885a7bSLuigi Rizzo 		"\t-H len		add empty virtio-net-header with size 'len'\n"
189137e3a6d3SLuigi Rizzo 		"\t-E pipes		allocate extra space for a number of pipes\n"
189237e3a6d3SLuigi Rizzo 		"\t-r			do not touch the buffers (send rubbish)\n"
189356717743SAdrian Chadd 	        "\t-P file		load packet from pcap file\n"
189456717743SAdrian Chadd 		"\t-z			use random IPv4 src address/port\n"
189556717743SAdrian Chadd 		"\t-Z			use random IPv4 dst address/port\n"
189637e3a6d3SLuigi Rizzo 		"\t-F num_frags		send multi-slot packets\n"
189737e3a6d3SLuigi Rizzo 		"\t-A			activate pps stats on receiver\n"
189868b8534bSLuigi Rizzo 		"",
189968b8534bSLuigi Rizzo 		cmd);
190068b8534bSLuigi Rizzo 
190168b8534bSLuigi Rizzo 	exit(0);
190268b8534bSLuigi Rizzo }
190368b8534bSLuigi Rizzo 
190437e3a6d3SLuigi Rizzo enum {
190537e3a6d3SLuigi Rizzo 	TD_TYPE_SENDER = 1,
190637e3a6d3SLuigi Rizzo 	TD_TYPE_RECEIVER,
190737e3a6d3SLuigi Rizzo 	TD_TYPE_OTHER,
190837e3a6d3SLuigi Rizzo };
190937e3a6d3SLuigi Rizzo 
1910f8e4e36aSLuigi Rizzo static void
1911f8e4e36aSLuigi Rizzo start_threads(struct glob_arg *g)
1912f8e4e36aSLuigi Rizzo {
1913f8e4e36aSLuigi Rizzo 	int i;
1914f8e4e36aSLuigi Rizzo 
1915f8e4e36aSLuigi Rizzo 	targs = calloc(g->nthreads, sizeof(*targs));
1916f8e4e36aSLuigi Rizzo 	/*
1917f8e4e36aSLuigi Rizzo 	 * Now create the desired number of threads, each one
1918f8e4e36aSLuigi Rizzo 	 * using a single descriptor.
1919f8e4e36aSLuigi Rizzo  	 */
1920f8e4e36aSLuigi Rizzo 	for (i = 0; i < g->nthreads; i++) {
1921f0ea3689SLuigi Rizzo 		struct targ *t = &targs[i];
1922f0ea3689SLuigi Rizzo 
1923f0ea3689SLuigi Rizzo 		bzero(t, sizeof(*t));
1924f0ea3689SLuigi Rizzo 		t->fd = -1; /* default, with pcap */
1925f0ea3689SLuigi Rizzo 		t->g = g;
1926f8e4e36aSLuigi Rizzo 
1927f8e4e36aSLuigi Rizzo 	    if (g->dev_type == DEV_NETMAP) {
1928f0ea3689SLuigi Rizzo 		struct nm_desc nmd = *g->nmd; /* copy, we overwrite ringid */
19294bf50f18SLuigi Rizzo 		uint64_t nmd_flags = 0;
19304bf50f18SLuigi Rizzo 		nmd.self = &nmd;
1931f8e4e36aSLuigi Rizzo 
193237e3a6d3SLuigi Rizzo 		if (i > 0) {
193337e3a6d3SLuigi Rizzo 			/* the first thread uses the fd opened by the main
193437e3a6d3SLuigi Rizzo 			 * thread, the other threads re-open /dev/netmap
193537e3a6d3SLuigi Rizzo 			 */
1936f0ea3689SLuigi Rizzo 			if (g->nthreads > 1) {
193737e3a6d3SLuigi Rizzo 				nmd.req.nr_flags =
193837e3a6d3SLuigi Rizzo 					g->nmd->req.nr_flags & ~NR_REG_MASK;
193937e3a6d3SLuigi Rizzo 				nmd.req.nr_flags |= NR_REG_ONE_NIC;
1940f0ea3689SLuigi Rizzo 				nmd.req.nr_ringid = i;
194117885a7bSLuigi Rizzo 			}
1942f0ea3689SLuigi Rizzo 			/* Only touch one of the rings (rx is already ok) */
194337e3a6d3SLuigi Rizzo 			if (g->td_type == TD_TYPE_RECEIVER)
19444bf50f18SLuigi Rizzo 				nmd_flags |= NETMAP_NO_TX_POLL;
1945f8e4e36aSLuigi Rizzo 
1946f0ea3689SLuigi Rizzo 			/* register interface. Override ifname and ringid etc. */
19474bf50f18SLuigi Rizzo 			t->nmd = nm_open(t->g->ifname, NULL, nmd_flags |
19484bf50f18SLuigi Rizzo 				NM_OPEN_IFNAME | NM_OPEN_NO_MMAP, &nmd);
1949f0ea3689SLuigi Rizzo 			if (t->nmd == NULL) {
1950f0ea3689SLuigi Rizzo 				D("Unable to open %s: %s",
1951f0ea3689SLuigi Rizzo 					t->g->ifname, strerror(errno));
1952f8e4e36aSLuigi Rizzo 				continue;
1953f8e4e36aSLuigi Rizzo 			}
195437e3a6d3SLuigi Rizzo 		} else {
195537e3a6d3SLuigi Rizzo 			t->nmd = g->nmd;
195637e3a6d3SLuigi Rizzo 		}
1957f0ea3689SLuigi Rizzo 		t->fd = t->nmd->fd;
1958f0ea3689SLuigi Rizzo 
1959f8e4e36aSLuigi Rizzo 	    } else {
1960f8e4e36aSLuigi Rizzo 		targs[i].fd = g->main_fd;
1961f8e4e36aSLuigi Rizzo 	    }
1962f0ea3689SLuigi Rizzo 		t->used = 1;
1963f0ea3689SLuigi Rizzo 		t->me = i;
1964f8e4e36aSLuigi Rizzo 		if (g->affinity >= 0) {
196537e3a6d3SLuigi Rizzo 			t->affinity = (g->affinity + i) % g->system_cpus;
1966f0ea3689SLuigi Rizzo 		} else {
1967f0ea3689SLuigi Rizzo 			t->affinity = -1;
1968f0ea3689SLuigi Rizzo 		}
1969f8e4e36aSLuigi Rizzo 		/* default, init packets */
1970f0ea3689SLuigi Rizzo 		initialize_packet(t);
1971f8e4e36aSLuigi Rizzo 
1972f0ea3689SLuigi Rizzo 		if (pthread_create(&t->thread, NULL, g->td_body, t) == -1) {
197317885a7bSLuigi Rizzo 			D("Unable to create thread %d: %s", i, strerror(errno));
1974f0ea3689SLuigi Rizzo 			t->used = 0;
1975f8e4e36aSLuigi Rizzo 		}
1976f8e4e36aSLuigi Rizzo 	}
1977f8e4e36aSLuigi Rizzo }
1978f8e4e36aSLuigi Rizzo 
1979f8e4e36aSLuigi Rizzo static void
1980f8e4e36aSLuigi Rizzo main_thread(struct glob_arg *g)
1981f8e4e36aSLuigi Rizzo {
1982f8e4e36aSLuigi Rizzo 	int i;
1983f8e4e36aSLuigi Rizzo 
198437e3a6d3SLuigi Rizzo 	struct my_ctrs prev, cur;
1985f8e4e36aSLuigi Rizzo 	double delta_t;
1986f8e4e36aSLuigi Rizzo 	struct timeval tic, toc;
1987f8e4e36aSLuigi Rizzo 
198837e3a6d3SLuigi Rizzo 	prev.pkts = prev.bytes = prev.events = 0;
198937e3a6d3SLuigi Rizzo 	gettimeofday(&prev.t, NULL);
1990f8e4e36aSLuigi Rizzo 	for (;;) {
199137e3a6d3SLuigi Rizzo 		char b1[40], b2[40], b3[40], b4[70];
199237e3a6d3SLuigi Rizzo 		uint64_t pps, usec;
199337e3a6d3SLuigi Rizzo 		struct my_ctrs x;
199437e3a6d3SLuigi Rizzo 		double abs;
1995f8e4e36aSLuigi Rizzo 		int done = 0;
1996f8e4e36aSLuigi Rizzo 
199737e3a6d3SLuigi Rizzo 		usec = wait_for_next_report(&prev.t, &cur.t,
199837e3a6d3SLuigi Rizzo 				g->report_interval);
199937e3a6d3SLuigi Rizzo 
200037e3a6d3SLuigi Rizzo 		cur.pkts = cur.bytes = cur.events = 0;
200137e3a6d3SLuigi Rizzo 		cur.min_space = 0;
200237e3a6d3SLuigi Rizzo 		if (usec < 10000) /* too short to be meaningful */
200337e3a6d3SLuigi Rizzo 			continue;
200437e3a6d3SLuigi Rizzo 		/* accumulate counts for all threads */
2005f8e4e36aSLuigi Rizzo 		for (i = 0; i < g->nthreads; i++) {
200637e3a6d3SLuigi Rizzo 			cur.pkts += targs[i].ctr.pkts;
200737e3a6d3SLuigi Rizzo 			cur.bytes += targs[i].ctr.bytes;
200837e3a6d3SLuigi Rizzo 			cur.events += targs[i].ctr.events;
200937e3a6d3SLuigi Rizzo 			cur.min_space += targs[i].ctr.min_space;
201037e3a6d3SLuigi Rizzo 			targs[i].ctr.min_space = 99999;
2011f8e4e36aSLuigi Rizzo 			if (targs[i].used == 0)
2012f8e4e36aSLuigi Rizzo 				done++;
2013f8e4e36aSLuigi Rizzo 		}
201437e3a6d3SLuigi Rizzo 		x.pkts = cur.pkts - prev.pkts;
201537e3a6d3SLuigi Rizzo 		x.bytes = cur.bytes - prev.bytes;
201637e3a6d3SLuigi Rizzo 		x.events = cur.events - prev.events;
201737e3a6d3SLuigi Rizzo 		pps = (x.pkts*1000000 + usec/2) / usec;
201837e3a6d3SLuigi Rizzo 		abs = (x.events > 0) ? (x.pkts / (double) x.events) : 0;
201937e3a6d3SLuigi Rizzo 
202037e3a6d3SLuigi Rizzo 		if (!(g->options & OPT_PPS_STATS)) {
202137e3a6d3SLuigi Rizzo 			strcpy(b4, "");
202237e3a6d3SLuigi Rizzo 		} else {
202337e3a6d3SLuigi Rizzo 			/* Compute some pps stats using a sliding window. */
202437e3a6d3SLuigi Rizzo 			double ppsavg = 0.0, ppsdev = 0.0;
202537e3a6d3SLuigi Rizzo 			int nsamples = 0;
202637e3a6d3SLuigi Rizzo 
202737e3a6d3SLuigi Rizzo 			g->win[g->win_idx] = pps;
202837e3a6d3SLuigi Rizzo 			g->win_idx = (g->win_idx + 1) % STATS_WIN;
202937e3a6d3SLuigi Rizzo 
203037e3a6d3SLuigi Rizzo 			for (i = 0; i < STATS_WIN; i++) {
203137e3a6d3SLuigi Rizzo 				ppsavg += g->win[i];
203237e3a6d3SLuigi Rizzo 				if (g->win[i]) {
203337e3a6d3SLuigi Rizzo 					nsamples ++;
203437e3a6d3SLuigi Rizzo 				}
203537e3a6d3SLuigi Rizzo 			}
203637e3a6d3SLuigi Rizzo 			ppsavg /= nsamples;
203737e3a6d3SLuigi Rizzo 
203837e3a6d3SLuigi Rizzo 			for (i = 0; i < STATS_WIN; i++) {
203937e3a6d3SLuigi Rizzo 				if (g->win[i] == 0) {
2040f8e4e36aSLuigi Rizzo 					continue;
204137e3a6d3SLuigi Rizzo 				}
204237e3a6d3SLuigi Rizzo 				ppsdev += (g->win[i] - ppsavg) * (g->win[i] - ppsavg);
204337e3a6d3SLuigi Rizzo 			}
204437e3a6d3SLuigi Rizzo 			ppsdev /= nsamples;
204537e3a6d3SLuigi Rizzo 			ppsdev = sqrt(ppsdev);
204637e3a6d3SLuigi Rizzo 
204737e3a6d3SLuigi Rizzo 			snprintf(b4, sizeof(b4), "[avg/std %s/%s pps]",
204837e3a6d3SLuigi Rizzo 				 norm(b1, ppsavg), norm(b2, ppsdev));
204937e3a6d3SLuigi Rizzo 		}
205037e3a6d3SLuigi Rizzo 
205137e3a6d3SLuigi Rizzo 		D("%spps %s(%spkts %sbps in %llu usec) %.2f avg_batch %d min_space",
205237e3a6d3SLuigi Rizzo 			norm(b1, pps), b4,
205337e3a6d3SLuigi Rizzo 			norm(b2, (double)x.pkts),
205437e3a6d3SLuigi Rizzo 			norm(b3, (double)x.bytes*8),
205537e3a6d3SLuigi Rizzo 			(unsigned long long)usec,
205637e3a6d3SLuigi Rizzo 			abs, (int)cur.min_space);
205737e3a6d3SLuigi Rizzo 		prev = cur;
205837e3a6d3SLuigi Rizzo 
2059f8e4e36aSLuigi Rizzo 		if (done == g->nthreads)
2060f8e4e36aSLuigi Rizzo 			break;
2061f8e4e36aSLuigi Rizzo 	}
2062f8e4e36aSLuigi Rizzo 
2063f8e4e36aSLuigi Rizzo 	timerclear(&tic);
2064f8e4e36aSLuigi Rizzo 	timerclear(&toc);
206537e3a6d3SLuigi Rizzo 	cur.pkts = cur.bytes = cur.events = 0;
206637e3a6d3SLuigi Rizzo 	/* final round */
2067f8e4e36aSLuigi Rizzo 	for (i = 0; i < g->nthreads; i++) {
20681cb4c501SLuigi Rizzo 		struct timespec t_tic, t_toc;
2069f8e4e36aSLuigi Rizzo 		/*
2070f8e4e36aSLuigi Rizzo 		 * Join active threads, unregister interfaces and close
2071f8e4e36aSLuigi Rizzo 		 * file descriptors.
2072f8e4e36aSLuigi Rizzo 		 */
20731cb4c501SLuigi Rizzo 		if (targs[i].used)
207437e3a6d3SLuigi Rizzo 			pthread_join(targs[i].thread, NULL); /* blocking */
207537e3a6d3SLuigi Rizzo 		if (g->dev_type == DEV_NETMAP) {
207637e3a6d3SLuigi Rizzo 			nm_close(targs[i].nmd);
207737e3a6d3SLuigi Rizzo 			targs[i].nmd = NULL;
207837e3a6d3SLuigi Rizzo 		} else {
2079f8e4e36aSLuigi Rizzo 			close(targs[i].fd);
208037e3a6d3SLuigi Rizzo 		}
2081f8e4e36aSLuigi Rizzo 
2082f8e4e36aSLuigi Rizzo 		if (targs[i].completed == 0)
2083f8e4e36aSLuigi Rizzo 			D("ouch, thread %d exited with error", i);
2084f8e4e36aSLuigi Rizzo 
2085f8e4e36aSLuigi Rizzo 		/*
2086f8e4e36aSLuigi Rizzo 		 * Collect threads output and extract information about
2087f8e4e36aSLuigi Rizzo 		 * how long it took to send all the packets.
2088f8e4e36aSLuigi Rizzo 		 */
208937e3a6d3SLuigi Rizzo 		cur.pkts += targs[i].ctr.pkts;
209037e3a6d3SLuigi Rizzo 		cur.bytes += targs[i].ctr.bytes;
209137e3a6d3SLuigi Rizzo 		cur.events += targs[i].ctr.events;
209237e3a6d3SLuigi Rizzo 		/* collect the largest start (tic) and end (toc) times,
209337e3a6d3SLuigi Rizzo 		 * XXX maybe we should do the earliest tic, or do a weighted
209437e3a6d3SLuigi Rizzo 		 * average ?
209537e3a6d3SLuigi Rizzo 		 */
20961cb4c501SLuigi Rizzo 		t_tic = timeval2spec(&tic);
20971cb4c501SLuigi Rizzo 		t_toc = timeval2spec(&toc);
20981cb4c501SLuigi Rizzo 		if (!timerisset(&tic) || timespec_ge(&targs[i].tic, &t_tic))
20991cb4c501SLuigi Rizzo 			tic = timespec2val(&targs[i].tic);
21001cb4c501SLuigi Rizzo 		if (!timerisset(&toc) || timespec_ge(&targs[i].toc, &t_toc))
21011cb4c501SLuigi Rizzo 			toc = timespec2val(&targs[i].toc);
2102f8e4e36aSLuigi Rizzo 	}
2103f8e4e36aSLuigi Rizzo 
2104f8e4e36aSLuigi Rizzo 	/* print output. */
2105f8e4e36aSLuigi Rizzo 	timersub(&toc, &tic, &toc);
2106f8e4e36aSLuigi Rizzo 	delta_t = toc.tv_sec + 1e-6* toc.tv_usec;
210737e3a6d3SLuigi Rizzo 	if (g->td_type == TD_TYPE_SENDER)
210837e3a6d3SLuigi Rizzo 		tx_output(&cur, delta_t, "Sent");
2109f8e4e36aSLuigi Rizzo 	else
211037e3a6d3SLuigi Rizzo 		tx_output(&cur, delta_t, "Received");
2111f8e4e36aSLuigi Rizzo }
2112f8e4e36aSLuigi Rizzo 
211337e3a6d3SLuigi Rizzo struct td_desc {
211437e3a6d3SLuigi Rizzo 	int ty;
2115f8e4e36aSLuigi Rizzo 	char *key;
2116f8e4e36aSLuigi Rizzo 	void *f;
2117f8e4e36aSLuigi Rizzo };
2118f8e4e36aSLuigi Rizzo 
211937e3a6d3SLuigi Rizzo static struct td_desc func[] = {
212037e3a6d3SLuigi Rizzo 	{ TD_TYPE_SENDER,	"tx",		sender_body },
212137e3a6d3SLuigi Rizzo 	{ TD_TYPE_RECEIVER,	"rx",		receiver_body },
212237e3a6d3SLuigi Rizzo 	{ TD_TYPE_OTHER,	"ping",		pinger_body },
212337e3a6d3SLuigi Rizzo 	{ TD_TYPE_OTHER,	"pong",		ponger_body },
212437e3a6d3SLuigi Rizzo 	{ TD_TYPE_SENDER,	"txseq",	txseq_body },
212537e3a6d3SLuigi Rizzo 	{ TD_TYPE_RECEIVER,	"rxseq",	rxseq_body },
212637e3a6d3SLuigi Rizzo 	{ 0,			NULL,	NULL }
2127f8e4e36aSLuigi Rizzo };
2128f8e4e36aSLuigi Rizzo 
2129f8e4e36aSLuigi Rizzo static int
2130f8e4e36aSLuigi Rizzo tap_alloc(char *dev)
2131f8e4e36aSLuigi Rizzo {
2132f8e4e36aSLuigi Rizzo 	struct ifreq ifr;
2133f8e4e36aSLuigi Rizzo 	int fd, err;
2134f8e4e36aSLuigi Rizzo 	char *clonedev = TAP_CLONEDEV;
2135f8e4e36aSLuigi Rizzo 
2136f8e4e36aSLuigi Rizzo 	(void)err;
2137f8e4e36aSLuigi Rizzo 	(void)dev;
2138f8e4e36aSLuigi Rizzo 	/* Arguments taken by the function:
2139f8e4e36aSLuigi Rizzo 	 *
2140f8e4e36aSLuigi Rizzo 	 * char *dev: the name of an interface (or '\0'). MUST have enough
2141f8e4e36aSLuigi Rizzo 	 *   space to hold the interface name if '\0' is passed
2142f8e4e36aSLuigi Rizzo 	 * int flags: interface flags (eg, IFF_TUN etc.)
2143f8e4e36aSLuigi Rizzo 	 */
2144f8e4e36aSLuigi Rizzo 
2145f8e4e36aSLuigi Rizzo #ifdef __FreeBSD__
2146f8e4e36aSLuigi Rizzo 	if (dev[3]) { /* tapSomething */
2147f8e4e36aSLuigi Rizzo 		static char buf[128];
2148f8e4e36aSLuigi Rizzo 		snprintf(buf, sizeof(buf), "/dev/%s", dev);
2149f8e4e36aSLuigi Rizzo 		clonedev = buf;
2150f8e4e36aSLuigi Rizzo 	}
2151f8e4e36aSLuigi Rizzo #endif
2152f8e4e36aSLuigi Rizzo 	/* open the device */
2153f8e4e36aSLuigi Rizzo 	if( (fd = open(clonedev, O_RDWR)) < 0 ) {
2154f8e4e36aSLuigi Rizzo 		return fd;
2155f8e4e36aSLuigi Rizzo 	}
2156f8e4e36aSLuigi Rizzo 	D("%s open successful", clonedev);
2157f8e4e36aSLuigi Rizzo 
2158f8e4e36aSLuigi Rizzo 	/* preparation of the struct ifr, of type "struct ifreq" */
2159f8e4e36aSLuigi Rizzo 	memset(&ifr, 0, sizeof(ifr));
2160f8e4e36aSLuigi Rizzo 
2161f8e4e36aSLuigi Rizzo #ifdef linux
2162f8e4e36aSLuigi Rizzo 	ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
2163f8e4e36aSLuigi Rizzo 
2164f8e4e36aSLuigi Rizzo 	if (*dev) {
2165f8e4e36aSLuigi Rizzo 		/* if a device name was specified, put it in the structure; otherwise,
2166f8e4e36aSLuigi Rizzo 		* the kernel will try to allocate the "next" device of the
2167f8e4e36aSLuigi Rizzo 		* specified type */
2168f8e4e36aSLuigi Rizzo 		strncpy(ifr.ifr_name, dev, IFNAMSIZ);
2169f8e4e36aSLuigi Rizzo 	}
2170f8e4e36aSLuigi Rizzo 
2171f8e4e36aSLuigi Rizzo 	/* try to create the device */
2172f8e4e36aSLuigi Rizzo 	if( (err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0 ) {
217317885a7bSLuigi Rizzo 		D("failed to to a TUNSETIFF: %s", strerror(errno));
2174f8e4e36aSLuigi Rizzo 		close(fd);
2175f8e4e36aSLuigi Rizzo 		return err;
2176f8e4e36aSLuigi Rizzo 	}
2177f8e4e36aSLuigi Rizzo 
2178f8e4e36aSLuigi Rizzo 	/* if the operation was successful, write back the name of the
2179f8e4e36aSLuigi Rizzo 	* interface to the variable "dev", so the caller can know
2180f8e4e36aSLuigi Rizzo 	* it. Note that the caller MUST reserve space in *dev (see calling
2181f8e4e36aSLuigi Rizzo 	* code below) */
2182f8e4e36aSLuigi Rizzo 	strcpy(dev, ifr.ifr_name);
2183f8e4e36aSLuigi Rizzo 	D("new name is %s", dev);
2184f8e4e36aSLuigi Rizzo #endif /* linux */
2185f8e4e36aSLuigi Rizzo 
2186f8e4e36aSLuigi Rizzo         /* this is the special file descriptor that the caller will use to talk
2187f8e4e36aSLuigi Rizzo          * with the virtual interface */
2188f8e4e36aSLuigi Rizzo         return fd;
2189f8e4e36aSLuigi Rizzo }
219068b8534bSLuigi Rizzo 
219168b8534bSLuigi Rizzo int
219268b8534bSLuigi Rizzo main(int arc, char **argv)
219368b8534bSLuigi Rizzo {
2194f8e4e36aSLuigi Rizzo 	int i;
219537e3a6d3SLuigi Rizzo 	struct sigaction sa;
219637e3a6d3SLuigi Rizzo 	sigset_t ss;
219768b8534bSLuigi Rizzo 
219868b8534bSLuigi Rizzo 	struct glob_arg g;
219968b8534bSLuigi Rizzo 
220068b8534bSLuigi Rizzo 	int ch;
220168b8534bSLuigi Rizzo 	int wait_link = 2;
220268b8534bSLuigi Rizzo 	int devqueues = 1;	/* how many device queues */
220368b8534bSLuigi Rizzo 
220468b8534bSLuigi Rizzo 	bzero(&g, sizeof(g));
220568b8534bSLuigi Rizzo 
2206f8e4e36aSLuigi Rizzo 	g.main_fd = -1;
2207f8e4e36aSLuigi Rizzo 	g.td_body = receiver_body;
220837e3a6d3SLuigi Rizzo 	g.td_type = TD_TYPE_RECEIVER;
2209f8e4e36aSLuigi Rizzo 	g.report_interval = 1000;	/* report interval */
2210f8e4e36aSLuigi Rizzo 	g.affinity = -1;
2211f8e4e36aSLuigi Rizzo 	/* ip addresses can also be a range x.x.x.x-x.x.x.y */
2212f8e4e36aSLuigi Rizzo 	g.src_ip.name = "10.0.0.1";
2213f8e4e36aSLuigi Rizzo 	g.dst_ip.name = "10.1.0.1";
2214f8e4e36aSLuigi Rizzo 	g.dst_mac.name = "ff:ff:ff:ff:ff:ff";
2215f8e4e36aSLuigi Rizzo 	g.src_mac.name = NULL;
221668b8534bSLuigi Rizzo 	g.pkt_size = 60;
221768b8534bSLuigi Rizzo 	g.burst = 512;		// default
221868b8534bSLuigi Rizzo 	g.nthreads = 1;
221937e3a6d3SLuigi Rizzo 	g.cpus = 1;		// default
2220b303f675SLuigi Rizzo 	g.forever = 1;
22211cb4c501SLuigi Rizzo 	g.tx_rate = 0;
2222ce3ee1e7SLuigi Rizzo 	g.frags = 1;
2223ce3ee1e7SLuigi Rizzo 	g.nmr_config = "";
222417885a7bSLuigi Rizzo 	g.virt_header = 0;
222568b8534bSLuigi Rizzo 
222668b8534bSLuigi Rizzo 	while ( (ch = getopt(arc, argv,
222737e3a6d3SLuigi Rizzo 			"a:f:F:n:i:Il:d:s:D:S:b:c:o:p:T:w:WvR:XC:H:e:E:m:rP:zZA")) != -1) {
222837e3a6d3SLuigi Rizzo 		struct td_desc *fn;
2229f8e4e36aSLuigi Rizzo 
223068b8534bSLuigi Rizzo 		switch(ch) {
223168b8534bSLuigi Rizzo 		default:
223268b8534bSLuigi Rizzo 			D("bad option %c %s", ch, optarg);
223368b8534bSLuigi Rizzo 			usage();
223468b8534bSLuigi Rizzo 			break;
2235f8e4e36aSLuigi Rizzo 
2236f8e4e36aSLuigi Rizzo 		case 'n':
223737e3a6d3SLuigi Rizzo 			g.npackets = strtoull(optarg, NULL, 10);
2238f8e4e36aSLuigi Rizzo 			break;
2239f8e4e36aSLuigi Rizzo 
2240ce3ee1e7SLuigi Rizzo 		case 'F':
2241ce3ee1e7SLuigi Rizzo 			i = atoi(optarg);
2242ce3ee1e7SLuigi Rizzo 			if (i < 1 || i > 63) {
2243ce3ee1e7SLuigi Rizzo 				D("invalid frags %d [1..63], ignore", i);
2244ce3ee1e7SLuigi Rizzo 				break;
2245ce3ee1e7SLuigi Rizzo 			}
2246ce3ee1e7SLuigi Rizzo 			g.frags = i;
2247ce3ee1e7SLuigi Rizzo 			break;
2248ce3ee1e7SLuigi Rizzo 
2249f8e4e36aSLuigi Rizzo 		case 'f':
2250f8e4e36aSLuigi Rizzo 			for (fn = func; fn->key; fn++) {
2251f8e4e36aSLuigi Rizzo 				if (!strcmp(fn->key, optarg))
2252f8e4e36aSLuigi Rizzo 					break;
2253f8e4e36aSLuigi Rizzo 			}
225437e3a6d3SLuigi Rizzo 			if (fn->key) {
2255f8e4e36aSLuigi Rizzo 				g.td_body = fn->f;
225637e3a6d3SLuigi Rizzo 				g.td_type = fn->ty;
225737e3a6d3SLuigi Rizzo 			} else {
2258f8e4e36aSLuigi Rizzo 				D("unrecognised function %s", optarg);
225937e3a6d3SLuigi Rizzo 			}
2260f8e4e36aSLuigi Rizzo 			break;
2261f8e4e36aSLuigi Rizzo 
2262f8e4e36aSLuigi Rizzo 		case 'o':	/* data generation options */
226399fb123fSLuigi Rizzo 			g.options = atoi(optarg);
226499fb123fSLuigi Rizzo 			break;
2265f8e4e36aSLuigi Rizzo 
2266f8e4e36aSLuigi Rizzo 		case 'a':       /* force affinity */
2267f8e4e36aSLuigi Rizzo 			g.affinity = atoi(optarg);
2268f8e4e36aSLuigi Rizzo 			break;
2269f8e4e36aSLuigi Rizzo 
227068b8534bSLuigi Rizzo 		case 'i':	/* interface */
2271f2637526SLuigi Rizzo 			/* a prefix of tap: netmap: or pcap: forces the mode.
2272f2637526SLuigi Rizzo 			 * otherwise we guess
2273f2637526SLuigi Rizzo 			 */
2274f2637526SLuigi Rizzo 			D("interface is %s", optarg);
2275f0ea3689SLuigi Rizzo 			if (strlen(optarg) > MAX_IFNAMELEN - 8) {
2276f0ea3689SLuigi Rizzo 				D("ifname too long %s", optarg);
2277f0ea3689SLuigi Rizzo 				break;
2278f0ea3689SLuigi Rizzo 			}
2279f0ea3689SLuigi Rizzo 			strcpy(g.ifname, optarg);
2280f2637526SLuigi Rizzo 			if (!strcmp(optarg, "null")) {
2281f8e4e36aSLuigi Rizzo 				g.dev_type = DEV_NETMAP;
2282ce3ee1e7SLuigi Rizzo 				g.dummy_send = 1;
2283f2637526SLuigi Rizzo 			} else if (!strncmp(optarg, "tap:", 4)) {
2284f2637526SLuigi Rizzo 				g.dev_type = DEV_TAP;
2285f0ea3689SLuigi Rizzo 				strcpy(g.ifname, optarg + 4);
2286f2637526SLuigi Rizzo 			} else if (!strncmp(optarg, "pcap:", 5)) {
2287f2637526SLuigi Rizzo 				g.dev_type = DEV_PCAP;
2288f0ea3689SLuigi Rizzo 				strcpy(g.ifname, optarg + 5);
2289f0ea3689SLuigi Rizzo 			} else if (!strncmp(optarg, "netmap:", 7) ||
2290f0ea3689SLuigi Rizzo 				   !strncmp(optarg, "vale", 4)) {
2291f2637526SLuigi Rizzo 				g.dev_type = DEV_NETMAP;
2292f2637526SLuigi Rizzo 			} else if (!strncmp(optarg, "tap", 3)) {
2293f2637526SLuigi Rizzo 				g.dev_type = DEV_TAP;
2294f0ea3689SLuigi Rizzo 			} else { /* prepend netmap: */
2295f2637526SLuigi Rizzo 				g.dev_type = DEV_NETMAP;
2296f0ea3689SLuigi Rizzo 				sprintf(g.ifname, "netmap:%s", optarg);
2297f2637526SLuigi Rizzo 			}
229868b8534bSLuigi Rizzo 			break;
2299f8e4e36aSLuigi Rizzo 
2300b303f675SLuigi Rizzo 		case 'I':
2301b303f675SLuigi Rizzo 			g.options |= OPT_INDIRECT;	/* XXX use indirect buffer */
2302b303f675SLuigi Rizzo 			break;
2303b303f675SLuigi Rizzo 
230468b8534bSLuigi Rizzo 		case 'l':	/* pkt_size */
230568b8534bSLuigi Rizzo 			g.pkt_size = atoi(optarg);
230668b8534bSLuigi Rizzo 			break;
2307f8e4e36aSLuigi Rizzo 
230868b8534bSLuigi Rizzo 		case 'd':
2309f8e4e36aSLuigi Rizzo 			g.dst_ip.name = optarg;
231068b8534bSLuigi Rizzo 			break;
2311f8e4e36aSLuigi Rizzo 
231268b8534bSLuigi Rizzo 		case 's':
2313f8e4e36aSLuigi Rizzo 			g.src_ip.name = optarg;
231468b8534bSLuigi Rizzo 			break;
2315f8e4e36aSLuigi Rizzo 
231668b8534bSLuigi Rizzo 		case 'T':	/* report interval */
2317f8e4e36aSLuigi Rizzo 			g.report_interval = atoi(optarg);
231868b8534bSLuigi Rizzo 			break;
2319f8e4e36aSLuigi Rizzo 
232068b8534bSLuigi Rizzo 		case 'w':
232168b8534bSLuigi Rizzo 			wait_link = atoi(optarg);
232268b8534bSLuigi Rizzo 			break;
2323f8e4e36aSLuigi Rizzo 
2324b303f675SLuigi Rizzo 		case 'W': /* XXX changed default */
2325b303f675SLuigi Rizzo 			g.forever = 0; /* do not exit rx even with no traffic */
2326f8e4e36aSLuigi Rizzo 			break;
2327f8e4e36aSLuigi Rizzo 
232868b8534bSLuigi Rizzo 		case 'b':	/* burst */
232968b8534bSLuigi Rizzo 			g.burst = atoi(optarg);
233068b8534bSLuigi Rizzo 			break;
233168b8534bSLuigi Rizzo 		case 'c':
233268b8534bSLuigi Rizzo 			g.cpus = atoi(optarg);
233368b8534bSLuigi Rizzo 			break;
233468b8534bSLuigi Rizzo 		case 'p':
233568b8534bSLuigi Rizzo 			g.nthreads = atoi(optarg);
233668b8534bSLuigi Rizzo 			break;
233768b8534bSLuigi Rizzo 
233868b8534bSLuigi Rizzo 		case 'D': /* destination mac */
2339f8e4e36aSLuigi Rizzo 			g.dst_mac.name = optarg;
234068b8534bSLuigi Rizzo 			break;
2341f8e4e36aSLuigi Rizzo 
234268b8534bSLuigi Rizzo 		case 'S': /* source mac */
2343f8e4e36aSLuigi Rizzo 			g.src_mac.name = optarg;
234468b8534bSLuigi Rizzo 			break;
234568b8534bSLuigi Rizzo 		case 'v':
234668b8534bSLuigi Rizzo 			verbose++;
23471cb4c501SLuigi Rizzo 			break;
23481cb4c501SLuigi Rizzo 		case 'R':
23491cb4c501SLuigi Rizzo 			g.tx_rate = atoi(optarg);
23501cb4c501SLuigi Rizzo 			break;
2351b303f675SLuigi Rizzo 		case 'X':
2352b303f675SLuigi Rizzo 			g.options |= OPT_DUMP;
2353ce3ee1e7SLuigi Rizzo 			break;
2354ce3ee1e7SLuigi Rizzo 		case 'C':
2355ce3ee1e7SLuigi Rizzo 			g.nmr_config = strdup(optarg);
235617885a7bSLuigi Rizzo 			break;
235717885a7bSLuigi Rizzo 		case 'H':
235817885a7bSLuigi Rizzo 			g.virt_header = atoi(optarg);
2359f2637526SLuigi Rizzo 			break;
2360f0ea3689SLuigi Rizzo 		case 'e': /* extra bufs */
2361f0ea3689SLuigi Rizzo 			g.extra_bufs = atoi(optarg);
2362f2637526SLuigi Rizzo 			break;
236337e3a6d3SLuigi Rizzo 		case 'E':
236437e3a6d3SLuigi Rizzo 			g.extra_pipes = atoi(optarg);
23654bf50f18SLuigi Rizzo 			break;
2366f284c737SGeorge V. Neville-Neil 		case 'P':
2367f284c737SGeorge V. Neville-Neil 			g.packet_file = strdup(optarg);
2368f284c737SGeorge V. Neville-Neil 			break;
236937e3a6d3SLuigi Rizzo 		case 'm':
237037e3a6d3SLuigi Rizzo 			/* ignored */
237137e3a6d3SLuigi Rizzo 			break;
237237e3a6d3SLuigi Rizzo 		case 'r':
237337e3a6d3SLuigi Rizzo 			g.options |= OPT_RUBBISH;
237437e3a6d3SLuigi Rizzo 			break;
237556717743SAdrian Chadd 		case 'z':
237656717743SAdrian Chadd 			g.options |= OPT_RANDOM_SRC;
237756717743SAdrian Chadd 			break;
237856717743SAdrian Chadd 		case 'Z':
237956717743SAdrian Chadd 			g.options |= OPT_RANDOM_DST;
238056717743SAdrian Chadd 			break;
238137e3a6d3SLuigi Rizzo 		case 'A':
238237e3a6d3SLuigi Rizzo 			g.options |= OPT_PPS_STATS;
238337e3a6d3SLuigi Rizzo 			break;
238468b8534bSLuigi Rizzo 		}
238568b8534bSLuigi Rizzo 	}
238668b8534bSLuigi Rizzo 
2387db6784f2SGeorge V. Neville-Neil 	if (strlen(g.ifname) <=0 ) {
238868b8534bSLuigi Rizzo 		D("missing ifname");
238968b8534bSLuigi Rizzo 		usage();
239068b8534bSLuigi Rizzo 	}
2391f8e4e36aSLuigi Rizzo 
239237e3a6d3SLuigi Rizzo 	g.system_cpus = i = system_ncpus();
2393f8e4e36aSLuigi Rizzo 	if (g.cpus < 0 || g.cpus > i) {
2394f8e4e36aSLuigi Rizzo 		D("%d cpus is too high, have only %d cpus", g.cpus, i);
239568b8534bSLuigi Rizzo 		usage();
239668b8534bSLuigi Rizzo 	}
239737e3a6d3SLuigi Rizzo D("running on %d cpus (have %d)", g.cpus, i);
239868b8534bSLuigi Rizzo 	if (g.cpus == 0)
2399f8e4e36aSLuigi Rizzo 		g.cpus = i;
2400f8e4e36aSLuigi Rizzo 
24014bf50f18SLuigi Rizzo 	if (g.pkt_size < 16 || g.pkt_size > MAX_PKTSIZE) {
24024bf50f18SLuigi Rizzo 		D("bad pktsize %d [16..%d]\n", g.pkt_size, MAX_PKTSIZE);
240368b8534bSLuigi Rizzo 		usage();
240468b8534bSLuigi Rizzo 	}
240568b8534bSLuigi Rizzo 
2406f8e4e36aSLuigi Rizzo 	if (g.src_mac.name == NULL) {
2407f8e4e36aSLuigi Rizzo 		static char mybuf[20] = "00:00:00:00:00:00";
240899fb123fSLuigi Rizzo 		/* retrieve source mac address. */
2409f8e4e36aSLuigi Rizzo 		if (source_hwaddr(g.ifname, mybuf) == -1) {
241099fb123fSLuigi Rizzo 			D("Unable to retrieve source mac");
241199fb123fSLuigi Rizzo 			// continue, fail later
241299fb123fSLuigi Rizzo 		}
2413f8e4e36aSLuigi Rizzo 		g.src_mac.name = mybuf;
241499fb123fSLuigi Rizzo 	}
2415f8e4e36aSLuigi Rizzo 	/* extract address ranges */
2416f8e4e36aSLuigi Rizzo 	extract_ip_range(&g.src_ip);
2417f8e4e36aSLuigi Rizzo 	extract_ip_range(&g.dst_ip);
2418f8e4e36aSLuigi Rizzo 	extract_mac_range(&g.src_mac);
2419f8e4e36aSLuigi Rizzo 	extract_mac_range(&g.dst_mac);
242099fb123fSLuigi Rizzo 
2421f2637526SLuigi Rizzo 	if (g.src_ip.start != g.src_ip.end ||
2422f2637526SLuigi Rizzo 	    g.src_ip.port0 != g.src_ip.port1 ||
2423f2637526SLuigi Rizzo 	    g.dst_ip.start != g.dst_ip.end ||
2424f2637526SLuigi Rizzo 	    g.dst_ip.port0 != g.dst_ip.port1)
2425f2637526SLuigi Rizzo 		g.options |= OPT_COPY;
2426f2637526SLuigi Rizzo 
242717885a7bSLuigi Rizzo 	if (g.virt_header != 0 && g.virt_header != VIRT_HDR_1
242817885a7bSLuigi Rizzo 			&& g.virt_header != VIRT_HDR_2) {
242917885a7bSLuigi Rizzo 		D("bad virtio-net-header length");
243017885a7bSLuigi Rizzo 		usage();
243117885a7bSLuigi Rizzo 	}
243217885a7bSLuigi Rizzo 
2433f8e4e36aSLuigi Rizzo     if (g.dev_type == DEV_TAP) {
2434f8e4e36aSLuigi Rizzo 	D("want to use tap %s", g.ifname);
2435f8e4e36aSLuigi Rizzo 	g.main_fd = tap_alloc(g.ifname);
2436f8e4e36aSLuigi Rizzo 	if (g.main_fd < 0) {
2437f8e4e36aSLuigi Rizzo 		D("cannot open tap %s", g.ifname);
243899fb123fSLuigi Rizzo 		usage();
243999fb123fSLuigi Rizzo 	}
2440f2637526SLuigi Rizzo #ifndef NO_PCAP
2441f2637526SLuigi Rizzo     } else if (g.dev_type == DEV_PCAP) {
2442f8e4e36aSLuigi Rizzo 	char pcap_errbuf[PCAP_ERRBUF_SIZE];
2443f8e4e36aSLuigi Rizzo 
2444f8e4e36aSLuigi Rizzo 	pcap_errbuf[0] = '\0'; // init the buffer
24454bf50f18SLuigi Rizzo 	g.p = pcap_open_live(g.ifname, 256 /* XXX */, 1, 100, pcap_errbuf);
2446f8e4e36aSLuigi Rizzo 	if (g.p == NULL) {
2447f8e4e36aSLuigi Rizzo 		D("cannot open pcap on %s", g.ifname);
2448f8e4e36aSLuigi Rizzo 		usage();
2449f8e4e36aSLuigi Rizzo 	}
24504bf50f18SLuigi Rizzo 	g.main_fd = pcap_fileno(g.p);
24514bf50f18SLuigi Rizzo 	D("using pcap on %s fileno %d", g.ifname, g.main_fd);
2452f2637526SLuigi Rizzo #endif /* !NO_PCAP */
2453f2637526SLuigi Rizzo     } else if (g.dummy_send) { /* but DEV_NETMAP */
2454ce3ee1e7SLuigi Rizzo 	D("using a dummy send routine");
245599fb123fSLuigi Rizzo     } else {
24564bf50f18SLuigi Rizzo 	struct nmreq base_nmd;
2457f0ea3689SLuigi Rizzo 
2458f0ea3689SLuigi Rizzo 	bzero(&base_nmd, sizeof(base_nmd));
2459f0ea3689SLuigi Rizzo 
24604bf50f18SLuigi Rizzo 	parse_nmr_config(g.nmr_config, &base_nmd);
2461f0ea3689SLuigi Rizzo 	if (g.extra_bufs) {
24624bf50f18SLuigi Rizzo 		base_nmd.nr_arg3 = g.extra_bufs;
2463f0ea3689SLuigi Rizzo 	}
246437e3a6d3SLuigi Rizzo 	if (g.extra_pipes) {
246537e3a6d3SLuigi Rizzo 	    base_nmd.nr_arg1 = g.extra_pipes;
246637e3a6d3SLuigi Rizzo 	}
246737e3a6d3SLuigi Rizzo 
246837e3a6d3SLuigi Rizzo 	base_nmd.nr_flags |= NR_ACCEPT_VNET_HDR;
2469f0ea3689SLuigi Rizzo 
247068b8534bSLuigi Rizzo 	/*
2471f0ea3689SLuigi Rizzo 	 * Open the netmap device using nm_open().
247268b8534bSLuigi Rizzo 	 *
247368b8534bSLuigi Rizzo 	 * protocol stack and may cause a reset of the card,
247468b8534bSLuigi Rizzo 	 * which in turn may take some time for the PHY to
2475f0ea3689SLuigi Rizzo 	 * reconfigure. We do the open here to have time to reset.
247668b8534bSLuigi Rizzo 	 */
24774bf50f18SLuigi Rizzo 	g.nmd = nm_open(g.ifname, &base_nmd, 0, NULL);
2478f0ea3689SLuigi Rizzo 	if (g.nmd == NULL) {
2479f0ea3689SLuigi Rizzo 		D("Unable to open %s: %s", g.ifname, strerror(errno));
2480f0ea3689SLuigi Rizzo 		goto out;
248168b8534bSLuigi Rizzo 	}
248237e3a6d3SLuigi Rizzo 
248337e3a6d3SLuigi Rizzo 	if (g.nthreads > 1) {
248437e3a6d3SLuigi Rizzo 		struct nm_desc saved_desc = *g.nmd;
248537e3a6d3SLuigi Rizzo 		saved_desc.self = &saved_desc;
248637e3a6d3SLuigi Rizzo 		saved_desc.mem = NULL;
248737e3a6d3SLuigi Rizzo 		nm_close(g.nmd);
248837e3a6d3SLuigi Rizzo 		saved_desc.req.nr_flags &= ~NR_REG_MASK;
248937e3a6d3SLuigi Rizzo 		saved_desc.req.nr_flags |= NR_REG_ONE_NIC;
249037e3a6d3SLuigi Rizzo 		saved_desc.req.nr_ringid = 0;
249137e3a6d3SLuigi Rizzo 		g.nmd = nm_open(g.ifname, &base_nmd, NM_OPEN_IFNAME, &saved_desc);
249237e3a6d3SLuigi Rizzo 		if (g.nmd == NULL) {
249337e3a6d3SLuigi Rizzo 			D("Unable to open %s: %s", g.ifname, strerror(errno));
249437e3a6d3SLuigi Rizzo 			goto out;
249537e3a6d3SLuigi Rizzo 		}
249637e3a6d3SLuigi Rizzo 	}
2497f0ea3689SLuigi Rizzo 	g.main_fd = g.nmd->fd;
2498f0ea3689SLuigi Rizzo 	D("mapped %dKB at %p", g.nmd->req.nr_memsize>>10, g.nmd->mem);
2499f0ea3689SLuigi Rizzo 
250037e3a6d3SLuigi Rizzo 	if (g.virt_header) {
250137e3a6d3SLuigi Rizzo 		/* Set the virtio-net header length, since the user asked
250237e3a6d3SLuigi Rizzo 		 * for it explicitely. */
250337e3a6d3SLuigi Rizzo 		set_vnet_hdr_len(&g);
250437e3a6d3SLuigi Rizzo 	} else {
250537e3a6d3SLuigi Rizzo 		/* Check whether the netmap port we opened requires us to send
250637e3a6d3SLuigi Rizzo 		 * and receive frames with virtio-net header. */
250737e3a6d3SLuigi Rizzo 		get_vnet_hdr_len(&g);
250837e3a6d3SLuigi Rizzo 	}
250937e3a6d3SLuigi Rizzo 
25104bf50f18SLuigi Rizzo 	/* get num of queues in tx or rx */
251137e3a6d3SLuigi Rizzo 	if (g.td_type == TD_TYPE_SENDER)
25124bf50f18SLuigi Rizzo 		devqueues = g.nmd->req.nr_tx_rings;
25134bf50f18SLuigi Rizzo 	else
2514f0ea3689SLuigi Rizzo 		devqueues = g.nmd->req.nr_rx_rings;
251568b8534bSLuigi Rizzo 
251668b8534bSLuigi Rizzo 	/* validate provided nthreads. */
251768b8534bSLuigi Rizzo 	if (g.nthreads < 1 || g.nthreads > devqueues) {
251868b8534bSLuigi Rizzo 		D("bad nthreads %d, have %d queues", g.nthreads, devqueues);
251968b8534bSLuigi Rizzo 		// continue, fail later
252068b8534bSLuigi Rizzo 	}
252168b8534bSLuigi Rizzo 
2522f2637526SLuigi Rizzo 	if (verbose) {
2523f0ea3689SLuigi Rizzo 		struct netmap_if *nifp = g.nmd->nifp;
2524f0ea3689SLuigi Rizzo 		struct nmreq *req = &g.nmd->req;
252568b8534bSLuigi Rizzo 
2526f0ea3689SLuigi Rizzo 		D("nifp at offset %d, %d tx %d rx region %d",
2527f0ea3689SLuigi Rizzo 		    req->nr_offset, req->nr_tx_rings, req->nr_rx_rings,
2528f0ea3689SLuigi Rizzo 		    req->nr_arg2);
2529f0ea3689SLuigi Rizzo 		for (i = 0; i <= req->nr_tx_rings; i++) {
25304bf50f18SLuigi Rizzo 			struct netmap_ring *ring = NETMAP_TXRING(nifp, i);
253137e3a6d3SLuigi Rizzo 			D("   TX%d at 0x%p slots %d", i,
253237e3a6d3SLuigi Rizzo 			    (void *)((char *)ring - (char *)nifp), ring->num_slots);
2533f2637526SLuigi Rizzo 		}
2534f0ea3689SLuigi Rizzo 		for (i = 0; i <= req->nr_rx_rings; i++) {
25354bf50f18SLuigi Rizzo 			struct netmap_ring *ring = NETMAP_RXRING(nifp, i);
253637e3a6d3SLuigi Rizzo 			D("   RX%d at 0x%p slots %d", i,
253737e3a6d3SLuigi Rizzo 			    (void *)((char *)ring - (char *)nifp), ring->num_slots);
2538f2637526SLuigi Rizzo 		}
2539f2637526SLuigi Rizzo 	}
254068b8534bSLuigi Rizzo 
254168b8534bSLuigi Rizzo 	/* Print some debug information. */
254268b8534bSLuigi Rizzo 	fprintf(stdout,
254368b8534bSLuigi Rizzo 		"%s %s: %d queues, %d threads and %d cpus.\n",
254437e3a6d3SLuigi Rizzo 		(g.td_type == TD_TYPE_SENDER) ? "Sending on" :
254537e3a6d3SLuigi Rizzo 			((g.td_type == TD_TYPE_RECEIVER) ? "Receiving from" :
254637e3a6d3SLuigi Rizzo 			"Working on"),
2547f8e4e36aSLuigi Rizzo 		g.ifname,
254868b8534bSLuigi Rizzo 		devqueues,
254968b8534bSLuigi Rizzo 		g.nthreads,
255068b8534bSLuigi Rizzo 		g.cpus);
255137e3a6d3SLuigi Rizzo 	if (g.td_type == TD_TYPE_SENDER) {
255268b8534bSLuigi Rizzo 		fprintf(stdout, "%s -> %s (%s -> %s)\n",
2553f8e4e36aSLuigi Rizzo 			g.src_ip.name, g.dst_ip.name,
2554f8e4e36aSLuigi Rizzo 			g.src_mac.name, g.dst_mac.name);
255568b8534bSLuigi Rizzo 	}
255668b8534bSLuigi Rizzo 
2557f0ea3689SLuigi Rizzo out:
255868b8534bSLuigi Rizzo 	/* Exit if something went wrong. */
2559f8e4e36aSLuigi Rizzo 	if (g.main_fd < 0) {
256068b8534bSLuigi Rizzo 		D("aborting");
256168b8534bSLuigi Rizzo 		usage();
256268b8534bSLuigi Rizzo 	}
256399fb123fSLuigi Rizzo     }
256468b8534bSLuigi Rizzo 
2565ce3ee1e7SLuigi Rizzo 
256699fb123fSLuigi Rizzo 	if (g.options) {
256737e3a6d3SLuigi Rizzo 		D("--- SPECIAL OPTIONS:%s%s%s%s%s%s\n",
256899fb123fSLuigi Rizzo 			g.options & OPT_PREFETCH ? " prefetch" : "",
256999fb123fSLuigi Rizzo 			g.options & OPT_ACCESS ? " access" : "",
257099fb123fSLuigi Rizzo 			g.options & OPT_MEMCPY ? " memcpy" : "",
2571b303f675SLuigi Rizzo 			g.options & OPT_INDIRECT ? " indirect" : "",
257237e3a6d3SLuigi Rizzo 			g.options & OPT_COPY ? " copy" : "",
257337e3a6d3SLuigi Rizzo 			g.options & OPT_RUBBISH ? " rubbish " : "");
257499fb123fSLuigi Rizzo 	}
25751cb4c501SLuigi Rizzo 
2576ce3ee1e7SLuigi Rizzo 	g.tx_period.tv_sec = g.tx_period.tv_nsec = 0;
2577ce3ee1e7SLuigi Rizzo 	if (g.tx_rate > 0) {
2578ce3ee1e7SLuigi Rizzo 		/* try to have at least something every second,
257917885a7bSLuigi Rizzo 		 * reducing the burst size to some 0.01s worth of data
2580ce3ee1e7SLuigi Rizzo 		 * (but no less than one full set of fragments)
2581ce3ee1e7SLuigi Rizzo 	 	 */
258217885a7bSLuigi Rizzo 		uint64_t x;
258317885a7bSLuigi Rizzo 		int lim = (g.tx_rate)/300;
258417885a7bSLuigi Rizzo 		if (g.burst > lim)
258517885a7bSLuigi Rizzo 			g.burst = lim;
2586ce3ee1e7SLuigi Rizzo 		if (g.burst < g.frags)
2587ce3ee1e7SLuigi Rizzo 			g.burst = g.frags;
258817885a7bSLuigi Rizzo 		x = ((uint64_t)1000000000 * (uint64_t)g.burst) / (uint64_t) g.tx_rate;
258917885a7bSLuigi Rizzo 		g.tx_period.tv_nsec = x;
25901cb4c501SLuigi Rizzo 		g.tx_period.tv_sec = g.tx_period.tv_nsec / 1000000000;
25911cb4c501SLuigi Rizzo 		g.tx_period.tv_nsec = g.tx_period.tv_nsec % 1000000000;
25921cb4c501SLuigi Rizzo 	}
259337e3a6d3SLuigi Rizzo 	if (g.td_type == TD_TYPE_SENDER)
2594ce3ee1e7SLuigi Rizzo 	    D("Sending %d packets every  %ld.%09ld s",
2595ce3ee1e7SLuigi Rizzo 			g.burst, g.tx_period.tv_sec, g.tx_period.tv_nsec);
259668b8534bSLuigi Rizzo 	/* Wait for PHY reset. */
259768b8534bSLuigi Rizzo 	D("Wait %d secs for phy reset", wait_link);
259868b8534bSLuigi Rizzo 	sleep(wait_link);
259968b8534bSLuigi Rizzo 	D("Ready...");
260068b8534bSLuigi Rizzo 
260168b8534bSLuigi Rizzo 	/* Install ^C handler. */
260268b8534bSLuigi Rizzo 	global_nthreads = g.nthreads;
260337e3a6d3SLuigi Rizzo 	sigemptyset(&ss);
260437e3a6d3SLuigi Rizzo 	sigaddset(&ss, SIGINT);
260537e3a6d3SLuigi Rizzo 	/* block SIGINT now, so that all created threads will inherit the mask */
260637e3a6d3SLuigi Rizzo 	if (pthread_sigmask(SIG_BLOCK, &ss, NULL) < 0) {
260737e3a6d3SLuigi Rizzo 		D("failed to block SIGINT: %s", strerror(errno));
260837e3a6d3SLuigi Rizzo 	}
2609f8e4e36aSLuigi Rizzo 	start_threads(&g);
261037e3a6d3SLuigi Rizzo 	/* Install the handler and re-enable SIGINT for the main thread */
261137e3a6d3SLuigi Rizzo 	sa.sa_handler = sigint_h;
261237e3a6d3SLuigi Rizzo 	if (sigaction(SIGINT, &sa, NULL) < 0) {
261337e3a6d3SLuigi Rizzo 		D("failed to install ^C handler: %s", strerror(errno));
261437e3a6d3SLuigi Rizzo 	}
261537e3a6d3SLuigi Rizzo 
261637e3a6d3SLuigi Rizzo 	if (pthread_sigmask(SIG_UNBLOCK, &ss, NULL) < 0) {
261737e3a6d3SLuigi Rizzo 		D("failed to re-enable SIGINT: %s", strerror(errno));
261837e3a6d3SLuigi Rizzo 	}
2619f8e4e36aSLuigi Rizzo 	main_thread(&g);
262037e3a6d3SLuigi Rizzo 	free(targs);
2621f8e4e36aSLuigi Rizzo 	return 0;
262268b8534bSLuigi Rizzo }
262368b8534bSLuigi Rizzo 
262468b8534bSLuigi Rizzo /* end of file */
2625