proto_ipv4: don't trim length of pkt_buff
[netsniff-ng.git] / src / ifpps.c
blobfd24c1244b344cc9a5c072d5743812f347500843
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, version 2.
7 * A tiny tool to provide top-like reliable networking statistics.
8 * Why? Well, some time ago I used iptraf to display network traffic
9 * statistics. During that time and probably also today, they are
10 * using libpcap to collect statistics. Well, bad idea since this
11 * will give you false statistics on high I/O load. Therefore, ifpps
12 * reads out the 'real' kernel statistics, so things your NIC sees
13 * and not some userland library.
15 * He had all the injured air of a liar suspected when for once he
16 * has told the truth, or part of it.
18 * -- The Lord of the Rings, On Gollum,
19 * Chapter 'The Black Gate is Closed'.
24 =head1 NAME
26 ifpps - fetch and format kernel network statistics
28 =head1 SYNOPSIS
30 ifpps -d|--dev <netdev> [-t|--interval <sec>][-p|--promisc][-c|--term]
31 [-C|--csv][-H|--csv-tablehead][-l|--loop][-v|--version][-h|--help]
33 =head1 DESCRIPTION
35 A tiny tool to provide top-like reliable networking statistics.
36 ifpps reads out the 'real' kernel statistics, so it does not give erroneous
37 statistics on high I/O load.
39 =head1 OPTIONS
41 =over
43 =item ifpps --dev eth0
45 Fetch eth0 interface statistics.
47 =item ifpps --dev eth0 --interval 60 --csv
49 Output eth0 interface statistics every minute in CSV format.
51 =back
53 =head1 OPTIONS
55 =over
57 =item -h|--help
59 Print help text and lists all options.
61 =item -v|--version
63 Print version.
65 =item -d|--dev <netdev>
67 Device to fetch statistics for i.e., eth0.
69 =item -p|--promisc
71 Put the device in promiscuous mode
73 =item -t|--interval <time>
75 Refresh time in sec (default 1 sec)
77 =item -c|--term
79 Output to terminal
81 =item -C|--csv
83 Output in CSV format.
84 E.g. post-processing with Gnuplot et al.
86 =item -H|--csv-tablehead
88 Print CSV table head.
90 =item -l|--loop
92 Loop terminal output.
94 =back
96 =head1 AUTHOR
98 Written by Daniel Borkmann <daniel@netsniff-ng.org>
100 =head1 DOCUMENTATION
102 Documentation by Emmanuel Roullit <emmanuel@netsniff-ng.org>
104 =head1 BUGS
106 Please report bugs to <bugs@netsniff-ng.org>
108 =cut
112 #include <stdio.h>
113 #include <string.h>
114 #include <curses.h>
115 #include <getopt.h>
116 #include <ctype.h>
117 #include <sys/socket.h>
118 #include <signal.h>
119 #include <stdint.h>
120 #include <stdlib.h>
122 #include "die.h"
123 #include "xmalloc.h"
124 #include "xsys.h"
125 #include "xio.h"
126 #include "built_in.h"
129 * TODO: Cleanups, this got quite a hack over time.
132 #define TERM_MODE_NORMAL 1
133 #define TERM_MODE_CSV 2
134 #define TERM_MODE_CSV_HDR 4
136 #define USER_HZ sysconf(_SC_CLK_TCK)
138 struct ifstat {
139 unsigned long rx_bytes;
140 unsigned long rx_packets;
141 unsigned long rx_drops;
142 unsigned long rx_errors;
143 unsigned long rx_fifo;
144 unsigned long rx_frame;
145 unsigned long rx_multi;
146 unsigned long tx_bytes;
147 unsigned long tx_packets;
148 unsigned long tx_drops;
149 unsigned long tx_errors;
150 unsigned long tx_fifo;
151 unsigned long tx_colls;
152 unsigned long tx_carrier;
153 unsigned long irq_nr;
154 unsigned long *irqs;
155 unsigned long *irqs_srx;
156 unsigned long *irqs_stx;
157 unsigned long *cpu_user;
158 unsigned long *cpu_nice;
159 unsigned long *cpu_sys;
160 unsigned long *cpu_idle;
161 unsigned long *cpu_iow;
162 unsigned long ctxt;
163 unsigned long forks;
164 unsigned long procs_run;
165 unsigned long procs_iow;
166 size_t irqs_len;
167 float mem_used;
168 int wifi_bitrate;
169 int wifi_link_qual;
170 int wifi_link_qual_max;
171 int wifi_signal_level;
172 int wifi_noise_level;
175 static int mode = 0;
176 static int loop = 0;
178 volatile sig_atomic_t sigint = 0;
180 static const char *short_options = "d:t:vhcCHlp";
182 static struct option long_options[] = {
183 {"dev", required_argument, 0, 'd'},
184 {"interval", required_argument, 0, 't'},
185 {"loop", no_argument, 0, 'l'},
186 {"term", no_argument, 0, 'c'},
187 {"promisc", no_argument, 0, 'p'},
188 {"csv", no_argument, 0, 'C'},
189 {"csv-tablehead", no_argument, 0, 'H'},
190 {"version", no_argument, 0, 'v'},
191 {"help", no_argument, 0, 'h'},
192 {0, 0, 0, 0}
195 static void signal_handler(int number)
197 switch (number) {
198 case SIGINT:
199 sigint = 1;
200 break;
201 case SIGHUP:
202 break;
203 default:
204 break;
208 static int rxtx_stats(const char *ifname, struct ifstat *s)
210 int ret, found = -1;
211 char *ptr;
212 char buf[1024];
214 FILE *fp = fopen("/proc/net/dev", "r");
215 if (!fp) {
216 whine("Cannot open /proc/net/dev!\n");
217 return -ENOENT;
220 /* Omit header */
221 ptr = fgets(buf, sizeof(buf), fp);
222 ptr = fgets(buf, sizeof(buf), fp);
224 memset(buf, 0, sizeof(buf));
225 while (fgets(buf, sizeof(buf), fp) != NULL) {
226 buf[sizeof(buf) -1] = 0;
228 if (strstr(buf, ifname) == NULL)
229 continue;
231 ptr = buf;
232 while (*ptr != ':')
233 ptr++;
234 ptr++;
236 ret = sscanf(ptr, "%lu%lu%lu%lu%lu%lu%lu%*u%lu%lu%lu%lu%lu%lu%lu",
237 &s->rx_bytes, &s->rx_packets, &s->rx_errors,
238 &s->rx_drops, &s->rx_fifo, &s->rx_frame,
239 &s->rx_multi,
240 &s->tx_bytes, &s->tx_packets, &s->tx_errors,
241 &s->tx_drops, &s->tx_fifo, &s->tx_colls,
242 &s->tx_carrier);
243 if (ret == 14) {
244 found = 0;
245 break;
248 memset(buf, 0, sizeof(buf));
251 fclose(fp);
253 return found;
256 static int wifi_stats(const char *ifname, struct ifstat *s)
258 int ret;
259 struct iw_statistics ws;
261 ret = wireless_sigqual(ifname, &ws);
262 if (ret != 0) {
263 /* We don't want to trouble in case of eth* */
264 s->wifi_bitrate = 0;
265 return 0;
268 s->wifi_bitrate = wireless_bitrate(ifname);
269 s->wifi_signal_level = adjust_dbm_level(ws.qual.level);
270 s->wifi_noise_level = adjust_dbm_level(ws.qual.noise);
271 s->wifi_link_qual = ws.qual.qual;
272 s->wifi_link_qual_max = wireless_rangemax_sigqual(ifname);
274 return ret;
277 static void stats_check_alloc(struct ifstat *s)
279 int cpus = get_number_cpus();
281 if (s->irqs_len != get_number_cpus()) {
282 if (s->irqs) xfree(s->irqs);
283 if (s->irqs_srx) xfree(s->irqs_srx);
284 if (s->irqs_stx) xfree(s->irqs_stx);
285 if (s->cpu_user) xfree(s->cpu_user);
286 if (s->cpu_nice) xfree(s->cpu_nice);
287 if (s->cpu_sys) xfree(s->cpu_sys);
288 if (s->cpu_idle) xfree(s->cpu_idle);
289 if (s->cpu_iow) xfree(s->cpu_iow);
291 s->irqs_srx = xzmalloc(sizeof(*(s->irqs_srx)) * cpus);
292 s->irqs_stx = xzmalloc(sizeof(*(s->irqs_stx)) * cpus);
293 s->irqs = xzmalloc(sizeof(*(s->irqs)) * cpus);
294 s->cpu_user = xzmalloc(sizeof(*(s->cpu_user)) * cpus);
295 s->cpu_nice = xzmalloc(sizeof(*(s->cpu_nice)) * cpus);
296 s->cpu_sys = xzmalloc(sizeof(*(s->cpu_sys)) * cpus);
297 s->cpu_idle = xzmalloc(sizeof(*(s->cpu_idle)) * cpus);
298 s->cpu_iow = xzmalloc(sizeof(*(s->cpu_iow)) * cpus);
299 s->irqs_len = cpus;
303 static int irq_sstats(struct ifstat *s)
305 int i, rx = 0;
306 char *ptr, *ptr2;
307 char buff[4096];
309 FILE *fp = fopen("/proc/softirqs", "r");
310 if (!fp) {
311 whine("Cannot open /proc/softirqs!\n");
312 return -ENOENT;
315 stats_check_alloc(s);
317 memset(buff, 0, sizeof(buff));
318 while (fgets(buff, sizeof(buff), fp) != NULL) {
319 buff[sizeof(buff) - 1] = 0;
321 if ((ptr = strstr(buff, "NET_TX:")) == NULL) {
322 ptr = strstr(buff, "NET_RX:");
324 if (ptr == NULL)
325 continue;
326 rx = 1;
327 } else {
328 rx = 0;
331 ptr += strlen("NET_TX:");
333 for (i = 0; i < s->irqs_len; ++i) {
334 ptr++;
335 while (*ptr == ' ')
336 ptr++;
337 ptr2 = ptr;
338 while (*ptr != ' ' && *ptr != 0)
339 ptr++;
340 *ptr = 0;
341 if (rx)
342 s->irqs_srx[i] = atoi(ptr2);
343 else
344 s->irqs_stx[i] = atoi(ptr2);
347 memset(buff, 0, sizeof(buff));
350 fclose(fp);
352 return 0;
355 static int mem_stats(struct ifstat *s)
357 int ret;
358 unsigned long total, free;
359 char *ptr;
360 char buff[4096];
362 FILE *fp = fopen("/proc/meminfo", "r");
363 if (!fp) {
364 whine("Cannot open /proc/meminfo!\n");
365 return -ENOENT;
368 memset(buff, 0, sizeof(buff));
369 while (fgets(buff, sizeof(buff), fp) != NULL) {
370 buff[sizeof(buff) - 1] = 0;
372 if ((ptr = strstr(buff, "MemTotal:")) != NULL) {
373 ptr += strlen("MemTotal:");
374 ptr++;
376 while (*ptr == ' ')
377 ptr++;
379 ret = sscanf(ptr, "%lu", &total);
380 if (ret != 1)
381 total = 0;
382 } else if ((ptr = strstr(buff, "MemFree:")) != NULL) {
383 ptr += strlen("MemFree:");
384 ptr++;
386 while (*ptr == ' ')
387 ptr++;
389 ret = sscanf(ptr, "%lu", &free);
390 if (ret != 1)
391 free = 0;
394 memset(buff, 0, sizeof(buff));
397 if (total > 0)
398 s->mem_used = 100.f * (total - free) / total;
399 else
400 s->mem_used = 0.f;
402 fclose(fp);
404 return 0;
407 static int sys_stats(struct ifstat *s)
409 int ret, cpu;
410 char *ptr, *ptr2;
411 char buff[4096];
413 FILE *fp = fopen("/proc/stat", "r");
414 if (!fp) {
415 whine("Cannot open /proc/stat!\n");
416 return -ENOENT;
419 stats_check_alloc(s);
421 memset(buff, 0, sizeof(buff));
422 while (fgets(buff, sizeof(buff), fp) != NULL) {
423 buff[sizeof(buff) - 1] = 0;
425 if ((ptr = strstr(buff, "cpu")) != NULL) {
426 ptr += strlen("cpu");
427 if (*ptr == ' ')
428 goto next;
429 ptr2 = ptr;
431 while (*ptr != ' ' && *ptr != 0)
432 ptr++;
433 *ptr = 0;
435 cpu = atoi(ptr2);
436 if (cpu < 0 || cpu >= s->irqs_len)
437 goto next;
438 ptr++;
440 ret = sscanf(ptr, "%lu%lu%lu%lu%lu", &s->cpu_user[cpu],
441 &s->cpu_nice[cpu], &s->cpu_sys[cpu],
442 &s->cpu_idle[cpu], &s->cpu_iow[cpu]);
443 if (ret != 5)
444 goto next;
445 } else if ((ptr = strstr(buff, "ctxt")) != NULL) {
446 ptr += strlen("ctxt");
447 ptr++;
449 while (*ptr == ' ')
450 ptr++;
452 ret = sscanf(ptr, "%lu", &s->ctxt);
453 if (ret != 1)
454 s->ctxt = 0;
455 } else if ((ptr = strstr(buff, "processes")) != NULL) {
456 ptr += strlen("processes");
457 ptr++;
459 while (*ptr == ' ')
460 ptr++;
462 ret = sscanf(ptr, "%lu", &s->forks);
463 if (ret != 1)
464 s->forks = 0;
465 } else if ((ptr = strstr(buff, "procs_running")) != NULL) {
466 ptr += strlen("procs_running");
467 ptr++;
469 while (*ptr == ' ')
470 ptr++;
472 ret = sscanf(ptr, "%lu", &s->procs_run);
473 if (ret != 1)
474 s->procs_run = 0;
475 } else if ((ptr = strstr(buff, "procs_blocked")) != NULL) {
476 ptr += strlen("procs_blocked");
477 ptr++;
479 while (*ptr == ' ')
480 ptr++;
482 ret = sscanf(ptr, "%lu", &s->procs_iow);
483 if (ret != 1)
484 s->procs_iow = 0;
486 next:
487 memset(buff, 0, sizeof(buff));
490 fclose(fp);
492 return 0;
495 static int irq_stats(const char *ifname, struct ifstat *s)
497 int i;
498 char *ptr, *ptr2;
499 char buff[4096];
501 /* We exclude lo! */
502 if (!strncmp("lo", ifname, strlen("lo")))
503 return 0;
505 FILE *fp = fopen("/proc/interrupts", "r");
506 if (!fp) {
507 whine("Cannot open /proc/interrupts!\n");
508 return -ENOENT;
511 stats_check_alloc(s);
513 memset(buff, 0, sizeof(buff));
514 while (fgets(buff, sizeof(buff), fp) != NULL) {
515 buff[sizeof(buff) - 1] = 0;
517 if (strstr(buff, ifname) == NULL)
518 continue;
520 ptr = buff;
521 while (*ptr != ':')
522 ptr++;
523 *ptr = 0;
524 s->irq_nr = atoi(buff);
526 bug_on(s->irq_nr == 0);
528 for (i = 0; i < s->irqs_len; ++i) {
529 ptr++;
530 ptr2 = ptr;
531 while (*ptr == ' ')
532 ptr++;
533 while (*ptr != ' ' && *ptr != 0)
534 ptr++;
535 *ptr = 0;
536 s->irqs[i] = atoi(ptr2);
539 memset(buff, 0, sizeof(buff));
542 fclose(fp);
544 return 0;
547 static void diff_stats(struct ifstat *old, struct ifstat *new,
548 struct ifstat *diff)
550 int i;
552 if(old->irqs_len != new->irqs_len)
553 return; /* Refetch stats and take old diff! */
555 diff->rx_bytes = new->rx_bytes - old->rx_bytes;
556 diff->rx_packets = new->rx_packets - old->rx_packets;
557 diff->rx_drops = new->rx_drops - old->rx_drops;
558 diff->rx_errors = new->rx_errors - old->rx_errors;
559 diff->rx_fifo = new->rx_fifo - old->rx_fifo;
560 diff->rx_frame = new->rx_frame - old->rx_frame;
561 diff->rx_multi = new->rx_multi - old->rx_multi;
562 diff->tx_bytes = new->tx_bytes - old->tx_bytes;
563 diff->tx_packets = new->tx_packets - old->tx_packets;
564 diff->tx_drops = new->tx_drops - old->tx_drops;
565 diff->tx_errors = new->tx_errors - old->tx_errors;
566 diff->tx_fifo = new->tx_fifo - old->tx_fifo;
567 diff->tx_colls = new->tx_colls - old->tx_colls;
568 diff->tx_carrier = new->tx_carrier - old->tx_carrier;
569 diff->wifi_signal_level = new->wifi_signal_level - old->wifi_signal_level;
570 diff->wifi_noise_level = new->wifi_noise_level - old->wifi_noise_level;
571 diff->wifi_link_qual = new->wifi_link_qual - old->wifi_link_qual;
572 diff->ctxt = new->ctxt - old->ctxt;
573 diff->forks = new->forks - old->forks;
574 diff->procs_run = new->procs_run - old->procs_run;
575 diff->procs_iow = new->procs_iow - old->procs_iow;
577 stats_check_alloc(diff);
579 diff->irq_nr = new->irq_nr;
581 for (i = 0; i < diff->irqs_len; ++i) {
582 diff->irqs[i] = new->irqs[i] - old->irqs[i];
583 diff->irqs_srx[i] = new->irqs_srx[i] - old->irqs_srx[i];
584 diff->irqs_stx[i] = new->irqs_stx[i] - old->irqs_stx[i];
585 diff->cpu_user[i] = new->cpu_user[i] - old->cpu_user[i];
586 diff->cpu_nice[i] = new->cpu_nice[i] - old->cpu_nice[i];
587 diff->cpu_sys[i] = new->cpu_sys[i] - old->cpu_sys[i];
588 diff->cpu_idle[i] = new->cpu_idle[i] - old->cpu_idle[i];
589 diff->cpu_iow[i] = new->cpu_iow[i] - old->cpu_iow[i];
593 static void screen_init(WINDOW **screen)
595 (*screen) = initscr();
596 noecho();
597 cbreak();
598 nodelay((*screen), TRUE);
599 refresh();
600 wrefresh((*screen));
603 static void screen_update(WINDOW *screen, const char *ifname,
604 struct ifstat *s, struct ifstat *t,
605 int *first, double interval)
607 int i, j = 0;
609 curs_set(0);
610 mvwprintw(screen, 1, 2, "Kernel net/sys statistics for %s, t=%.2lfs",
611 ifname, interval);
612 attron(A_REVERSE);
613 mvwprintw(screen, 3, 0,
614 " RX: %16.3f MiB/t %10lu pkts/t %10lu drops/t %10lu errors/t ",
615 1.f * s->rx_bytes / (1 << 20), s->rx_packets, s->rx_drops,
616 s->rx_errors);
617 mvwprintw(screen, 4, 0,
618 " TX: %16.3f MiB/t %10lu pkts/t %10lu drops/t %10lu errors/t ",
619 1.f * s->tx_bytes / (1 << 20), s->tx_packets, s->tx_drops,
620 s->tx_errors);
621 attroff(A_REVERSE);
622 mvwprintw(screen, 6, 2,
623 "RX: %16.3f MiB %10lu pkts %10lu drops %10lu errors",
624 1.f * t->rx_bytes / (1 << 20), t->rx_packets, t->rx_drops,
625 t->rx_errors);
626 mvwprintw(screen, 7, 2,
627 "TX: %16.3f MiB %10lu pkts %10lu drops %10lu errors",
628 1.f * t->tx_bytes / (1 << 20), t->tx_packets, t->tx_drops,
629 t->tx_errors);
630 j = 9;
631 mvwprintw(screen, j++, 2, "SYS: %14ld cs/t %10.1f%% mem "
632 "%13ld running %10ld iowait",
633 s->ctxt, t->mem_used, t->procs_run, t->procs_iow);
634 j++;
635 if (s->irq_nr != 0) {
636 for(i = 0; i < s->irqs_len; ++i) {
637 unsigned long all = s->cpu_user[i] + s->cpu_nice[i] +
638 s->cpu_sys[i] + s->cpu_idle[i] +
639 s->cpu_iow[i];
640 mvwprintw(screen, j++, 2, "CPU%d: %13.1f%% usr/t "
641 "%9.1f%% sys/t %10.1f%% idl/t %11.1f%% iow/t ",
643 100.f * (s->cpu_user[i] + s->cpu_nice[i]) / all,
644 100.f * s->cpu_sys[i] / all,
645 100.f * s->cpu_idle[i] /all,
646 100.f * s->cpu_iow[i] / all);
648 j++;
649 for(i = 0; i < s->irqs_len; ++i)
650 mvwprintw(screen, j++, 2, "CPU%d: %14ld irqs/t "
651 "%15ld soirq RX/t %15ld soirq TX/t ",
652 i, s->irqs[i], s->irqs_srx[i], s->irqs_stx[i]);
653 j++;
654 for(i = 0; i < s->irqs_len; ++i)
655 mvwprintw(screen, j++, 2, "CPU%d: %14ld irqs",
656 i, t->irqs[i]);
657 j++;
659 if (t->wifi_bitrate > 0) {
660 mvwprintw(screen, j++, 2, "LinkQual: %7d/%d (%d/t) ",
661 t->wifi_link_qual, t->wifi_link_qual_max,
662 s->wifi_link_qual);
663 mvwprintw(screen, j++, 2, "Signal: %8d dBm (%d dBm/t) ",
664 t->wifi_signal_level, s->wifi_signal_level);
665 mvwprintw(screen, j++, 2, "Noise: %8d dBm (%d dBm/t) ",
666 t->wifi_noise_level, s->wifi_noise_level);
667 j++;
669 if (*first) {
670 mvwprintw(screen, 2, 2, "Collecting data ...");
671 *first = 0;
672 } else
673 mvwprintw(screen, 2, 2, " ");
675 wrefresh(screen);
676 refresh();
679 static void screen_end(void)
681 endwin();
684 static void print_update(const char *ifname, struct ifstat *s,
685 struct ifstat *t, double interval)
687 int i;
689 printf("RX: %16.3f MiB/t %10lu Pkts/t %10lu Drops/t %10lu Errors/t\n",
690 1.f * s->rx_bytes / (1 << 20), s->rx_packets, s->rx_drops,
691 s->rx_errors);
692 printf("TX: %16.3f MiB/t %10lu Pkts/t %10lu Drops/t %10lu Errors/t\n",
693 1.f * s->tx_bytes / (1 << 20), s->tx_packets, s->tx_drops,
694 s->tx_errors);
695 if (s->irq_nr != 0)
696 for(i = 0; i < s->irqs_len; ++i)
697 printf("CPU%d: %10ld IRQs/t "
698 "%10ld SoIRQ RX/t "
699 "%10ld SoIRQ TX/t\n", i,
700 s->irqs[i], s->irqs_srx[i], s->irqs_stx[i]);
701 if (t->wifi_bitrate > 0) {
702 printf("LinkQual: %6d/%d (%d/t)\n", t->wifi_link_qual,
703 t->wifi_link_qual_max, s->wifi_link_qual);
704 printf("Signal: %8d dBm (%d dBm/t)\n", t->wifi_signal_level,
705 s->wifi_signal_level);
706 printf("Noise: %8d dBm (%d dBm/t)\n", t->wifi_noise_level,
707 s->wifi_noise_level);
711 static void print_update_csv(const char *ifname, struct ifstat *s,
712 struct ifstat *t, double interval)
714 int i;
716 printf("%lu,%lu,%lu,%lu,", s->rx_bytes, s->rx_packets, s->rx_drops,
717 s->rx_errors);
718 printf("%lu,%lu,%lu,%lu", s->tx_bytes, s->tx_packets, s->tx_drops,
719 s->tx_errors);
720 if (s->irq_nr != 0)
721 for(i = 0; i < s->irqs_len; ++i)
722 printf(",%ld,%ld,%ld", s->irqs[i], s->irqs_srx[i],
723 s->irqs_stx[i]);
724 if (t->wifi_bitrate > 0) {
725 printf(",%d,%d", t->wifi_link_qual, t->wifi_link_qual_max);
726 printf(",%d", t->wifi_signal_level);
727 printf(",%d", t->wifi_noise_level);
729 printf("\n");
732 static void print_update_csv_hdr(const char *ifname, struct ifstat *s,
733 struct ifstat *t, double interval)
735 int i;
737 printf("RX Byte/t,RX Pkts/t,RX Drops/t,RX Errors/t,");
738 printf("TX Byte/t,TX Pkts/t,TX Drops/t,TX Errors/t");
739 if (s->irq_nr != 0)
740 for(i = 0; i < s->irqs_len; ++i)
741 printf(",CPU%d IRQs/t,CPU%d SoIRQ RX/t,"
742 "CPU%d SoIRQ TX/t", i, i, i);
743 if (t->wifi_bitrate > 0)
744 printf(",LinkQual,LinkQualMax,Signal Level,Noise Level");
745 printf("\n");
748 static inline int do_stats(const char *ifname, struct ifstat *s)
750 int ret = 0;
752 ret += rxtx_stats(ifname, s);
753 ret += irq_stats(ifname, s);
754 ret += irq_sstats(s);
755 ret += sys_stats(s);
756 ret += mem_stats(s);
757 ret += wifi_stats(ifname, s);
759 return ret;
762 static int screen_loop(const char *ifname, uint32_t interval)
764 int ret = 0, first = 1;
765 struct ifstat old, new, curr;
766 WINDOW *screen = NULL;
768 memset(&old, 0, sizeof(old));
769 memset(&new, 0, sizeof(new));
770 memset(&curr, 0, sizeof(curr));
772 screen_init(&screen);
774 while (!sigint) {
775 if (getch() == 'q')
776 goto out;
778 screen_update(screen, ifname, &curr, &new, &first, interval);
780 ret = do_stats(ifname, &old);
781 if (ret != 0)
782 goto out;
784 sleep(interval);
786 ret = do_stats(ifname, &new);
787 if (ret != 0)
788 goto out;
790 diff_stats(&old, &new, &curr);
792 out:
793 screen_end();
795 if (ret != 0)
796 whine("Error fetching stats!\n");
797 if (old.irqs)
798 xfree(old.irqs);
799 if (new.irqs)
800 xfree(new.irqs);
801 if (curr.irqs)
802 xfree(curr.irqs);
804 return 0;
807 static int print_loop(const char *ifname, uint32_t interval)
809 int ret, first = 1;
810 struct ifstat old, new, curr;
812 memset(&old, 0, sizeof(old));
813 memset(&new, 0, sizeof(new));
814 memset(&curr, 0, sizeof(curr));
815 do {
816 ret = do_stats(ifname, &old);
817 if (ret != 0)
818 goto out;
820 sleep(interval);
822 ret = do_stats(ifname, &new);
823 if (ret != 0)
824 goto out;
826 diff_stats(&old, &new, &curr);
828 if (first && (mode & TERM_MODE_CSV_HDR) ==
829 TERM_MODE_CSV_HDR) {
830 print_update_csv_hdr(ifname, &curr, &new, interval);
831 first = 0;
834 if ((mode & TERM_MODE_CSV) == TERM_MODE_CSV)
835 print_update_csv(ifname, &curr, &new, interval);
836 else if ((mode & TERM_MODE_NORMAL) == TERM_MODE_NORMAL)
837 print_update(ifname, &curr, &new, interval);
838 } while (loop && !sigint);
839 out:
840 if (ret != 0)
841 whine("Error fetching stats!\n");
842 if (old.irqs)
843 xfree(old.irqs);
844 if (new.irqs)
845 xfree(new.irqs);
846 if (curr.irqs)
847 xfree(curr.irqs);
849 return 0;
852 static void help(void)
854 printf("\nifpps %s, kernel networking and system statistics\n",
855 VERSION_STRING);
856 printf("http://www.netsniff-ng.org\n\n");
857 printf("Usage: ifpps [options] || ifpps <netdev>\n");
858 printf("Options:\n");
859 printf(" -d|--dev <netdev> Device to fetch statistics for i.e., eth0\n");
860 printf(" -p|--promisc Promiscuous mode\n");
861 printf(" -t|--interval <time> Refresh time in sec (default 1 s)\n");
862 printf(" -c|--term Output to terminal\n");
863 printf(" -C|--csv Output to terminal as CSV\n");
864 printf(" E.g. post-processing with Gnuplot et al.\n");
865 printf(" -H|--csv-tablehead Print CSV table head\n");
866 printf(" -l|--loop Loop terminal output\n");
867 printf(" -v|--version Print version\n");
868 printf(" -h|--help Print this help\n");
869 printf("\n");
870 printf("Examples:\n");
871 printf(" ifpps --dev eth0\n");
872 printf(" ifpps --dev eth0 --interval 60 --csv\n\n");
873 printf("Please report bugs to <bugs@netsniff-ng.org>\n");
874 printf("Copyright (C) 2009-2012 Daniel Borkmann <daniel@netsniff-ng.org>\n");
875 printf("License: GNU GPL version 2\n");
876 printf("This is free software: you are free to change and redistribute it.\n");
877 printf("There is NO WARRANTY, to the extent permitted by law.\n\n");
878 die();
881 static void version(void)
883 printf("\nifpps %s, kernel networking statistics per sec\n",
884 VERSION_STRING);
885 printf("http://www.netsniff-ng.org\n\n");
886 printf("Please report bugs to <bugs@netsniff-ng.org>\n");
887 printf("Copyright (C) 2009-2012 Daniel Borkmann <daniel@netsniff-ng.org>\n");
888 printf("License: GNU GPL version 2\n");
889 printf("This is free software: you are free to change and redistribute it.\n");
890 printf("There is NO WARRANTY, to the extent permitted by law.\n\n");
891 die();
894 int main(int argc, char **argv)
896 short ifflags = 0;
897 int c, opt_index, ret;
898 unsigned int promisc = 0;
899 char *ifname = NULL;
900 uint32_t interval = 1;
901 int (*main_loop)(const char *ifname, uint32_t interval) = screen_loop;
903 while ((c = getopt_long(argc, argv, short_options, long_options,
904 &opt_index)) != EOF) {
905 switch (c) {
906 case 'h':
907 help();
908 break;
909 case 'v':
910 version();
911 break;
912 case 'd':
913 ifname = xstrndup(optarg, IFNAMSIZ);
914 break;
915 case 't':
916 interval = atoi(optarg);
917 break;
918 case 'c':
919 mode |= TERM_MODE_NORMAL;
920 main_loop = print_loop;
921 break;
922 case 'l':
923 loop = 1;
924 break;
925 case 'p':
926 promisc = 1;
927 break;
928 case 'C':
929 mode |= TERM_MODE_CSV;
930 main_loop = print_loop;
931 break;
932 case 'H':
933 mode |= TERM_MODE_CSV_HDR;
934 main_loop = print_loop;
935 break;
936 case '?':
937 switch (optopt) {
938 case 'd':
939 case 't':
940 panic("Option -%c requires an argument!\n",
941 optopt);
942 default:
943 if (isprint(optopt))
944 whine("Unknown option character "
945 "`0x%X\'!\n", optopt);
946 die();
948 default:
949 break;
953 if (argc == 1)
954 help();
955 if (argc == 2)
956 ifname = xstrndup(argv[1], IFNAMSIZ);
957 if (ifname == NULL)
958 panic("No networking device given!\n");
959 if (!strncmp("lo", ifname, IFNAMSIZ))
960 panic("lo is not supported!\n");
961 if (device_mtu(ifname) == 0)
962 panic("This is no networking device!\n");
963 register_signal(SIGINT, signal_handler);
964 register_signal(SIGHUP, signal_handler);
965 if (promisc) {
966 check_for_root_maybe_die();
967 ifflags = enter_promiscuous_mode(ifname);
969 ret = main_loop(ifname, interval);
970 if (promisc)
971 leave_promiscuous_mode(ifname, ifflags);
972 xfree(ifname);
974 return ret;