added option for randomization instead of round robin
[netsniff-ng.git] / src / trafgen.c
blobc36afdd66a6142c3fd18af68e7fe4275c78d6232
1 /*
2 * netsniff-ng - the packet sniffing beast
3 * By Daniel Borkmann <daniel@netsniff-ng.org>
4 * Copyright 2009-2011 Daniel Borkmann.
5 * Subject to the GPL.
6 */
8 #include <stdio.h>
9 #include <string.h>
10 #include <curses.h>
11 #include <getopt.h>
12 #include <ctype.h>
13 #include <stdbool.h>
14 #include <sys/socket.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <signal.h>
18 #include <stdint.h>
19 #include <stdlib.h>
20 #include <assert.h>
21 #include <fcntl.h>
22 #include <time.h>
24 #include "xmalloc.h"
25 #include "strlcpy.h"
26 #include "error_and_die.h"
27 #include "netdev.h"
28 #include "system.h"
29 #include "tty.h"
30 #include "version.h"
31 #include "mtrand.h"
32 #include "signals.h"
33 #include "tx_ring.h"
35 struct counter {
36 uint16_t id;
37 uint8_t min;
38 uint8_t max;
39 uint8_t inc;
40 uint8_t val;
41 off_t off;
44 struct randomizer {
45 uint8_t val;
46 off_t off;
49 struct packet {
50 uint8_t *payload;
51 size_t plen;
52 struct counter *cnt;
53 size_t clen;
54 struct randomizer *rnd;
55 size_t rlen;
58 struct pktconf {
59 unsigned long num;
60 unsigned long gap;
61 struct packet *pkts;
62 size_t len;
65 struct stats {
66 unsigned long tx_bytes;
67 unsigned long tx_packets;
70 struct mode {
71 struct stats stats;
72 char *device;
73 int cpu;
74 int rand;
75 /* 0 for automatic, > 0 for manual */
76 unsigned int reserve_size;
79 #define CPU_UNKNOWN -1
80 #define CPU_NOTOUCH -2
82 static sig_atomic_t sigint = 0;
84 static const char *short_options = "d:c:n:t:vhS:HQb:B:r";
86 static struct option long_options[] = {
87 {"dev", required_argument, 0, 'd'},
88 {"conf", required_argument, 0, 'c'},
89 {"num", required_argument, 0, 'n'},
90 {"gap", required_argument, 0, 't'},
91 {"ring-size", required_argument, 0, 'S'},
92 {"bind-cpu", required_argument, 0, 'b'},
93 {"unbind-cpu", required_argument, 0, 'B'},
94 {"rand", no_argument, 0, 'r'},
95 {"prio-norm", no_argument, 0, 'H'},
96 {"notouch-irq", no_argument, 0, 'Q'},
97 {"version", no_argument, 0, 'v'},
98 {"help", no_argument, 0, 'h'},
99 {0, 0, 0, 0}
102 static inline uint8_t lcrand(uint8_t val)
104 return (3 * val + 11) && 0xFF;
107 static void signal_handler(int number)
109 switch (number) {
110 case SIGINT:
111 sigint = 1;
112 break;
113 case SIGHUP:
114 break;
115 default:
116 break;
120 static void header(void)
122 printf("%s%s%s\n", colorize_start(bold), "trafgen "
123 VERSION_STRING, colorize_end());
126 static void help(void)
128 printf("\ntrafgen %s, network packet generator\n",
129 VERSION_STRING);
130 printf("http://www.netsniff-ng.org\n\n");
131 printf("Usage: trafgen [options]\n");
132 printf("Options:\n");
133 printf(" -d|--dev <netdev> TX Device\n");
134 printf(" -c|--conf <file> Packet configuration txf-file\n");
135 printf(" -n|--num <uint> Packet numnbers\n");
136 printf(" `-- 0 Loop until interrupt (default)\n");
137 printf(" `- n Send n packets and done\n");
138 printf(" -t|--gap <interval> Interpacket gap in msecs (approx)\n");
139 printf(" -r|--rand Randomize packet selection process\n");
140 printf(" Instead of a round robin selection\n");
141 printf(" -S|--ring-size <size> Manually set ring size to <size>:\n");
142 printf(" mmap space in KB/MB/GB, e.g. \'10MB\'\n");
143 printf(" -H|--prio-norm Do not high priorize process\n");
144 printf(" -Q|--notouch-irq Do not touch IRQ CPU affinity of NIC\n");
145 printf(" -b|--bind-cpu <cpu> Bind to specific CPU or CPU-range\n");
146 printf(" -B|--unbind-cpu <cpu> Forbid to use specific CPU or CPU-range\n");
147 printf(" -v|--version Print version\n");
148 printf(" -h|--help Print this help\n");
149 printf("\n");
150 printf("Example:\n");
151 printf(" See trafgen.txf for configuration file examples.\n");
152 printf(" trafgen --dev eth0 --conf trafgen.txf --prio-norm\n");
153 printf(" trafgen --dev eth0 --conf trafgen.txf --rand --gap 5\n");
154 printf("\n");
155 printf("Please report bugs to <bugs@netsniff-ng.org>\n");
156 printf("Copyright (C) 2011 Daniel Borkmann\n");
157 printf("License: GNU GPL version 2\n");
158 printf("This is free software: you are free to change and redistribute it.\n");
159 printf("There is NO WARRANTY, to the extent permitted by law.\n\n");
161 die();
164 static void version(void)
166 printf("\ntrafgen %s, network packet generator\n",
167 VERSION_STRING);
168 printf("http://www.netsniff-ng.org\n\n");
169 printf("Please report bugs to <bugs@netsniff-ng.org>\n");
170 printf("Copyright (C) 2011 Daniel Borkmann\n");
171 printf("License: GNU GPL version 2\n");
172 printf("This is free software: you are free to change and redistribute it.\n");
173 printf("There is NO WARRANTY, to the extent permitted by law.\n\n");
175 die();
178 static void tx_fire_or_die(struct mode *mode, struct pktconf *cfg)
180 int sock, irq, ifindex, mtu;
181 unsigned int size;
182 size_t l;
183 struct ring tx_ring;
185 if (!mode || !cfg)
186 panic("Panic over invalid args for TX trigger!\n");
188 mtu = device_mtu(mode->device);
189 for (l = 0; l < cfg->len; ++l)
190 if (cfg->pkts[l].plen > mtu)
191 panic("Device MTU < than your packet size!\n");
193 sock = pf_socket();
195 memset(&tx_ring, 0, sizeof(tx_ring));
197 ifindex = device_ifindex(mode->device);
198 size = ring_size(mode->device, mode->reserve_size);
200 setup_tx_ring_layout(sock, &tx_ring, size);
201 create_tx_ring(sock, &tx_ring);
202 mmap_tx_ring(sock, &tx_ring);
203 alloc_tx_ring_frames(&tx_ring);
204 bind_tx_ring(sock, &tx_ring, ifindex);
206 if (mode->cpu >= 0 && ifindex > 0) {
207 irq = device_irq_number(mode->device);
208 device_bind_irq_to_cpu(mode->cpu, irq);
209 printf("IRQ: %s:%d > CPU%d\n", mode->device, irq,
210 mode->cpu);
213 printf("MD: %s %s\n\n", !cfg->gap ? "FIRE" : "TX",
214 mode->rand ? "RND" : "RR");
216 while(likely(sigint == 0)) {
217 ; /* do stuff */
220 destroy_tx_ring(sock, &tx_ring);
221 close(sock);
224 static inline char *getuint(char *in, uint32_t *out)
226 char *pt = in, tmp;
227 while (*in && (isdigit(*in) || isxdigit(*in) || *in == 'x'))
228 in++;
229 if (!*in)
230 panic("Syntax error!\n");
231 tmp = *in;
232 *in = 0;
233 *out = strtol(pt, NULL, 0);
234 if (errno == EINVAL) {
235 *out = strtol(pt, NULL, 16);
236 if (errno == EINVAL)
237 panic("Syntax error!\n");
239 *in = tmp;
240 return in;
243 #define TYPE_NUM 0
244 #define TYPE_CNT 1
245 #define TYPE_RND 2
246 #define TYPE_EOL 3
248 static inline char *getuint_or_obj(char *in, uint32_t *out, int *type)
250 if (*in == '\n') {
251 *type = TYPE_EOL;
252 } else if (*in == '$') {
253 in++;
254 if (!strncmp("II", in, strlen("II"))) {
255 in += 2;
256 in = getuint(in, out);
257 *type = TYPE_CNT;
258 } else if (!strncmp("PRB", in, strlen("PRB"))) {
259 *type = TYPE_RND;
260 in += 3;
261 } else
262 panic("Syntax error!\n");
263 } else {
264 in = getuint(in, out);
265 *type = TYPE_NUM;
268 return in;
271 static inline char *skipchar(char *in, char c)
273 if (*in != c)
274 panic("Syntax error!\n");
275 return ++in;
278 static inline char *skipchar_s(char *in, char c)
280 in = skips(in);
281 if (*in == '\n')
282 return in;
283 in = skipchar(in, c);
284 in = skips(in);
286 return in;
289 static void dump_conf(struct pktconf *cfg)
291 size_t i, j;
293 info("n %lu, gap %lu ms, pkts %zu\n", cfg->num, cfg->gap, cfg->len);
294 if (cfg->len == 0)
295 return;
297 for (i = 0; i < cfg->len; ++i) {
298 info("[%zu] pkt\n", i);
299 info(" len %zu cnts %zu rnds %zu\n", cfg->pkts[i].plen,
300 cfg->pkts[i].clen, cfg->pkts[i].rlen);
301 info(" payload ");
302 for (j = 0; j < cfg->pkts[i].plen; ++j)
303 info("%02x ", cfg->pkts[i].payload[j]);
304 info("\n");
305 for (j = 0; j < cfg->pkts[i].clen; ++j)
306 info(" cnt%zu [%u,%u], inc %u, off %zu\n",
307 j, cfg->pkts[i].cnt[j].min,
308 cfg->pkts[i].cnt[j].max,
309 cfg->pkts[i].cnt[j].inc,
310 cfg->pkts[i].cnt[j].off);
311 for (j = 0; j < cfg->pkts[i].rlen; ++j)
312 info(" rnd%zu off %zu\n",
313 cfg->pkts[i].rnd[j].off);
317 /* Seems to need a rewrite later ;-) */
318 static void parse_conf_or_die(char *file, struct pktconf *cfg)
320 int withinpkt = 0;
321 unsigned long line = 0;
322 char *pb, buff[1024];
323 FILE *fp;
324 struct counter *cnts = NULL;
325 size_t l = 0;
326 off_t offset = 0;
328 if (!file || !cfg)
329 panic("Panic over invalid args for the parser!\n");
331 fp = fopen(file, "r");
332 if (!fp)
333 panic("Cannot open config file!\n");
334 memset(buff, 0, sizeof(buff));
336 info("CFG:\n");
337 srand(time(NULL));
339 while (fgets(buff, sizeof(buff), fp) != NULL) {
340 line++;
341 buff[sizeof(buff) - 1] = 0;
342 pb = skips(buff);
344 /* A comment or junk. Skip this line */
345 if (*pb == '#' || *pb == '\n') {
346 memset(buff, 0, sizeof(buff));
347 continue;
350 if (!withinpkt && *pb == '$') {
351 pb++;
352 if (!strncmp("II", pb, strlen("II"))) {
353 uint32_t id, min = 0, max = 0xFF, inc = 1;
354 pb += 2;
355 pb = getuint(pb, &id);
356 pb = skipchar(pb, ':');
357 pb = skips(pb);
358 pb = getuint(pb, &min);
359 pb = skipchar(pb, ',');
360 pb = getuint(pb, &max);
361 pb = skipchar(pb, ',');
362 pb = getuint(pb, &inc);
363 l++;
364 cnts = xrealloc(cnts, 1, l * sizeof(*cnts));
365 cnts[l - 1].id = 0xFF & id;
366 cnts[l - 1].min = 0xFF & min;
367 cnts[l - 1].max = 0xFF & max;
368 cnts[l - 1].inc = 0xFF & inc;
369 } else if (!strncmp("P", pb, strlen("P"))) {
370 uint32_t id;
371 pb++;
372 pb = getuint(pb, &id);
373 pb = skips(pb);
374 pb = skipchar(pb, '{');
375 withinpkt = 1;
376 cfg->len++;
377 cfg->pkts = xrealloc(cfg->pkts, 1,
378 cfg->len * sizeof(*cfg->pkts));
379 memset(&cfg->pkts[cfg->len - 1], 0,
380 sizeof(cfg->pkts[cfg->len - 1]));
381 offset = 0;
382 } else
383 panic("Unknown instruction! Syntax error "
384 "on line %lu!\n", line);
385 } else if (withinpkt && *pb == '}')
386 withinpkt = 0;
387 else if (withinpkt) {
388 int type, i, found;
389 uint32_t val;
390 while (1) {
391 found = 0;
392 pb = getuint_or_obj(pb, &val, &type);
393 if (type == TYPE_EOL)
394 break;
395 if (type == TYPE_CNT) {
396 size_t z;
397 struct counter *new;
398 for (i = 0; i < l; ++i) {
399 if (val == cnts[i].id) {
400 found = 1;
401 break;
404 if (!found)
405 panic("Counter %u not found!\n");
407 val = cnts[i].min;
408 z = ++(cfg->pkts[cfg->len - 1].clen);
409 cfg->pkts[cfg->len - 1].cnt =
410 xrealloc(cfg->pkts[cfg->len - 1].cnt,
411 1, z * sizeof(struct counter));
412 new = &cfg->pkts[cfg->len - 1].cnt[z - 1];
413 new->min = cnts[i].min;
414 new->max = cnts[i].max;
415 new->inc = cnts[i].inc;
416 new->off = offset;
417 new->val = val;
418 } else if (type == TYPE_RND) {
419 size_t z;
420 struct randomizer *new;
422 val = 0xFF & rand();
423 z = ++(cfg->pkts[cfg->len - 1].rlen);
424 cfg->pkts[cfg->len - 1].rnd =
425 xrealloc(cfg->pkts[cfg->len - 1].rnd,
426 1, z * sizeof(struct randomizer));
427 new = &cfg->pkts[cfg->len - 1].rnd[z - 1];
428 new->val = val;
429 new->off = offset;
432 cfg->pkts[cfg->len - 1].plen++;
433 cfg->pkts[cfg->len - 1].payload =
434 xrealloc(cfg->pkts[cfg->len - 1].payload,
435 1, cfg->pkts[cfg->len - 1].plen);
436 cfg->pkts[cfg->len - 1].payload[cfg->pkts[cfg->len - 1].plen - 1] =
437 0xFF & val;
438 offset++;
439 pb = skipchar_s(pb, ',');
441 } else
442 panic("Syntax error!\n");
443 memset(buff, 0, sizeof(buff));
446 fclose(fp);
447 xfree(cnts);
449 dump_conf(cfg);
452 static void cleanup_cfg(struct pktconf *cfg)
454 size_t l;
456 for (l = 0; l < cfg->len; ++l) {
457 if (cfg->pkts[l].plen > 0)
458 xfree(cfg->pkts[l].payload);
459 if (cfg->pkts[l].clen > 0)
460 xfree(cfg->pkts[l].cnt);
461 if (cfg->pkts[l].rlen > 0)
462 xfree(cfg->pkts[l].rnd);
465 if (cfg->len > 0)
466 xfree(cfg->pkts);
469 static int main_loop(struct mode *mode, char *confname, unsigned long pkts,
470 unsigned long gap)
472 struct pktconf cfg = {
473 .num = pkts,
474 .gap = gap,
475 .len = 0,
478 parse_conf_or_die(confname, &cfg);
479 tx_fire_or_die(mode, &cfg);
480 cleanup_cfg(&cfg);
482 return 0;
485 int main(int argc, char **argv)
487 int c, opt_index, ret, i, j;
488 char *confname = NULL, *ptr;
489 unsigned long pkts = 0, gap = 0;
490 bool prio_high = true;
491 struct mode mode;
493 check_for_root_maybe_die();
495 memset(&mode, 0, sizeof(mode));
496 mode.cpu = CPU_UNKNOWN;
498 while ((c = getopt_long(argc, argv, short_options, long_options,
499 &opt_index)) != EOF) {
500 switch (c) {
501 case 'h':
502 help();
503 break;
504 case 'v':
505 version();
506 break;
507 case 'd':
508 mode.device = xstrndup(optarg, IFNAMSIZ);
509 break;
510 case 'r':
511 mode.rand = 1;
512 break;
513 case 'c':
514 confname = xstrdup(optarg);
515 break;
516 case 'n':
517 pkts = atol(optarg);
518 break;
519 case 't':
520 gap = atol(optarg);
521 break;
522 case 'S':
523 ptr = optarg;
524 mode.reserve_size = 0;
526 for (j = i = strlen(optarg); i > 0; --i) {
527 if (!isdigit(optarg[j - i]))
528 break;
529 ptr++;
532 if (!strncmp(ptr, "KB", strlen("KB")))
533 mode.reserve_size = 1 << 10;
534 else if (!strncmp(ptr, "MB", strlen("MB")))
535 mode.reserve_size = 1 << 20;
536 else if (!strncmp(ptr, "GB", strlen("GB")))
537 mode.reserve_size = 1 << 30;
538 else
539 panic("Syntax error in ring size param!\n");
541 *ptr = 0;
542 mode.reserve_size *= atoi(optarg);
543 break;
544 case 'b':
545 set_cpu_affinity(optarg, 0);
546 /* Take the first CPU for rebinding the IRQ */
547 if (mode.cpu != CPU_NOTOUCH)
548 mode.cpu = atoi(optarg);
549 break;
550 case 'B':
551 set_cpu_affinity(optarg, 1);
552 break;
553 case 'H':
554 prio_high = false;
555 break;
556 case 'Q':
557 mode.cpu = CPU_NOTOUCH;
558 break;
559 case '?':
560 switch (optopt) {
561 case 'd':
562 case 'c':
563 case 'n':
564 case 'S':
565 case 'b':
566 case 'B':
567 case 't':
568 error_and_die(EXIT_FAILURE, "Option -%c "
569 "requires an argument!\n",
570 optopt);
571 default:
572 if (isprint(optopt))
573 whine("Unknown option character "
574 "`0x%X\'!\n", optopt);
575 die();
577 default:
578 break;
582 if (argc < 5)
583 help();
584 if (mode.device == NULL)
585 error_and_die(EXIT_FAILURE, "No networking device given!\n");
586 if (confname == NULL)
587 error_and_die(EXIT_FAILURE, "No configuration file given!\n");
588 if (device_mtu(mode.device) == 0)
589 error_and_die(EXIT_FAILURE, "This is no networking device!\n");
591 register_signal(SIGINT, signal_handler);
592 register_signal(SIGHUP, signal_handler);
593 register_signal(SIGSEGV, muntrace_handler);
595 header();
597 if (prio_high == true) {
598 set_proc_prio(DEFAULT_PROCESS_PRIO);
599 set_sched_status(DEFAULT_SCHED_POLICY, DEFAULT_SCHED_PRIO);
602 ret = main_loop(&mode, confname, pkts, gap);
604 xfree(mode.device);
605 xfree(confname);
606 return ret;