1 /* For terms of usage/redistribution/modification see the LICENSE file */
2 /* For authors and contributors see the AUTHORS file */
6 ifstats.c - the interface statistics module
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"
39 char ifname
[IFNAMSIZ
];
41 unsigned long long iptotal
;
42 unsigned long long ip6total
;
43 unsigned long badtotal
;
44 unsigned long long noniptotal
;
45 unsigned long long total
;
49 unsigned long peakrate
;
51 struct iflist
*prev_entry
;
52 struct iflist
*next_entry
;
58 struct iflist
*firstvisible
;
59 struct iflist
*lastvisible
;
60 struct pkt_counter totals
;
61 struct rate rate_total
;
62 struct rate rate_totalpps
;
70 * USR1 log-rotation signal handlers
73 static void rotate_gstat_log(int s __unused
)
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",
89 while (ptmp
!= NULL
) {
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
);
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
);
108 ptmp
= ptmp
->next_entry
;
111 fprintf(fd
, "\n%lu seconds running time\n", nsecs
);
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
;
125 while ((ptmp
!= NULL
) && (result
== 0)) {
126 result
= (strcmp(ifname
, ptmp
->ifname
) == 0);
127 ptmp
= ptmp
->next_entry
;
133 static struct iflist
*alloc_iflist_entry(void)
135 struct iflist
*tmp
= xmallocz(sizeof(struct iflist
));
137 rate_alloc(&tmp
->rate
, 5);
142 static void free_iflist_entry(struct iflist
*ptr
)
147 rate_destroy(&ptr
->rate
);
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
161 static void initiflist(struct iflist
**list
)
163 char ifname
[IFNAMSIZ
];
167 FILE *fd
= open_procnetdev();
169 tui_error(ANYKEY_MSG
, "Unable to obtain interface list");
173 while (get_next_iface(fd
, ifname
, sizeof(ifname
))) {
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.
189 int ifindex
= dev_get_ifindex(ifname
);
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
) {
204 cur
= cur
->next_entry
;
206 itmp
->prev_entry
= last
;
207 itmp
->next_entry
= cur
;
209 cur
->prev_entry
= itmp
;
211 last
->next_entry
= itmp
;
217 /* let the index follow the sorted linked list */
218 unsigned int index
= 1;
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
)) {
231 ptmp
= ptmp
->next_entry
;
233 /* no interface was found, try to create new one */
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
);
240 write_error("Error getting interface name");
241 free_iflist_entry(itmp
);
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
;
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
);
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
;
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
;
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
;
298 wattrset(table
->statwin
, HIGHATTR
);
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))
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
;
337 unsigned int winht
= LINES
- 4;
340 printifentry(table
, ptmp
);
343 table
->lastvisible
= ptmp
;
345 ptmp
= ptmp
->next_entry
;
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
);
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
)
397 wattrset(table
->statwin
, STDATTR
);
398 if (direction
== SCROLLUP
) {
399 for (int i
= 0; i
< lines
; i
++) {
400 if (table
->lastvisible
->next_entry
== NULL
)
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
);
414 for (int i
= 0; i
< lines
; i
++) {
415 if (table
->firstvisible
== table
->head
)
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
);
430 static void ifstats_process_key(struct iftab
*table
, int ch
)
434 scrollgstatwin(table
, SCROLLDOWN
, 1);
437 scrollgstatwin(table
, SCROLLUP
, 1);
441 scrollgstatwin(table
, SCROLLDOWN
, LINES
- 5);
445 scrollgstatwin(table
, SCROLLUP
, LINES
- 5);
448 scrollgstatwin(table
, SCROLLDOWN
, INT_MAX
);
451 scrollgstatwin(table
, SCROLLUP
, INT_MAX
);
468 /* no key ready, do nothing */
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
,
479 switch (pkt_result
) {
480 case PACKET_OK
: /* we only handle these */
484 default: /* drop others */
488 pkt_counter_update(&table
->totals
, pkt
->pkt_len
);
490 struct iflist
*ptmp
= positionptr(table
->head
, pkt
->from
->sll_ifindex
);
496 ptmp
->spanbr
+= pkt
->pkt_len
;
497 ptmp
->br
+= pkt
->pkt_len
;
499 if (pkt
->pkt_protocol
== ETH_P_IP
) {
502 if (pkt_result
== CHECKSUM_ERROR
) {
506 } else if (pkt
->pkt_protocol
== ETH_P_IPV6
) {
514 * The general interface statistics function
517 void ifstats(time_t facilitytime
)
519 int logging
= options
.logging
;
522 FILE *logfile
= NULL
;
530 initiflist(&(table
.head
));
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");
550 if (strcmp(current_logfile
, "") == 0) {
551 strcpy(current_logfile
, GSTATLOG
);
554 input_logfile(current_logfile
, &logging
);
559 opentlog(&logfile
, GSTATLOG
);
565 signal(SIGUSR1
, rotate_gstat_log
);
568 writelog(logging
, logfile
,
569 "******** General interface statistics started ********");
572 table
.firstvisible
= table
.head
;
573 print_if_entries(&table
);
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
;
595 log_next
= now
.tv_sec
+ options
.logspan
;
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
);
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
);
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 ",
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
)
629 if (screen_update_needed(&now
, &last_update
)) {
630 print_if_entries(&table
);
637 if (capt_get_packet(&capt
, &pkt
, &ch
, table
.statwin
) == -1) {
638 write_error("Packet receive failed");
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
);
655 signal(SIGUSR1
, SIG_DFL
);
656 writegstatlog(&table
, time(NULL
) - starttime
, logfile
);
657 writelog(logging
, logfile
,
658 "******** General interface statistics stopped ********");
661 strcpy(current_logfile
, "");
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
);
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
)
687 struct scroll_list scrolllist
;
697 if ((withall
) && (list
!= NULL
)) {
698 ptmp
= alloc_iflist_entry();
699 strncpy(ptmp
->ifname
, "All interfaces", sizeof(ptmp
->ifname
));
702 ptmp
->prev_entry
= NULL
;
703 list
->prev_entry
= ptmp
;
704 ptmp
->next_entry
= list
;
707 tx_listkeyhelp(STDATTR
, HIGHATTR
);
711 tx_init_listbox(&scrolllist
, 24, 14, (COLS
- 24) / 2 - 9,
712 (LINES
- 14) / 2, STDATTR
, BOXATTR
, BARSTDATTR
,
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 */
731 strcpy(ifname
, ptmp
->ifname
);
734 tx_destroy_list(&scrolllist
);