/*
* 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 <config.h>
#endif
#ifdef HAVE_SLCURSES
#include <slcurses.h>
#endif
#ifdef HAVE_NCURSES
#include <ncurses.h>
#endif
#ifdef HAVE_CURSES
#include <curses.h>
#endif
#include <sys/types.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/udp.h>
#include <netinet/tcp.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#ifdef TIME_WITH_SYS_TIME
#include <time.h>
#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;
}
syntax highlighted by Code2HTML, v. 0.9.1