rollback
[netsniff-ng.git] / src / ifpps.c
blobe9750e6c14497d2924a9f30bcbfa091e0142ccc4
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];
413 /* We exclude lo! */
414 if (!strncmp("lo", ifname, strlen("lo")))
415 return 0;
417 FILE *fp = fopen("/proc/interrupts", "r");
418 if (!fp) {
419 whine("Cannot open /proc/interrupts!\n");
420 return -ENOENT;
423 stats_check_alloc(s);
425 memset(buff, 0, sizeof(buff));
426 while (fgets(buff, sizeof(buff), fp) != NULL) {
427 buff[sizeof(buff) - 1] = 0;
429 if (strstr(buff, ifname) == NULL)
430 continue;
432 ptr = buff;
433 while (*ptr != ':')
434 ptr++;
435 *ptr = 0;
436 s->irq_nr = atoi(buff);
438 bug_on(s->irq_nr == 0);
440 for (i = 0; i < s->irqs_len; ++i) {
441 ptr++;
442 ptr2 = ptr;
443 while (*ptr == ' ')
444 ptr++;
445 while (*ptr != ' ' && *ptr != 0)
446 ptr++;
447 *ptr = 0;
448 s->irqs[i] = atoi(ptr2);
451 memset(buff, 0, sizeof(buff));
454 fclose(fp);
456 return 0;
459 static void diff_stats(struct ifstat *old, struct ifstat *new,
460 struct ifstat *diff)
462 int i;
464 if(old->irqs_len != new->irqs_len)
465 return; /* Refetch stats and take old diff! */
467 diff->rx_bytes = new->rx_bytes - old->rx_bytes;
468 diff->rx_packets = new->rx_packets - old->rx_packets;
469 diff->rx_drops = new->rx_drops - old->rx_drops;
470 diff->rx_errors = new->rx_errors - old->rx_errors;
471 diff->rx_fifo = new->rx_fifo - old->rx_fifo;
472 diff->rx_frame = new->rx_frame - old->rx_frame;
473 diff->rx_multi = new->rx_multi - old->rx_multi;
474 diff->tx_bytes = new->tx_bytes - old->tx_bytes;
475 diff->tx_packets = new->tx_packets - old->tx_packets;
476 diff->tx_drops = new->tx_drops - old->tx_drops;
477 diff->tx_errors = new->tx_errors - old->tx_errors;
478 diff->tx_fifo = new->tx_fifo - old->tx_fifo;
479 diff->tx_colls = new->tx_colls - old->tx_colls;
480 diff->tx_carrier = new->tx_carrier - old->tx_carrier;
481 diff->wifi_signal_level = new->wifi_signal_level - old->wifi_signal_level;
482 diff->wifi_noise_level = new->wifi_noise_level - old->wifi_noise_level;
483 diff->wifi_link_qual = new->wifi_link_qual - old->wifi_link_qual;
484 diff->ctxt = new->ctxt - old->ctxt;
485 diff->forks = new->forks - old->forks;
486 diff->procs_run = new->procs_run - old->procs_run;
487 diff->procs_iow = new->procs_iow - old->procs_iow;
489 stats_check_alloc(diff);
491 diff->irq_nr = new->irq_nr;
493 for (i = 0; i < diff->irqs_len; ++i) {
494 diff->irqs[i] = new->irqs[i] - old->irqs[i];
495 diff->irqs_srx[i] = new->irqs_srx[i] - old->irqs_srx[i];
496 diff->irqs_stx[i] = new->irqs_stx[i] - old->irqs_stx[i];
497 diff->cpu_user[i] = new->cpu_user[i] - old->cpu_user[i];
498 diff->cpu_nice[i] = new->cpu_nice[i] - old->cpu_nice[i];
499 diff->cpu_sys[i] = new->cpu_sys[i] - old->cpu_sys[i];
500 diff->cpu_idle[i] = new->cpu_idle[i] - old->cpu_idle[i];
501 diff->cpu_iow[i] = new->cpu_iow[i] - old->cpu_iow[i];
505 static char *snr_to_str(int level)
507 // empirical values
508 if (level > 40)
509 return "very good signal";
510 if (level > 25 && level <= 40)
511 return "good signal";
512 if (level > 15 && level <= 25)
513 return "poor signal";
514 if (level > 10 && level <= 15)
515 return "very poor signal";
516 if (level <= 10)
517 return "no signal";
518 /* unreachable */
519 return "unknown";
522 static void screen_init(WINDOW **screen)
524 (*screen) = initscr();
525 noecho();
526 cbreak();
527 nodelay((*screen), TRUE);
528 refresh();
529 wrefresh((*screen));
532 static void screen_update(WINDOW *screen, const char *ifname,
533 struct ifstat *s, struct ifstat *t,
534 int *first, double interval)
536 int i, j = 0;
538 curs_set(0);
539 mvwprintw(screen, 1, 2, "Kernel net/sys statistics for %s, t=%.2lfs",
540 ifname, interval);
541 attron(A_REVERSE);
542 mvwprintw(screen, 3, 0,
543 " RX: %16.3f MiB/t %10lu pkts/t %10lu drops/t %10lu errors/t ",
544 1.f * s->rx_bytes / (1 << 20), s->rx_packets, s->rx_drops,
545 s->rx_errors);
546 mvwprintw(screen, 4, 0,
547 " TX: %16.3f MiB/t %10lu pkts/t %10lu drops/t %10lu errors/t ",
548 1.f * s->tx_bytes / (1 << 20), s->tx_packets, s->tx_drops,
549 s->tx_errors);
550 attroff(A_REVERSE);
551 mvwprintw(screen, 6, 2,
552 "RX: %16.3f MiB %10lu pkts %10lu drops %10lu errors",
553 1.f * t->rx_bytes / (1 << 20), t->rx_packets, t->rx_drops,
554 t->rx_errors);
555 mvwprintw(screen, 7, 2,
556 "TX: %16.3f MiB %10lu pkts %10lu drops %10lu errors",
557 1.f * t->tx_bytes / (1 << 20), t->tx_packets, t->tx_drops,
558 t->tx_errors);
559 j = 9;
560 mvwprintw(screen, j++, 2, "SYS: %14ld cs/t %10.1f%% mem "
561 "%13ld running %10ld iowait",
562 s->ctxt, t->mem_used, t->procs_run, t->procs_iow);
563 j++;
564 if (s->irq_nr != 0) {
565 for(i = 0; i < s->irqs_len; ++i) {
566 unsigned long all = s->cpu_user[i] + s->cpu_nice[i] +
567 s->cpu_sys[i] + s->cpu_idle[i] +
568 s->cpu_iow[i];
569 mvwprintw(screen, j++, 2, "CPU%d: %13.1f%% usr/t "
570 "%9.1f%% sys/t %10.1f%% idl/t %11.1f%% iow/t ",
572 100.f * (s->cpu_user[i] + s->cpu_nice[i]) / all,
573 100.f * s->cpu_sys[i] / all,
574 100.f * s->cpu_idle[i] /all,
575 100.f * s->cpu_iow[i] / all);
577 j++;
578 for(i = 0; i < s->irqs_len; ++i)
579 mvwprintw(screen, j++, 2, "CPU%d: %14ld irqs/t "
580 "%15ld soirq RX/t %15ld soirq TX/t ",
581 i, s->irqs[i], s->irqs_srx[i], s->irqs_stx[i]);
582 j++;
583 for(i = 0; i < s->irqs_len; ++i)
584 mvwprintw(screen, j++, 2, "CPU%d: %14ld irqs",
585 i, t->irqs[i]);
586 j++;
588 if (t->wifi_bitrate > 0) {
589 mvwprintw(screen, j++, 2, "LinkQual: %7d/%d (%d/t) ",
590 t->wifi_link_qual, t->wifi_link_qual_max,
591 s->wifi_link_qual);
592 mvwprintw(screen, j++, 2, "Signal: %8d dBm (%d dBm/t) ",
593 t->wifi_signal_level, s->wifi_signal_level);
594 mvwprintw(screen, j++, 2, "Noise: %8d dBm (%d dBm/t) ",
595 t->wifi_noise_level, s->wifi_noise_level);
596 mvwprintw(screen, j++, 2, "SNR: %8d dBm (%s) ",
597 t->wifi_signal_level - t->wifi_noise_level,
598 snr_to_str(t->wifi_signal_level - t->wifi_noise_level));
599 j++;
601 if (*first) {
602 mvwprintw(screen, 2, 2, "Collecting data ...");
603 *first = 0;
604 } else
605 mvwprintw(screen, 2, 2, " ");
607 wrefresh(screen);
608 refresh();
611 static void screen_end(void)
613 endwin();
616 static void print_update(const char *ifname, struct ifstat *s,
617 struct ifstat *t, double interval)
619 int i;
621 printf("RX: %16.3f MiB/t %10lu Pkts/t %10lu Drops/t %10lu Errors/t\n",
622 1.f * s->rx_bytes / (1 << 20), s->rx_packets, s->rx_drops,
623 s->rx_errors);
624 printf("TX: %16.3f MiB/t %10lu Pkts/t %10lu Drops/t %10lu Errors/t\n",
625 1.f * s->tx_bytes / (1 << 20), s->tx_packets, s->tx_drops,
626 s->tx_errors);
627 if (s->irq_nr != 0)
628 for(i = 0; i < s->irqs_len; ++i)
629 printf("CPU%d: %10ld IRQs/t "
630 "%10ld SoIRQ RX/t "
631 "%10ld SoIRQ TX/t\n", i,
632 s->irqs[i], s->irqs_srx[i], s->irqs_stx[i]);
633 if (t->wifi_bitrate > 0) {
634 printf("LinkQual: %6d/%d (%d/t)\n", t->wifi_link_qual,
635 t->wifi_link_qual_max, s->wifi_link_qual);
636 printf("Signal: %8d dBm (%d dBm/t)\n", t->wifi_signal_level,
637 s->wifi_signal_level);
638 printf("Noise: %8d dBm (%d dBm/t)\n", t->wifi_noise_level,
639 s->wifi_noise_level);
643 static void print_update_csv(const char *ifname, struct ifstat *s,
644 struct ifstat *t, double interval)
646 int i;
648 printf("%ld,%lu,%lu,%lu,%lu,", time(0), s->rx_bytes, s->rx_packets,
649 s->rx_drops, s->rx_errors);
650 printf("%lu,%lu,%lu,%lu", s->tx_bytes, s->tx_packets, s->tx_drops,
651 s->tx_errors);
652 if (s->irq_nr != 0)
653 for(i = 0; i < s->irqs_len; ++i)
654 printf(",%ld,%ld,%ld", s->irqs[i], s->irqs_srx[i],
655 s->irqs_stx[i]);
656 if (t->wifi_bitrate > 0) {
657 printf(",%d,%d", t->wifi_link_qual, t->wifi_link_qual_max);
658 printf(",%d", t->wifi_signal_level);
659 printf(",%d", t->wifi_noise_level);
661 printf("\n");
664 static void print_update_csv_hdr(const char *ifname, struct ifstat *s,
665 struct ifstat *t, double interval)
667 int i;
669 printf("Unixtime,RX Byte/t,RX Pkts/t,RX Drops/t,RX Errors/t,");
670 printf("TX Byte/t,TX Pkts/t,TX Drops/t,TX Errors/t");
671 if (s->irq_nr != 0)
672 for(i = 0; i < s->irqs_len; ++i)
673 printf(",CPU%d IRQs/t,CPU%d SoIRQ RX/t,"
674 "CPU%d SoIRQ TX/t", i, i, i);
675 if (t->wifi_bitrate > 0)
676 printf(",LinkQual,LinkQualMax,Signal Level,Noise Level");
677 printf("\n");
680 static inline int do_stats(const char *ifname, struct ifstat *s)
682 int ret = 0;
684 ret += rxtx_stats(ifname, s);
685 ret += irq_stats(ifname, s);
686 ret += irq_sstats(s);
687 ret += sys_stats(s);
688 ret += mem_stats(s);
689 ret += wifi_stats(ifname, s);
691 return ret;
694 static int screen_loop(const char *ifname, uint32_t interval)
696 int ret = 0, first = 1;
697 struct ifstat old, new, curr;
698 WINDOW *screen = NULL;
700 memset(&old, 0, sizeof(old));
701 memset(&new, 0, sizeof(new));
702 memset(&curr, 0, sizeof(curr));
704 screen_init(&screen);
706 while (!sigint) {
707 if (getch() == 'q')
708 goto out;
710 screen_update(screen, ifname, &curr, &new, &first, interval);
712 ret = do_stats(ifname, &old);
713 if (ret != 0)
714 goto out;
716 sleep(interval);
718 ret = do_stats(ifname, &new);
719 if (ret != 0)
720 goto out;
722 diff_stats(&old, &new, &curr);
724 out:
725 screen_end();
727 if (ret != 0)
728 whine("Error fetching stats!\n");
729 if (old.irqs)
730 xfree(old.irqs);
731 if (new.irqs)
732 xfree(new.irqs);
733 if (curr.irqs)
734 xfree(curr.irqs);
736 return 0;
739 static int print_loop(const char *ifname, uint32_t interval)
741 int ret, first = 1;
742 struct ifstat old, new, curr;
744 memset(&old, 0, sizeof(old));
745 memset(&new, 0, sizeof(new));
746 memset(&curr, 0, sizeof(curr));
747 do {
748 ret = do_stats(ifname, &old);
749 if (ret != 0)
750 goto out;
752 sleep(interval);
754 ret = do_stats(ifname, &new);
755 if (ret != 0)
756 goto out;
758 diff_stats(&old, &new, &curr);
760 if (first && (mode & TERM_MODE_CSV_HDR) ==
761 TERM_MODE_CSV_HDR) {
762 print_update_csv_hdr(ifname, &curr, &new, interval);
763 first = 0;
766 if ((mode & TERM_MODE_CSV) == TERM_MODE_CSV)
767 print_update_csv(ifname, &curr, &new, interval);
768 else if ((mode & TERM_MODE_NORMAL) == TERM_MODE_NORMAL)
769 print_update(ifname, &curr, &new, interval);
770 } while (loop && !sigint);
771 out:
772 if (ret != 0)
773 whine("Error fetching stats!\n");
774 if (old.irqs)
775 xfree(old.irqs);
776 if (new.irqs)
777 xfree(new.irqs);
778 if (curr.irqs)
779 xfree(curr.irqs);
781 return 0;
784 static void help(void)
786 printf("\n%s %s, top-like kernel networking and system statistics\n",
787 PROGNAME_STRING, VERSION_STRING);
788 puts("http://www.netsniff-ng.org\n\n"
789 "Usage: ifpps [options] || ifpps <netdev>\n"
790 "Options:\n"
791 " -d|--dev <netdev> Device to fetch statistics for i.e., eth0\n"
792 " -p|--promisc Promiscuous mode\n"
793 " -t|--interval <time> Refresh time in sec (default 1 s)\n"
794 " -c|--term Output to terminal\n"
795 " -C|--csv Output to terminal as CSV\n"
796 " E.g. post-processing with Gnuplot et al.\n"
797 " -H|--csv-tablehead Print CSV table head\n"
798 " -l|--loop Loop terminal output\n"
799 " -v|--version Print version\n"
800 " -h|--help Print this help\n\n"
801 "Examples:\n"
802 " ifpps --dev eth0\n"
803 " ifpps --dev eth0 --interval 60 --csv\n\n"
804 "Please report bugs to <bugs@netsniff-ng.org>\n"
805 "Copyright (C) 2009-2012 Daniel Borkmann <daniel@netsniff-ng.org>\n"
806 "License: GNU GPL version 2.0\n"
807 "This is free software: you are free to change and redistribute it.\n"
808 "There is NO WARRANTY, to the extent permitted by law.\n");
809 die();
812 static void version(void)
814 printf("\n%s %s, top-like kernel networking statistics per sec\n",
815 PROGNAME_STRING, VERSION_STRING);
816 puts("http://www.netsniff-ng.org\n\n"
817 "Please report bugs to <bugs@netsniff-ng.org>\n"
818 "Copyright (C) 2009-2012 Daniel Borkmann <daniel@netsniff-ng.org>\n"
819 "License: GNU GPL version 2.0\n"
820 "This is free software: you are free to change and redistribute it.\n"
821 "There is NO WARRANTY, to the extent permitted by law.\n");
822 die();
825 int main(int argc, char **argv)
827 short ifflags = 0;
828 int c, opt_index, ret;
829 unsigned int promisc = 0;
830 char *ifname = NULL;
831 uint32_t interval = 1;
832 int (*main_loop)(const char *ifname, uint32_t interval) = screen_loop;
834 while ((c = getopt_long(argc, argv, short_options, long_options,
835 &opt_index)) != EOF) {
836 switch (c) {
837 case 'h':
838 help();
839 break;
840 case 'v':
841 version();
842 break;
843 case 'd':
844 ifname = xstrndup(optarg, IFNAMSIZ);
845 break;
846 case 't':
847 interval = atoi(optarg);
848 break;
849 case 'c':
850 mode |= TERM_MODE_NORMAL;
851 main_loop = print_loop;
852 break;
853 case 'l':
854 loop = 1;
855 break;
856 case 'p':
857 promisc = 1;
858 break;
859 case 'C':
860 mode |= TERM_MODE_CSV;
861 main_loop = print_loop;
862 break;
863 case 'H':
864 mode |= TERM_MODE_CSV_HDR;
865 main_loop = print_loop;
866 break;
867 case '?':
868 switch (optopt) {
869 case 'd':
870 case 't':
871 panic("Option -%c requires an argument!\n",
872 optopt);
873 default:
874 if (isprint(optopt))
875 whine("Unknown option character "
876 "`0x%X\'!\n", optopt);
877 die();
879 default:
880 break;
884 if (argc == 1)
885 help();
886 if (argc == 2)
887 ifname = xstrndup(argv[1], IFNAMSIZ);
888 if (ifname == NULL)
889 panic("No networking device given!\n");
890 if (!strncmp("lo", ifname, IFNAMSIZ))
891 panic("lo is not supported!\n");
892 if (device_mtu(ifname) == 0)
893 panic("This is no networking device!\n");
894 register_signal(SIGINT, signal_handler);
895 register_signal(SIGHUP, signal_handler);
896 if (promisc) {
897 check_for_root_maybe_die();
898 ifflags = enter_promiscuous_mode(ifname);
900 ret = main_loop(ifname, interval);
901 if (promisc)
902 leave_promiscuous_mode(ifname, ifflags);
903 xfree(ifname);
905 return ret;