pkt-gen.c revision 228276
1/*
2 * Copyright (C) 2011 Matteo Landi, Luigi Rizzo. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *   1. Redistributions of source code must retain the above copyright
8 *      notice, this list of conditions and the following disclaimer.
9 *   2. Redistributions in binary form must reproduce the above copyright
10 *      notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 */
25
26/*
27 * $FreeBSD: head/tools/tools/netmap/pkt-gen.c 228276 2011-12-05 12:06:53Z luigi $
28 * $Id: pkt-gen.c 9827 2011-12-05 11:29:34Z luigi $
29 *
30 * Example program to show how to build a multithreaded packet
31 * source/sink using the netmap device.
32 *
33 * In this example we create a programmable number of threads
34 * to take care of all the queues of the interface used to
35 * send or receive traffic.
36 *
37 */
38
39const char *default_payload="netmap pkt-gen Luigi Rizzo and Matteo Landi\n"
40	"http://info.iet.unipi.it/~luigi/netmap/ ";
41
42#include <errno.h>
43#include <pthread.h>	/* pthread_* */
44#include <pthread_np.h>	/* pthread w/ affinity */
45#include <signal.h>	/* signal */
46#include <stdlib.h>
47#include <stdio.h>
48#include <inttypes.h>	/* PRI* macros */
49#include <string.h>	/* strcmp */
50#include <fcntl.h>	/* open */
51#include <unistd.h>	/* close */
52#include <ifaddrs.h>	/* getifaddrs */
53
54#include <sys/mman.h>	/* PROT_* */
55#include <sys/ioctl.h>	/* ioctl */
56#include <sys/poll.h>
57#include <sys/socket.h>	/* sockaddr.. */
58#include <arpa/inet.h>	/* ntohs */
59#include <sys/param.h>
60#include <sys/cpuset.h>	/* cpu_set */
61#include <sys/sysctl.h>	/* sysctl */
62#include <sys/time.h>	/* timersub */
63
64#include <net/ethernet.h>
65#include <net/if.h>	/* ifreq */
66#include <net/if_dl.h>	/* LLADDR */
67
68#include <netinet/in.h>
69#include <netinet/ip.h>
70#include <netinet/udp.h>
71
72#include <net/netmap.h>
73#include <net/netmap_user.h>
74#include <pcap/pcap.h>
75
76
77static inline int min(int a, int b) { return a < b ? a : b; }
78
79/* debug support */
80#define D(format, ...)				\
81	fprintf(stderr, "%s [%d] " format "\n", 	\
82	__FUNCTION__, __LINE__, ##__VA_ARGS__)
83
84#ifndef EXPERIMENTAL
85#define EXPERIMENTAL 0
86#endif
87
88int verbose = 0;
89#define MAX_QUEUES 64	/* no need to limit */
90
91#define SKIP_PAYLOAD 1 /* do not check payload. */
92
93#if EXPERIMENTAL
94/* Wrapper around `rdtsc' to take reliable timestamps flushing the pipeline */
95#define netmap_rdtsc(t) \
96	do { \
97		u_int __regs[4];					\
98									\
99		do_cpuid(0, __regs);					\
100		(t) = rdtsc();						\
101	} while (0)
102
103static __inline void
104do_cpuid(u_int ax, u_int *p)
105{
106	__asm __volatile("cpuid"
107			 : "=a" (p[0]), "=b" (p[1]), "=c" (p[2]), "=d" (p[3])
108			 :  "0" (ax));
109}
110
111static __inline uint64_t
112rdtsc(void)
113{
114	uint64_t rv;
115
116	__asm __volatile("rdtsc" : "=A" (rv));
117	return (rv);
118}
119#define MAX_SAMPLES 100000
120#endif /* EXPERIMENTAL */
121
122
123struct pkt {
124	struct ether_header eh;
125	struct ip ip;
126	struct udphdr udp;
127	uint8_t body[NETMAP_BUF_SIZE];
128} __attribute__((__packed__));
129
130/*
131 * global arguments for all threads
132 */
133struct glob_arg {
134	const char *src_ip;
135	const char *dst_ip;
136	const char *src_mac;
137	const char *dst_mac;
138	int pkt_size;
139	int burst;
140	int npackets;	/* total packets to send */
141	int nthreads;
142	int cpus;
143	int use_pcap;
144	pcap_t *p;
145};
146
147struct mystat {
148	uint64_t containers[8];
149};
150
151/*
152 * Arguments for a new thread. The same structure is used by
153 * the source and the sink
154 */
155struct targ {
156	struct glob_arg *g;
157	int used;
158	int completed;
159	int fd;
160	struct nmreq nmr;
161	struct netmap_if *nifp;
162	uint16_t	qfirst, qlast; /* range of queues to scan */
163	uint64_t count;
164	struct timeval tic, toc;
165	int me;
166	pthread_t thread;
167	int affinity;
168
169	uint8_t	dst_mac[6];
170	uint8_t	src_mac[6];
171	u_int dst_mac_range;
172	u_int src_mac_range;
173	uint32_t dst_ip;
174	uint32_t src_ip;
175	u_int dst_ip_range;
176	u_int src_ip_range;
177
178	struct pkt pkt;
179};
180
181
182static struct targ *targs;
183static int global_nthreads;
184
185/* control-C handler */
186static void
187sigint_h(__unused int sig)
188{
189	for (int i = 0; i < global_nthreads; i++) {
190		/* cancel active threads. */
191		if (targs[i].used == 0)
192			continue;
193
194		D("Cancelling thread #%d\n", i);
195		pthread_cancel(targs[i].thread);
196		targs[i].used = 0;
197	}
198
199	signal(SIGINT, SIG_DFL);
200}
201
202
203/* sysctl wrapper to return the number of active CPUs */
204static int
205system_ncpus(void)
206{
207	int mib[2], ncpus;
208	size_t len;
209
210	mib[0] = CTL_HW;
211	mib[1] = HW_NCPU;
212	len = sizeof(mib);
213	sysctl(mib, 2, &ncpus, &len, NULL, 0);
214
215	return (ncpus);
216}
217
218/*
219 * locate the src mac address for our interface, put it
220 * into the user-supplied buffer. return 0 if ok, -1 on error.
221 */
222static int
223source_hwaddr(const char *ifname, char *buf)
224{
225	struct ifaddrs *ifaphead, *ifap;
226	int l = sizeof(ifap->ifa_name);
227
228	if (getifaddrs(&ifaphead) != 0) {
229		D("getifaddrs %s failed", ifname);
230		return (-1);
231	}
232
233	for (ifap = ifaphead; ifap; ifap = ifap->ifa_next) {
234		struct sockaddr_dl *sdl =
235			(struct sockaddr_dl *)ifap->ifa_addr;
236		uint8_t *mac;
237
238		if (!sdl || sdl->sdl_family != AF_LINK)
239			continue;
240		if (strncmp(ifap->ifa_name, ifname, l) != 0)
241			continue;
242		mac = (uint8_t *)LLADDR(sdl);
243		sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x",
244			mac[0], mac[1], mac[2],
245			mac[3], mac[4], mac[5]);
246		if (verbose)
247			D("source hwaddr %s", buf);
248		break;
249	}
250	freeifaddrs(ifaphead);
251	return ifap ? 0 : 1;
252}
253
254
255/* set the thread affinity. */
256static int
257setaffinity(pthread_t me, int i)
258{
259	cpuset_t cpumask;
260
261	if (i == -1)
262		return 0;
263
264	/* Set thread affinity affinity.*/
265	CPU_ZERO(&cpumask);
266	CPU_SET(i, &cpumask);
267
268	if (pthread_setaffinity_np(me, sizeof(cpuset_t), &cpumask) != 0) {
269		D("Unable to set affinity");
270		return 1;
271	}
272	return 0;
273}
274
275/* Compute the checksum of the given ip header. */
276static uint16_t
277checksum(const void *data, uint16_t len)
278{
279        const uint8_t *addr = data;
280        uint32_t sum = 0;
281
282        while (len > 1) {
283                sum += addr[0] * 256 + addr[1];
284                addr += 2;
285                len -= 2;
286        }
287
288        if (len == 1)
289                sum += *addr * 256;
290
291        sum = (sum >> 16) + (sum & 0xffff);
292        sum += (sum >> 16);
293
294        sum = htons(sum);
295
296        return ~sum;
297}
298
299/*
300 * Fill a packet with some payload.
301 */
302static void
303initialize_packet(struct targ *targ)
304{
305	struct pkt *pkt = &targ->pkt;
306	struct ether_header *eh;
307	struct ip *ip;
308	struct udphdr *udp;
309	uint16_t paylen = targ->g->pkt_size - sizeof(*eh) - sizeof(*ip);
310	int i, l, l0 = strlen(default_payload);
311	char *p;
312
313	for (i = 0; i < paylen;) {
314		l = min(l0, paylen - i);
315		bcopy(default_payload, pkt->body + i, l);
316		i += l;
317	}
318	pkt->body[i-1] = '\0';
319
320	udp = &pkt->udp;
321	udp->uh_sport = htons(1234);
322        udp->uh_dport = htons(4321);
323	udp->uh_ulen = htons(paylen);
324	udp->uh_sum = 0; // checksum(udp, sizeof(*udp));
325
326	ip = &pkt->ip;
327        ip->ip_v = IPVERSION;
328        ip->ip_hl = 5;
329        ip->ip_id = 0;
330        ip->ip_tos = IPTOS_LOWDELAY;
331	ip->ip_len = ntohs(targ->g->pkt_size - sizeof(*eh));
332        ip->ip_id = 0;
333        ip->ip_off = htons(IP_DF); /* Don't fragment */
334        ip->ip_ttl = IPDEFTTL;
335	ip->ip_p = IPPROTO_UDP;
336	inet_aton(targ->g->src_ip, (struct in_addr *)&ip->ip_src);
337	inet_aton(targ->g->dst_ip, (struct in_addr *)&ip->ip_dst);
338	targ->dst_ip = ip->ip_dst.s_addr;
339	targ->src_ip = ip->ip_src.s_addr;
340	p = index(targ->g->src_ip, '-');
341	if (p) {
342		targ->dst_ip_range = atoi(p+1);
343		D("dst-ip sweep %d addresses", targ->dst_ip_range);
344	}
345	ip->ip_sum = checksum(ip, sizeof(*ip));
346
347	eh = &pkt->eh;
348	bcopy(ether_aton(targ->g->src_mac), targ->src_mac, 6);
349	bcopy(targ->src_mac, eh->ether_shost, 6);
350	p = index(targ->g->src_mac, '-');
351	if (p)
352		targ->src_mac_range = atoi(p+1);
353
354	bcopy(ether_aton(targ->g->dst_mac), targ->dst_mac, 6);
355	bcopy(targ->dst_mac, eh->ether_dhost, 6);
356	p = index(targ->g->dst_mac, '-');
357	if (p)
358		targ->dst_mac_range = atoi(p+1);
359	eh->ether_type = htons(ETHERTYPE_IP);
360}
361
362/* Check the payload of the packet for errors (use it for debug).
363 * Look for consecutive ascii representations of the size of the packet.
364 */
365static void
366check_payload(char *p, int psize)
367{
368	char temp[64];
369	int n_read, size, sizelen;
370
371	/* get the length in ASCII of the length of the packet. */
372	sizelen = sprintf(temp, "%d", psize) + 1; // include a whitespace
373
374	/* dummy payload. */
375	p += 14; /* skip packet header. */
376	n_read = 14;
377	while (psize - n_read >= sizelen) {
378		sscanf(p, "%d", &size);
379		if (size != psize) {
380			D("Read %d instead of %d", size, psize);
381			break;
382		}
383
384		p += sizelen;
385		n_read += sizelen;
386	}
387}
388
389
390/*
391 * create and enqueue a batch of packets on a ring.
392 * On the last one set NS_REPORT to tell the driver to generate
393 * an interrupt when done.
394 */
395static int
396send_packets(struct netmap_ring *ring, struct pkt *pkt,
397		int size, u_int count, int fill_all)
398{
399	u_int sent, cur = ring->cur;
400
401	if (ring->avail < count)
402		count = ring->avail;
403
404	for (sent = 0; sent < count; sent++) {
405		struct netmap_slot *slot = &ring->slot[cur];
406		char *p = NETMAP_BUF(ring, slot->buf_idx);
407
408		if (fill_all)
409			memcpy(p, pkt, size);
410
411		slot->len = size;
412		if (sent == count - 1)
413			slot->flags |= NS_REPORT;
414		cur = NETMAP_RING_NEXT(ring, cur);
415	}
416	ring->avail -= sent;
417	ring->cur = cur;
418
419	return (sent);
420}
421
422static void *
423sender_body(void *data)
424{
425	struct targ *targ = (struct targ *) data;
426
427	struct pollfd fds[1];
428	struct netmap_if *nifp = targ->nifp;
429	struct netmap_ring *txring;
430	int i, n = targ->g->npackets / targ->g->nthreads, sent = 0;
431	int fill_all = 1;
432
433	if (setaffinity(targ->thread, targ->affinity))
434		goto quit;
435	/* setup poll(2) machanism. */
436	memset(fds, 0, sizeof(fds));
437	fds[0].fd = targ->fd;
438	fds[0].events = (POLLOUT);
439
440	/* main loop.*/
441	gettimeofday(&targ->tic, NULL);
442    if (targ->g->use_pcap) {
443	int size = targ->g->pkt_size;
444	void *pkt = &targ->pkt;
445	pcap_t *p = targ->g->p;
446
447	for (; sent < n; sent++) {
448		if (pcap_inject(p, pkt, size) == -1)
449			break;
450	}
451    } else {
452	while (sent < n) {
453
454		/*
455		 * wait for available room in the send queue(s)
456		 */
457		if (poll(fds, 1, 2000) <= 0) {
458			D("poll error/timeout on queue %d\n", targ->me);
459			goto quit;
460		}
461		/*
462		 * scan our queues and send on those with room
463		 */
464		if (sent > 100000)
465			fill_all = 0;
466		for (i = targ->qfirst; i < targ->qlast; i++) {
467			int m, limit = MIN(n - sent, targ->g->burst);
468
469			txring = NETMAP_TXRING(nifp, i);
470			if (txring->avail == 0)
471				continue;
472			m = send_packets(txring, &targ->pkt, targ->g->pkt_size,
473					 limit, fill_all);
474			sent += m;
475			targ->count = sent;
476		}
477	}
478	/* Tell the interface that we have new packets. */
479	ioctl(fds[0].fd, NIOCTXSYNC, NULL);
480
481	/* final part: wait all the TX queues to be empty. */
482	for (i = targ->qfirst; i < targ->qlast; i++) {
483		txring = NETMAP_TXRING(nifp, i);
484		while (!NETMAP_TX_RING_EMPTY(txring)) {
485			ioctl(fds[0].fd, NIOCTXSYNC, NULL);
486			usleep(1); /* wait 1 tick */
487		}
488	}
489    }
490
491	gettimeofday(&targ->toc, NULL);
492	targ->completed = 1;
493	targ->count = sent;
494
495quit:
496	/* reset the ``used`` flag. */
497	targ->used = 0;
498
499	return (NULL);
500}
501
502
503static void
504receive_pcap(u_char *user, __unused const struct pcap_pkthdr * h,
505	__unused const u_char * bytes)
506{
507	int *count = (int *)user;
508	(*count)++;
509}
510
511static int
512receive_packets(struct netmap_ring *ring, u_int limit, int skip_payload)
513{
514	u_int cur, rx;
515
516	cur = ring->cur;
517	if (ring->avail < limit)
518		limit = ring->avail;
519	for (rx = 0; rx < limit; rx++) {
520		struct netmap_slot *slot = &ring->slot[cur];
521		char *p = NETMAP_BUF(ring, slot->buf_idx);
522
523		if (!skip_payload)
524			check_payload(p, slot->len);
525
526		cur = NETMAP_RING_NEXT(ring, cur);
527	}
528	ring->avail -= rx;
529	ring->cur = cur;
530
531	return (rx);
532}
533
534static void *
535receiver_body(void *data)
536{
537	struct targ *targ = (struct targ *) data;
538	struct pollfd fds[1];
539	struct netmap_if *nifp = targ->nifp;
540	struct netmap_ring *rxring;
541	int i, received = 0;
542
543	if (setaffinity(targ->thread, targ->affinity))
544		goto quit;
545
546	/* setup poll(2) machanism. */
547	memset(fds, 0, sizeof(fds));
548	fds[0].fd = targ->fd;
549	fds[0].events = (POLLIN);
550
551	/* unbounded wait for the first packet. */
552	for (;;) {
553		i = poll(fds, 1, 1000);
554		if (i > 0 && !(fds[0].revents & POLLERR))
555			break;
556		D("waiting for initial packets, poll returns %d %d", i, fds[0].revents);
557	}
558
559	/* main loop, exit after 1s silence */
560	gettimeofday(&targ->tic, NULL);
561    if (targ->g->use_pcap) {
562	for (;;) {
563		pcap_dispatch(targ->g->p, targ->g->burst, receive_pcap, NULL);
564	}
565    } else {
566	while (1) {
567		/* Once we started to receive packets, wait at most 1 seconds
568		   before quitting. */
569		if (poll(fds, 1, 1 * 1000) <= 0) {
570			gettimeofday(&targ->toc, NULL);
571			targ->toc.tv_sec -= 1; /* Substract timeout time. */
572			break;
573		}
574
575		for (i = targ->qfirst; i < targ->qlast; i++) {
576			int m;
577
578			rxring = NETMAP_RXRING(nifp, i);
579			if (rxring->avail == 0)
580				continue;
581
582			m = receive_packets(rxring, targ->g->burst,
583					SKIP_PAYLOAD);
584			received += m;
585			targ->count = received;
586		}
587
588		// tell the card we have read the data
589		//ioctl(fds[0].fd, NIOCRXSYNC, NULL);
590	}
591    }
592
593	targ->completed = 1;
594	targ->count = received;
595
596quit:
597	/* reset the ``used`` flag. */
598	targ->used = 0;
599
600	return (NULL);
601}
602
603static void
604tx_output(uint64_t sent, int size, double delta)
605{
606	double amount = 8.0 * (1.0 * size * sent) / delta;
607	double pps = sent / delta;
608	char units[4] = { '\0', 'K', 'M', 'G' };
609	int aunit = 0, punit = 0;
610
611	while (amount >= 1000) {
612		amount /= 1000;
613		aunit += 1;
614	}
615	while (pps >= 1000) {
616		pps /= 1000;
617		punit += 1;
618	}
619
620	printf("Sent %" PRIu64 " packets, %d bytes each, in %.2f seconds.\n",
621	       sent, size, delta);
622	printf("Speed: %.2f%cpps. Bandwidth: %.2f%cbps.\n",
623	       pps, units[punit], amount, units[aunit]);
624}
625
626
627static void
628rx_output(uint64_t received, double delta)
629{
630
631	double pps = received / delta;
632	char units[4] = { '\0', 'K', 'M', 'G' };
633	int punit = 0;
634
635	while (pps >= 1000) {
636		pps /= 1000;
637		punit += 1;
638	}
639
640	printf("Received %" PRIu64 " packets, in %.2f seconds.\n", received, delta);
641	printf("Speed: %.2f%cpps.\n", pps, units[punit]);
642}
643
644static void
645usage(void)
646{
647	const char *cmd = "pkt-gen";
648	fprintf(stderr,
649		"Usage:\n"
650		"%s arguments\n"
651		"\t-i interface		interface name\n"
652		"\t-t pkts_to_send	also forces send mode\n"
653		"\t-r pkts_to_receive	also forces receive mode\n"
654		"\t-l pkts_size		in bytes excluding CRC\n"
655		"\t-d dst-ip		end with %%n to sweep n addresses\n"
656		"\t-s src-ip		end with %%n to sweep n addresses\n"
657		"\t-D dst-mac		end with %%n to sweep n addresses\n"
658		"\t-S src-mac		end with %%n to sweep n addresses\n"
659		"\t-b burst size		testing, mostly\n"
660		"\t-c cores		cores to use\n"
661		"\t-p threads		processes/threads to use\n"
662		"\t-T report_ms		milliseconds between reports\n"
663		"\t-w wait_for_link_time	in seconds\n"
664		"",
665		cmd);
666
667	exit(0);
668}
669
670
671int
672main(int arc, char **argv)
673{
674	int i, fd;
675
676	struct glob_arg g;
677
678	struct nmreq nmr;
679	void *mmap_addr;		/* the mmap address */
680	void *(*td_body)(void *) = receiver_body;
681	int ch;
682	int report_interval = 1000;	/* report interval */
683	char *ifname = NULL;
684	int wait_link = 2;
685	int devqueues = 1;	/* how many device queues */
686
687	bzero(&g, sizeof(g));
688
689	g.src_ip = "10.0.0.1";
690	g.dst_ip = "10.1.0.1";
691	g.dst_mac = "ff:ff:ff:ff:ff:ff";
692	g.src_mac = NULL;
693	g.pkt_size = 60;
694	g.burst = 512;		// default
695	g.nthreads = 1;
696	g.cpus = 1;
697
698	while ( (ch = getopt(arc, argv,
699			"i:t:r:l:d:s:D:S:b:c:p:T:w:v")) != -1) {
700		switch(ch) {
701		default:
702			D("bad option %c %s", ch, optarg);
703			usage();
704			break;
705		case 'i':	/* interface */
706			ifname = optarg;
707			break;
708		case 't':	/* send */
709			td_body = sender_body;
710			g.npackets = atoi(optarg);
711			break;
712		case 'r':	/* receive */
713			td_body = receiver_body;
714			g.npackets = atoi(optarg);
715			break;
716		case 'l':	/* pkt_size */
717			g.pkt_size = atoi(optarg);
718			break;
719		case 'd':
720			g.dst_ip = optarg;
721			break;
722		case 's':
723			g.src_ip = optarg;
724			break;
725		case 'T':	/* report interval */
726			report_interval = atoi(optarg);
727			break;
728		case 'w':
729			wait_link = atoi(optarg);
730			break;
731		case 'b':	/* burst */
732			g.burst = atoi(optarg);
733			break;
734		case 'c':
735			g.cpus = atoi(optarg);
736			break;
737		case 'p':
738			g.nthreads = atoi(optarg);
739			break;
740
741		case 'P':
742			g.use_pcap = 1;
743			break;
744
745		case 'D': /* destination mac */
746			g.dst_mac = optarg;
747	{
748		struct ether_addr *mac = ether_aton(g.dst_mac);
749		D("ether_aton(%s) gives %p", g.dst_mac, mac);
750	}
751			break;
752		case 'S': /* source mac */
753			g.src_mac = optarg;
754			break;
755		case 'v':
756			verbose++;
757		}
758	}
759
760	if (ifname == NULL) {
761		D("missing ifname");
762		usage();
763	}
764	{
765		int n = system_ncpus();
766		if (g.cpus < 0 || g.cpus > n) {
767			D("%d cpus is too high, have only %d cpus", g.cpus, n);
768			usage();
769		}
770		if (g.cpus == 0)
771			g.cpus = n;
772	}
773	if (g.pkt_size < 16 || g.pkt_size > 1536) {
774		D("bad pktsize %d\n", g.pkt_size);
775		usage();
776	}
777
778	bzero(&nmr, sizeof(nmr));
779	/*
780	 * Open the netmap device to fetch the number of queues of our
781	 * interface.
782	 *
783	 * The first NIOCREGIF also detaches the card from the
784	 * protocol stack and may cause a reset of the card,
785	 * which in turn may take some time for the PHY to
786	 * reconfigure.
787	 */
788	fd = open("/dev/netmap", O_RDWR);
789	if (fd == -1) {
790		D("Unable to open /dev/netmap");
791		// fail later
792	} else {
793		if ((ioctl(fd, NIOCGINFO, &nmr)) == -1) {
794			D("Unable to get if info without name");
795		} else {
796			D("map size is %d Kb", nmr.nr_memsize >> 10);
797		}
798		bzero(&nmr, sizeof(nmr));
799		strncpy(nmr.nr_name, ifname, sizeof(nmr.nr_name));
800		if ((ioctl(fd, NIOCGINFO, &nmr)) == -1) {
801			D("Unable to get if info for %s", ifname);
802		}
803		devqueues = nmr.nr_numrings;
804	}
805
806	/* validate provided nthreads. */
807	if (g.nthreads < 1 || g.nthreads > devqueues) {
808		D("bad nthreads %d, have %d queues", g.nthreads, devqueues);
809		// continue, fail later
810	}
811
812	if (td_body == sender_body && g.src_mac == NULL) {
813		static char mybuf[20] = "ff:ff:ff:ff:ff:ff";
814		/* retrieve source mac address. */
815		if (source_hwaddr(ifname, mybuf) == -1) {
816			D("Unable to retrieve source mac");
817			// continue, fail later
818		}
819		g.src_mac = mybuf;
820	}
821
822	/*
823	 * Map the netmap shared memory: instead of issuing mmap()
824	 * inside the body of the threads, we prefer to keep this
825	 * operation here to simplify the thread logic.
826	 */
827	D("mmapping %d Kbytes", nmr.nr_memsize>>10);
828	mmap_addr = (struct netmap_d *) mmap(0, nmr.nr_memsize,
829					    PROT_WRITE | PROT_READ,
830					    MAP_SHARED, fd, 0);
831	if (mmap_addr == MAP_FAILED) {
832		D("Unable to mmap %d KB", nmr.nr_memsize >> 10);
833		// continue, fail later
834	}
835
836	/*
837	 * Register the interface on the netmap device: from now on,
838	 * we can operate on the network interface without any
839	 * interference from the legacy network stack.
840	 *
841	 * We decide to put the first interface registration here to
842	 * give time to cards that take a long time to reset the PHY.
843	 */
844	if (ioctl(fd, NIOCREGIF, &nmr) == -1) {
845		D("Unable to register interface %s", ifname);
846		//continue, fail later
847	}
848
849
850	/* Print some debug information. */
851	fprintf(stdout,
852		"%s %s: %d queues, %d threads and %d cpus.\n",
853		(td_body == sender_body) ? "Sending on" : "Receiving from",
854		ifname,
855		devqueues,
856		g.nthreads,
857		g.cpus);
858	if (td_body == sender_body) {
859		fprintf(stdout, "%s -> %s (%s -> %s)\n",
860			g.src_ip, g.dst_ip,
861			g.src_mac, g.dst_mac);
862	}
863
864	/* Exit if something went wrong. */
865	if (fd < 0) {
866		D("aborting");
867		usage();
868	}
869
870
871	/* Wait for PHY reset. */
872	D("Wait %d secs for phy reset", wait_link);
873	sleep(wait_link);
874	D("Ready...");
875
876	/* Install ^C handler. */
877	global_nthreads = g.nthreads;
878	signal(SIGINT, sigint_h);
879
880	if (g.use_pcap) {
881		// XXX g.p = pcap_open_live(..);
882	}
883
884	targs = calloc(g.nthreads, sizeof(*targs));
885	/*
886	 * Now create the desired number of threads, each one
887	 * using a single descriptor.
888 	 */
889	for (i = 0; i < g.nthreads; i++) {
890		struct netmap_if *tnifp;
891		struct nmreq tifreq;
892		int tfd;
893
894	    if (g.use_pcap) {
895		tfd = -1;
896		tnifp = NULL;
897	    } else {
898		/* register interface. */
899		tfd = open("/dev/netmap", O_RDWR);
900		if (tfd == -1) {
901			D("Unable to open /dev/netmap");
902			continue;
903		}
904
905		bzero(&tifreq, sizeof(tifreq));
906		strncpy(tifreq.nr_name, ifname, sizeof(tifreq.nr_name));
907		tifreq.nr_ringid = (g.nthreads > 1) ? (i | NETMAP_HW_RING) : 0;
908
909		/*
910		 * if we are acting as a receiver only, do not touch the transmit ring.
911		 * This is not the default because many apps may use the interface
912		 * in both directions, but a pure receiver does not.
913		 */
914		if (td_body == receiver_body) {
915			tifreq.nr_ringid |= NETMAP_NO_TX_POLL;
916		}
917
918		if ((ioctl(tfd, NIOCREGIF, &tifreq)) == -1) {
919			D("Unable to register %s", ifname);
920			continue;
921		}
922		tnifp = NETMAP_IF(mmap_addr, tifreq.nr_offset);
923	    }
924		/* start threads. */
925		bzero(&targs[i], sizeof(targs[i]));
926		targs[i].g = &g;
927		targs[i].used = 1;
928		targs[i].completed = 0;
929		targs[i].fd = tfd;
930		targs[i].nmr = tifreq;
931		targs[i].nifp = tnifp;
932		targs[i].qfirst = (g.nthreads > 1) ? i : 0;
933		targs[i].qlast = (g.nthreads > 1) ? i+1 : tifreq.nr_numrings;
934		targs[i].me = i;
935		targs[i].affinity = g.cpus ? i % g.cpus : -1;
936		if (td_body == sender_body) {
937			/* initialize the packet to send. */
938			initialize_packet(&targs[i]);
939		}
940
941		if (pthread_create(&targs[i].thread, NULL, td_body,
942				   &targs[i]) == -1) {
943			D("Unable to create thread %d", i);
944			targs[i].used = 0;
945		}
946	}
947
948    {
949	uint64_t my_count = 0, prev = 0;
950	uint64_t count = 0;
951	double delta_t;
952	struct timeval tic, toc;
953
954	gettimeofday(&toc, NULL);
955	for (;;) {
956		struct timeval now, delta;
957		uint64_t pps;
958		int done = 0;
959
960		delta.tv_sec = report_interval/1000;
961		delta.tv_usec = (report_interval%1000)*1000;
962		select(0, NULL, NULL, NULL, &delta);
963		gettimeofday(&now, NULL);
964		timersub(&now, &toc, &toc);
965		my_count = 0;
966		for (i = 0; i < g.nthreads; i++) {
967			my_count += targs[i].count;
968			if (targs[i].used == 0)
969				done++;
970		}
971		pps = toc.tv_sec* 1000000 + toc.tv_usec;
972		if (pps < 10000)
973			continue;
974		pps = (my_count - prev)*1000000 / pps;
975		D("%" PRIu64 " pps", pps);
976		prev = my_count;
977		toc = now;
978		if (done == g.nthreads)
979			break;
980	}
981
982	timerclear(&tic);
983	timerclear(&toc);
984	for (i = 0; i < g.nthreads; i++) {
985		/*
986		 * Join active threads, unregister interfaces and close
987		 * file descriptors.
988		 */
989		pthread_join(targs[i].thread, NULL);
990		ioctl(targs[i].fd, NIOCUNREGIF, &targs[i].nmr);
991		close(targs[i].fd);
992
993		if (targs[i].completed == 0)
994			continue;
995
996		/*
997		 * Collect threads o1utput and extract information about
998		 * how log it took to send all the packets.
999		 */
1000		count += targs[i].count;
1001		if (!timerisset(&tic) || timercmp(&targs[i].tic, &tic, <))
1002			tic = targs[i].tic;
1003		if (!timerisset(&toc) || timercmp(&targs[i].toc, &toc, >))
1004			toc = targs[i].toc;
1005	}
1006
1007	/* print output. */
1008	timersub(&toc, &tic, &toc);
1009	delta_t = toc.tv_sec + 1e-6* toc.tv_usec;
1010	if (td_body == sender_body)
1011		tx_output(count, g.pkt_size, delta_t);
1012	else
1013		rx_output(count, delta_t);
1014    }
1015
1016	ioctl(fd, NIOCUNREGIF, &nmr);
1017	munmap(mmap_addr, nmr.nr_memsize);
1018	close(fd);
1019
1020	return (0);
1021}
1022/* end of file */
1023