xref: /freebsd/tools/tools/netmap/pkt-gen.c (revision 66a698c9)
168b8534bSLuigi Rizzo /*
268b8534bSLuigi Rizzo  * Copyright (C) 2011 Matteo Landi, Luigi Rizzo. All rights reserved.
368b8534bSLuigi Rizzo  *
468b8534bSLuigi Rizzo  * Redistribution and use in source and binary forms, with or without
568b8534bSLuigi Rizzo  * modification, are permitted provided that the following conditions
668b8534bSLuigi Rizzo  * are met:
768b8534bSLuigi Rizzo  *   1. Redistributions of source code must retain the above copyright
868b8534bSLuigi Rizzo  *      notice, this list of conditions and the following disclaimer.
968b8534bSLuigi Rizzo  *   2. Redistributions in binary form must reproduce the above copyright
1068b8534bSLuigi Rizzo  *      notice, this list of conditions and the following disclaimer in the
1168b8534bSLuigi Rizzo  *    documentation and/or other materials provided with the distribution.
1268b8534bSLuigi Rizzo  *
1368b8534bSLuigi Rizzo  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1468b8534bSLuigi Rizzo  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1568b8534bSLuigi Rizzo  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1668b8534bSLuigi Rizzo  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1768b8534bSLuigi Rizzo  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1868b8534bSLuigi Rizzo  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
1968b8534bSLuigi Rizzo  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2068b8534bSLuigi Rizzo  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2168b8534bSLuigi Rizzo  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2268b8534bSLuigi Rizzo  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2368b8534bSLuigi Rizzo  * SUCH DAMAGE.
2468b8534bSLuigi Rizzo  */
2568b8534bSLuigi Rizzo 
2668b8534bSLuigi Rizzo /*
2768b8534bSLuigi Rizzo  * $FreeBSD$
2899fb123fSLuigi Rizzo  * $Id: pkt-gen.c 10967 2012-05-03 11:29:23Z luigi $
2968b8534bSLuigi Rizzo  *
3068b8534bSLuigi Rizzo  * Example program to show how to build a multithreaded packet
3168b8534bSLuigi Rizzo  * source/sink using the netmap device.
3268b8534bSLuigi Rizzo  *
3368b8534bSLuigi Rizzo  * In this example we create a programmable number of threads
3468b8534bSLuigi Rizzo  * to take care of all the queues of the interface used to
3568b8534bSLuigi Rizzo  * send or receive traffic.
3668b8534bSLuigi Rizzo  *
3768b8534bSLuigi Rizzo  */
3868b8534bSLuigi Rizzo 
3968b8534bSLuigi Rizzo const char *default_payload="netmap pkt-gen Luigi Rizzo and Matteo Landi\n"
4068b8534bSLuigi Rizzo 	"http://info.iet.unipi.it/~luigi/netmap/ ";
4168b8534bSLuigi Rizzo 
4268b8534bSLuigi Rizzo #include <errno.h>
4368b8534bSLuigi Rizzo #include <pthread.h>	/* pthread_* */
4468b8534bSLuigi Rizzo #include <pthread_np.h>	/* pthread w/ affinity */
4568b8534bSLuigi Rizzo #include <signal.h>	/* signal */
4668b8534bSLuigi Rizzo #include <stdlib.h>
4768b8534bSLuigi Rizzo #include <stdio.h>
48506cc70cSLuigi Rizzo #include <inttypes.h>	/* PRI* macros */
4968b8534bSLuigi Rizzo #include <string.h>	/* strcmp */
5068b8534bSLuigi Rizzo #include <fcntl.h>	/* open */
5168b8534bSLuigi Rizzo #include <unistd.h>	/* close */
5268b8534bSLuigi Rizzo #include <ifaddrs.h>	/* getifaddrs */
5368b8534bSLuigi Rizzo 
5468b8534bSLuigi Rizzo #include <sys/mman.h>	/* PROT_* */
5568b8534bSLuigi Rizzo #include <sys/ioctl.h>	/* ioctl */
5668b8534bSLuigi Rizzo #include <sys/poll.h>
5768b8534bSLuigi Rizzo #include <sys/socket.h>	/* sockaddr.. */
5868b8534bSLuigi Rizzo #include <arpa/inet.h>	/* ntohs */
5968b8534bSLuigi Rizzo #include <sys/param.h>
6068b8534bSLuigi Rizzo #include <sys/cpuset.h>	/* cpu_set */
6168b8534bSLuigi Rizzo #include <sys/sysctl.h>	/* sysctl */
6268b8534bSLuigi Rizzo #include <sys/time.h>	/* timersub */
6368b8534bSLuigi Rizzo 
6468b8534bSLuigi Rizzo #include <net/ethernet.h>
6568b8534bSLuigi Rizzo #include <net/if.h>	/* ifreq */
6668b8534bSLuigi Rizzo #include <net/if_dl.h>	/* LLADDR */
6768b8534bSLuigi Rizzo 
6868b8534bSLuigi Rizzo #include <netinet/in.h>
6968b8534bSLuigi Rizzo #include <netinet/ip.h>
7068b8534bSLuigi Rizzo #include <netinet/udp.h>
7168b8534bSLuigi Rizzo 
7268b8534bSLuigi Rizzo #include <net/netmap.h>
7368b8534bSLuigi Rizzo #include <net/netmap_user.h>
7468b8534bSLuigi Rizzo #include <pcap/pcap.h>
7568b8534bSLuigi Rizzo 
7668b8534bSLuigi Rizzo 
7768b8534bSLuigi Rizzo static inline int min(int a, int b) { return a < b ? a : b; }
7868b8534bSLuigi Rizzo 
7968b8534bSLuigi Rizzo /* debug support */
8068b8534bSLuigi Rizzo #define D(format, ...)				\
8168b8534bSLuigi Rizzo 	fprintf(stderr, "%s [%d] " format "\n", 	\
8268b8534bSLuigi Rizzo 	__FUNCTION__, __LINE__, ##__VA_ARGS__)
8368b8534bSLuigi Rizzo 
8468b8534bSLuigi Rizzo #ifndef EXPERIMENTAL
8568b8534bSLuigi Rizzo #define EXPERIMENTAL 0
8668b8534bSLuigi Rizzo #endif
8768b8534bSLuigi Rizzo 
8868b8534bSLuigi Rizzo int verbose = 0;
8968b8534bSLuigi Rizzo #define MAX_QUEUES 64	/* no need to limit */
9068b8534bSLuigi Rizzo 
9168b8534bSLuigi Rizzo #define SKIP_PAYLOAD 1 /* do not check payload. */
9268b8534bSLuigi Rizzo 
9399fb123fSLuigi Rizzo inline void prefetch (const void *x)
9499fb123fSLuigi Rizzo {
9599fb123fSLuigi Rizzo         __asm volatile("prefetcht0 %0" :: "m" (*(const unsigned long *)x));
9699fb123fSLuigi Rizzo }
9799fb123fSLuigi Rizzo 
988585b1b8SEd Maste // XXX only for multiples of 64 bytes, non overlapped.
9999fb123fSLuigi Rizzo static inline void
10099fb123fSLuigi Rizzo pkt_copy(void *_src, void *_dst, int l)
10199fb123fSLuigi Rizzo {
10299fb123fSLuigi Rizzo 	uint64_t *src = _src;
10399fb123fSLuigi Rizzo 	uint64_t *dst = _dst;
10499fb123fSLuigi Rizzo #define likely(x)       __builtin_expect(!!(x), 1)
10599fb123fSLuigi Rizzo #define unlikely(x)       __builtin_expect(!!(x), 0)
10699fb123fSLuigi Rizzo 	if (unlikely(l >= 1024)) {
10799fb123fSLuigi Rizzo 		bcopy(src, dst, l);
10899fb123fSLuigi Rizzo 		return;
10999fb123fSLuigi Rizzo 	}
11099fb123fSLuigi Rizzo 	for (; l > 0; l-=64) {
11199fb123fSLuigi Rizzo 		*dst++ = *src++;
11299fb123fSLuigi Rizzo 		*dst++ = *src++;
11399fb123fSLuigi Rizzo 		*dst++ = *src++;
11499fb123fSLuigi Rizzo 		*dst++ = *src++;
11599fb123fSLuigi Rizzo 		*dst++ = *src++;
11699fb123fSLuigi Rizzo 		*dst++ = *src++;
11799fb123fSLuigi Rizzo 		*dst++ = *src++;
11899fb123fSLuigi Rizzo 		*dst++ = *src++;
11999fb123fSLuigi Rizzo 	}
12099fb123fSLuigi Rizzo }
12199fb123fSLuigi Rizzo 
12299fb123fSLuigi Rizzo 
12368b8534bSLuigi Rizzo #if EXPERIMENTAL
12468b8534bSLuigi Rizzo /* Wrapper around `rdtsc' to take reliable timestamps flushing the pipeline */
12568b8534bSLuigi Rizzo #define netmap_rdtsc(t) \
12668b8534bSLuigi Rizzo 	do { \
12768b8534bSLuigi Rizzo 		u_int __regs[4];					\
12868b8534bSLuigi Rizzo 									\
12968b8534bSLuigi Rizzo 		do_cpuid(0, __regs);					\
13068b8534bSLuigi Rizzo 		(t) = rdtsc();						\
13168b8534bSLuigi Rizzo 	} while (0)
13268b8534bSLuigi Rizzo 
13368b8534bSLuigi Rizzo static __inline void
13468b8534bSLuigi Rizzo do_cpuid(u_int ax, u_int *p)
13568b8534bSLuigi Rizzo {
13668b8534bSLuigi Rizzo 	__asm __volatile("cpuid"
13768b8534bSLuigi Rizzo 			 : "=a" (p[0]), "=b" (p[1]), "=c" (p[2]), "=d" (p[3])
13868b8534bSLuigi Rizzo 			 :  "0" (ax));
13968b8534bSLuigi Rizzo }
14068b8534bSLuigi Rizzo 
14168b8534bSLuigi Rizzo static __inline uint64_t
14268b8534bSLuigi Rizzo rdtsc(void)
14368b8534bSLuigi Rizzo {
14468b8534bSLuigi Rizzo 	uint64_t rv;
14568b8534bSLuigi Rizzo 
14668b8534bSLuigi Rizzo 	__asm __volatile("rdtsc" : "=A" (rv));
14768b8534bSLuigi Rizzo 	return (rv);
14868b8534bSLuigi Rizzo }
14968b8534bSLuigi Rizzo #define MAX_SAMPLES 100000
15068b8534bSLuigi Rizzo #endif /* EXPERIMENTAL */
15168b8534bSLuigi Rizzo 
15268b8534bSLuigi Rizzo 
15368b8534bSLuigi Rizzo struct pkt {
15468b8534bSLuigi Rizzo 	struct ether_header eh;
15568b8534bSLuigi Rizzo 	struct ip ip;
15668b8534bSLuigi Rizzo 	struct udphdr udp;
1575819da83SLuigi Rizzo 	uint8_t body[2048];	// XXX hardwired
15868b8534bSLuigi Rizzo } __attribute__((__packed__));
15968b8534bSLuigi Rizzo 
16068b8534bSLuigi Rizzo /*
16168b8534bSLuigi Rizzo  * global arguments for all threads
16268b8534bSLuigi Rizzo  */
16368b8534bSLuigi Rizzo struct glob_arg {
16468b8534bSLuigi Rizzo 	const char *src_ip;
16568b8534bSLuigi Rizzo 	const char *dst_ip;
16668b8534bSLuigi Rizzo 	const char *src_mac;
16768b8534bSLuigi Rizzo 	const char *dst_mac;
16868b8534bSLuigi Rizzo 	int pkt_size;
16968b8534bSLuigi Rizzo 	int burst;
17068b8534bSLuigi Rizzo 	int npackets;	/* total packets to send */
17168b8534bSLuigi Rizzo 	int nthreads;
17268b8534bSLuigi Rizzo 	int cpus;
17399fb123fSLuigi Rizzo 	int options;	/* testing */
17499fb123fSLuigi Rizzo #define OPT_PREFETCH	1
17599fb123fSLuigi Rizzo #define OPT_ACCESS	2
17699fb123fSLuigi Rizzo #define OPT_COPY	4
17799fb123fSLuigi Rizzo #define OPT_MEMCPY	8
17868b8534bSLuigi Rizzo 	int use_pcap;
17968b8534bSLuigi Rizzo 	pcap_t *p;
18068b8534bSLuigi Rizzo };
18168b8534bSLuigi Rizzo 
18268b8534bSLuigi Rizzo struct mystat {
18368b8534bSLuigi Rizzo 	uint64_t containers[8];
18468b8534bSLuigi Rizzo };
18568b8534bSLuigi Rizzo 
18668b8534bSLuigi Rizzo /*
18768b8534bSLuigi Rizzo  * Arguments for a new thread. The same structure is used by
18868b8534bSLuigi Rizzo  * the source and the sink
18968b8534bSLuigi Rizzo  */
19068b8534bSLuigi Rizzo struct targ {
19168b8534bSLuigi Rizzo 	struct glob_arg *g;
19268b8534bSLuigi Rizzo 	int used;
19368b8534bSLuigi Rizzo 	int completed;
1943fe77e68SEd Maste 	int cancel;
19568b8534bSLuigi Rizzo 	int fd;
19668b8534bSLuigi Rizzo 	struct nmreq nmr;
19768b8534bSLuigi Rizzo 	struct netmap_if *nifp;
19868b8534bSLuigi Rizzo 	uint16_t	qfirst, qlast; /* range of queues to scan */
19968b8534bSLuigi Rizzo 	uint64_t count;
20068b8534bSLuigi Rizzo 	struct timeval tic, toc;
20168b8534bSLuigi Rizzo 	int me;
20268b8534bSLuigi Rizzo 	pthread_t thread;
20368b8534bSLuigi Rizzo 	int affinity;
20468b8534bSLuigi Rizzo 
20568b8534bSLuigi Rizzo 	uint8_t	dst_mac[6];
20668b8534bSLuigi Rizzo 	uint8_t	src_mac[6];
20768b8534bSLuigi Rizzo 	u_int dst_mac_range;
20868b8534bSLuigi Rizzo 	u_int src_mac_range;
20968b8534bSLuigi Rizzo 	uint32_t dst_ip;
21068b8534bSLuigi Rizzo 	uint32_t src_ip;
21168b8534bSLuigi Rizzo 	u_int dst_ip_range;
21268b8534bSLuigi Rizzo 	u_int src_ip_range;
21368b8534bSLuigi Rizzo 
21468b8534bSLuigi Rizzo 	struct pkt pkt;
21568b8534bSLuigi Rizzo };
21668b8534bSLuigi Rizzo 
21768b8534bSLuigi Rizzo 
21868b8534bSLuigi Rizzo static struct targ *targs;
21968b8534bSLuigi Rizzo static int global_nthreads;
22068b8534bSLuigi Rizzo 
22168b8534bSLuigi Rizzo /* control-C handler */
22268b8534bSLuigi Rizzo static void
22368b8534bSLuigi Rizzo sigint_h(__unused int sig)
22468b8534bSLuigi Rizzo {
2253fe77e68SEd Maste 	for (int i = 0; i < global_nthreads; i++)
2263fe77e68SEd Maste 		targs[i].cancel = 1;
22768b8534bSLuigi Rizzo 
22868b8534bSLuigi Rizzo 	signal(SIGINT, SIG_DFL);
22968b8534bSLuigi Rizzo }
23068b8534bSLuigi Rizzo 
23168b8534bSLuigi Rizzo 
23268b8534bSLuigi Rizzo /* sysctl wrapper to return the number of active CPUs */
23368b8534bSLuigi Rizzo static int
23468b8534bSLuigi Rizzo system_ncpus(void)
23568b8534bSLuigi Rizzo {
23668b8534bSLuigi Rizzo 	int mib[2], ncpus;
23768b8534bSLuigi Rizzo 	size_t len;
23868b8534bSLuigi Rizzo 
23968b8534bSLuigi Rizzo 	mib[0] = CTL_HW;
24068b8534bSLuigi Rizzo 	mib[1] = HW_NCPU;
24168b8534bSLuigi Rizzo 	len = sizeof(mib);
24268b8534bSLuigi Rizzo 	sysctl(mib, 2, &ncpus, &len, NULL, 0);
24368b8534bSLuigi Rizzo 
24468b8534bSLuigi Rizzo 	return (ncpus);
24568b8534bSLuigi Rizzo }
24668b8534bSLuigi Rizzo 
24768b8534bSLuigi Rizzo /*
24868b8534bSLuigi Rizzo  * locate the src mac address for our interface, put it
24968b8534bSLuigi Rizzo  * into the user-supplied buffer. return 0 if ok, -1 on error.
25068b8534bSLuigi Rizzo  */
25168b8534bSLuigi Rizzo static int
25268b8534bSLuigi Rizzo source_hwaddr(const char *ifname, char *buf)
25368b8534bSLuigi Rizzo {
25468b8534bSLuigi Rizzo 	struct ifaddrs *ifaphead, *ifap;
25568b8534bSLuigi Rizzo 	int l = sizeof(ifap->ifa_name);
25668b8534bSLuigi Rizzo 
25768b8534bSLuigi Rizzo 	if (getifaddrs(&ifaphead) != 0) {
25868b8534bSLuigi Rizzo 		D("getifaddrs %s failed", ifname);
25968b8534bSLuigi Rizzo 		return (-1);
26068b8534bSLuigi Rizzo 	}
26168b8534bSLuigi Rizzo 
26268b8534bSLuigi Rizzo 	for (ifap = ifaphead; ifap; ifap = ifap->ifa_next) {
26368b8534bSLuigi Rizzo 		struct sockaddr_dl *sdl =
26468b8534bSLuigi Rizzo 			(struct sockaddr_dl *)ifap->ifa_addr;
26568b8534bSLuigi Rizzo 		uint8_t *mac;
26668b8534bSLuigi Rizzo 
26768b8534bSLuigi Rizzo 		if (!sdl || sdl->sdl_family != AF_LINK)
26868b8534bSLuigi Rizzo 			continue;
26968b8534bSLuigi Rizzo 		if (strncmp(ifap->ifa_name, ifname, l) != 0)
27068b8534bSLuigi Rizzo 			continue;
27168b8534bSLuigi Rizzo 		mac = (uint8_t *)LLADDR(sdl);
27268b8534bSLuigi Rizzo 		sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x",
27368b8534bSLuigi Rizzo 			mac[0], mac[1], mac[2],
27468b8534bSLuigi Rizzo 			mac[3], mac[4], mac[5]);
27568b8534bSLuigi Rizzo 		if (verbose)
27668b8534bSLuigi Rizzo 			D("source hwaddr %s", buf);
27768b8534bSLuigi Rizzo 		break;
27868b8534bSLuigi Rizzo 	}
27968b8534bSLuigi Rizzo 	freeifaddrs(ifaphead);
28068b8534bSLuigi Rizzo 	return ifap ? 0 : 1;
28168b8534bSLuigi Rizzo }
28268b8534bSLuigi Rizzo 
28368b8534bSLuigi Rizzo 
28468b8534bSLuigi Rizzo /* set the thread affinity. */
28568b8534bSLuigi Rizzo static int
28668b8534bSLuigi Rizzo setaffinity(pthread_t me, int i)
28768b8534bSLuigi Rizzo {
28868b8534bSLuigi Rizzo 	cpuset_t cpumask;
28968b8534bSLuigi Rizzo 
29068b8534bSLuigi Rizzo 	if (i == -1)
29168b8534bSLuigi Rizzo 		return 0;
29268b8534bSLuigi Rizzo 
29368b8534bSLuigi Rizzo 	/* Set thread affinity affinity.*/
29468b8534bSLuigi Rizzo 	CPU_ZERO(&cpumask);
29568b8534bSLuigi Rizzo 	CPU_SET(i, &cpumask);
29668b8534bSLuigi Rizzo 
29768b8534bSLuigi Rizzo 	if (pthread_setaffinity_np(me, sizeof(cpuset_t), &cpumask) != 0) {
29868b8534bSLuigi Rizzo 		D("Unable to set affinity");
29968b8534bSLuigi Rizzo 		return 1;
30068b8534bSLuigi Rizzo 	}
30168b8534bSLuigi Rizzo 	return 0;
30268b8534bSLuigi Rizzo }
30368b8534bSLuigi Rizzo 
30468b8534bSLuigi Rizzo /* Compute the checksum of the given ip header. */
30568b8534bSLuigi Rizzo static uint16_t
30668b8534bSLuigi Rizzo checksum(const void *data, uint16_t len)
30768b8534bSLuigi Rizzo {
30868b8534bSLuigi Rizzo         const uint8_t *addr = data;
30968b8534bSLuigi Rizzo         uint32_t sum = 0;
31068b8534bSLuigi Rizzo 
31168b8534bSLuigi Rizzo         while (len > 1) {
31268b8534bSLuigi Rizzo                 sum += addr[0] * 256 + addr[1];
31368b8534bSLuigi Rizzo                 addr += 2;
31468b8534bSLuigi Rizzo                 len -= 2;
31568b8534bSLuigi Rizzo         }
31668b8534bSLuigi Rizzo 
31768b8534bSLuigi Rizzo         if (len == 1)
31868b8534bSLuigi Rizzo                 sum += *addr * 256;
31968b8534bSLuigi Rizzo 
32068b8534bSLuigi Rizzo         sum = (sum >> 16) + (sum & 0xffff);
32168b8534bSLuigi Rizzo         sum += (sum >> 16);
32268b8534bSLuigi Rizzo 
32368b8534bSLuigi Rizzo         sum = htons(sum);
32468b8534bSLuigi Rizzo 
32568b8534bSLuigi Rizzo         return ~sum;
32668b8534bSLuigi Rizzo }
32768b8534bSLuigi Rizzo 
32868b8534bSLuigi Rizzo /*
32968b8534bSLuigi Rizzo  * Fill a packet with some payload.
33068b8534bSLuigi Rizzo  */
33168b8534bSLuigi Rizzo static void
33268b8534bSLuigi Rizzo initialize_packet(struct targ *targ)
33368b8534bSLuigi Rizzo {
33468b8534bSLuigi Rizzo 	struct pkt *pkt = &targ->pkt;
33568b8534bSLuigi Rizzo 	struct ether_header *eh;
33668b8534bSLuigi Rizzo 	struct ip *ip;
33768b8534bSLuigi Rizzo 	struct udphdr *udp;
33868b8534bSLuigi Rizzo 	uint16_t paylen = targ->g->pkt_size - sizeof(*eh) - sizeof(*ip);
33968b8534bSLuigi Rizzo 	int i, l, l0 = strlen(default_payload);
34068b8534bSLuigi Rizzo 	char *p;
34168b8534bSLuigi Rizzo 
34268b8534bSLuigi Rizzo 	for (i = 0; i < paylen;) {
34368b8534bSLuigi Rizzo 		l = min(l0, paylen - i);
34468b8534bSLuigi Rizzo 		bcopy(default_payload, pkt->body + i, l);
34568b8534bSLuigi Rizzo 		i += l;
34668b8534bSLuigi Rizzo 	}
34768b8534bSLuigi Rizzo 	pkt->body[i-1] = '\0';
34868b8534bSLuigi Rizzo 
34968b8534bSLuigi Rizzo 	udp = &pkt->udp;
35068b8534bSLuigi Rizzo 	udp->uh_sport = htons(1234);
35168b8534bSLuigi Rizzo         udp->uh_dport = htons(4321);
35268b8534bSLuigi Rizzo 	udp->uh_ulen = htons(paylen);
35368b8534bSLuigi Rizzo 	udp->uh_sum = 0; // checksum(udp, sizeof(*udp));
35468b8534bSLuigi Rizzo 
35568b8534bSLuigi Rizzo 	ip = &pkt->ip;
35668b8534bSLuigi Rizzo         ip->ip_v = IPVERSION;
35768b8534bSLuigi Rizzo         ip->ip_hl = 5;
35868b8534bSLuigi Rizzo         ip->ip_id = 0;
35968b8534bSLuigi Rizzo         ip->ip_tos = IPTOS_LOWDELAY;
36068b8534bSLuigi Rizzo 	ip->ip_len = ntohs(targ->g->pkt_size - sizeof(*eh));
36168b8534bSLuigi Rizzo         ip->ip_id = 0;
36268b8534bSLuigi Rizzo         ip->ip_off = htons(IP_DF); /* Don't fragment */
36368b8534bSLuigi Rizzo         ip->ip_ttl = IPDEFTTL;
36468b8534bSLuigi Rizzo 	ip->ip_p = IPPROTO_UDP;
36568b8534bSLuigi Rizzo 	inet_aton(targ->g->src_ip, (struct in_addr *)&ip->ip_src);
36668b8534bSLuigi Rizzo 	inet_aton(targ->g->dst_ip, (struct in_addr *)&ip->ip_dst);
36768b8534bSLuigi Rizzo 	targ->dst_ip = ip->ip_dst.s_addr;
36868b8534bSLuigi Rizzo 	targ->src_ip = ip->ip_src.s_addr;
36968b8534bSLuigi Rizzo 	p = index(targ->g->src_ip, '-');
37068b8534bSLuigi Rizzo 	if (p) {
37168b8534bSLuigi Rizzo 		targ->dst_ip_range = atoi(p+1);
37268b8534bSLuigi Rizzo 		D("dst-ip sweep %d addresses", targ->dst_ip_range);
37368b8534bSLuigi Rizzo 	}
37468b8534bSLuigi Rizzo 	ip->ip_sum = checksum(ip, sizeof(*ip));
37568b8534bSLuigi Rizzo 
37668b8534bSLuigi Rizzo 	eh = &pkt->eh;
37768b8534bSLuigi Rizzo 	bcopy(ether_aton(targ->g->src_mac), targ->src_mac, 6);
37868b8534bSLuigi Rizzo 	bcopy(targ->src_mac, eh->ether_shost, 6);
37968b8534bSLuigi Rizzo 	p = index(targ->g->src_mac, '-');
38068b8534bSLuigi Rizzo 	if (p)
38168b8534bSLuigi Rizzo 		targ->src_mac_range = atoi(p+1);
38268b8534bSLuigi Rizzo 
38368b8534bSLuigi Rizzo 	bcopy(ether_aton(targ->g->dst_mac), targ->dst_mac, 6);
38468b8534bSLuigi Rizzo 	bcopy(targ->dst_mac, eh->ether_dhost, 6);
38568b8534bSLuigi Rizzo 	p = index(targ->g->dst_mac, '-');
38668b8534bSLuigi Rizzo 	if (p)
38768b8534bSLuigi Rizzo 		targ->dst_mac_range = atoi(p+1);
38868b8534bSLuigi Rizzo 	eh->ether_type = htons(ETHERTYPE_IP);
38968b8534bSLuigi Rizzo }
39068b8534bSLuigi Rizzo 
39168b8534bSLuigi Rizzo /* Check the payload of the packet for errors (use it for debug).
39268b8534bSLuigi Rizzo  * Look for consecutive ascii representations of the size of the packet.
39368b8534bSLuigi Rizzo  */
39468b8534bSLuigi Rizzo static void
39568b8534bSLuigi Rizzo check_payload(char *p, int psize)
39668b8534bSLuigi Rizzo {
39768b8534bSLuigi Rizzo 	char temp[64];
39868b8534bSLuigi Rizzo 	int n_read, size, sizelen;
39968b8534bSLuigi Rizzo 
40068b8534bSLuigi Rizzo 	/* get the length in ASCII of the length of the packet. */
40168b8534bSLuigi Rizzo 	sizelen = sprintf(temp, "%d", psize) + 1; // include a whitespace
40268b8534bSLuigi Rizzo 
40368b8534bSLuigi Rizzo 	/* dummy payload. */
40468b8534bSLuigi Rizzo 	p += 14; /* skip packet header. */
40568b8534bSLuigi Rizzo 	n_read = 14;
40668b8534bSLuigi Rizzo 	while (psize - n_read >= sizelen) {
40768b8534bSLuigi Rizzo 		sscanf(p, "%d", &size);
40868b8534bSLuigi Rizzo 		if (size != psize) {
40968b8534bSLuigi Rizzo 			D("Read %d instead of %d", size, psize);
41068b8534bSLuigi Rizzo 			break;
41168b8534bSLuigi Rizzo 		}
41268b8534bSLuigi Rizzo 
41368b8534bSLuigi Rizzo 		p += sizelen;
41468b8534bSLuigi Rizzo 		n_read += sizelen;
41568b8534bSLuigi Rizzo 	}
41668b8534bSLuigi Rizzo }
41768b8534bSLuigi Rizzo 
41868b8534bSLuigi Rizzo 
41968b8534bSLuigi Rizzo /*
42068b8534bSLuigi Rizzo  * create and enqueue a batch of packets on a ring.
42168b8534bSLuigi Rizzo  * On the last one set NS_REPORT to tell the driver to generate
42268b8534bSLuigi Rizzo  * an interrupt when done.
42368b8534bSLuigi Rizzo  */
42468b8534bSLuigi Rizzo static int
42568b8534bSLuigi Rizzo send_packets(struct netmap_ring *ring, struct pkt *pkt,
42699fb123fSLuigi Rizzo 		int size, u_int count, int options)
42768b8534bSLuigi Rizzo {
42868b8534bSLuigi Rizzo 	u_int sent, cur = ring->cur;
42968b8534bSLuigi Rizzo 
43068b8534bSLuigi Rizzo 	if (ring->avail < count)
43168b8534bSLuigi Rizzo 		count = ring->avail;
43268b8534bSLuigi Rizzo 
43399fb123fSLuigi Rizzo #if 0
43499fb123fSLuigi Rizzo 	if (options & (OPT_COPY | OPT_PREFETCH) ) {
43568b8534bSLuigi Rizzo 		for (sent = 0; sent < count; sent++) {
43668b8534bSLuigi Rizzo 			struct netmap_slot *slot = &ring->slot[cur];
43768b8534bSLuigi Rizzo 			char *p = NETMAP_BUF(ring, slot->buf_idx);
43868b8534bSLuigi Rizzo 
43999fb123fSLuigi Rizzo 			prefetch(p);
44099fb123fSLuigi Rizzo 			cur = NETMAP_RING_NEXT(ring, cur);
44199fb123fSLuigi Rizzo 		}
44299fb123fSLuigi Rizzo 		cur = ring->cur;
44399fb123fSLuigi Rizzo 	}
44499fb123fSLuigi Rizzo #endif
44599fb123fSLuigi Rizzo 	for (sent = 0; sent < count; sent++) {
44699fb123fSLuigi Rizzo 		struct netmap_slot *slot = &ring->slot[cur];
44799fb123fSLuigi Rizzo 		char *p = NETMAP_BUF(ring, slot->buf_idx);
44899fb123fSLuigi Rizzo 
44999fb123fSLuigi Rizzo 		if (options & OPT_COPY)
45099fb123fSLuigi Rizzo 			pkt_copy(pkt, p, size);
45199fb123fSLuigi Rizzo 		else if (options & OPT_MEMCPY)
45268b8534bSLuigi Rizzo 			memcpy(p, pkt, size);
45399fb123fSLuigi Rizzo 		else if (options & OPT_PREFETCH)
45499fb123fSLuigi Rizzo 			prefetch(p);
45568b8534bSLuigi Rizzo 
45668b8534bSLuigi Rizzo 		slot->len = size;
45768b8534bSLuigi Rizzo 		if (sent == count - 1)
45868b8534bSLuigi Rizzo 			slot->flags |= NS_REPORT;
45968b8534bSLuigi Rizzo 		cur = NETMAP_RING_NEXT(ring, cur);
46068b8534bSLuigi Rizzo 	}
46168b8534bSLuigi Rizzo 	ring->avail -= sent;
46268b8534bSLuigi Rizzo 	ring->cur = cur;
46368b8534bSLuigi Rizzo 
46468b8534bSLuigi Rizzo 	return (sent);
46568b8534bSLuigi Rizzo }
46668b8534bSLuigi Rizzo 
46768b8534bSLuigi Rizzo static void *
46868b8534bSLuigi Rizzo sender_body(void *data)
46968b8534bSLuigi Rizzo {
47068b8534bSLuigi Rizzo 	struct targ *targ = (struct targ *) data;
47168b8534bSLuigi Rizzo 
47268b8534bSLuigi Rizzo 	struct pollfd fds[1];
47368b8534bSLuigi Rizzo 	struct netmap_if *nifp = targ->nifp;
47468b8534bSLuigi Rizzo 	struct netmap_ring *txring;
47568b8534bSLuigi Rizzo 	int i, n = targ->g->npackets / targ->g->nthreads, sent = 0;
47699fb123fSLuigi Rizzo 	int options = targ->g->options | OPT_COPY;
47799fb123fSLuigi Rizzo D("start");
47868b8534bSLuigi Rizzo 	if (setaffinity(targ->thread, targ->affinity))
47968b8534bSLuigi Rizzo 		goto quit;
4808ce070c1SUlrich Spörlein 	/* setup poll(2) mechanism. */
48168b8534bSLuigi Rizzo 	memset(fds, 0, sizeof(fds));
48268b8534bSLuigi Rizzo 	fds[0].fd = targ->fd;
48368b8534bSLuigi Rizzo 	fds[0].events = (POLLOUT);
48468b8534bSLuigi Rizzo 
48568b8534bSLuigi Rizzo 	/* main loop.*/
48668b8534bSLuigi Rizzo 	gettimeofday(&targ->tic, NULL);
48768b8534bSLuigi Rizzo     if (targ->g->use_pcap) {
48868b8534bSLuigi Rizzo 	int size = targ->g->pkt_size;
48968b8534bSLuigi Rizzo 	void *pkt = &targ->pkt;
49068b8534bSLuigi Rizzo 	pcap_t *p = targ->g->p;
49168b8534bSLuigi Rizzo 
4923fe77e68SEd Maste 	for (i = 0; sent < n && !targ->cancel; i++) {
49399fb123fSLuigi Rizzo 		if (pcap_inject(p, pkt, size) != -1)
49499fb123fSLuigi Rizzo 			sent++;
49599fb123fSLuigi Rizzo 		if (i > 10000) {
49699fb123fSLuigi Rizzo 			targ->count = sent;
49799fb123fSLuigi Rizzo 			i = 0;
49899fb123fSLuigi Rizzo 		}
49968b8534bSLuigi Rizzo 	}
50068b8534bSLuigi Rizzo     } else {
50168b8534bSLuigi Rizzo 	while (sent < n) {
50268b8534bSLuigi Rizzo 
50368b8534bSLuigi Rizzo 		/*
50468b8534bSLuigi Rizzo 		 * wait for available room in the send queue(s)
50568b8534bSLuigi Rizzo 		 */
50668b8534bSLuigi Rizzo 		if (poll(fds, 1, 2000) <= 0) {
5073fe77e68SEd Maste 			if (targ->cancel)
5083fe77e68SEd Maste 				break;
50968b8534bSLuigi Rizzo 			D("poll error/timeout on queue %d\n", targ->me);
51068b8534bSLuigi Rizzo 			goto quit;
51168b8534bSLuigi Rizzo 		}
51268b8534bSLuigi Rizzo 		/*
51368b8534bSLuigi Rizzo 		 * scan our queues and send on those with room
51468b8534bSLuigi Rizzo 		 */
51599fb123fSLuigi Rizzo 		if (sent > 100000 && !(targ->g->options & OPT_COPY) )
51699fb123fSLuigi Rizzo 			options &= ~OPT_COPY;
5173fe77e68SEd Maste 		for (i = targ->qfirst; i < targ->qlast && !targ->cancel; i++) {
51868b8534bSLuigi Rizzo 			int m, limit = MIN(n - sent, targ->g->burst);
51968b8534bSLuigi Rizzo 
52068b8534bSLuigi Rizzo 			txring = NETMAP_TXRING(nifp, i);
52168b8534bSLuigi Rizzo 			if (txring->avail == 0)
52268b8534bSLuigi Rizzo 				continue;
52368b8534bSLuigi Rizzo 			m = send_packets(txring, &targ->pkt, targ->g->pkt_size,
52499fb123fSLuigi Rizzo 					 limit, options);
52568b8534bSLuigi Rizzo 			sent += m;
52668b8534bSLuigi Rizzo 			targ->count = sent;
52768b8534bSLuigi Rizzo 		}
5283fe77e68SEd Maste 		if (targ->cancel)
5293fe77e68SEd Maste 			break;
53068b8534bSLuigi Rizzo 	}
53199fb123fSLuigi Rizzo 	/* flush any remaining packets */
53268b8534bSLuigi Rizzo 	ioctl(fds[0].fd, NIOCTXSYNC, NULL);
53368b8534bSLuigi Rizzo 
53468b8534bSLuigi Rizzo 	/* final part: wait all the TX queues to be empty. */
53568b8534bSLuigi Rizzo 	for (i = targ->qfirst; i < targ->qlast; i++) {
53668b8534bSLuigi Rizzo 		txring = NETMAP_TXRING(nifp, i);
53768b8534bSLuigi Rizzo 		while (!NETMAP_TX_RING_EMPTY(txring)) {
53868b8534bSLuigi Rizzo 			ioctl(fds[0].fd, NIOCTXSYNC, NULL);
53968b8534bSLuigi Rizzo 			usleep(1); /* wait 1 tick */
54068b8534bSLuigi Rizzo 		}
54168b8534bSLuigi Rizzo 	}
54268b8534bSLuigi Rizzo     }
54368b8534bSLuigi Rizzo 
54468b8534bSLuigi Rizzo 	gettimeofday(&targ->toc, NULL);
54568b8534bSLuigi Rizzo 	targ->completed = 1;
54668b8534bSLuigi Rizzo 	targ->count = sent;
54768b8534bSLuigi Rizzo 
54868b8534bSLuigi Rizzo quit:
54968b8534bSLuigi Rizzo 	/* reset the ``used`` flag. */
55068b8534bSLuigi Rizzo 	targ->used = 0;
55168b8534bSLuigi Rizzo 
55268b8534bSLuigi Rizzo 	return (NULL);
55368b8534bSLuigi Rizzo }
55468b8534bSLuigi Rizzo 
55568b8534bSLuigi Rizzo 
55668b8534bSLuigi Rizzo static void
55768b8534bSLuigi Rizzo receive_pcap(u_char *user, __unused const struct pcap_pkthdr * h,
55868b8534bSLuigi Rizzo 	__unused const u_char * bytes)
55968b8534bSLuigi Rizzo {
56068b8534bSLuigi Rizzo 	int *count = (int *)user;
56168b8534bSLuigi Rizzo 	(*count)++;
56268b8534bSLuigi Rizzo }
56368b8534bSLuigi Rizzo 
56468b8534bSLuigi Rizzo static int
56568b8534bSLuigi Rizzo receive_packets(struct netmap_ring *ring, u_int limit, int skip_payload)
56668b8534bSLuigi Rizzo {
56768b8534bSLuigi Rizzo 	u_int cur, rx;
56868b8534bSLuigi Rizzo 
56968b8534bSLuigi Rizzo 	cur = ring->cur;
57068b8534bSLuigi Rizzo 	if (ring->avail < limit)
57168b8534bSLuigi Rizzo 		limit = ring->avail;
57268b8534bSLuigi Rizzo 	for (rx = 0; rx < limit; rx++) {
57368b8534bSLuigi Rizzo 		struct netmap_slot *slot = &ring->slot[cur];
57468b8534bSLuigi Rizzo 		char *p = NETMAP_BUF(ring, slot->buf_idx);
57568b8534bSLuigi Rizzo 
57668b8534bSLuigi Rizzo 		if (!skip_payload)
57768b8534bSLuigi Rizzo 			check_payload(p, slot->len);
57868b8534bSLuigi Rizzo 
57968b8534bSLuigi Rizzo 		cur = NETMAP_RING_NEXT(ring, cur);
58068b8534bSLuigi Rizzo 	}
58168b8534bSLuigi Rizzo 	ring->avail -= rx;
58268b8534bSLuigi Rizzo 	ring->cur = cur;
58368b8534bSLuigi Rizzo 
58468b8534bSLuigi Rizzo 	return (rx);
58568b8534bSLuigi Rizzo }
58668b8534bSLuigi Rizzo 
58768b8534bSLuigi Rizzo static void *
58868b8534bSLuigi Rizzo receiver_body(void *data)
58968b8534bSLuigi Rizzo {
59068b8534bSLuigi Rizzo 	struct targ *targ = (struct targ *) data;
59168b8534bSLuigi Rizzo 	struct pollfd fds[1];
59268b8534bSLuigi Rizzo 	struct netmap_if *nifp = targ->nifp;
59368b8534bSLuigi Rizzo 	struct netmap_ring *rxring;
59468b8534bSLuigi Rizzo 	int i, received = 0;
59568b8534bSLuigi Rizzo 
59668b8534bSLuigi Rizzo 	if (setaffinity(targ->thread, targ->affinity))
59768b8534bSLuigi Rizzo 		goto quit;
59868b8534bSLuigi Rizzo 
5998ce070c1SUlrich Spörlein 	/* setup poll(2) mechanism. */
60068b8534bSLuigi Rizzo 	memset(fds, 0, sizeof(fds));
60168b8534bSLuigi Rizzo 	fds[0].fd = targ->fd;
60268b8534bSLuigi Rizzo 	fds[0].events = (POLLIN);
60368b8534bSLuigi Rizzo 
60468b8534bSLuigi Rizzo 	/* unbounded wait for the first packet. */
60568b8534bSLuigi Rizzo 	for (;;) {
60668b8534bSLuigi Rizzo 		i = poll(fds, 1, 1000);
60768b8534bSLuigi Rizzo 		if (i > 0 && !(fds[0].revents & POLLERR))
60868b8534bSLuigi Rizzo 			break;
60968b8534bSLuigi Rizzo 		D("waiting for initial packets, poll returns %d %d", i, fds[0].revents);
61068b8534bSLuigi Rizzo 	}
61168b8534bSLuigi Rizzo 
61268b8534bSLuigi Rizzo 	/* main loop, exit after 1s silence */
61368b8534bSLuigi Rizzo 	gettimeofday(&targ->tic, NULL);
61468b8534bSLuigi Rizzo     if (targ->g->use_pcap) {
6153fe77e68SEd Maste 	while (!targ->cancel) {
61668b8534bSLuigi Rizzo 		pcap_dispatch(targ->g->p, targ->g->burst, receive_pcap, NULL);
61768b8534bSLuigi Rizzo 	}
61868b8534bSLuigi Rizzo     } else {
6193fe77e68SEd Maste 	while (!targ->cancel) {
62068b8534bSLuigi Rizzo 		/* Once we started to receive packets, wait at most 1 seconds
62168b8534bSLuigi Rizzo 		   before quitting. */
62268b8534bSLuigi Rizzo 		if (poll(fds, 1, 1 * 1000) <= 0) {
62368b8534bSLuigi Rizzo 			gettimeofday(&targ->toc, NULL);
6248ce070c1SUlrich Spörlein 			targ->toc.tv_sec -= 1; /* Subtract timeout time. */
62568b8534bSLuigi Rizzo 			break;
62668b8534bSLuigi Rizzo 		}
62768b8534bSLuigi Rizzo 
62868b8534bSLuigi Rizzo 		for (i = targ->qfirst; i < targ->qlast; i++) {
62968b8534bSLuigi Rizzo 			int m;
63068b8534bSLuigi Rizzo 
63168b8534bSLuigi Rizzo 			rxring = NETMAP_RXRING(nifp, i);
63268b8534bSLuigi Rizzo 			if (rxring->avail == 0)
63368b8534bSLuigi Rizzo 				continue;
63468b8534bSLuigi Rizzo 
63568b8534bSLuigi Rizzo 			m = receive_packets(rxring, targ->g->burst,
63668b8534bSLuigi Rizzo 					SKIP_PAYLOAD);
63768b8534bSLuigi Rizzo 			received += m;
63868b8534bSLuigi Rizzo 			targ->count = received;
63968b8534bSLuigi Rizzo 		}
64068b8534bSLuigi Rizzo 
64168b8534bSLuigi Rizzo 		// tell the card we have read the data
64268b8534bSLuigi Rizzo 		//ioctl(fds[0].fd, NIOCRXSYNC, NULL);
64368b8534bSLuigi Rizzo 	}
64468b8534bSLuigi Rizzo     }
64568b8534bSLuigi Rizzo 
64668b8534bSLuigi Rizzo 	targ->completed = 1;
64768b8534bSLuigi Rizzo 	targ->count = received;
64868b8534bSLuigi Rizzo 
64968b8534bSLuigi Rizzo quit:
65068b8534bSLuigi Rizzo 	/* reset the ``used`` flag. */
65168b8534bSLuigi Rizzo 	targ->used = 0;
65268b8534bSLuigi Rizzo 
65368b8534bSLuigi Rizzo 	return (NULL);
65468b8534bSLuigi Rizzo }
65568b8534bSLuigi Rizzo 
65666a698c9SEd Maste static char *
65766a698c9SEd Maste scaled_val(double val)
65866a698c9SEd Maste {
65966a698c9SEd Maste 	static char buf[64];
66066a698c9SEd Maste 	const char *units[] = {"", "K", "M", "G"};
66166a698c9SEd Maste 	int i = 0;
66266a698c9SEd Maste 
66366a698c9SEd Maste 	while (val >= 1000 && i < 3) {
66466a698c9SEd Maste 		val /= 1000;
66566a698c9SEd Maste 		i++;
66666a698c9SEd Maste 	}
66766a698c9SEd Maste 	snprintf(buf, sizeof(buf), "%.2f%s", val, units[i]);
66866a698c9SEd Maste 	return (buf);
66966a698c9SEd Maste }
67066a698c9SEd Maste 
67168b8534bSLuigi Rizzo static void
67268b8534bSLuigi Rizzo tx_output(uint64_t sent, int size, double delta)
67368b8534bSLuigi Rizzo {
67466a698c9SEd Maste 	uint64_t bytes_sent = sent * size;
67566a698c9SEd Maste 	double bw = 8.0 * bytes_sent / delta;
67668b8534bSLuigi Rizzo 	double pps = sent / delta;
67766a698c9SEd Maste 	/*
67866a698c9SEd Maste 	 * Assume Ethernet overhead of 24 bytes per packet excluding header:
67966a698c9SEd Maste 	 * FCS       4 bytes
68066a698c9SEd Maste 	 * Preamble  8 bytes
68166a698c9SEd Maste 	 * IFG      12 bytes
68266a698c9SEd Maste 	 */
68366a698c9SEd Maste 	double bw_with_overhead = 8.0 * (bytes_sent + sent * 24) / delta;
68468b8534bSLuigi Rizzo 
685506cc70cSLuigi Rizzo 	printf("Sent %" PRIu64 " packets, %d bytes each, in %.2f seconds.\n",
68668b8534bSLuigi Rizzo 	       sent, size, delta);
68766a698c9SEd Maste 	printf("Speed: %spps. ", scaled_val(pps));
68866a698c9SEd Maste 	printf("Bandwidth: %sbps ", scaled_val(bw));
68966a698c9SEd Maste 	printf("(%sbps with overhead).\n", scaled_val(bw_with_overhead));
69066a698c9SEd Maste 
69168b8534bSLuigi Rizzo }
69268b8534bSLuigi Rizzo 
69368b8534bSLuigi Rizzo 
69468b8534bSLuigi Rizzo static void
69568b8534bSLuigi Rizzo rx_output(uint64_t received, double delta)
69668b8534bSLuigi Rizzo {
69768b8534bSLuigi Rizzo 
69868b8534bSLuigi Rizzo 	double pps = received / delta;
69968b8534bSLuigi Rizzo 	char units[4] = { '\0', 'K', 'M', 'G' };
70068b8534bSLuigi Rizzo 	int punit = 0;
70168b8534bSLuigi Rizzo 
70268b8534bSLuigi Rizzo 	while (pps >= 1000) {
70368b8534bSLuigi Rizzo 		pps /= 1000;
70468b8534bSLuigi Rizzo 		punit += 1;
70568b8534bSLuigi Rizzo 	}
70668b8534bSLuigi Rizzo 
707506cc70cSLuigi Rizzo 	printf("Received %" PRIu64 " packets, in %.2f seconds.\n", received, delta);
70868b8534bSLuigi Rizzo 	printf("Speed: %.2f%cpps.\n", pps, units[punit]);
70968b8534bSLuigi Rizzo }
71068b8534bSLuigi Rizzo 
71168b8534bSLuigi Rizzo static void
71268b8534bSLuigi Rizzo usage(void)
71368b8534bSLuigi Rizzo {
71468b8534bSLuigi Rizzo 	const char *cmd = "pkt-gen";
71568b8534bSLuigi Rizzo 	fprintf(stderr,
71668b8534bSLuigi Rizzo 		"Usage:\n"
71768b8534bSLuigi Rizzo 		"%s arguments\n"
71868b8534bSLuigi Rizzo 		"\t-i interface		interface name\n"
71968b8534bSLuigi Rizzo 		"\t-t pkts_to_send	also forces send mode\n"
72068b8534bSLuigi Rizzo 		"\t-r pkts_to_receive	also forces receive mode\n"
72168b8534bSLuigi Rizzo 		"\t-l pkts_size		in bytes excluding CRC\n"
72268b8534bSLuigi Rizzo 		"\t-d dst-ip		end with %%n to sweep n addresses\n"
72368b8534bSLuigi Rizzo 		"\t-s src-ip		end with %%n to sweep n addresses\n"
72468b8534bSLuigi Rizzo 		"\t-D dst-mac		end with %%n to sweep n addresses\n"
72568b8534bSLuigi Rizzo 		"\t-S src-mac		end with %%n to sweep n addresses\n"
72668b8534bSLuigi Rizzo 		"\t-b burst size		testing, mostly\n"
72768b8534bSLuigi Rizzo 		"\t-c cores		cores to use\n"
72868b8534bSLuigi Rizzo 		"\t-p threads		processes/threads to use\n"
72968b8534bSLuigi Rizzo 		"\t-T report_ms		milliseconds between reports\n"
73068b8534bSLuigi Rizzo 		"\t-w wait_for_link_time	in seconds\n"
73168b8534bSLuigi Rizzo 		"",
73268b8534bSLuigi Rizzo 		cmd);
73368b8534bSLuigi Rizzo 
73468b8534bSLuigi Rizzo 	exit(0);
73568b8534bSLuigi Rizzo }
73668b8534bSLuigi Rizzo 
73768b8534bSLuigi Rizzo 
73868b8534bSLuigi Rizzo int
73968b8534bSLuigi Rizzo main(int arc, char **argv)
74068b8534bSLuigi Rizzo {
74168b8534bSLuigi Rizzo 	int i, fd;
74299fb123fSLuigi Rizzo 	char pcap_errbuf[PCAP_ERRBUF_SIZE];
74368b8534bSLuigi Rizzo 
74468b8534bSLuigi Rizzo 	struct glob_arg g;
74568b8534bSLuigi Rizzo 
74668b8534bSLuigi Rizzo 	struct nmreq nmr;
74768b8534bSLuigi Rizzo 	void *mmap_addr;		/* the mmap address */
74868b8534bSLuigi Rizzo 	void *(*td_body)(void *) = receiver_body;
74968b8534bSLuigi Rizzo 	int ch;
75068b8534bSLuigi Rizzo 	int report_interval = 1000;	/* report interval */
75168b8534bSLuigi Rizzo 	char *ifname = NULL;
75268b8534bSLuigi Rizzo 	int wait_link = 2;
75368b8534bSLuigi Rizzo 	int devqueues = 1;	/* how many device queues */
75468b8534bSLuigi Rizzo 
75568b8534bSLuigi Rizzo 	bzero(&g, sizeof(g));
75668b8534bSLuigi Rizzo 
75768b8534bSLuigi Rizzo 	g.src_ip = "10.0.0.1";
75868b8534bSLuigi Rizzo 	g.dst_ip = "10.1.0.1";
75968b8534bSLuigi Rizzo 	g.dst_mac = "ff:ff:ff:ff:ff:ff";
76068b8534bSLuigi Rizzo 	g.src_mac = NULL;
76168b8534bSLuigi Rizzo 	g.pkt_size = 60;
76268b8534bSLuigi Rizzo 	g.burst = 512;		// default
76368b8534bSLuigi Rizzo 	g.nthreads = 1;
76468b8534bSLuigi Rizzo 	g.cpus = 1;
76568b8534bSLuigi Rizzo 
76668b8534bSLuigi Rizzo 	while ( (ch = getopt(arc, argv,
76799fb123fSLuigi Rizzo 			"i:t:r:l:d:s:D:S:b:c:o:p:PT:w:v")) != -1) {
76868b8534bSLuigi Rizzo 		switch(ch) {
76968b8534bSLuigi Rizzo 		default:
77068b8534bSLuigi Rizzo 			D("bad option %c %s", ch, optarg);
77168b8534bSLuigi Rizzo 			usage();
77268b8534bSLuigi Rizzo 			break;
77399fb123fSLuigi Rizzo 		case 'o':
77499fb123fSLuigi Rizzo 			g.options = atoi(optarg);
77599fb123fSLuigi Rizzo 			break;
77668b8534bSLuigi Rizzo 		case 'i':	/* interface */
77768b8534bSLuigi Rizzo 			ifname = optarg;
77868b8534bSLuigi Rizzo 			break;
77968b8534bSLuigi Rizzo 		case 't':	/* send */
78068b8534bSLuigi Rizzo 			td_body = sender_body;
78168b8534bSLuigi Rizzo 			g.npackets = atoi(optarg);
78268b8534bSLuigi Rizzo 			break;
78368b8534bSLuigi Rizzo 		case 'r':	/* receive */
78468b8534bSLuigi Rizzo 			td_body = receiver_body;
78568b8534bSLuigi Rizzo 			g.npackets = atoi(optarg);
78668b8534bSLuigi Rizzo 			break;
78768b8534bSLuigi Rizzo 		case 'l':	/* pkt_size */
78868b8534bSLuigi Rizzo 			g.pkt_size = atoi(optarg);
78968b8534bSLuigi Rizzo 			break;
79068b8534bSLuigi Rizzo 		case 'd':
79168b8534bSLuigi Rizzo 			g.dst_ip = optarg;
79268b8534bSLuigi Rizzo 			break;
79368b8534bSLuigi Rizzo 		case 's':
79468b8534bSLuigi Rizzo 			g.src_ip = optarg;
79568b8534bSLuigi Rizzo 			break;
79668b8534bSLuigi Rizzo 		case 'T':	/* report interval */
79768b8534bSLuigi Rizzo 			report_interval = atoi(optarg);
79868b8534bSLuigi Rizzo 			break;
79968b8534bSLuigi Rizzo 		case 'w':
80068b8534bSLuigi Rizzo 			wait_link = atoi(optarg);
80168b8534bSLuigi Rizzo 			break;
80268b8534bSLuigi Rizzo 		case 'b':	/* burst */
80368b8534bSLuigi Rizzo 			g.burst = atoi(optarg);
80468b8534bSLuigi Rizzo 			break;
80568b8534bSLuigi Rizzo 		case 'c':
80668b8534bSLuigi Rizzo 			g.cpus = atoi(optarg);
80768b8534bSLuigi Rizzo 			break;
80868b8534bSLuigi Rizzo 		case 'p':
80968b8534bSLuigi Rizzo 			g.nthreads = atoi(optarg);
81068b8534bSLuigi Rizzo 			break;
81168b8534bSLuigi Rizzo 
81268b8534bSLuigi Rizzo 		case 'P':
81368b8534bSLuigi Rizzo 			g.use_pcap = 1;
81468b8534bSLuigi Rizzo 			break;
81568b8534bSLuigi Rizzo 
81668b8534bSLuigi Rizzo 		case 'D': /* destination mac */
81768b8534bSLuigi Rizzo 			g.dst_mac = optarg;
81868b8534bSLuigi Rizzo 	{
81968b8534bSLuigi Rizzo 		struct ether_addr *mac = ether_aton(g.dst_mac);
82068b8534bSLuigi Rizzo 		D("ether_aton(%s) gives %p", g.dst_mac, mac);
82168b8534bSLuigi Rizzo 	}
82268b8534bSLuigi Rizzo 			break;
82368b8534bSLuigi Rizzo 		case 'S': /* source mac */
82468b8534bSLuigi Rizzo 			g.src_mac = optarg;
82568b8534bSLuigi Rizzo 			break;
82668b8534bSLuigi Rizzo 		case 'v':
82768b8534bSLuigi Rizzo 			verbose++;
82868b8534bSLuigi Rizzo 		}
82968b8534bSLuigi Rizzo 	}
83068b8534bSLuigi Rizzo 
83168b8534bSLuigi Rizzo 	if (ifname == NULL) {
83268b8534bSLuigi Rizzo 		D("missing ifname");
83368b8534bSLuigi Rizzo 		usage();
83468b8534bSLuigi Rizzo 	}
83568b8534bSLuigi Rizzo 	{
83668b8534bSLuigi Rizzo 		int n = system_ncpus();
83768b8534bSLuigi Rizzo 		if (g.cpus < 0 || g.cpus > n) {
83868b8534bSLuigi Rizzo 			D("%d cpus is too high, have only %d cpus", g.cpus, n);
83968b8534bSLuigi Rizzo 			usage();
84068b8534bSLuigi Rizzo 		}
84168b8534bSLuigi Rizzo 		if (g.cpus == 0)
84268b8534bSLuigi Rizzo 			g.cpus = n;
84368b8534bSLuigi Rizzo 	}
84468b8534bSLuigi Rizzo 	if (g.pkt_size < 16 || g.pkt_size > 1536) {
84568b8534bSLuigi Rizzo 		D("bad pktsize %d\n", g.pkt_size);
84668b8534bSLuigi Rizzo 		usage();
84768b8534bSLuigi Rizzo 	}
84868b8534bSLuigi Rizzo 
84999fb123fSLuigi Rizzo 	if (td_body == sender_body && g.src_mac == NULL) {
85099fb123fSLuigi Rizzo 		static char mybuf[20] = "ff:ff:ff:ff:ff:ff";
85199fb123fSLuigi Rizzo 		/* retrieve source mac address. */
85299fb123fSLuigi Rizzo 		if (source_hwaddr(ifname, mybuf) == -1) {
85399fb123fSLuigi Rizzo 			D("Unable to retrieve source mac");
85499fb123fSLuigi Rizzo 			// continue, fail later
85599fb123fSLuigi Rizzo 		}
85699fb123fSLuigi Rizzo 		g.src_mac = mybuf;
85799fb123fSLuigi Rizzo 	}
85899fb123fSLuigi Rizzo 
85999fb123fSLuigi Rizzo     if (g.use_pcap) {
86099fb123fSLuigi Rizzo 	D("using pcap on %s", ifname);
86199fb123fSLuigi Rizzo 	g.p = pcap_open_live(ifname, 0, 1, 100, pcap_errbuf);
86299fb123fSLuigi Rizzo 	if (g.p == NULL) {
86399fb123fSLuigi Rizzo 		D("cannot open pcap on %s", ifname);
86499fb123fSLuigi Rizzo 		usage();
86599fb123fSLuigi Rizzo 	}
86699fb123fSLuigi Rizzo 	mmap_addr = NULL;
86799fb123fSLuigi Rizzo 	fd = -1;
86899fb123fSLuigi Rizzo     } else {
86968b8534bSLuigi Rizzo 	bzero(&nmr, sizeof(nmr));
87064ae02c3SLuigi Rizzo 	nmr.nr_version = NETMAP_API;
87168b8534bSLuigi Rizzo 	/*
87268b8534bSLuigi Rizzo 	 * Open the netmap device to fetch the number of queues of our
87368b8534bSLuigi Rizzo 	 * interface.
87468b8534bSLuigi Rizzo 	 *
87568b8534bSLuigi Rizzo 	 * The first NIOCREGIF also detaches the card from the
87668b8534bSLuigi Rizzo 	 * protocol stack and may cause a reset of the card,
87768b8534bSLuigi Rizzo 	 * which in turn may take some time for the PHY to
87868b8534bSLuigi Rizzo 	 * reconfigure.
87968b8534bSLuigi Rizzo 	 */
88068b8534bSLuigi Rizzo 	fd = open("/dev/netmap", O_RDWR);
88168b8534bSLuigi Rizzo 	if (fd == -1) {
88268b8534bSLuigi Rizzo 		D("Unable to open /dev/netmap");
88368b8534bSLuigi Rizzo 		// fail later
88468b8534bSLuigi Rizzo 	} else {
88568b8534bSLuigi Rizzo 		if ((ioctl(fd, NIOCGINFO, &nmr)) == -1) {
88668b8534bSLuigi Rizzo 			D("Unable to get if info without name");
88768b8534bSLuigi Rizzo 		} else {
88868b8534bSLuigi Rizzo 			D("map size is %d Kb", nmr.nr_memsize >> 10);
88968b8534bSLuigi Rizzo 		}
89068b8534bSLuigi Rizzo 		bzero(&nmr, sizeof(nmr));
89164ae02c3SLuigi Rizzo 		nmr.nr_version = NETMAP_API;
89268b8534bSLuigi Rizzo 		strncpy(nmr.nr_name, ifname, sizeof(nmr.nr_name));
89368b8534bSLuigi Rizzo 		if ((ioctl(fd, NIOCGINFO, &nmr)) == -1) {
89468b8534bSLuigi Rizzo 			D("Unable to get if info for %s", ifname);
89568b8534bSLuigi Rizzo 		}
89664ae02c3SLuigi Rizzo 		devqueues = nmr.nr_rx_rings;
89768b8534bSLuigi Rizzo 	}
89868b8534bSLuigi Rizzo 
89968b8534bSLuigi Rizzo 	/* validate provided nthreads. */
90068b8534bSLuigi Rizzo 	if (g.nthreads < 1 || g.nthreads > devqueues) {
90168b8534bSLuigi Rizzo 		D("bad nthreads %d, have %d queues", g.nthreads, devqueues);
90268b8534bSLuigi Rizzo 		// continue, fail later
90368b8534bSLuigi Rizzo 	}
90468b8534bSLuigi Rizzo 
90568b8534bSLuigi Rizzo 	/*
90668b8534bSLuigi Rizzo 	 * Map the netmap shared memory: instead of issuing mmap()
90768b8534bSLuigi Rizzo 	 * inside the body of the threads, we prefer to keep this
90868b8534bSLuigi Rizzo 	 * operation here to simplify the thread logic.
90968b8534bSLuigi Rizzo 	 */
91068b8534bSLuigi Rizzo 	D("mmapping %d Kbytes", nmr.nr_memsize>>10);
91168b8534bSLuigi Rizzo 	mmap_addr = (struct netmap_d *) mmap(0, nmr.nr_memsize,
91268b8534bSLuigi Rizzo 					    PROT_WRITE | PROT_READ,
91368b8534bSLuigi Rizzo 					    MAP_SHARED, fd, 0);
91468b8534bSLuigi Rizzo 	if (mmap_addr == MAP_FAILED) {
91568b8534bSLuigi Rizzo 		D("Unable to mmap %d KB", nmr.nr_memsize >> 10);
91668b8534bSLuigi Rizzo 		// continue, fail later
91768b8534bSLuigi Rizzo 	}
91868b8534bSLuigi Rizzo 
91968b8534bSLuigi Rizzo 	/*
92068b8534bSLuigi Rizzo 	 * Register the interface on the netmap device: from now on,
92168b8534bSLuigi Rizzo 	 * we can operate on the network interface without any
92268b8534bSLuigi Rizzo 	 * interference from the legacy network stack.
92368b8534bSLuigi Rizzo 	 *
92468b8534bSLuigi Rizzo 	 * We decide to put the first interface registration here to
92568b8534bSLuigi Rizzo 	 * give time to cards that take a long time to reset the PHY.
92668b8534bSLuigi Rizzo 	 */
92764ae02c3SLuigi Rizzo 	nmr.nr_version = NETMAP_API;
92868b8534bSLuigi Rizzo 	if (ioctl(fd, NIOCREGIF, &nmr) == -1) {
92968b8534bSLuigi Rizzo 		D("Unable to register interface %s", ifname);
93068b8534bSLuigi Rizzo 		//continue, fail later
93168b8534bSLuigi Rizzo 	}
93268b8534bSLuigi Rizzo 
93368b8534bSLuigi Rizzo 
93468b8534bSLuigi Rizzo 	/* Print some debug information. */
93568b8534bSLuigi Rizzo 	fprintf(stdout,
93668b8534bSLuigi Rizzo 		"%s %s: %d queues, %d threads and %d cpus.\n",
93768b8534bSLuigi Rizzo 		(td_body == sender_body) ? "Sending on" : "Receiving from",
93868b8534bSLuigi Rizzo 		ifname,
93968b8534bSLuigi Rizzo 		devqueues,
94068b8534bSLuigi Rizzo 		g.nthreads,
94168b8534bSLuigi Rizzo 		g.cpus);
94268b8534bSLuigi Rizzo 	if (td_body == sender_body) {
94368b8534bSLuigi Rizzo 		fprintf(stdout, "%s -> %s (%s -> %s)\n",
94468b8534bSLuigi Rizzo 			g.src_ip, g.dst_ip,
94568b8534bSLuigi Rizzo 			g.src_mac, g.dst_mac);
94668b8534bSLuigi Rizzo 	}
94768b8534bSLuigi Rizzo 
94868b8534bSLuigi Rizzo 	/* Exit if something went wrong. */
94968b8534bSLuigi Rizzo 	if (fd < 0) {
95068b8534bSLuigi Rizzo 		D("aborting");
95168b8534bSLuigi Rizzo 		usage();
95268b8534bSLuigi Rizzo 	}
95399fb123fSLuigi Rizzo     }
95468b8534bSLuigi Rizzo 
95599fb123fSLuigi Rizzo 	if (g.options) {
95699fb123fSLuigi Rizzo 		D("special options:%s%s%s%s\n",
95799fb123fSLuigi Rizzo 			g.options & OPT_PREFETCH ? " prefetch" : "",
95899fb123fSLuigi Rizzo 			g.options & OPT_ACCESS ? " access" : "",
95999fb123fSLuigi Rizzo 			g.options & OPT_MEMCPY ? " memcpy" : "",
96099fb123fSLuigi Rizzo 			g.options & OPT_COPY ? " copy" : "");
96199fb123fSLuigi Rizzo 	}
96268b8534bSLuigi Rizzo 	/* Wait for PHY reset. */
96368b8534bSLuigi Rizzo 	D("Wait %d secs for phy reset", wait_link);
96468b8534bSLuigi Rizzo 	sleep(wait_link);
96568b8534bSLuigi Rizzo 	D("Ready...");
96668b8534bSLuigi Rizzo 
96768b8534bSLuigi Rizzo 	/* Install ^C handler. */
96868b8534bSLuigi Rizzo 	global_nthreads = g.nthreads;
96968b8534bSLuigi Rizzo 	signal(SIGINT, sigint_h);
97068b8534bSLuigi Rizzo 
97168b8534bSLuigi Rizzo 	if (g.use_pcap) {
97299fb123fSLuigi Rizzo 		g.p = pcap_open_live(ifname, 0, 1, 100, NULL);
97399fb123fSLuigi Rizzo 		if (g.p == NULL) {
97499fb123fSLuigi Rizzo 			D("cannot open pcap on %s", ifname);
97599fb123fSLuigi Rizzo 			usage();
97699fb123fSLuigi Rizzo 		} else
97799fb123fSLuigi Rizzo 			D("using pcap %p on %s", g.p, ifname);
97868b8534bSLuigi Rizzo 	}
97968b8534bSLuigi Rizzo 
98068b8534bSLuigi Rizzo 	targs = calloc(g.nthreads, sizeof(*targs));
98168b8534bSLuigi Rizzo 	/*
98268b8534bSLuigi Rizzo 	 * Now create the desired number of threads, each one
98368b8534bSLuigi Rizzo 	 * using a single descriptor.
98468b8534bSLuigi Rizzo  	 */
98568b8534bSLuigi Rizzo 	for (i = 0; i < g.nthreads; i++) {
98668b8534bSLuigi Rizzo 		struct netmap_if *tnifp;
98768b8534bSLuigi Rizzo 		struct nmreq tifreq;
98868b8534bSLuigi Rizzo 		int tfd;
98968b8534bSLuigi Rizzo 
99068b8534bSLuigi Rizzo 	    if (g.use_pcap) {
99168b8534bSLuigi Rizzo 		tfd = -1;
99268b8534bSLuigi Rizzo 		tnifp = NULL;
99368b8534bSLuigi Rizzo 	    } else {
99468b8534bSLuigi Rizzo 		/* register interface. */
99568b8534bSLuigi Rizzo 		tfd = open("/dev/netmap", O_RDWR);
99668b8534bSLuigi Rizzo 		if (tfd == -1) {
99768b8534bSLuigi Rizzo 			D("Unable to open /dev/netmap");
99868b8534bSLuigi Rizzo 			continue;
99968b8534bSLuigi Rizzo 		}
100068b8534bSLuigi Rizzo 
100168b8534bSLuigi Rizzo 		bzero(&tifreq, sizeof(tifreq));
100268b8534bSLuigi Rizzo 		strncpy(tifreq.nr_name, ifname, sizeof(tifreq.nr_name));
100364ae02c3SLuigi Rizzo 		tifreq.nr_version = NETMAP_API;
100468b8534bSLuigi Rizzo 		tifreq.nr_ringid = (g.nthreads > 1) ? (i | NETMAP_HW_RING) : 0;
100568b8534bSLuigi Rizzo 
100668b8534bSLuigi Rizzo 		/*
100768b8534bSLuigi Rizzo 		 * if we are acting as a receiver only, do not touch the transmit ring.
100868b8534bSLuigi Rizzo 		 * This is not the default because many apps may use the interface
100968b8534bSLuigi Rizzo 		 * in both directions, but a pure receiver does not.
101068b8534bSLuigi Rizzo 		 */
101168b8534bSLuigi Rizzo 		if (td_body == receiver_body) {
101268b8534bSLuigi Rizzo 			tifreq.nr_ringid |= NETMAP_NO_TX_POLL;
101368b8534bSLuigi Rizzo 		}
101468b8534bSLuigi Rizzo 
101568b8534bSLuigi Rizzo 		if ((ioctl(tfd, NIOCREGIF, &tifreq)) == -1) {
101668b8534bSLuigi Rizzo 			D("Unable to register %s", ifname);
101768b8534bSLuigi Rizzo 			continue;
101868b8534bSLuigi Rizzo 		}
101968b8534bSLuigi Rizzo 		tnifp = NETMAP_IF(mmap_addr, tifreq.nr_offset);
102068b8534bSLuigi Rizzo 	    }
102168b8534bSLuigi Rizzo 		/* start threads. */
102268b8534bSLuigi Rizzo 		bzero(&targs[i], sizeof(targs[i]));
102368b8534bSLuigi Rizzo 		targs[i].g = &g;
102468b8534bSLuigi Rizzo 		targs[i].used = 1;
102568b8534bSLuigi Rizzo 		targs[i].completed = 0;
102668b8534bSLuigi Rizzo 		targs[i].fd = tfd;
102768b8534bSLuigi Rizzo 		targs[i].nmr = tifreq;
102868b8534bSLuigi Rizzo 		targs[i].nifp = tnifp;
102968b8534bSLuigi Rizzo 		targs[i].qfirst = (g.nthreads > 1) ? i : 0;
103064ae02c3SLuigi Rizzo 		targs[i].qlast = (g.nthreads > 1) ? i+1 :
103164ae02c3SLuigi Rizzo 			(td_body == receiver_body ? tifreq.nr_rx_rings : tifreq.nr_tx_rings);
103268b8534bSLuigi Rizzo 		targs[i].me = i;
103368b8534bSLuigi Rizzo 		targs[i].affinity = g.cpus ? i % g.cpus : -1;
103468b8534bSLuigi Rizzo 		if (td_body == sender_body) {
103568b8534bSLuigi Rizzo 			/* initialize the packet to send. */
103668b8534bSLuigi Rizzo 			initialize_packet(&targs[i]);
103768b8534bSLuigi Rizzo 		}
103868b8534bSLuigi Rizzo 
103968b8534bSLuigi Rizzo 		if (pthread_create(&targs[i].thread, NULL, td_body,
104068b8534bSLuigi Rizzo 				   &targs[i]) == -1) {
104168b8534bSLuigi Rizzo 			D("Unable to create thread %d", i);
104268b8534bSLuigi Rizzo 			targs[i].used = 0;
104368b8534bSLuigi Rizzo 		}
104468b8534bSLuigi Rizzo 	}
104568b8534bSLuigi Rizzo 
104668b8534bSLuigi Rizzo     {
104768b8534bSLuigi Rizzo 	uint64_t my_count = 0, prev = 0;
104868b8534bSLuigi Rizzo 	uint64_t count = 0;
104968b8534bSLuigi Rizzo 	double delta_t;
105068b8534bSLuigi Rizzo 	struct timeval tic, toc;
105168b8534bSLuigi Rizzo 
105268b8534bSLuigi Rizzo 	gettimeofday(&toc, NULL);
105368b8534bSLuigi Rizzo 	for (;;) {
105468b8534bSLuigi Rizzo 		struct timeval now, delta;
105568b8534bSLuigi Rizzo 		uint64_t pps;
105668b8534bSLuigi Rizzo 		int done = 0;
105768b8534bSLuigi Rizzo 
105868b8534bSLuigi Rizzo 		delta.tv_sec = report_interval/1000;
105968b8534bSLuigi Rizzo 		delta.tv_usec = (report_interval%1000)*1000;
106068b8534bSLuigi Rizzo 		select(0, NULL, NULL, NULL, &delta);
106168b8534bSLuigi Rizzo 		gettimeofday(&now, NULL);
106268b8534bSLuigi Rizzo 		timersub(&now, &toc, &toc);
106368b8534bSLuigi Rizzo 		my_count = 0;
106468b8534bSLuigi Rizzo 		for (i = 0; i < g.nthreads; i++) {
106568b8534bSLuigi Rizzo 			my_count += targs[i].count;
106668b8534bSLuigi Rizzo 			if (targs[i].used == 0)
106768b8534bSLuigi Rizzo 				done++;
106868b8534bSLuigi Rizzo 		}
106968b8534bSLuigi Rizzo 		pps = toc.tv_sec* 1000000 + toc.tv_usec;
107068b8534bSLuigi Rizzo 		if (pps < 10000)
107168b8534bSLuigi Rizzo 			continue;
107268b8534bSLuigi Rizzo 		pps = (my_count - prev)*1000000 / pps;
1073506cc70cSLuigi Rizzo 		D("%" PRIu64 " pps", pps);
107468b8534bSLuigi Rizzo 		prev = my_count;
107568b8534bSLuigi Rizzo 		toc = now;
107668b8534bSLuigi Rizzo 		if (done == g.nthreads)
107768b8534bSLuigi Rizzo 			break;
107868b8534bSLuigi Rizzo 	}
107968b8534bSLuigi Rizzo 
108068b8534bSLuigi Rizzo 	timerclear(&tic);
108168b8534bSLuigi Rizzo 	timerclear(&toc);
108268b8534bSLuigi Rizzo 	for (i = 0; i < g.nthreads; i++) {
108368b8534bSLuigi Rizzo 		/*
108468b8534bSLuigi Rizzo 		 * Join active threads, unregister interfaces and close
108568b8534bSLuigi Rizzo 		 * file descriptors.
108668b8534bSLuigi Rizzo 		 */
108768b8534bSLuigi Rizzo 		pthread_join(targs[i].thread, NULL);
108868b8534bSLuigi Rizzo 		ioctl(targs[i].fd, NIOCUNREGIF, &targs[i].nmr);
108968b8534bSLuigi Rizzo 		close(targs[i].fd);
109068b8534bSLuigi Rizzo 
109168b8534bSLuigi Rizzo 		if (targs[i].completed == 0)
109268b8534bSLuigi Rizzo 			continue;
109368b8534bSLuigi Rizzo 
109468b8534bSLuigi Rizzo 		/*
10958ce070c1SUlrich Spörlein 		 * Collect threads output and extract information about
10968ce070c1SUlrich Spörlein 		 * how long it took to send all the packets.
109768b8534bSLuigi Rizzo 		 */
109868b8534bSLuigi Rizzo 		count += targs[i].count;
109968b8534bSLuigi Rizzo 		if (!timerisset(&tic) || timercmp(&targs[i].tic, &tic, <))
110068b8534bSLuigi Rizzo 			tic = targs[i].tic;
110168b8534bSLuigi Rizzo 		if (!timerisset(&toc) || timercmp(&targs[i].toc, &toc, >))
110268b8534bSLuigi Rizzo 			toc = targs[i].toc;
110368b8534bSLuigi Rizzo 	}
110468b8534bSLuigi Rizzo 
110568b8534bSLuigi Rizzo 	/* print output. */
110668b8534bSLuigi Rizzo 	timersub(&toc, &tic, &toc);
110768b8534bSLuigi Rizzo 	delta_t = toc.tv_sec + 1e-6* toc.tv_usec;
110868b8534bSLuigi Rizzo 	if (td_body == sender_body)
110968b8534bSLuigi Rizzo 		tx_output(count, g.pkt_size, delta_t);
111068b8534bSLuigi Rizzo 	else
111168b8534bSLuigi Rizzo 		rx_output(count, delta_t);
111268b8534bSLuigi Rizzo     }
111368b8534bSLuigi Rizzo 
111499fb123fSLuigi Rizzo     if (g.use_pcap == 0) {
111568b8534bSLuigi Rizzo 	ioctl(fd, NIOCUNREGIF, &nmr);
111668b8534bSLuigi Rizzo 	munmap(mmap_addr, nmr.nr_memsize);
111768b8534bSLuigi Rizzo 	close(fd);
111899fb123fSLuigi Rizzo     }
111968b8534bSLuigi Rizzo 
112068b8534bSLuigi Rizzo 	return (0);
112168b8534bSLuigi Rizzo }
112268b8534bSLuigi Rizzo /* end of file */
1123