all: minor: updated copyright year
[netsniff-ng.git] / src / ifpps.c
blob98afcb796ab6c79133ab0dd3b0be6f301e37e900
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 <assert.h>
123 #include "die.h"
124 #include "xmalloc.h"
125 #include "xsys.h"
126 #include "xio.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;
219 ptr = fgets(buf, sizeof(buf), fp);
220 ptr = fgets(buf, sizeof(buf), fp);
221 memset(buf, 0, sizeof(buf));
222 while (fgets(buf, sizeof(buf), fp) != NULL) {
223 buf[sizeof(buf) -1] = 0;
224 if (strstr(buf, ifname) == NULL)
225 continue;
226 ptr = buf;
227 while (*ptr != ':')
228 ptr++;
229 ptr++;
230 ret = sscanf(ptr, "%lu%lu%lu%lu%lu%lu%lu%*u%lu%lu%lu%lu%lu%lu%lu",
231 &s->rx_bytes, &s->rx_packets, &s->rx_errors,
232 &s->rx_drops, &s->rx_fifo, &s->rx_frame,
233 &s->rx_multi,
234 &s->tx_bytes, &s->tx_packets, &s->tx_errors,
235 &s->tx_drops, &s->tx_fifo, &s->tx_colls,
236 &s->tx_carrier);
237 if (ret == 14) {
238 found = 0;
239 break;
241 memset(buf, 0, sizeof(buf));
243 fclose(fp);
244 return found;
247 static int wifi_stats(const char *ifname, struct ifstat *s)
249 int ret;
250 struct iw_statistics ws;
252 ret = wireless_sigqual(ifname, &ws);
253 if (ret != 0) {
254 /* We don't want to trouble in case of eth* */
255 s->wifi_bitrate = 0;
256 return 0;
258 s->wifi_bitrate = wireless_bitrate(ifname);
259 s->wifi_signal_level = adjust_dbm_level(ws.qual.level);
260 s->wifi_noise_level = adjust_dbm_level(ws.qual.noise);
261 s->wifi_link_qual = ws.qual.qual;
262 s->wifi_link_qual_max = wireless_rangemax_sigqual(ifname);
263 return ret;
266 static void stats_check_alloc(struct ifstat *s)
268 int cpus = get_number_cpus();
270 if (s->irqs_len != get_number_cpus()) {
271 if (s->irqs) xfree(s->irqs);
272 if (s->irqs_srx) xfree(s->irqs_srx);
273 if (s->irqs_stx) xfree(s->irqs_stx);
274 if (s->cpu_user) xfree(s->cpu_user);
275 if (s->cpu_nice) xfree(s->cpu_nice);
276 if (s->cpu_sys) xfree(s->cpu_sys);
277 if (s->cpu_idle) xfree(s->cpu_idle);
278 if (s->cpu_iow) xfree(s->cpu_iow);
279 s->irqs_srx = xzmalloc(sizeof(*(s->irqs_srx)) * cpus);
280 s->irqs_stx = xzmalloc(sizeof(*(s->irqs_stx)) * cpus);
281 s->irqs = xzmalloc(sizeof(*(s->irqs)) * cpus);
282 s->cpu_user = xzmalloc(sizeof(*(s->cpu_user)) * cpus);
283 s->cpu_nice = xzmalloc(sizeof(*(s->cpu_nice)) * cpus);
284 s->cpu_sys = xzmalloc(sizeof(*(s->cpu_sys)) * cpus);
285 s->cpu_idle = xzmalloc(sizeof(*(s->cpu_idle)) * cpus);
286 s->cpu_iow = xzmalloc(sizeof(*(s->cpu_iow)) * cpus);
287 s->irqs_len = cpus;
291 static int irq_sstats(struct ifstat *s)
293 int i, rx = 0;
294 char *ptr, *ptr2;
295 char buff[4096];
297 FILE *fp = fopen("/proc/softirqs", "r");
298 if (!fp) {
299 whine("Cannot open /proc/softirqs!\n");
300 return -ENOENT;
302 stats_check_alloc(s);
303 memset(buff, 0, sizeof(buff));
304 while (fgets(buff, sizeof(buff), fp) != NULL) {
305 buff[sizeof(buff) - 1] = 0;
306 if ((ptr = strstr(buff, "NET_TX:")) == NULL) {
307 ptr = strstr(buff, "NET_RX:");
308 if (ptr == NULL)
309 continue;
310 rx = 1;
311 } else
312 rx = 0;
313 ptr += strlen("NET_TX:");
314 for (i = 0; i < s->irqs_len; ++i) {
315 ptr++;
316 while (*ptr == ' ')
317 ptr++;
318 ptr2 = ptr;
319 while (*ptr != ' ' && *ptr != 0)
320 ptr++;
321 *ptr = 0;
322 if (rx)
323 s->irqs_srx[i] = atoi(ptr2);
324 else
325 s->irqs_stx[i] = atoi(ptr2);
327 memset(buff, 0, sizeof(buff));
329 fclose(fp);
330 return 0;
333 static int mem_stats(struct ifstat *s)
335 int ret;
336 unsigned long total, free;
337 char *ptr;
338 char buff[4096];
340 FILE *fp = fopen("/proc/meminfo", "r");
341 if (!fp) {
342 whine("Cannot open /proc/meminfo!\n");
343 return -ENOENT;
345 memset(buff, 0, sizeof(buff));
346 while (fgets(buff, sizeof(buff), fp) != NULL) {
347 buff[sizeof(buff) - 1] = 0;
348 if ((ptr = strstr(buff, "MemTotal:")) != NULL) {
349 ptr += strlen("MemTotal:");
350 ptr++;
351 while (*ptr == ' ')
352 ptr++;
353 ret = sscanf(ptr, "%lu", &total);
354 if (ret != 1)
355 total = 0;
356 } else if ((ptr = strstr(buff, "MemFree:")) != NULL) {
357 ptr += strlen("MemFree:");
358 ptr++;
359 while (*ptr == ' ')
360 ptr++;
361 ret = sscanf(ptr, "%lu", &free);
362 if (ret != 1)
363 free = 0;
365 memset(buff, 0, sizeof(buff));
367 if (total > 0)
368 s->mem_used = 100.f * (total - free) / total;
369 else
370 s->mem_used = 0.f;
371 fclose(fp);
372 return 0;
375 static int sys_stats(struct ifstat *s)
377 int ret, cpu;
378 char *ptr, *ptr2;
379 char buff[4096];
381 FILE *fp = fopen("/proc/stat", "r");
382 if (!fp) {
383 whine("Cannot open /proc/stat!\n");
384 return -ENOENT;
386 stats_check_alloc(s);
387 memset(buff, 0, sizeof(buff));
388 while (fgets(buff, sizeof(buff), fp) != NULL) {
389 buff[sizeof(buff) - 1] = 0;
390 if ((ptr = strstr(buff, "cpu")) != NULL) {
391 ptr += strlen("cpu");
392 if (*ptr == ' ')
393 goto next;
394 ptr2 = ptr;
395 while (*ptr != ' ' && *ptr != 0)
396 ptr++;
397 *ptr = 0;
398 cpu = atoi(ptr2);
399 if (cpu < 0 || cpu >= s->irqs_len)
400 goto next;
401 ptr++;
402 ret = sscanf(ptr, "%lu%lu%lu%lu%lu", &s->cpu_user[cpu],
403 &s->cpu_nice[cpu], &s->cpu_sys[cpu],
404 &s->cpu_idle[cpu], &s->cpu_iow[cpu]);
405 if (ret != 5)
406 goto next;
407 } else if ((ptr = strstr(buff, "ctxt")) != NULL) {
408 ptr += strlen("ctxt");
409 ptr++;
410 while (*ptr == ' ')
411 ptr++;
412 ret = sscanf(ptr, "%lu", &s->ctxt);
413 if (ret != 1)
414 s->ctxt = 0;
415 } else if ((ptr = strstr(buff, "processes")) != NULL) {
416 ptr += strlen("processes");
417 ptr++;
418 while (*ptr == ' ')
419 ptr++;
420 ret = sscanf(ptr, "%lu", &s->forks);
421 if (ret != 1)
422 s->forks = 0;
423 } else if ((ptr = strstr(buff, "procs_running")) != NULL) {
424 ptr += strlen("procs_running");
425 ptr++;
426 while (*ptr == ' ')
427 ptr++;
428 ret = sscanf(ptr, "%lu", &s->procs_run);
429 if (ret != 1)
430 s->procs_run = 0;
431 } else if ((ptr = strstr(buff, "procs_blocked")) != NULL) {
432 ptr += strlen("procs_blocked");
433 ptr++;
434 while (*ptr == ' ')
435 ptr++;
436 ret = sscanf(ptr, "%lu", &s->procs_iow);
437 if (ret != 1)
438 s->procs_iow = 0;
440 next:
441 memset(buff, 0, sizeof(buff));
443 fclose(fp);
444 return 0;
447 static int irq_stats(const char *ifname, struct ifstat *s)
449 int i;
450 char *ptr, *ptr2;
451 char buff[4096];
453 /* We exclude lo! */
454 if (!strncmp("lo", ifname, strlen("lo")))
455 return 0;
456 FILE *fp = fopen("/proc/interrupts", "r");
457 if (!fp) {
458 whine("Cannot open /proc/interrupts!\n");
459 return -ENOENT;
461 stats_check_alloc(s);
462 memset(buff, 0, sizeof(buff));
463 while (fgets(buff, sizeof(buff), fp) != NULL) {
464 buff[sizeof(buff) - 1] = 0;
465 if (strstr(buff, ifname) == NULL)
466 continue;
467 ptr = buff;
468 while (*ptr != ':')
469 ptr++;
470 *ptr = 0;
471 s->irq_nr = atoi(buff);
472 assert(s->irq_nr != 0);
473 for (i = 0; i < s->irqs_len; ++i) {
474 ptr++;
475 ptr2 = ptr;
476 while (*ptr == ' ')
477 ptr++;
478 while (*ptr != ' ' && *ptr != 0)
479 ptr++;
480 *ptr = 0;
481 s->irqs[i] = atoi(ptr2);
483 memset(buff, 0, sizeof(buff));
485 fclose(fp);
486 return 0;
489 static void diff_stats(struct ifstat *old, struct ifstat *new,
490 struct ifstat *diff)
492 int i;
493 if(old->irqs_len != new->irqs_len)
494 return; /* Refetch stats and take old diff! */
495 diff->rx_bytes = new->rx_bytes - old->rx_bytes;
496 diff->rx_packets = new->rx_packets - old->rx_packets;
497 diff->rx_drops = new->rx_drops - old->rx_drops;
498 diff->rx_errors = new->rx_errors - old->rx_errors;
499 diff->rx_fifo = new->rx_fifo - old->rx_fifo;
500 diff->rx_frame = new->rx_frame - old->rx_frame;
501 diff->rx_multi = new->rx_multi - old->rx_multi;
502 diff->tx_bytes = new->tx_bytes - old->tx_bytes;
503 diff->tx_packets = new->tx_packets - old->tx_packets;
504 diff->tx_drops = new->tx_drops - old->tx_drops;
505 diff->tx_errors = new->tx_errors - old->tx_errors;
506 diff->tx_fifo = new->tx_fifo - old->tx_fifo;
507 diff->tx_colls = new->tx_colls - old->tx_colls;
508 diff->tx_carrier = new->tx_carrier - old->tx_carrier;
509 diff->wifi_signal_level = new->wifi_signal_level - old->wifi_signal_level;
510 diff->wifi_noise_level = new->wifi_noise_level - old->wifi_noise_level;
511 diff->wifi_link_qual = new->wifi_link_qual - old->wifi_link_qual;
512 diff->ctxt = new->ctxt - old->ctxt;
513 diff->forks = new->forks - old->forks;
514 diff->procs_run = new->procs_run - old->procs_run;
515 diff->procs_iow = new->procs_iow - old->procs_iow;
516 stats_check_alloc(diff);
517 diff->irq_nr = new->irq_nr;
518 for (i = 0; i < diff->irqs_len; ++i) {
519 diff->irqs[i] = new->irqs[i] - old->irqs[i];
520 diff->irqs_srx[i] = new->irqs_srx[i] - old->irqs_srx[i];
521 diff->irqs_stx[i] = new->irqs_stx[i] - old->irqs_stx[i];
522 diff->cpu_user[i] = new->cpu_user[i] - old->cpu_user[i];
523 diff->cpu_nice[i] = new->cpu_nice[i] - old->cpu_nice[i];
524 diff->cpu_sys[i] = new->cpu_sys[i] - old->cpu_sys[i];
525 diff->cpu_idle[i] = new->cpu_idle[i] - old->cpu_idle[i];
526 diff->cpu_iow[i] = new->cpu_iow[i] - old->cpu_iow[i];
530 static void screen_init(WINDOW **screen)
532 (*screen) = initscr();
533 noecho();
534 cbreak();
535 nodelay((*screen), TRUE);
536 refresh();
537 wrefresh((*screen));
540 static void screen_update(WINDOW *screen, const char *ifname,
541 struct ifstat *s, struct ifstat *t,
542 int *first, double interval)
544 int i, j = 0;
546 curs_set(0);
547 mvwprintw(screen, 1, 2, "Kernel net/sys statistics for %s, t=%.2lfs",
548 ifname, interval);
549 attron(A_REVERSE);
550 mvwprintw(screen, 3, 0,
551 " RX: %16.3f MiB/t %10lu pkts/t %10lu drops/t %10lu errors/t ",
552 1.f * s->rx_bytes / (1 << 20), s->rx_packets, s->rx_drops,
553 s->rx_errors);
554 mvwprintw(screen, 4, 0,
555 " TX: %16.3f MiB/t %10lu pkts/t %10lu drops/t %10lu errors/t ",
556 1.f * s->tx_bytes / (1 << 20), s->tx_packets, s->tx_drops,
557 s->tx_errors);
558 attroff(A_REVERSE);
559 mvwprintw(screen, 6, 2,
560 "RX: %16.3f MiB %10lu pkts %10lu drops %10lu errors",
561 1.f * t->rx_bytes / (1 << 20), t->rx_packets, t->rx_drops,
562 t->rx_errors);
563 mvwprintw(screen, 7, 2,
564 "TX: %16.3f MiB %10lu pkts %10lu drops %10lu errors",
565 1.f * t->tx_bytes / (1 << 20), t->tx_packets, t->tx_drops,
566 t->tx_errors);
567 j = 9;
568 mvwprintw(screen, j++, 2, "SYS: %14ld cs/t %10.1f%% mem "
569 "%13ld running %10ld iowait",
570 s->ctxt, t->mem_used, t->procs_run, t->procs_iow);
571 j++;
572 if (s->irq_nr != 0) {
573 for(i = 0; i < s->irqs_len; ++i) {
574 unsigned long all = s->cpu_user[i] + s->cpu_nice[i] +
575 s->cpu_sys[i] + s->cpu_idle[i] +
576 s->cpu_iow[i];
577 mvwprintw(screen, j++, 2, "CPU%d: %13.1f%% usr/t "
578 "%9.1f%% sys/t %10.1f%% idl/t %11.1f%% iow/t ",
580 100.f * (s->cpu_user[i] + s->cpu_nice[i]) / all,
581 100.f * s->cpu_sys[i] / all,
582 100.f * s->cpu_idle[i] /all,
583 100.f * s->cpu_iow[i] / all);
585 j++;
586 for(i = 0; i < s->irqs_len; ++i)
587 mvwprintw(screen, j++, 2, "CPU%d: %14ld irqs/t "
588 "%15ld soirq RX/t %15ld soirq TX/t ",
589 i, s->irqs[i], s->irqs_srx[i], s->irqs_stx[i]);
590 j++;
591 for(i = 0; i < s->irqs_len; ++i)
592 mvwprintw(screen, j++, 2, "CPU%d: %14ld irqs",
593 i, t->irqs[i]);
594 j++;
596 if (t->wifi_bitrate > 0) {
597 mvwprintw(screen, j++, 2, "LinkQual: %7d/%d (%d/t) ",
598 t->wifi_link_qual, t->wifi_link_qual_max,
599 s->wifi_link_qual);
600 mvwprintw(screen, j++, 2, "Signal: %8d dBm (%d dBm/t) ",
601 t->wifi_signal_level, s->wifi_signal_level);
602 mvwprintw(screen, j++, 2, "Noise: %8d dBm (%d dBm/t) ",
603 t->wifi_noise_level, s->wifi_noise_level);
604 j++;
606 if (*first) {
607 mvwprintw(screen, 2, 2, "Collecting data ...");
608 *first = 0;
609 } else
610 mvwprintw(screen, 2, 2, " ");
612 wrefresh(screen);
613 refresh();
616 static void screen_end(void)
618 endwin();
621 static void print_update(const char *ifname, struct ifstat *s,
622 struct ifstat *t, double interval)
624 int i;
625 printf("RX: %16.3f MiB/t %10lu Pkts/t %10lu Drops/t %10lu Errors/t\n",
626 1.f * s->rx_bytes / (1 << 20), s->rx_packets, s->rx_drops,
627 s->rx_errors);
628 printf("TX: %16.3f MiB/t %10lu Pkts/t %10lu Drops/t %10lu Errors/t\n",
629 1.f * s->tx_bytes / (1 << 20), s->tx_packets, s->tx_drops,
630 s->tx_errors);
631 if (s->irq_nr != 0)
632 for(i = 0; i < s->irqs_len; ++i)
633 printf("CPU%d: %10ld IRQs/t "
634 "%10ld SoIRQ RX/t "
635 "%10ld SoIRQ TX/t\n", i,
636 s->irqs[i], s->irqs_srx[i], s->irqs_stx[i]);
637 if (t->wifi_bitrate > 0) {
638 printf("LinkQual: %6d/%d (%d/t)\n", t->wifi_link_qual,
639 t->wifi_link_qual_max, s->wifi_link_qual);
640 printf("Signal: %8d dBm (%d dBm/t)\n", t->wifi_signal_level,
641 s->wifi_signal_level);
642 printf("Noise: %8d dBm (%d dBm/t)\n", t->wifi_noise_level,
643 s->wifi_noise_level);
647 static void print_update_csv(const char *ifname, struct ifstat *s,
648 struct ifstat *t, double interval)
650 int i;
652 printf("%lu,%lu,%lu,%lu,", s->rx_bytes, s->rx_packets, s->rx_drops,
653 s->rx_errors);
654 printf("%lu,%lu,%lu,%lu", s->tx_bytes, s->tx_packets, s->tx_drops,
655 s->tx_errors);
656 if (s->irq_nr != 0)
657 for(i = 0; i < s->irqs_len; ++i)
658 printf(",%ld,%ld,%ld", s->irqs[i], s->irqs_srx[i],
659 s->irqs_stx[i]);
660 if (t->wifi_bitrate > 0) {
661 printf(",%d,%d", t->wifi_link_qual, t->wifi_link_qual_max);
662 printf(",%d", t->wifi_signal_level);
663 printf(",%d", t->wifi_noise_level);
665 printf("\n");
668 static void print_update_csv_hdr(const char *ifname, struct ifstat *s,
669 struct ifstat *t, double interval)
671 int i;
673 printf("RX Byte/t,RX Pkts/t,RX Drops/t,RX Errors/t,");
674 printf("TX Byte/t,TX Pkts/t,TX Drops/t,TX Errors/t");
675 if (s->irq_nr != 0)
676 for(i = 0; i < s->irqs_len; ++i)
677 printf(",CPU%d IRQs/t,CPU%d SoIRQ RX/t,"
678 "CPU%d SoIRQ TX/t", i, i, i);
679 if (t->wifi_bitrate > 0)
680 printf(",LinkQual,LinkQualMax,Signal Level,Noise Level");
681 printf("\n");
684 static inline int do_stats(const char *ifname, struct ifstat *s)
686 int ret = 0;
687 ret += rxtx_stats(ifname, s);
688 ret += irq_stats(ifname, s);
689 ret += irq_sstats(s);
690 ret += sys_stats(s);
691 ret += mem_stats(s);
692 ret += wifi_stats(ifname, s);
693 return ret;
696 static int screen_loop(const char *ifname, uint32_t interval)
698 int ret = 0, first = 1;
699 struct ifstat old, new, curr;
700 WINDOW *screen = NULL;
702 memset(&old, 0, sizeof(old));
703 memset(&new, 0, sizeof(new));
704 memset(&curr, 0, sizeof(curr));
705 screen_init(&screen);
706 while (!sigint) {
707 if (getch() == 'q')
708 goto out;
709 screen_update(screen, ifname, &curr, &new, &first, interval);
710 ret = do_stats(ifname, &old);
711 if (ret != 0)
712 goto out;
713 sleep(interval);
714 ret = do_stats(ifname, &new);
715 if (ret != 0)
716 goto out;
717 diff_stats(&old, &new, &curr);
719 out:
720 screen_end();
721 if (ret != 0)
722 whine("Error fetching stats!\n");
723 if (old.irqs)
724 xfree(old.irqs);
725 if (new.irqs)
726 xfree(new.irqs);
727 if (curr.irqs)
728 xfree(curr.irqs);
729 return 0;
732 static int print_loop(const char *ifname, uint32_t interval)
734 int ret, first = 1;
735 struct ifstat old, new, curr;
737 memset(&old, 0, sizeof(old));
738 memset(&new, 0, sizeof(new));
739 memset(&curr, 0, sizeof(curr));
740 do {
741 ret = do_stats(ifname, &old);
742 if (ret != 0)
743 goto out;
744 sleep(interval);
745 ret = do_stats(ifname, &new);
746 if (ret != 0)
747 goto out;
748 diff_stats(&old, &new, &curr);
749 if (first && (mode & TERM_MODE_CSV_HDR) ==
750 TERM_MODE_CSV_HDR) {
751 print_update_csv_hdr(ifname, &curr, &new, interval);
752 first = 0;
754 if ((mode & TERM_MODE_CSV) == TERM_MODE_CSV)
755 print_update_csv(ifname, &curr, &new, interval);
756 else if ((mode & TERM_MODE_NORMAL) == TERM_MODE_NORMAL)
757 print_update(ifname, &curr, &new, interval);
758 } while (loop && !sigint);
759 out:
760 if (ret != 0)
761 whine("Error fetching stats!\n");
762 if (old.irqs)
763 xfree(old.irqs);
764 if (new.irqs)
765 xfree(new.irqs);
766 if (curr.irqs)
767 xfree(curr.irqs);
768 return 0;
771 static void help(void)
773 printf("\nifpps %s, kernel networking and system statistics\n",
774 VERSION_STRING);
775 printf("http://www.netsniff-ng.org\n\n");
776 printf("Usage: ifpps [options] || ifpps <netdev>\n");
777 printf("Options:\n");
778 printf(" -d|--dev <netdev> Device to fetch statistics for i.e., eth0\n");
779 printf(" -p|--promisc Promiscuous mode\n");
780 printf(" -t|--interval <time> Refresh time in sec (default 1 s)\n");
781 printf(" -c|--term Output to terminal\n");
782 printf(" -C|--csv Output to terminal as CSV\n");
783 printf(" E.g. post-processing with Gnuplot et al.\n");
784 printf(" -H|--csv-tablehead Print CSV table head\n");
785 printf(" -l|--loop Loop terminal output\n");
786 printf(" -v|--version Print version\n");
787 printf(" -h|--help Print this help\n");
788 printf("\n");
789 printf("Examples:\n");
790 printf(" ifpps --dev eth0\n");
791 printf(" ifpps --dev eth0 --interval 60 --csv\n\n");
792 printf("Please report bugs to <bugs@netsniff-ng.org>\n");
793 printf("Copyright (C) 2009-2012 Daniel Borkmann <daniel@netsniff-ng.org>\n");
794 printf("License: GNU GPL version 2\n");
795 printf("This is free software: you are free to change and redistribute it.\n");
796 printf("There is NO WARRANTY, to the extent permitted by law.\n\n");
797 die();
800 static void version(void)
802 printf("\nifpps %s, kernel networking statistics per sec\n",
803 VERSION_STRING);
804 printf("http://www.netsniff-ng.org\n\n");
805 printf("Please report bugs to <bugs@netsniff-ng.org>\n");
806 printf("Copyright (C) 2009-2012 Daniel Borkmann <daniel@netsniff-ng.org>\n");
807 printf("License: GNU GPL version 2\n");
808 printf("This is free software: you are free to change and redistribute it.\n");
809 printf("There is NO WARRANTY, to the extent permitted by law.\n\n");
810 die();
813 int main(int argc, char **argv)
815 short ifflags = 0;
816 int c, opt_index, ret;
817 unsigned int promisc = 0;
818 char *ifname = NULL;
819 uint32_t interval = 1;
820 int (*main_loop)(const char *ifname, uint32_t interval) = screen_loop;
822 while ((c = getopt_long(argc, argv, short_options, long_options,
823 &opt_index)) != EOF) {
824 switch (c) {
825 case 'h':
826 help();
827 break;
828 case 'v':
829 version();
830 break;
831 case 'd':
832 ifname = xstrndup(optarg, IFNAMSIZ);
833 break;
834 case 't':
835 interval = atoi(optarg);
836 break;
837 case 'c':
838 mode |= TERM_MODE_NORMAL;
839 main_loop = print_loop;
840 break;
841 case 'l':
842 loop = 1;
843 break;
844 case 'p':
845 promisc = 1;
846 break;
847 case 'C':
848 mode |= TERM_MODE_CSV;
849 main_loop = print_loop;
850 break;
851 case 'H':
852 mode |= TERM_MODE_CSV_HDR;
853 main_loop = print_loop;
854 break;
855 case '?':
856 switch (optopt) {
857 case 'd':
858 case 't':
859 panic("Option -%c requires an argument!\n",
860 optopt);
861 default:
862 if (isprint(optopt))
863 whine("Unknown option character "
864 "`0x%X\'!\n", optopt);
865 die();
867 default:
868 break;
872 if (argc == 1)
873 help();
874 if (argc == 2)
875 ifname = xstrndup(argv[1], IFNAMSIZ);
876 if (ifname == NULL)
877 panic("No networking device given!\n");
878 if (!strncmp("lo", ifname, IFNAMSIZ))
879 panic("lo is not supported!\n");
880 if (device_mtu(ifname) == 0)
881 panic("This is no networking device!\n");
882 register_signal(SIGINT, signal_handler);
883 register_signal(SIGHUP, signal_handler);
884 if (promisc) {
885 check_for_root_maybe_die();
886 ifflags = enter_promiscuous_mode(ifname);
888 ret = main_loop(ifname, interval);
889 if (promisc)
890 leave_promiscuous_mode(ifname, ifflags);
891 xfree(ifname);
892 return ret;