docs: authors: add Doug as minor contr. (thanks)
[netsniff-ng.git] / src / ifpps.c
blobd8ca178742f6325edd268abed347cd3e97ce1058
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";
92 static const struct option long_options[] = {
93 {"dev", required_argument, NULL, 'd'},
94 {"interval", required_argument, NULL, 't'},
95 {"loop", no_argument, NULL, 'l'},
96 {"term", no_argument, NULL, 'c'},
97 {"promisc", no_argument, NULL, 'p'},
98 {"csv", no_argument, NULL, 'C'},
99 {"csv-tablehead", no_argument, NULL, 'H'},
100 {"version", no_argument, NULL, 'v'},
101 {"help", no_argument, NULL, 'h'},
102 {NULL, 0, NULL, 0}
105 static void signal_handler(int number)
107 switch (number) {
108 case SIGINT:
109 sigint = 1;
110 break;
111 case SIGHUP:
112 break;
113 default:
114 break;
118 static int rxtx_stats(const char *ifname, struct ifstat *s)
120 int ret, found = -1;
121 char *ptr;
122 char buf[1024];
124 FILE *fp = fopen("/proc/net/dev", "r");
125 if (!fp) {
126 whine("Cannot open /proc/net/dev!\n");
127 return -ENOENT;
130 /* Omit header */
131 ptr = fgets(buf, sizeof(buf), fp);
132 ptr = fgets(buf, sizeof(buf), fp);
134 memset(buf, 0, sizeof(buf));
135 while (fgets(buf, sizeof(buf), fp) != NULL) {
136 buf[sizeof(buf) -1] = 0;
138 if (strstr(buf, ifname) == NULL)
139 continue;
141 ptr = buf;
142 while (*ptr != ':')
143 ptr++;
144 ptr++;
146 ret = sscanf(ptr, "%lu%lu%lu%lu%lu%lu%lu%*u%lu%lu%lu%lu%lu%lu%lu",
147 &s->rx_bytes, &s->rx_packets, &s->rx_errors,
148 &s->rx_drops, &s->rx_fifo, &s->rx_frame,
149 &s->rx_multi,
150 &s->tx_bytes, &s->tx_packets, &s->tx_errors,
151 &s->tx_drops, &s->tx_fifo, &s->tx_colls,
152 &s->tx_carrier);
153 if (ret == 14) {
154 found = 0;
155 break;
158 memset(buf, 0, sizeof(buf));
161 fclose(fp);
163 return found;
166 static int wifi_stats(const char *ifname, struct ifstat *s)
168 int ret;
169 struct iw_statistics ws;
171 ret = wireless_sigqual(ifname, &ws);
172 if (ret != 0) {
173 /* We don't want to trouble in case of eth* */
174 s->wifi_bitrate = 0;
175 return 0;
178 s->wifi_bitrate = wireless_bitrate(ifname);
179 s->wifi_signal_level = adjust_dbm_level(ws.qual.updated & IW_QUAL_DBM,
180 ws.qual.level);
181 s->wifi_noise_level = adjust_dbm_level(ws.qual.updated & IW_QUAL_DBM,
182 ws.qual.noise);
183 s->wifi_link_qual = ws.qual.qual;
184 s->wifi_link_qual_max = wireless_rangemax_sigqual(ifname);
186 return ret;
189 static void stats_check_alloc(struct ifstat *s)
191 int cpus = get_number_cpus();
193 if (s->irqs_len != get_number_cpus()) {
194 if (s->irqs) xfree(s->irqs);
195 if (s->irqs_srx) xfree(s->irqs_srx);
196 if (s->irqs_stx) xfree(s->irqs_stx);
197 if (s->cpu_user) xfree(s->cpu_user);
198 if (s->cpu_nice) xfree(s->cpu_nice);
199 if (s->cpu_sys) xfree(s->cpu_sys);
200 if (s->cpu_idle) xfree(s->cpu_idle);
201 if (s->cpu_iow) xfree(s->cpu_iow);
203 s->irqs_srx = xzmalloc(sizeof(*(s->irqs_srx)) * cpus);
204 s->irqs_stx = xzmalloc(sizeof(*(s->irqs_stx)) * cpus);
205 s->irqs = xzmalloc(sizeof(*(s->irqs)) * cpus);
206 s->cpu_user = xzmalloc(sizeof(*(s->cpu_user)) * cpus);
207 s->cpu_nice = xzmalloc(sizeof(*(s->cpu_nice)) * cpus);
208 s->cpu_sys = xzmalloc(sizeof(*(s->cpu_sys)) * cpus);
209 s->cpu_idle = xzmalloc(sizeof(*(s->cpu_idle)) * cpus);
210 s->cpu_iow = xzmalloc(sizeof(*(s->cpu_iow)) * cpus);
211 s->irqs_len = cpus;
215 static int irq_sstats(struct ifstat *s)
217 int i, rx = 0;
218 char *ptr, *ptr2;
219 char buff[4096];
221 FILE *fp = fopen("/proc/softirqs", "r");
222 if (!fp) {
223 whine("Cannot open /proc/softirqs!\n");
224 return -ENOENT;
227 stats_check_alloc(s);
229 memset(buff, 0, sizeof(buff));
230 while (fgets(buff, sizeof(buff), fp) != NULL) {
231 buff[sizeof(buff) - 1] = 0;
233 if ((ptr = strstr(buff, "NET_TX:")) == NULL) {
234 ptr = strstr(buff, "NET_RX:");
236 if (ptr == NULL)
237 continue;
238 rx = 1;
239 } else {
240 rx = 0;
243 ptr += strlen("NET_TX:");
245 for (i = 0; i < s->irqs_len; ++i) {
246 ptr++;
247 while (*ptr == ' ')
248 ptr++;
249 ptr2 = ptr;
250 while (*ptr != ' ' && *ptr != 0)
251 ptr++;
252 *ptr = 0;
253 if (rx)
254 s->irqs_srx[i] = atoi(ptr2);
255 else
256 s->irqs_stx[i] = atoi(ptr2);
259 memset(buff, 0, sizeof(buff));
262 fclose(fp);
264 return 0;
267 static int mem_stats(struct ifstat *s)
269 int ret;
270 unsigned long total, free;
271 char *ptr;
272 char buff[4096];
274 FILE *fp = fopen("/proc/meminfo", "r");
275 if (!fp) {
276 whine("Cannot open /proc/meminfo!\n");
277 return -ENOENT;
280 memset(buff, 0, sizeof(buff));
281 while (fgets(buff, sizeof(buff), fp) != NULL) {
282 buff[sizeof(buff) - 1] = 0;
284 if ((ptr = strstr(buff, "MemTotal:")) != NULL) {
285 ptr += strlen("MemTotal:");
286 ptr++;
288 while (*ptr == ' ')
289 ptr++;
291 ret = sscanf(ptr, "%lu", &total);
292 if (ret != 1)
293 total = 0;
294 } else if ((ptr = strstr(buff, "MemFree:")) != NULL) {
295 ptr += strlen("MemFree:");
296 ptr++;
298 while (*ptr == ' ')
299 ptr++;
301 ret = sscanf(ptr, "%lu", &free);
302 if (ret != 1)
303 free = 0;
306 memset(buff, 0, sizeof(buff));
309 if (total > 0)
310 s->mem_used = 100.f * (total - free) / total;
311 else
312 s->mem_used = 0.f;
314 fclose(fp);
316 return 0;
319 static int sys_stats(struct ifstat *s)
321 int ret, cpu;
322 char *ptr, *ptr2;
323 char buff[4096];
325 FILE *fp = fopen("/proc/stat", "r");
326 if (!fp) {
327 whine("Cannot open /proc/stat!\n");
328 return -ENOENT;
331 stats_check_alloc(s);
333 memset(buff, 0, sizeof(buff));
334 while (fgets(buff, sizeof(buff), fp) != NULL) {
335 buff[sizeof(buff) - 1] = 0;
337 if ((ptr = strstr(buff, "cpu")) != NULL) {
338 ptr += strlen("cpu");
339 if (*ptr == ' ')
340 goto next;
341 ptr2 = ptr;
343 while (*ptr != ' ' && *ptr != 0)
344 ptr++;
345 *ptr = 0;
347 cpu = atoi(ptr2);
348 if (cpu < 0 || cpu >= s->irqs_len)
349 goto next;
350 ptr++;
352 ret = sscanf(ptr, "%lu%lu%lu%lu%lu", &s->cpu_user[cpu],
353 &s->cpu_nice[cpu], &s->cpu_sys[cpu],
354 &s->cpu_idle[cpu], &s->cpu_iow[cpu]);
355 if (ret != 5)
356 goto next;
357 } else if ((ptr = strstr(buff, "ctxt")) != NULL) {
358 ptr += strlen("ctxt");
359 ptr++;
361 while (*ptr == ' ')
362 ptr++;
364 ret = sscanf(ptr, "%lu", &s->ctxt);
365 if (ret != 1)
366 s->ctxt = 0;
367 } else if ((ptr = strstr(buff, "processes")) != NULL) {
368 ptr += strlen("processes");
369 ptr++;
371 while (*ptr == ' ')
372 ptr++;
374 ret = sscanf(ptr, "%lu", &s->forks);
375 if (ret != 1)
376 s->forks = 0;
377 } else if ((ptr = strstr(buff, "procs_running")) != NULL) {
378 ptr += strlen("procs_running");
379 ptr++;
381 while (*ptr == ' ')
382 ptr++;
384 ret = sscanf(ptr, "%lu", &s->procs_run);
385 if (ret != 1)
386 s->procs_run = 0;
387 } else if ((ptr = strstr(buff, "procs_blocked")) != NULL) {
388 ptr += strlen("procs_blocked");
389 ptr++;
391 while (*ptr == ' ')
392 ptr++;
394 ret = sscanf(ptr, "%lu", &s->procs_iow);
395 if (ret != 1)
396 s->procs_iow = 0;
398 next:
399 memset(buff, 0, sizeof(buff));
402 fclose(fp);
404 return 0;
407 static int irq_stats(const char *ifname, struct ifstat *s)
409 int i;
410 char *ptr, *ptr2;
411 char buff[4096];
412 FILE *fp;
414 /* We exclude lo! */
415 if (!strncmp("lo", ifname, strlen("lo")))
416 return 0;
418 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, top-like kernel networking and system statistics\n",
788 VERSION_STRING);
789 puts("http://www.netsniff-ng.org\n\n"
790 "Usage: ifpps [options] || ifpps <netdev>\n"
791 "Options:\n"
792 " -d|--dev <netdev> Device to fetch statistics for i.e., eth0\n"
793 " -p|--promisc Promiscuous mode\n"
794 " -t|--interval <time> Refresh time in sec (default 1 s)\n"
795 " -c|--term Output to terminal\n"
796 " -C|--csv Output to terminal as CSV\n"
797 " E.g. post-processing with Gnuplot et al.\n"
798 " -H|--csv-tablehead Print CSV table head\n"
799 " -l|--loop Loop terminal output\n"
800 " -v|--version Print version\n"
801 " -h|--help Print this help\n\n"
802 "Examples:\n"
803 " ifpps --dev eth0\n"
804 " ifpps --dev eth0 --interval 60 --csv\n\n"
805 "Please report bugs to <bugs@netsniff-ng.org>\n"
806 "Copyright (C) 2009-2012 Daniel Borkmann <daniel@netsniff-ng.org>\n"
807 "License: GNU GPL version 2.0\n"
808 "This is free software: you are free to change and redistribute it.\n"
809 "There is NO WARRANTY, to the extent permitted by law.\n");
810 die();
813 static void version(void)
815 printf("\nifpps %s, top-like kernel networking statistics per sec\n",
816 VERSION_STRING);
817 puts("http://www.netsniff-ng.org\n\n"
818 "Please report bugs to <bugs@netsniff-ng.org>\n"
819 "Copyright (C) 2009-2012 Daniel Borkmann <daniel@netsniff-ng.org>\n"
820 "License: GNU GPL version 2.0\n"
821 "This is free software: you are free to change and redistribute it.\n"
822 "There is NO WARRANTY, to the extent permitted by law.\n");
823 die();
826 int main(int argc, char **argv)
828 short ifflags = 0;
829 int c, opt_index, ret;
830 unsigned int promisc = 0;
831 char *ifname = NULL;
832 uint32_t interval = 1;
833 int (*main_loop)(const char *ifname, uint32_t interval) = screen_loop;
835 while ((c = getopt_long(argc, argv, short_options, long_options,
836 &opt_index)) != EOF) {
837 switch (c) {
838 case 'h':
839 help();
840 break;
841 case 'v':
842 version();
843 break;
844 case 'd':
845 ifname = xstrndup(optarg, IFNAMSIZ);
846 break;
847 case 't':
848 interval = atoi(optarg);
849 break;
850 case 'c':
851 mode |= TERM_MODE_NORMAL;
852 main_loop = print_loop;
853 break;
854 case 'l':
855 loop = 1;
856 break;
857 case 'p':
858 promisc = 1;
859 break;
860 case 'C':
861 mode |= TERM_MODE_CSV;
862 main_loop = print_loop;
863 break;
864 case 'H':
865 mode |= TERM_MODE_CSV_HDR;
866 main_loop = print_loop;
867 break;
868 case '?':
869 switch (optopt) {
870 case 'd':
871 case 't':
872 panic("Option -%c requires an argument!\n",
873 optopt);
874 default:
875 if (isprint(optopt))
876 whine("Unknown option character "
877 "`0x%X\'!\n", optopt);
878 die();
880 default:
881 break;
885 if (argc == 1)
886 help();
887 if (argc == 2)
888 ifname = xstrndup(argv[1], IFNAMSIZ);
889 if (ifname == NULL)
890 panic("No networking device given!\n");
891 if (!strncmp("lo", ifname, IFNAMSIZ))
892 panic("lo is not supported!\n");
893 if (device_mtu(ifname) == 0)
894 panic("This is no networking device!\n");
895 register_signal(SIGINT, signal_handler);
896 register_signal(SIGHUP, signal_handler);
898 if (promisc)
899 ifflags = enter_promiscuous_mode(ifname);
900 ret = main_loop(ifname, interval);
901 if (promisc)
902 leave_promiscuous_mode(ifname, ifflags);
904 xfree(ifname);
905 return ret;