trafgen: comment out unimplemented functions
[netsniff-ng.git] / src / ifpps.c
blob411c425ae66a6994e195084cec6a01348be5d957
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>
121 #include <time.h>
123 #include "die.h"
124 #include "xmalloc.h"
125 #include "xutils.h"
126 #include "xio.h"
127 #include "built_in.h"
130 * TODO: Cleanups, this got quite a hack over time.
133 #define TERM_MODE_NORMAL 1
134 #define TERM_MODE_CSV 2
135 #define TERM_MODE_CSV_HDR 4
137 #define USER_HZ sysconf(_SC_CLK_TCK)
139 struct ifstat {
140 unsigned long rx_bytes;
141 unsigned long rx_packets;
142 unsigned long rx_drops;
143 unsigned long rx_errors;
144 unsigned long rx_fifo;
145 unsigned long rx_frame;
146 unsigned long rx_multi;
147 unsigned long tx_bytes;
148 unsigned long tx_packets;
149 unsigned long tx_drops;
150 unsigned long tx_errors;
151 unsigned long tx_fifo;
152 unsigned long tx_colls;
153 unsigned long tx_carrier;
154 unsigned long irq_nr;
155 unsigned long *irqs;
156 unsigned long *irqs_srx;
157 unsigned long *irqs_stx;
158 unsigned long *cpu_user;
159 unsigned long *cpu_nice;
160 unsigned long *cpu_sys;
161 unsigned long *cpu_idle;
162 unsigned long *cpu_iow;
163 unsigned long ctxt;
164 unsigned long forks;
165 unsigned long procs_run;
166 unsigned long procs_iow;
167 size_t irqs_len;
168 float mem_used;
169 int wifi_bitrate;
170 int wifi_link_qual;
171 int wifi_link_qual_max;
172 int wifi_signal_level;
173 int wifi_noise_level;
176 static int mode = 0;
177 static int loop = 0;
179 volatile sig_atomic_t sigint = 0;
181 static const char *short_options = "d:t:vhcCHlp";
183 static struct option long_options[] = {
184 {"dev", required_argument, 0, 'd'},
185 {"interval", required_argument, 0, 't'},
186 {"loop", no_argument, 0, 'l'},
187 {"term", no_argument, 0, 'c'},
188 {"promisc", no_argument, 0, 'p'},
189 {"csv", no_argument, 0, 'C'},
190 {"csv-tablehead", no_argument, 0, 'H'},
191 {"version", no_argument, 0, 'v'},
192 {"help", no_argument, 0, 'h'},
193 {0, 0, 0, 0}
196 static void signal_handler(int number)
198 switch (number) {
199 case SIGINT:
200 sigint = 1;
201 break;
202 case SIGHUP:
203 break;
204 default:
205 break;
209 static int rxtx_stats(const char *ifname, struct ifstat *s)
211 int ret, found = -1;
212 char *ptr;
213 char buf[1024];
215 FILE *fp = fopen("/proc/net/dev", "r");
216 if (!fp) {
217 whine("Cannot open /proc/net/dev!\n");
218 return -ENOENT;
221 /* Omit header */
222 ptr = fgets(buf, sizeof(buf), fp);
223 ptr = fgets(buf, sizeof(buf), fp);
225 memset(buf, 0, sizeof(buf));
226 while (fgets(buf, sizeof(buf), fp) != NULL) {
227 buf[sizeof(buf) -1] = 0;
229 if (strstr(buf, ifname) == NULL)
230 continue;
232 ptr = buf;
233 while (*ptr != ':')
234 ptr++;
235 ptr++;
237 ret = sscanf(ptr, "%lu%lu%lu%lu%lu%lu%lu%*u%lu%lu%lu%lu%lu%lu%lu",
238 &s->rx_bytes, &s->rx_packets, &s->rx_errors,
239 &s->rx_drops, &s->rx_fifo, &s->rx_frame,
240 &s->rx_multi,
241 &s->tx_bytes, &s->tx_packets, &s->tx_errors,
242 &s->tx_drops, &s->tx_fifo, &s->tx_colls,
243 &s->tx_carrier);
244 if (ret == 14) {
245 found = 0;
246 break;
249 memset(buf, 0, sizeof(buf));
252 fclose(fp);
254 return found;
257 static int wifi_stats(const char *ifname, struct ifstat *s)
259 int ret;
260 struct iw_statistics ws;
262 ret = wireless_sigqual(ifname, &ws);
263 if (ret != 0) {
264 /* We don't want to trouble in case of eth* */
265 s->wifi_bitrate = 0;
266 return 0;
269 s->wifi_bitrate = wireless_bitrate(ifname);
270 s->wifi_signal_level = adjust_dbm_level(ws.qual.updated & IW_QUAL_DBM,
271 ws.qual.level);
272 s->wifi_noise_level = adjust_dbm_level(ws.qual.updated & IW_QUAL_DBM,
273 ws.qual.noise);
274 s->wifi_link_qual = ws.qual.qual;
275 s->wifi_link_qual_max = wireless_rangemax_sigqual(ifname);
277 return ret;
280 static void stats_check_alloc(struct ifstat *s)
282 int cpus = get_number_cpus();
284 if (s->irqs_len != get_number_cpus()) {
285 if (s->irqs) xfree(s->irqs);
286 if (s->irqs_srx) xfree(s->irqs_srx);
287 if (s->irqs_stx) xfree(s->irqs_stx);
288 if (s->cpu_user) xfree(s->cpu_user);
289 if (s->cpu_nice) xfree(s->cpu_nice);
290 if (s->cpu_sys) xfree(s->cpu_sys);
291 if (s->cpu_idle) xfree(s->cpu_idle);
292 if (s->cpu_iow) xfree(s->cpu_iow);
294 s->irqs_srx = xzmalloc(sizeof(*(s->irqs_srx)) * cpus);
295 s->irqs_stx = xzmalloc(sizeof(*(s->irqs_stx)) * cpus);
296 s->irqs = xzmalloc(sizeof(*(s->irqs)) * cpus);
297 s->cpu_user = xzmalloc(sizeof(*(s->cpu_user)) * cpus);
298 s->cpu_nice = xzmalloc(sizeof(*(s->cpu_nice)) * cpus);
299 s->cpu_sys = xzmalloc(sizeof(*(s->cpu_sys)) * cpus);
300 s->cpu_idle = xzmalloc(sizeof(*(s->cpu_idle)) * cpus);
301 s->cpu_iow = xzmalloc(sizeof(*(s->cpu_iow)) * cpus);
302 s->irqs_len = cpus;
306 static int irq_sstats(struct ifstat *s)
308 int i, rx = 0;
309 char *ptr, *ptr2;
310 char buff[4096];
312 FILE *fp = fopen("/proc/softirqs", "r");
313 if (!fp) {
314 whine("Cannot open /proc/softirqs!\n");
315 return -ENOENT;
318 stats_check_alloc(s);
320 memset(buff, 0, sizeof(buff));
321 while (fgets(buff, sizeof(buff), fp) != NULL) {
322 buff[sizeof(buff) - 1] = 0;
324 if ((ptr = strstr(buff, "NET_TX:")) == NULL) {
325 ptr = strstr(buff, "NET_RX:");
327 if (ptr == NULL)
328 continue;
329 rx = 1;
330 } else {
331 rx = 0;
334 ptr += strlen("NET_TX:");
336 for (i = 0; i < s->irqs_len; ++i) {
337 ptr++;
338 while (*ptr == ' ')
339 ptr++;
340 ptr2 = ptr;
341 while (*ptr != ' ' && *ptr != 0)
342 ptr++;
343 *ptr = 0;
344 if (rx)
345 s->irqs_srx[i] = atoi(ptr2);
346 else
347 s->irqs_stx[i] = atoi(ptr2);
350 memset(buff, 0, sizeof(buff));
353 fclose(fp);
355 return 0;
358 static int mem_stats(struct ifstat *s)
360 int ret;
361 unsigned long total, free;
362 char *ptr;
363 char buff[4096];
365 FILE *fp = fopen("/proc/meminfo", "r");
366 if (!fp) {
367 whine("Cannot open /proc/meminfo!\n");
368 return -ENOENT;
371 memset(buff, 0, sizeof(buff));
372 while (fgets(buff, sizeof(buff), fp) != NULL) {
373 buff[sizeof(buff) - 1] = 0;
375 if ((ptr = strstr(buff, "MemTotal:")) != NULL) {
376 ptr += strlen("MemTotal:");
377 ptr++;
379 while (*ptr == ' ')
380 ptr++;
382 ret = sscanf(ptr, "%lu", &total);
383 if (ret != 1)
384 total = 0;
385 } else if ((ptr = strstr(buff, "MemFree:")) != NULL) {
386 ptr += strlen("MemFree:");
387 ptr++;
389 while (*ptr == ' ')
390 ptr++;
392 ret = sscanf(ptr, "%lu", &free);
393 if (ret != 1)
394 free = 0;
397 memset(buff, 0, sizeof(buff));
400 if (total > 0)
401 s->mem_used = 100.f * (total - free) / total;
402 else
403 s->mem_used = 0.f;
405 fclose(fp);
407 return 0;
410 static int sys_stats(struct ifstat *s)
412 int ret, cpu;
413 char *ptr, *ptr2;
414 char buff[4096];
416 FILE *fp = fopen("/proc/stat", "r");
417 if (!fp) {
418 whine("Cannot open /proc/stat!\n");
419 return -ENOENT;
422 stats_check_alloc(s);
424 memset(buff, 0, sizeof(buff));
425 while (fgets(buff, sizeof(buff), fp) != NULL) {
426 buff[sizeof(buff) - 1] = 0;
428 if ((ptr = strstr(buff, "cpu")) != NULL) {
429 ptr += strlen("cpu");
430 if (*ptr == ' ')
431 goto next;
432 ptr2 = ptr;
434 while (*ptr != ' ' && *ptr != 0)
435 ptr++;
436 *ptr = 0;
438 cpu = atoi(ptr2);
439 if (cpu < 0 || cpu >= s->irqs_len)
440 goto next;
441 ptr++;
443 ret = sscanf(ptr, "%lu%lu%lu%lu%lu", &s->cpu_user[cpu],
444 &s->cpu_nice[cpu], &s->cpu_sys[cpu],
445 &s->cpu_idle[cpu], &s->cpu_iow[cpu]);
446 if (ret != 5)
447 goto next;
448 } else if ((ptr = strstr(buff, "ctxt")) != NULL) {
449 ptr += strlen("ctxt");
450 ptr++;
452 while (*ptr == ' ')
453 ptr++;
455 ret = sscanf(ptr, "%lu", &s->ctxt);
456 if (ret != 1)
457 s->ctxt = 0;
458 } else if ((ptr = strstr(buff, "processes")) != NULL) {
459 ptr += strlen("processes");
460 ptr++;
462 while (*ptr == ' ')
463 ptr++;
465 ret = sscanf(ptr, "%lu", &s->forks);
466 if (ret != 1)
467 s->forks = 0;
468 } else if ((ptr = strstr(buff, "procs_running")) != NULL) {
469 ptr += strlen("procs_running");
470 ptr++;
472 while (*ptr == ' ')
473 ptr++;
475 ret = sscanf(ptr, "%lu", &s->procs_run);
476 if (ret != 1)
477 s->procs_run = 0;
478 } else if ((ptr = strstr(buff, "procs_blocked")) != NULL) {
479 ptr += strlen("procs_blocked");
480 ptr++;
482 while (*ptr == ' ')
483 ptr++;
485 ret = sscanf(ptr, "%lu", &s->procs_iow);
486 if (ret != 1)
487 s->procs_iow = 0;
489 next:
490 memset(buff, 0, sizeof(buff));
493 fclose(fp);
495 return 0;
498 static int irq_stats(const char *ifname, struct ifstat *s)
500 int i;
501 char *ptr, *ptr2;
502 char buff[4096];
504 /* We exclude lo! */
505 if (!strncmp("lo", ifname, strlen("lo")))
506 return 0;
508 FILE *fp = fopen("/proc/interrupts", "r");
509 if (!fp) {
510 whine("Cannot open /proc/interrupts!\n");
511 return -ENOENT;
514 stats_check_alloc(s);
516 memset(buff, 0, sizeof(buff));
517 while (fgets(buff, sizeof(buff), fp) != NULL) {
518 buff[sizeof(buff) - 1] = 0;
520 if (strstr(buff, ifname) == NULL)
521 continue;
523 ptr = buff;
524 while (*ptr != ':')
525 ptr++;
526 *ptr = 0;
527 s->irq_nr = atoi(buff);
529 bug_on(s->irq_nr == 0);
531 for (i = 0; i < s->irqs_len; ++i) {
532 ptr++;
533 ptr2 = ptr;
534 while (*ptr == ' ')
535 ptr++;
536 while (*ptr != ' ' && *ptr != 0)
537 ptr++;
538 *ptr = 0;
539 s->irqs[i] = atoi(ptr2);
542 memset(buff, 0, sizeof(buff));
545 fclose(fp);
547 return 0;
550 static void diff_stats(struct ifstat *old, struct ifstat *new,
551 struct ifstat *diff)
553 int i;
555 if(old->irqs_len != new->irqs_len)
556 return; /* Refetch stats and take old diff! */
558 diff->rx_bytes = new->rx_bytes - old->rx_bytes;
559 diff->rx_packets = new->rx_packets - old->rx_packets;
560 diff->rx_drops = new->rx_drops - old->rx_drops;
561 diff->rx_errors = new->rx_errors - old->rx_errors;
562 diff->rx_fifo = new->rx_fifo - old->rx_fifo;
563 diff->rx_frame = new->rx_frame - old->rx_frame;
564 diff->rx_multi = new->rx_multi - old->rx_multi;
565 diff->tx_bytes = new->tx_bytes - old->tx_bytes;
566 diff->tx_packets = new->tx_packets - old->tx_packets;
567 diff->tx_drops = new->tx_drops - old->tx_drops;
568 diff->tx_errors = new->tx_errors - old->tx_errors;
569 diff->tx_fifo = new->tx_fifo - old->tx_fifo;
570 diff->tx_colls = new->tx_colls - old->tx_colls;
571 diff->tx_carrier = new->tx_carrier - old->tx_carrier;
572 diff->wifi_signal_level = new->wifi_signal_level - old->wifi_signal_level;
573 diff->wifi_noise_level = new->wifi_noise_level - old->wifi_noise_level;
574 diff->wifi_link_qual = new->wifi_link_qual - old->wifi_link_qual;
575 diff->ctxt = new->ctxt - old->ctxt;
576 diff->forks = new->forks - old->forks;
577 diff->procs_run = new->procs_run - old->procs_run;
578 diff->procs_iow = new->procs_iow - old->procs_iow;
580 stats_check_alloc(diff);
582 diff->irq_nr = new->irq_nr;
584 for (i = 0; i < diff->irqs_len; ++i) {
585 diff->irqs[i] = new->irqs[i] - old->irqs[i];
586 diff->irqs_srx[i] = new->irqs_srx[i] - old->irqs_srx[i];
587 diff->irqs_stx[i] = new->irqs_stx[i] - old->irqs_stx[i];
588 diff->cpu_user[i] = new->cpu_user[i] - old->cpu_user[i];
589 diff->cpu_nice[i] = new->cpu_nice[i] - old->cpu_nice[i];
590 diff->cpu_sys[i] = new->cpu_sys[i] - old->cpu_sys[i];
591 diff->cpu_idle[i] = new->cpu_idle[i] - old->cpu_idle[i];
592 diff->cpu_iow[i] = new->cpu_iow[i] - old->cpu_iow[i];
596 static char *snr_to_str(int level)
598 // empirical values
599 if (level > 40)
600 return "very good signal";
601 if (level > 25 && level <= 40)
602 return "good signal";
603 if (level > 15 && level <= 25)
604 return "poor signal";
605 if (level > 10 && level <= 15)
606 return "very poor signal";
607 if (level <= 10)
608 return "no signal";
609 /* unreachable */
610 return "unknown";
613 static void screen_init(WINDOW **screen)
615 (*screen) = initscr();
616 noecho();
617 cbreak();
618 nodelay((*screen), TRUE);
619 refresh();
620 wrefresh((*screen));
623 static void screen_update(WINDOW *screen, const char *ifname,
624 struct ifstat *s, struct ifstat *t,
625 int *first, double interval)
627 int i, j = 0;
629 curs_set(0);
630 mvwprintw(screen, 1, 2, "Kernel net/sys statistics for %s, t=%.2lfs",
631 ifname, interval);
632 attron(A_REVERSE);
633 mvwprintw(screen, 3, 0,
634 " RX: %16.3f MiB/t %10lu pkts/t %10lu drops/t %10lu errors/t ",
635 1.f * s->rx_bytes / (1 << 20), s->rx_packets, s->rx_drops,
636 s->rx_errors);
637 mvwprintw(screen, 4, 0,
638 " TX: %16.3f MiB/t %10lu pkts/t %10lu drops/t %10lu errors/t ",
639 1.f * s->tx_bytes / (1 << 20), s->tx_packets, s->tx_drops,
640 s->tx_errors);
641 attroff(A_REVERSE);
642 mvwprintw(screen, 6, 2,
643 "RX: %16.3f MiB %10lu pkts %10lu drops %10lu errors",
644 1.f * t->rx_bytes / (1 << 20), t->rx_packets, t->rx_drops,
645 t->rx_errors);
646 mvwprintw(screen, 7, 2,
647 "TX: %16.3f MiB %10lu pkts %10lu drops %10lu errors",
648 1.f * t->tx_bytes / (1 << 20), t->tx_packets, t->tx_drops,
649 t->tx_errors);
650 j = 9;
651 mvwprintw(screen, j++, 2, "SYS: %14ld cs/t %10.1f%% mem "
652 "%13ld running %10ld iowait",
653 s->ctxt, t->mem_used, t->procs_run, t->procs_iow);
654 j++;
655 if (s->irq_nr != 0) {
656 for(i = 0; i < s->irqs_len; ++i) {
657 unsigned long all = s->cpu_user[i] + s->cpu_nice[i] +
658 s->cpu_sys[i] + s->cpu_idle[i] +
659 s->cpu_iow[i];
660 mvwprintw(screen, j++, 2, "CPU%d: %13.1f%% usr/t "
661 "%9.1f%% sys/t %10.1f%% idl/t %11.1f%% iow/t ",
663 100.f * (s->cpu_user[i] + s->cpu_nice[i]) / all,
664 100.f * s->cpu_sys[i] / all,
665 100.f * s->cpu_idle[i] /all,
666 100.f * s->cpu_iow[i] / all);
668 j++;
669 for(i = 0; i < s->irqs_len; ++i)
670 mvwprintw(screen, j++, 2, "CPU%d: %14ld irqs/t "
671 "%15ld soirq RX/t %15ld soirq TX/t ",
672 i, s->irqs[i], s->irqs_srx[i], s->irqs_stx[i]);
673 j++;
674 for(i = 0; i < s->irqs_len; ++i)
675 mvwprintw(screen, j++, 2, "CPU%d: %14ld irqs",
676 i, t->irqs[i]);
677 j++;
679 if (t->wifi_bitrate > 0) {
680 mvwprintw(screen, j++, 2, "LinkQual: %7d/%d (%d/t) ",
681 t->wifi_link_qual, t->wifi_link_qual_max,
682 s->wifi_link_qual);
683 mvwprintw(screen, j++, 2, "Signal: %8d dBm (%d dBm/t) ",
684 t->wifi_signal_level, s->wifi_signal_level);
685 mvwprintw(screen, j++, 2, "Noise: %8d dBm (%d dBm/t) ",
686 t->wifi_noise_level, s->wifi_noise_level);
687 mvwprintw(screen, j++, 2, "SNR: %8d dBm (%s) ",
688 t->wifi_signal_level - t->wifi_noise_level,
689 snr_to_str(t->wifi_signal_level - t->wifi_noise_level));
690 j++;
692 if (*first) {
693 mvwprintw(screen, 2, 2, "Collecting data ...");
694 *first = 0;
695 } else
696 mvwprintw(screen, 2, 2, " ");
698 wrefresh(screen);
699 refresh();
702 static void screen_end(void)
704 endwin();
707 static void print_update(const char *ifname, struct ifstat *s,
708 struct ifstat *t, double interval)
710 int i;
712 printf("RX: %16.3f MiB/t %10lu Pkts/t %10lu Drops/t %10lu Errors/t\n",
713 1.f * s->rx_bytes / (1 << 20), s->rx_packets, s->rx_drops,
714 s->rx_errors);
715 printf("TX: %16.3f MiB/t %10lu Pkts/t %10lu Drops/t %10lu Errors/t\n",
716 1.f * s->tx_bytes / (1 << 20), s->tx_packets, s->tx_drops,
717 s->tx_errors);
718 if (s->irq_nr != 0)
719 for(i = 0; i < s->irqs_len; ++i)
720 printf("CPU%d: %10ld IRQs/t "
721 "%10ld SoIRQ RX/t "
722 "%10ld SoIRQ TX/t\n", i,
723 s->irqs[i], s->irqs_srx[i], s->irqs_stx[i]);
724 if (t->wifi_bitrate > 0) {
725 printf("LinkQual: %6d/%d (%d/t)\n", t->wifi_link_qual,
726 t->wifi_link_qual_max, s->wifi_link_qual);
727 printf("Signal: %8d dBm (%d dBm/t)\n", t->wifi_signal_level,
728 s->wifi_signal_level);
729 printf("Noise: %8d dBm (%d dBm/t)\n", t->wifi_noise_level,
730 s->wifi_noise_level);
734 static void print_update_csv(const char *ifname, struct ifstat *s,
735 struct ifstat *t, double interval)
737 int i;
739 printf("%ld,%lu,%lu,%lu,%lu,", time(0), s->rx_bytes, s->rx_packets,
740 s->rx_drops, s->rx_errors);
741 printf("%lu,%lu,%lu,%lu", s->tx_bytes, s->tx_packets, s->tx_drops,
742 s->tx_errors);
743 if (s->irq_nr != 0)
744 for(i = 0; i < s->irqs_len; ++i)
745 printf(",%ld,%ld,%ld", s->irqs[i], s->irqs_srx[i],
746 s->irqs_stx[i]);
747 if (t->wifi_bitrate > 0) {
748 printf(",%d,%d", t->wifi_link_qual, t->wifi_link_qual_max);
749 printf(",%d", t->wifi_signal_level);
750 printf(",%d", t->wifi_noise_level);
752 printf("\n");
755 static void print_update_csv_hdr(const char *ifname, struct ifstat *s,
756 struct ifstat *t, double interval)
758 int i;
760 printf("Unixtime,RX Byte/t,RX Pkts/t,RX Drops/t,RX Errors/t,");
761 printf("TX Byte/t,TX Pkts/t,TX Drops/t,TX Errors/t");
762 if (s->irq_nr != 0)
763 for(i = 0; i < s->irqs_len; ++i)
764 printf(",CPU%d IRQs/t,CPU%d SoIRQ RX/t,"
765 "CPU%d SoIRQ TX/t", i, i, i);
766 if (t->wifi_bitrate > 0)
767 printf(",LinkQual,LinkQualMax,Signal Level,Noise Level");
768 printf("\n");
771 static inline int do_stats(const char *ifname, struct ifstat *s)
773 int ret = 0;
775 ret += rxtx_stats(ifname, s);
776 ret += irq_stats(ifname, s);
777 ret += irq_sstats(s);
778 ret += sys_stats(s);
779 ret += mem_stats(s);
780 ret += wifi_stats(ifname, s);
782 return ret;
785 static int screen_loop(const char *ifname, uint32_t interval)
787 int ret = 0, first = 1;
788 struct ifstat old, new, curr;
789 WINDOW *screen = NULL;
791 memset(&old, 0, sizeof(old));
792 memset(&new, 0, sizeof(new));
793 memset(&curr, 0, sizeof(curr));
795 screen_init(&screen);
797 while (!sigint) {
798 if (getch() == 'q')
799 goto out;
801 screen_update(screen, ifname, &curr, &new, &first, interval);
803 ret = do_stats(ifname, &old);
804 if (ret != 0)
805 goto out;
807 sleep(interval);
809 ret = do_stats(ifname, &new);
810 if (ret != 0)
811 goto out;
813 diff_stats(&old, &new, &curr);
815 out:
816 screen_end();
818 if (ret != 0)
819 whine("Error fetching stats!\n");
820 if (old.irqs)
821 xfree(old.irqs);
822 if (new.irqs)
823 xfree(new.irqs);
824 if (curr.irqs)
825 xfree(curr.irqs);
827 return 0;
830 static int print_loop(const char *ifname, uint32_t interval)
832 int ret, first = 1;
833 struct ifstat old, new, curr;
835 memset(&old, 0, sizeof(old));
836 memset(&new, 0, sizeof(new));
837 memset(&curr, 0, sizeof(curr));
838 do {
839 ret = do_stats(ifname, &old);
840 if (ret != 0)
841 goto out;
843 sleep(interval);
845 ret = do_stats(ifname, &new);
846 if (ret != 0)
847 goto out;
849 diff_stats(&old, &new, &curr);
851 if (first && (mode & TERM_MODE_CSV_HDR) ==
852 TERM_MODE_CSV_HDR) {
853 print_update_csv_hdr(ifname, &curr, &new, interval);
854 first = 0;
857 if ((mode & TERM_MODE_CSV) == TERM_MODE_CSV)
858 print_update_csv(ifname, &curr, &new, interval);
859 else if ((mode & TERM_MODE_NORMAL) == TERM_MODE_NORMAL)
860 print_update(ifname, &curr, &new, interval);
861 } while (loop && !sigint);
862 out:
863 if (ret != 0)
864 whine("Error fetching stats!\n");
865 if (old.irqs)
866 xfree(old.irqs);
867 if (new.irqs)
868 xfree(new.irqs);
869 if (curr.irqs)
870 xfree(curr.irqs);
872 return 0;
875 static void help(void)
877 printf("\nifpps %s, kernel networking and system statistics\n",
878 VERSION_STRING);
879 printf("http://www.netsniff-ng.org\n\n");
880 printf("Usage: ifpps [options] || ifpps <netdev>\n");
881 printf("Options:\n");
882 printf(" -d|--dev <netdev> Device to fetch statistics for i.e., eth0\n");
883 printf(" -p|--promisc Promiscuous mode\n");
884 printf(" -t|--interval <time> Refresh time in sec (default 1 s)\n");
885 printf(" -c|--term Output to terminal\n");
886 printf(" -C|--csv Output to terminal as CSV\n");
887 printf(" E.g. post-processing with Gnuplot et al.\n");
888 printf(" -H|--csv-tablehead Print CSV table head\n");
889 printf(" -l|--loop Loop terminal output\n");
890 printf(" -v|--version Print version\n");
891 printf(" -h|--help Print this help\n");
892 printf("\n");
893 printf("Examples:\n");
894 printf(" ifpps --dev eth0\n");
895 printf(" ifpps --dev eth0 --interval 60 --csv\n\n");
896 printf("Please report bugs to <bugs@netsniff-ng.org>\n");
897 printf("Copyright (C) 2009-2012 Daniel Borkmann <daniel@netsniff-ng.org>\n");
898 printf("License: GNU GPL version 2\n");
899 printf("This is free software: you are free to change and redistribute it.\n");
900 printf("There is NO WARRANTY, to the extent permitted by law.\n\n");
901 die();
904 static void version(void)
906 printf("\nifpps %s, kernel networking statistics per sec\n",
907 VERSION_STRING);
908 printf("http://www.netsniff-ng.org\n\n");
909 printf("Please report bugs to <bugs@netsniff-ng.org>\n");
910 printf("Copyright (C) 2009-2012 Daniel Borkmann <daniel@netsniff-ng.org>\n");
911 printf("License: GNU GPL version 2\n");
912 printf("This is free software: you are free to change and redistribute it.\n");
913 printf("There is NO WARRANTY, to the extent permitted by law.\n\n");
914 die();
917 int main(int argc, char **argv)
919 short ifflags = 0;
920 int c, opt_index, ret;
921 unsigned int promisc = 0;
922 char *ifname = NULL;
923 uint32_t interval = 1;
924 int (*main_loop)(const char *ifname, uint32_t interval) = screen_loop;
926 while ((c = getopt_long(argc, argv, short_options, long_options,
927 &opt_index)) != EOF) {
928 switch (c) {
929 case 'h':
930 help();
931 break;
932 case 'v':
933 version();
934 break;
935 case 'd':
936 ifname = xstrndup(optarg, IFNAMSIZ);
937 break;
938 case 't':
939 interval = atoi(optarg);
940 break;
941 case 'c':
942 mode |= TERM_MODE_NORMAL;
943 main_loop = print_loop;
944 break;
945 case 'l':
946 loop = 1;
947 break;
948 case 'p':
949 promisc = 1;
950 break;
951 case 'C':
952 mode |= TERM_MODE_CSV;
953 main_loop = print_loop;
954 break;
955 case 'H':
956 mode |= TERM_MODE_CSV_HDR;
957 main_loop = print_loop;
958 break;
959 case '?':
960 switch (optopt) {
961 case 'd':
962 case 't':
963 panic("Option -%c requires an argument!\n",
964 optopt);
965 default:
966 if (isprint(optopt))
967 whine("Unknown option character "
968 "`0x%X\'!\n", optopt);
969 die();
971 default:
972 break;
976 if (argc == 1)
977 help();
978 if (argc == 2)
979 ifname = xstrndup(argv[1], IFNAMSIZ);
980 if (ifname == NULL)
981 panic("No networking device given!\n");
982 if (!strncmp("lo", ifname, IFNAMSIZ))
983 panic("lo is not supported!\n");
984 if (device_mtu(ifname) == 0)
985 panic("This is no networking device!\n");
986 register_signal(SIGINT, signal_handler);
987 register_signal(SIGHUP, signal_handler);
988 if (promisc) {
989 check_for_root_maybe_die();
990 ifflags = enter_promiscuous_mode(ifname);
992 ret = main_loop(ifname, interval);
993 if (promisc)
994 leave_promiscuous_mode(ifname, ifflags);
995 xfree(ifname);
997 return ret;