offline pcap reading working
[netsniff-ng.git] / src / ifpps.c
blob4c27a45561415c2b0a54ea79a0bacc25f81d4253
1 /*
2 * netsniff-ng - the packet sniffing beast
3 * By Daniel Borkmann <daniel@netsniff-ng.org>
4 * Copyright 2009, 2010 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 <sys/socket.h>
14 #include <signal.h>
15 #include <stdint.h>
16 #include <stdlib.h>
17 #include <assert.h>
19 #include "die.h"
20 #include "xmalloc.h"
21 #include "psched.h"
22 #include "misc.h"
23 #include "timespec.h"
24 #include "tty.h"
25 #include "version.h"
26 #include "netdev.h"
27 #include "signals.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
38 struct ifstat {
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;
53 unsigned long irq_nr;
54 unsigned long *irqs;
55 size_t irqs_len;
56 int wifi_bitrate;
57 int wifi_link_qual;
58 int wifi_link_qual_max;
59 int wifi_signal_level;
60 int wifi_noise_level;
63 static int mode = 0;
64 static int loop = 0;
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'},
80 {0, 0, 0, 0}
83 static void signal_handler(int number)
85 switch (number) {
86 case SIGINT:
87 sigint = 1;
88 break;
89 case SIGHUP:
90 break;
91 default:
92 break;
96 static int rxtx_stats(const char *ifname, struct ifstat *s)
98 int ret, found = -1;
99 char *ptr;
100 char buf[1024];
102 FILE *fp = fopen("/proc/net/dev", "r");
103 if (!fp) {
104 whine("Cannot open /proc/net/dev!\n");
105 return -ENOENT;
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)
117 continue;
119 ptr = buf;
120 while (*ptr != ':')
121 ptr++;
122 ptr++;
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,
127 &s->rx_multi,
128 &s->tx_bytes, &s->tx_packets, &s->tx_errors,
129 &s->tx_drops, &s->tx_fifo, &s->tx_colls,
130 &s->tx_carrier);
131 if (ret == 14) {
132 found = 0;
133 break;
136 memset(buf, 0, sizeof(buf));
139 fclose(fp);
140 return found;
143 static int wifi_stats(const char *ifname, struct ifstat *s)
145 int ret;
146 struct iw_statistics ws;
148 ret = wireless_sigqual(ifname, &ws);
149 if (ret != 0) {
150 /* We don't want to trouble in case of eth* */
151 s->wifi_bitrate = 0;
152 return 0;
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);
161 return ret;
164 static int irq_stats(const char *ifname, struct ifstat *s)
166 int i;
167 char *ptr, *ptr2;
168 char buff[4096];
170 /* We exclude lo! */
171 if (!strncmp("lo", ifname, strlen("lo")))
172 return 0;
174 FILE *fp = fopen("/proc/interrupts", "r");
175 if (!fp) {
176 whine("Cannot open /proc/interrupts!\n");
177 return -ENOENT;
180 if (s->irqs_len != NR_CPUS) {
181 if (s->irqs)
182 xfree(s->irqs);
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)
193 continue;
195 ptr = buff;
196 while (*ptr != ':')
197 ptr++;
198 *ptr = 0;
199 s->irq_nr = atoi(buff);
200 for (i = 0; i < s->irqs_len; ++i) {
201 ptr++;
202 ptr2 = ptr;
203 while (*ptr == ' ')
204 ptr++;
205 while (*ptr != ' ')
206 ptr++;
207 *ptr = 0;
208 s->irqs[i] = atoi(ptr2);
211 memset(buff, 0, sizeof(buff));
214 fclose(fp);
215 return 0;
218 static void diff_stats(struct ifstat *old, struct ifstat *new,
219 struct ifstat *diff)
221 int i;
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) {
245 if (diff->irqs)
246 xfree(diff->irqs);
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();
260 noecho();
261 cbreak();
262 nodelay((*screen), TRUE);
263 refresh();
264 wrefresh((*screen));
267 static void screen_update(WINDOW *screen, const char *ifname,
268 struct ifstat *s, struct ifstat *t, int *first)
270 int i, j = 0;
272 curs_set(0);
273 mvwprintw(screen, 1, 2, "Kernel networking statistics for %s",
274 ifname);
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,
279 s->rx_errors);
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,
283 s->tx_errors);
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,
288 t->rx_errors);
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,
292 t->tx_errors);
293 j = 9;
295 if (s->irq_nr != 0) {
296 /* IRQ statistics */
297 for(i = 0; i < s->irqs_len; ++i)
298 mvwprintw(screen, j++, 2, "CPU%d: %10ld IRQs/t", i,
299 s->irqs[i]);
300 j++;
301 for(i = 0; i < s->irqs_len; ++i)
302 mvwprintw(screen, j++, 2, "CPU%d: %10ld IRQs",
303 i, t->irqs[i]);
304 j++;
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,
311 s->wifi_link_qual);
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);
316 j++;
319 if (*first) {
320 mvwprintw(screen, 2, 2, "Collecting data ...");
321 *first = 0;
322 } else
323 mvwprintw(screen, 2, 2, " ");
325 wrefresh(screen);
326 refresh();
329 static void screen_end(void)
331 endwin();
334 static void print_update(const char *ifname, struct ifstat *s,
335 struct ifstat *t)
337 int i;
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,
341 s->rx_errors);
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,
344 s->tx_errors);
346 if (s->irq_nr != 0)
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,
360 struct ifstat *t)
362 int i;
364 printf("%lu,%lu,%lu,%lu,", s->rx_bytes, s->rx_packets, s->rx_drops,
365 s->rx_errors);
366 printf("%lu,%lu,%lu,%lu", s->tx_bytes, s->tx_packets, s->tx_drops,
367 s->tx_errors);
369 if (s->irq_nr != 0)
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);
378 printf("\n");
381 static void print_update_csv_hdr(const char *ifname, struct ifstat *s,
382 struct ifstat *t)
384 int i;
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");
389 if (s->irq_nr != 0)
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");
395 printf("\n");
398 static inline int do_stats(const char *ifname, struct ifstat *s)
400 int ret = 0;
402 ret += rxtx_stats(ifname, s);
403 ret += irq_stats(ifname, s);
404 ret += wifi_stats(ifname, s);
406 return ret;
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);
421 while (!sigint) {
422 if (getch() == 'q')
423 goto out;
425 screen_update(screen, ifname, &curr, &new, &first);
427 ret = do_stats(ifname, &old);
428 if (ret != 0)
429 goto out;
430 xnanosleep(interval);
431 ret = do_stats(ifname, &new);
432 if (ret != 0)
433 goto out;
435 diff_stats(&old, &new, &curr);
438 out:
439 screen_end();
440 if (ret != 0)
441 whine("Error fetching stats!\n");
442 if (old.irqs)
443 xfree(old.irqs);
444 if (new.irqs)
445 xfree(new.irqs);
446 if (curr.irqs)
447 xfree(curr.irqs);
449 return 0;
452 static int print_loop(const char *ifname, double interval)
454 int ret, first = 1;
455 struct ifstat old, new, curr;
457 memset(&old, 0, sizeof(old));
458 memset(&new, 0, sizeof(new));
459 memset(&curr, 0, sizeof(curr));
461 do {
462 ret = do_stats(ifname, &old);
463 if (ret != 0)
464 goto out;
465 xnanosleep(interval);
466 ret = do_stats(ifname, &new);
467 if (ret != 0)
468 goto out;
470 diff_stats(&old, &new, &curr);
472 if (first && (mode & TERM_MODE_CSV_HDR) ==
473 TERM_MODE_CSV_HDR) {
474 print_update_csv_hdr(ifname, &curr, &new);
475 first = 0;
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);
484 out:
485 if (ret != 0)
486 whine("Error fetching stats!\n");
487 if (old.irqs)
488 xfree(old.irqs);
489 if (new.irqs)
490 xfree(new.irqs);
491 if (curr.irqs)
492 xfree(curr.irqs);
494 return 0;
497 static void help(void)
499 printf("\nifpps %s, kernel networking statistics per sec\n",
500 VERSION_STRING);
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");
514 printf("\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");
524 exit(EXIT_SUCCESS);
527 static void version(void)
529 printf("\nifpps %s, kernel networking statistics per sec\n",
530 VERSION_STRING);
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");
538 exit(EXIT_SUCCESS);
541 int main(int argc, char **argv)
543 short ifflags = 0;
544 int c, opt_index, ret, promisc = 0;
545 char *ifname = NULL;
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) {
551 switch (c) {
552 case 'h':
553 help();
554 break;
555 case 'v':
556 version();
557 break;
558 case 'd':
559 ifname = xstrndup(optarg, IFNAMSIZ);
560 break;
561 case 't':
562 interval = atof(optarg);
563 break;
564 case 'c':
565 mode |= TERM_MODE_NORMAL;
566 main_loop = print_loop;
567 break;
568 case 'l':
569 loop = 1;
570 break;
571 case 'p':
572 promisc = 1;
573 break;
574 case 'C':
575 mode |= TERM_MODE_CSV;
576 main_loop = print_loop;
577 break;
578 case 'H':
579 mode |= TERM_MODE_CSV_HDR;
580 main_loop = print_loop;
581 break;
582 case '?':
583 switch (optopt) {
584 case 'd':
585 case 't':
586 error_and_die(EXIT_FAILURE, "Option -%c "
587 "requires an argument!\n",
588 optopt);
589 default:
590 if (isprint(optopt))
591 whine("Unknown option character "
592 "`0x%X\'!\n", optopt);
593 exit(EXIT_FAILURE);
595 default:
596 break;
600 if (argc == 1)
601 help();
602 if (argc == 2)
603 ifname = xstrndup(argv[1], IFNAMSIZ);
604 if (ifname == NULL)
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);
615 if (promisc) {
616 check_for_root_maybe_die();
617 ifflags = enter_promiscuous_mode(ifname);
619 ret = main_loop(ifname, interval);
620 if (promisc)
621 leave_promiscuous_mode(ifname, ifflags);
623 xfree(ifname);
624 return ret;