capt_get_packet(): check for key press only every 20ms
[iptraf-ng.git] / src / ifstats.c
blob2d099a2f02cb376b00f097c2fe832bff51a45e1a
1 /* For terms of usage/redistribution/modification see the LICENSE file */
2 /* For authors and contributors see the AUTHORS file */
4 /***
6 ifstats.c - the interface statistics module
8 ***/
10 #include "iptraf-ng-compat.h"
12 #include "tui/labels.h"
13 #include "tui/listbox.h"
14 #include "tui/msgboxes.h"
15 #include "tui/winops.h"
17 #include "ifaces.h"
18 #include "fltdefs.h"
19 #include "packet.h"
20 #include "options.h"
21 #include "log.h"
22 #include "dirs.h"
23 #include "deskman.h"
24 #include "attrs.h"
25 #include "serv.h"
26 #include "timer.h"
27 #include "logvars.h"
28 #include "promisc.h"
29 #include "error.h"
30 #include "ifstats.h"
31 #include "rate.h"
32 #include "capt.h"
33 #include "counters.h"
35 #define SCROLLUP 0
36 #define SCROLLDOWN 1
38 struct iflist {
39 char ifname[IFNAMSIZ];
40 int ifindex;
41 unsigned long long iptotal;
42 unsigned long long ip6total;
43 unsigned long badtotal;
44 unsigned long long noniptotal;
45 unsigned long long total;
46 unsigned int spanbr;
47 unsigned long br;
48 struct rate rate;
49 unsigned long peakrate;
50 unsigned int index;
51 struct iflist *prev_entry;
52 struct iflist *next_entry;
55 struct iftab {
56 struct iflist *head;
57 struct iflist *tail;
58 struct iflist *firstvisible;
59 struct iflist *lastvisible;
60 struct pkt_counter totals;
61 struct rate rate_total;
62 struct rate rate_totalpps;
63 WINDOW *borderwin;
64 PANEL *borderpanel;
65 WINDOW *statwin;
66 PANEL *statpanel;
70 * USR1 log-rotation signal handlers
73 static void rotate_gstat_log(int s __unused)
75 rotate_flag = 1;
76 strcpy(target_logname, GSTATLOG);
77 signal(SIGUSR1, rotate_gstat_log);
80 static void writegstatlog(struct iftab *table, unsigned long nsecs, FILE *fd)
82 struct iflist *ptmp = table->head;
83 char atime[TIME_TARGET_MAX];
85 genatime(time(NULL), atime);
86 fprintf(fd, "\n*** General interface statistics log generated %s\n\n",
87 atime);
89 while (ptmp != NULL) {
91 fprintf(fd,
92 "%s: %llu total, %llu IP, %llu non-IP, %lu IP checksum errors",
93 ptmp->ifname, ptmp->total, ptmp->iptotal,
94 ptmp->noniptotal, ptmp->badtotal);
96 if (nsecs > 5) {
97 char buf[64];
99 rate_print(ptmp->br / nsecs, buf, sizeof(buf));
100 fprintf(fd, ", average activity %s", buf);
101 rate_print(ptmp->peakrate, buf, sizeof(buf));
102 fprintf(fd, ", peak activity %s", buf);
103 rate_print(rate_get_average(&ptmp->rate), buf, sizeof(buf));
104 fprintf(fd, ", last 5-second average activity %s", buf);
106 fprintf(fd, "\n");
108 ptmp = ptmp->next_entry;
111 fprintf(fd, "\n%lu seconds running time\n", nsecs);
112 fflush(fd);
116 * Function to check if an interface is already in the interface list.
117 * This eliminates duplicate interface entries due to aliases
120 static int ifinlist(struct iflist *list, char *ifname)
122 struct iflist *ptmp = list;
123 int result = 0;
125 while ((ptmp != NULL) && (result == 0)) {
126 result = (strcmp(ifname, ptmp->ifname) == 0);
127 ptmp = ptmp->next_entry;
130 return result;
133 static struct iflist *alloc_iflist_entry(void)
135 struct iflist *tmp = xmallocz(sizeof(struct iflist));
137 rate_alloc(&tmp->rate, 5);
139 return tmp;
142 static void free_iflist_entry(struct iflist *ptr)
144 if (!ptr)
145 return;
147 rate_destroy(&ptr->rate);
148 free(ptr);
152 * Initialize the list of interfaces. This linked list is used in the
153 * selection boxes as well as in the general interface statistics screen.
155 * This function parses the /proc/net/dev file and grabs the interface names
156 * from there. The SIOGIFFLAGS ioctl() call is used to determine whether the
157 * interfaces are active. Inactive interfaces are omitted from selection
158 * lists.
161 static void initiflist(struct iflist **list)
163 char ifname[IFNAMSIZ];
165 *list = NULL;
167 FILE *fd = open_procnetdev();
168 if (fd == NULL) {
169 tui_error(ANYKEY_MSG, "Unable to obtain interface list");
170 return;
173 while (get_next_iface(fd, ifname, sizeof(ifname))) {
174 if (!*ifname)
175 continue;
177 if (ifinlist(*list, ifname)) /* ignore entry if already in */
178 continue; /* interface list */
181 * Check if the interface is actually up running. This prevents
182 * inactive devices in /proc/net/dev from actually appearing in
183 * interface lists used by IPTraf.
186 if (!dev_up(ifname))
187 continue;
189 int ifindex = dev_get_ifindex(ifname);
190 if (ifindex < 0)
191 continue;
193 * At this point, the interface is now sure to be up and running.
196 struct iflist *itmp = alloc_iflist_entry();
197 itmp->ifindex = ifindex;
198 strcpy(itmp->ifname, ifname);
200 /* make the linked list sorted by ifindex */
201 struct iflist *cur = *list, *last = NULL;
202 while (cur != NULL && cur->ifindex < ifindex) {
203 last = cur;
204 cur = cur->next_entry;
206 itmp->prev_entry = last;
207 itmp->next_entry = cur;
208 if (cur)
209 cur->prev_entry = itmp;
210 if (last)
211 last->next_entry = itmp;
212 else
213 *list = itmp;
215 fclose(fd);
217 /* let the index follow the sorted linked list */
218 unsigned int index = 1;
219 struct iflist *cur;
220 for (cur = *list; cur != NULL; cur = cur->next_entry)
221 cur->index = index++;
224 static struct iflist *positionptr(struct iflist *iflist, const int ifindex)
226 struct iflist *ptmp = iflist;
227 struct iflist *last = ptmp;
229 while ((ptmp != NULL) && (ptmp->ifindex != ifindex)) {
230 last = ptmp;
231 ptmp = ptmp->next_entry;
233 /* no interface was found, try to create new one */
234 if (ptmp == NULL) {
235 struct iflist *itmp = alloc_iflist_entry();
236 itmp->ifindex = ifindex;
237 itmp->index = last->index + 1;
238 int r = dev_get_ifname(ifindex, itmp->ifname);
239 if (r != 0) {
240 write_error("Error getting interface name");
241 free_iflist_entry(itmp);
242 return(NULL);
245 /* last can't be NULL otherwise we will have empty iflist */
246 last->next_entry = itmp;
247 itmp->prev_entry = last;
248 itmp->next_entry = NULL;
249 ptmp = itmp;
251 return(ptmp);
254 static void destroyiflist(struct iflist *list)
256 struct iflist *ptmp = list;
258 while (ptmp != NULL) {
259 struct iflist *ctmp = ptmp->next_entry;
261 free_iflist_entry(ptmp);
262 ptmp = ctmp;
266 static void no_ifaces_error(void)
268 write_error("No active interfaces. Check their status or the /proc filesystem");
271 static void updaterates(struct iftab *table, unsigned long msecs)
273 struct iflist *ptmp = table->head;
274 unsigned long rate;
276 rate_add_rate(&table->rate_total, table->totals.pc_bytes, msecs);
277 rate_add_rate(&table->rate_totalpps, table->totals.pc_packets, msecs);
278 pkt_counter_reset(&table->totals);
279 while (ptmp != NULL) {
280 rate_add_rate(&ptmp->rate, ptmp->spanbr, msecs);
281 rate = rate_get_average(&ptmp->rate);
283 if (rate > ptmp->peakrate)
284 ptmp->peakrate = rate;
286 ptmp->spanbr = 0;
287 ptmp = ptmp->next_entry;
291 static void showrates(struct iftab *table)
293 struct iflist *ptmp = table->firstvisible;
294 unsigned int idx = table->firstvisible->index;
295 unsigned long rate;
296 char buf[64];
298 wattrset(table->statwin, HIGHATTR);
299 do {
300 rate = rate_get_average(&ptmp->rate);
301 rate_print(rate, buf, sizeof(buf));
302 wmove(table->statwin, ptmp->index - idx, 63 * COLS / 80);
303 wprintw(table->statwin, "%s", buf);
305 ptmp = ptmp->next_entry;
306 } while (ptmp != table->lastvisible->next_entry);
309 static void printifentry(struct iftab *table, struct iflist *ptmp)
311 int target_row = ptmp->index - table->firstvisible->index;
312 WINDOW *win = table->statwin;
314 if ((target_row < 0) || (target_row > LINES - 5))
315 return;
317 wattrset(win, STDATTR);
318 mvwprintw(win, target_row, 1, "%s", ptmp->ifname);
319 wattrset(win, HIGHATTR);
320 wmove(win, target_row, 14 * COLS / 80);
321 printlargenum(ptmp->total, win);
322 wmove(win, target_row, 24 * COLS / 80);
323 printlargenum(ptmp->iptotal, win);
324 wmove(win, target_row, 34 * COLS / 80);
325 printlargenum(ptmp->ip6total, win);
326 wmove(win, target_row, 44 * COLS / 80);
327 printlargenum(ptmp->noniptotal, win);
328 wmove(win, target_row, 53 * COLS / 80);
329 wprintw(win, "%7lu", ptmp->badtotal);
332 static void print_if_entries(struct iftab *table)
334 struct iflist *ptmp = table->firstvisible;
335 unsigned int i = 1;
337 unsigned int winht = LINES - 4;
339 do {
340 printifentry(table, ptmp);
342 if (i <= winht)
343 table->lastvisible = ptmp;
345 ptmp = ptmp->next_entry;
346 i++;
347 } while ((ptmp != NULL) && (i <= winht));
350 static void labelstats(WINDOW *win)
352 mvwprintw(win, 0, 1, " Iface ");
353 /* 14, 24, 34, ... from printifentry() */
354 /* 10 = strlen(printed number); from printlargenum() */
355 /* 7 = strlen(" Total ") */
356 /* 1 = align the string on 'l' from " Total " */
357 mvwprintw(win, 0, (14 * COLS / 80) + 10 - 7 + 1, " Total ");
358 mvwprintw(win, 0, (24 * COLS / 80) + 10 - 6 + 1, " IPv4 ");
359 mvwprintw(win, 0, (34 * COLS / 80) + 10 - 6 + 1, " IPv6 ");
360 mvwprintw(win, 0, (44 * COLS / 80) + 10 - 7 + 1, " NonIP ");
361 mvwprintw(win, 0, (53 * COLS / 80) + 8 - 7 + 1, " BadIP ");
362 mvwprintw(win, 0, (63 * COLS / 80) + 14 - 10, " Activity ");
365 static void initiftab(struct iftab *table)
367 table->borderwin = newwin(LINES - 2, COLS, 1, 0);
368 table->borderpanel = new_panel(table->borderwin);
370 rate_alloc(&table->rate_total, 5);
371 rate_alloc(&table->rate_totalpps, 5);
372 pkt_counter_reset(&table->totals);
374 move(LINES - 1, 1);
375 scrollkeyhelp();
376 stdexitkeyhelp();
377 wattrset(table->borderwin, BOXATTR);
378 tx_box(table->borderwin, ACS_VLINE, ACS_HLINE);
379 labelstats(table->borderwin);
380 table->statwin = newwin(LINES - 4, COLS - 2, 2, 1);
381 table->statpanel = new_panel(table->statwin);
382 tx_stdwinset(table->statwin);
383 wtimeout(table->statwin, -1);
384 wattrset(table->statwin, STDATTR);
385 tx_colorwin(table->statwin);
389 * Scrolling routines for the general interface statistics window
392 static void scrollgstatwin(struct iftab *table, int direction, int lines)
394 if (lines < 1)
395 return;
397 wattrset(table->statwin, STDATTR);
398 if (direction == SCROLLUP) {
399 for (int i = 0; i < lines; i++) {
400 if (table->lastvisible->next_entry == NULL)
401 break;
403 table->firstvisible = table->firstvisible->next_entry;
404 table->lastvisible = table->lastvisible->next_entry;
406 wscrl(table->statwin, 1);
407 scrollok(table->statwin, 0);
408 mvwprintw(table->statwin, LINES - 5, 0, "%*c", COLS - 2, ' ');
409 scrollok(table->statwin, 1);
411 printifentry(table, table->lastvisible);
413 } else {
414 for (int i = 0; i < lines; i++) {
415 if (table->firstvisible == table->head)
416 break;
418 table->firstvisible = table->firstvisible->prev_entry;
419 table->lastvisible = table->lastvisible->prev_entry;
421 wscrl(table->statwin, -1);
422 mvwprintw(table->statwin, 0, 0, "%*c", COLS - 2, ' ');
424 printifentry(table, table->firstvisible);
427 showrates(table);
430 static void ifstats_process_key(struct iftab *table, int ch)
432 switch (ch) {
433 case KEY_UP:
434 scrollgstatwin(table, SCROLLDOWN, 1);
435 break;
436 case KEY_DOWN:
437 scrollgstatwin(table, SCROLLUP, 1);
438 break;
439 case KEY_PPAGE:
440 case '-':
441 scrollgstatwin(table, SCROLLDOWN, LINES - 5);
442 break;
443 case KEY_NPAGE:
444 case ' ':
445 scrollgstatwin(table, SCROLLUP, LINES - 5);
446 break;
447 case KEY_HOME:
448 scrollgstatwin(table, SCROLLDOWN, INT_MAX);
449 break;
450 case KEY_END:
451 scrollgstatwin(table, SCROLLUP, INT_MAX);
452 break;
453 case 12:
454 case 'l':
455 case 'L':
456 tx_refresh_screen();
457 break;
458 case 'Q':
459 case 'q':
460 case 'X':
461 case 'x':
462 case 27:
463 case 24:
464 exitloop = 1;
465 break;
466 case ERR:
467 default:
468 /* no key ready, do nothing */
469 break;
473 static void ifstats_process_packet(struct iftab *table, struct pkt_hdr *pkt)
475 int pkt_result = packet_process(pkt, NULL, NULL, NULL,
476 MATCH_OPPOSITE_USECONFIG,
477 options.v6inv4asv6);
479 switch (pkt_result) {
480 case PACKET_OK: /* we only handle these */
481 case MORE_FRAGMENTS:
482 case CHECKSUM_ERROR:
483 break;
484 default: /* drop others */
485 return;
488 pkt_counter_update(&table->totals, pkt->pkt_len);
490 struct iflist *ptmp = positionptr(table->head, pkt->from->sll_ifindex);
491 if (!ptmp)
492 return;
494 ptmp->total++;
496 ptmp->spanbr += pkt->pkt_len;
497 ptmp->br += pkt->pkt_len;
499 if (pkt->pkt_protocol == ETH_P_IP) {
500 ptmp->iptotal++;
502 if (pkt_result == CHECKSUM_ERROR) {
503 ptmp->badtotal++;
504 return;
506 } else if (pkt->pkt_protocol == ETH_P_IPV6) {
507 ptmp->ip6total++;
508 } else {
509 ptmp->noniptotal++;
514 * The general interface statistics function
517 void ifstats(time_t facilitytime)
519 int logging = options.logging;
520 struct iftab table;
522 FILE *logfile = NULL;
524 int ch;
526 struct capt capt;
528 struct pkt_hdr pkt;
530 initiflist(&(table.head));
531 if (!table.head) {
532 no_ifaces_error();
533 return;
536 initiftab(&table);
538 LIST_HEAD(promisc);
539 if (options.promisc) {
540 promisc_init(&promisc, NULL);
541 promisc_set_list(&promisc);
544 if (capt_init(&capt, NULL) == -1) {
545 write_error("Unable to initialize packet capture interface");
546 goto err;
549 if (logging) {
550 if (strcmp(current_logfile, "") == 0) {
551 strcpy(current_logfile, GSTATLOG);
553 if (!daemonized)
554 input_logfile(current_logfile, &logging);
558 if (logging) {
559 opentlog(&logfile, GSTATLOG);
561 if (logfile == NULL)
562 logging = 0;
564 if (logging) {
565 signal(SIGUSR1, rotate_gstat_log);
567 rotate_flag = 0;
568 writelog(logging, logfile,
569 "******** General interface statistics started ********");
572 table.firstvisible = table.head;
573 print_if_entries(&table);
574 showrates(&table);
576 update_panels();
577 doupdate();
579 packet_init(&pkt);
581 exitloop = 0;
583 struct timeval now;
584 gettimeofday(&now, NULL);
585 struct timeval last_time = now;
586 struct timeval last_update = now;
588 time_t starttime = now.tv_sec;
589 time_t endtime = INT_MAX;
590 if (facilitytime != 0)
591 endtime = now.tv_sec + facilitytime * 60;
593 time_t log_next = INT_MAX;
594 if (logging)
595 log_next = now.tv_sec + options.logspan;
597 while (!exitloop) {
598 gettimeofday(&now, NULL);
600 if (now.tv_sec > last_time.tv_sec) {
601 unsigned long msecs = timeval_diff_msec(&now, &last_time);
602 updaterates(&table, msecs);
603 showrates(&table);
605 printelapsedtime(now.tv_sec - starttime, 1, table.borderwin);
607 print_packet_drops(capt_get_dropped(&capt), table.borderwin, 61);
609 wattrset(table.borderwin, BOXATTR);
610 char buf[64];
611 rate_print(rate_get_average(&table.rate_total), buf, sizeof(buf));
612 mvwprintw(table.borderwin,
613 getmaxy(table.borderwin) - 1, 19,
614 " Total: %s / %9lu pps ",
615 buf,
616 rate_get_average(&table.rate_totalpps));
618 if (logging && (now.tv_sec > log_next)) {
619 check_rotate_flag(&logfile);
620 writegstatlog(&table, now.tv_sec - starttime, logfile);
621 log_next = now.tv_sec + options.logspan;
624 if (now.tv_sec > endtime)
625 exitloop = 1;
627 last_time = now;
629 if (screen_update_needed(&now, &last_update)) {
630 print_if_entries(&table);
631 update_panels();
632 doupdate();
634 last_update = now;
637 if (capt_get_packet(&capt, &pkt, &ch, table.statwin) == -1) {
638 write_error("Packet receive failed");
639 exitloop = 1;
640 break;
643 if (ch != ERR)
644 ifstats_process_key(&table, ch);
646 if (pkt.pkt_len > 0) {
647 ifstats_process_packet(&table, &pkt);
648 capt_put_packet(&capt, &pkt);
652 packet_destroy(&pkt);
654 if (logging) {
655 signal(SIGUSR1, SIG_DFL);
656 writegstatlog(&table, time(NULL) - starttime, logfile);
657 writelog(logging, logfile,
658 "******** General interface statistics stopped ********");
659 fclose(logfile);
661 strcpy(current_logfile, "");
663 capt_destroy(&capt);
664 err:
665 if (options.promisc) {
666 promisc_restore_list(&promisc);
667 promisc_destroy(&promisc);
670 del_panel(table.statpanel);
671 delwin(table.statwin);
672 del_panel(table.borderpanel);
673 delwin(table.borderwin);
674 update_panels();
675 doupdate();
677 destroyiflist(table.head);
678 rate_destroy(&table.rate_total);
679 rate_destroy(&table.rate_totalpps);
682 void selectiface(char *ifname, int withall, int *aborted)
684 struct iflist *list;
685 struct iflist *ptmp;
687 struct scroll_list scrolllist;
689 initiflist(&list);
691 if (list == NULL) {
692 no_ifaces_error();
693 *aborted = 1;
694 return;
697 if ((withall) && (list != NULL)) {
698 ptmp = alloc_iflist_entry();
699 strncpy(ptmp->ifname, "All interfaces", sizeof(ptmp->ifname));
700 ptmp->ifindex = 0;
702 ptmp->prev_entry = NULL;
703 list->prev_entry = ptmp;
704 ptmp->next_entry = list;
705 list = ptmp;
707 tx_listkeyhelp(STDATTR, HIGHATTR);
709 ptmp = list;
711 tx_init_listbox(&scrolllist, 24, 14, (COLS - 24) / 2 - 9,
712 (LINES - 14) / 2, STDATTR, BOXATTR, BARSTDATTR,
713 HIGHATTR);
715 tx_set_listbox_title(&scrolllist, "Select Interface", 1);
717 while (ptmp != NULL) {
718 tx_add_list_entry(&scrolllist, (char *) ptmp, ptmp->ifname);
719 ptmp = ptmp->next_entry;
722 tx_show_listbox(&scrolllist);
723 tx_operate_listbox(&scrolllist, aborted);
724 tx_close_listbox(&scrolllist);
726 if (!(*aborted) && (list != NULL)) {
727 ptmp = (struct iflist *) scrolllist.textptr->nodeptr;
728 if ((withall) && (ptmp->prev_entry == NULL)) /* All Interfaces */
729 strcpy(ifname, "");
730 else
731 strcpy(ifname, ptmp->ifname);
734 tx_destroy_list(&scrolllist);
735 destroyiflist(list);
736 update_panels();
737 doupdate();