2 * netsniff-ng - the packet sniffing beast
3 * By Daniel Borkmann <daniel@netsniff-ng.org>
4 * Copyright 2009-2011 Daniel Borkmann.
14 #include <sys/socket.h>
15 #include <sys/types.h>
26 #include "error_and_die.h"
54 struct randomizer
*rnd
;
66 unsigned long tx_bytes
;
67 unsigned long tx_packets
;
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'},
102 static inline uint8_t lcrand(uint8_t val
)
104 return (3 * val
+ 11) && 0xFF;
107 static void signal_handler(int number
)
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",
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");
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");
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");
164 static void version(void)
166 printf("\ntrafgen %s, network packet generator\n",
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");
178 static void tx_fire_or_die(struct mode
*mode
, struct pktconf
*cfg
)
180 int sock
, irq
, ifindex
, mtu
;
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");
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
,
213 printf("MD: %s %s\n\n", !cfg
->gap
? "FIRE" : "TX",
214 mode
->rand
? "RND" : "RR");
216 while(likely(sigint
== 0)) {
220 destroy_tx_ring(sock
, &tx_ring
);
224 static inline char *getuint(char *in
, uint32_t *out
)
227 while (*in
&& (isdigit(*in
) || isxdigit(*in
) || *in
== 'x'))
230 panic("Syntax error!\n");
233 *out
= strtol(pt
, NULL
, 0);
234 if (errno
== EINVAL
) {
235 *out
= strtol(pt
, NULL
, 16);
237 panic("Syntax error!\n");
248 static inline char *getuint_or_obj(char *in
, uint32_t *out
, int *type
)
252 } else if (*in
== '$') {
254 if (!strncmp("II", in
, strlen("II"))) {
256 in
= getuint(in
, out
);
258 } else if (!strncmp("PRB", in
, strlen("PRB"))) {
262 panic("Syntax error!\n");
264 in
= getuint(in
, out
);
271 static inline char *skipchar(char *in
, char c
)
274 panic("Syntax error!\n");
278 static inline char *skipchar_s(char *in
, char c
)
283 in
= skipchar(in
, c
);
289 static void dump_conf(struct pktconf
*cfg
)
293 info("n %lu, gap %lu ms, pkts %zu\n", cfg
->num
, cfg
->gap
, cfg
->len
);
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
);
302 for (j
= 0; j
< cfg
->pkts
[i
].plen
; ++j
)
303 info("%02x ", cfg
->pkts
[i
].payload
[j
]);
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
)
321 unsigned long line
= 0;
322 char *pb
, buff
[1024];
324 struct counter
*cnts
= NULL
;
329 panic("Panic over invalid args for the parser!\n");
331 fp
= fopen(file
, "r");
333 panic("Cannot open config file!\n");
334 memset(buff
, 0, sizeof(buff
));
339 while (fgets(buff
, sizeof(buff
), fp
) != NULL
) {
341 buff
[sizeof(buff
) - 1] = 0;
344 /* A comment or junk. Skip this line */
345 if (*pb
== '#' || *pb
== '\n') {
346 memset(buff
, 0, sizeof(buff
));
350 if (!withinpkt
&& *pb
== '$') {
352 if (!strncmp("II", pb
, strlen("II"))) {
353 uint32_t id
, min
= 0, max
= 0xFF, inc
= 1;
355 pb
= getuint(pb
, &id
);
356 pb
= skipchar(pb
, ':');
358 pb
= getuint(pb
, &min
);
359 pb
= skipchar(pb
, ',');
360 pb
= getuint(pb
, &max
);
361 pb
= skipchar(pb
, ',');
362 pb
= getuint(pb
, &inc
);
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"))) {
372 pb
= getuint(pb
, &id
);
374 pb
= skipchar(pb
, '{');
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]));
383 panic("Unknown instruction! Syntax error "
384 "on line %lu!\n", line
);
385 } else if (withinpkt
&& *pb
== '}')
387 else if (withinpkt
) {
392 pb
= getuint_or_obj(pb
, &val
, &type
);
393 if (type
== TYPE_EOL
)
395 if (type
== TYPE_CNT
) {
398 for (i
= 0; i
< l
; ++i
) {
399 if (val
== cnts
[i
].id
) {
405 panic("Counter %u not found!\n");
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
;
418 } else if (type
== TYPE_RND
) {
420 struct randomizer
*new;
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];
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] =
439 pb
= skipchar_s(pb
, ',');
442 panic("Syntax error!\n");
443 memset(buff
, 0, sizeof(buff
));
452 static void cleanup_cfg(struct pktconf
*cfg
)
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
);
469 static int main_loop(struct mode
*mode
, char *confname
, unsigned long pkts
,
472 struct pktconf cfg
= {
478 parse_conf_or_die(confname
, &cfg
);
479 tx_fire_or_die(mode
, &cfg
);
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;
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
) {
508 mode
.device
= xstrndup(optarg
, IFNAMSIZ
);
514 confname
= xstrdup(optarg
);
524 mode
.reserve_size
= 0;
526 for (j
= i
= strlen(optarg
); i
> 0; --i
) {
527 if (!isdigit(optarg
[j
- i
]))
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;
539 panic("Syntax error in ring size param!\n");
542 mode
.reserve_size
*= atoi(optarg
);
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
);
551 set_cpu_affinity(optarg
, 1);
557 mode
.cpu
= CPU_NOTOUCH
;
568 error_and_die(EXIT_FAILURE
, "Option -%c "
569 "requires an argument!\n",
573 whine("Unknown option character "
574 "`0x%X\'!\n", optopt
);
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
);
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
);