docs: update doc to new diff
[netsniff-ng.git] / src / ifpps.c
blobb2026d82be1c538d74a8e1c0a2559f6eb67fdeef
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'.
22 #include <stdio.h>
23 #include <string.h>
24 #include <curses.h>
25 #include <getopt.h>
26 #include <ctype.h>
27 #include <sys/socket.h>
28 #include <signal.h>
29 #include <stdint.h>
30 #include <stdlib.h>
31 #include <time.h>
33 #include "die.h"
34 #include "xmalloc.h"
35 #include "xutils.h"
36 #include "xio.h"
37 #include "built_in.h"
40 * TODO: Cleanups, this got quite a hack over time.
43 #define TERM_MODE_NORMAL 1
44 #define TERM_MODE_CSV 2
45 #define TERM_MODE_CSV_HDR 4
47 #define USER_HZ sysconf(_SC_CLK_TCK)
49 struct ifstat {
50 unsigned long rx_bytes;
51 unsigned long rx_packets;
52 unsigned long rx_drops;
53 unsigned long rx_errors;
54 unsigned long rx_fifo;
55 unsigned long rx_frame;
56 unsigned long rx_multi;
57 unsigned long tx_bytes;
58 unsigned long tx_packets;
59 unsigned long tx_drops;
60 unsigned long tx_errors;
61 unsigned long tx_fifo;
62 unsigned long tx_colls;
63 unsigned long tx_carrier;
64 unsigned long irq_nr;
65 unsigned long *irqs;
66 unsigned long *irqs_srx;
67 unsigned long *irqs_stx;
68 unsigned long *cpu_user;
69 unsigned long *cpu_nice;
70 unsigned long *cpu_sys;
71 unsigned long *cpu_idle;
72 unsigned long *cpu_iow;
73 unsigned long ctxt;
74 unsigned long forks;
75 unsigned long procs_run;
76 unsigned long procs_iow;
77 size_t irqs_len;
78 float mem_used;
79 int wifi_bitrate;
80 int wifi_link_qual;
81 int wifi_link_qual_max;
82 int wifi_signal_level;
83 int wifi_noise_level;
86 static int mode = 0;
87 static int loop = 0;
89 volatile sig_atomic_t sigint = 0;
91 static const char *short_options = "d:t:vhcCHlp";
93 static struct option long_options[] = {
94 {"dev", required_argument, 0, 'd'},
95 {"interval", required_argument, 0, 't'},
96 {"loop", no_argument, 0, 'l'},
97 {"term", no_argument, 0, 'c'},
98 {"promisc", no_argument, 0, 'p'},
99 {"csv", no_argument, 0, 'C'},
100 {"csv-tablehead", no_argument, 0, 'H'},
101 {"version", no_argument, 0, 'v'},
102 {"help", no_argument, 0, 'h'},
103 {0, 0, 0, 0}
106 static void signal_handler(int number)
108 switch (number) {
109 case SIGINT:
110 sigint = 1;
111 break;
112 case SIGHUP:
113 break;
114 default:
115 break;
119 static int rxtx_stats(const char *ifname, struct ifstat *s)
121 int ret, found = -1;
122 char *ptr;
123 char buf[1024];
125 FILE *fp = fopen("/proc/net/dev", "r");
126 if (!fp) {
127 whine("Cannot open /proc/net/dev!\n");
128 return -ENOENT;
131 /* Omit header */
132 ptr = fgets(buf, sizeof(buf), fp);
133 ptr = fgets(buf, sizeof(buf), fp);
135 memset(buf, 0, sizeof(buf));
136 while (fgets(buf, sizeof(buf), fp) != NULL) {
137 buf[sizeof(buf) -1] = 0;
139 if (strstr(buf, ifname) == NULL)
140 continue;
142 ptr = buf;
143 while (*ptr != ':')
144 ptr++;
145 ptr++;
147 ret = sscanf(ptr, "%lu%lu%lu%lu%lu%lu%lu%*u%lu%lu%lu%lu%lu%lu%lu",
148 &s->rx_bytes, &s->rx_packets, &s->rx_errors,
149 &s->rx_drops, &s->rx_fifo, &s->rx_frame,
150 &s->rx_multi,
151 &s->tx_bytes, &s->tx_packets, &s->tx_errors,
152 &s->tx_drops, &s->tx_fifo, &s->tx_colls,
153 &s->tx_carrier);
154 if (ret == 14) {
155 found = 0;
156 break;
159 memset(buf, 0, sizeof(buf));
162 fclose(fp);
164 return found;
167 static int wifi_stats(const char *ifname, struct ifstat *s)
169 int ret;
170 struct iw_statistics ws;
172 ret = wireless_sigqual(ifname, &ws);
173 if (ret != 0) {
174 /* We don't want to trouble in case of eth* */
175 s->wifi_bitrate = 0;
176 return 0;
179 s->wifi_bitrate = wireless_bitrate(ifname);
180 s->wifi_signal_level = adjust_dbm_level(ws.qual.updated & IW_QUAL_DBM,
181 ws.qual.level);
182 s->wifi_noise_level = adjust_dbm_level(ws.qual.updated & IW_QUAL_DBM,
183 ws.qual.noise);
184 s->wifi_link_qual = ws.qual.qual;
185 s->wifi_link_qual_max = wireless_rangemax_sigqual(ifname);
187 return ret;
190 static void stats_check_alloc(struct ifstat *s)
192 int cpus = get_number_cpus();
194 if (s->irqs_len != get_number_cpus()) {
195 if (s->irqs) xfree(s->irqs);
196 if (s->irqs_srx) xfree(s->irqs_srx);
197 if (s->irqs_stx) xfree(s->irqs_stx);
198 if (s->cpu_user) xfree(s->cpu_user);
199 if (s->cpu_nice) xfree(s->cpu_nice);
200 if (s->cpu_sys) xfree(s->cpu_sys);
201 if (s->cpu_idle) xfree(s->cpu_idle);
202 if (s->cpu_iow) xfree(s->cpu_iow);
204 s->irqs_srx = xzmalloc(sizeof(*(s->irqs_srx)) * cpus);
205 s->irqs_stx = xzmalloc(sizeof(*(s->irqs_stx)) * cpus);
206 s->irqs = xzmalloc(sizeof(*(s->irqs)) * cpus);
207 s->cpu_user = xzmalloc(sizeof(*(s->cpu_user)) * cpus);
208 s->cpu_nice = xzmalloc(sizeof(*(s->cpu_nice)) * cpus);
209 s->cpu_sys = xzmalloc(sizeof(*(s->cpu_sys)) * cpus);
210 s->cpu_idle = xzmalloc(sizeof(*(s->cpu_idle)) * cpus);
211 s->cpu_iow = xzmalloc(sizeof(*(s->cpu_iow)) * cpus);
212 s->irqs_len = cpus;
216 static int irq_sstats(struct ifstat *s)
218 int i, rx = 0;
219 char *ptr, *ptr2;
220 char buff[4096];
222 FILE *fp = fopen("/proc/softirqs", "r");
223 if (!fp) {
224 whine("Cannot open /proc/softirqs!\n");
225 return -ENOENT;
228 stats_check_alloc(s);
230 memset(buff, 0, sizeof(buff));
231 while (fgets(buff, sizeof(buff), fp) != NULL) {
232 buff[sizeof(buff) - 1] = 0;
234 if ((ptr = strstr(buff, "NET_TX:")) == NULL) {
235 ptr = strstr(buff, "NET_RX:");
237 if (ptr == NULL)
238 continue;
239 rx = 1;
240 } else {
241 rx = 0;
244 ptr += strlen("NET_TX:");
246 for (i = 0; i < s->irqs_len; ++i) {
247 ptr++;
248 while (*ptr == ' ')
249 ptr++;
250 ptr2 = ptr;
251 while (*ptr != ' ' && *ptr != 0)
252 ptr++;
253 *ptr = 0;
254 if (rx)
255 s->irqs_srx[i] = atoi(ptr2);
256 else
257 s->irqs_stx[i] = atoi(ptr2);
260 memset(buff, 0, sizeof(buff));
263 fclose(fp);
265 return 0;
268 static int mem_stats(struct ifstat *s)
270 int ret;
271 unsigned long total, free;
272 char *ptr;
273 char buff[4096];
275 FILE *fp = fopen("/proc/meminfo", "r");
276 if (!fp) {
277 whine("Cannot open /proc/meminfo!\n");
278 return -ENOENT;
281 memset(buff, 0, sizeof(buff));
282 while (fgets(buff, sizeof(buff), fp) != NULL) {
283 buff[sizeof(buff) - 1] = 0;
285 if ((ptr = strstr(buff, "MemTotal:")) != NULL) {
286 ptr += strlen("MemTotal:");
287 ptr++;
289 while (*ptr == ' ')
290 ptr++;
292 ret = sscanf(ptr, "%lu", &total);
293 if (ret != 1)
294 total = 0;
295 } else if ((ptr = strstr(buff, "MemFree:")) != NULL) {
296 ptr += strlen("MemFree:");
297 ptr++;
299 while (*ptr == ' ')
300 ptr++;
302 ret = sscanf(ptr, "%lu", &free);
303 if (ret != 1)
304 free = 0;
307 memset(buff, 0, sizeof(buff));
310 if (total > 0)
311 s->mem_used = 100.f * (total - free) / total;
312 else
313 s->mem_used = 0.f;
315 fclose(fp);
317 return 0;
320 static int sys_stats(struct ifstat *s)
322 int ret, cpu;
323 char *ptr, *ptr2;
324 char buff[4096];
326 FILE *fp = fopen("/proc/stat", "r");
327 if (!fp) {
328 whine("Cannot open /proc/stat!\n");
329 return -ENOENT;
332 stats_check_alloc(s);
334 memset(buff, 0, sizeof(buff));
335 while (fgets(buff, sizeof(buff), fp) != NULL) {
336 buff[sizeof(buff) - 1] = 0;
338 if ((ptr = strstr(buff, "cpu")) != NULL) {
339 ptr += strlen("cpu");
340 if (*ptr == ' ')
341 goto next;
342 ptr2 = ptr;
344 while (*ptr != ' ' && *ptr != 0)
345 ptr++;
346 *ptr = 0;
348 cpu = atoi(ptr2);
349 if (cpu < 0 || cpu >= s->irqs_len)
350 goto next;
351 ptr++;
353 ret = sscanf(ptr, "%lu%lu%lu%lu%lu", &s->cpu_user[cpu],
354 &s->cpu_nice[cpu], &s->cpu_sys[cpu],
355 &s->cpu_idle[cpu], &s->cpu_iow[cpu]);
356 if (ret != 5)
357 goto next;
358 } else if ((ptr = strstr(buff, "ctxt")) != NULL) {
359 ptr += strlen("ctxt");
360 ptr++;
362 while (*ptr == ' ')
363 ptr++;
365 ret = sscanf(ptr, "%lu", &s->ctxt);
366 if (ret != 1)
367 s->ctxt = 0;
368 } else if ((ptr = strstr(buff, "processes")) != NULL) {
369 ptr += strlen("processes");
370 ptr++;
372 while (*ptr == ' ')
373 ptr++;
375 ret = sscanf(ptr, "%lu", &s->forks);
376 if (ret != 1)
377 s->forks = 0;
378 } else if ((ptr = strstr(buff, "procs_running")) != NULL) {
379 ptr += strlen("procs_running");
380 ptr++;
382 while (*ptr == ' ')
383 ptr++;
385 ret = sscanf(ptr, "%lu", &s->procs_run);
386 if (ret != 1)
387 s->procs_run = 0;
388 } else if ((ptr = strstr(buff, "procs_blocked")) != NULL) {
389 ptr += strlen("procs_blocked");
390 ptr++;
392 while (*ptr == ' ')
393 ptr++;
395 ret = sscanf(ptr, "%lu", &s->procs_iow);
396 if (ret != 1)
397 s->procs_iow = 0;
399 next:
400 memset(buff, 0, sizeof(buff));
403 fclose(fp);
405 return 0;
408 static int irq_stats(const char *ifname, struct ifstat *s)
410 int i;
411 char *ptr, *ptr2;
412 char buff[4096];
414 /* We exclude lo! */
415 if (!strncmp("lo", ifname, strlen("lo")))
416 return 0;
418 FILE *fp = fopen("/proc/interrupts", "r");
419 if (!fp) {
420 whine("Cannot open /proc/interrupts!\n");
421 return -ENOENT;
424 stats_check_alloc(s);
426 memset(buff, 0, sizeof(buff));
427 while (fgets(buff, sizeof(buff), fp) != NULL) {
428 buff[sizeof(buff) - 1] = 0;
430 if (strstr(buff, ifname) == NULL)
431 continue;
433 ptr = buff;
434 while (*ptr != ':')
435 ptr++;
436 *ptr = 0;
437 s->irq_nr = atoi(buff);
439 bug_on(s->irq_nr == 0);
441 for (i = 0; i < s->irqs_len; ++i) {
442 ptr++;
443 ptr2 = ptr;
444 while (*ptr == ' ')
445 ptr++;
446 while (*ptr != ' ' && *ptr != 0)
447 ptr++;
448 *ptr = 0;
449 s->irqs[i] = atoi(ptr2);
452 memset(buff, 0, sizeof(buff));
455 fclose(fp);
457 return 0;
460 static void diff_stats(struct ifstat *old, struct ifstat *new,
461 struct ifstat *diff)
463 int i;
465 if(old->irqs_len != new->irqs_len)
466 return; /* Refetch stats and take old diff! */
468 diff->rx_bytes = new->rx_bytes - old->rx_bytes;
469 diff->rx_packets = new->rx_packets - old->rx_packets;
470 diff->rx_drops = new->rx_drops - old->rx_drops;
471 diff->rx_errors = new->rx_errors - old->rx_errors;
472 diff->rx_fifo = new->rx_fifo - old->rx_fifo;
473 diff->rx_frame = new->rx_frame - old->rx_frame;
474 diff->rx_multi = new->rx_multi - old->rx_multi;
475 diff->tx_bytes = new->tx_bytes - old->tx_bytes;
476 diff->tx_packets = new->tx_packets - old->tx_packets;
477 diff->tx_drops = new->tx_drops - old->tx_drops;
478 diff->tx_errors = new->tx_errors - old->tx_errors;
479 diff->tx_fifo = new->tx_fifo - old->tx_fifo;
480 diff->tx_colls = new->tx_colls - old->tx_colls;
481 diff->tx_carrier = new->tx_carrier - old->tx_carrier;
482 diff->wifi_signal_level = new->wifi_signal_level - old->wifi_signal_level;
483 diff->wifi_noise_level = new->wifi_noise_level - old->wifi_noise_level;
484 diff->wifi_link_qual = new->wifi_link_qual - old->wifi_link_qual;
485 diff->ctxt = new->ctxt - old->ctxt;
486 diff->forks = new->forks - old->forks;
487 diff->procs_run = new->procs_run - old->procs_run;
488 diff->procs_iow = new->procs_iow - old->procs_iow;
490 stats_check_alloc(diff);
492 diff->irq_nr = new->irq_nr;
494 for (i = 0; i < diff->irqs_len; ++i) {
495 diff->irqs[i] = new->irqs[i] - old->irqs[i];
496 diff->irqs_srx[i] = new->irqs_srx[i] - old->irqs_srx[i];
497 diff->irqs_stx[i] = new->irqs_stx[i] - old->irqs_stx[i];
498 diff->cpu_user[i] = new->cpu_user[i] - old->cpu_user[i];
499 diff->cpu_nice[i] = new->cpu_nice[i] - old->cpu_nice[i];
500 diff->cpu_sys[i] = new->cpu_sys[i] - old->cpu_sys[i];
501 diff->cpu_idle[i] = new->cpu_idle[i] - old->cpu_idle[i];
502 diff->cpu_iow[i] = new->cpu_iow[i] - old->cpu_iow[i];
506 static char *snr_to_str(int level)
508 // empirical values
509 if (level > 40)
510 return "very good signal";
511 if (level > 25 && level <= 40)
512 return "good signal";
513 if (level > 15 && level <= 25)
514 return "poor signal";
515 if (level > 10 && level <= 15)
516 return "very poor signal";
517 if (level <= 10)
518 return "no signal";
519 /* unreachable */
520 return "unknown";
523 static void screen_init(WINDOW **screen)
525 (*screen) = initscr();
526 noecho();
527 cbreak();
528 nodelay((*screen), TRUE);
529 refresh();
530 wrefresh((*screen));
533 static void screen_update(WINDOW *screen, const char *ifname,
534 struct ifstat *s, struct ifstat *t,
535 int *first, double interval)
537 int i, j = 0;
539 curs_set(0);
540 mvwprintw(screen, 1, 2, "Kernel net/sys statistics for %s, t=%.2lfs",
541 ifname, interval);
542 attron(A_REVERSE);
543 mvwprintw(screen, 3, 0,
544 " RX: %16.3f MiB/t %10lu pkts/t %10lu drops/t %10lu errors/t ",
545 1.f * s->rx_bytes / (1 << 20), s->rx_packets, s->rx_drops,
546 s->rx_errors);
547 mvwprintw(screen, 4, 0,
548 " TX: %16.3f MiB/t %10lu pkts/t %10lu drops/t %10lu errors/t ",
549 1.f * s->tx_bytes / (1 << 20), s->tx_packets, s->tx_drops,
550 s->tx_errors);
551 attroff(A_REVERSE);
552 mvwprintw(screen, 6, 2,
553 "RX: %16.3f MiB %10lu pkts %10lu drops %10lu errors",
554 1.f * t->rx_bytes / (1 << 20), t->rx_packets, t->rx_drops,
555 t->rx_errors);
556 mvwprintw(screen, 7, 2,
557 "TX: %16.3f MiB %10lu pkts %10lu drops %10lu errors",
558 1.f * t->tx_bytes / (1 << 20), t->tx_packets, t->tx_drops,
559 t->tx_errors);
560 j = 9;
561 mvwprintw(screen, j++, 2, "SYS: %14ld cs/t %10.1f%% mem "
562 "%13ld running %10ld iowait",
563 s->ctxt, t->mem_used, t->procs_run, t->procs_iow);
564 j++;
565 if (s->irq_nr != 0) {
566 for(i = 0; i < s->irqs_len; ++i) {
567 unsigned long all = s->cpu_user[i] + s->cpu_nice[i] +
568 s->cpu_sys[i] + s->cpu_idle[i] +
569 s->cpu_iow[i];
570 mvwprintw(screen, j++, 2, "CPU%d: %13.1f%% usr/t "
571 "%9.1f%% sys/t %10.1f%% idl/t %11.1f%% iow/t ",
573 100.f * (s->cpu_user[i] + s->cpu_nice[i]) / all,
574 100.f * s->cpu_sys[i] / all,
575 100.f * s->cpu_idle[i] /all,
576 100.f * s->cpu_iow[i] / all);
578 j++;
579 for(i = 0; i < s->irqs_len; ++i)
580 mvwprintw(screen, j++, 2, "CPU%d: %14ld irqs/t "
581 "%15ld soirq RX/t %15ld soirq TX/t ",
582 i, s->irqs[i], s->irqs_srx[i], s->irqs_stx[i]);
583 j++;
584 for(i = 0; i < s->irqs_len; ++i)
585 mvwprintw(screen, j++, 2, "CPU%d: %14ld irqs",
586 i, t->irqs[i]);
587 j++;
589 if (t->wifi_bitrate > 0) {
590 mvwprintw(screen, j++, 2, "LinkQual: %7d/%d (%d/t) ",
591 t->wifi_link_qual, t->wifi_link_qual_max,
592 s->wifi_link_qual);
593 mvwprintw(screen, j++, 2, "Signal: %8d dBm (%d dBm/t) ",
594 t->wifi_signal_level, s->wifi_signal_level);
595 mvwprintw(screen, j++, 2, "Noise: %8d dBm (%d dBm/t) ",
596 t->wifi_noise_level, s->wifi_noise_level);
597 mvwprintw(screen, j++, 2, "SNR: %8d dBm (%s) ",
598 t->wifi_signal_level - t->wifi_noise_level,
599 snr_to_str(t->wifi_signal_level - t->wifi_noise_level));
600 j++;
602 if (*first) {
603 mvwprintw(screen, 2, 2, "Collecting data ...");
604 *first = 0;
605 } else
606 mvwprintw(screen, 2, 2, " ");
608 wrefresh(screen);
609 refresh();
612 static void screen_end(void)
614 endwin();
617 static void print_update(const char *ifname, struct ifstat *s,
618 struct ifstat *t, double interval)
620 int i;
622 printf("RX: %16.3f MiB/t %10lu Pkts/t %10lu Drops/t %10lu Errors/t\n",
623 1.f * s->rx_bytes / (1 << 20), s->rx_packets, s->rx_drops,
624 s->rx_errors);
625 printf("TX: %16.3f MiB/t %10lu Pkts/t %10lu Drops/t %10lu Errors/t\n",
626 1.f * s->tx_bytes / (1 << 20), s->tx_packets, s->tx_drops,
627 s->tx_errors);
628 if (s->irq_nr != 0)
629 for(i = 0; i < s->irqs_len; ++i)
630 printf("CPU%d: %10ld IRQs/t "
631 "%10ld SoIRQ RX/t "
632 "%10ld SoIRQ TX/t\n", i,
633 s->irqs[i], s->irqs_srx[i], s->irqs_stx[i]);
634 if (t->wifi_bitrate > 0) {
635 printf("LinkQual: %6d/%d (%d/t)\n", t->wifi_link_qual,
636 t->wifi_link_qual_max, s->wifi_link_qual);
637 printf("Signal: %8d dBm (%d dBm/t)\n", t->wifi_signal_level,
638 s->wifi_signal_level);
639 printf("Noise: %8d dBm (%d dBm/t)\n", t->wifi_noise_level,
640 s->wifi_noise_level);
644 static void print_update_csv(const char *ifname, struct ifstat *s,
645 struct ifstat *t, double interval)
647 int i;
649 printf("%ld,%lu,%lu,%lu,%lu,", time(0), s->rx_bytes, s->rx_packets,
650 s->rx_drops, s->rx_errors);
651 printf("%lu,%lu,%lu,%lu", s->tx_bytes, s->tx_packets, s->tx_drops,
652 s->tx_errors);
653 if (s->irq_nr != 0)
654 for(i = 0; i < s->irqs_len; ++i)
655 printf(",%ld,%ld,%ld", s->irqs[i], s->irqs_srx[i],
656 s->irqs_stx[i]);
657 if (t->wifi_bitrate > 0) {
658 printf(",%d,%d", t->wifi_link_qual, t->wifi_link_qual_max);
659 printf(",%d", t->wifi_signal_level);
660 printf(",%d", t->wifi_noise_level);
662 printf("\n");
665 static void print_update_csv_hdr(const char *ifname, struct ifstat *s,
666 struct ifstat *t, double interval)
668 int i;
670 printf("Unixtime,RX Byte/t,RX Pkts/t,RX Drops/t,RX Errors/t,");
671 printf("TX Byte/t,TX Pkts/t,TX Drops/t,TX Errors/t");
672 if (s->irq_nr != 0)
673 for(i = 0; i < s->irqs_len; ++i)
674 printf(",CPU%d IRQs/t,CPU%d SoIRQ RX/t,"
675 "CPU%d SoIRQ TX/t", i, i, i);
676 if (t->wifi_bitrate > 0)
677 printf(",LinkQual,LinkQualMax,Signal Level,Noise Level");
678 printf("\n");
681 static inline int do_stats(const char *ifname, struct ifstat *s)
683 int ret = 0;
685 ret += rxtx_stats(ifname, s);
686 ret += irq_stats(ifname, s);
687 ret += irq_sstats(s);
688 ret += sys_stats(s);
689 ret += mem_stats(s);
690 ret += wifi_stats(ifname, s);
692 return ret;
695 static int screen_loop(const char *ifname, uint32_t interval)
697 int ret = 0, first = 1;
698 struct ifstat old, new, curr;
699 WINDOW *screen = NULL;
701 memset(&old, 0, sizeof(old));
702 memset(&new, 0, sizeof(new));
703 memset(&curr, 0, sizeof(curr));
705 screen_init(&screen);
707 while (!sigint) {
708 if (getch() == 'q')
709 goto out;
711 screen_update(screen, ifname, &curr, &new, &first, interval);
713 ret = do_stats(ifname, &old);
714 if (ret != 0)
715 goto out;
717 sleep(interval);
719 ret = do_stats(ifname, &new);
720 if (ret != 0)
721 goto out;
723 diff_stats(&old, &new, &curr);
725 out:
726 screen_end();
728 if (ret != 0)
729 whine("Error fetching stats!\n");
730 if (old.irqs)
731 xfree(old.irqs);
732 if (new.irqs)
733 xfree(new.irqs);
734 if (curr.irqs)
735 xfree(curr.irqs);
737 return 0;
740 static int print_loop(const char *ifname, uint32_t interval)
742 int ret, first = 1;
743 struct ifstat old, new, curr;
745 memset(&old, 0, sizeof(old));
746 memset(&new, 0, sizeof(new));
747 memset(&curr, 0, sizeof(curr));
748 do {
749 ret = do_stats(ifname, &old);
750 if (ret != 0)
751 goto out;
753 sleep(interval);
755 ret = do_stats(ifname, &new);
756 if (ret != 0)
757 goto out;
759 diff_stats(&old, &new, &curr);
761 if (first && (mode & TERM_MODE_CSV_HDR) ==
762 TERM_MODE_CSV_HDR) {
763 print_update_csv_hdr(ifname, &curr, &new, interval);
764 first = 0;
767 if ((mode & TERM_MODE_CSV) == TERM_MODE_CSV)
768 print_update_csv(ifname, &curr, &new, interval);
769 else if ((mode & TERM_MODE_NORMAL) == TERM_MODE_NORMAL)
770 print_update(ifname, &curr, &new, interval);
771 } while (loop && !sigint);
772 out:
773 if (ret != 0)
774 whine("Error fetching stats!\n");
775 if (old.irqs)
776 xfree(old.irqs);
777 if (new.irqs)
778 xfree(new.irqs);
779 if (curr.irqs)
780 xfree(curr.irqs);
782 return 0;
785 static void help(void)
787 printf("\nifpps %s, kernel networking and system statistics\n",
788 VERSION_STRING);
789 printf("http://www.netsniff-ng.org\n\n");
790 printf("Usage: ifpps [options] || ifpps <netdev>\n");
791 printf("Options:\n");
792 printf(" -d|--dev <netdev> Device to fetch statistics for i.e., eth0\n");
793 printf(" -p|--promisc Promiscuous mode\n");
794 printf(" -t|--interval <time> Refresh time in sec (default 1 s)\n");
795 printf(" -c|--term Output to terminal\n");
796 printf(" -C|--csv Output to terminal as CSV\n");
797 printf(" E.g. post-processing with Gnuplot et al.\n");
798 printf(" -H|--csv-tablehead Print CSV table head\n");
799 printf(" -l|--loop Loop terminal output\n");
800 printf(" -v|--version Print version\n");
801 printf(" -h|--help Print this help\n");
802 printf("\n");
803 printf("Examples:\n");
804 printf(" ifpps --dev eth0\n");
805 printf(" ifpps --dev eth0 --interval 60 --csv\n\n");
806 printf("Please report bugs to <bugs@netsniff-ng.org>\n");
807 printf("Copyright (C) 2009-2012 Daniel Borkmann <daniel@netsniff-ng.org>\n");
808 printf("License: GNU GPL version 2\n");
809 printf("This is free software: you are free to change and redistribute it.\n");
810 printf("There is NO WARRANTY, to the extent permitted by law.\n\n");
811 die();
814 static void version(void)
816 printf("\nifpps %s, kernel networking statistics per sec\n",
817 VERSION_STRING);
818 printf("http://www.netsniff-ng.org\n\n");
819 printf("Please report bugs to <bugs@netsniff-ng.org>\n");
820 printf("Copyright (C) 2009-2012 Daniel Borkmann <daniel@netsniff-ng.org>\n");
821 printf("License: GNU GPL version 2\n");
822 printf("This is free software: you are free to change and redistribute it.\n");
823 printf("There is NO WARRANTY, to the extent permitted by law.\n\n");
824 die();
827 int main(int argc, char **argv)
829 short ifflags = 0;
830 int c, opt_index, ret;
831 unsigned int promisc = 0;
832 char *ifname = NULL;
833 uint32_t interval = 1;
834 int (*main_loop)(const char *ifname, uint32_t interval) = screen_loop;
836 while ((c = getopt_long(argc, argv, short_options, long_options,
837 &opt_index)) != EOF) {
838 switch (c) {
839 case 'h':
840 help();
841 break;
842 case 'v':
843 version();
844 break;
845 case 'd':
846 ifname = xstrndup(optarg, IFNAMSIZ);
847 break;
848 case 't':
849 interval = atoi(optarg);
850 break;
851 case 'c':
852 mode |= TERM_MODE_NORMAL;
853 main_loop = print_loop;
854 break;
855 case 'l':
856 loop = 1;
857 break;
858 case 'p':
859 promisc = 1;
860 break;
861 case 'C':
862 mode |= TERM_MODE_CSV;
863 main_loop = print_loop;
864 break;
865 case 'H':
866 mode |= TERM_MODE_CSV_HDR;
867 main_loop = print_loop;
868 break;
869 case '?':
870 switch (optopt) {
871 case 'd':
872 case 't':
873 panic("Option -%c requires an argument!\n",
874 optopt);
875 default:
876 if (isprint(optopt))
877 whine("Unknown option character "
878 "`0x%X\'!\n", optopt);
879 die();
881 default:
882 break;
886 if (argc == 1)
887 help();
888 if (argc == 2)
889 ifname = xstrndup(argv[1], IFNAMSIZ);
890 if (ifname == NULL)
891 panic("No networking device given!\n");
892 if (!strncmp("lo", ifname, IFNAMSIZ))
893 panic("lo is not supported!\n");
894 if (device_mtu(ifname) == 0)
895 panic("This is no networking device!\n");
896 register_signal(SIGINT, signal_handler);
897 register_signal(SIGHUP, signal_handler);
898 if (promisc) {
899 check_for_root_maybe_die();
900 ifflags = enter_promiscuous_mode(ifname);
902 ret = main_loop(ifname, interval);
903 if (promisc)
904 leave_promiscuous_mode(ifname, ifflags);
905 xfree(ifname);
907 return ret;