/* For terms of usage/redistribution/modification see the LICENSE file */ /* For authors and contributors see the AUTHORS file */ /*** detstats.c - the interface statistics module ***/ #include "iptraf-ng-compat.h" #include "tui/winops.h" #include "counters.h" #include "ifaces.h" #include "fltdefs.h" #include "packet.h" #include "options.h" #include "log.h" #include "dirs.h" #include "deskman.h" #include "attrs.h" #include "serv.h" #include "timer.h" #include "logvars.h" #include "promisc.h" #include "error.h" #include "detstats.h" #include "rate.h" struct ifcounts { struct proto_counter total; struct pkt_counter bcast; struct pkt_counter bad; struct proto_counter ipv4; struct proto_counter ipv6; struct proto_counter nonip; struct proto_counter tcp; struct proto_counter udp; struct proto_counter icmp; struct proto_counter other; }; /* USR1 log-rotation signal handlers */ static void rotate_dstat_log(int s __unused) { rotate_flag = 1; strcpy(target_logname, current_logfile); signal(SIGUSR1, rotate_dstat_log); } static void writedstatlog(char *ifname, unsigned long peakactivity, unsigned long peakpps, unsigned long peakactivity_in, unsigned long peakpps_in, unsigned long peakactivity_out, unsigned long peakpps_out, struct ifcounts *ts, unsigned long nsecs, FILE *fd) { char atime[TIME_TARGET_MAX]; genatime(time(NULL), atime); fprintf(fd, "\n*** Detailed statistics for interface %s, generated %s\n\n", ifname, atime); fprintf(fd, "Total: \t%llu packets, %llu bytes\n", ts->total.proto_total.pc_packets, ts->total.proto_total.pc_bytes); fprintf(fd, "\t(incoming: %llu packets, %llu bytes; outgoing: %llu packets, %llu bytes)\n", ts->total.proto_in.pc_packets, ts->total.proto_in.pc_bytes, ts->total.proto_out.pc_packets, ts->total.proto_out.pc_bytes); fprintf(fd, "IP: \t%llu packets, %llu bytes\n", ts->ipv4.proto_total.pc_packets, ts->ipv4.proto_total.pc_bytes); fprintf(fd, "\t(incoming: %llu packets, %llu bytes; outgoing: %llu packets, %llu bytes)\n", ts->ipv4.proto_in.pc_packets, ts->ipv4.proto_in.pc_bytes, ts->ipv4.proto_out.pc_packets, ts->ipv4.proto_out.pc_bytes); fprintf(fd, "TCP: %llu packets, %llu bytes\n", ts->tcp.proto_total.pc_packets, ts->tcp.proto_total.pc_bytes); fprintf(fd, "\t(incoming: %llu packets, %llu bytes; outgoing: %llu packets, %llu bytes)\n", ts->tcp.proto_in.pc_packets, ts->tcp.proto_in.pc_bytes, ts->tcp.proto_out.pc_packets, ts->tcp.proto_out.pc_bytes); fprintf(fd, "UDP: %llu packets, %llu bytes\n", ts->udp.proto_total.pc_packets, ts->udp.proto_total.pc_bytes); fprintf(fd, "\t(incoming: %llu packets, %llu bytes; outgoing: %llu packets, %llu bytes)\n", ts->udp.proto_in.pc_packets, ts->udp.proto_in.pc_bytes, ts->udp.proto_out.pc_packets, ts->udp.proto_out.pc_bytes); fprintf(fd, "ICMP: %llu packets, %llu bytes\n", ts->icmp.proto_total.pc_packets, ts->icmp.proto_total.pc_bytes); fprintf(fd, "\t(incoming: %llu packets, %llu bytes; outgoing: %llu packets, %llu bytes)\n", ts->icmp.proto_in.pc_packets, ts->icmp.proto_in.pc_bytes, ts->icmp.proto_out.pc_packets, ts->icmp.proto_out.pc_bytes); fprintf(fd, "Other IP: %llu packets, %llu bytes\n", ts->other.proto_total.pc_packets, ts->other.proto_total.pc_bytes); fprintf(fd, "\t(incoming: %llu packets, %llu bytes; outgoing: %llu packets, %llu bytes)\n", ts->other.proto_in.pc_packets, ts->other.proto_in.pc_bytes, ts->other.proto_out.pc_packets, ts->other.proto_out.pc_bytes); fprintf(fd, "Non-IP: %llu packets, %llu bytes\n", ts->nonip.proto_total.pc_packets, ts->nonip.proto_total.pc_bytes); fprintf(fd, "\t(incoming: %llu packets, %llu bytes; outgoing: %llu packets, %llu bytes)\n", ts->nonip.proto_in.pc_packets, ts->nonip.proto_in.pc_bytes, ts->nonip.proto_out.pc_packets, ts->nonip.proto_out.pc_bytes); fprintf(fd, "Broadcast: %llu packets, %llu bytes\n", ts->bcast.pc_packets, ts->bcast.pc_bytes); if (nsecs > 5) { char bps_string[64]; char pps_string[64]; fprintf(fd, "\nAverage rates:\n"); rate_print(ts->total.proto_total.pc_bytes / nsecs, bps_string, sizeof(bps_string)); rate_print_pps(ts->total.proto_total.pc_packets / nsecs, pps_string, sizeof(pps_string)); fprintf(fd, " Total:\t%s, %s\n", bps_string, pps_string); rate_print(ts->total.proto_in.pc_bytes / nsecs, bps_string, sizeof(bps_string)); rate_print_pps(ts->total.proto_in.pc_packets / nsecs, pps_string, sizeof(pps_string)); fprintf(fd, " Incoming:\t%s, %s\n", bps_string, pps_string); rate_print(ts->total.proto_out.pc_bytes / nsecs, bps_string, sizeof(bps_string)); rate_print_pps(ts->total.proto_out.pc_packets / nsecs, pps_string, sizeof(pps_string)); fprintf(fd, " Outgoing:\t%s, %s\n", bps_string, pps_string); rate_print(peakactivity, bps_string, sizeof(bps_string)); rate_print_pps(peakpps, pps_string, sizeof(pps_string)); fprintf(fd, "\nPeak total activity: %s, %s\n", bps_string, pps_string); rate_print(peakactivity_in, bps_string, sizeof(bps_string)); rate_print_pps(peakpps_in, pps_string, sizeof(pps_string)); fprintf(fd, "Peak incoming rate: %s, %s\n", bps_string, pps_string); rate_print(peakactivity_out, bps_string, sizeof(bps_string)); rate_print_pps(peakpps_out, pps_string, sizeof(pps_string)); fprintf(fd, "Peak outgoing rate: %s, %s\n\n", bps_string, pps_string); } fprintf(fd, "IP checksum errors: %llu\n\n", ts->bad.pc_packets); fprintf(fd, "Running time: %lu seconds\n", nsecs); fflush(fd); } static void printdetlabels(WINDOW * win) { wattrset(win, BOXATTR); mvwprintw(win, 2, 14, " Total Total Incoming Incoming Outgoing Outgoing"); mvwprintw(win, 3, 14, "Packets Bytes Packets Bytes Packets Bytes"); wattrset(win, STDATTR); mvwprintw(win, 4, 2, "Total:"); mvwprintw(win, 5, 2, "IPv4:"); mvwprintw(win, 6, 2, "IPv6:"); mvwprintw(win, 7, 2, "TCP:"); mvwprintw(win, 8, 2, "UDP:"); mvwprintw(win, 9, 2, "ICMP:"); mvwprintw(win, 10, 2, "Other IP:"); mvwprintw(win, 11, 2, "Non-IP:"); mvwprintw(win, 14, 2, "Total rates:"); mvwprintw(win, 17, 2, "Incoming rates:"); mvwprintw(win, 20, 2, "Outgoing rates:"); mvwprintw(win, 14, 45, "Broadcast packets:"); mvwprintw(win, 15, 45, "Broadcast bytes:"); mvwprintw(win, 19, 45, "IP checksum errors:"); update_panels(); doupdate(); } static void printstatrow(WINDOW * win, int row, unsigned long long total, unsigned long long btotal, unsigned long long total_in, unsigned long long btotal_in, unsigned long long total_out, unsigned long long btotal_out) { wmove(win, row, 12); printlargenum(total, win); wmove(win, row, 23); printlargenum(btotal, win); wmove(win, row, 35); printlargenum(total_in, win); wmove(win, row, 46); printlargenum(btotal_in, win); wmove(win, row, 58); printlargenum(total_out, win); wmove(win, row, 69); printlargenum(btotal_out, win); } static void printstatrow_proto(WINDOW *win, int row, struct proto_counter *proto_counter) { printstatrow(win, row, proto_counter->proto_total.pc_packets, proto_counter->proto_total.pc_bytes, proto_counter->proto_in.pc_packets, proto_counter->proto_in.pc_bytes, proto_counter->proto_out.pc_packets, proto_counter->proto_out.pc_bytes); } static void printdetails(struct ifcounts *ifcounts, WINDOW * win) { wattrset(win, HIGHATTR); /* Print totals on the IP protocols */ printstatrow_proto(win, 4, &ifcounts->total); printstatrow_proto(win, 5, &ifcounts->ipv4); printstatrow_proto(win, 6, &ifcounts->ipv6); printstatrow_proto(win, 7, &ifcounts->tcp); printstatrow_proto(win, 8, &ifcounts->udp); printstatrow_proto(win, 9, &ifcounts->icmp); printstatrow_proto(win, 10, &ifcounts->other); /* Print non-IP totals */ printstatrow_proto(win, 11, &ifcounts->nonip); /* Broadcast totals */ wmove(win, 14, 67); printlargenum(ifcounts->bcast.pc_packets, win); wmove(win, 15, 67); printlargenum(ifcounts->bcast.pc_bytes, win); /* Bad packet count */ mvwprintw(win, 19, 68, "%8lu", ifcounts->bad.pc_packets); } /* * The detailed interface statistics function */ void detstats(char *iface, time_t facilitytime) { int logging = options.logging; WINDOW *statwin; PANEL *statpanel; int pkt_result = 0; FILE *logfile = NULL; unsigned int iplen = 0; struct ifcounts ifcounts; int ch; struct timeval tv; struct timeval start_tv; struct timeval updtime; time_t starttime; time_t now; time_t statbegin; time_t startlog; struct proto_counter span; struct rate rate; struct rate rate_in; struct rate rate_out; unsigned long peakactivity = 0; unsigned long peakactivity_in = 0; unsigned long peakactivity_out = 0; struct rate pps_rate; struct rate pps_rate_in; struct rate pps_rate_out; unsigned long peakpps = 0; unsigned long peakpps_in = 0; unsigned long peakpps_out = 0; int fd; if (!dev_up(iface)) { err_iface_down(); return; } LIST_HEAD(promisc); if (options.promisc) { promisc_init(&promisc, iface); promisc_set_list(&promisc); } move(LINES - 1, 1); stdexitkeyhelp(); statwin = newwin(LINES - 2, COLS, 1, 0); statpanel = new_panel(statwin); tx_stdwinset(statwin); wtimeout(statwin, -1); wattrset(statwin, BOXATTR); tx_colorwin(statwin); tx_box(statwin, ACS_VLINE, ACS_HLINE); wmove(statwin, 0, 1); wprintw(statwin, " Statistics for %s ", iface); wattrset(statwin, STDATTR); update_panels(); doupdate(); memset(&ifcounts, 0, sizeof(struct ifcounts)); if (logging) { if (strcmp(current_logfile, "") == 0) { snprintf(current_logfile, 64, "%s-%s.log", DSTATLOG, iface); if (!daemonized) input_logfile(current_logfile, &logging); } } if (logging) { opentlog(&logfile, current_logfile); if (logfile == NULL) logging = 0; } if (logging) { signal(SIGUSR1, rotate_dstat_log); rotate_flag = 0; writelog(logging, logfile, "******** Detailed interface statistics started ********"); } printdetlabels(statwin); printdetails(&ifcounts, statwin); update_panels(); doupdate(); memset(&span, 0, sizeof(span)); rate_alloc(&rate, 5); rate_alloc(&rate_in, 5); rate_alloc(&rate_out, 5); rate_alloc(&pps_rate, 5); rate_alloc(&pps_rate_in, 5); rate_alloc(&pps_rate_out, 5); gettimeofday(&tv, NULL); start_tv = tv; updtime = tv; starttime = startlog = statbegin = tv.tv_sec; leaveok(statwin, TRUE); fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if(fd == -1) { write_error("Unable to obtain monitoring socket"); goto err; } if(dev_bind_ifname(fd, iface) == -1) { write_error("Unable to bind interface on the socket"); goto err_close; } exitloop = 0; PACKET_INIT(pkt); /* * Data-gathering loop */ while (!exitloop) { gettimeofday(&tv, NULL); now = tv.tv_sec; if ((now - starttime) >= 1) { char buf[64]; unsigned long activity, activity_in, activity_out; unsigned long pps, pps_in, pps_out; unsigned long msecs; wattrset(statwin, BOXATTR); printelapsedtime(statbegin, now, LINES - 3, 1, statwin); msecs = timeval_diff_msec(&tv, &start_tv); rate_add_rate(&rate, span.proto_total.pc_bytes, msecs); activity = rate_get_average(&rate); rate_add_rate(&rate_in, span.proto_in.pc_bytes, msecs); activity_in = rate_get_average(&rate_in); rate_add_rate(&rate_out, span.proto_out.pc_bytes, msecs); activity_out = rate_get_average(&rate_out); rate_add_rate(&pps_rate, span.proto_total.pc_packets, msecs); pps = rate_get_average(&pps_rate); rate_add_rate(&pps_rate_in, span.proto_in.pc_packets, msecs); pps_in = rate_get_average(&pps_rate_in); rate_add_rate(&pps_rate_out, span.proto_out.pc_packets, msecs); pps_out = rate_get_average(&pps_rate_out); memset(&span, 0, sizeof(span)); starttime = now; start_tv = tv; wattrset(statwin, HIGHATTR); rate_print(activity, buf, sizeof(buf)); mvwprintw(statwin, 14, 19, "%s", buf); rate_print_pps(pps, buf, sizeof(buf)); mvwprintw(statwin, 15, 19, "%s", buf); rate_print(activity_in, buf, sizeof(buf)); mvwprintw(statwin, 17, 19, "%s", buf); rate_print_pps(pps_in, buf, sizeof(buf)); mvwprintw(statwin, 18, 19, "%s", buf); rate_print(activity_out, buf, sizeof(buf)); mvwprintw(statwin, 20, 19, "%s", buf); rate_print_pps(pps_out, buf, sizeof(buf)); mvwprintw(statwin, 21, 19, "%s", buf); if (activity > peakactivity) peakactivity = activity; if (activity_in > peakactivity_in) peakactivity_in = activity_in; if (activity_out > peakactivity_out) peakactivity_out = activity_out; if (pps > peakpps) peakpps = pps; if (pps_in > peakpps_in) peakpps_in = pps_in; if (pps_out > peakpps_out) peakpps_out = pps_out; } if (logging) { check_rotate_flag(&logfile); if ((now - startlog) >= options.logspan) { writedstatlog(iface, peakactivity, peakpps, peakactivity_in, peakpps_in, peakactivity_out, peakpps_out, &ifcounts, time(NULL) - statbegin, logfile); startlog = now; } } if (screen_update_needed(&tv, &updtime)) { printdetails(&ifcounts, statwin); update_panels(); doupdate(); updtime = tv; } if ((facilitytime != 0) && (((now - statbegin) / 60) >= facilitytime)) exitloop = 1; if (packet_get(fd, &pkt, &ch, statwin) == -1) { write_error("Packet receive failed"); exitloop = 1; break; } switch (ch) { case ERR: /* no key ready, do nothing */ break; case 12: case 'l': case 'L': tx_refresh_screen(); break; case 'Q': case 'q': case 'X': case 'x': case 24: case 27: exitloop = 1; break; } if (pkt.pkt_len <= 0) continue; int outgoing; pkt_result = packet_process(&pkt, NULL, NULL, NULL, MATCH_OPPOSITE_USECONFIG, options.v6inv4asv6); if (pkt_result != PACKET_OK && pkt_result != MORE_FRAGMENTS) continue; outgoing = (pkt.pkt_pkttype == PACKET_OUTGOING); update_proto_counter(&ifcounts.total, outgoing, pkt.pkt_len); if (pkt.pkt_pkttype == PACKET_BROADCAST) { update_pkt_counter(&ifcounts.bcast, pkt.pkt_len); } update_proto_counter(&span, outgoing, pkt.pkt_len); /* account network layer protocol */ switch(pkt.pkt_protocol) { case ETH_P_IP: if (pkt_result == CHECKSUM_ERROR) { update_pkt_counter(&ifcounts.bad, pkt.pkt_len); continue; } iplen = ntohs(pkt.iphdr->tot_len); update_proto_counter(&ifcounts.ipv4, outgoing, iplen); break; case ETH_P_IPV6: iplen = ntohs(pkt.ip6_hdr->ip6_plen) + 40; update_proto_counter(&ifcounts.ipv6, outgoing, iplen); break; default: update_proto_counter(&ifcounts.nonip, outgoing, iplen); continue; } __u8 ip_protocol = pkt_ip_protocol(&pkt); /* account transport layer protocol */ switch (ip_protocol) { case IPPROTO_TCP: update_proto_counter(&ifcounts.tcp, outgoing, iplen); break; case IPPROTO_UDP: update_proto_counter(&ifcounts.udp, outgoing, iplen); break; case IPPROTO_ICMP: case IPPROTO_ICMPV6: update_proto_counter(&ifcounts.icmp, outgoing, iplen); break; default: update_proto_counter(&ifcounts.other, outgoing, iplen); break; } } err_close: close(fd); err: rate_destroy(&pps_rate_out); rate_destroy(&pps_rate_in); rate_destroy(&pps_rate); rate_destroy(&rate_out); rate_destroy(&rate_in); rate_destroy(&rate); if (options.promisc) { promisc_restore_list(&promisc); promisc_destroy(&promisc); } if (logging) { signal(SIGUSR1, SIG_DFL); writedstatlog(iface, peakactivity, peakpps, peakactivity_in, peakpps_in, peakactivity_out, peakpps_out, &ifcounts, time(NULL) - statbegin, logfile); writelog(logging, logfile, "******** Detailed interface statistics stopped ********"); fclose(logfile); } del_panel(statpanel); delwin(statwin); strcpy(current_logfile, ""); pkt_cleanup(); update_panels(); doupdate(); }