/* * Copyright (c) 1998,2004 Rinet Corp., Novosibirsk, Russia * * Redistribution and use in source forms, with and without modification, * are permitted provided that this entire comment appears intact. * * THIS SOURCE CODE IS PROVIDED ``AS IS'' WITHOUT ANY WARRANTIES OF ANY KIND. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include "show_stat.h" #include "trafshow.h" #include "screen.h" #include "selector.h" #include "netstat.h" #include "getkey.h" #include "addrtoname.h" ShowStatMode show_stat_mode = Size; static int find_backflow(NETSTAT **list, int items, NETSTAT *at); static void sort_backflow(NETSTAT **list, int items); static void scale_size(addr, prot, data, rate) int *addr, *prot, *data, *rate; { *addr = line_factor * (double)SHOW_STAT_ADDR; *prot = line_factor * (double)SHOW_STAT_PROT; *data = line_factor * (double)SHOW_STAT_DATA; *rate = line_factor * (double)SHOW_STAT_RATE; } static int compare_pkt_len(p1, p2) register const NETSTAT **p1, **p2; { if ((*p1)->pkt_len > (*p2)->pkt_len) return -1; if ((*p1)->pkt_len < (*p2)->pkt_len) return 1; return 0; } static int compare_data_len(p1, p2) register const NETSTAT **p1, **p2; { if ((*p1)->data_len > (*p2)->data_len) return -1; if ((*p1)->data_len < (*p2)->data_len) return 1; return 0; } static int compare_pkt_cnt(p1, p2) register const NETSTAT **p1, **p2; { if ((*p1)->pkt_cnt > (*p2)->pkt_cnt) return -1; if ((*p1)->pkt_cnt < (*p2)->pkt_cnt) return 1; return 0; } static int find_backflow(list, items, at) NETSTAT **list; int items; NETSTAT *at; { int i; /* sanity check */ if (!list || items < 1 || !at) return -1; for (i = 0; i < items; i++) { if (netstat_bidir(at, list[i])) return i; } return -1; } /* too bad implementation -- it take alot of CPU cycles like deadloop. XXX */ static void sort_backflow(list, items) NETSTAT **list; int items; { int i = 0, at; NETSTAT *ns; while (i < items-1) { ns = list[i++]; if ((at = find_backflow(&list[i], items - i, ns)) < 0) continue; if (at) { ns = list[i + at]; memmove(&list[i + 1], &list[i], at * sizeof(NETSTAT *)); list[i] = ns; } i++; } } /* * Pretty print an Internet address (net address + port). */ static char * ip_print(ver, proto, addr, dst, size) int ver; int proto; const struct ip_address *addr; char *dst; int size; { const char *cp = 0; char buf[100]; if (ver == 4 && addr->ip_addr.s_addr) { /*cp = intoa(addr->ip_addr.s_addr);*/ cp = ipaddr_string(&addr->ip_addr); } #ifdef INET6 else if (ver == 6 && !IN6_IS_ADDR_UNSPECIFIED(&addr->ip6_addr)) { /*cp = inet_ntop(AF_INET6, &addr->ip6_addr, buf, sizeof(buf));*/ cp = ip6addr_string(&addr->ip6_addr); } #endif if (cp) { (void)strncpy(dst, cp, size); dst[size-1] = '\0'; } else snprintf(dst, size, "IPv%d", ver); if (addr->ip_port) { char pb[20]; int len; switch (proto) { case IPPROTO_TCP: if (nflag) { sprintf(pb, "%d", ntohs(addr->ip_port)); cp = pb; } else cp = tcpport_string(ntohs(addr->ip_port)); break; case IPPROTO_UDP: if (nflag) { sprintf(pb, "%d", ntohs(addr->ip_port)); cp = pb; } else cp = udpport_string(ntohs(addr->ip_port)); break; case IPPROTO_ICMP: if (nflag) { sprintf(pb, "%04x", addr->ip_port - 1); cp = pb; } else cp = icmp_string(addr->ip_port - 1); break; #ifdef INET6 case IPPROTO_ICMPV6: if (nflag) { sprintf(pb, "%04x", addr->ip_port - 1); cp = pb; } else cp = icmpv6_string(addr->ip_port - 1); break; #endif default: /* nonsense, but be strong */ sprintf(pb, "%d", ntohs(addr->ip_port)); cp = pb; } buf[0] = ','; (void)strncpy(&buf[1], cp, 10); buf[11] = '\0'; len = strlen(buf); if (strlen(dst) + len < size) (void)strcat(dst, buf); else (void)strcpy(&dst[size - len - 1], buf); } return dst; } static char * sap_print(addr, sap, dst, size) const u_char *addr; u_char sap; char *dst; int size; { char buf[20]; int len; (void)strncpy(dst, etheraddr_string(addr), size); dst[size-1] = '\0'; buf[0] = '/'; if (nflag) sprintf(&buf[1], "sap-%02x", sap & 0xff); else (void)strncpy(&buf[1], llcsap_string(sap), 10); buf[11] = '\0'; len = strlen(buf); if (strlen(dst) + len < size) (void)strcat(dst, buf); else (void)strcpy(&dst[size - len - 1], buf); return dst; } void hdr2str(nh, src_buf, src_len, dst_buf, dst_len, proto_buf, proto_len) const struct netstat_header *nh; char *src_buf, *dst_buf, *proto_buf; int src_len, dst_len, proto_len; { const NETSTAT *ns; if (src_buf) *src_buf = '\0'; if (dst_buf) *dst_buf = '\0'; if (proto_buf) *proto_buf = '\0'; /* sanity check */ if (!nh) return; ns = (NETSTAT *)nh; if (ns->ip_ver) { if (src_buf && src_len > 1) { ip_print(ns->ip_ver, ns->ip_proto, &ns->ip_src_addr, src_buf, src_len); } if (dst_buf && dst_len > 1) { ip_print(ns->ip_ver, ns->ip_proto, &ns->ip_dst_addr, dst_buf, dst_len); } if (proto_buf && proto_len > 1) { if (nflag) snprintf(proto_buf, proto_len, "%d", (int)ns->ip_proto); else (void)strncpy(proto_buf, ipproto_string(ns->ip_proto), proto_len); proto_buf[proto_len-1] = '\0'; } } else if (ntohs(ns->eth_type) > 1500) { /* Ethernet II (DIX) */ if (src_buf && src_len > 1) { (void)strncpy(src_buf, etheraddr_string(ns->eth_src_addr), src_len); src_buf[src_len-1] = '\0'; } if (dst_buf && dst_len > 1) { (void)strncpy(dst_buf, etheraddr_string(ns->eth_dst_addr), dst_len); dst_buf[dst_len-1] = '\0'; } if (proto_buf && proto_len > 1) { if (nflag) snprintf(proto_buf, proto_len, "%04x", ntohs(ns->eth_type)); else (void)strncpy(proto_buf, ethertype_string(ns->eth_type), proto_len); proto_buf[proto_len-1] = '\0'; } } else if (ns->eth_ssap == ns->eth_dsap) { if (src_buf && src_len > 1) { (void)strncpy(src_buf, etheraddr_string(ns->eth_src_addr), src_len); src_buf[src_len-1] = '\0'; } if (dst_buf && dst_len > 1) { (void)strncpy(dst_buf, etheraddr_string(ns->eth_dst_addr), dst_len); dst_buf[dst_len-1] = '\0'; } if (proto_buf && proto_len > 1) { if (nflag) snprintf(proto_buf, proto_len, "sap-%02x", (int)(ns->eth_ssap & 0xff)); else (void)strncpy(proto_buf, llcsap_string(ns->eth_ssap), proto_len); proto_buf[proto_len-1] = '\0'; } } else { if (src_buf && src_len > 1) { sap_print(ns->eth_src_addr, ns->eth_ssap, src_buf, src_len); } if (dst_buf && dst_len > 1) { sap_print(ns->eth_dst_addr, ns->eth_dsap, dst_buf, dst_len); } if (proto_buf && proto_len > 1) { (void)strncpy(proto_buf, "802.3", proto_len); proto_buf[proto_len-1] = '\0'; } } } static int show_stat_header(dst, size, ph) char *dst; int size; const PCAP_HANDLER *ph; { int addr_sz, prot_sz, data_sz, rate_sz; char src_buf[100], dst_buf[100]; const char *data_ptr, *rate_ptr; /* sanity check */ if (!dst || size < 1 || !ph) return 0; scale_size(&addr_sz, &prot_sz, &data_sz, &rate_sz); (void)strcpy(src_buf, "Source"); (void)strcpy(dst_buf, "Destination"); if (ph->masklen >= 0) { sprintf(src_buf + strlen(src_buf), "/%d", ph->masklen); sprintf(dst_buf + strlen(dst_buf), "/%d", ph->masklen); } data_ptr = rate_ptr = "?"; switch (show_stat_mode) { case Size: data_ptr = "Size"; rate_ptr = "CPS"; break; case Data: data_ptr = "Data"; rate_ptr = "CPS"; break; case Packets: data_ptr = "Packets"; rate_ptr = "PPS"; break; } snprintf(dst, size, "%-*.*s %-*.*s %-*.*s %-*.*s %-*.*s", addr_sz, addr_sz, src_buf, addr_sz, addr_sz, dst_buf, prot_sz, prot_sz, "Protocol", data_sz, data_sz, data_ptr, rate_sz, rate_sz, rate_ptr); return 0; } static int show_stat_line(dst, size, ns_list, idx) char *dst; int size; const NETSTAT **ns_list; int idx; { int addr_sz, prot_sz, data_sz, rate_sz; const NETSTAT *ns; char src_buf[100], dst_buf[100], proto_buf[20], data_buf[20], rate_buf[20]; /* sanity check */ if (!dst || size < 1 || !ns_list) return 0; ns = ns_list[idx]; scale_size(&addr_sz, &prot_sz, &data_sz, &rate_sz); hdr2str(&ns->ns_hdr, src_buf, MIN(sizeof(src_buf), addr_sz + 1), dst_buf, MIN(sizeof(dst_buf), addr_sz + 1), proto_buf, MIN(sizeof(proto_buf), prot_sz + 1)); data_buf[0] = '\0'; rate_buf[0] = '\0'; switch (show_stat_mode) { case Size: if (ns->pkt_len >= 10000) snprintf(data_buf, sizeof(data_buf), "%uK", ns->pkt_len / 1024); else snprintf(data_buf, sizeof(data_buf), "%u", ns->pkt_len); if (ns->pkt_len_rate >= 10000) snprintf(rate_buf, sizeof(rate_buf), "%uK", ns->pkt_len_rate / 1024); else if (ns->pkt_len_rate > 0) snprintf(rate_buf, sizeof(rate_buf), "%u", ns->pkt_len_rate); break; case Data: if (ns->data_len >= 10000) snprintf(data_buf, sizeof(data_buf), "%uK", ns->data_len / 1024); else snprintf(data_buf, sizeof(data_buf), "%u", ns->data_len); if (ns->data_len_rate >= 10000) snprintf(rate_buf, sizeof(rate_buf), "%uK", ns->data_len_rate / 1024); else if (ns->data_len_rate > 0) snprintf(rate_buf, sizeof(rate_buf), "%u", ns->data_len_rate); break; case Packets: snprintf(data_buf, sizeof(data_buf), "%u", ns->pkt_cnt); if (ns->pkt_cnt_rate > 0) snprintf(rate_buf, sizeof(rate_buf), "%u", ns->pkt_cnt_rate); break; } snprintf(dst, size, "%-*.*s %-*.*s %-*.*s %-*.*s %-*.*s", addr_sz, addr_sz, src_buf, addr_sz, addr_sz, dst_buf, prot_sz, prot_sz, proto_buf, data_sz, data_sz, data_buf, rate_sz, rate_sz, rate_buf); return ns->attr; } static int show_stat_footer(dst, size, ph) char *dst; int size; const PCAP_HANDLER *ph; { const PCAP_HANDLER *top; int addr_sz, prot_sz, data_sz, rate_sz, depth; u_int64_t total = 0, rate = 0; char stat_buf[50], data_buf[20], rate_buf[20]; /* sanity check */ if (!dst || size < 1 || !ph) return 0; scale_size(&addr_sz, &prot_sz, &data_sz, &rate_sz); depth = 0; for (top = ph->top; top; top = top->top) depth++; if (depth) { snprintf(stat_buf, sizeof(stat_buf), "%d Flows (depth %d)", netstat_count(ph), depth); } else { snprintf(stat_buf, sizeof(stat_buf), "%d Flows", netstat_count(ph)); } switch (show_stat_mode) { case Size: total = ph->pkt_len; rate = ph->pkt_len_rate; break; case Data: total = ph->data_len; rate = ph->data_len_rate; break; case Packets: total = ph->pkt_cnt; rate = ph->pkt_cnt_rate; break; } if (total >= 10000000) snprintf(data_buf, sizeof(data_buf), "%uM", (unsigned int)(total / (1024 * 1024))); else if (total >= 10000) snprintf(data_buf, sizeof(data_buf), "%uK", (unsigned int)(total / 1024)); else snprintf(data_buf, sizeof(data_buf), "%u", (unsigned int)total); if (rate >= 10000000) snprintf(rate_buf, sizeof(rate_buf), "%uM", (unsigned int)(rate / (1024 * 1024))); else if (rate >= 10000) snprintf(rate_buf, sizeof(rate_buf), "%uK", (unsigned int)(rate / 1024)); else snprintf(rate_buf, sizeof(rate_buf), "%u", (unsigned int)rate); snprintf(dst, size, "%-*.*s %-*.*s %-*.*s %-*.*s %-*.*s", addr_sz, addr_sz, ph->name, addr_sz, addr_sz, stat_buf, prot_sz, prot_sz, "Total:", data_sz, data_sz, data_buf, rate_sz, rate_sz, rate_buf); return 0; } int show_stat_input(ph, ch) PCAP_HANDLER *ph; int ch; { switch (ch) { case '[': /* rotate list mode left */ case K_LEFT: if (show_stat_mode == Size) show_stat_mode = Packets; else show_stat_mode--; return 1; case ']': /* rotate list mode right */ case K_RIGHT: if (show_stat_mode == Packets) show_stat_mode = Size; else show_stat_mode++; return 1; case K_CTRL('R'): /* reset current netstat hash */ if (ph) { netstat_purge(ph, 0); show_stat_list(ph); return 1; } break; case K_TAB: /* follow to backflow */ if (ph) { SELECTOR *sp = show_stat_selector(ph); int idx = selector_get(sp); if (idx >= 0) { NETSTAT **ns_list = (NETSTAT **)sp->list; const NETSTAT *ns = ns_list[idx]; for (idx = 0; idx < sp->items; idx++) { if (netstat_bidir(ns, ns_list[idx])) { selector_set(idx, sp); return 1; } } } } break; } return 0; } SELECTOR * show_stat_selector(ph) PCAP_HANDLER *ph; { if (!ph) return 0; if (!ph->selector && (ph->selector = selector_init()) != 0) { ph->selector->get_header = show_stat_header; ph->selector->get_line = show_stat_line; ph->selector->get_footer = show_stat_footer; } return ph->selector; } NETSTAT * show_stat_get(ph, idx) PCAP_HANDLER *ph; int idx; { SELECTOR *sp; NETSTAT **ns_list; /* sanity check */ if (!ph || idx < 0 || (sp = show_stat_selector(ph)) == 0 || idx >= sp->items) return 0; ns_list = (NETSTAT **)sp->list; return ns_list[idx]; } int show_stat_search(ph, str) PCAP_HANDLER *ph; const char *str; { SELECTOR *sp; NETSTAT **ns_list; const NETSTAT *ns; int idx; char src_buf[100], dst_buf[100], proto_buf[20]; /* sanity check */ if (!ph || !str || *str == '\0' || (sp = show_stat_selector(ph)) == 0) return -1; ns_list = (NETSTAT **)sp->list; for (idx = 0; idx < sp->items; idx++) { ns = ns_list[idx]; hdr2str(&ns->ns_hdr, src_buf, sizeof(src_buf), dst_buf, sizeof(dst_buf), proto_buf, sizeof(proto_buf)); if (strstr(src_buf, str) || strstr(dst_buf, str) || strstr(proto_buf, str)) return idx; } return -1; } SELECTOR * show_stat_list(ph) PCAP_HANDLER *ph; { SELECTOR *sp; int cnt; /* sanity check */ if (!ph || (sp = show_stat_selector(ph)) == 0) return 0; sp->header = ph; sp->footer = ph; if ((cnt = netstat_fetch((NETSTAT ***)&sp->list, ph)) < 0) return sp; sp->items = cnt; if (cnt < 2) /* no sorting required */ return sp; /* sort it accroding to current mode */ switch (show_stat_mode) { case Size: qsort(sp->list, sp->items, sizeof(NETSTAT *), compare_pkt_len); break; case Data: qsort(sp->list, sp->items, sizeof(NETSTAT *), compare_data_len); break; case Packets: qsort(sp->list, sp->items, sizeof(NETSTAT *), compare_pkt_cnt); break; } if (popbackflow) sort_backflow(sp->list, sp->items); return sp; }