/* * Copyright (c) 1993-1997 JSC Rinet, Novosibirsk, Russia * * Redistribution and use in source forms, with and without modification, * are permitted provided that this entire comment appears intact. * Redistribution in binary form may occur without any restrictions. * * THIS SOFTWARE IS PROVIDED ``AS IS'' WITHOUT ANY WARRANTIES OF ANY KIND. */ /* display.c -- collect and display network traffic */ char copyright[] = "Copyright (c) 1993-1997 JSC Rinet, Novosibirsk, Russia"; #ifdef HAVE_CONFIG_H #include #endif #ifdef HAVE_SLCURSES #include #endif #ifdef HAVE_NCURSES #include #endif #ifdef HAVE_CURSES #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #ifdef TIME_WITH_SYS_TIME #include #endif #include "trafshow.h" typedef struct t_entry *p_entry; struct t_entry *entries; int n_entry = 0; u_long bytes_total = 0; u_long packets_total = 0; int page = 0; int page_size; static int l_nflag, l_eflag; static int n_entries; static int err_pos; extern struct t_entry t_mask; /* traffic mask */ void init_display(reinit) int reinit; { if (!reinit) { n_entries = LINES * MAX_PAGES; entries = (struct t_entry *)calloc(n_entries, sizeof(struct t_entry)); if (!entries) error(1, "init_display: calloc"); l_nflag = nflag; l_eflag = eflag; alarm(scr_interval); } else { int i = LINES * MAX_PAGES; if (i > n_entries) { n_entries = i; entries = (struct t_entry *)realloc(entries, i * sizeof(struct t_entry)); if (!entries) error(1, "init_display: realloc"); } page = 0; } err_pos = strlen(device_name) + count_size + 15; } void header_line() { int i; mvprintw(0, 0, "%-*.*s %-*.*s %-*.*s %*.*s %-*.*s", addr_size, addr_size, "From Address", addr_size, addr_size, "To Address", proto_size, proto_size, "Proto", bytes_size, bytes_size, "Bytes", cps_size, cps_size, "CPS"); for (i = 0; i < COLS; i++) mvaddch(1, i, '='); } void status_line(reinit) int reinit; { static time_t tp = 0; static u_long bytes_prev = 0, packets_prev = 0; if (reinit) { time_t tn = time(NULL); register sec = tn - tp; if (sec < 1) sec = 1; mvprintw(LINES-1, 0, "(%.5s) %*ld kb/total %*d pkts/sec %*d bytes/sec", device_name, count_size, bytes_total/1024, count_size, (packets_total-packets_prev)/sec, count_size, (bytes_total-bytes_prev)/sec); packets_prev = packets_total; bytes_prev = bytes_total; tp = tn; } else mvprintw(LINES-1, 0, "(%.5s) %*ld kb/total", device_name, count_size, bytes_total/1024); mvprintw(LINES-1, COLS-13, " Page %2d/%-2d", page+1, n_entry/page_size+1); clrtoeol(); } /* * Pretty print an Internet address (net address + port). */ static char * inet_print(in, port, proto) struct in_addr in; u_short port, proto; { register char *cp; char pline[20]; int alen, plen; static char aline[1024]; static char *icmp_type[ICMP_MAXTYPE+1] = { "echoreply", "unknown", "unknown", "dstunreach", "srcquench", "redirect", "unknown", "unknown", "echoreqst", "rtradvert", "rtrsolicit", "timeexceed", "paramprobl", "stampreqst", "stampreply", "inforeqst", "inforeply", "maskreqst", "maskreply" }; if (l_nflag) cp = intoa(in.s_addr); else cp = ipaddr_string(&in); (void) sprintf(aline, "%-*.*s", addr_size, addr_size, cp); if (port) { if (proto == IPPROTO_TCP) cp = tcpport_string(port); else if (proto == IPPROTO_UDP) cp = udpport_string(port); else if (proto == IPPROTO_ICMP && port <= ICMP_MAXTYPE) cp = icmp_type[port]; else cp = "unknown"; plen = sprintf(pline, "..%.10s", cp); if ((cp = strchr(aline, ' ')) != NULL) alen = cp - aline; else alen = addr_size; if ((alen + plen) >= addr_size) memcpy(&aline[addr_size - plen], pline, plen); else memcpy(&aline[alen], pline, plen); } return aline; } static void main_print(line, i) int line; register i; { char *proto; int normal = TRUE; move(SCR_OFFS+line, 0); #ifdef HAVE_HAS_COLORS if (use_colors) normal = !color_on(&entries[i]); #endif if (l_eflag) { printw("%-*.*s", addr_size, addr_size, l_nflag ? entoa(entries[i].eh.ether_shost) : etheraddr_string(entries[i].eh.ether_shost)); addch(' '); printw("%-*.*s", addr_size, addr_size, l_nflag ? entoa(entries[i].eh.ether_dhost) : etheraddr_string(entries[i].eh.ether_dhost)); proto = etherproto_string(entries[i].eh.ether_type); } else { addstr(inet_print(entries[i].src, entries[i].sport, entries[i].proto)); addch(' '); addstr(inet_print(entries[i].dst, entries[i].dport, entries[i].proto)); proto = getprotoname(entries[i].proto); if (proto == NULL) proto = "unknown"; } printw(" %-*.*s", proto_size, proto_size, proto); #ifdef HAVE_HAS_COLORS if (!normal) attrset(A_NORMAL); #endif printw(" %*ld", bytes_size, entries[i].bytes); if (entries[i].cps) printw(" %-*d", cps_size, entries[i].cps); clrtoeol(); } static int sortbybytes(e1, e2) register p_entry e1, e2; { if (e1->bytes > e2->bytes) return -1; if (e1->bytes < e2->bytes) return 1; return 0; } /* * If scr_interval time any entry wasn't changed then purge it * and redraw whole screen. */ void scr_redraw(del_old) int del_old; { register i, j; if (del_old) { alarm(0); for (i = j = 0; i < n_entry; i++) { if (entries[i].bytes != entries[i].obytes) entries[i].cps = (entries[i].bytes - entries[i].obytes)/scr_interval; else entries[i].bytes = 0, j++; entries[i].obytes = entries[i].bytes; } qsort(entries, n_entry, sizeof(struct t_entry), sortbybytes); n_entry -= j; } j = n_entry/page_size; if (page > j) page = j; move(SCR_OFFS, 0); if (n_entry) { for (i = 0, j = page * page_size; i < page_size && j < n_entry; i++, j++) main_print(i, j); } else { clrtoeol(); switch (alarm_flag) { case 1: addstr("Where is the tcp or udp packets contained data? It is exactly what I want! Now!"); break; case 2: addstr("Nothing to show, this interface sleeping or broken. blah-blah-gluk-gluk-wait..."); break; default: i = COLS/2 - strlen(copyright)/2; if (i < 0) i = 0; mvaddstr(SCR_OFFS, i, copyright); alarm_flag = 1; } } clrtobot(); status_line(del_old); if (del_old) alarm(scr_interval); } void scr_update() { #ifdef HAVE_HAS_COLORS move(LINES-1, COLS-1); #else move(0, COLS-1); #endif refresh(); } /* * Add new entry or add bytes to existed record. */ static void collect(e) register p_entry e; { register i, j; packets_total++; bytes_total += e->bytes; j = page * page_size; e->src.s_addr &= t_mask.src.s_addr; e->dst.s_addr &= t_mask.dst.s_addr; e->sport &= t_mask.sport; e->dport &= t_mask.dport; e->proto &= t_mask.proto; for (i = 0; i < n_entry; i++) { if (memcmp(&e->eh, &entries[i].eh, sizeof(e->eh)) == 0 && e->src.s_addr == entries[i].src.s_addr && e->sport == entries[i].sport && e->dst.s_addr == entries[i].dst.s_addr && e->dport == entries[i].dport && e->proto == entries[i].proto) { entries[i].bytes += e->bytes; if (i >= j && i < j + page_size) mvprintw(i-j+SCR_OFFS, addr_size*2+proto_size+3, "%*ld", bytes_size, entries[i].bytes); return; } } if (i >= LINES * MAX_PAGES) { mvaddstr(LINES-1, COLS-13, " Overflow"); clrtoeol(); return; } /* add new entry */ entries[i] = *e; entries[i].obytes = entries[i].cps = 0; if (i >= j && i < j + page_size) main_print(i-j, i); n_entry++; status_line(0); } void display(eh, ip, length) struct ether_header *eh; register struct ip *ip; int length; { register iplen; int hlen, not_ip = 0; register u_char *cp; struct t_entry te; if ((u_char *)(ip + 1) > snapend) { if (!eflag) return; /* not ip proto? discard silently */ not_ip++; } else if (length < sizeof(*ip)) { if (!eflag) { mvprintw(LINES-1, err_pos, "\ truncated-ip: discard %d bytes", length); clrtoeol(); goto refresh_screen; } not_ip++; } else if (ip->ip_v != IPVERSION) { if (!eflag) { mvprintw(LINES-1, err_pos, "\ ip ver != %d: discard %d bytes", IPVERSION, length); clrtoeol(); goto refresh_screen; } not_ip++; } else if ((iplen = ntohs(ip->ip_len)) < 1) { if (!eflag) return; /* empty ip packet? discard silently */ not_ip++; } else if (length < iplen) { if (!eflag) { mvprintw(LINES-1, err_pos, "\ truncated-ip: %d bytes missing", iplen - length); clrtoeol(); goto refresh_screen; } not_ip++; } if (eh) te.eh = *eh; else memset(&te.eh, 0, sizeof(te.eh)); te.sport = te.dport = 0; if (!not_ip) { /* parse ip packet */ /* * If this is fragment zero, hand it to the next higher level * protocol to fetch ports or icmp type else it is fragmented * ip datagram. */ if ((ntohs(ip->ip_off) & 0x1fff) == 0) { /* advance to high level protocol header */ hlen = ip->ip_hl * 4; cp = (u_char *)ip + hlen; if (ip->ip_p == IPPROTO_TCP) { if (cp + sizeof(struct tcphdr) > snapend || iplen - hlen < sizeof(struct tcphdr)) { mvprintw(LINES-1, err_pos, "\ truncated-tcp: wrong ip hdrlen"); clrtoeol(); goto refresh_screen; } te.sport = ntohs(((struct tcphdr *)cp)->th_sport); te.dport = ntohs(((struct tcphdr *)cp)->th_dport); } else if (ip->ip_p == IPPROTO_UDP) { if (cp + sizeof(struct udphdr) > snapend || iplen - hlen < sizeof(struct udphdr)) { mvprintw(LINES-1, err_pos, "\ truncated-udp: wrong ip hdrlen"); clrtoeol(); goto refresh_screen; } te.sport = ntohs(((struct udphdr *)cp)->uh_sport); te.dport = ntohs(((struct udphdr *)cp)->uh_dport); } else if (ip->ip_p == IPPROTO_ICMP) { if (cp + sizeof(struct icmp) > snapend || iplen - hlen < sizeof(struct icmp)) { mvprintw(LINES-1, err_pos, "\ truncated-icmp: wrong ip hdrlen"); clrtoeol(); goto refresh_screen; } te.sport = ((struct icmp *)cp)->icmp_type; } } te.src.s_addr = ip->ip_src.s_addr; te.dst.s_addr = ip->ip_dst.s_addr; te.proto = ip->ip_p; te.bytes = iplen; } else { /* other than ip protocol packets */ te.src.s_addr = te.dst.s_addr = 0; te.proto = 0; te.bytes = length; } collect(&te); if (alarm_flag) { alarm_flag = 0; if (resize_flag) { resize_flag = 0; init_term(TRUE); } scr_redraw(TRUE); } refresh_screen: if (kflag && kbhit(0) && get_keyb()) scr_redraw(FALSE); scr_update(); return; } int inputchar() { return getch(); } int get_keyb() { int ch; if ((ch = getch()) == ERR) error(1, "get_keyb: getch"); switch(ch) { case ESC: switch(get_arrowkey(inputchar)) { case KEYMAP_UP: case KEYMAP_PAGE_UP: goto page_up; case KEYMAP_DOWN: case KEYMAP_PAGE_DOWN: goto page_down; case KEYMAP_LEFT: case KEYMAP_HOME: goto home_list; case KEYMAP_RIGHT: case KEYMAP_END: goto end_list; default: mvaddstr(LINES-1, COLS-13, " Bad command"); return 0; } break; case 'k': /* line up */ case ctrl('P'): case '\b': /* page up */ case 'b': case ctrl('U'): case ctrl('B'): page_up: if (page > 0) page--; break; case 'j': case ctrl('N'): /* line down */ case ' ': /* page down */ case ctrl('D'): case ctrl('F'): page_down: if (page < n_entry/page_size) page++; break; case ctrl('A'): /* home */ home_list: page = 0; break; case ctrl('E'): /* end */ end_list: page = n_entry/page_size; break; case '\r': /* enter */ case '\n': l_eflag ^= 1; break; case '\t': /* tab */ l_nflag ^= 1; break; case 'q': /* quit */ cleanup(SIGINT); case ctrl('L'): /* refresh screen */ clear(); header_line(); scr_redraw(FALSE); scr_update(); break; default: mvaddstr(LINES-1, COLS-13, " Bad command"); return 0; } return 1; }