pkt-gen.c revision 246896
1227614Sluigi/*
2246896Sluigi * Copyright (C) 2011-2012 Matteo Landi, Luigi Rizzo. All rights reserved.
3227614Sluigi *
4227614Sluigi * Redistribution and use in source and binary forms, with or without
5227614Sluigi * modification, are permitted provided that the following conditions
6227614Sluigi * are met:
7228276Sluigi *   1. Redistributions of source code must retain the above copyright
8228276Sluigi *      notice, this list of conditions and the following disclaimer.
9228276Sluigi *   2. Redistributions in binary form must reproduce the above copyright
10228276Sluigi *      notice, this list of conditions and the following disclaimer in the
11227614Sluigi *    documentation and/or other materials provided with the distribution.
12227614Sluigi *
13227614Sluigi * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14227614Sluigi * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15227614Sluigi * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16227614Sluigi * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17227614Sluigi * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18227614Sluigi * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19227614Sluigi * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20227614Sluigi * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21227614Sluigi * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22227614Sluigi * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23227614Sluigi * SUCH DAMAGE.
24227614Sluigi */
25227614Sluigi
26227614Sluigi/*
27227614Sluigi * $FreeBSD: head/tools/tools/netmap/pkt-gen.c 246896 2013-02-17 04:43:22Z luigi $
28246896Sluigi * $Id: pkt-gen.c 12024 2013-01-25 05:41:51Z luigi $
29227614Sluigi *
30227614Sluigi * Example program to show how to build a multithreaded packet
31227614Sluigi * source/sink using the netmap device.
32227614Sluigi *
33227614Sluigi * In this example we create a programmable number of threads
34227614Sluigi * to take care of all the queues of the interface used to
35227614Sluigi * send or receive traffic.
36227614Sluigi *
37227614Sluigi */
38227614Sluigi
39246896Sluigi#include "nm_util.h"
40246896Sluigi
41246896Sluigiconst char *default_payload="netmap pkt-gen payload\n"
42227614Sluigi	"http://info.iet.unipi.it/~luigi/netmap/ ";
43227614Sluigi
44246896Sluigiint time_second;	// support for RD() debugging macro
45227614Sluigi
46227614Sluigiint verbose = 0;
47227614Sluigi
48227614Sluigi#define SKIP_PAYLOAD 1 /* do not check payload. */
49227614Sluigi
50227614Sluigistruct pkt {
51227614Sluigi	struct ether_header eh;
52227614Sluigi	struct ip ip;
53227614Sluigi	struct udphdr udp;
54231198Sluigi	uint8_t body[2048];	// XXX hardwired
55227614Sluigi} __attribute__((__packed__));
56227614Sluigi
57246896Sluigistruct ip_range {
58246896Sluigi	char *name;
59246896Sluigi	struct in_addr start, end, cur;
60246896Sluigi	uint16_t port0, port1, cur_p;
61246896Sluigi};
62246896Sluigi
63246896Sluigistruct mac_range {
64246896Sluigi	char *name;
65246896Sluigi	struct ether_addr start, end;
66246896Sluigi};
67246896Sluigi
68227614Sluigi/*
69227614Sluigi * global arguments for all threads
70227614Sluigi */
71246896Sluigi
72227614Sluigistruct glob_arg {
73246896Sluigi	struct ip_range src_ip;
74246896Sluigi	struct ip_range dst_ip;
75246896Sluigi	struct mac_range dst_mac;
76246896Sluigi	struct mac_range src_mac;
77227614Sluigi	int pkt_size;
78227614Sluigi	int burst;
79246896Sluigi	int forever;
80227614Sluigi	int npackets;	/* total packets to send */
81227614Sluigi	int nthreads;
82227614Sluigi	int cpus;
83234956Sluigi	int options;	/* testing */
84234956Sluigi#define OPT_PREFETCH	1
85234956Sluigi#define OPT_ACCESS	2
86234956Sluigi#define OPT_COPY	4
87234956Sluigi#define OPT_MEMCPY	8
88246896Sluigi#define OPT_TS		16	/* add a timestamp */
89246896Sluigi	int dev_type;
90227614Sluigi	pcap_t *p;
91227614Sluigi
92246896Sluigi	int affinity;
93246896Sluigi	int main_fd;
94246896Sluigi	int report_interval;
95246896Sluigi	void *(*td_body)(void *);
96246896Sluigi	void *mmap_addr;
97246896Sluigi	int mmap_size;
98246896Sluigi	char *ifname;
99227614Sluigi};
100246896Sluigienum dev_type { DEV_NONE, DEV_NETMAP, DEV_PCAP, DEV_TAP };
101227614Sluigi
102246896Sluigi
103227614Sluigi/*
104227614Sluigi * Arguments for a new thread. The same structure is used by
105227614Sluigi * the source and the sink
106227614Sluigi */
107227614Sluigistruct targ {
108227614Sluigi	struct glob_arg *g;
109227614Sluigi	int used;
110227614Sluigi	int completed;
111238165Semaste	int cancel;
112227614Sluigi	int fd;
113227614Sluigi	struct nmreq nmr;
114227614Sluigi	struct netmap_if *nifp;
115227614Sluigi	uint16_t	qfirst, qlast; /* range of queues to scan */
116246896Sluigi	volatile uint64_t count;
117227614Sluigi	struct timeval tic, toc;
118227614Sluigi	int me;
119227614Sluigi	pthread_t thread;
120227614Sluigi	int affinity;
121227614Sluigi
122227614Sluigi	struct pkt pkt;
123227614Sluigi};
124227614Sluigi
125227614Sluigi
126246896Sluigi/*
127246896Sluigi * extract the extremes from a range of ipv4 addresses.
128246896Sluigi * addr_lo[-addr_hi][:port_lo[-port_hi]]
129246896Sluigi */
130246896Sluigistatic void
131246896Sluigiextract_ip_range(struct ip_range *r)
132246896Sluigi{
133246896Sluigi	char *p_lo, *p_hi;
134246896Sluigi	char buf1[16]; // one ip address
135246896Sluigi
136246896Sluigi	D("extract IP range from %s", r->name);
137246896Sluigi	p_lo = index(r->name, ':');	/* do we have ports ? */
138246896Sluigi	if (p_lo) {
139246896Sluigi		D(" found ports at %s", p_lo);
140246896Sluigi		*p_lo++ = '\0';
141246896Sluigi		p_hi = index(p_lo, '-');
142246896Sluigi		if (p_hi)
143246896Sluigi			*p_hi++ = '\0';
144246896Sluigi		else
145246896Sluigi			p_hi = p_lo;
146246896Sluigi		r->port0 = strtol(p_lo, NULL, 0);
147246896Sluigi		r->port1 = strtol(p_hi, NULL, 0);
148246896Sluigi		if (r->port1 < r->port0) {
149246896Sluigi			r->cur_p = r->port0;
150246896Sluigi			r->port0 = r->port1;
151246896Sluigi			r->port1 = r->cur_p;
152246896Sluigi		}
153246896Sluigi		r->cur_p = r->port0;
154246896Sluigi		D("ports are %d to %d", r->port0, r->port1);
155246896Sluigi	}
156246896Sluigi	p_hi = index(r->name, '-');	/* do we have upper ip ? */
157246896Sluigi	if (p_hi) {
158246896Sluigi		*p_hi++ = '\0';
159246896Sluigi	} else
160246896Sluigi		p_hi = r->name;
161246896Sluigi	inet_aton(r->name, &r->start);
162246896Sluigi	inet_aton(p_hi, &r->end);
163246896Sluigi	if (r->start.s_addr > r->end.s_addr) {
164246896Sluigi		r->cur = r->start;
165246896Sluigi		r->start = r->end;
166246896Sluigi		r->end = r->cur;
167246896Sluigi	}
168246896Sluigi	r->cur = r->start;
169246896Sluigi	strncpy(buf1, inet_ntoa(r->end), sizeof(buf1));
170246896Sluigi	D("range is %s %d to %s %d", inet_ntoa(r->start), r->port0,
171246896Sluigi		buf1, r->port1);
172246896Sluigi}
173246896Sluigi
174246896Sluigistatic void
175246896Sluigiextract_mac_range(struct mac_range *r)
176246896Sluigi{
177246896Sluigi	D("extract MAC range from %s", r->name);
178246896Sluigi	bcopy(ether_aton(r->name), &r->start, 6);
179246896Sluigi	bcopy(ether_aton(r->name), &r->end, 6);
180246896Sluigi#if 0
181246896Sluigi	bcopy(targ->src_mac, eh->ether_shost, 6);
182246896Sluigi	p = index(targ->g->src_mac, '-');
183246896Sluigi	if (p)
184246896Sluigi		targ->src_mac_range = atoi(p+1);
185246896Sluigi
186246896Sluigi	bcopy(ether_aton(targ->g->dst_mac), targ->dst_mac, 6);
187246896Sluigi	bcopy(targ->dst_mac, eh->ether_dhost, 6);
188246896Sluigi	p = index(targ->g->dst_mac, '-');
189246896Sluigi	if (p)
190246896Sluigi		targ->dst_mac_range = atoi(p+1);
191246896Sluigi#endif
192246896Sluigi	D("%s starts at %s", r->name, ether_ntoa(&r->start));
193246896Sluigi}
194246896Sluigi
195227614Sluigistatic struct targ *targs;
196227614Sluigistatic int global_nthreads;
197227614Sluigi
198227614Sluigi/* control-C handler */
199227614Sluigistatic void
200246896Sluigisigint_h(int sig)
201227614Sluigi{
202246896Sluigi	int i;
203246896Sluigi
204246896Sluigi	(void)sig;	/* UNUSED */
205246896Sluigi	for (i = 0; i < global_nthreads; i++) {
206238165Semaste		targs[i].cancel = 1;
207246896Sluigi	}
208227614Sluigi	signal(SIGINT, SIG_DFL);
209227614Sluigi}
210227614Sluigi
211227614Sluigi/* sysctl wrapper to return the number of active CPUs */
212227614Sluigistatic int
213227614Sluigisystem_ncpus(void)
214227614Sluigi{
215246896Sluigi#ifdef __FreeBSD__
216227614Sluigi	int mib[2], ncpus;
217227614Sluigi	size_t len;
218227614Sluigi
219227614Sluigi	mib[0] = CTL_HW;
220227614Sluigi	mib[1] = HW_NCPU;
221227614Sluigi	len = sizeof(mib);
222227614Sluigi	sysctl(mib, 2, &ncpus, &len, NULL, 0);
223227614Sluigi
224227614Sluigi	return (ncpus);
225246896Sluigi#else
226246896Sluigi	return 1;
227246896Sluigi#endif /* !__FreeBSD__ */
228227614Sluigi}
229227614Sluigi
230246896Sluigi#ifdef __linux__
231246896Sluigi#define sockaddr_dl    sockaddr_ll
232246896Sluigi#define sdl_family     sll_family
233246896Sluigi#define AF_LINK        AF_PACKET
234246896Sluigi#define LLADDR(s)      s->sll_addr;
235246896Sluigi#include <linux/if_tun.h>
236246896Sluigi#define TAP_CLONEDEV	"/dev/net/tun"
237246896Sluigi#endif /* __linux__ */
238246896Sluigi
239246896Sluigi#ifdef __FreeBSD__
240246896Sluigi#include <net/if_tun.h>
241246896Sluigi#define TAP_CLONEDEV	"/dev/tap"
242246896Sluigi#endif /* __FreeBSD */
243246896Sluigi
244246896Sluigi#ifdef __APPLE__
245246896Sluigi// #warning TAP not supported on apple ?
246246896Sluigi#include <net/if_utun.h>
247246896Sluigi#define TAP_CLONEDEV	"/dev/tap"
248246896Sluigi#endif /* __APPLE__ */
249246896Sluigi
250246896Sluigi
251227614Sluigi/*
252227614Sluigi * locate the src mac address for our interface, put it
253227614Sluigi * into the user-supplied buffer. return 0 if ok, -1 on error.
254227614Sluigi */
255227614Sluigistatic int
256227614Sluigisource_hwaddr(const char *ifname, char *buf)
257227614Sluigi{
258227614Sluigi	struct ifaddrs *ifaphead, *ifap;
259227614Sluigi	int l = sizeof(ifap->ifa_name);
260227614Sluigi
261227614Sluigi	if (getifaddrs(&ifaphead) != 0) {
262227614Sluigi		D("getifaddrs %s failed", ifname);
263227614Sluigi		return (-1);
264227614Sluigi	}
265227614Sluigi
266227614Sluigi	for (ifap = ifaphead; ifap; ifap = ifap->ifa_next) {
267227614Sluigi		struct sockaddr_dl *sdl =
268227614Sluigi			(struct sockaddr_dl *)ifap->ifa_addr;
269227614Sluigi		uint8_t *mac;
270227614Sluigi
271227614Sluigi		if (!sdl || sdl->sdl_family != AF_LINK)
272227614Sluigi			continue;
273227614Sluigi		if (strncmp(ifap->ifa_name, ifname, l) != 0)
274227614Sluigi			continue;
275227614Sluigi		mac = (uint8_t *)LLADDR(sdl);
276227614Sluigi		sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x",
277227614Sluigi			mac[0], mac[1], mac[2],
278227614Sluigi			mac[3], mac[4], mac[5]);
279227614Sluigi		if (verbose)
280227614Sluigi			D("source hwaddr %s", buf);
281227614Sluigi		break;
282227614Sluigi	}
283227614Sluigi	freeifaddrs(ifaphead);
284227614Sluigi	return ifap ? 0 : 1;
285227614Sluigi}
286227614Sluigi
287227614Sluigi
288227614Sluigi/* set the thread affinity. */
289227614Sluigistatic int
290227614Sluigisetaffinity(pthread_t me, int i)
291227614Sluigi{
292246896Sluigi#ifdef __FreeBSD__
293227614Sluigi	cpuset_t cpumask;
294227614Sluigi
295227614Sluigi	if (i == -1)
296227614Sluigi		return 0;
297227614Sluigi
298227614Sluigi	/* Set thread affinity affinity.*/
299227614Sluigi	CPU_ZERO(&cpumask);
300227614Sluigi	CPU_SET(i, &cpumask);
301227614Sluigi
302227614Sluigi	if (pthread_setaffinity_np(me, sizeof(cpuset_t), &cpumask) != 0) {
303227614Sluigi		D("Unable to set affinity");
304227614Sluigi		return 1;
305227614Sluigi	}
306246896Sluigi#else
307246896Sluigi	(void)me; /* suppress 'unused' warnings */
308246896Sluigi	(void)i;
309246896Sluigi#endif /* __FreeBSD__ */
310227614Sluigi	return 0;
311227614Sluigi}
312227614Sluigi
313227614Sluigi/* Compute the checksum of the given ip header. */
314227614Sluigistatic uint16_t
315246896Sluigichecksum(const void *data, uint16_t len, uint32_t sum)
316227614Sluigi{
317227614Sluigi        const uint8_t *addr = data;
318246896Sluigi	uint32_t i;
319227614Sluigi
320246896Sluigi        /* Checksum all the pairs of bytes first... */
321246896Sluigi        for (i = 0; i < (len & ~1U); i += 2) {
322246896Sluigi                sum += (u_int16_t)ntohs(*((u_int16_t *)(addr + i)));
323246896Sluigi                if (sum > 0xFFFF)
324246896Sluigi                        sum -= 0xFFFF;
325227614Sluigi        }
326246896Sluigi	/*
327246896Sluigi	 * If there's a single byte left over, checksum it, too.
328246896Sluigi	 * Network byte order is big-endian, so the remaining byte is
329246896Sluigi	 * the high byte.
330246896Sluigi	 */
331246896Sluigi	if (i < len) {
332246896Sluigi		sum += addr[i] << 8;
333246896Sluigi		if (sum > 0xFFFF)
334246896Sluigi			sum -= 0xFFFF;
335246896Sluigi	}
336246896Sluigi	return sum;
337246896Sluigi}
338227614Sluigi
339246896Sluigistatic u_int16_t
340246896Sluigiwrapsum(u_int32_t sum)
341246896Sluigi{
342246896Sluigi	sum = ~sum & 0xFFFF;
343246896Sluigi	return (htons(sum));
344227614Sluigi}
345227614Sluigi
346227614Sluigi/*
347227614Sluigi * Fill a packet with some payload.
348246896Sluigi * We create a UDP packet so the payload starts at
349246896Sluigi *	14+20+8 = 42 bytes.
350227614Sluigi */
351246896Sluigi#ifdef __linux__
352246896Sluigi#define uh_sport source
353246896Sluigi#define uh_dport dest
354246896Sluigi#define uh_ulen len
355246896Sluigi#define uh_sum check
356246896Sluigi#endif /* linux */
357227614Sluigistatic void
358227614Sluigiinitialize_packet(struct targ *targ)
359227614Sluigi{
360227614Sluigi	struct pkt *pkt = &targ->pkt;
361227614Sluigi	struct ether_header *eh;
362227614Sluigi	struct ip *ip;
363227614Sluigi	struct udphdr *udp;
364246896Sluigi	uint16_t paylen = targ->g->pkt_size - sizeof(*eh) - sizeof(struct ip);
365227614Sluigi	int i, l, l0 = strlen(default_payload);
366227614Sluigi
367227614Sluigi	for (i = 0; i < paylen;) {
368227614Sluigi		l = min(l0, paylen - i);
369227614Sluigi		bcopy(default_payload, pkt->body + i, l);
370227614Sluigi		i += l;
371227614Sluigi	}
372227614Sluigi	pkt->body[i-1] = '\0';
373246896Sluigi	ip = &pkt->ip;
374227614Sluigi
375227614Sluigi        ip->ip_v = IPVERSION;
376227614Sluigi        ip->ip_hl = 5;
377227614Sluigi        ip->ip_id = 0;
378227614Sluigi        ip->ip_tos = IPTOS_LOWDELAY;
379227614Sluigi	ip->ip_len = ntohs(targ->g->pkt_size - sizeof(*eh));
380227614Sluigi        ip->ip_id = 0;
381227614Sluigi        ip->ip_off = htons(IP_DF); /* Don't fragment */
382227614Sluigi        ip->ip_ttl = IPDEFTTL;
383227614Sluigi	ip->ip_p = IPPROTO_UDP;
384246896Sluigi	ip->ip_dst.s_addr = targ->g->dst_ip.cur.s_addr;
385246896Sluigi	if (++targ->g->dst_ip.cur.s_addr > targ->g->dst_ip.end.s_addr)
386246896Sluigi		targ->g->dst_ip.cur.s_addr = targ->g->dst_ip.start.s_addr;
387246896Sluigi	ip->ip_src.s_addr = targ->g->src_ip.cur.s_addr;
388246896Sluigi	if (++targ->g->src_ip.cur.s_addr > targ->g->src_ip.end.s_addr)
389246896Sluigi		targ->g->src_ip.cur.s_addr = targ->g->src_ip.start.s_addr;
390246896Sluigi	ip->ip_sum = wrapsum(checksum(ip, sizeof(*ip), 0));
391227614Sluigi
392246896Sluigi
393246896Sluigi	udp = &pkt->udp;
394246896Sluigi        udp->uh_sport = htons(targ->g->src_ip.cur_p);
395246896Sluigi	if (++targ->g->src_ip.cur_p > targ->g->src_ip.port1)
396246896Sluigi		targ->g->src_ip.cur_p = targ->g->src_ip.port0;
397246896Sluigi        udp->uh_dport = htons(targ->g->dst_ip.cur_p);
398246896Sluigi	if (++targ->g->dst_ip.cur_p > targ->g->dst_ip.port1)
399246896Sluigi		targ->g->dst_ip.cur_p = targ->g->dst_ip.port0;
400246896Sluigi	udp->uh_ulen = htons(paylen);
401246896Sluigi	/* Magic: taken from sbin/dhclient/packet.c */
402246896Sluigi	udp->uh_sum = wrapsum(checksum(udp, sizeof(*udp),
403246896Sluigi                    checksum(pkt->body,
404246896Sluigi                        paylen - sizeof(*udp),
405246896Sluigi                        checksum(&ip->ip_src, 2 * sizeof(ip->ip_src),
406246896Sluigi                            IPPROTO_UDP + (u_int32_t)ntohs(udp->uh_ulen)
407246896Sluigi                        )
408246896Sluigi                    )
409246896Sluigi                ));
410246896Sluigi
411227614Sluigi	eh = &pkt->eh;
412246896Sluigi	bcopy(&targ->g->src_mac.start, eh->ether_shost, 6);
413246896Sluigi	bcopy(&targ->g->dst_mac.start, eh->ether_dhost, 6);
414227614Sluigi	eh->ether_type = htons(ETHERTYPE_IP);
415227614Sluigi}
416227614Sluigi
417227614Sluigi/* Check the payload of the packet for errors (use it for debug).
418227614Sluigi * Look for consecutive ascii representations of the size of the packet.
419227614Sluigi */
420227614Sluigistatic void
421227614Sluigicheck_payload(char *p, int psize)
422227614Sluigi{
423227614Sluigi	char temp[64];
424227614Sluigi	int n_read, size, sizelen;
425227614Sluigi
426227614Sluigi	/* get the length in ASCII of the length of the packet. */
427227614Sluigi	sizelen = sprintf(temp, "%d", psize) + 1; // include a whitespace
428227614Sluigi
429227614Sluigi	/* dummy payload. */
430227614Sluigi	p += 14; /* skip packet header. */
431227614Sluigi	n_read = 14;
432227614Sluigi	while (psize - n_read >= sizelen) {
433227614Sluigi		sscanf(p, "%d", &size);
434227614Sluigi		if (size != psize) {
435227614Sluigi			D("Read %d instead of %d", size, psize);
436227614Sluigi			break;
437227614Sluigi		}
438227614Sluigi
439227614Sluigi		p += sizelen;
440227614Sluigi		n_read += sizelen;
441227614Sluigi	}
442227614Sluigi}
443227614Sluigi
444227614Sluigi
445227614Sluigi/*
446227614Sluigi * create and enqueue a batch of packets on a ring.
447227614Sluigi * On the last one set NS_REPORT to tell the driver to generate
448227614Sluigi * an interrupt when done.
449227614Sluigi */
450227614Sluigistatic int
451227614Sluigisend_packets(struct netmap_ring *ring, struct pkt *pkt,
452234956Sluigi		int size, u_int count, int options)
453227614Sluigi{
454227614Sluigi	u_int sent, cur = ring->cur;
455227614Sluigi
456227614Sluigi	if (ring->avail < count)
457227614Sluigi		count = ring->avail;
458227614Sluigi
459234956Sluigi#if 0
460234956Sluigi	if (options & (OPT_COPY | OPT_PREFETCH) ) {
461234956Sluigi		for (sent = 0; sent < count; sent++) {
462234956Sluigi			struct netmap_slot *slot = &ring->slot[cur];
463234956Sluigi			char *p = NETMAP_BUF(ring, slot->buf_idx);
464234956Sluigi
465234956Sluigi			prefetch(p);
466234956Sluigi			cur = NETMAP_RING_NEXT(ring, cur);
467234956Sluigi		}
468234956Sluigi		cur = ring->cur;
469234956Sluigi	}
470234956Sluigi#endif
471227614Sluigi	for (sent = 0; sent < count; sent++) {
472227614Sluigi		struct netmap_slot *slot = &ring->slot[cur];
473227614Sluigi		char *p = NETMAP_BUF(ring, slot->buf_idx);
474227614Sluigi
475234956Sluigi		if (options & OPT_COPY)
476234956Sluigi			pkt_copy(pkt, p, size);
477234956Sluigi		else if (options & OPT_MEMCPY)
478227614Sluigi			memcpy(p, pkt, size);
479234956Sluigi		else if (options & OPT_PREFETCH)
480234956Sluigi			prefetch(p);
481227614Sluigi		slot->len = size;
482227614Sluigi		if (sent == count - 1)
483227614Sluigi			slot->flags |= NS_REPORT;
484227614Sluigi		cur = NETMAP_RING_NEXT(ring, cur);
485227614Sluigi	}
486227614Sluigi	ring->avail -= sent;
487227614Sluigi	ring->cur = cur;
488227614Sluigi
489227614Sluigi	return (sent);
490227614Sluigi}
491227614Sluigi
492246896Sluigi/*
493246896Sluigi * Send a packet, and wait for a response.
494246896Sluigi * The payload (after UDP header, ofs 42) has a 4-byte sequence
495246896Sluigi * followed by a struct timeval (or bintime?)
496246896Sluigi */
497246896Sluigi#define	PAY_OFS	42	/* where in the pkt... */
498246896Sluigi
499227614Sluigistatic void *
500246896Sluigipinger_body(void *data)
501246896Sluigi{
502246896Sluigi	struct targ *targ = (struct targ *) data;
503246896Sluigi	struct pollfd fds[1];
504246896Sluigi	struct netmap_if *nifp = targ->nifp;
505246896Sluigi	int i, rx = 0, n = targ->g->npackets;
506246896Sluigi
507246896Sluigi	fds[0].fd = targ->fd;
508246896Sluigi	fds[0].events = (POLLIN);
509246896Sluigi	static uint32_t sent;
510246896Sluigi	struct timespec ts, now, last_print;
511246896Sluigi	uint32_t count = 0, min = 1000000000, av = 0;
512246896Sluigi
513246896Sluigi	if (targ->g->nthreads > 1) {
514246896Sluigi		D("can only ping with 1 thread");
515246896Sluigi		return NULL;
516246896Sluigi	}
517246896Sluigi
518246896Sluigi	clock_gettime(CLOCK_REALTIME_PRECISE, &last_print);
519246896Sluigi	while (n == 0 || (int)sent < n) {
520246896Sluigi		struct netmap_ring *ring = NETMAP_TXRING(nifp, 0);
521246896Sluigi		struct netmap_slot *slot;
522246896Sluigi		char *p;
523246896Sluigi	    for (i = 0; i < 1; i++) {
524246896Sluigi		slot = &ring->slot[ring->cur];
525246896Sluigi		slot->len = targ->g->pkt_size;
526246896Sluigi		p = NETMAP_BUF(ring, slot->buf_idx);
527246896Sluigi
528246896Sluigi		if (ring->avail == 0) {
529246896Sluigi			D("-- ouch, cannot send");
530246896Sluigi		} else {
531246896Sluigi			pkt_copy(&targ->pkt, p, targ->g->pkt_size);
532246896Sluigi			clock_gettime(CLOCK_REALTIME_PRECISE, &ts);
533246896Sluigi			bcopy(&sent, p+42, sizeof(sent));
534246896Sluigi			bcopy(&ts, p+46, sizeof(ts));
535246896Sluigi			sent++;
536246896Sluigi			ring->cur = NETMAP_RING_NEXT(ring, ring->cur);
537246896Sluigi			ring->avail--;
538246896Sluigi		}
539246896Sluigi	    }
540246896Sluigi		/* should use a parameter to decide how often to send */
541246896Sluigi		if (poll(fds, 1, 3000) <= 0) {
542246896Sluigi			D("poll error/timeout on queue %d", targ->me);
543246896Sluigi			continue;
544246896Sluigi		}
545246896Sluigi		/* see what we got back */
546246896Sluigi		for (i = targ->qfirst; i < targ->qlast; i++) {
547246896Sluigi			ring = NETMAP_RXRING(nifp, i);
548246896Sluigi			while (ring->avail > 0) {
549246896Sluigi				uint32_t seq;
550246896Sluigi				slot = &ring->slot[ring->cur];
551246896Sluigi				p = NETMAP_BUF(ring, slot->buf_idx);
552246896Sluigi
553246896Sluigi				clock_gettime(CLOCK_REALTIME_PRECISE, &now);
554246896Sluigi				bcopy(p+42, &seq, sizeof(seq));
555246896Sluigi				bcopy(p+46, &ts, sizeof(ts));
556246896Sluigi				ts.tv_sec = now.tv_sec - ts.tv_sec;
557246896Sluigi				ts.tv_nsec = now.tv_nsec - ts.tv_nsec;
558246896Sluigi				if (ts.tv_nsec < 0) {
559246896Sluigi					ts.tv_nsec += 1000000000;
560246896Sluigi					ts.tv_sec--;
561246896Sluigi				}
562246896Sluigi				if (1) D("seq %d/%d delta %d.%09d", seq, sent,
563246896Sluigi					(int)ts.tv_sec, (int)ts.tv_nsec);
564246896Sluigi				if (ts.tv_nsec < (int)min)
565246896Sluigi					min = ts.tv_nsec;
566246896Sluigi				count ++;
567246896Sluigi				av += ts.tv_nsec;
568246896Sluigi				ring->avail--;
569246896Sluigi				ring->cur = NETMAP_RING_NEXT(ring, ring->cur);
570246896Sluigi				rx++;
571246896Sluigi			}
572246896Sluigi		}
573246896Sluigi		//D("tx %d rx %d", sent, rx);
574246896Sluigi		//usleep(100000);
575246896Sluigi		ts.tv_sec = now.tv_sec - last_print.tv_sec;
576246896Sluigi		ts.tv_nsec = now.tv_nsec - last_print.tv_nsec;
577246896Sluigi		if (ts.tv_nsec < 0) {
578246896Sluigi			ts.tv_nsec += 1000000000;
579246896Sluigi			ts.tv_sec--;
580246896Sluigi		}
581246896Sluigi		if (ts.tv_sec >= 1) {
582246896Sluigi			D("count %d min %d av %d",
583246896Sluigi				count, min, av/count);
584246896Sluigi			count = 0;
585246896Sluigi			av = 0;
586246896Sluigi			min = 100000000;
587246896Sluigi			last_print = now;
588246896Sluigi		}
589246896Sluigi	}
590246896Sluigi	return NULL;
591246896Sluigi}
592246896Sluigi
593246896Sluigi
594246896Sluigi/*
595246896Sluigi * reply to ping requests
596246896Sluigi */
597246896Sluigistatic void *
598246896Sluigiponger_body(void *data)
599246896Sluigi{
600246896Sluigi	struct targ *targ = (struct targ *) data;
601246896Sluigi	struct pollfd fds[1];
602246896Sluigi	struct netmap_if *nifp = targ->nifp;
603246896Sluigi	struct netmap_ring *txring, *rxring;
604246896Sluigi	int i, rx = 0, sent = 0, n = targ->g->npackets;
605246896Sluigi	fds[0].fd = targ->fd;
606246896Sluigi	fds[0].events = (POLLIN);
607246896Sluigi
608246896Sluigi	if (targ->g->nthreads > 1) {
609246896Sluigi		D("can only reply ping with 1 thread");
610246896Sluigi		return NULL;
611246896Sluigi	}
612246896Sluigi	D("understood ponger %d but don't know how to do it", n);
613246896Sluigi	while (n == 0 || sent < n) {
614246896Sluigi		uint32_t txcur, txavail;
615246896Sluigi//#define BUSYWAIT
616246896Sluigi#ifdef BUSYWAIT
617246896Sluigi		ioctl(fds[0].fd, NIOCRXSYNC, NULL);
618246896Sluigi#else
619246896Sluigi		if (poll(fds, 1, 1000) <= 0) {
620246896Sluigi			D("poll error/timeout on queue %d", targ->me);
621246896Sluigi			continue;
622246896Sluigi		}
623246896Sluigi#endif
624246896Sluigi		txring = NETMAP_TXRING(nifp, 0);
625246896Sluigi		txcur = txring->cur;
626246896Sluigi		txavail = txring->avail;
627246896Sluigi		/* see what we got back */
628246896Sluigi		for (i = targ->qfirst; i < targ->qlast; i++) {
629246896Sluigi			rxring = NETMAP_RXRING(nifp, i);
630246896Sluigi			while (rxring->avail > 0) {
631246896Sluigi				uint16_t *spkt, *dpkt;
632246896Sluigi				uint32_t cur = rxring->cur;
633246896Sluigi				struct netmap_slot *slot = &rxring->slot[cur];
634246896Sluigi				char *src, *dst;
635246896Sluigi				src = NETMAP_BUF(rxring, slot->buf_idx);
636246896Sluigi				//D("got pkt %p of size %d", src, slot->len);
637246896Sluigi				rxring->avail--;
638246896Sluigi				rxring->cur = NETMAP_RING_NEXT(rxring, cur);
639246896Sluigi				rx++;
640246896Sluigi				if (txavail == 0)
641246896Sluigi					continue;
642246896Sluigi				dst = NETMAP_BUF(txring,
643246896Sluigi				    txring->slot[txcur].buf_idx);
644246896Sluigi				/* copy... */
645246896Sluigi				dpkt = (uint16_t *)dst;
646246896Sluigi				spkt = (uint16_t *)src;
647246896Sluigi				pkt_copy(src, dst, slot->len);
648246896Sluigi				dpkt[0] = spkt[3];
649246896Sluigi				dpkt[1] = spkt[4];
650246896Sluigi				dpkt[2] = spkt[5];
651246896Sluigi				dpkt[3] = spkt[0];
652246896Sluigi				dpkt[4] = spkt[1];
653246896Sluigi				dpkt[5] = spkt[2];
654246896Sluigi				txring->slot[txcur].len = slot->len;
655246896Sluigi				/* XXX swap src dst mac */
656246896Sluigi				txcur = NETMAP_RING_NEXT(txring, txcur);
657246896Sluigi				txavail--;
658246896Sluigi				sent++;
659246896Sluigi			}
660246896Sluigi		}
661246896Sluigi		txring->cur = txcur;
662246896Sluigi		txring->avail = txavail;
663246896Sluigi		targ->count = sent;
664246896Sluigi#ifdef BUSYWAIT
665246896Sluigi		ioctl(fds[0].fd, NIOCTXSYNC, NULL);
666246896Sluigi#endif
667246896Sluigi		//D("tx %d rx %d", sent, rx);
668246896Sluigi	}
669246896Sluigi	return NULL;
670246896Sluigi}
671246896Sluigi
672246896Sluigi
673246896Sluigistatic void *
674227614Sluigisender_body(void *data)
675227614Sluigi{
676227614Sluigi	struct targ *targ = (struct targ *) data;
677246896Sluigi
678227614Sluigi	struct pollfd fds[1];
679227614Sluigi	struct netmap_if *nifp = targ->nifp;
680227614Sluigi	struct netmap_ring *txring;
681246896Sluigi	int i, n = targ->g->npackets / targ->g->nthreads, sent = 0;
682234956Sluigi	int options = targ->g->options | OPT_COPY;
683234956SluigiD("start");
684227614Sluigi	if (setaffinity(targ->thread, targ->affinity))
685227614Sluigi		goto quit;
686228975Suqs	/* setup poll(2) mechanism. */
687227614Sluigi	memset(fds, 0, sizeof(fds));
688227614Sluigi	fds[0].fd = targ->fd;
689227614Sluigi	fds[0].events = (POLLOUT);
690227614Sluigi
691227614Sluigi	/* main loop.*/
692227614Sluigi	gettimeofday(&targ->tic, NULL);
693227614Sluigi
694246896Sluigi    if (targ->g->dev_type == DEV_PCAP) {
695246896Sluigi	    int size = targ->g->pkt_size;
696246896Sluigi	    void *pkt = &targ->pkt;
697246896Sluigi	    pcap_t *p = targ->g->p;
698246896Sluigi
699246896Sluigi	    for (i = 0; !targ->cancel && (n == 0 || sent < n); i++) {
700234956Sluigi		if (pcap_inject(p, pkt, size) != -1)
701234956Sluigi			sent++;
702234956Sluigi		if (i > 10000) {
703234956Sluigi			targ->count = sent;
704234956Sluigi			i = 0;
705234956Sluigi		}
706246896Sluigi	    }
707246896Sluigi    } else if (targ->g->dev_type == DEV_TAP) { /* tap */
708246896Sluigi	    int size = targ->g->pkt_size;
709246896Sluigi	    void *pkt = &targ->pkt;
710246896Sluigi	    D("writing to file desc %d", targ->g->main_fd);
711246896Sluigi
712246896Sluigi	    for (i = 0; !targ->cancel && (n == 0 || sent < n); i++) {
713246896Sluigi		if (write(targ->g->main_fd, pkt, size) != -1)
714246896Sluigi			sent++;
715246896Sluigi		if (i > 10000) {
716246896Sluigi			targ->count = sent;
717246896Sluigi			i = 0;
718246896Sluigi		}
719246896Sluigi	    }
720227614Sluigi    } else {
721246896Sluigi	while (!targ->cancel && (n == 0 || sent < n)) {
722227614Sluigi
723227614Sluigi		/*
724227614Sluigi		 * wait for available room in the send queue(s)
725227614Sluigi		 */
726246896Sluigi		if (poll(fds, 1, 2000) <= 0) {
727238165Semaste			if (targ->cancel)
728238165Semaste				break;
729246896Sluigi			D("poll error/timeout on queue %d", targ->me);
730227614Sluigi			goto quit;
731227614Sluigi		}
732227614Sluigi		/*
733227614Sluigi		 * scan our queues and send on those with room
734227614Sluigi		 */
735246896Sluigi		if (options & OPT_COPY && sent > 100000 && !(targ->g->options & OPT_COPY) ) {
736246896Sluigi			D("drop copy");
737234956Sluigi			options &= ~OPT_COPY;
738246896Sluigi		}
739246896Sluigi		for (i = targ->qfirst; i < targ->qlast; i++) {
740238175Semaste			int m, limit = targ->g->burst;
741246896Sluigi			if (n > 0 && n - sent < limit)
742246896Sluigi				limit = n - sent;
743227614Sluigi			txring = NETMAP_TXRING(nifp, i);
744227614Sluigi			if (txring->avail == 0)
745227614Sluigi				continue;
746227614Sluigi			m = send_packets(txring, &targ->pkt, targ->g->pkt_size,
747234956Sluigi					 limit, options);
748227614Sluigi			sent += m;
749227614Sluigi			targ->count = sent;
750227614Sluigi		}
751227614Sluigi	}
752234956Sluigi	/* flush any remaining packets */
753227614Sluigi	ioctl(fds[0].fd, NIOCTXSYNC, NULL);
754227614Sluigi
755227614Sluigi	/* final part: wait all the TX queues to be empty. */
756227614Sluigi	for (i = targ->qfirst; i < targ->qlast; i++) {
757227614Sluigi		txring = NETMAP_TXRING(nifp, i);
758227614Sluigi		while (!NETMAP_TX_RING_EMPTY(txring)) {
759227614Sluigi			ioctl(fds[0].fd, NIOCTXSYNC, NULL);
760227614Sluigi			usleep(1); /* wait 1 tick */
761227614Sluigi		}
762227614Sluigi	}
763227614Sluigi    }
764227614Sluigi
765227614Sluigi	gettimeofday(&targ->toc, NULL);
766227614Sluigi	targ->completed = 1;
767227614Sluigi	targ->count = sent;
768227614Sluigi
769227614Sluigiquit:
770227614Sluigi	/* reset the ``used`` flag. */
771227614Sluigi	targ->used = 0;
772227614Sluigi
773227614Sluigi	return (NULL);
774227614Sluigi}
775227614Sluigi
776227614Sluigi
777227614Sluigistatic void
778246896Sluigireceive_pcap(u_char *user, const struct pcap_pkthdr * h,
779246896Sluigi	const u_char * bytes)
780227614Sluigi{
781227614Sluigi	int *count = (int *)user;
782246896Sluigi	(void)h;	/* UNUSED */
783246896Sluigi	(void)bytes;	/* UNUSED */
784227614Sluigi	(*count)++;
785227614Sluigi}
786227614Sluigi
787227614Sluigistatic int
788227614Sluigireceive_packets(struct netmap_ring *ring, u_int limit, int skip_payload)
789227614Sluigi{
790227614Sluigi	u_int cur, rx;
791227614Sluigi
792227614Sluigi	cur = ring->cur;
793227614Sluigi	if (ring->avail < limit)
794227614Sluigi		limit = ring->avail;
795227614Sluigi	for (rx = 0; rx < limit; rx++) {
796227614Sluigi		struct netmap_slot *slot = &ring->slot[cur];
797227614Sluigi		char *p = NETMAP_BUF(ring, slot->buf_idx);
798227614Sluigi
799227614Sluigi		if (!skip_payload)
800227614Sluigi			check_payload(p, slot->len);
801227614Sluigi
802227614Sluigi		cur = NETMAP_RING_NEXT(ring, cur);
803227614Sluigi	}
804227614Sluigi	ring->avail -= rx;
805227614Sluigi	ring->cur = cur;
806227614Sluigi
807227614Sluigi	return (rx);
808227614Sluigi}
809227614Sluigi
810227614Sluigistatic void *
811227614Sluigireceiver_body(void *data)
812227614Sluigi{
813227614Sluigi	struct targ *targ = (struct targ *) data;
814227614Sluigi	struct pollfd fds[1];
815227614Sluigi	struct netmap_if *nifp = targ->nifp;
816227614Sluigi	struct netmap_ring *rxring;
817246896Sluigi	int i;
818246896Sluigi	uint64_t received = 0;
819227614Sluigi
820227614Sluigi	if (setaffinity(targ->thread, targ->affinity))
821227614Sluigi		goto quit;
822227614Sluigi
823228975Suqs	/* setup poll(2) mechanism. */
824227614Sluigi	memset(fds, 0, sizeof(fds));
825227614Sluigi	fds[0].fd = targ->fd;
826227614Sluigi	fds[0].events = (POLLIN);
827227614Sluigi
828227614Sluigi	/* unbounded wait for the first packet. */
829246896Sluigi	for (;;) {
830227614Sluigi		i = poll(fds, 1, 1000);
831227614Sluigi		if (i > 0 && !(fds[0].revents & POLLERR))
832227614Sluigi			break;
833227614Sluigi		D("waiting for initial packets, poll returns %d %d", i, fds[0].revents);
834227614Sluigi	}
835227614Sluigi
836227614Sluigi	/* main loop, exit after 1s silence */
837227614Sluigi	gettimeofday(&targ->tic, NULL);
838246896Sluigi    if (targ->g->dev_type == DEV_PCAP) {
839238165Semaste	while (!targ->cancel) {
840246896Sluigi		/* XXX should we poll ? */
841227614Sluigi		pcap_dispatch(targ->g->p, targ->g->burst, receive_pcap, NULL);
842227614Sluigi	}
843246896Sluigi    } else if (targ->g->dev_type == DEV_TAP) {
844246896Sluigi	D("reading from %s fd %d", targ->g->ifname, targ->g->main_fd);
845246896Sluigi	while (!targ->cancel) {
846246896Sluigi		char buf[2048];
847246896Sluigi		/* XXX should we poll ? */
848246896Sluigi		if (read(targ->g->main_fd, buf, sizeof(buf)) > 0)
849246896Sluigi			targ->count++;
850246896Sluigi	}
851227614Sluigi    } else {
852238165Semaste	while (!targ->cancel) {
853227614Sluigi		/* Once we started to receive packets, wait at most 1 seconds
854227614Sluigi		   before quitting. */
855246896Sluigi		if (poll(fds, 1, 1 * 1000) <= 0 && targ->g->forever == 0) {
856227614Sluigi			gettimeofday(&targ->toc, NULL);
857228975Suqs			targ->toc.tv_sec -= 1; /* Subtract timeout time. */
858227614Sluigi			break;
859227614Sluigi		}
860227614Sluigi
861227614Sluigi		for (i = targ->qfirst; i < targ->qlast; i++) {
862227614Sluigi			int m;
863227614Sluigi
864227614Sluigi			rxring = NETMAP_RXRING(nifp, i);
865227614Sluigi			if (rxring->avail == 0)
866227614Sluigi				continue;
867227614Sluigi
868227614Sluigi			m = receive_packets(rxring, targ->g->burst,
869227614Sluigi					SKIP_PAYLOAD);
870227614Sluigi			received += m;
871227614Sluigi		}
872246896Sluigi		targ->count = received;
873227614Sluigi
874227614Sluigi		// tell the card we have read the data
875227614Sluigi		//ioctl(fds[0].fd, NIOCRXSYNC, NULL);
876227614Sluigi	}
877227614Sluigi    }
878227614Sluigi
879227614Sluigi	targ->completed = 1;
880227614Sluigi	targ->count = received;
881227614Sluigi
882227614Sluigiquit:
883227614Sluigi	/* reset the ``used`` flag. */
884227614Sluigi	targ->used = 0;
885227614Sluigi
886227614Sluigi	return (NULL);
887227614Sluigi}
888227614Sluigi
889246896Sluigi/* very crude code to print a number in normalized form.
890246896Sluigi * Caller has to make sure that the buffer is large enough.
891246896Sluigi */
892246896Sluigistatic const char *
893246896Sluiginorm(char *buf, double val)
894238170Semaste{
895246896Sluigi	char *units[] = { "", "K", "M", "G" };
896246896Sluigi	u_int i;
897238170Semaste
898246896Sluigi	for (i = 0; val >=1000 && i < sizeof(units)/sizeof(char *); i++)
899238170Semaste		val /= 1000;
900246896Sluigi	sprintf(buf, "%.2f %s", val, units[i]);
901246896Sluigi	return buf;
902238170Semaste}
903238170Semaste
904227614Sluigistatic void
905227614Sluigitx_output(uint64_t sent, int size, double delta)
906227614Sluigi{
907246896Sluigi	double bw, raw_bw, pps;
908246896Sluigi	char b1[40], b2[80], b3[80];
909227614Sluigi
910228276Sluigi	printf("Sent %" PRIu64 " packets, %d bytes each, in %.2f seconds.\n",
911227614Sluigi	       sent, size, delta);
912246896Sluigi	if (delta == 0)
913246896Sluigi		delta = 1e-6;
914246896Sluigi	if (size < 60)		/* correct for min packet size */
915246896Sluigi		size = 60;
916246896Sluigi	pps = sent / delta;
917246896Sluigi	bw = (8.0 * size * sent) / delta;
918246896Sluigi	/* raw packets have4 bytes crc + 20 bytes framing */
919246896Sluigi	raw_bw = (8.0 * (size + 24) * sent) / delta;
920238170Semaste
921246896Sluigi	printf("Speed: %spps Bandwidth: %sbps (raw %sbps)\n",
922246896Sluigi		norm(b1, pps), norm(b2, bw), norm(b3, raw_bw) );
923227614Sluigi}
924227614Sluigi
925227614Sluigi
926227614Sluigistatic void
927227614Sluigirx_output(uint64_t received, double delta)
928227614Sluigi{
929246896Sluigi	double pps;
930246896Sluigi	char b1[40];
931227614Sluigi
932246896Sluigi	printf("Received %" PRIu64 " packets, in %.2f seconds.\n", received, delta);
933227614Sluigi
934246896Sluigi	if (delta == 0)
935246896Sluigi		delta = 1e-6;
936246896Sluigi	pps = received / delta;
937246896Sluigi	printf("Speed: %spps\n", norm(b1, pps));
938227614Sluigi}
939227614Sluigi
940227614Sluigistatic void
941227614Sluigiusage(void)
942227614Sluigi{
943227614Sluigi	const char *cmd = "pkt-gen";
944227614Sluigi	fprintf(stderr,
945227614Sluigi		"Usage:\n"
946227614Sluigi		"%s arguments\n"
947227614Sluigi		"\t-i interface		interface name\n"
948246896Sluigi		"\t-f function		tx rx ping pong\n"
949246896Sluigi		"\t-n count		number of iterations (can be 0)\n"
950246896Sluigi		"\t-t pkts_to_send		also forces tx mode\n"
951246896Sluigi		"\t-r pkts_to_receive	also forces rx mode\n"
952227614Sluigi		"\t-l pkts_size		in bytes excluding CRC\n"
953227614Sluigi		"\t-d dst-ip		end with %%n to sweep n addresses\n"
954227614Sluigi		"\t-s src-ip		end with %%n to sweep n addresses\n"
955227614Sluigi		"\t-D dst-mac		end with %%n to sweep n addresses\n"
956227614Sluigi		"\t-S src-mac		end with %%n to sweep n addresses\n"
957246896Sluigi		"\t-a cpu_id		use setaffinity\n"
958227614Sluigi		"\t-b burst size		testing, mostly\n"
959227614Sluigi		"\t-c cores		cores to use\n"
960227614Sluigi		"\t-p threads		processes/threads to use\n"
961227614Sluigi		"\t-T report_ms		milliseconds between reports\n"
962246896Sluigi		"\t-P				use libpcap instead of netmap\n"
963227614Sluigi		"\t-w wait_for_link_time	in seconds\n"
964227614Sluigi		"",
965227614Sluigi		cmd);
966227614Sluigi
967227614Sluigi	exit(0);
968227614Sluigi}
969227614Sluigi
970246896Sluigistatic void
971246896Sluigistart_threads(struct glob_arg *g)
972246896Sluigi{
973246896Sluigi	int i;
974227614Sluigi
975246896Sluigi	targs = calloc(g->nthreads, sizeof(*targs));
976246896Sluigi	/*
977246896Sluigi	 * Now create the desired number of threads, each one
978246896Sluigi	 * using a single descriptor.
979246896Sluigi 	 */
980246896Sluigi	for (i = 0; i < g->nthreads; i++) {
981246896Sluigi		bzero(&targs[i], sizeof(targs[i]));
982246896Sluigi		targs[i].fd = -1; /* default, with pcap */
983246896Sluigi		targs[i].g = g;
984246896Sluigi
985246896Sluigi	    if (g->dev_type == DEV_NETMAP) {
986246896Sluigi		struct nmreq tifreq;
987246896Sluigi		int tfd;
988246896Sluigi
989246896Sluigi		/* register interface. */
990246896Sluigi		tfd = open("/dev/netmap", O_RDWR);
991246896Sluigi		if (tfd == -1) {
992246896Sluigi			D("Unable to open /dev/netmap");
993246896Sluigi			continue;
994246896Sluigi		}
995246896Sluigi		targs[i].fd = tfd;
996246896Sluigi
997246896Sluigi		bzero(&tifreq, sizeof(tifreq));
998246896Sluigi		strncpy(tifreq.nr_name, g->ifname, sizeof(tifreq.nr_name));
999246896Sluigi		tifreq.nr_version = NETMAP_API;
1000246896Sluigi		tifreq.nr_ringid = (g->nthreads > 1) ? (i | NETMAP_HW_RING) : 0;
1001246896Sluigi
1002246896Sluigi		/*
1003246896Sluigi		 * if we are acting as a receiver only, do not touch the transmit ring.
1004246896Sluigi		 * This is not the default because many apps may use the interface
1005246896Sluigi		 * in both directions, but a pure receiver does not.
1006246896Sluigi		 */
1007246896Sluigi		if (g->td_body == receiver_body) {
1008246896Sluigi			tifreq.nr_ringid |= NETMAP_NO_TX_POLL;
1009246896Sluigi		}
1010246896Sluigi
1011246896Sluigi		if ((ioctl(tfd, NIOCREGIF, &tifreq)) == -1) {
1012246896Sluigi			D("Unable to register %s", g->ifname);
1013246896Sluigi			continue;
1014246896Sluigi		}
1015246896Sluigi		targs[i].nmr = tifreq;
1016246896Sluigi		targs[i].nifp = NETMAP_IF(g->mmap_addr, tifreq.nr_offset);
1017246896Sluigi		/* start threads. */
1018246896Sluigi		targs[i].qfirst = (g->nthreads > 1) ? i : 0;
1019246896Sluigi		targs[i].qlast = (g->nthreads > 1) ? i+1 :
1020246896Sluigi			(g->td_body == receiver_body ? tifreq.nr_rx_rings : tifreq.nr_tx_rings);
1021246896Sluigi	    } else {
1022246896Sluigi		targs[i].fd = g->main_fd;
1023246896Sluigi	    }
1024246896Sluigi		targs[i].used = 1;
1025246896Sluigi		targs[i].me = i;
1026246896Sluigi		if (g->affinity >= 0) {
1027246896Sluigi			if (g->affinity < g->cpus)
1028246896Sluigi				targs[i].affinity = g->affinity;
1029246896Sluigi			else
1030246896Sluigi				targs[i].affinity = i % g->cpus;
1031246896Sluigi		} else
1032246896Sluigi			targs[i].affinity = -1;
1033246896Sluigi		/* default, init packets */
1034246896Sluigi		initialize_packet(&targs[i]);
1035246896Sluigi
1036246896Sluigi		if (pthread_create(&targs[i].thread, NULL, g->td_body,
1037246896Sluigi				   &targs[i]) == -1) {
1038246896Sluigi			D("Unable to create thread %d", i);
1039246896Sluigi			targs[i].used = 0;
1040246896Sluigi		}
1041246896Sluigi	}
1042246896Sluigi}
1043246896Sluigi
1044246896Sluigistatic void
1045246896Sluigimain_thread(struct glob_arg *g)
1046246896Sluigi{
1047246896Sluigi	int i;
1048246896Sluigi
1049246896Sluigi	uint64_t prev = 0;
1050246896Sluigi	uint64_t count = 0;
1051246896Sluigi	double delta_t;
1052246896Sluigi	struct timeval tic, toc;
1053246896Sluigi
1054246896Sluigi	gettimeofday(&toc, NULL);
1055246896Sluigi	for (;;) {
1056246896Sluigi		struct timeval now, delta;
1057246896Sluigi		uint64_t pps, usec, my_count, npkts;
1058246896Sluigi		int done = 0;
1059246896Sluigi
1060246896Sluigi		delta.tv_sec = g->report_interval/1000;
1061246896Sluigi		delta.tv_usec = (g->report_interval%1000)*1000;
1062246896Sluigi		select(0, NULL, NULL, NULL, &delta);
1063246896Sluigi		gettimeofday(&now, NULL);
1064246896Sluigi		time_second = now.tv_sec;
1065246896Sluigi		timersub(&now, &toc, &toc);
1066246896Sluigi		my_count = 0;
1067246896Sluigi		for (i = 0; i < g->nthreads; i++) {
1068246896Sluigi			my_count += targs[i].count;
1069246896Sluigi			if (targs[i].used == 0)
1070246896Sluigi				done++;
1071246896Sluigi		}
1072246896Sluigi		usec = toc.tv_sec* 1000000 + toc.tv_usec;
1073246896Sluigi		if (usec < 10000)
1074246896Sluigi			continue;
1075246896Sluigi		npkts = my_count - prev;
1076246896Sluigi		pps = (npkts*1000000 + usec/2) / usec;
1077246896Sluigi		D("%" PRIu64 " pps (%" PRIu64 " pkts in %" PRIu64 " usec)",
1078246896Sluigi			pps, npkts, usec);
1079246896Sluigi		prev = my_count;
1080246896Sluigi		toc = now;
1081246896Sluigi		if (done == g->nthreads)
1082246896Sluigi			break;
1083246896Sluigi	}
1084246896Sluigi
1085246896Sluigi	timerclear(&tic);
1086246896Sluigi	timerclear(&toc);
1087246896Sluigi	for (i = 0; i < g->nthreads; i++) {
1088246896Sluigi		/*
1089246896Sluigi		 * Join active threads, unregister interfaces and close
1090246896Sluigi		 * file descriptors.
1091246896Sluigi		 */
1092246896Sluigi		pthread_join(targs[i].thread, NULL);
1093246896Sluigi		close(targs[i].fd);
1094246896Sluigi
1095246896Sluigi		if (targs[i].completed == 0)
1096246896Sluigi			D("ouch, thread %d exited with error", i);
1097246896Sluigi
1098246896Sluigi		/*
1099246896Sluigi		 * Collect threads output and extract information about
1100246896Sluigi		 * how long it took to send all the packets.
1101246896Sluigi		 */
1102246896Sluigi		count += targs[i].count;
1103246896Sluigi		if (!timerisset(&tic) || timercmp(&targs[i].tic, &tic, <))
1104246896Sluigi			tic = targs[i].tic;
1105246896Sluigi		if (!timerisset(&toc) || timercmp(&targs[i].toc, &toc, >))
1106246896Sluigi			toc = targs[i].toc;
1107246896Sluigi	}
1108246896Sluigi
1109246896Sluigi	/* print output. */
1110246896Sluigi	timersub(&toc, &tic, &toc);
1111246896Sluigi	delta_t = toc.tv_sec + 1e-6* toc.tv_usec;
1112246896Sluigi	if (g->td_body == sender_body)
1113246896Sluigi		tx_output(count, g->pkt_size, delta_t);
1114246896Sluigi	else
1115246896Sluigi		rx_output(count, delta_t);
1116246896Sluigi
1117246896Sluigi	if (g->dev_type == DEV_NETMAP) {
1118246896Sluigi		ioctl(g->main_fd, NIOCUNREGIF, NULL); // XXX deprecated
1119246896Sluigi		munmap(g->mmap_addr, g->mmap_size);
1120246896Sluigi		close(g->main_fd);
1121246896Sluigi	}
1122246896Sluigi}
1123246896Sluigi
1124246896Sluigi
1125246896Sluigistruct sf {
1126246896Sluigi	char *key;
1127246896Sluigi	void *f;
1128246896Sluigi};
1129246896Sluigi
1130246896Sluigistatic struct sf func[] = {
1131246896Sluigi	{ "tx",	sender_body },
1132246896Sluigi	{ "rx",	receiver_body },
1133246896Sluigi	{ "ping",	pinger_body },
1134246896Sluigi	{ "pong",	ponger_body },
1135246896Sluigi	{ NULL, NULL }
1136246896Sluigi};
1137246896Sluigi
1138246896Sluigistatic int
1139246896Sluigitap_alloc(char *dev)
1140246896Sluigi{
1141246896Sluigi	struct ifreq ifr;
1142246896Sluigi	int fd, err;
1143246896Sluigi	char *clonedev = TAP_CLONEDEV;
1144246896Sluigi
1145246896Sluigi	(void)err;
1146246896Sluigi	(void)dev;
1147246896Sluigi	/* Arguments taken by the function:
1148246896Sluigi	 *
1149246896Sluigi	 * char *dev: the name of an interface (or '\0'). MUST have enough
1150246896Sluigi	 *   space to hold the interface name if '\0' is passed
1151246896Sluigi	 * int flags: interface flags (eg, IFF_TUN etc.)
1152246896Sluigi	 */
1153246896Sluigi
1154246896Sluigi#ifdef __FreeBSD__
1155246896Sluigi	if (dev[3]) { /* tapSomething */
1156246896Sluigi		static char buf[128];
1157246896Sluigi		snprintf(buf, sizeof(buf), "/dev/%s", dev);
1158246896Sluigi		clonedev = buf;
1159246896Sluigi	}
1160246896Sluigi#endif
1161246896Sluigi	/* open the device */
1162246896Sluigi	if( (fd = open(clonedev, O_RDWR)) < 0 ) {
1163246896Sluigi		return fd;
1164246896Sluigi	}
1165246896Sluigi	D("%s open successful", clonedev);
1166246896Sluigi
1167246896Sluigi	/* preparation of the struct ifr, of type "struct ifreq" */
1168246896Sluigi	memset(&ifr, 0, sizeof(ifr));
1169246896Sluigi
1170246896Sluigi#ifdef linux
1171246896Sluigi	ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
1172246896Sluigi
1173246896Sluigi	if (*dev) {
1174246896Sluigi		/* if a device name was specified, put it in the structure; otherwise,
1175246896Sluigi		* the kernel will try to allocate the "next" device of the
1176246896Sluigi		* specified type */
1177246896Sluigi		strncpy(ifr.ifr_name, dev, IFNAMSIZ);
1178246896Sluigi	}
1179246896Sluigi
1180246896Sluigi	/* try to create the device */
1181246896Sluigi	if( (err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0 ) {
1182246896Sluigi		D("failed to to a TUNSETIFF");
1183246896Sluigi		close(fd);
1184246896Sluigi		return err;
1185246896Sluigi	}
1186246896Sluigi
1187246896Sluigi	/* if the operation was successful, write back the name of the
1188246896Sluigi	* interface to the variable "dev", so the caller can know
1189246896Sluigi	* it. Note that the caller MUST reserve space in *dev (see calling
1190246896Sluigi	* code below) */
1191246896Sluigi	strcpy(dev, ifr.ifr_name);
1192246896Sluigi	D("new name is %s", dev);
1193246896Sluigi#endif /* linux */
1194246896Sluigi
1195246896Sluigi        /* this is the special file descriptor that the caller will use to talk
1196246896Sluigi         * with the virtual interface */
1197246896Sluigi        return fd;
1198246896Sluigi}
1199246896Sluigi
1200227614Sluigiint
1201227614Sluigimain(int arc, char **argv)
1202227614Sluigi{
1203246896Sluigi	int i;
1204227614Sluigi
1205227614Sluigi	struct glob_arg g;
1206227614Sluigi
1207227614Sluigi	struct nmreq nmr;
1208227614Sluigi	int ch;
1209227614Sluigi	int wait_link = 2;
1210227614Sluigi	int devqueues = 1;	/* how many device queues */
1211227614Sluigi
1212227614Sluigi	bzero(&g, sizeof(g));
1213227614Sluigi
1214246896Sluigi	g.main_fd = -1;
1215246896Sluigi	g.td_body = receiver_body;
1216246896Sluigi	g.report_interval = 1000;	/* report interval */
1217246896Sluigi	g.affinity = -1;
1218246896Sluigi	/* ip addresses can also be a range x.x.x.x-x.x.x.y */
1219246896Sluigi	g.src_ip.name = "10.0.0.1";
1220246896Sluigi	g.dst_ip.name = "10.1.0.1";
1221246896Sluigi	g.dst_mac.name = "ff:ff:ff:ff:ff:ff";
1222246896Sluigi	g.src_mac.name = NULL;
1223227614Sluigi	g.pkt_size = 60;
1224227614Sluigi	g.burst = 512;		// default
1225227614Sluigi	g.nthreads = 1;
1226227614Sluigi	g.cpus = 1;
1227227614Sluigi
1228227614Sluigi	while ( (ch = getopt(arc, argv,
1229246896Sluigi			"a:f:n:i:t:r:l:d:s:D:S:b:c:o:p:PT:w:Wv")) != -1) {
1230246896Sluigi		struct sf *fn;
1231246896Sluigi
1232227614Sluigi		switch(ch) {
1233227614Sluigi		default:
1234227614Sluigi			D("bad option %c %s", ch, optarg);
1235227614Sluigi			usage();
1236227614Sluigi			break;
1237246896Sluigi
1238246896Sluigi		case 'n':
1239246896Sluigi			g.npackets = atoi(optarg);
1240246896Sluigi			break;
1241246896Sluigi
1242246896Sluigi		case 'f':
1243246896Sluigi			for (fn = func; fn->key; fn++) {
1244246896Sluigi				if (!strcmp(fn->key, optarg))
1245246896Sluigi					break;
1246246896Sluigi			}
1247246896Sluigi			if (fn->key)
1248246896Sluigi				g.td_body = fn->f;
1249246896Sluigi			else
1250246896Sluigi				D("unrecognised function %s", optarg);
1251246896Sluigi			break;
1252246896Sluigi
1253246896Sluigi		case 'o':	/* data generation options */
1254234956Sluigi			g.options = atoi(optarg);
1255234956Sluigi			break;
1256246896Sluigi
1257246896Sluigi		case 'a':       /* force affinity */
1258246896Sluigi			g.affinity = atoi(optarg);
1259246896Sluigi			break;
1260246896Sluigi
1261227614Sluigi		case 'i':	/* interface */
1262246896Sluigi			g.ifname = optarg;
1263246896Sluigi			if (!strncmp(optarg, "tap", 3))
1264246896Sluigi				g.dev_type = DEV_TAP;
1265246896Sluigi			else
1266246896Sluigi				g.dev_type = DEV_NETMAP;
1267227614Sluigi			break;
1268246896Sluigi
1269246896Sluigi		case 't':	/* send, deprecated */
1270246896Sluigi			D("-t deprecated, please use -f tx -n %s", optarg);
1271246896Sluigi			g.td_body = sender_body;
1272227614Sluigi			g.npackets = atoi(optarg);
1273227614Sluigi			break;
1274246896Sluigi
1275227614Sluigi		case 'r':	/* receive */
1276246896Sluigi			D("-r deprecated, please use -f rx -n %s", optarg);
1277246896Sluigi			g.td_body = receiver_body;
1278227614Sluigi			g.npackets = atoi(optarg);
1279227614Sluigi			break;
1280246896Sluigi
1281227614Sluigi		case 'l':	/* pkt_size */
1282227614Sluigi			g.pkt_size = atoi(optarg);
1283227614Sluigi			break;
1284246896Sluigi
1285227614Sluigi		case 'd':
1286246896Sluigi			g.dst_ip.name = optarg;
1287227614Sluigi			break;
1288246896Sluigi
1289227614Sluigi		case 's':
1290246896Sluigi			g.src_ip.name = optarg;
1291227614Sluigi			break;
1292246896Sluigi
1293227614Sluigi		case 'T':	/* report interval */
1294246896Sluigi			g.report_interval = atoi(optarg);
1295227614Sluigi			break;
1296246896Sluigi
1297227614Sluigi		case 'w':
1298227614Sluigi			wait_link = atoi(optarg);
1299227614Sluigi			break;
1300246896Sluigi
1301246896Sluigi		case 'W':
1302246896Sluigi			g.forever = 1; /* do not exit rx even with no traffic */
1303246896Sluigi			break;
1304246896Sluigi
1305227614Sluigi		case 'b':	/* burst */
1306227614Sluigi			g.burst = atoi(optarg);
1307227614Sluigi			break;
1308227614Sluigi		case 'c':
1309227614Sluigi			g.cpus = atoi(optarg);
1310227614Sluigi			break;
1311227614Sluigi		case 'p':
1312227614Sluigi			g.nthreads = atoi(optarg);
1313227614Sluigi			break;
1314227614Sluigi
1315227614Sluigi		case 'P':
1316246896Sluigi			g.dev_type = DEV_PCAP;
1317227614Sluigi			break;
1318227614Sluigi
1319227614Sluigi		case 'D': /* destination mac */
1320246896Sluigi			g.dst_mac.name = optarg;
1321227614Sluigi			break;
1322246896Sluigi
1323227614Sluigi		case 'S': /* source mac */
1324246896Sluigi			g.src_mac.name = optarg;
1325227614Sluigi			break;
1326227614Sluigi		case 'v':
1327227614Sluigi			verbose++;
1328227614Sluigi		}
1329227614Sluigi	}
1330227614Sluigi
1331246896Sluigi	if (g.ifname == NULL) {
1332227614Sluigi		D("missing ifname");
1333227614Sluigi		usage();
1334227614Sluigi	}
1335246896Sluigi
1336246896Sluigi	i = system_ncpus();
1337246896Sluigi	if (g.cpus < 0 || g.cpus > i) {
1338246896Sluigi		D("%d cpus is too high, have only %d cpus", g.cpus, i);
1339246896Sluigi		usage();
1340227614Sluigi	}
1341246896Sluigi	if (g.cpus == 0)
1342246896Sluigi		g.cpus = i;
1343246896Sluigi
1344227614Sluigi	if (g.pkt_size < 16 || g.pkt_size > 1536) {
1345227614Sluigi		D("bad pktsize %d\n", g.pkt_size);
1346227614Sluigi		usage();
1347227614Sluigi	}
1348227614Sluigi
1349246896Sluigi	if (g.src_mac.name == NULL) {
1350246896Sluigi		static char mybuf[20] = "00:00:00:00:00:00";
1351234956Sluigi		/* retrieve source mac address. */
1352246896Sluigi		if (source_hwaddr(g.ifname, mybuf) == -1) {
1353234956Sluigi			D("Unable to retrieve source mac");
1354234956Sluigi			// continue, fail later
1355234956Sluigi		}
1356246896Sluigi		g.src_mac.name = mybuf;
1357234956Sluigi	}
1358246896Sluigi	/* extract address ranges */
1359246896Sluigi	extract_ip_range(&g.src_ip);
1360246896Sluigi	extract_ip_range(&g.dst_ip);
1361246896Sluigi	extract_mac_range(&g.src_mac);
1362246896Sluigi	extract_mac_range(&g.dst_mac);
1363234956Sluigi
1364246896Sluigi    if (g.dev_type == DEV_TAP) {
1365246896Sluigi	D("want to use tap %s", g.ifname);
1366246896Sluigi	g.main_fd = tap_alloc(g.ifname);
1367246896Sluigi	if (g.main_fd < 0) {
1368246896Sluigi		D("cannot open tap %s", g.ifname);
1369246896Sluigi		usage();
1370246896Sluigi	}
1371246896Sluigi    } else if (g.dev_type > DEV_NETMAP) {
1372246896Sluigi	char pcap_errbuf[PCAP_ERRBUF_SIZE];
1373246896Sluigi
1374246896Sluigi	D("using pcap on %s", g.ifname);
1375246896Sluigi	pcap_errbuf[0] = '\0'; // init the buffer
1376246896Sluigi	g.p = pcap_open_live(g.ifname, 0, 1, 100, pcap_errbuf);
1377234956Sluigi	if (g.p == NULL) {
1378246896Sluigi		D("cannot open pcap on %s", g.ifname);
1379234956Sluigi		usage();
1380234956Sluigi	}
1381234956Sluigi    } else {
1382227614Sluigi	bzero(&nmr, sizeof(nmr));
1383232238Sluigi	nmr.nr_version = NETMAP_API;
1384227614Sluigi	/*
1385227614Sluigi	 * Open the netmap device to fetch the number of queues of our
1386227614Sluigi	 * interface.
1387227614Sluigi	 *
1388227614Sluigi	 * The first NIOCREGIF also detaches the card from the
1389227614Sluigi	 * protocol stack and may cause a reset of the card,
1390227614Sluigi	 * which in turn may take some time for the PHY to
1391227614Sluigi	 * reconfigure.
1392227614Sluigi	 */
1393246896Sluigi	g.main_fd = open("/dev/netmap", O_RDWR);
1394246896Sluigi	if (g.main_fd == -1) {
1395227614Sluigi		D("Unable to open /dev/netmap");
1396246896Sluigi		// fail later
1397227614Sluigi	} else {
1398246896Sluigi		if ((ioctl(g.main_fd, NIOCGINFO, &nmr)) == -1) {
1399227614Sluigi			D("Unable to get if info without name");
1400227614Sluigi		} else {
1401227614Sluigi			D("map size is %d Kb", nmr.nr_memsize >> 10);
1402227614Sluigi		}
1403227614Sluigi		bzero(&nmr, sizeof(nmr));
1404232238Sluigi		nmr.nr_version = NETMAP_API;
1405246896Sluigi		strncpy(nmr.nr_name, g.ifname, sizeof(nmr.nr_name));
1406246896Sluigi		if ((ioctl(g.main_fd, NIOCGINFO, &nmr)) == -1) {
1407246896Sluigi			D("Unable to get if info for %s", g.ifname);
1408227614Sluigi		}
1409232238Sluigi		devqueues = nmr.nr_rx_rings;
1410227614Sluigi	}
1411227614Sluigi
1412227614Sluigi	/* validate provided nthreads. */
1413227614Sluigi	if (g.nthreads < 1 || g.nthreads > devqueues) {
1414227614Sluigi		D("bad nthreads %d, have %d queues", g.nthreads, devqueues);
1415227614Sluigi		// continue, fail later
1416227614Sluigi	}
1417227614Sluigi
1418227614Sluigi	/*
1419227614Sluigi	 * Map the netmap shared memory: instead of issuing mmap()
1420227614Sluigi	 * inside the body of the threads, we prefer to keep this
1421227614Sluigi	 * operation here to simplify the thread logic.
1422227614Sluigi	 */
1423246896Sluigi	D("mapping %d Kbytes", nmr.nr_memsize>>10);
1424246896Sluigi	g.mmap_size = nmr.nr_memsize;
1425246896Sluigi	g.mmap_addr = (struct netmap_d *) mmap(0, nmr.nr_memsize,
1426227614Sluigi					    PROT_WRITE | PROT_READ,
1427246896Sluigi					    MAP_SHARED, g.main_fd, 0);
1428246896Sluigi	if (g.mmap_addr == MAP_FAILED) {
1429227614Sluigi		D("Unable to mmap %d KB", nmr.nr_memsize >> 10);
1430227614Sluigi		// continue, fail later
1431227614Sluigi	}
1432227614Sluigi
1433227614Sluigi	/*
1434227614Sluigi	 * Register the interface on the netmap device: from now on,
1435227614Sluigi	 * we can operate on the network interface without any
1436227614Sluigi	 * interference from the legacy network stack.
1437227614Sluigi	 *
1438227614Sluigi	 * We decide to put the first interface registration here to
1439227614Sluigi	 * give time to cards that take a long time to reset the PHY.
1440227614Sluigi	 */
1441232238Sluigi	nmr.nr_version = NETMAP_API;
1442246896Sluigi	if (ioctl(g.main_fd, NIOCREGIF, &nmr) == -1) {
1443246896Sluigi		D("Unable to register interface %s", g.ifname);
1444227614Sluigi		//continue, fail later
1445227614Sluigi	}
1446227614Sluigi
1447227614Sluigi
1448227614Sluigi	/* Print some debug information. */
1449227614Sluigi	fprintf(stdout,
1450227614Sluigi		"%s %s: %d queues, %d threads and %d cpus.\n",
1451246896Sluigi		(g.td_body == sender_body) ? "Sending on" : "Receiving from",
1452246896Sluigi		g.ifname,
1453227614Sluigi		devqueues,
1454227614Sluigi		g.nthreads,
1455227614Sluigi		g.cpus);
1456246896Sluigi	if (g.td_body == sender_body) {
1457227614Sluigi		fprintf(stdout, "%s -> %s (%s -> %s)\n",
1458246896Sluigi			g.src_ip.name, g.dst_ip.name,
1459246896Sluigi			g.src_mac.name, g.dst_mac.name);
1460227614Sluigi	}
1461227614Sluigi
1462227614Sluigi	/* Exit if something went wrong. */
1463246896Sluigi	if (g.main_fd < 0) {
1464227614Sluigi		D("aborting");
1465227614Sluigi		usage();
1466227614Sluigi	}
1467234956Sluigi    }
1468227614Sluigi
1469234956Sluigi	if (g.options) {
1470234956Sluigi		D("special options:%s%s%s%s\n",
1471234956Sluigi			g.options & OPT_PREFETCH ? " prefetch" : "",
1472234956Sluigi			g.options & OPT_ACCESS ? " access" : "",
1473234956Sluigi			g.options & OPT_MEMCPY ? " memcpy" : "",
1474234956Sluigi			g.options & OPT_COPY ? " copy" : "");
1475234956Sluigi	}
1476227614Sluigi	/* Wait for PHY reset. */
1477227614Sluigi	D("Wait %d secs for phy reset", wait_link);
1478227614Sluigi	sleep(wait_link);
1479227614Sluigi	D("Ready...");
1480227614Sluigi
1481227614Sluigi	/* Install ^C handler. */
1482227614Sluigi	global_nthreads = g.nthreads;
1483227614Sluigi	signal(SIGINT, sigint_h);
1484227614Sluigi
1485246896Sluigi#if 0 // XXX this is not needed, i believe
1486246896Sluigi	if (g.dev_type > DEV_NETMAP) {
1487246896Sluigi		g.p = pcap_open_live(g.ifname, 0, 1, 100, NULL);
1488234956Sluigi		if (g.p == NULL) {
1489246896Sluigi			D("cannot open pcap on %s", g.ifname);
1490234956Sluigi			usage();
1491234956Sluigi		} else
1492246896Sluigi			D("using pcap %p on %s", g.p, g.ifname);
1493227614Sluigi	}
1494246896Sluigi#endif // XXX
1495246896Sluigi	start_threads(&g);
1496246896Sluigi	main_thread(&g);
1497246896Sluigi	return 0;
1498246896Sluigi}
1499227614Sluigi
1500227614Sluigi/* end of file */
1501