make: add flags and fix some warnings
[netsniff-ng.git] / src / trafgen.c
blob7cc38c5ed89abcd55950f97a5dd583cae320d776
1 /*
2 * netsniff-ng - the packet sniffing beast
3 * By Daniel Borkmann <daniel@netsniff-ng.org>
4 * Copyright 2011 - 2013 Daniel Borkmann <dborkma@tik.ee.ethz.ch>,
5 * Swiss federal institute of technology (ETH Zurich)
6 * Subject to the GPL, version 2.
8 * A high-performance network traffic generator that uses the zero-copy
9 * kernelspace TX_RING for network I/O. On comodity Gigabit hardware up
10 * to 1,488,095 pps 64 Byte pps have been achieved with 2 trafgen instances
11 * bound to different CPUs from the userspace and turned off pause frames,
12 * ask Ronald from NST (Network Security Toolkit) for more details. ;-)
13 * So, this line-rate result is the very same as pktgen from kernelspace!
15 * Who can now hold the fords when the King of the Nine Riders comes? And
16 * other armies will come. I am too late. All is lost. I tarried on the
17 * way. All is lost. Even if my errand is performed, no one will ever
18 * know. There will be no one I can tell. It will be in vain.
20 * -- The Lord of the Rings, Frodo thinking,
21 * Chapter 'The Stairs of Cirith Ungol'.
24 #include <stdio.h>
25 #include <string.h>
26 #include <getopt.h>
27 #include <ctype.h>
28 #include <stdbool.h>
29 #include <sys/socket.h>
30 #include <sys/types.h>
31 #include <sys/fsuid.h>
32 #include <sys/stat.h>
33 #include <sys/time.h>
34 #include <sys/wait.h>
35 #include <sys/mman.h>
36 #include <net/ethernet.h>
37 #include <netinet/in.h>
38 #include <netinet/ip.h>
39 #include <linux/icmp.h>
40 #include <arpa/inet.h>
41 #include <signal.h>
42 #include <stdint.h>
43 #include <stdlib.h>
44 #include <fcntl.h>
45 #include <time.h>
46 #include <poll.h>
47 #include <netdb.h>
48 #include <math.h>
50 #include "xmalloc.h"
51 #include "die.h"
52 #include "mac80211.h"
53 #include "xutils.h"
54 #include "xio.h"
55 #include "built_in.h"
56 #include "trafgen_conf.h"
57 #include "tprintf.h"
58 #include "ring_tx.h"
60 struct ctx {
61 bool rand, rfraw, jumbo_support, verbose, smoke_test;
62 unsigned long kpull, num, gap, reserve_size, cpus;
63 struct sockaddr_in dest;
64 char *device, *device_trans, *rhost;
67 struct cpu_stats {
68 unsigned long long tx_packets, tx_bytes;
69 unsigned long tv_sec, tv_usec;
70 unsigned int state;
73 #define CPU_STATS_STATE_CFG 1
74 #define CPU_STATS_STATE_RES 2
76 sig_atomic_t sigint = 0;
78 struct packet *packets = NULL;
79 size_t plen = 0;
81 struct packet_dyn *packet_dyn = NULL;
82 size_t dlen = 0;
84 static const char *short_options = "d:c:n:t:vJhS:rk:i:o:VRsP:";
85 static const struct option long_options[] = {
86 {"dev", required_argument, NULL, 'd'},
87 {"out", required_argument, NULL, 'o'},
88 {"in", required_argument, NULL, 'i'},
89 {"conf", required_argument, NULL, 'c'},
90 {"num", required_argument, NULL, 'n'},
91 {"gap", required_argument, NULL, 't'},
92 {"cpus", required_argument, NULL, 'P'},
93 {"ring-size", required_argument, NULL, 'S'},
94 {"kernel-pull", required_argument, NULL, 'k'},
95 {"smoke-test", required_argument, NULL, 's'},
96 {"jumbo-support", no_argument, NULL, 'J'},
97 {"rfraw", no_argument, NULL, 'R'},
98 {"rand", no_argument, NULL, 'r'},
99 {"verbose", no_argument, NULL, 'V'},
100 {"version", no_argument, NULL, 'v'},
101 {"help", no_argument, NULL, 'h'},
102 {NULL, 0, NULL, 0}
105 static int sock;
107 static struct itimerval itimer;
109 static unsigned long interval = TX_KERNEL_PULL_INT;
111 static struct cpu_stats *stats;
113 #define set_system_socket_memory(vals) \
114 do { \
115 if ((vals[0] = get_system_socket_mem(sock_rmem_max)) < SMEM_SUG_MAX) \
116 set_system_socket_mem(sock_rmem_max, SMEM_SUG_MAX); \
117 if ((vals[1] = get_system_socket_mem(sock_rmem_def)) < SMEM_SUG_DEF) \
118 set_system_socket_mem(sock_rmem_def, SMEM_SUG_DEF); \
119 if ((vals[2] = get_system_socket_mem(sock_wmem_max)) < SMEM_SUG_MAX) \
120 set_system_socket_mem(sock_wmem_max, SMEM_SUG_MAX); \
121 if ((vals[3] = get_system_socket_mem(sock_wmem_def)) < SMEM_SUG_DEF) \
122 set_system_socket_mem(sock_wmem_def, SMEM_SUG_DEF); \
123 } while (0)
125 #define reset_system_socket_memory(vals) \
126 do { \
127 set_system_socket_mem(sock_rmem_max, vals[0]); \
128 set_system_socket_mem(sock_rmem_def, vals[1]); \
129 set_system_socket_mem(sock_wmem_max, vals[2]); \
130 set_system_socket_mem(sock_wmem_def, vals[3]); \
131 } while (0)
133 #ifndef ICMP_FILTER
134 # define ICMP_FILTER 1
136 struct icmp_filter {
137 __u32 data;
139 #endif
141 static void signal_handler(int number)
143 switch (number) {
144 case SIGINT:
145 sigint = 1;
146 case SIGHUP:
147 default:
148 break;
152 static void timer_elapsed(int number)
154 itimer.it_interval.tv_sec = 0;
155 itimer.it_interval.tv_usec = interval;
157 itimer.it_value.tv_sec = 0;
158 itimer.it_value.tv_usec = interval;
160 pull_and_flush_tx_ring(sock);
161 setitimer(ITIMER_REAL, &itimer, NULL);
164 static void header(void)
166 printf("%s%s%s\n", colorize_start(bold), "trafgen " VERSION_STRING, colorize_end());
169 static void help(void)
171 printf("\ntrafgen %s, multithreaded zero-copy network packet generator\n", VERSION_STRING);
172 puts("http://www.netsniff-ng.org\n\n"
173 "Usage: trafgen [options]\n"
174 "Options:\n"
175 " -o|-d|--out|--dev <netdev> Networking Device i.e., eth0\n"
176 " -i|-c|--in|--conf <cfg-file> Packet configuration file\n"
177 " -J|--jumbo-support Support 64KB Super Jumbo Frames (def: 2048B)\n"
178 " -R|--rfraw Inject raw 802.11 frames\n"
179 " -s|--smoke-test <ipv4-receiver> Test if machine survived packet\n"
180 " -n|--num <uint> Number of packets until exit (def: 0)\n"
181 " -r|--rand Randomize packet selection (def: round robin)\n"
182 " -P|--cpus <uint> Specify number of forks(<= CPUs) (def: #CPUs)\n"
183 " -t|--gap <uint> Interpacket gap in us (approx)\n"
184 " -S|--ring-size <size> Manually set mmap size (KB/MB/GB): e.g.\'10MB\'\n"
185 " -k|--kernel-pull <uint> Kernel batch interval in us (def: 10us)\n"
186 " -V|--verbose Be more verbose\n"
187 " -v|--version Show version\n"
188 " -h|--help Guess what?!\n\n"
189 "Examples:\n"
190 " See trafgen.txf for configuration file examples.\n"
191 " trafgen --dev eth0 --conf trafgen.txf\n"
192 " trafgen --dev eth0 --conf trafgen.txf --smoke-test 10.0.0.1\n"
193 " trafgen --dev wlan0 --rfraw --conf beacon-test.txf -V --cpus 2\n"
194 " trafgen --dev eth0 --conf trafgen.txf --rand --gap 1000\n"
195 " trafgen --dev eth0 --conf trafgen.txf --rand --num 1400000 -k1000\n\n"
196 "Packet config examples:\n"
197 " Run packet on all CPUs: { fill(0xff, 64) }\n"
198 " Run packet only on CPU1: cpu(1): { fill(0xff, 64) }\n"
199 " Run packet only on CPU1-2: cpu(1:2): { fill(0xff, 64) }\n"
200 "Note:\n"
201 " Smoke test example: machine A, 10.0.0.2 (trafgen) is directly\n"
202 " connected to machine B (test kernel), 10.0.0.1. If ICMP reply fails\n"
203 " we assume the kernel crashed, thus we print the packet and quit.\n\n"
204 " This tool is targeted for network developers! You should\n"
205 " be aware of what you are doing and what these options above\n"
206 " mean! Only use this tool in an isolated LAN that you own!\n\n"
207 "Please report bugs to <bugs@netsniff-ng.org>\n"
208 "Copyright (C) 2011-2013 Daniel Borkmann <dborkma@tik.ee.ethz.ch>,\n"
209 "Swiss federal institute of technology (ETH Zurich)\n"
210 "License: GNU GPL version 2.0\n"
211 "This is free software: you are free to change and redistribute it.\n"
212 "There is NO WARRANTY, to the extent permitted by law.\n");
213 die();
216 static void version(void)
218 printf("\ntrafgen %s, multithreaded zero-copy network packet generator\n", VERSION_STRING);
219 puts("http://www.netsniff-ng.org\n\n"
220 "Please report bugs to <bugs@netsniff-ng.org>\n"
221 "Copyright (C) 2011-2013 Daniel Borkmann <dborkma@tik.ee.ethz.ch>,\n"
222 "Swiss federal institute of technology (ETH Zurich)\n"
223 "License: GNU GPL version 2.0\n"
224 "This is free software: you are free to change and redistribute it.\n"
225 "There is NO WARRANTY, to the extent permitted by law.\n");
226 die();
229 static inline void apply_counter(int counter_id)
231 int j;
232 size_t counter_max = packet_dyn[counter_id].clen;
234 for (j = 0; j < counter_max; ++j) {
235 uint8_t val;
236 struct counter *counter;
238 counter = &packet_dyn[counter_id].cnt[j];
239 val = counter->val - counter->min;
241 switch (counter->type) {
242 case TYPE_INC:
243 val = (val + counter->inc) % (counter->max - counter->min + 1);
244 break;
245 case TYPE_DEC:
246 val = (val - counter->inc) % (counter->min - counter->max + 1);
247 break;
248 default:
249 bug();
252 counter->val = val + counter->min;
253 packets[counter_id].payload[counter->off] = val;
257 static inline void apply_randomizer(int rand_id)
259 int j;
260 size_t rand_max = packet_dyn[rand_id].rlen;
262 for (j = 0; j < rand_max; ++j) {
263 uint8_t val;
264 struct randomizer *randomizer;
266 val = (uint8_t) rand();
268 randomizer = &packet_dyn[rand_id].rnd[j];
269 randomizer->val = val;
271 packets[rand_id].payload[randomizer->off] = val;
275 static struct cpu_stats *setup_shared_var(unsigned long cpus)
277 int fd;
278 char zbuff[cpus * sizeof(struct cpu_stats)];
279 struct cpu_stats *buff;
281 memset(zbuff, 0, sizeof(zbuff));
283 fd = creat(".tmp_mmap", S_IRUSR | S_IWUSR);
284 bug_on(fd < 0);
285 close(fd);
287 fd = open_or_die_m(".tmp_mmap", O_RDWR | O_CREAT | O_TRUNC,
288 S_IRUSR | S_IWUSR);
289 write_or_die(fd, zbuff, sizeof(zbuff));
291 buff = (void *) mmap(0, sizeof(zbuff), PROT_READ | PROT_WRITE,
292 MAP_SHARED, fd, 0);
293 if (buff == (void *) -1)
294 panic("Cannot setup shared variable!\n");
296 close(fd);
297 unlink(".tmp_mmap");
299 memset(buff, 0, sizeof(zbuff));
301 return buff;
304 static void destroy_shared_var(void *buff, unsigned long cpus)
306 munmap(buff, cpus * sizeof(struct cpu_stats));
309 static void dump_trafgen_snippet(uint8_t *payload, size_t len)
311 int i;
313 printf("{");
314 for (i = 0; i < len; ++i) {
315 if (i % 15 == 0)
316 printf("\n ");
317 printf("0x%02x, ", payload[i]);
319 printf("\n}\n");
320 fflush(stdout);
323 static inline unsigned short csum(unsigned short *buf, int nwords)
325 unsigned long sum;
327 for (sum = 0; nwords > 0; nwords--)
328 sum += *buf++;
329 sum = (sum >> 16) + (sum & 0xffff);
330 sum += (sum >> 16);
332 return ~sum;
335 static int xmit_smoke_setup(struct ctx *ctx)
337 int icmp_sock, ret, ttl = 64;
338 struct icmp_filter filter;
340 icmp_sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
341 if (icmp_sock < 0)
342 panic("Cannot get a ICMP socket: %s!\n", strerror(errno));
344 filter.data = ~(1 << ICMP_ECHOREPLY);
346 ret = setsockopt(icmp_sock, SOL_RAW, ICMP_FILTER, &filter, sizeof(filter));
347 if (ret < 0)
348 panic("Cannot install filter!\n");
350 ret = setsockopt(icmp_sock, SOL_IP, IP_TTL, &ttl, sizeof(ttl));
351 if (ret < 0)
352 panic("Cannot set TTL!\n");
354 memset(&ctx->dest, 0, sizeof(ctx->dest));
355 ctx->dest.sin_family = AF_INET;
356 ctx->dest.sin_port = 0;
358 ret = inet_aton(ctx->rhost, &ctx->dest.sin_addr);
359 if (ret < 0)
360 panic("Cannot resolv address!\n");
362 return icmp_sock;
365 static int xmit_smoke_probe(int icmp_sock, struct ctx *ctx)
367 int ret, i, probes = 5;
368 short ident, cnt = 1;
369 uint8_t outpack[512], *data;
370 struct icmphdr *icmp;
371 struct iphdr *ip;
372 size_t len = sizeof(*icmp) + 56;
373 struct sockaddr_in from;
374 socklen_t from_len;
375 struct pollfd fds = {
376 .fd = icmp_sock,
377 .events = POLLIN,
380 while (probes-- > 0) {
381 ident = htons((short) rand());
383 memset(outpack, 0, sizeof(outpack));
384 icmp = (void *) outpack;
385 icmp->type = ICMP_ECHO;
386 icmp->code = 0;
387 icmp->checksum = 0;
388 icmp->un.echo.id = ident;
389 icmp->un.echo.sequence = htons(cnt++);
391 data = ((uint8_t *) outpack + sizeof(*icmp));
392 for (i = 0; i < 56; ++i)
393 data[i] = (uint8_t) rand();
395 icmp->checksum = csum((unsigned short *) outpack,
396 len / sizeof(unsigned short));
398 ret = sendto(icmp_sock, outpack, len, MSG_DONTWAIT,
399 (struct sockaddr *) &ctx->dest, sizeof(ctx->dest));
400 if (unlikely(ret != len))
401 panic("Cannot send out probe: %s!\n", strerror(errno));
403 ret = poll(&fds, 1, 500);
404 if (ret < 0)
405 panic("Poll failed!\n");
407 if (fds.revents & POLLIN) {
408 ret = recvfrom(icmp_sock, outpack, sizeof(outpack), 0,
409 (struct sockaddr *) &from, &from_len);
410 if (unlikely(ret <= 0))
411 panic("Probe receive failed!\n");
412 if (unlikely(from_len != sizeof(ctx->dest)))
413 continue;
414 if (unlikely(memcmp(&from, &ctx->dest, sizeof(ctx->dest))))
415 continue;
416 if (unlikely(ret < sizeof(*ip) + sizeof(*icmp)))
417 continue;
418 ip = (void *) outpack;
419 if (unlikely(ip->ihl * 4 + sizeof(*icmp) > ret))
420 continue;
421 icmp = (void *) outpack + ip->ihl * 4;
422 if (unlikely(icmp->un.echo.id != ident))
423 continue;
425 return 0;
429 return -1;
432 static void xmit_slowpath_or_die(struct ctx *ctx, int cpu)
434 int ret, icmp_sock = -1;
435 unsigned long num = 1, i = 0;
436 struct timeval start, end, diff;
437 unsigned long long tx_bytes = 0, tx_packets = 0;
438 struct sockaddr_ll saddr = {
439 .sll_family = PF_PACKET,
440 .sll_halen = ETH_ALEN,
441 .sll_ifindex = device_ifindex(ctx->device),
444 if (ctx->num > 0)
445 num = ctx->num;
447 if (ctx->smoke_test)
448 icmp_sock = xmit_smoke_setup(ctx);
450 bug_on(gettimeofday(&start, NULL));
452 while (likely(sigint == 0) && likely(num > 0)) {
453 apply_counter(i);
454 apply_randomizer(i);
455 retry:
456 ret = sendto(sock, packets[i].payload, packets[i].len, 0,
457 (struct sockaddr *) &saddr, sizeof(saddr));
458 if (unlikely(ret < 0)) {
459 if (errno == ENOBUFS) {
460 sched_yield();
461 goto retry;
464 panic("Sendto error: %s!\n", strerror(errno));
467 tx_bytes += packets[i].len;
468 tx_packets++;
470 if (ctx->smoke_test) {
471 ret = xmit_smoke_probe(icmp_sock, ctx);
472 if (unlikely(ret < 0)) {
473 printf("%sSmoke test alert:%s\n", colorize_start(bold), colorize_end());
474 printf(" Remote host seems to be unresponsive to ICMP pings!\n");
475 printf(" Last instance was packet%lu, trafgen snippet:\n\n", i);
477 dump_trafgen_snippet(packets[i].payload, packets[i].len);
478 break;
482 if (!ctx->rand) {
483 i++;
484 if (i >= plen)
485 i = 0;
486 } else
487 i = rand() % plen;
489 if (ctx->num > 0)
490 num--;
492 if (ctx->gap > 0)
493 usleep(ctx->gap);
496 bug_on(gettimeofday(&end, NULL));
497 diff = tv_subtract(end, start);
499 if (ctx->smoke_test)
500 close(icmp_sock);
502 stats[cpu].tx_packets = tx_packets;
503 stats[cpu].tx_bytes = tx_bytes;
504 stats[cpu].tv_sec = diff.tv_sec;
505 stats[cpu].tv_usec = diff.tv_usec;
506 stats[cpu].state = CPU_STATS_STATE_RES;
509 static void xmit_fastpath_or_die(struct ctx *ctx, int cpu)
511 int ifindex = device_ifindex(ctx->device);
512 uint8_t *out = NULL;
513 unsigned int it = 0;
514 unsigned long num = 1, i = 0, size;
515 struct ring tx_ring;
516 struct frame_map *hdr;
517 struct timeval start, end, diff;
518 unsigned long long tx_bytes = 0, tx_packets = 0;
520 fmemset(&tx_ring, 0, sizeof(tx_ring));
522 size = ring_size(ctx->device, ctx->reserve_size);
524 set_sock_prio(sock, 512);
525 set_packet_loss_discard(sock);
527 setup_tx_ring_layout(sock, &tx_ring, size, ctx->jumbo_support);
528 create_tx_ring(sock, &tx_ring, ctx->verbose);
529 mmap_tx_ring(sock, &tx_ring);
530 alloc_tx_ring_frames(&tx_ring);
531 bind_tx_ring(sock, &tx_ring, ifindex);
533 if (ctx->kpull)
534 interval = ctx->kpull;
535 if (ctx->num > 0)
536 num = ctx->num;
538 itimer.it_interval.tv_sec = 0;
539 itimer.it_interval.tv_usec = interval;
541 itimer.it_value.tv_sec = 0;
542 itimer.it_value.tv_usec = interval;
544 setitimer(ITIMER_REAL, &itimer, NULL);
546 bug_on(gettimeofday(&start, NULL));
548 while (likely(sigint == 0) && likely(num > 0)) {
549 while (user_may_pull_from_tx(tx_ring.frames[it].iov_base) && likely(num > 0)) {
550 hdr = tx_ring.frames[it].iov_base;
552 /* Kernel assumes: data = ph.raw + po->tp_hdrlen -
553 * sizeof(struct sockaddr_ll); */
554 out = ((uint8_t *) hdr) + TPACKET2_HDRLEN - sizeof(struct sockaddr_ll);
556 hdr->tp_h.tp_snaplen = packets[i].len;
557 hdr->tp_h.tp_len = packets[i].len;
559 apply_counter(i);
560 apply_randomizer(i);
562 fmemcpy(out, packets[i].payload, packets[i].len);
564 tx_bytes += packets[i].len;
565 tx_packets++;
567 if (!ctx->rand) {
568 i++;
569 if (i >= plen)
570 i = 0;
571 } else
572 i = rand() % plen;
574 kernel_may_pull_from_tx(&hdr->tp_h);
576 it++;
577 if (it >= tx_ring.layout.tp_frame_nr)
578 it = 0;
580 if (ctx->num > 0)
581 num--;
583 if (unlikely(sigint == 1))
584 break;
588 bug_on(gettimeofday(&end, NULL));
589 diff = tv_subtract(end, start);
591 destroy_tx_ring(sock, &tx_ring);
593 stats[cpu].tx_packets = tx_packets;
594 stats[cpu].tx_bytes = tx_bytes;
595 stats[cpu].tv_sec = diff.tv_sec;
596 stats[cpu].tv_usec = diff.tv_usec;
597 stats[cpu].state = CPU_STATS_STATE_RES;
600 static int xmit_packet_precheck(struct ctx *ctx, int cpu)
602 int i;
603 unsigned long plen_total;
604 size_t mtu, total_len = 0;
606 bug_on(plen != dlen);
608 for (i = 0; i < plen; ++i)
609 total_len += packets[i].len;
611 stats[cpu].tx_packets = plen;
612 stats[cpu].tx_bytes = total_len;
613 stats[cpu].state = CPU_STATS_STATE_CFG;
615 for (i = 0, plen_total = plen; i < ctx->cpus; i++) {
616 if (i == cpu)
617 continue;
618 while (stats[i].state != CPU_STATS_STATE_CFG)
619 sleep(0);
620 plen_total += stats[i].tx_packets;
623 if (ctx->num > 0)
624 ctx->num = (unsigned long) round((1.0 * plen / plen_total) * ctx->num);
626 for (mtu = device_mtu(ctx->device), i = 0; i < plen; ++i) {
627 if (packets[i].len > mtu + 14)
628 panic("Device MTU < than packet%d's size!\n", i);
629 if (packets[i].len <= 14)
630 panic("Packet%d's size too short!\n", i);
633 if (plen == 0) {
634 memset(&stats[cpu], 0, sizeof(stats[cpu]));
635 stats[cpu].state = CPU_STATS_STATE_RES;
636 return -1;
639 return 0;
642 static void main_loop(struct ctx *ctx, char *confname, bool slow, int cpu)
644 compile_packets(confname, ctx->verbose, cpu);
645 if (xmit_packet_precheck(ctx, cpu) < 0)
646 return;
648 if (cpu == 0) {
649 int i;
650 size_t total_len = 0, total_pkts = 0;
652 for (i = 0; i < ctx->cpus; ++i) {
653 total_len += stats[i].tx_bytes;
654 total_pkts += stats[i].tx_packets;
657 printf("%6zu packets to schedule\n", total_pkts);
658 printf("%6zu bytes in total\n", total_len);
659 printf("Running! Hang up with ^C!\n\n");
660 fflush(stdout);
663 sock = pf_socket();
665 if (slow)
666 xmit_slowpath_or_die(ctx, cpu);
667 else
668 xmit_fastpath_or_die(ctx, cpu);
670 close(sock);
672 cleanup_packets();
675 int main(int argc, char **argv)
677 bool slow = false;
678 int c, opt_index, i, j, vals[4] = {0}, irq;
679 char *confname = NULL, *ptr;
680 unsigned long cpus_tmp, num_orig = 0;
681 unsigned long long tx_packets, tx_bytes;
682 struct ctx ctx;
684 setfsuid(getuid());
685 setfsgid(getgid());
687 srand(time(NULL));
688 fmemset(&ctx, 0, sizeof(ctx));
689 ctx.cpus = get_number_cpus_online();
691 while ((c = getopt_long(argc, argv, short_options, long_options,
692 &opt_index)) != EOF) {
693 switch (c) {
694 case 'h':
695 help();
696 break;
697 case 'v':
698 version();
699 break;
700 case 'V':
701 ctx.verbose = true;
702 break;
703 case 'P':
704 cpus_tmp = strtoul(optarg, NULL, 0);
705 if (cpus_tmp > 0 && cpus_tmp < ctx.cpus)
706 ctx.cpus = cpus_tmp;
707 break;
708 case 'd':
709 case 'o':
710 ctx.device = xstrndup(optarg, IFNAMSIZ);
711 break;
712 case 'r':
713 ctx.rand = true;
714 break;
715 case 's':
716 slow = true;
717 ctx.cpus = 1;
718 ctx.smoke_test = true;
719 ctx.rhost = xstrdup(optarg);
720 break;
721 case 'R':
722 ctx.rfraw = true;
723 break;
724 case 'J':
725 ctx.jumbo_support = true;
726 break;
727 case 'c':
728 case 'i':
729 confname = xstrdup(optarg);
730 break;
731 case 'k':
732 ctx.kpull = strtoul(optarg, NULL, 0);
733 break;
734 case 'n':
735 num_orig = ctx.num = strtoul(optarg, NULL, 0);
736 break;
737 case 't':
738 slow = true;
739 ctx.gap = strtoul(optarg, NULL, 0);
740 if (ctx.gap > 0)
741 /* Fall back to single core to have correct timing */
742 ctx.cpus = 1;
743 break;
744 case 'S':
745 ptr = optarg;
746 ctx.reserve_size = 0;
748 for (j = i = strlen(optarg); i > 0; --i) {
749 if (!isdigit(optarg[j - i]))
750 break;
751 ptr++;
754 if (!strncmp(ptr, "KB", strlen("KB")))
755 ctx.reserve_size = 1 << 10;
756 else if (!strncmp(ptr, "MB", strlen("MB")))
757 ctx.reserve_size = 1 << 20;
758 else if (!strncmp(ptr, "GB", strlen("GB")))
759 ctx.reserve_size = 1 << 30;
760 else
761 panic("Syntax error in ring size param!\n");
762 *ptr = 0;
764 ctx.reserve_size *= strtol(optarg, NULL, 0);
765 break;
766 case '?':
767 switch (optopt) {
768 case 'd':
769 case 'c':
770 case 'n':
771 case 'S':
772 case 's':
773 case 'P':
774 case 'o':
775 case 'i':
776 case 'k':
777 case 't':
778 panic("Option -%c requires an argument!\n",
779 optopt);
780 default:
781 if (isprint(optopt))
782 whine("Unknown option character "
783 "`0x%X\'!\n", optopt);
784 die();
786 default:
787 break;
791 if (argc < 5)
792 help();
793 if (ctx.device == NULL)
794 panic("No networking device given!\n");
795 if (confname == NULL)
796 panic("No configuration file given!\n");
797 if (device_mtu(ctx.device) == 0)
798 panic("This is no networking device!\n");
799 if (!ctx.rfraw && device_up_and_running(ctx.device) == 0)
800 panic("Networking device not running!\n");
802 register_signal(SIGINT, signal_handler);
803 register_signal(SIGHUP, signal_handler);
804 register_signal_f(SIGALRM, timer_elapsed, SA_SIGINFO);
806 header();
808 set_system_socket_memory(vals);
810 if (ctx.rfraw) {
811 ctx.device_trans = xstrdup(ctx.device);
812 xfree(ctx.device);
814 enter_rfmon_mac80211(ctx.device_trans, &ctx.device);
815 sleep(0);
818 irq = device_irq_number(ctx.device);
819 device_reset_irq_affinity(irq);
821 if (ctx.num > 0 && ctx.num <= ctx.cpus)
822 ctx.cpus = 1;
824 stats = setup_shared_var(ctx.cpus);
826 for (i = 0; i < ctx.cpus; i++) {
827 pid_t pid = fork();
829 switch (pid) {
830 case 0:
831 cpu_affinity(i);
832 main_loop(&ctx, confname, slow, i);
834 goto thread_out;
835 case -1:
836 panic("Cannot fork processes!\n");
840 for (i = 0; i < ctx.cpus; i++) {
841 int status;
843 wait(&status);
846 if (ctx.rfraw)
847 leave_rfmon_mac80211(ctx.device_trans, ctx.device);
849 reset_system_socket_memory(vals);
851 for (i = 0, tx_packets = tx_bytes = 0; i < ctx.cpus; i++) {
852 while (stats[i].state != CPU_STATS_STATE_RES)
853 sleep(0);
854 tx_packets += stats[i].tx_packets;
855 tx_bytes += stats[i].tx_bytes;
858 if (num_orig > 0 && sigint == 0)
859 bug_on(num_orig != tx_packets);
861 fflush(stdout);
862 printf("\n");
863 printf("\r%12llu packets outgoing\n", tx_packets);
864 printf("\r%12llu bytes outgoing\n", tx_bytes);
865 for (i = 0; i < ctx.cpus; i++)
866 printf("\r%12lu sec, %lu usec on CPU%d (%llu packets)\n",
867 stats[i].tv_sec, stats[i].tv_usec, i,
868 stats[i].tx_packets);
870 thread_out:
871 destroy_shared_var(stats, ctx.cpus);
873 free(ctx.device);
874 free(ctx.device_trans);
875 free(ctx.rhost);
876 free(confname);
878 return 0;