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