xref: /freebsd/tools/tools/netmap/pkt-gen.c (revision b303f675)
168b8534bSLuigi Rizzo /*
2f8e4e36aSLuigi Rizzo  * Copyright (C) 2011-2012 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$
281cb4c501SLuigi Rizzo  * $Id$
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 
39f8e4e36aSLuigi Rizzo #include "nm_util.h"
40f8e4e36aSLuigi Rizzo 
41b303f675SLuigi Rizzo #include <ctype.h>	// isprint()
42b303f675SLuigi Rizzo 
43f8e4e36aSLuigi Rizzo const char *default_payload="netmap pkt-gen payload\n"
4468b8534bSLuigi Rizzo 	"http://info.iet.unipi.it/~luigi/netmap/ ";
4568b8534bSLuigi Rizzo 
46f8e4e36aSLuigi Rizzo int time_second;	// support for RD() debugging macro
4768b8534bSLuigi Rizzo 
4868b8534bSLuigi Rizzo int verbose = 0;
4968b8534bSLuigi Rizzo 
5068b8534bSLuigi Rizzo #define SKIP_PAYLOAD 1 /* do not check payload. */
5168b8534bSLuigi Rizzo 
5268b8534bSLuigi Rizzo struct pkt {
5368b8534bSLuigi Rizzo 	struct ether_header eh;
5468b8534bSLuigi Rizzo 	struct ip ip;
5568b8534bSLuigi Rizzo 	struct udphdr udp;
565819da83SLuigi Rizzo 	uint8_t body[2048];	// XXX hardwired
5768b8534bSLuigi Rizzo } __attribute__((__packed__));
5868b8534bSLuigi Rizzo 
59f8e4e36aSLuigi Rizzo struct ip_range {
60f8e4e36aSLuigi Rizzo 	char *name;
61f8e4e36aSLuigi Rizzo 	struct in_addr start, end, cur;
62f8e4e36aSLuigi Rizzo 	uint16_t port0, port1, cur_p;
63f8e4e36aSLuigi Rizzo };
64f8e4e36aSLuigi Rizzo 
65f8e4e36aSLuigi Rizzo struct mac_range {
66f8e4e36aSLuigi Rizzo 	char *name;
67f8e4e36aSLuigi Rizzo 	struct ether_addr start, end;
68f8e4e36aSLuigi Rizzo };
69f8e4e36aSLuigi Rizzo 
7068b8534bSLuigi Rizzo /*
7168b8534bSLuigi Rizzo  * global arguments for all threads
7268b8534bSLuigi Rizzo  */
73f8e4e36aSLuigi Rizzo 
7468b8534bSLuigi Rizzo struct glob_arg {
75f8e4e36aSLuigi Rizzo 	struct ip_range src_ip;
76f8e4e36aSLuigi Rizzo 	struct ip_range dst_ip;
77f8e4e36aSLuigi Rizzo 	struct mac_range dst_mac;
78f8e4e36aSLuigi Rizzo 	struct mac_range src_mac;
7968b8534bSLuigi Rizzo 	int pkt_size;
8068b8534bSLuigi Rizzo 	int burst;
81f8e4e36aSLuigi Rizzo 	int forever;
8268b8534bSLuigi Rizzo 	int npackets;	/* total packets to send */
8368b8534bSLuigi Rizzo 	int nthreads;
8468b8534bSLuigi Rizzo 	int cpus;
8599fb123fSLuigi Rizzo 	int options;	/* testing */
8699fb123fSLuigi Rizzo #define OPT_PREFETCH	1
8799fb123fSLuigi Rizzo #define OPT_ACCESS	2
8899fb123fSLuigi Rizzo #define OPT_COPY	4
8999fb123fSLuigi Rizzo #define OPT_MEMCPY	8
90f8e4e36aSLuigi Rizzo #define OPT_TS		16	/* add a timestamp */
91b303f675SLuigi Rizzo #define OPT_INDIRECT	32	/* use indirect buffers, tx only */
92b303f675SLuigi Rizzo #define OPT_DUMP	64	/* dump rx/tx traffic */
93f8e4e36aSLuigi Rizzo 	int dev_type;
9468b8534bSLuigi Rizzo 	pcap_t *p;
9568b8534bSLuigi Rizzo 
961cb4c501SLuigi Rizzo 	int tx_rate;
971cb4c501SLuigi Rizzo 	struct timespec tx_period;
981cb4c501SLuigi Rizzo 
99f8e4e36aSLuigi Rizzo 	int affinity;
100f8e4e36aSLuigi Rizzo 	int main_fd;
101f8e4e36aSLuigi Rizzo 	int report_interval;
102f8e4e36aSLuigi Rizzo 	void *(*td_body)(void *);
103f8e4e36aSLuigi Rizzo 	void *mmap_addr;
104f8e4e36aSLuigi Rizzo 	int mmap_size;
105f8e4e36aSLuigi Rizzo 	char *ifname;
10668b8534bSLuigi Rizzo };
107f8e4e36aSLuigi Rizzo enum dev_type { DEV_NONE, DEV_NETMAP, DEV_PCAP, DEV_TAP };
108f8e4e36aSLuigi Rizzo 
10968b8534bSLuigi Rizzo 
11068b8534bSLuigi Rizzo /*
11168b8534bSLuigi Rizzo  * Arguments for a new thread. The same structure is used by
11268b8534bSLuigi Rizzo  * the source and the sink
11368b8534bSLuigi Rizzo  */
11468b8534bSLuigi Rizzo struct targ {
11568b8534bSLuigi Rizzo 	struct glob_arg *g;
11668b8534bSLuigi Rizzo 	int used;
11768b8534bSLuigi Rizzo 	int completed;
1183fe77e68SEd Maste 	int cancel;
11968b8534bSLuigi Rizzo 	int fd;
12068b8534bSLuigi Rizzo 	struct nmreq nmr;
12168b8534bSLuigi Rizzo 	struct netmap_if *nifp;
12268b8534bSLuigi Rizzo 	uint16_t	qfirst, qlast; /* range of queues to scan */
123f8e4e36aSLuigi Rizzo 	volatile uint64_t count;
1241cb4c501SLuigi Rizzo 	struct timespec tic, toc;
12568b8534bSLuigi Rizzo 	int me;
12668b8534bSLuigi Rizzo 	pthread_t thread;
12768b8534bSLuigi Rizzo 	int affinity;
12868b8534bSLuigi Rizzo 
12968b8534bSLuigi Rizzo 	struct pkt pkt;
13068b8534bSLuigi Rizzo };
13168b8534bSLuigi Rizzo 
13268b8534bSLuigi Rizzo 
133f8e4e36aSLuigi Rizzo /*
134f8e4e36aSLuigi Rizzo  * extract the extremes from a range of ipv4 addresses.
135f8e4e36aSLuigi Rizzo  * addr_lo[-addr_hi][:port_lo[-port_hi]]
136f8e4e36aSLuigi Rizzo  */
137f8e4e36aSLuigi Rizzo static void
138f8e4e36aSLuigi Rizzo extract_ip_range(struct ip_range *r)
139f8e4e36aSLuigi Rizzo {
140f8e4e36aSLuigi Rizzo 	char *p_lo, *p_hi;
141f8e4e36aSLuigi Rizzo 	char buf1[16]; // one ip address
142f8e4e36aSLuigi Rizzo 
143f8e4e36aSLuigi Rizzo 	D("extract IP range from %s", r->name);
144f8e4e36aSLuigi Rizzo 	p_lo = index(r->name, ':');	/* do we have ports ? */
145f8e4e36aSLuigi Rizzo 	if (p_lo) {
146f8e4e36aSLuigi Rizzo 		D(" found ports at %s", p_lo);
147f8e4e36aSLuigi Rizzo 		*p_lo++ = '\0';
148f8e4e36aSLuigi Rizzo 		p_hi = index(p_lo, '-');
149f8e4e36aSLuigi Rizzo 		if (p_hi)
150f8e4e36aSLuigi Rizzo 			*p_hi++ = '\0';
151f8e4e36aSLuigi Rizzo 		else
152f8e4e36aSLuigi Rizzo 			p_hi = p_lo;
153f8e4e36aSLuigi Rizzo 		r->port0 = strtol(p_lo, NULL, 0);
154f8e4e36aSLuigi Rizzo 		r->port1 = strtol(p_hi, NULL, 0);
155f8e4e36aSLuigi Rizzo 		if (r->port1 < r->port0) {
156f8e4e36aSLuigi Rizzo 			r->cur_p = r->port0;
157f8e4e36aSLuigi Rizzo 			r->port0 = r->port1;
158f8e4e36aSLuigi Rizzo 			r->port1 = r->cur_p;
159f8e4e36aSLuigi Rizzo 		}
160f8e4e36aSLuigi Rizzo 		r->cur_p = r->port0;
161f8e4e36aSLuigi Rizzo 		D("ports are %d to %d", r->port0, r->port1);
162f8e4e36aSLuigi Rizzo 	}
163f8e4e36aSLuigi Rizzo 	p_hi = index(r->name, '-');	/* do we have upper ip ? */
164f8e4e36aSLuigi Rizzo 	if (p_hi) {
165f8e4e36aSLuigi Rizzo 		*p_hi++ = '\0';
166f8e4e36aSLuigi Rizzo 	} else
167f8e4e36aSLuigi Rizzo 		p_hi = r->name;
168f8e4e36aSLuigi Rizzo 	inet_aton(r->name, &r->start);
169f8e4e36aSLuigi Rizzo 	inet_aton(p_hi, &r->end);
170f8e4e36aSLuigi Rizzo 	if (r->start.s_addr > r->end.s_addr) {
171f8e4e36aSLuigi Rizzo 		r->cur = r->start;
172f8e4e36aSLuigi Rizzo 		r->start = r->end;
173f8e4e36aSLuigi Rizzo 		r->end = r->cur;
174f8e4e36aSLuigi Rizzo 	}
175f8e4e36aSLuigi Rizzo 	r->cur = r->start;
176f8e4e36aSLuigi Rizzo 	strncpy(buf1, inet_ntoa(r->end), sizeof(buf1));
177f8e4e36aSLuigi Rizzo 	D("range is %s %d to %s %d", inet_ntoa(r->start), r->port0,
178f8e4e36aSLuigi Rizzo 		buf1, r->port1);
179f8e4e36aSLuigi Rizzo }
180f8e4e36aSLuigi Rizzo 
181f8e4e36aSLuigi Rizzo static void
182f8e4e36aSLuigi Rizzo extract_mac_range(struct mac_range *r)
183f8e4e36aSLuigi Rizzo {
184f8e4e36aSLuigi Rizzo 	D("extract MAC range from %s", r->name);
185f8e4e36aSLuigi Rizzo 	bcopy(ether_aton(r->name), &r->start, 6);
186f8e4e36aSLuigi Rizzo 	bcopy(ether_aton(r->name), &r->end, 6);
187f8e4e36aSLuigi Rizzo #if 0
188f8e4e36aSLuigi Rizzo 	bcopy(targ->src_mac, eh->ether_shost, 6);
189f8e4e36aSLuigi Rizzo 	p = index(targ->g->src_mac, '-');
190f8e4e36aSLuigi Rizzo 	if (p)
191f8e4e36aSLuigi Rizzo 		targ->src_mac_range = atoi(p+1);
192f8e4e36aSLuigi Rizzo 
193f8e4e36aSLuigi Rizzo 	bcopy(ether_aton(targ->g->dst_mac), targ->dst_mac, 6);
194f8e4e36aSLuigi Rizzo 	bcopy(targ->dst_mac, eh->ether_dhost, 6);
195f8e4e36aSLuigi Rizzo 	p = index(targ->g->dst_mac, '-');
196f8e4e36aSLuigi Rizzo 	if (p)
197f8e4e36aSLuigi Rizzo 		targ->dst_mac_range = atoi(p+1);
198f8e4e36aSLuigi Rizzo #endif
199f8e4e36aSLuigi Rizzo 	D("%s starts at %s", r->name, ether_ntoa(&r->start));
200f8e4e36aSLuigi Rizzo }
201f8e4e36aSLuigi Rizzo 
20268b8534bSLuigi Rizzo static struct targ *targs;
20368b8534bSLuigi Rizzo static int global_nthreads;
20468b8534bSLuigi Rizzo 
20568b8534bSLuigi Rizzo /* control-C handler */
20668b8534bSLuigi Rizzo static void
207f8e4e36aSLuigi Rizzo sigint_h(int sig)
20868b8534bSLuigi Rizzo {
209f8e4e36aSLuigi Rizzo 	int i;
21068b8534bSLuigi Rizzo 
211f8e4e36aSLuigi Rizzo 	(void)sig;	/* UNUSED */
212f8e4e36aSLuigi Rizzo 	for (i = 0; i < global_nthreads; i++) {
213f8e4e36aSLuigi Rizzo 		targs[i].cancel = 1;
214f8e4e36aSLuigi Rizzo 	}
21568b8534bSLuigi Rizzo 	signal(SIGINT, SIG_DFL);
21668b8534bSLuigi Rizzo }
21768b8534bSLuigi Rizzo 
21868b8534bSLuigi Rizzo /* sysctl wrapper to return the number of active CPUs */
21968b8534bSLuigi Rizzo static int
22068b8534bSLuigi Rizzo system_ncpus(void)
22168b8534bSLuigi Rizzo {
222f8e4e36aSLuigi Rizzo #ifdef __FreeBSD__
22368b8534bSLuigi Rizzo 	int mib[2], ncpus;
22468b8534bSLuigi Rizzo 	size_t len;
22568b8534bSLuigi Rizzo 
22668b8534bSLuigi Rizzo 	mib[0] = CTL_HW;
22768b8534bSLuigi Rizzo 	mib[1] = HW_NCPU;
22868b8534bSLuigi Rizzo 	len = sizeof(mib);
22968b8534bSLuigi Rizzo 	sysctl(mib, 2, &ncpus, &len, NULL, 0);
23068b8534bSLuigi Rizzo 
23168b8534bSLuigi Rizzo 	return (ncpus);
232f8e4e36aSLuigi Rizzo #else
233f8e4e36aSLuigi Rizzo 	return 1;
234f8e4e36aSLuigi Rizzo #endif /* !__FreeBSD__ */
23568b8534bSLuigi Rizzo }
23668b8534bSLuigi Rizzo 
237f8e4e36aSLuigi Rizzo #ifdef __linux__
238f8e4e36aSLuigi Rizzo #define sockaddr_dl    sockaddr_ll
239f8e4e36aSLuigi Rizzo #define sdl_family     sll_family
240f8e4e36aSLuigi Rizzo #define AF_LINK        AF_PACKET
241f8e4e36aSLuigi Rizzo #define LLADDR(s)      s->sll_addr;
242f8e4e36aSLuigi Rizzo #include <linux/if_tun.h>
243f8e4e36aSLuigi Rizzo #define TAP_CLONEDEV	"/dev/net/tun"
244f8e4e36aSLuigi Rizzo #endif /* __linux__ */
245f8e4e36aSLuigi Rizzo 
246f8e4e36aSLuigi Rizzo #ifdef __FreeBSD__
247f8e4e36aSLuigi Rizzo #include <net/if_tun.h>
248f8e4e36aSLuigi Rizzo #define TAP_CLONEDEV	"/dev/tap"
249f8e4e36aSLuigi Rizzo #endif /* __FreeBSD */
250f8e4e36aSLuigi Rizzo 
251f8e4e36aSLuigi Rizzo #ifdef __APPLE__
252f8e4e36aSLuigi Rizzo // #warning TAP not supported on apple ?
253f8e4e36aSLuigi Rizzo #include <net/if_utun.h>
254f8e4e36aSLuigi Rizzo #define TAP_CLONEDEV	"/dev/tap"
255f8e4e36aSLuigi Rizzo #endif /* __APPLE__ */
256f8e4e36aSLuigi Rizzo 
257f8e4e36aSLuigi Rizzo 
25868b8534bSLuigi Rizzo /*
25968b8534bSLuigi Rizzo  * locate the src mac address for our interface, put it
26068b8534bSLuigi Rizzo  * into the user-supplied buffer. return 0 if ok, -1 on error.
26168b8534bSLuigi Rizzo  */
26268b8534bSLuigi Rizzo static int
26368b8534bSLuigi Rizzo source_hwaddr(const char *ifname, char *buf)
26468b8534bSLuigi Rizzo {
26568b8534bSLuigi Rizzo 	struct ifaddrs *ifaphead, *ifap;
26668b8534bSLuigi Rizzo 	int l = sizeof(ifap->ifa_name);
26768b8534bSLuigi Rizzo 
26868b8534bSLuigi Rizzo 	if (getifaddrs(&ifaphead) != 0) {
26968b8534bSLuigi Rizzo 		D("getifaddrs %s failed", ifname);
27068b8534bSLuigi Rizzo 		return (-1);
27168b8534bSLuigi Rizzo 	}
27268b8534bSLuigi Rizzo 
27368b8534bSLuigi Rizzo 	for (ifap = ifaphead; ifap; ifap = ifap->ifa_next) {
27468b8534bSLuigi Rizzo 		struct sockaddr_dl *sdl =
27568b8534bSLuigi Rizzo 			(struct sockaddr_dl *)ifap->ifa_addr;
27668b8534bSLuigi Rizzo 		uint8_t *mac;
27768b8534bSLuigi Rizzo 
27868b8534bSLuigi Rizzo 		if (!sdl || sdl->sdl_family != AF_LINK)
27968b8534bSLuigi Rizzo 			continue;
28068b8534bSLuigi Rizzo 		if (strncmp(ifap->ifa_name, ifname, l) != 0)
28168b8534bSLuigi Rizzo 			continue;
28268b8534bSLuigi Rizzo 		mac = (uint8_t *)LLADDR(sdl);
28368b8534bSLuigi Rizzo 		sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x",
28468b8534bSLuigi Rizzo 			mac[0], mac[1], mac[2],
28568b8534bSLuigi Rizzo 			mac[3], mac[4], mac[5]);
28668b8534bSLuigi Rizzo 		if (verbose)
28768b8534bSLuigi Rizzo 			D("source hwaddr %s", buf);
28868b8534bSLuigi Rizzo 		break;
28968b8534bSLuigi Rizzo 	}
29068b8534bSLuigi Rizzo 	freeifaddrs(ifaphead);
29168b8534bSLuigi Rizzo 	return ifap ? 0 : 1;
29268b8534bSLuigi Rizzo }
29368b8534bSLuigi Rizzo 
29468b8534bSLuigi Rizzo 
29568b8534bSLuigi Rizzo /* set the thread affinity. */
29668b8534bSLuigi Rizzo static int
29768b8534bSLuigi Rizzo setaffinity(pthread_t me, int i)
29868b8534bSLuigi Rizzo {
299f8e4e36aSLuigi Rizzo #ifdef __FreeBSD__
30068b8534bSLuigi Rizzo 	cpuset_t cpumask;
30168b8534bSLuigi Rizzo 
30268b8534bSLuigi Rizzo 	if (i == -1)
30368b8534bSLuigi Rizzo 		return 0;
30468b8534bSLuigi Rizzo 
30568b8534bSLuigi Rizzo 	/* Set thread affinity affinity.*/
30668b8534bSLuigi Rizzo 	CPU_ZERO(&cpumask);
30768b8534bSLuigi Rizzo 	CPU_SET(i, &cpumask);
30868b8534bSLuigi Rizzo 
30968b8534bSLuigi Rizzo 	if (pthread_setaffinity_np(me, sizeof(cpuset_t), &cpumask) != 0) {
31068b8534bSLuigi Rizzo 		D("Unable to set affinity");
31168b8534bSLuigi Rizzo 		return 1;
31268b8534bSLuigi Rizzo 	}
313f8e4e36aSLuigi Rizzo #else
314f8e4e36aSLuigi Rizzo 	(void)me; /* suppress 'unused' warnings */
315f8e4e36aSLuigi Rizzo 	(void)i;
316f8e4e36aSLuigi Rizzo #endif /* __FreeBSD__ */
31768b8534bSLuigi Rizzo 	return 0;
31868b8534bSLuigi Rizzo }
31968b8534bSLuigi Rizzo 
32068b8534bSLuigi Rizzo /* Compute the checksum of the given ip header. */
32168b8534bSLuigi Rizzo static uint16_t
322f8e4e36aSLuigi Rizzo checksum(const void *data, uint16_t len, uint32_t sum)
32368b8534bSLuigi Rizzo {
32468b8534bSLuigi Rizzo         const uint8_t *addr = data;
325f8e4e36aSLuigi Rizzo 	uint32_t i;
32668b8534bSLuigi Rizzo 
327f8e4e36aSLuigi Rizzo         /* Checksum all the pairs of bytes first... */
328f8e4e36aSLuigi Rizzo         for (i = 0; i < (len & ~1U); i += 2) {
329f8e4e36aSLuigi Rizzo                 sum += (u_int16_t)ntohs(*((u_int16_t *)(addr + i)));
330f8e4e36aSLuigi Rizzo                 if (sum > 0xFFFF)
331f8e4e36aSLuigi Rizzo                         sum -= 0xFFFF;
332f8e4e36aSLuigi Rizzo         }
333f8e4e36aSLuigi Rizzo 	/*
334f8e4e36aSLuigi Rizzo 	 * If there's a single byte left over, checksum it, too.
335f8e4e36aSLuigi Rizzo 	 * Network byte order is big-endian, so the remaining byte is
336f8e4e36aSLuigi Rizzo 	 * the high byte.
337f8e4e36aSLuigi Rizzo 	 */
338f8e4e36aSLuigi Rizzo 	if (i < len) {
339f8e4e36aSLuigi Rizzo 		sum += addr[i] << 8;
340f8e4e36aSLuigi Rizzo 		if (sum > 0xFFFF)
341f8e4e36aSLuigi Rizzo 			sum -= 0xFFFF;
342f8e4e36aSLuigi Rizzo 	}
343f8e4e36aSLuigi Rizzo 	return sum;
34468b8534bSLuigi Rizzo }
34568b8534bSLuigi Rizzo 
346f8e4e36aSLuigi Rizzo static u_int16_t
347f8e4e36aSLuigi Rizzo wrapsum(u_int32_t sum)
348f8e4e36aSLuigi Rizzo {
349f8e4e36aSLuigi Rizzo 	sum = ~sum & 0xFFFF;
350f8e4e36aSLuigi Rizzo 	return (htons(sum));
35168b8534bSLuigi Rizzo }
35268b8534bSLuigi Rizzo 
353b303f675SLuigi Rizzo /* Check the payload of the packet for errors (use it for debug).
354b303f675SLuigi Rizzo  * Look for consecutive ascii representations of the size of the packet.
355b303f675SLuigi Rizzo  */
356b303f675SLuigi Rizzo static void
357b303f675SLuigi Rizzo dump_payload(char *p, int len, struct netmap_ring *ring, int cur)
358b303f675SLuigi Rizzo {
359b303f675SLuigi Rizzo 	char buf[128];
360b303f675SLuigi Rizzo 	int i, j, i0;
361b303f675SLuigi Rizzo 
362b303f675SLuigi Rizzo 	/* get the length in ASCII of the length of the packet. */
363b303f675SLuigi Rizzo 
364b303f675SLuigi Rizzo 	printf("ring %p cur %5d len %5d buf %p\n", ring, cur, len, p);
365b303f675SLuigi Rizzo 	/* hexdump routine */
366b303f675SLuigi Rizzo 	for (i = 0; i < len; ) {
367b303f675SLuigi Rizzo 		memset(buf, sizeof(buf), ' ');
368b303f675SLuigi Rizzo 		sprintf(buf, "%5d: ", i);
369b303f675SLuigi Rizzo 		i0 = i;
370b303f675SLuigi Rizzo 		for (j=0; j < 16 && i < len; i++, j++)
371b303f675SLuigi Rizzo 			sprintf(buf+7+j*3, "%02x ", (uint8_t)(p[i]));
372b303f675SLuigi Rizzo 		i = i0;
373b303f675SLuigi Rizzo 		for (j=0; j < 16 && i < len; i++, j++)
374b303f675SLuigi Rizzo 			sprintf(buf+7+j + 48, "%c",
375b303f675SLuigi Rizzo 				isprint(p[i]) ? p[i] : '.');
376b303f675SLuigi Rizzo 		printf("%s\n", buf);
377b303f675SLuigi Rizzo 	}
378b303f675SLuigi Rizzo }
379b303f675SLuigi Rizzo 
38068b8534bSLuigi Rizzo /*
38168b8534bSLuigi Rizzo  * Fill a packet with some payload.
382f8e4e36aSLuigi Rizzo  * We create a UDP packet so the payload starts at
383f8e4e36aSLuigi Rizzo  *	14+20+8 = 42 bytes.
38468b8534bSLuigi Rizzo  */
385f8e4e36aSLuigi Rizzo #ifdef __linux__
386f8e4e36aSLuigi Rizzo #define uh_sport source
387f8e4e36aSLuigi Rizzo #define uh_dport dest
388f8e4e36aSLuigi Rizzo #define uh_ulen len
389f8e4e36aSLuigi Rizzo #define uh_sum check
390f8e4e36aSLuigi Rizzo #endif /* linux */
391b303f675SLuigi Rizzo 
39268b8534bSLuigi Rizzo static void
39368b8534bSLuigi Rizzo initialize_packet(struct targ *targ)
39468b8534bSLuigi Rizzo {
39568b8534bSLuigi Rizzo 	struct pkt *pkt = &targ->pkt;
39668b8534bSLuigi Rizzo 	struct ether_header *eh;
39768b8534bSLuigi Rizzo 	struct ip *ip;
39868b8534bSLuigi Rizzo 	struct udphdr *udp;
399f8e4e36aSLuigi Rizzo 	uint16_t paylen = targ->g->pkt_size - sizeof(*eh) - sizeof(struct ip);
400b303f675SLuigi Rizzo 	const char *payload = targ->g->options & OPT_INDIRECT ?
401b303f675SLuigi Rizzo 		"XXXXXXXXXXXXXXXXXXXXXX" : default_payload;
402b303f675SLuigi Rizzo 	int i, l, l0 = strlen(payload);
40368b8534bSLuigi Rizzo 
40468b8534bSLuigi Rizzo 	for (i = 0; i < paylen;) {
40568b8534bSLuigi Rizzo 		l = min(l0, paylen - i);
406b303f675SLuigi Rizzo 		bcopy(payload, pkt->body + i, l);
40768b8534bSLuigi Rizzo 		i += l;
40868b8534bSLuigi Rizzo 	}
40968b8534bSLuigi Rizzo 	pkt->body[i-1] = '\0';
41068b8534bSLuigi Rizzo 	ip = &pkt->ip;
411f8e4e36aSLuigi Rizzo 
41268b8534bSLuigi Rizzo         ip->ip_v = IPVERSION;
41368b8534bSLuigi Rizzo         ip->ip_hl = 5;
41468b8534bSLuigi Rizzo         ip->ip_id = 0;
41568b8534bSLuigi Rizzo         ip->ip_tos = IPTOS_LOWDELAY;
41668b8534bSLuigi Rizzo 	ip->ip_len = ntohs(targ->g->pkt_size - sizeof(*eh));
41768b8534bSLuigi Rizzo         ip->ip_id = 0;
41868b8534bSLuigi Rizzo         ip->ip_off = htons(IP_DF); /* Don't fragment */
41968b8534bSLuigi Rizzo         ip->ip_ttl = IPDEFTTL;
42068b8534bSLuigi Rizzo 	ip->ip_p = IPPROTO_UDP;
421f8e4e36aSLuigi Rizzo 	ip->ip_dst.s_addr = targ->g->dst_ip.cur.s_addr;
422f8e4e36aSLuigi Rizzo 	if (++targ->g->dst_ip.cur.s_addr > targ->g->dst_ip.end.s_addr)
423f8e4e36aSLuigi Rizzo 		targ->g->dst_ip.cur.s_addr = targ->g->dst_ip.start.s_addr;
424f8e4e36aSLuigi Rizzo 	ip->ip_src.s_addr = targ->g->src_ip.cur.s_addr;
425f8e4e36aSLuigi Rizzo 	if (++targ->g->src_ip.cur.s_addr > targ->g->src_ip.end.s_addr)
426f8e4e36aSLuigi Rizzo 		targ->g->src_ip.cur.s_addr = targ->g->src_ip.start.s_addr;
427f8e4e36aSLuigi Rizzo 	ip->ip_sum = wrapsum(checksum(ip, sizeof(*ip), 0));
428f8e4e36aSLuigi Rizzo 
429f8e4e36aSLuigi Rizzo 
430f8e4e36aSLuigi Rizzo 	udp = &pkt->udp;
431f8e4e36aSLuigi Rizzo         udp->uh_sport = htons(targ->g->src_ip.cur_p);
432f8e4e36aSLuigi Rizzo 	if (++targ->g->src_ip.cur_p > targ->g->src_ip.port1)
433f8e4e36aSLuigi Rizzo 		targ->g->src_ip.cur_p = targ->g->src_ip.port0;
434f8e4e36aSLuigi Rizzo         udp->uh_dport = htons(targ->g->dst_ip.cur_p);
435f8e4e36aSLuigi Rizzo 	if (++targ->g->dst_ip.cur_p > targ->g->dst_ip.port1)
436f8e4e36aSLuigi Rizzo 		targ->g->dst_ip.cur_p = targ->g->dst_ip.port0;
437f8e4e36aSLuigi Rizzo 	udp->uh_ulen = htons(paylen);
438f8e4e36aSLuigi Rizzo 	/* Magic: taken from sbin/dhclient/packet.c */
439f8e4e36aSLuigi Rizzo 	udp->uh_sum = wrapsum(checksum(udp, sizeof(*udp),
440f8e4e36aSLuigi Rizzo                     checksum(pkt->body,
441f8e4e36aSLuigi Rizzo                         paylen - sizeof(*udp),
442f8e4e36aSLuigi Rizzo                         checksum(&ip->ip_src, 2 * sizeof(ip->ip_src),
443f8e4e36aSLuigi Rizzo                             IPPROTO_UDP + (u_int32_t)ntohs(udp->uh_ulen)
444f8e4e36aSLuigi Rizzo                         )
445f8e4e36aSLuigi Rizzo                     )
446f8e4e36aSLuigi Rizzo                 ));
44768b8534bSLuigi Rizzo 
44868b8534bSLuigi Rizzo 	eh = &pkt->eh;
449f8e4e36aSLuigi Rizzo 	bcopy(&targ->g->src_mac.start, eh->ether_shost, 6);
450f8e4e36aSLuigi Rizzo 	bcopy(&targ->g->dst_mac.start, eh->ether_dhost, 6);
45168b8534bSLuigi Rizzo 	eh->ether_type = htons(ETHERTYPE_IP);
452b303f675SLuigi Rizzo 	// dump_payload((void *)pkt, targ->g->pkt_size, NULL, 0);
45368b8534bSLuigi Rizzo }
45468b8534bSLuigi Rizzo 
45568b8534bSLuigi Rizzo 
45668b8534bSLuigi Rizzo 
45768b8534bSLuigi Rizzo /*
45868b8534bSLuigi Rizzo  * create and enqueue a batch of packets on a ring.
45968b8534bSLuigi Rizzo  * On the last one set NS_REPORT to tell the driver to generate
46068b8534bSLuigi Rizzo  * an interrupt when done.
46168b8534bSLuigi Rizzo  */
46268b8534bSLuigi Rizzo static int
46368b8534bSLuigi Rizzo send_packets(struct netmap_ring *ring, struct pkt *pkt,
46499fb123fSLuigi Rizzo 		int size, u_int count, int options)
46568b8534bSLuigi Rizzo {
46668b8534bSLuigi Rizzo 	u_int sent, cur = ring->cur;
46768b8534bSLuigi Rizzo 
46868b8534bSLuigi Rizzo 	if (ring->avail < count)
46968b8534bSLuigi Rizzo 		count = ring->avail;
47068b8534bSLuigi Rizzo 
47199fb123fSLuigi Rizzo #if 0
47299fb123fSLuigi Rizzo 	if (options & (OPT_COPY | OPT_PREFETCH) ) {
47368b8534bSLuigi Rizzo 		for (sent = 0; sent < count; sent++) {
47468b8534bSLuigi Rizzo 			struct netmap_slot *slot = &ring->slot[cur];
47568b8534bSLuigi Rizzo 			char *p = NETMAP_BUF(ring, slot->buf_idx);
47668b8534bSLuigi Rizzo 
47799fb123fSLuigi Rizzo 			prefetch(p);
47899fb123fSLuigi Rizzo 			cur = NETMAP_RING_NEXT(ring, cur);
47999fb123fSLuigi Rizzo 		}
48099fb123fSLuigi Rizzo 		cur = ring->cur;
48199fb123fSLuigi Rizzo 	}
48299fb123fSLuigi Rizzo #endif
48399fb123fSLuigi Rizzo 	for (sent = 0; sent < count; sent++) {
48499fb123fSLuigi Rizzo 		struct netmap_slot *slot = &ring->slot[cur];
48599fb123fSLuigi Rizzo 		char *p = NETMAP_BUF(ring, slot->buf_idx);
48699fb123fSLuigi Rizzo 
487b303f675SLuigi Rizzo 		slot->flags = 0;
488b303f675SLuigi Rizzo 		if (options & OPT_DUMP)
489b303f675SLuigi Rizzo 			dump_payload(p, size, ring, cur);
490b303f675SLuigi Rizzo 		if (options & OPT_INDIRECT) {
491b303f675SLuigi Rizzo 			slot->flags |= NS_INDIRECT;
492b303f675SLuigi Rizzo 			*((struct pkt **)(void *)p) = pkt;
493b303f675SLuigi Rizzo 		} else if (options & OPT_COPY)
49499fb123fSLuigi Rizzo 			pkt_copy(pkt, p, size);
49599fb123fSLuigi Rizzo 		else if (options & OPT_MEMCPY)
49668b8534bSLuigi Rizzo 			memcpy(p, pkt, size);
49799fb123fSLuigi Rizzo 		else if (options & OPT_PREFETCH)
49899fb123fSLuigi Rizzo 			prefetch(p);
49968b8534bSLuigi Rizzo 		slot->len = size;
50068b8534bSLuigi Rizzo 		if (sent == count - 1)
50168b8534bSLuigi Rizzo 			slot->flags |= NS_REPORT;
50268b8534bSLuigi Rizzo 		cur = NETMAP_RING_NEXT(ring, cur);
50368b8534bSLuigi Rizzo 	}
50468b8534bSLuigi Rizzo 	ring->avail -= sent;
50568b8534bSLuigi Rizzo 	ring->cur = cur;
50668b8534bSLuigi Rizzo 
50768b8534bSLuigi Rizzo 	return (sent);
50868b8534bSLuigi Rizzo }
50968b8534bSLuigi Rizzo 
510f8e4e36aSLuigi Rizzo /*
511f8e4e36aSLuigi Rizzo  * Send a packet, and wait for a response.
512f8e4e36aSLuigi Rizzo  * The payload (after UDP header, ofs 42) has a 4-byte sequence
513f8e4e36aSLuigi Rizzo  * followed by a struct timeval (or bintime?)
514f8e4e36aSLuigi Rizzo  */
515f8e4e36aSLuigi Rizzo #define	PAY_OFS	42	/* where in the pkt... */
516f8e4e36aSLuigi Rizzo 
51768b8534bSLuigi Rizzo static void *
518f8e4e36aSLuigi Rizzo pinger_body(void *data)
51968b8534bSLuigi Rizzo {
52068b8534bSLuigi Rizzo 	struct targ *targ = (struct targ *) data;
52168b8534bSLuigi Rizzo 	struct pollfd fds[1];
52268b8534bSLuigi Rizzo 	struct netmap_if *nifp = targ->nifp;
523f8e4e36aSLuigi Rizzo 	int i, rx = 0, n = targ->g->npackets;
524e5ecae38SEd Maste 
525f8e4e36aSLuigi Rizzo 	fds[0].fd = targ->fd;
526f8e4e36aSLuigi Rizzo 	fds[0].events = (POLLIN);
527f8e4e36aSLuigi Rizzo 	static uint32_t sent;
528f8e4e36aSLuigi Rizzo 	struct timespec ts, now, last_print;
529f8e4e36aSLuigi Rizzo 	uint32_t count = 0, min = 1000000000, av = 0;
530f8e4e36aSLuigi Rizzo 
531f8e4e36aSLuigi Rizzo 	if (targ->g->nthreads > 1) {
532f8e4e36aSLuigi Rizzo 		D("can only ping with 1 thread");
533f8e4e36aSLuigi Rizzo 		return NULL;
534f95a30bdSEd Maste 	}
535f8e4e36aSLuigi Rizzo 
536f8e4e36aSLuigi Rizzo 	clock_gettime(CLOCK_REALTIME_PRECISE, &last_print);
537f8e4e36aSLuigi Rizzo 	while (n == 0 || (int)sent < n) {
538f8e4e36aSLuigi Rizzo 		struct netmap_ring *ring = NETMAP_TXRING(nifp, 0);
539f8e4e36aSLuigi Rizzo 		struct netmap_slot *slot;
540f8e4e36aSLuigi Rizzo 		char *p;
541f8e4e36aSLuigi Rizzo 	    for (i = 0; i < 1; i++) {
542f8e4e36aSLuigi Rizzo 		slot = &ring->slot[ring->cur];
543f8e4e36aSLuigi Rizzo 		slot->len = targ->g->pkt_size;
544f8e4e36aSLuigi Rizzo 		p = NETMAP_BUF(ring, slot->buf_idx);
545f8e4e36aSLuigi Rizzo 
546f8e4e36aSLuigi Rizzo 		if (ring->avail == 0) {
547f8e4e36aSLuigi Rizzo 			D("-- ouch, cannot send");
548f8e4e36aSLuigi Rizzo 		} else {
549f8e4e36aSLuigi Rizzo 			pkt_copy(&targ->pkt, p, targ->g->pkt_size);
550f8e4e36aSLuigi Rizzo 			clock_gettime(CLOCK_REALTIME_PRECISE, &ts);
551f8e4e36aSLuigi Rizzo 			bcopy(&sent, p+42, sizeof(sent));
552f8e4e36aSLuigi Rizzo 			bcopy(&ts, p+46, sizeof(ts));
553f8e4e36aSLuigi Rizzo 			sent++;
554f8e4e36aSLuigi Rizzo 			ring->cur = NETMAP_RING_NEXT(ring, ring->cur);
555f8e4e36aSLuigi Rizzo 			ring->avail--;
556f8e4e36aSLuigi Rizzo 		}
557f8e4e36aSLuigi Rizzo 	    }
558f8e4e36aSLuigi Rizzo 		/* should use a parameter to decide how often to send */
559f8e4e36aSLuigi Rizzo 		if (poll(fds, 1, 3000) <= 0) {
560f8e4e36aSLuigi Rizzo 			D("poll error/timeout on queue %d", targ->me);
561f8e4e36aSLuigi Rizzo 			continue;
562f8e4e36aSLuigi Rizzo 		}
563f8e4e36aSLuigi Rizzo 		/* see what we got back */
564f8e4e36aSLuigi Rizzo 		for (i = targ->qfirst; i < targ->qlast; i++) {
565f8e4e36aSLuigi Rizzo 			ring = NETMAP_RXRING(nifp, i);
566f8e4e36aSLuigi Rizzo 			while (ring->avail > 0) {
567f8e4e36aSLuigi Rizzo 				uint32_t seq;
568f8e4e36aSLuigi Rizzo 				slot = &ring->slot[ring->cur];
569f8e4e36aSLuigi Rizzo 				p = NETMAP_BUF(ring, slot->buf_idx);
570f8e4e36aSLuigi Rizzo 
571f8e4e36aSLuigi Rizzo 				clock_gettime(CLOCK_REALTIME_PRECISE, &now);
572f8e4e36aSLuigi Rizzo 				bcopy(p+42, &seq, sizeof(seq));
573f8e4e36aSLuigi Rizzo 				bcopy(p+46, &ts, sizeof(ts));
574f8e4e36aSLuigi Rizzo 				ts.tv_sec = now.tv_sec - ts.tv_sec;
575f8e4e36aSLuigi Rizzo 				ts.tv_nsec = now.tv_nsec - ts.tv_nsec;
576f8e4e36aSLuigi Rizzo 				if (ts.tv_nsec < 0) {
577f8e4e36aSLuigi Rizzo 					ts.tv_nsec += 1000000000;
578f8e4e36aSLuigi Rizzo 					ts.tv_sec--;
579f8e4e36aSLuigi Rizzo 				}
580f8e4e36aSLuigi Rizzo 				if (1) D("seq %d/%d delta %d.%09d", seq, sent,
581f8e4e36aSLuigi Rizzo 					(int)ts.tv_sec, (int)ts.tv_nsec);
582f8e4e36aSLuigi Rizzo 				if (ts.tv_nsec < (int)min)
583f8e4e36aSLuigi Rizzo 					min = ts.tv_nsec;
584f8e4e36aSLuigi Rizzo 				count ++;
585f8e4e36aSLuigi Rizzo 				av += ts.tv_nsec;
586f8e4e36aSLuigi Rizzo 				ring->avail--;
587f8e4e36aSLuigi Rizzo 				ring->cur = NETMAP_RING_NEXT(ring, ring->cur);
588f8e4e36aSLuigi Rizzo 				rx++;
589f8e4e36aSLuigi Rizzo 			}
590f8e4e36aSLuigi Rizzo 		}
591f8e4e36aSLuigi Rizzo 		//D("tx %d rx %d", sent, rx);
592f8e4e36aSLuigi Rizzo 		//usleep(100000);
593f8e4e36aSLuigi Rizzo 		ts.tv_sec = now.tv_sec - last_print.tv_sec;
594f8e4e36aSLuigi Rizzo 		ts.tv_nsec = now.tv_nsec - last_print.tv_nsec;
595f8e4e36aSLuigi Rizzo 		if (ts.tv_nsec < 0) {
596f8e4e36aSLuigi Rizzo 			ts.tv_nsec += 1000000000;
597f8e4e36aSLuigi Rizzo 			ts.tv_sec--;
598f8e4e36aSLuigi Rizzo 		}
599f8e4e36aSLuigi Rizzo 		if (ts.tv_sec >= 1) {
600f8e4e36aSLuigi Rizzo 			D("count %d min %d av %d",
601f8e4e36aSLuigi Rizzo 				count, min, av/count);
602f8e4e36aSLuigi Rizzo 			count = 0;
603f8e4e36aSLuigi Rizzo 			av = 0;
604f8e4e36aSLuigi Rizzo 			min = 100000000;
605f8e4e36aSLuigi Rizzo 			last_print = now;
606f8e4e36aSLuigi Rizzo 		}
607f8e4e36aSLuigi Rizzo 	}
608f8e4e36aSLuigi Rizzo 	return NULL;
609f8e4e36aSLuigi Rizzo }
610f8e4e36aSLuigi Rizzo 
611f8e4e36aSLuigi Rizzo 
612f8e4e36aSLuigi Rizzo /*
613f8e4e36aSLuigi Rizzo  * reply to ping requests
614f8e4e36aSLuigi Rizzo  */
615f8e4e36aSLuigi Rizzo static void *
616f8e4e36aSLuigi Rizzo ponger_body(void *data)
617f8e4e36aSLuigi Rizzo {
618f8e4e36aSLuigi Rizzo 	struct targ *targ = (struct targ *) data;
619f8e4e36aSLuigi Rizzo 	struct pollfd fds[1];
620f8e4e36aSLuigi Rizzo 	struct netmap_if *nifp = targ->nifp;
621f8e4e36aSLuigi Rizzo 	struct netmap_ring *txring, *rxring;
622f8e4e36aSLuigi Rizzo 	int i, rx = 0, sent = 0, n = targ->g->npackets;
623f8e4e36aSLuigi Rizzo 	fds[0].fd = targ->fd;
624f8e4e36aSLuigi Rizzo 	fds[0].events = (POLLIN);
625f8e4e36aSLuigi Rizzo 
626f8e4e36aSLuigi Rizzo 	if (targ->g->nthreads > 1) {
627f8e4e36aSLuigi Rizzo 		D("can only reply ping with 1 thread");
628f8e4e36aSLuigi Rizzo 		return NULL;
629f8e4e36aSLuigi Rizzo 	}
630f8e4e36aSLuigi Rizzo 	D("understood ponger %d but don't know how to do it", n);
631f8e4e36aSLuigi Rizzo 	while (n == 0 || sent < n) {
632f8e4e36aSLuigi Rizzo 		uint32_t txcur, txavail;
633f8e4e36aSLuigi Rizzo //#define BUSYWAIT
634f8e4e36aSLuigi Rizzo #ifdef BUSYWAIT
635f8e4e36aSLuigi Rizzo 		ioctl(fds[0].fd, NIOCRXSYNC, NULL);
636f8e4e36aSLuigi Rizzo #else
637f8e4e36aSLuigi Rizzo 		if (poll(fds, 1, 1000) <= 0) {
638f8e4e36aSLuigi Rizzo 			D("poll error/timeout on queue %d", targ->me);
639f8e4e36aSLuigi Rizzo 			continue;
640f8e4e36aSLuigi Rizzo 		}
641f8e4e36aSLuigi Rizzo #endif
642f8e4e36aSLuigi Rizzo 		txring = NETMAP_TXRING(nifp, 0);
643f8e4e36aSLuigi Rizzo 		txcur = txring->cur;
644f8e4e36aSLuigi Rizzo 		txavail = txring->avail;
645f8e4e36aSLuigi Rizzo 		/* see what we got back */
646f8e4e36aSLuigi Rizzo 		for (i = targ->qfirst; i < targ->qlast; i++) {
647f8e4e36aSLuigi Rizzo 			rxring = NETMAP_RXRING(nifp, i);
648f8e4e36aSLuigi Rizzo 			while (rxring->avail > 0) {
649f8e4e36aSLuigi Rizzo 				uint16_t *spkt, *dpkt;
650f8e4e36aSLuigi Rizzo 				uint32_t cur = rxring->cur;
651f8e4e36aSLuigi Rizzo 				struct netmap_slot *slot = &rxring->slot[cur];
652f8e4e36aSLuigi Rizzo 				char *src, *dst;
653f8e4e36aSLuigi Rizzo 				src = NETMAP_BUF(rxring, slot->buf_idx);
654f8e4e36aSLuigi Rizzo 				//D("got pkt %p of size %d", src, slot->len);
655f8e4e36aSLuigi Rizzo 				rxring->avail--;
656f8e4e36aSLuigi Rizzo 				rxring->cur = NETMAP_RING_NEXT(rxring, cur);
657f8e4e36aSLuigi Rizzo 				rx++;
658f8e4e36aSLuigi Rizzo 				if (txavail == 0)
659f8e4e36aSLuigi Rizzo 					continue;
660f8e4e36aSLuigi Rizzo 				dst = NETMAP_BUF(txring,
661f8e4e36aSLuigi Rizzo 				    txring->slot[txcur].buf_idx);
662f8e4e36aSLuigi Rizzo 				/* copy... */
663f8e4e36aSLuigi Rizzo 				dpkt = (uint16_t *)dst;
664f8e4e36aSLuigi Rizzo 				spkt = (uint16_t *)src;
665f8e4e36aSLuigi Rizzo 				pkt_copy(src, dst, slot->len);
666f8e4e36aSLuigi Rizzo 				dpkt[0] = spkt[3];
667f8e4e36aSLuigi Rizzo 				dpkt[1] = spkt[4];
668f8e4e36aSLuigi Rizzo 				dpkt[2] = spkt[5];
669f8e4e36aSLuigi Rizzo 				dpkt[3] = spkt[0];
670f8e4e36aSLuigi Rizzo 				dpkt[4] = spkt[1];
671f8e4e36aSLuigi Rizzo 				dpkt[5] = spkt[2];
672f8e4e36aSLuigi Rizzo 				txring->slot[txcur].len = slot->len;
673f8e4e36aSLuigi Rizzo 				/* XXX swap src dst mac */
674f8e4e36aSLuigi Rizzo 				txcur = NETMAP_RING_NEXT(txring, txcur);
675f8e4e36aSLuigi Rizzo 				txavail--;
676f8e4e36aSLuigi Rizzo 				sent++;
677f8e4e36aSLuigi Rizzo 			}
678f8e4e36aSLuigi Rizzo 		}
679f8e4e36aSLuigi Rizzo 		txring->cur = txcur;
680f8e4e36aSLuigi Rizzo 		txring->avail = txavail;
681f8e4e36aSLuigi Rizzo 		targ->count = sent;
682f8e4e36aSLuigi Rizzo #ifdef BUSYWAIT
683f8e4e36aSLuigi Rizzo 		ioctl(fds[0].fd, NIOCTXSYNC, NULL);
684f8e4e36aSLuigi Rizzo #endif
685f8e4e36aSLuigi Rizzo 		//D("tx %d rx %d", sent, rx);
686f8e4e36aSLuigi Rizzo 	}
687f8e4e36aSLuigi Rizzo 	return NULL;
688f8e4e36aSLuigi Rizzo }
689f8e4e36aSLuigi Rizzo 
6901cb4c501SLuigi Rizzo static __inline int
6911cb4c501SLuigi Rizzo timespec_ge(const struct timespec *a, const struct timespec *b)
6921cb4c501SLuigi Rizzo {
6931cb4c501SLuigi Rizzo 
6941cb4c501SLuigi Rizzo 	if (a->tv_sec > b->tv_sec)
6951cb4c501SLuigi Rizzo 		return (1);
6961cb4c501SLuigi Rizzo 	if (a->tv_sec < b->tv_sec)
6971cb4c501SLuigi Rizzo 		return (0);
6981cb4c501SLuigi Rizzo 	if (a->tv_nsec >= b->tv_nsec)
6991cb4c501SLuigi Rizzo 		return (1);
7001cb4c501SLuigi Rizzo 	return (0);
7011cb4c501SLuigi Rizzo }
7021cb4c501SLuigi Rizzo 
7031cb4c501SLuigi Rizzo static __inline struct timespec
7041cb4c501SLuigi Rizzo timeval2spec(const struct timeval *a)
7051cb4c501SLuigi Rizzo {
7061cb4c501SLuigi Rizzo 	struct timespec ts = {
7071cb4c501SLuigi Rizzo 		.tv_sec = a->tv_sec,
7081cb4c501SLuigi Rizzo 		.tv_nsec = a->tv_usec * 1000
7091cb4c501SLuigi Rizzo 	};
7101cb4c501SLuigi Rizzo 	return ts;
7111cb4c501SLuigi Rizzo }
7121cb4c501SLuigi Rizzo 
7131cb4c501SLuigi Rizzo static __inline struct timeval
7141cb4c501SLuigi Rizzo timespec2val(const struct timespec *a)
7151cb4c501SLuigi Rizzo {
7161cb4c501SLuigi Rizzo 	struct timeval tv = {
7171cb4c501SLuigi Rizzo 		.tv_sec = a->tv_sec,
7181cb4c501SLuigi Rizzo 		.tv_usec = a->tv_nsec / 1000
7191cb4c501SLuigi Rizzo 	};
7201cb4c501SLuigi Rizzo 	return tv;
7211cb4c501SLuigi Rizzo }
7221cb4c501SLuigi Rizzo 
7231cb4c501SLuigi Rizzo 
7241cb4c501SLuigi Rizzo static int
7251cb4c501SLuigi Rizzo wait_time(struct timespec ts, struct timespec *wakeup_ts, long long *waited)
7261cb4c501SLuigi Rizzo {
7271cb4c501SLuigi Rizzo 	struct timespec curtime;
7281cb4c501SLuigi Rizzo 
7291cb4c501SLuigi Rizzo 	curtime.tv_sec = 0;
7301cb4c501SLuigi Rizzo 	curtime.tv_nsec = 0;
7311cb4c501SLuigi Rizzo 
7321cb4c501SLuigi Rizzo 	if (clock_gettime(CLOCK_REALTIME_PRECISE, &curtime) == -1) {
7331cb4c501SLuigi Rizzo 		D("clock_gettime: %s", strerror(errno));
7341cb4c501SLuigi Rizzo 		return (-1);
7351cb4c501SLuigi Rizzo 	}
7361cb4c501SLuigi Rizzo 	while (timespec_ge(&ts, &curtime)) {
7371cb4c501SLuigi Rizzo 		if (waited != NULL)
7381cb4c501SLuigi Rizzo 			(*waited)++;
7391cb4c501SLuigi Rizzo 		if (clock_gettime(CLOCK_REALTIME_PRECISE, &curtime) == -1) {
7401cb4c501SLuigi Rizzo 			D("clock_gettime");
7411cb4c501SLuigi Rizzo 			return (-1);
7421cb4c501SLuigi Rizzo 		}
7431cb4c501SLuigi Rizzo 	}
7441cb4c501SLuigi Rizzo 	if (wakeup_ts != NULL)
7451cb4c501SLuigi Rizzo 		*wakeup_ts = curtime;
7461cb4c501SLuigi Rizzo 	return (0);
7471cb4c501SLuigi Rizzo }
7481cb4c501SLuigi Rizzo 
7491cb4c501SLuigi Rizzo static __inline void
7501cb4c501SLuigi Rizzo timespec_add(struct timespec *tsa, struct timespec *tsb)
7511cb4c501SLuigi Rizzo {
7521cb4c501SLuigi Rizzo 	tsa->tv_sec += tsb->tv_sec;
7531cb4c501SLuigi Rizzo 	tsa->tv_nsec += tsb->tv_nsec;
7541cb4c501SLuigi Rizzo 	if (tsa->tv_nsec >= 1000000000) {
7551cb4c501SLuigi Rizzo 		tsa->tv_sec++;
7561cb4c501SLuigi Rizzo 		tsa->tv_nsec -= 1000000000;
7571cb4c501SLuigi Rizzo 	}
7581cb4c501SLuigi Rizzo }
7591cb4c501SLuigi Rizzo 
760f8e4e36aSLuigi Rizzo 
761f8e4e36aSLuigi Rizzo static void *
762f8e4e36aSLuigi Rizzo sender_body(void *data)
763f8e4e36aSLuigi Rizzo {
764f8e4e36aSLuigi Rizzo 	struct targ *targ = (struct targ *) data;
765f8e4e36aSLuigi Rizzo 
766f8e4e36aSLuigi Rizzo 	struct pollfd fds[1];
767f8e4e36aSLuigi Rizzo 	struct netmap_if *nifp = targ->nifp;
768f8e4e36aSLuigi Rizzo 	struct netmap_ring *txring;
769f8e4e36aSLuigi Rizzo 	int i, n = targ->g->npackets / targ->g->nthreads, sent = 0;
770f8e4e36aSLuigi Rizzo 	int options = targ->g->options | OPT_COPY;
7711cb4c501SLuigi Rizzo 	struct timespec tmptime, nexttime = { 0, 0}; // XXX silence compiler
7721cb4c501SLuigi Rizzo 	int rate_limit = targ->g->tx_rate;
7731cb4c501SLuigi Rizzo 	long long waited = 0;
774b303f675SLuigi Rizzo 
775f8e4e36aSLuigi Rizzo 	D("start");
77668b8534bSLuigi Rizzo 	if (setaffinity(targ->thread, targ->affinity))
77768b8534bSLuigi Rizzo 		goto quit;
7788ce070c1SUlrich Spörlein 	/* setup poll(2) mechanism. */
77968b8534bSLuigi Rizzo 	memset(fds, 0, sizeof(fds));
78068b8534bSLuigi Rizzo 	fds[0].fd = targ->fd;
78168b8534bSLuigi Rizzo 	fds[0].events = (POLLOUT);
78268b8534bSLuigi Rizzo 
78368b8534bSLuigi Rizzo 	/* main loop.*/
7841cb4c501SLuigi Rizzo 	clock_gettime(CLOCK_REALTIME_PRECISE, &targ->tic);
7851cb4c501SLuigi Rizzo 	if (rate_limit) {
7861cb4c501SLuigi Rizzo 		tmptime.tv_sec = 2;
7871cb4c501SLuigi Rizzo 		tmptime.tv_nsec = 0;
7881cb4c501SLuigi Rizzo 		timespec_add(&targ->tic, &tmptime);
7891cb4c501SLuigi Rizzo 		targ->tic.tv_nsec = 0;
7901cb4c501SLuigi Rizzo 		if (wait_time(targ->tic, NULL, NULL) == -1) {
7911cb4c501SLuigi Rizzo 			D("wait_time: %s", strerror(errno));
7921cb4c501SLuigi Rizzo 			goto quit;
7931cb4c501SLuigi Rizzo 		}
7941cb4c501SLuigi Rizzo 		nexttime = targ->tic;
7951cb4c501SLuigi Rizzo 	}
796f8e4e36aSLuigi Rizzo     if (targ->g->dev_type == DEV_PCAP) {
79768b8534bSLuigi Rizzo 	    int size = targ->g->pkt_size;
79868b8534bSLuigi Rizzo 	    void *pkt = &targ->pkt;
79968b8534bSLuigi Rizzo 	    pcap_t *p = targ->g->p;
80068b8534bSLuigi Rizzo 
801f8e4e36aSLuigi Rizzo 	    for (i = 0; !targ->cancel && (n == 0 || sent < n); i++) {
80299fb123fSLuigi Rizzo 		if (pcap_inject(p, pkt, size) != -1)
80399fb123fSLuigi Rizzo 			sent++;
80499fb123fSLuigi Rizzo 		if (i > 10000) {
80599fb123fSLuigi Rizzo 			targ->count = sent;
80699fb123fSLuigi Rizzo 			i = 0;
80799fb123fSLuigi Rizzo 		}
80868b8534bSLuigi Rizzo 	    }
809f8e4e36aSLuigi Rizzo     } else if (targ->g->dev_type == DEV_TAP) { /* tap */
810f8e4e36aSLuigi Rizzo 	    int size = targ->g->pkt_size;
811f8e4e36aSLuigi Rizzo 	    void *pkt = &targ->pkt;
812f8e4e36aSLuigi Rizzo 	    D("writing to file desc %d", targ->g->main_fd);
813f8e4e36aSLuigi Rizzo 
814f8e4e36aSLuigi Rizzo 	    for (i = 0; !targ->cancel && (n == 0 || sent < n); i++) {
815f8e4e36aSLuigi Rizzo 		if (write(targ->g->main_fd, pkt, size) != -1)
816f8e4e36aSLuigi Rizzo 			sent++;
817f8e4e36aSLuigi Rizzo 		if (i > 10000) {
818f8e4e36aSLuigi Rizzo 			targ->count = sent;
819f8e4e36aSLuigi Rizzo 			i = 0;
820f8e4e36aSLuigi Rizzo 		}
821f8e4e36aSLuigi Rizzo 	    }
82268b8534bSLuigi Rizzo     } else {
8231cb4c501SLuigi Rizzo 	int tosend = 0;
824f8e4e36aSLuigi Rizzo 	while (!targ->cancel && (n == 0 || sent < n)) {
82568b8534bSLuigi Rizzo 
8261cb4c501SLuigi Rizzo 		if (rate_limit && tosend <= 0) {
8271cb4c501SLuigi Rizzo 			tosend = targ->g->burst;
8281cb4c501SLuigi Rizzo 			timespec_add(&nexttime, &targ->g->tx_period);
8291cb4c501SLuigi Rizzo 			if (wait_time(nexttime, &tmptime, &waited) == -1) {
8301cb4c501SLuigi Rizzo 				D("wait_time");
8311cb4c501SLuigi Rizzo 				goto quit;
8321cb4c501SLuigi Rizzo 			}
8331cb4c501SLuigi Rizzo 		}
8341cb4c501SLuigi Rizzo 
83568b8534bSLuigi Rizzo 		/*
83668b8534bSLuigi Rizzo 		 * wait for available room in the send queue(s)
83768b8534bSLuigi Rizzo 		 */
838f8e4e36aSLuigi Rizzo 		if (poll(fds, 1, 2000) <= 0) {
8393fe77e68SEd Maste 			if (targ->cancel)
8403fe77e68SEd Maste 				break;
841f8e4e36aSLuigi Rizzo 			D("poll error/timeout on queue %d", targ->me);
84268b8534bSLuigi Rizzo 			goto quit;
84368b8534bSLuigi Rizzo 		}
84468b8534bSLuigi Rizzo 		/*
84568b8534bSLuigi Rizzo 		 * scan our queues and send on those with room
84668b8534bSLuigi Rizzo 		 */
847f8e4e36aSLuigi Rizzo 		if (options & OPT_COPY && sent > 100000 && !(targ->g->options & OPT_COPY) ) {
848f8e4e36aSLuigi Rizzo 			D("drop copy");
84999fb123fSLuigi Rizzo 			options &= ~OPT_COPY;
850f8e4e36aSLuigi Rizzo 		}
851f8e4e36aSLuigi Rizzo 		for (i = targ->qfirst; i < targ->qlast; i++) {
8521cb4c501SLuigi Rizzo 			int m, limit = rate_limit ?  tosend : targ->g->burst;
853f8e4e36aSLuigi Rizzo 			if (n > 0 && n - sent < limit)
854f8e4e36aSLuigi Rizzo 				limit = n - sent;
85568b8534bSLuigi Rizzo 			txring = NETMAP_TXRING(nifp, i);
85668b8534bSLuigi Rizzo 			if (txring->avail == 0)
85768b8534bSLuigi Rizzo 				continue;
85868b8534bSLuigi Rizzo 			m = send_packets(txring, &targ->pkt, targ->g->pkt_size,
85999fb123fSLuigi Rizzo 					 limit, options);
86068b8534bSLuigi Rizzo 			sent += m;
8611cb4c501SLuigi Rizzo 			tosend -= m;
86268b8534bSLuigi Rizzo 			targ->count = sent;
86368b8534bSLuigi Rizzo 		}
86468b8534bSLuigi Rizzo 	}
86599fb123fSLuigi Rizzo 	/* flush any remaining packets */
86668b8534bSLuigi Rizzo 	ioctl(fds[0].fd, NIOCTXSYNC, NULL);
86768b8534bSLuigi Rizzo 
86868b8534bSLuigi Rizzo 	/* final part: wait all the TX queues to be empty. */
86968b8534bSLuigi Rizzo 	for (i = targ->qfirst; i < targ->qlast; i++) {
87068b8534bSLuigi Rizzo 		txring = NETMAP_TXRING(nifp, i);
87168b8534bSLuigi Rizzo 		while (!NETMAP_TX_RING_EMPTY(txring)) {
87268b8534bSLuigi Rizzo 			ioctl(fds[0].fd, NIOCTXSYNC, NULL);
87368b8534bSLuigi Rizzo 			usleep(1); /* wait 1 tick */
87468b8534bSLuigi Rizzo 		}
87568b8534bSLuigi Rizzo 	}
87668b8534bSLuigi Rizzo     }
87768b8534bSLuigi Rizzo 
8781cb4c501SLuigi Rizzo 	clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc);
87968b8534bSLuigi Rizzo 	targ->completed = 1;
88068b8534bSLuigi Rizzo 	targ->count = sent;
88168b8534bSLuigi Rizzo 
88268b8534bSLuigi Rizzo quit:
88368b8534bSLuigi Rizzo 	/* reset the ``used`` flag. */
88468b8534bSLuigi Rizzo 	targ->used = 0;
88568b8534bSLuigi Rizzo 
88668b8534bSLuigi Rizzo 	return (NULL);
88768b8534bSLuigi Rizzo }
88868b8534bSLuigi Rizzo 
88968b8534bSLuigi Rizzo 
89068b8534bSLuigi Rizzo static void
891f8e4e36aSLuigi Rizzo receive_pcap(u_char *user, const struct pcap_pkthdr * h,
892f8e4e36aSLuigi Rizzo 	const u_char * bytes)
89368b8534bSLuigi Rizzo {
89468b8534bSLuigi Rizzo 	int *count = (int *)user;
895f8e4e36aSLuigi Rizzo 	(void)h;	/* UNUSED */
896f8e4e36aSLuigi Rizzo 	(void)bytes;	/* UNUSED */
89768b8534bSLuigi Rizzo 	(*count)++;
89868b8534bSLuigi Rizzo }
89968b8534bSLuigi Rizzo 
90068b8534bSLuigi Rizzo static int
901b303f675SLuigi Rizzo receive_packets(struct netmap_ring *ring, u_int limit, int dump)
90268b8534bSLuigi Rizzo {
90368b8534bSLuigi Rizzo 	u_int cur, rx;
90468b8534bSLuigi Rizzo 
90568b8534bSLuigi Rizzo 	cur = ring->cur;
90668b8534bSLuigi Rizzo 	if (ring->avail < limit)
90768b8534bSLuigi Rizzo 		limit = ring->avail;
90868b8534bSLuigi Rizzo 	for (rx = 0; rx < limit; rx++) {
90968b8534bSLuigi Rizzo 		struct netmap_slot *slot = &ring->slot[cur];
91068b8534bSLuigi Rizzo 		char *p = NETMAP_BUF(ring, slot->buf_idx);
91168b8534bSLuigi Rizzo 
912b303f675SLuigi Rizzo 		slot->flags = OPT_INDIRECT; // XXX
913b303f675SLuigi Rizzo 		if (dump)
914b303f675SLuigi Rizzo 			dump_payload(p, slot->len, ring, cur);
91568b8534bSLuigi Rizzo 
91668b8534bSLuigi Rizzo 		cur = NETMAP_RING_NEXT(ring, cur);
91768b8534bSLuigi Rizzo 	}
91868b8534bSLuigi Rizzo 	ring->avail -= rx;
91968b8534bSLuigi Rizzo 	ring->cur = cur;
92068b8534bSLuigi Rizzo 
92168b8534bSLuigi Rizzo 	return (rx);
92268b8534bSLuigi Rizzo }
92368b8534bSLuigi Rizzo 
92468b8534bSLuigi Rizzo static void *
92568b8534bSLuigi Rizzo receiver_body(void *data)
92668b8534bSLuigi Rizzo {
92768b8534bSLuigi Rizzo 	struct targ *targ = (struct targ *) data;
92868b8534bSLuigi Rizzo 	struct pollfd fds[1];
92968b8534bSLuigi Rizzo 	struct netmap_if *nifp = targ->nifp;
93068b8534bSLuigi Rizzo 	struct netmap_ring *rxring;
931f8e4e36aSLuigi Rizzo 	int i;
932f8e4e36aSLuigi Rizzo 	uint64_t received = 0;
93368b8534bSLuigi Rizzo 
93468b8534bSLuigi Rizzo 	if (setaffinity(targ->thread, targ->affinity))
93568b8534bSLuigi Rizzo 		goto quit;
93668b8534bSLuigi Rizzo 
9378ce070c1SUlrich Spörlein 	/* setup poll(2) mechanism. */
93868b8534bSLuigi Rizzo 	memset(fds, 0, sizeof(fds));
93968b8534bSLuigi Rizzo 	fds[0].fd = targ->fd;
94068b8534bSLuigi Rizzo 	fds[0].events = (POLLIN);
94168b8534bSLuigi Rizzo 
94268b8534bSLuigi Rizzo 	/* unbounded wait for the first packet. */
943f8e4e36aSLuigi Rizzo 	for (;;) {
94468b8534bSLuigi Rizzo 		i = poll(fds, 1, 1000);
94568b8534bSLuigi Rizzo 		if (i > 0 && !(fds[0].revents & POLLERR))
94668b8534bSLuigi Rizzo 			break;
94768b8534bSLuigi Rizzo 		D("waiting for initial packets, poll returns %d %d", i, fds[0].revents);
94868b8534bSLuigi Rizzo 	}
94968b8534bSLuigi Rizzo 
95068b8534bSLuigi Rizzo 	/* main loop, exit after 1s silence */
9511cb4c501SLuigi Rizzo 	clock_gettime(CLOCK_REALTIME_PRECISE, &targ->tic);
952f8e4e36aSLuigi Rizzo     if (targ->g->dev_type == DEV_PCAP) {
9533fe77e68SEd Maste 	while (!targ->cancel) {
954f8e4e36aSLuigi Rizzo 		/* XXX should we poll ? */
95568b8534bSLuigi Rizzo 		pcap_dispatch(targ->g->p, targ->g->burst, receive_pcap, NULL);
95668b8534bSLuigi Rizzo 	}
957f8e4e36aSLuigi Rizzo     } else if (targ->g->dev_type == DEV_TAP) {
958f8e4e36aSLuigi Rizzo 	D("reading from %s fd %d", targ->g->ifname, targ->g->main_fd);
959f8e4e36aSLuigi Rizzo 	while (!targ->cancel) {
960f8e4e36aSLuigi Rizzo 		char buf[2048];
961f8e4e36aSLuigi Rizzo 		/* XXX should we poll ? */
962f8e4e36aSLuigi Rizzo 		if (read(targ->g->main_fd, buf, sizeof(buf)) > 0)
963f8e4e36aSLuigi Rizzo 			targ->count++;
964f8e4e36aSLuigi Rizzo 	}
96568b8534bSLuigi Rizzo     } else {
966b303f675SLuigi Rizzo 	int dump = targ->g->options & OPT_DUMP;
9673fe77e68SEd Maste 	while (!targ->cancel) {
96868b8534bSLuigi Rizzo 		/* Once we started to receive packets, wait at most 1 seconds
96968b8534bSLuigi Rizzo 		   before quitting. */
9701cb4c501SLuigi Rizzo 		if (poll(fds, 1, 1 * 1000) <= 0 && !targ->g->forever) {
9711cb4c501SLuigi Rizzo 			clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc);
9728ce070c1SUlrich Spörlein 			targ->toc.tv_sec -= 1; /* Subtract timeout time. */
97368b8534bSLuigi Rizzo 			break;
97468b8534bSLuigi Rizzo 		}
97568b8534bSLuigi Rizzo 
97668b8534bSLuigi Rizzo 		for (i = targ->qfirst; i < targ->qlast; i++) {
97768b8534bSLuigi Rizzo 			int m;
97868b8534bSLuigi Rizzo 
97968b8534bSLuigi Rizzo 			rxring = NETMAP_RXRING(nifp, i);
98068b8534bSLuigi Rizzo 			if (rxring->avail == 0)
98168b8534bSLuigi Rizzo 				continue;
98268b8534bSLuigi Rizzo 
983b303f675SLuigi Rizzo 			m = receive_packets(rxring, targ->g->burst, dump);
98468b8534bSLuigi Rizzo 			received += m;
98568b8534bSLuigi Rizzo 		}
986f8e4e36aSLuigi Rizzo 		targ->count = received;
98768b8534bSLuigi Rizzo 
98868b8534bSLuigi Rizzo 		// tell the card we have read the data
98968b8534bSLuigi Rizzo 		//ioctl(fds[0].fd, NIOCRXSYNC, NULL);
99068b8534bSLuigi Rizzo 	}
99168b8534bSLuigi Rizzo     }
99268b8534bSLuigi Rizzo 
99368b8534bSLuigi Rizzo 	targ->completed = 1;
99468b8534bSLuigi Rizzo 	targ->count = received;
99568b8534bSLuigi Rizzo 
99668b8534bSLuigi Rizzo quit:
99768b8534bSLuigi Rizzo 	/* reset the ``used`` flag. */
99868b8534bSLuigi Rizzo 	targ->used = 0;
99968b8534bSLuigi Rizzo 
100068b8534bSLuigi Rizzo 	return (NULL);
100168b8534bSLuigi Rizzo }
100268b8534bSLuigi Rizzo 
1003f8e4e36aSLuigi Rizzo /* very crude code to print a number in normalized form.
1004f8e4e36aSLuigi Rizzo  * Caller has to make sure that the buffer is large enough.
1005f8e4e36aSLuigi Rizzo  */
1006f8e4e36aSLuigi Rizzo static const char *
1007f8e4e36aSLuigi Rizzo norm(char *buf, double val)
100866a698c9SEd Maste {
1009f8e4e36aSLuigi Rizzo 	char *units[] = { "", "K", "M", "G" };
1010f8e4e36aSLuigi Rizzo 	u_int i;
101166a698c9SEd Maste 
1012f8e4e36aSLuigi Rizzo 	for (i = 0; val >=1000 && i < sizeof(units)/sizeof(char *); i++)
101366a698c9SEd Maste 		val /= 1000;
1014f8e4e36aSLuigi Rizzo 	sprintf(buf, "%.2f %s", val, units[i]);
1015f8e4e36aSLuigi Rizzo 	return buf;
101666a698c9SEd Maste }
101766a698c9SEd Maste 
101868b8534bSLuigi Rizzo static void
101968b8534bSLuigi Rizzo tx_output(uint64_t sent, int size, double delta)
102068b8534bSLuigi Rizzo {
1021f8e4e36aSLuigi Rizzo 	double bw, raw_bw, pps;
1022f8e4e36aSLuigi Rizzo 	char b1[40], b2[80], b3[80];
102368b8534bSLuigi Rizzo 
1024506cc70cSLuigi Rizzo 	printf("Sent %" PRIu64 " packets, %d bytes each, in %.2f seconds.\n",
102568b8534bSLuigi Rizzo 	       sent, size, delta);
1026f8e4e36aSLuigi Rizzo 	if (delta == 0)
1027f8e4e36aSLuigi Rizzo 		delta = 1e-6;
1028f8e4e36aSLuigi Rizzo 	if (size < 60)		/* correct for min packet size */
1029f8e4e36aSLuigi Rizzo 		size = 60;
1030f8e4e36aSLuigi Rizzo 	pps = sent / delta;
1031f8e4e36aSLuigi Rizzo 	bw = (8.0 * size * sent) / delta;
1032f8e4e36aSLuigi Rizzo 	/* raw packets have4 bytes crc + 20 bytes framing */
1033f8e4e36aSLuigi Rizzo 	raw_bw = (8.0 * (size + 24) * sent) / delta;
103466a698c9SEd Maste 
1035f8e4e36aSLuigi Rizzo 	printf("Speed: %spps Bandwidth: %sbps (raw %sbps)\n",
1036f8e4e36aSLuigi Rizzo 		norm(b1, pps), norm(b2, bw), norm(b3, raw_bw) );
103768b8534bSLuigi Rizzo }
103868b8534bSLuigi Rizzo 
103968b8534bSLuigi Rizzo 
104068b8534bSLuigi Rizzo static void
104168b8534bSLuigi Rizzo rx_output(uint64_t received, double delta)
104268b8534bSLuigi Rizzo {
1043f8e4e36aSLuigi Rizzo 	double pps;
1044f8e4e36aSLuigi Rizzo 	char b1[40];
104568b8534bSLuigi Rizzo 
1046506cc70cSLuigi Rizzo 	printf("Received %" PRIu64 " packets, in %.2f seconds.\n", received, delta);
1047f8e4e36aSLuigi Rizzo 
1048f8e4e36aSLuigi Rizzo 	if (delta == 0)
1049f8e4e36aSLuigi Rizzo 		delta = 1e-6;
1050f8e4e36aSLuigi Rizzo 	pps = received / delta;
1051f8e4e36aSLuigi Rizzo 	printf("Speed: %spps\n", norm(b1, pps));
105268b8534bSLuigi Rizzo }
105368b8534bSLuigi Rizzo 
105468b8534bSLuigi Rizzo static void
105568b8534bSLuigi Rizzo usage(void)
105668b8534bSLuigi Rizzo {
105768b8534bSLuigi Rizzo 	const char *cmd = "pkt-gen";
105868b8534bSLuigi Rizzo 	fprintf(stderr,
105968b8534bSLuigi Rizzo 		"Usage:\n"
106068b8534bSLuigi Rizzo 		"%s arguments\n"
106168b8534bSLuigi Rizzo 		"\t-i interface		interface name\n"
1062f8e4e36aSLuigi Rizzo 		"\t-f function		tx rx ping pong\n"
1063f8e4e36aSLuigi Rizzo 		"\t-n count		number of iterations (can be 0)\n"
1064f8e4e36aSLuigi Rizzo 		"\t-t pkts_to_send		also forces tx mode\n"
1065f8e4e36aSLuigi Rizzo 		"\t-r pkts_to_receive	also forces rx mode\n"
106668b8534bSLuigi Rizzo 		"\t-l pkts_size		in bytes excluding CRC\n"
106768b8534bSLuigi Rizzo 		"\t-d dst-ip		end with %%n to sweep n addresses\n"
106868b8534bSLuigi Rizzo 		"\t-s src-ip		end with %%n to sweep n addresses\n"
106968b8534bSLuigi Rizzo 		"\t-D dst-mac		end with %%n to sweep n addresses\n"
107068b8534bSLuigi Rizzo 		"\t-S src-mac		end with %%n to sweep n addresses\n"
1071f8e4e36aSLuigi Rizzo 		"\t-a cpu_id		use setaffinity\n"
107268b8534bSLuigi Rizzo 		"\t-b burst size		testing, mostly\n"
107368b8534bSLuigi Rizzo 		"\t-c cores		cores to use\n"
107468b8534bSLuigi Rizzo 		"\t-p threads		processes/threads to use\n"
107568b8534bSLuigi Rizzo 		"\t-T report_ms		milliseconds between reports\n"
1076f8e4e36aSLuigi Rizzo 		"\t-P				use libpcap instead of netmap\n"
107768b8534bSLuigi Rizzo 		"\t-w wait_for_link_time	in seconds\n"
107868b8534bSLuigi Rizzo 		"",
107968b8534bSLuigi Rizzo 		cmd);
108068b8534bSLuigi Rizzo 
108168b8534bSLuigi Rizzo 	exit(0);
108268b8534bSLuigi Rizzo }
108368b8534bSLuigi Rizzo 
1084f8e4e36aSLuigi Rizzo static void
1085f8e4e36aSLuigi Rizzo start_threads(struct glob_arg *g)
1086f8e4e36aSLuigi Rizzo {
1087f8e4e36aSLuigi Rizzo 	int i;
1088f8e4e36aSLuigi Rizzo 
1089f8e4e36aSLuigi Rizzo 	targs = calloc(g->nthreads, sizeof(*targs));
1090f8e4e36aSLuigi Rizzo 	/*
1091f8e4e36aSLuigi Rizzo 	 * Now create the desired number of threads, each one
1092f8e4e36aSLuigi Rizzo 	 * using a single descriptor.
1093f8e4e36aSLuigi Rizzo  	 */
1094f8e4e36aSLuigi Rizzo 	for (i = 0; i < g->nthreads; i++) {
1095f8e4e36aSLuigi Rizzo 		bzero(&targs[i], sizeof(targs[i]));
1096f8e4e36aSLuigi Rizzo 		targs[i].fd = -1; /* default, with pcap */
1097f8e4e36aSLuigi Rizzo 		targs[i].g = g;
1098f8e4e36aSLuigi Rizzo 
1099f8e4e36aSLuigi Rizzo 	    if (g->dev_type == DEV_NETMAP) {
1100f8e4e36aSLuigi Rizzo 		struct nmreq tifreq;
1101f8e4e36aSLuigi Rizzo 		int tfd;
1102f8e4e36aSLuigi Rizzo 
1103f8e4e36aSLuigi Rizzo 		/* register interface. */
1104f8e4e36aSLuigi Rizzo 		tfd = open("/dev/netmap", O_RDWR);
1105f8e4e36aSLuigi Rizzo 		if (tfd == -1) {
1106f8e4e36aSLuigi Rizzo 			D("Unable to open /dev/netmap");
1107f8e4e36aSLuigi Rizzo 			continue;
1108f8e4e36aSLuigi Rizzo 		}
1109f8e4e36aSLuigi Rizzo 		targs[i].fd = tfd;
1110f8e4e36aSLuigi Rizzo 
1111f8e4e36aSLuigi Rizzo 		bzero(&tifreq, sizeof(tifreq));
1112f8e4e36aSLuigi Rizzo 		strncpy(tifreq.nr_name, g->ifname, sizeof(tifreq.nr_name));
1113f8e4e36aSLuigi Rizzo 		tifreq.nr_version = NETMAP_API;
1114f8e4e36aSLuigi Rizzo 		tifreq.nr_ringid = (g->nthreads > 1) ? (i | NETMAP_HW_RING) : 0;
1115f8e4e36aSLuigi Rizzo 
1116f8e4e36aSLuigi Rizzo 		/*
1117f8e4e36aSLuigi Rizzo 		 * if we are acting as a receiver only, do not touch the transmit ring.
1118f8e4e36aSLuigi Rizzo 		 * This is not the default because many apps may use the interface
1119f8e4e36aSLuigi Rizzo 		 * in both directions, but a pure receiver does not.
1120f8e4e36aSLuigi Rizzo 		 */
1121f8e4e36aSLuigi Rizzo 		if (g->td_body == receiver_body) {
1122f8e4e36aSLuigi Rizzo 			tifreq.nr_ringid |= NETMAP_NO_TX_POLL;
1123f8e4e36aSLuigi Rizzo 		}
1124f8e4e36aSLuigi Rizzo 
1125f8e4e36aSLuigi Rizzo 		if ((ioctl(tfd, NIOCREGIF, &tifreq)) == -1) {
1126f8e4e36aSLuigi Rizzo 			D("Unable to register %s", g->ifname);
1127f8e4e36aSLuigi Rizzo 			continue;
1128f8e4e36aSLuigi Rizzo 		}
1129f8e4e36aSLuigi Rizzo 		targs[i].nmr = tifreq;
1130f8e4e36aSLuigi Rizzo 		targs[i].nifp = NETMAP_IF(g->mmap_addr, tifreq.nr_offset);
1131f8e4e36aSLuigi Rizzo 		/* start threads. */
1132f8e4e36aSLuigi Rizzo 		targs[i].qfirst = (g->nthreads > 1) ? i : 0;
1133f8e4e36aSLuigi Rizzo 		targs[i].qlast = (g->nthreads > 1) ? i+1 :
1134f8e4e36aSLuigi Rizzo 			(g->td_body == receiver_body ? tifreq.nr_rx_rings : tifreq.nr_tx_rings);
1135f8e4e36aSLuigi Rizzo 	    } else {
1136f8e4e36aSLuigi Rizzo 		targs[i].fd = g->main_fd;
1137f8e4e36aSLuigi Rizzo 	    }
1138f8e4e36aSLuigi Rizzo 		targs[i].used = 1;
1139f8e4e36aSLuigi Rizzo 		targs[i].me = i;
1140f8e4e36aSLuigi Rizzo 		if (g->affinity >= 0) {
1141f8e4e36aSLuigi Rizzo 			if (g->affinity < g->cpus)
1142f8e4e36aSLuigi Rizzo 				targs[i].affinity = g->affinity;
1143f8e4e36aSLuigi Rizzo 			else
1144f8e4e36aSLuigi Rizzo 				targs[i].affinity = i % g->cpus;
1145f8e4e36aSLuigi Rizzo 		} else
1146f8e4e36aSLuigi Rizzo 			targs[i].affinity = -1;
1147f8e4e36aSLuigi Rizzo 		/* default, init packets */
1148f8e4e36aSLuigi Rizzo 		initialize_packet(&targs[i]);
1149f8e4e36aSLuigi Rizzo 
1150f8e4e36aSLuigi Rizzo 		if (pthread_create(&targs[i].thread, NULL, g->td_body,
1151f8e4e36aSLuigi Rizzo 				   &targs[i]) == -1) {
1152f8e4e36aSLuigi Rizzo 			D("Unable to create thread %d", i);
1153f8e4e36aSLuigi Rizzo 			targs[i].used = 0;
1154f8e4e36aSLuigi Rizzo 		}
1155f8e4e36aSLuigi Rizzo 	}
1156f8e4e36aSLuigi Rizzo }
1157f8e4e36aSLuigi Rizzo 
1158f8e4e36aSLuigi Rizzo static void
1159f8e4e36aSLuigi Rizzo main_thread(struct glob_arg *g)
1160f8e4e36aSLuigi Rizzo {
1161f8e4e36aSLuigi Rizzo 	int i;
1162f8e4e36aSLuigi Rizzo 
1163f8e4e36aSLuigi Rizzo 	uint64_t prev = 0;
1164f8e4e36aSLuigi Rizzo 	uint64_t count = 0;
1165f8e4e36aSLuigi Rizzo 	double delta_t;
1166f8e4e36aSLuigi Rizzo 	struct timeval tic, toc;
1167f8e4e36aSLuigi Rizzo 
1168f8e4e36aSLuigi Rizzo 	gettimeofday(&toc, NULL);
1169f8e4e36aSLuigi Rizzo 	for (;;) {
1170f8e4e36aSLuigi Rizzo 		struct timeval now, delta;
1171f8e4e36aSLuigi Rizzo 		uint64_t pps, usec, my_count, npkts;
1172f8e4e36aSLuigi Rizzo 		int done = 0;
1173f8e4e36aSLuigi Rizzo 
1174f8e4e36aSLuigi Rizzo 		delta.tv_sec = g->report_interval/1000;
1175f8e4e36aSLuigi Rizzo 		delta.tv_usec = (g->report_interval%1000)*1000;
1176f8e4e36aSLuigi Rizzo 		select(0, NULL, NULL, NULL, &delta);
1177f8e4e36aSLuigi Rizzo 		gettimeofday(&now, NULL);
1178f8e4e36aSLuigi Rizzo 		time_second = now.tv_sec;
1179f8e4e36aSLuigi Rizzo 		timersub(&now, &toc, &toc);
1180f8e4e36aSLuigi Rizzo 		my_count = 0;
1181f8e4e36aSLuigi Rizzo 		for (i = 0; i < g->nthreads; i++) {
1182f8e4e36aSLuigi Rizzo 			my_count += targs[i].count;
1183f8e4e36aSLuigi Rizzo 			if (targs[i].used == 0)
1184f8e4e36aSLuigi Rizzo 				done++;
1185f8e4e36aSLuigi Rizzo 		}
1186f8e4e36aSLuigi Rizzo 		usec = toc.tv_sec* 1000000 + toc.tv_usec;
1187f8e4e36aSLuigi Rizzo 		if (usec < 10000)
1188f8e4e36aSLuigi Rizzo 			continue;
1189f8e4e36aSLuigi Rizzo 		npkts = my_count - prev;
1190f8e4e36aSLuigi Rizzo 		pps = (npkts*1000000 + usec/2) / usec;
1191f8e4e36aSLuigi Rizzo 		D("%" PRIu64 " pps (%" PRIu64 " pkts in %" PRIu64 " usec)",
1192f8e4e36aSLuigi Rizzo 			pps, npkts, usec);
1193f8e4e36aSLuigi Rizzo 		prev = my_count;
1194f8e4e36aSLuigi Rizzo 		toc = now;
1195f8e4e36aSLuigi Rizzo 		if (done == g->nthreads)
1196f8e4e36aSLuigi Rizzo 			break;
1197f8e4e36aSLuigi Rizzo 	}
1198f8e4e36aSLuigi Rizzo 
1199f8e4e36aSLuigi Rizzo 	timerclear(&tic);
1200f8e4e36aSLuigi Rizzo 	timerclear(&toc);
1201f8e4e36aSLuigi Rizzo 	for (i = 0; i < g->nthreads; i++) {
12021cb4c501SLuigi Rizzo 		struct timespec t_tic, t_toc;
1203f8e4e36aSLuigi Rizzo 		/*
1204f8e4e36aSLuigi Rizzo 		 * Join active threads, unregister interfaces and close
1205f8e4e36aSLuigi Rizzo 		 * file descriptors.
1206f8e4e36aSLuigi Rizzo 		 */
12071cb4c501SLuigi Rizzo 		if (targs[i].used)
1208f8e4e36aSLuigi Rizzo 			pthread_join(targs[i].thread, NULL);
1209f8e4e36aSLuigi Rizzo 		close(targs[i].fd);
1210f8e4e36aSLuigi Rizzo 
1211f8e4e36aSLuigi Rizzo 		if (targs[i].completed == 0)
1212f8e4e36aSLuigi Rizzo 			D("ouch, thread %d exited with error", i);
1213f8e4e36aSLuigi Rizzo 
1214f8e4e36aSLuigi Rizzo 		/*
1215f8e4e36aSLuigi Rizzo 		 * Collect threads output and extract information about
1216f8e4e36aSLuigi Rizzo 		 * how long it took to send all the packets.
1217f8e4e36aSLuigi Rizzo 		 */
1218f8e4e36aSLuigi Rizzo 		count += targs[i].count;
12191cb4c501SLuigi Rizzo 		t_tic = timeval2spec(&tic);
12201cb4c501SLuigi Rizzo 		t_toc = timeval2spec(&toc);
12211cb4c501SLuigi Rizzo 		if (!timerisset(&tic) || timespec_ge(&targs[i].tic, &t_tic))
12221cb4c501SLuigi Rizzo 			tic = timespec2val(&targs[i].tic);
12231cb4c501SLuigi Rizzo 		if (!timerisset(&toc) || timespec_ge(&targs[i].toc, &t_toc))
12241cb4c501SLuigi Rizzo 			toc = timespec2val(&targs[i].toc);
1225f8e4e36aSLuigi Rizzo 	}
1226f8e4e36aSLuigi Rizzo 
1227f8e4e36aSLuigi Rizzo 	/* print output. */
1228f8e4e36aSLuigi Rizzo 	timersub(&toc, &tic, &toc);
1229f8e4e36aSLuigi Rizzo 	delta_t = toc.tv_sec + 1e-6* toc.tv_usec;
1230f8e4e36aSLuigi Rizzo 	if (g->td_body == sender_body)
1231f8e4e36aSLuigi Rizzo 		tx_output(count, g->pkt_size, delta_t);
1232f8e4e36aSLuigi Rizzo 	else
1233f8e4e36aSLuigi Rizzo 		rx_output(count, delta_t);
1234f8e4e36aSLuigi Rizzo 
1235f8e4e36aSLuigi Rizzo 	if (g->dev_type == DEV_NETMAP) {
1236f8e4e36aSLuigi Rizzo 		munmap(g->mmap_addr, g->mmap_size);
1237f8e4e36aSLuigi Rizzo 		close(g->main_fd);
1238f8e4e36aSLuigi Rizzo 	}
1239f8e4e36aSLuigi Rizzo }
1240f8e4e36aSLuigi Rizzo 
1241f8e4e36aSLuigi Rizzo 
1242f8e4e36aSLuigi Rizzo struct sf {
1243f8e4e36aSLuigi Rizzo 	char *key;
1244f8e4e36aSLuigi Rizzo 	void *f;
1245f8e4e36aSLuigi Rizzo };
1246f8e4e36aSLuigi Rizzo 
1247f8e4e36aSLuigi Rizzo static struct sf func[] = {
1248f8e4e36aSLuigi Rizzo 	{ "tx",	sender_body },
1249f8e4e36aSLuigi Rizzo 	{ "rx",	receiver_body },
1250f8e4e36aSLuigi Rizzo 	{ "ping",	pinger_body },
1251f8e4e36aSLuigi Rizzo 	{ "pong",	ponger_body },
1252f8e4e36aSLuigi Rizzo 	{ NULL, NULL }
1253f8e4e36aSLuigi Rizzo };
1254f8e4e36aSLuigi Rizzo 
1255f8e4e36aSLuigi Rizzo static int
1256f8e4e36aSLuigi Rizzo tap_alloc(char *dev)
1257f8e4e36aSLuigi Rizzo {
1258f8e4e36aSLuigi Rizzo 	struct ifreq ifr;
1259f8e4e36aSLuigi Rizzo 	int fd, err;
1260f8e4e36aSLuigi Rizzo 	char *clonedev = TAP_CLONEDEV;
1261f8e4e36aSLuigi Rizzo 
1262f8e4e36aSLuigi Rizzo 	(void)err;
1263f8e4e36aSLuigi Rizzo 	(void)dev;
1264f8e4e36aSLuigi Rizzo 	/* Arguments taken by the function:
1265f8e4e36aSLuigi Rizzo 	 *
1266f8e4e36aSLuigi Rizzo 	 * char *dev: the name of an interface (or '\0'). MUST have enough
1267f8e4e36aSLuigi Rizzo 	 *   space to hold the interface name if '\0' is passed
1268f8e4e36aSLuigi Rizzo 	 * int flags: interface flags (eg, IFF_TUN etc.)
1269f8e4e36aSLuigi Rizzo 	 */
1270f8e4e36aSLuigi Rizzo 
1271f8e4e36aSLuigi Rizzo #ifdef __FreeBSD__
1272f8e4e36aSLuigi Rizzo 	if (dev[3]) { /* tapSomething */
1273f8e4e36aSLuigi Rizzo 		static char buf[128];
1274f8e4e36aSLuigi Rizzo 		snprintf(buf, sizeof(buf), "/dev/%s", dev);
1275f8e4e36aSLuigi Rizzo 		clonedev = buf;
1276f8e4e36aSLuigi Rizzo 	}
1277f8e4e36aSLuigi Rizzo #endif
1278f8e4e36aSLuigi Rizzo 	/* open the device */
1279f8e4e36aSLuigi Rizzo 	if( (fd = open(clonedev, O_RDWR)) < 0 ) {
1280f8e4e36aSLuigi Rizzo 		return fd;
1281f8e4e36aSLuigi Rizzo 	}
1282f8e4e36aSLuigi Rizzo 	D("%s open successful", clonedev);
1283f8e4e36aSLuigi Rizzo 
1284f8e4e36aSLuigi Rizzo 	/* preparation of the struct ifr, of type "struct ifreq" */
1285f8e4e36aSLuigi Rizzo 	memset(&ifr, 0, sizeof(ifr));
1286f8e4e36aSLuigi Rizzo 
1287f8e4e36aSLuigi Rizzo #ifdef linux
1288f8e4e36aSLuigi Rizzo 	ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
1289f8e4e36aSLuigi Rizzo 
1290f8e4e36aSLuigi Rizzo 	if (*dev) {
1291f8e4e36aSLuigi Rizzo 		/* if a device name was specified, put it in the structure; otherwise,
1292f8e4e36aSLuigi Rizzo 		* the kernel will try to allocate the "next" device of the
1293f8e4e36aSLuigi Rizzo 		* specified type */
1294f8e4e36aSLuigi Rizzo 		strncpy(ifr.ifr_name, dev, IFNAMSIZ);
1295f8e4e36aSLuigi Rizzo 	}
1296f8e4e36aSLuigi Rizzo 
1297f8e4e36aSLuigi Rizzo 	/* try to create the device */
1298f8e4e36aSLuigi Rizzo 	if( (err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0 ) {
1299f8e4e36aSLuigi Rizzo 		D("failed to to a TUNSETIFF");
1300f8e4e36aSLuigi Rizzo 		close(fd);
1301f8e4e36aSLuigi Rizzo 		return err;
1302f8e4e36aSLuigi Rizzo 	}
1303f8e4e36aSLuigi Rizzo 
1304f8e4e36aSLuigi Rizzo 	/* if the operation was successful, write back the name of the
1305f8e4e36aSLuigi Rizzo 	* interface to the variable "dev", so the caller can know
1306f8e4e36aSLuigi Rizzo 	* it. Note that the caller MUST reserve space in *dev (see calling
1307f8e4e36aSLuigi Rizzo 	* code below) */
1308f8e4e36aSLuigi Rizzo 	strcpy(dev, ifr.ifr_name);
1309f8e4e36aSLuigi Rizzo 	D("new name is %s", dev);
1310f8e4e36aSLuigi Rizzo #endif /* linux */
1311f8e4e36aSLuigi Rizzo 
1312f8e4e36aSLuigi Rizzo         /* this is the special file descriptor that the caller will use to talk
1313f8e4e36aSLuigi Rizzo          * with the virtual interface */
1314f8e4e36aSLuigi Rizzo         return fd;
1315f8e4e36aSLuigi Rizzo }
131668b8534bSLuigi Rizzo 
131768b8534bSLuigi Rizzo int
131868b8534bSLuigi Rizzo main(int arc, char **argv)
131968b8534bSLuigi Rizzo {
1320f8e4e36aSLuigi Rizzo 	int i;
132168b8534bSLuigi Rizzo 
132268b8534bSLuigi Rizzo 	struct glob_arg g;
132368b8534bSLuigi Rizzo 
132468b8534bSLuigi Rizzo 	struct nmreq nmr;
132568b8534bSLuigi Rizzo 	int ch;
132668b8534bSLuigi Rizzo 	int wait_link = 2;
132768b8534bSLuigi Rizzo 	int devqueues = 1;	/* how many device queues */
132868b8534bSLuigi Rizzo 
132968b8534bSLuigi Rizzo 	bzero(&g, sizeof(g));
133068b8534bSLuigi Rizzo 
1331f8e4e36aSLuigi Rizzo 	g.main_fd = -1;
1332f8e4e36aSLuigi Rizzo 	g.td_body = receiver_body;
1333f8e4e36aSLuigi Rizzo 	g.report_interval = 1000;	/* report interval */
1334f8e4e36aSLuigi Rizzo 	g.affinity = -1;
1335f8e4e36aSLuigi Rizzo 	/* ip addresses can also be a range x.x.x.x-x.x.x.y */
1336f8e4e36aSLuigi Rizzo 	g.src_ip.name = "10.0.0.1";
1337f8e4e36aSLuigi Rizzo 	g.dst_ip.name = "10.1.0.1";
1338f8e4e36aSLuigi Rizzo 	g.dst_mac.name = "ff:ff:ff:ff:ff:ff";
1339f8e4e36aSLuigi Rizzo 	g.src_mac.name = NULL;
134068b8534bSLuigi Rizzo 	g.pkt_size = 60;
134168b8534bSLuigi Rizzo 	g.burst = 512;		// default
134268b8534bSLuigi Rizzo 	g.nthreads = 1;
134368b8534bSLuigi Rizzo 	g.cpus = 1;
1344b303f675SLuigi Rizzo 	g.forever = 1;
13451cb4c501SLuigi Rizzo 	g.tx_rate = 0;
134668b8534bSLuigi Rizzo 
134768b8534bSLuigi Rizzo 	while ( (ch = getopt(arc, argv,
1348b303f675SLuigi Rizzo 			"a:f:n:i:It:r:l:d:s:D:S:b:c:o:p:PT:w:WvR:X")) != -1) {
1349f8e4e36aSLuigi Rizzo 		struct sf *fn;
1350f8e4e36aSLuigi Rizzo 
135168b8534bSLuigi Rizzo 		switch(ch) {
135268b8534bSLuigi Rizzo 		default:
135368b8534bSLuigi Rizzo 			D("bad option %c %s", ch, optarg);
135468b8534bSLuigi Rizzo 			usage();
135568b8534bSLuigi Rizzo 			break;
1356f8e4e36aSLuigi Rizzo 
1357f8e4e36aSLuigi Rizzo 		case 'n':
1358f8e4e36aSLuigi Rizzo 			g.npackets = atoi(optarg);
1359f8e4e36aSLuigi Rizzo 			break;
1360f8e4e36aSLuigi Rizzo 
1361f8e4e36aSLuigi Rizzo 		case 'f':
1362f8e4e36aSLuigi Rizzo 			for (fn = func; fn->key; fn++) {
1363f8e4e36aSLuigi Rizzo 				if (!strcmp(fn->key, optarg))
1364f8e4e36aSLuigi Rizzo 					break;
1365f8e4e36aSLuigi Rizzo 			}
1366f8e4e36aSLuigi Rizzo 			if (fn->key)
1367f8e4e36aSLuigi Rizzo 				g.td_body = fn->f;
1368f8e4e36aSLuigi Rizzo 			else
1369f8e4e36aSLuigi Rizzo 				D("unrecognised function %s", optarg);
1370f8e4e36aSLuigi Rizzo 			break;
1371f8e4e36aSLuigi Rizzo 
1372f8e4e36aSLuigi Rizzo 		case 'o':	/* data generation options */
137399fb123fSLuigi Rizzo 			g.options = atoi(optarg);
137499fb123fSLuigi Rizzo 			break;
1375f8e4e36aSLuigi Rizzo 
1376f8e4e36aSLuigi Rizzo 		case 'a':       /* force affinity */
1377f8e4e36aSLuigi Rizzo 			g.affinity = atoi(optarg);
1378f8e4e36aSLuigi Rizzo 			break;
1379f8e4e36aSLuigi Rizzo 
138068b8534bSLuigi Rizzo 		case 'i':	/* interface */
1381f8e4e36aSLuigi Rizzo 			g.ifname = optarg;
1382f8e4e36aSLuigi Rizzo 			if (!strncmp(optarg, "tap", 3))
1383f8e4e36aSLuigi Rizzo 				g.dev_type = DEV_TAP;
1384f8e4e36aSLuigi Rizzo 			else
1385f8e4e36aSLuigi Rizzo 				g.dev_type = DEV_NETMAP;
138668b8534bSLuigi Rizzo 			break;
1387f8e4e36aSLuigi Rizzo 
1388b303f675SLuigi Rizzo 		case 'I':
1389b303f675SLuigi Rizzo 			g.options |= OPT_INDIRECT;	/* XXX use indirect buffer */
1390b303f675SLuigi Rizzo 			break;
1391b303f675SLuigi Rizzo 
1392f8e4e36aSLuigi Rizzo 		case 't':	/* send, deprecated */
1393f8e4e36aSLuigi Rizzo 			D("-t deprecated, please use -f tx -n %s", optarg);
1394f8e4e36aSLuigi Rizzo 			g.td_body = sender_body;
139568b8534bSLuigi Rizzo 			g.npackets = atoi(optarg);
139668b8534bSLuigi Rizzo 			break;
1397f8e4e36aSLuigi Rizzo 
139868b8534bSLuigi Rizzo 		case 'r':	/* receive */
1399f8e4e36aSLuigi Rizzo 			D("-r deprecated, please use -f rx -n %s", optarg);
1400f8e4e36aSLuigi Rizzo 			g.td_body = receiver_body;
140168b8534bSLuigi Rizzo 			g.npackets = atoi(optarg);
140268b8534bSLuigi Rizzo 			break;
1403f8e4e36aSLuigi Rizzo 
140468b8534bSLuigi Rizzo 		case 'l':	/* pkt_size */
140568b8534bSLuigi Rizzo 			g.pkt_size = atoi(optarg);
140668b8534bSLuigi Rizzo 			break;
1407f8e4e36aSLuigi Rizzo 
140868b8534bSLuigi Rizzo 		case 'd':
1409f8e4e36aSLuigi Rizzo 			g.dst_ip.name = optarg;
141068b8534bSLuigi Rizzo 			break;
1411f8e4e36aSLuigi Rizzo 
141268b8534bSLuigi Rizzo 		case 's':
1413f8e4e36aSLuigi Rizzo 			g.src_ip.name = optarg;
141468b8534bSLuigi Rizzo 			break;
1415f8e4e36aSLuigi Rizzo 
141668b8534bSLuigi Rizzo 		case 'T':	/* report interval */
1417f8e4e36aSLuigi Rizzo 			g.report_interval = atoi(optarg);
141868b8534bSLuigi Rizzo 			break;
1419f8e4e36aSLuigi Rizzo 
142068b8534bSLuigi Rizzo 		case 'w':
142168b8534bSLuigi Rizzo 			wait_link = atoi(optarg);
142268b8534bSLuigi Rizzo 			break;
1423f8e4e36aSLuigi Rizzo 
1424b303f675SLuigi Rizzo 		case 'W': /* XXX changed default */
1425b303f675SLuigi Rizzo 			g.forever = 0; /* do not exit rx even with no traffic */
1426f8e4e36aSLuigi Rizzo 			break;
1427f8e4e36aSLuigi Rizzo 
142868b8534bSLuigi Rizzo 		case 'b':	/* burst */
142968b8534bSLuigi Rizzo 			g.burst = atoi(optarg);
143068b8534bSLuigi Rizzo 			break;
143168b8534bSLuigi Rizzo 		case 'c':
143268b8534bSLuigi Rizzo 			g.cpus = atoi(optarg);
143368b8534bSLuigi Rizzo 			break;
143468b8534bSLuigi Rizzo 		case 'p':
143568b8534bSLuigi Rizzo 			g.nthreads = atoi(optarg);
143668b8534bSLuigi Rizzo 			break;
143768b8534bSLuigi Rizzo 
143868b8534bSLuigi Rizzo 		case 'P':
1439f8e4e36aSLuigi Rizzo 			g.dev_type = DEV_PCAP;
144068b8534bSLuigi Rizzo 			break;
144168b8534bSLuigi Rizzo 
144268b8534bSLuigi Rizzo 		case 'D': /* destination mac */
1443f8e4e36aSLuigi Rizzo 			g.dst_mac.name = optarg;
144468b8534bSLuigi Rizzo 			break;
1445f8e4e36aSLuigi Rizzo 
144668b8534bSLuigi Rizzo 		case 'S': /* source mac */
1447f8e4e36aSLuigi Rizzo 			g.src_mac.name = optarg;
144868b8534bSLuigi Rizzo 			break;
144968b8534bSLuigi Rizzo 		case 'v':
145068b8534bSLuigi Rizzo 			verbose++;
14511cb4c501SLuigi Rizzo 			break;
14521cb4c501SLuigi Rizzo 		case 'R':
14531cb4c501SLuigi Rizzo 			g.tx_rate = atoi(optarg);
14541cb4c501SLuigi Rizzo 			break;
1455b303f675SLuigi Rizzo 		case 'X':
1456b303f675SLuigi Rizzo 			g.options |= OPT_DUMP;
145768b8534bSLuigi Rizzo 		}
145868b8534bSLuigi Rizzo 	}
145968b8534bSLuigi Rizzo 
1460f8e4e36aSLuigi Rizzo 	if (g.ifname == NULL) {
146168b8534bSLuigi Rizzo 		D("missing ifname");
146268b8534bSLuigi Rizzo 		usage();
146368b8534bSLuigi Rizzo 	}
1464f8e4e36aSLuigi Rizzo 
1465f8e4e36aSLuigi Rizzo 	i = system_ncpus();
1466f8e4e36aSLuigi Rizzo 	if (g.cpus < 0 || g.cpus > i) {
1467f8e4e36aSLuigi Rizzo 		D("%d cpus is too high, have only %d cpus", g.cpus, i);
146868b8534bSLuigi Rizzo 		usage();
146968b8534bSLuigi Rizzo 	}
147068b8534bSLuigi Rizzo 	if (g.cpus == 0)
1471f8e4e36aSLuigi Rizzo 		g.cpus = i;
1472f8e4e36aSLuigi Rizzo 
147368b8534bSLuigi Rizzo 	if (g.pkt_size < 16 || g.pkt_size > 1536) {
147468b8534bSLuigi Rizzo 		D("bad pktsize %d\n", g.pkt_size);
147568b8534bSLuigi Rizzo 		usage();
147668b8534bSLuigi Rizzo 	}
147768b8534bSLuigi Rizzo 
1478f8e4e36aSLuigi Rizzo 	if (g.src_mac.name == NULL) {
1479f8e4e36aSLuigi Rizzo 		static char mybuf[20] = "00:00:00:00:00:00";
148099fb123fSLuigi Rizzo 		/* retrieve source mac address. */
1481f8e4e36aSLuigi Rizzo 		if (source_hwaddr(g.ifname, mybuf) == -1) {
148299fb123fSLuigi Rizzo 			D("Unable to retrieve source mac");
148399fb123fSLuigi Rizzo 			// continue, fail later
148499fb123fSLuigi Rizzo 		}
1485f8e4e36aSLuigi Rizzo 		g.src_mac.name = mybuf;
148699fb123fSLuigi Rizzo 	}
1487f8e4e36aSLuigi Rizzo 	/* extract address ranges */
1488f8e4e36aSLuigi Rizzo 	extract_ip_range(&g.src_ip);
1489f8e4e36aSLuigi Rizzo 	extract_ip_range(&g.dst_ip);
1490f8e4e36aSLuigi Rizzo 	extract_mac_range(&g.src_mac);
1491f8e4e36aSLuigi Rizzo 	extract_mac_range(&g.dst_mac);
149299fb123fSLuigi Rizzo 
1493f8e4e36aSLuigi Rizzo     if (g.dev_type == DEV_TAP) {
1494f8e4e36aSLuigi Rizzo 	D("want to use tap %s", g.ifname);
1495f8e4e36aSLuigi Rizzo 	g.main_fd = tap_alloc(g.ifname);
1496f8e4e36aSLuigi Rizzo 	if (g.main_fd < 0) {
1497f8e4e36aSLuigi Rizzo 		D("cannot open tap %s", g.ifname);
149899fb123fSLuigi Rizzo 		usage();
149999fb123fSLuigi Rizzo 	}
1500f8e4e36aSLuigi Rizzo     } else if (g.dev_type > DEV_NETMAP) {
1501f8e4e36aSLuigi Rizzo 	char pcap_errbuf[PCAP_ERRBUF_SIZE];
1502f8e4e36aSLuigi Rizzo 
1503f8e4e36aSLuigi Rizzo 	D("using pcap on %s", g.ifname);
1504f8e4e36aSLuigi Rizzo 	pcap_errbuf[0] = '\0'; // init the buffer
1505f8e4e36aSLuigi Rizzo 	g.p = pcap_open_live(g.ifname, 0, 1, 100, pcap_errbuf);
1506f8e4e36aSLuigi Rizzo 	if (g.p == NULL) {
1507f8e4e36aSLuigi Rizzo 		D("cannot open pcap on %s", g.ifname);
1508f8e4e36aSLuigi Rizzo 		usage();
1509f8e4e36aSLuigi Rizzo 	}
151099fb123fSLuigi Rizzo     } else {
151168b8534bSLuigi Rizzo 	bzero(&nmr, sizeof(nmr));
151264ae02c3SLuigi Rizzo 	nmr.nr_version = NETMAP_API;
151368b8534bSLuigi Rizzo 	/*
151468b8534bSLuigi Rizzo 	 * Open the netmap device to fetch the number of queues of our
151568b8534bSLuigi Rizzo 	 * interface.
151668b8534bSLuigi Rizzo 	 *
151768b8534bSLuigi Rizzo 	 * The first NIOCREGIF also detaches the card from the
151868b8534bSLuigi Rizzo 	 * protocol stack and may cause a reset of the card,
151968b8534bSLuigi Rizzo 	 * which in turn may take some time for the PHY to
152068b8534bSLuigi Rizzo 	 * reconfigure.
152168b8534bSLuigi Rizzo 	 */
1522f8e4e36aSLuigi Rizzo 	g.main_fd = open("/dev/netmap", O_RDWR);
1523f8e4e36aSLuigi Rizzo 	if (g.main_fd == -1) {
152468b8534bSLuigi Rizzo 		D("Unable to open /dev/netmap");
1525f8e4e36aSLuigi Rizzo 		// fail later
152668b8534bSLuigi Rizzo 	} else {
1527f8e4e36aSLuigi Rizzo 		if ((ioctl(g.main_fd, NIOCGINFO, &nmr)) == -1) {
152868b8534bSLuigi Rizzo 			D("Unable to get if info without name");
152968b8534bSLuigi Rizzo 		} else {
153068b8534bSLuigi Rizzo 			D("map size is %d Kb", nmr.nr_memsize >> 10);
153168b8534bSLuigi Rizzo 		}
153268b8534bSLuigi Rizzo 		bzero(&nmr, sizeof(nmr));
153364ae02c3SLuigi Rizzo 		nmr.nr_version = NETMAP_API;
1534f8e4e36aSLuigi Rizzo 		strncpy(nmr.nr_name, g.ifname, sizeof(nmr.nr_name));
1535f8e4e36aSLuigi Rizzo 		if ((ioctl(g.main_fd, NIOCGINFO, &nmr)) == -1) {
1536f8e4e36aSLuigi Rizzo 			D("Unable to get if info for %s", g.ifname);
153768b8534bSLuigi Rizzo 		}
153864ae02c3SLuigi Rizzo 		devqueues = nmr.nr_rx_rings;
153968b8534bSLuigi Rizzo 	}
154068b8534bSLuigi Rizzo 
154168b8534bSLuigi Rizzo 	/* validate provided nthreads. */
154268b8534bSLuigi Rizzo 	if (g.nthreads < 1 || g.nthreads > devqueues) {
154368b8534bSLuigi Rizzo 		D("bad nthreads %d, have %d queues", g.nthreads, devqueues);
154468b8534bSLuigi Rizzo 		// continue, fail later
154568b8534bSLuigi Rizzo 	}
154668b8534bSLuigi Rizzo 
154768b8534bSLuigi Rizzo 	/*
154868b8534bSLuigi Rizzo 	 * Map the netmap shared memory: instead of issuing mmap()
154968b8534bSLuigi Rizzo 	 * inside the body of the threads, we prefer to keep this
155068b8534bSLuigi Rizzo 	 * operation here to simplify the thread logic.
155168b8534bSLuigi Rizzo 	 */
1552f8e4e36aSLuigi Rizzo 	D("mapping %d Kbytes", nmr.nr_memsize>>10);
1553f8e4e36aSLuigi Rizzo 	g.mmap_size = nmr.nr_memsize;
1554f8e4e36aSLuigi Rizzo 	g.mmap_addr = (struct netmap_d *) mmap(0, nmr.nr_memsize,
155568b8534bSLuigi Rizzo 					    PROT_WRITE | PROT_READ,
1556f8e4e36aSLuigi Rizzo 					    MAP_SHARED, g.main_fd, 0);
1557f8e4e36aSLuigi Rizzo 	if (g.mmap_addr == MAP_FAILED) {
155868b8534bSLuigi Rizzo 		D("Unable to mmap %d KB", nmr.nr_memsize >> 10);
155968b8534bSLuigi Rizzo 		// continue, fail later
156068b8534bSLuigi Rizzo 	}
156168b8534bSLuigi Rizzo 
156268b8534bSLuigi Rizzo 	/*
156368b8534bSLuigi Rizzo 	 * Register the interface on the netmap device: from now on,
156468b8534bSLuigi Rizzo 	 * we can operate on the network interface without any
156568b8534bSLuigi Rizzo 	 * interference from the legacy network stack.
156668b8534bSLuigi Rizzo 	 *
156768b8534bSLuigi Rizzo 	 * We decide to put the first interface registration here to
156868b8534bSLuigi Rizzo 	 * give time to cards that take a long time to reset the PHY.
156968b8534bSLuigi Rizzo 	 */
157064ae02c3SLuigi Rizzo 	nmr.nr_version = NETMAP_API;
1571f8e4e36aSLuigi Rizzo 	if (ioctl(g.main_fd, NIOCREGIF, &nmr) == -1) {
1572f8e4e36aSLuigi Rizzo 		D("Unable to register interface %s", g.ifname);
157368b8534bSLuigi Rizzo 		//continue, fail later
157468b8534bSLuigi Rizzo 	}
157568b8534bSLuigi Rizzo 
157668b8534bSLuigi Rizzo 
157768b8534bSLuigi Rizzo 	/* Print some debug information. */
157868b8534bSLuigi Rizzo 	fprintf(stdout,
157968b8534bSLuigi Rizzo 		"%s %s: %d queues, %d threads and %d cpus.\n",
1580f8e4e36aSLuigi Rizzo 		(g.td_body == sender_body) ? "Sending on" : "Receiving from",
1581f8e4e36aSLuigi Rizzo 		g.ifname,
158268b8534bSLuigi Rizzo 		devqueues,
158368b8534bSLuigi Rizzo 		g.nthreads,
158468b8534bSLuigi Rizzo 		g.cpus);
1585f8e4e36aSLuigi Rizzo 	if (g.td_body == sender_body) {
158668b8534bSLuigi Rizzo 		fprintf(stdout, "%s -> %s (%s -> %s)\n",
1587f8e4e36aSLuigi Rizzo 			g.src_ip.name, g.dst_ip.name,
1588f8e4e36aSLuigi Rizzo 			g.src_mac.name, g.dst_mac.name);
158968b8534bSLuigi Rizzo 	}
159068b8534bSLuigi Rizzo 
159168b8534bSLuigi Rizzo 	/* Exit if something went wrong. */
1592f8e4e36aSLuigi Rizzo 	if (g.main_fd < 0) {
159368b8534bSLuigi Rizzo 		D("aborting");
159468b8534bSLuigi Rizzo 		usage();
159568b8534bSLuigi Rizzo 	}
159699fb123fSLuigi Rizzo     }
159768b8534bSLuigi Rizzo 
159899fb123fSLuigi Rizzo 	if (g.options) {
1599b303f675SLuigi Rizzo 		D("--- SPECIAL OPTIONS:%s%s%s%s%s\n",
160099fb123fSLuigi Rizzo 			g.options & OPT_PREFETCH ? " prefetch" : "",
160199fb123fSLuigi Rizzo 			g.options & OPT_ACCESS ? " access" : "",
160299fb123fSLuigi Rizzo 			g.options & OPT_MEMCPY ? " memcpy" : "",
1603b303f675SLuigi Rizzo 			g.options & OPT_INDIRECT ? " indirect" : "",
160499fb123fSLuigi Rizzo 			g.options & OPT_COPY ? " copy" : "");
160599fb123fSLuigi Rizzo 	}
16061cb4c501SLuigi Rizzo 
16071cb4c501SLuigi Rizzo 	if (g.tx_rate == 0) {
16081cb4c501SLuigi Rizzo 		g.tx_period.tv_sec = 0;
16091cb4c501SLuigi Rizzo 		g.tx_period.tv_nsec = 0;
16101cb4c501SLuigi Rizzo 	} else if (g.tx_rate == 1) {
16111cb4c501SLuigi Rizzo 		g.tx_period.tv_sec = 1;
16121cb4c501SLuigi Rizzo 		g.tx_period.tv_nsec = 0;
16131cb4c501SLuigi Rizzo 	} else {
16141cb4c501SLuigi Rizzo 		g.tx_period.tv_sec = 0;
16151cb4c501SLuigi Rizzo 		g.tx_period.tv_nsec = (1e9 / g.tx_rate) * g.burst;
16161cb4c501SLuigi Rizzo 		if (g.tx_period.tv_nsec > 1000000000) {
16171cb4c501SLuigi Rizzo 			g.tx_period.tv_sec = g.tx_period.tv_nsec / 1000000000;
16181cb4c501SLuigi Rizzo 			g.tx_period.tv_nsec = g.tx_period.tv_nsec % 1000000000;
16191cb4c501SLuigi Rizzo 		}
16201cb4c501SLuigi Rizzo 	}
16211cb4c501SLuigi Rizzo 	D("Sending %d packets every  %d.%09d ns",
16221cb4c501SLuigi Rizzo 			g.burst, (int)g.tx_period.tv_sec, (int)g.tx_period.tv_nsec);
162368b8534bSLuigi Rizzo 	/* Wait for PHY reset. */
162468b8534bSLuigi Rizzo 	D("Wait %d secs for phy reset", wait_link);
162568b8534bSLuigi Rizzo 	sleep(wait_link);
162668b8534bSLuigi Rizzo 	D("Ready...");
162768b8534bSLuigi Rizzo 
162868b8534bSLuigi Rizzo 	/* Install ^C handler. */
162968b8534bSLuigi Rizzo 	global_nthreads = g.nthreads;
163068b8534bSLuigi Rizzo 	signal(SIGINT, sigint_h);
163168b8534bSLuigi Rizzo 
1632f8e4e36aSLuigi Rizzo #if 0 // XXX this is not needed, i believe
1633f8e4e36aSLuigi Rizzo 	if (g.dev_type > DEV_NETMAP) {
1634f8e4e36aSLuigi Rizzo 		g.p = pcap_open_live(g.ifname, 0, 1, 100, NULL);
163599fb123fSLuigi Rizzo 		if (g.p == NULL) {
1636f8e4e36aSLuigi Rizzo 			D("cannot open pcap on %s", g.ifname);
163799fb123fSLuigi Rizzo 			usage();
163899fb123fSLuigi Rizzo 		} else
1639f8e4e36aSLuigi Rizzo 			D("using pcap %p on %s", g.p, g.ifname);
1640f8e4e36aSLuigi Rizzo 	}
1641f8e4e36aSLuigi Rizzo #endif // XXX
1642f8e4e36aSLuigi Rizzo 	start_threads(&g);
1643f8e4e36aSLuigi Rizzo 	main_thread(&g);
1644f8e4e36aSLuigi Rizzo 	return 0;
164568b8534bSLuigi Rizzo }
164668b8534bSLuigi Rizzo 
164768b8534bSLuigi Rizzo /* end of file */
1648