2 * netsniff-ng - the packet sniffing beast
3 * By Daniel Borkmann <daniel@netsniff-ng.org>
4 * Copyright 2009, 2010 Daniel Borkmann.
13 #include <sys/socket.h>
30 * TODO: Maybe interesting ethtool -S stats, too?
31 * Approximation for longer intervals.
34 #define TERM_MODE_NORMAL 1
35 #define TERM_MODE_CSV 2
36 #define TERM_MODE_CSV_HDR 4
39 unsigned long rx_bytes
;
40 unsigned long rx_packets
;
41 unsigned long rx_drops
;
42 unsigned long rx_errors
;
43 unsigned long rx_fifo
;
44 unsigned long rx_frame
;
45 unsigned long rx_multi
;
46 unsigned long tx_bytes
;
47 unsigned long tx_packets
;
48 unsigned long tx_drops
;
49 unsigned long tx_errors
;
50 unsigned long tx_fifo
;
51 unsigned long tx_colls
;
52 unsigned long tx_carrier
;
58 int wifi_link_qual_max
;
59 int wifi_signal_level
;
66 static sig_atomic_t sigint
= 0;
68 static const char *short_options
= "d:t:vhcCHlp";
70 static struct option long_options
[] = {
71 {"dev", required_argument
, 0, 'd'},
72 {"interval", required_argument
, 0, 't'},
73 {"loop", no_argument
, 0, 'l'},
74 {"term", no_argument
, 0, 'c'},
75 {"promisc", no_argument
, 0, 'p'},
76 {"csv", no_argument
, 0, 'C'},
77 {"csv-tablehead", no_argument
, 0, 'H'},
78 {"version", no_argument
, 0, 'v'},
79 {"help", no_argument
, 0, 'h'},
83 static void signal_handler(int number
)
96 static int rxtx_stats(const char *ifname
, struct ifstat
*s
)
102 FILE *fp
= fopen("/proc/net/dev", "r");
104 whine("Cannot open /proc/net/dev!\n");
108 ptr
= fgets(buf
, sizeof(buf
), fp
);
109 ptr
= fgets(buf
, sizeof(buf
), fp
);
111 memset(buf
, 0, sizeof(buf
));
113 while (fgets(buf
, sizeof(buf
), fp
) != NULL
) {
114 buf
[sizeof(buf
) -1] = 0;
116 if (strstr(buf
, ifname
) == NULL
)
124 ret
= sscanf(ptr
, "%lu%lu%lu%lu%lu%lu%lu%*u%lu%lu%lu%lu%lu%lu%lu",
125 &s
->rx_bytes
, &s
->rx_packets
, &s
->rx_errors
,
126 &s
->rx_drops
, &s
->rx_fifo
, &s
->rx_frame
,
128 &s
->tx_bytes
, &s
->tx_packets
, &s
->tx_errors
,
129 &s
->tx_drops
, &s
->tx_fifo
, &s
->tx_colls
,
136 memset(buf
, 0, sizeof(buf
));
143 static int wifi_stats(const char *ifname
, struct ifstat
*s
)
146 struct iw_statistics ws
;
148 ret
= wireless_sigqual(ifname
, &ws
);
150 /* We don't want to trouble in case of eth* */
155 s
->wifi_bitrate
= wireless_bitrate(ifname
);
156 s
->wifi_signal_level
= adjust_dbm_level(ws
.qual
.level
);
157 s
->wifi_noise_level
= adjust_dbm_level(ws
.qual
.noise
);
158 s
->wifi_link_qual
= ws
.qual
.qual
;
159 s
->wifi_link_qual_max
= wireless_rangemax_sigqual(ifname
);
164 static int irq_stats(const char *ifname
, struct ifstat
*s
)
171 if (!strncmp("lo", ifname
, strlen("lo")))
174 FILE *fp
= fopen("/proc/interrupts", "r");
176 whine("Cannot open /proc/interrupts!\n");
180 if (s
->irqs_len
!= NR_CPUS
) {
183 s
->irqs
= xzmalloc(sizeof(*(s
->irqs
)) * NR_CPUS
);
184 s
->irqs_len
= NR_CPUS
;
187 memset(buff
, 0, sizeof(buff
));
189 while (fgets(buff
, sizeof(buff
), fp
) != NULL
) {
190 buff
[sizeof(buff
) - 1] = 0;
192 if (strstr(buff
, ifname
) == NULL
)
199 s
->irq_nr
= atoi(buff
);
200 for (i
= 0; i
< s
->irqs_len
; ++i
) {
208 s
->irqs
[i
] = atoi(ptr2
);
211 memset(buff
, 0, sizeof(buff
));
218 static void diff_stats(struct ifstat
*old
, struct ifstat
*new,
223 if(old
->irqs_len
!= new->irqs_len
)
224 return; /* Refetch stats and take old diff! */
226 diff
->rx_bytes
= new->rx_bytes
- old
->rx_bytes
;
227 diff
->rx_packets
= new->rx_packets
- old
->rx_packets
;
228 diff
->rx_drops
= new->rx_drops
- old
->rx_drops
;
229 diff
->rx_errors
= new->rx_errors
- old
->rx_errors
;
230 diff
->rx_fifo
= new->rx_fifo
- old
->rx_fifo
;
231 diff
->rx_frame
= new->rx_frame
- old
->rx_frame
;
232 diff
->rx_multi
= new->rx_multi
- old
->rx_multi
;
233 diff
->tx_bytes
= new->tx_bytes
- old
->tx_bytes
;
234 diff
->tx_packets
= new->tx_packets
- old
->tx_packets
;
235 diff
->tx_drops
= new->tx_drops
- old
->tx_drops
;
236 diff
->tx_errors
= new->tx_errors
- old
->tx_errors
;
237 diff
->tx_fifo
= new->tx_fifo
- old
->tx_fifo
;
238 diff
->tx_colls
= new->tx_colls
- old
->tx_colls
;
239 diff
->tx_carrier
= new->tx_carrier
- old
->tx_carrier
;
240 diff
->wifi_signal_level
= new->wifi_signal_level
- old
->wifi_signal_level
;
241 diff
->wifi_noise_level
= new->wifi_noise_level
- old
->wifi_noise_level
;
242 diff
->wifi_link_qual
= new->wifi_link_qual
- old
->wifi_link_qual
;
244 if (diff
->irqs_len
!= NR_CPUS
) {
247 diff
->irqs
= xzmalloc(sizeof(*(diff
->irqs
)) * NR_CPUS
);
248 diff
->irqs_len
= NR_CPUS
;
249 diff
->irq_nr
= new->irq_nr
;
252 for (i
= 0; i
< diff
->irqs_len
; ++i
)
253 diff
->irqs
[i
] = new->irqs
[i
] - old
->irqs
[i
];
256 static void screen_init(WINDOW
**screen
)
258 (*screen
) = initscr();
262 nodelay((*screen
), TRUE
);
267 static void screen_update(WINDOW
*screen
, const char *ifname
,
268 struct ifstat
*s
, struct ifstat
*t
, int *first
)
273 mvwprintw(screen
, 1, 2, "Kernel networking statistics for %s",
276 mvwprintw(screen
, 3, 2,
277 "RX: %16.3f MB/t %10lu Pkts/t %10lu Drops/t %10lu Errors/t",
278 1.f
* s
->rx_bytes
/ (1 << 20), s
->rx_packets
, s
->rx_drops
,
280 mvwprintw(screen
, 4, 2,
281 "TX: %16.3f MB/t %10lu Pkts/t %10lu Drops/t %10lu Errors/t",
282 1.f
* s
->tx_bytes
/ (1 << 20), s
->tx_packets
, s
->tx_drops
,
285 mvwprintw(screen
, 6, 2,
286 "RX: %16.3f MB %10lu Pkts %10lu Drops %10lu Errors",
287 1.f
* t
->rx_bytes
/ (1 << 20), t
->rx_packets
, t
->rx_drops
,
289 mvwprintw(screen
, 7, 2,
290 "TX: %16.3f MB %10lu Pkts %10lu Drops %10lu Errors",
291 1.f
* t
->tx_bytes
/ (1 << 20), t
->tx_packets
, t
->tx_drops
,
295 if (s
->irq_nr
!= 0) {
297 for(i
= 0; i
< s
->irqs_len
; ++i
)
298 mvwprintw(screen
, j
++, 2, "CPU%d: %10ld IRQs/t", i
,
301 for(i
= 0; i
< s
->irqs_len
; ++i
)
302 mvwprintw(screen
, j
++, 2, "CPU%d: %10ld IRQs",
307 if (t
->wifi_bitrate
> 0) {
308 /* WiFi statistics */
309 mvwprintw(screen
, j
++, 2, "LinkQual: %6d/%d (%d/t) ",
310 t
->wifi_link_qual
, t
->wifi_link_qual_max
,
312 mvwprintw(screen
, j
++, 2, "Signal: %8d dBm (%d dBm/t) ",
313 t
->wifi_signal_level
, s
->wifi_signal_level
);
314 mvwprintw(screen
, j
++, 2, "Noise: %8d dBm (%d dBm/t) ",
315 t
->wifi_noise_level
, s
->wifi_noise_level
);
320 mvwprintw(screen
, 2, 2, "Collecting data ...");
323 mvwprintw(screen
, 2, 2, " ");
329 static void screen_end(void)
334 static void print_update(const char *ifname
, struct ifstat
*s
,
339 printf("RX: %16.3f MB/t %10lu Pkts/t %10lu Drops/t %10lu Errors/t\n",
340 1.f
* s
->rx_bytes
/ (1 << 20), s
->rx_packets
, s
->rx_drops
,
342 printf("TX: %16.3f MB/t %10lu Pkts/t %10lu Drops/t %10lu Errors/t\n",
343 1.f
* s
->tx_bytes
/ (1 << 20), s
->tx_packets
, s
->tx_drops
,
347 for(i
= 0; i
< s
->irqs_len
; ++i
)
348 printf("CPU%d: %10ld IRQs/t\n", i
, s
->irqs
[i
]);
349 if (t
->wifi_bitrate
> 0) {
350 printf("LinkQual: %6d/%d (%d/t)\n", t
->wifi_link_qual
,
351 t
->wifi_link_qual_max
, s
->wifi_link_qual
);
352 printf("Signal: %8d dBm (%d dBm/t)\n", t
->wifi_signal_level
,
353 s
->wifi_signal_level
);
354 printf("Noise: %8d dBm (%d dBm/t)\n", t
->wifi_noise_level
,
355 s
->wifi_noise_level
);
359 static void print_update_csv(const char *ifname
, struct ifstat
*s
,
364 printf("%lu,%lu,%lu,%lu,", s
->rx_bytes
, s
->rx_packets
, s
->rx_drops
,
366 printf("%lu,%lu,%lu,%lu", s
->tx_bytes
, s
->tx_packets
, s
->tx_drops
,
370 for(i
= 0; i
< s
->irqs_len
; ++i
)
371 printf(",%ld", s
->irqs
[i
]);
372 if (t
->wifi_bitrate
> 0) {
373 printf(",%d,%d", t
->wifi_link_qual
, t
->wifi_link_qual_max
);
374 printf(",%d", t
->wifi_signal_level
);
375 printf(",%d", t
->wifi_noise_level
);
381 static void print_update_csv_hdr(const char *ifname
, struct ifstat
*s
,
386 printf("RX Byte/t,RX Pkts/t,RX Drops/t,RX Errors/t,");
387 printf("TX Byte/t,TX Pkts/t,TX Drops/t,TX Errors/t");
390 for(i
= 0; i
< s
->irqs_len
; ++i
)
391 printf(",CPU%d IRQs/t", i
);
392 if (t
->wifi_bitrate
> 0)
393 printf(",LinkQual,LinkQualMax,Signal Level,Noise Level");
398 static inline int do_stats(const char *ifname
, struct ifstat
*s
)
402 ret
+= rxtx_stats(ifname
, s
);
403 ret
+= irq_stats(ifname
, s
);
404 ret
+= wifi_stats(ifname
, s
);
409 static int screen_loop(const char *ifname
, double interval
)
411 int ret
= 0, first
= 1;
412 struct ifstat old
, new, curr
;
413 WINDOW
*screen
= NULL
;
415 memset(&old
, 0, sizeof(old
));
416 memset(&new, 0, sizeof(new));
417 memset(&curr
, 0, sizeof(curr
));
419 screen_init(&screen
);
425 screen_update(screen
, ifname
, &curr
, &new, &first
);
427 ret
= do_stats(ifname
, &old
);
430 xnanosleep(interval
);
431 ret
= do_stats(ifname
, &new);
435 diff_stats(&old
, &new, &curr
);
441 whine("Error fetching stats!\n");
452 static int print_loop(const char *ifname
, double interval
)
455 struct ifstat old
, new, curr
;
457 memset(&old
, 0, sizeof(old
));
458 memset(&new, 0, sizeof(new));
459 memset(&curr
, 0, sizeof(curr
));
462 ret
= do_stats(ifname
, &old
);
465 xnanosleep(interval
);
466 ret
= do_stats(ifname
, &new);
470 diff_stats(&old
, &new, &curr
);
472 if (first
&& (mode
& TERM_MODE_CSV_HDR
) ==
474 print_update_csv_hdr(ifname
, &curr
, &new);
478 if ((mode
& TERM_MODE_CSV
) == TERM_MODE_CSV
)
479 print_update_csv(ifname
, &curr
, &new);
480 else if ((mode
& TERM_MODE_NORMAL
) == TERM_MODE_NORMAL
)
481 print_update(ifname
, &curr
, &new);
482 } while (loop
&& !sigint
);
486 whine("Error fetching stats!\n");
497 static void help(void)
499 printf("\nifpps %s, kernel networking statistics per sec\n",
501 printf("http://www.netsniff-ng.org\n\n");
502 printf("Usage: ifpps [options] || ifpps <netdev>\n");
503 printf("Options:\n");
504 printf(" -d|--dev <netdev> Device to fetch statistics for\n");
505 printf(" -t|--interval <time> Refresh time in seconds as float (default 1.0)\n");
506 printf(" -c|--term Output to terminal\n");
507 printf(" -p|--promisc Promisc mode\n");
508 printf(" -C|--csv Output to terminal as CSV\n");
509 printf(" E.g. post-processing with Gnuplot et al.\n");
510 printf(" -H|--csv-tablehead Print CSV table head\n");
511 printf(" -l|--loop Loop terminal output\n");
512 printf(" -v|--version Print version\n");
513 printf(" -h|--help Print this help\n");
515 printf("Examples:\n");
516 printf(" ifpps --dev eth0\n");
517 printf(" ifpps --dev eth0 --interval 60 --csv\n");
518 printf("Please report bugs to <bugs@netsniff-ng.org>\n");
519 printf("Copyright (C) 2009, 2010 Daniel Borkmann\n");
520 printf("License: GNU GPL version 2\n");
521 printf("This is free software: you are free to change and redistribute it.\n");
522 printf("There is NO WARRANTY, to the extent permitted by law.\n\n");
527 static void version(void)
529 printf("\nifpps %s, kernel networking statistics per sec\n",
531 printf("http://www.netsniff-ng.org\n\n");
532 printf("Please report bugs to <bugs@netsniff-ng.org>\n");
533 printf("Copyright (C) 2009, 2010 Daniel Borkmann\n");
534 printf("License: GNU GPL version 2\n");
535 printf("This is free software: you are free to change and redistribute it.\n");
536 printf("There is NO WARRANTY, to the extent permitted by law.\n\n");
541 int main(int argc
, char **argv
)
544 int c
, opt_index
, ret
, promisc
= 0;
546 double interval
= 1.0;
547 int (*main_loop
)(const char *ifname
, double interval
) = screen_loop
;
549 while ((c
= getopt_long(argc
, argv
, short_options
, long_options
,
550 &opt_index
)) != EOF
) {
559 ifname
= xstrndup(optarg
, IFNAMSIZ
);
562 interval
= atof(optarg
);
565 mode
|= TERM_MODE_NORMAL
;
566 main_loop
= print_loop
;
575 mode
|= TERM_MODE_CSV
;
576 main_loop
= print_loop
;
579 mode
|= TERM_MODE_CSV_HDR
;
580 main_loop
= print_loop
;
586 error_and_die(EXIT_FAILURE
, "Option -%c "
587 "requires an argument!\n",
591 whine("Unknown option character "
592 "`0x%X\'!\n", optopt
);
603 ifname
= xstrndup(argv
[1], IFNAMSIZ
);
605 error_and_die(EXIT_FAILURE
, "No networking device given!\n");
606 if (!strncmp("lo", ifname
, IFNAMSIZ
))
607 error_and_die(EXIT_FAILURE
, "lo is not supported!\n");
608 if (device_mtu(ifname
) == 0)
609 error_and_die(EXIT_FAILURE
, "This is no networking device!\n");
611 register_signal(SIGINT
, signal_handler
);
612 register_signal(SIGHUP
, signal_handler
);
613 register_signal(SIGSEGV
, muntrace_handler
);
616 check_for_root_maybe_die();
617 ifflags
= enter_promiscuous_mode(ifname
);
619 ret
= main_loop(ifname
, interval
);
621 leave_promiscuous_mode(ifname
, ifflags
);