diff --git a/CHANGES b/CHANGES index a9d9282..d592cb2 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,10 @@ Change log for iftop $Id$ +0.8 + +Added support for displaying port numbers + 0.7 02/10/02 Fixed missing sll.h file. diff --git a/TODO b/TODO index 4640f74..3e9ced0 100644 --- a/TODO +++ b/TODO @@ -1,12 +1,12 @@ Things to do for iftop $Id$ -* Interface types other than ethernet? Should at least detect these.... +* Include ports in display + - implement service name hash + - add key press for src/dest port aggregation * IP types other than v4? -* Include ports in display. - * Which average to use for the bar graph? Show several and peaks? Colours? * Single keypress firewalling of troublesome connections, a la top(1)'s K diff --git a/addr_hash.c b/addr_hash.c index 996ea44..c92e0f1 100644 --- a/addr_hash.c +++ b/addr_hash.c @@ -12,7 +12,9 @@ int compare(void* a, void* b) { addr_pair* aa = (addr_pair*)a; addr_pair* bb = (addr_pair*)b; return (aa->src.s_addr == bb->src.s_addr - && aa->dst.s_addr == bb->dst.s_addr); + && aa->src_port == bb->src_port + && aa->dst.s_addr == bb->dst.s_addr + && aa->dst_port == bb->dst_port); } int hash(void* key) { @@ -25,13 +27,15 @@ int hash(void* key) { hash = ((addr & 0x000000FF) + (addr & 0x0000FF00 >> 8) + (addr & 0x00FF0000 >> 16) - + (addr & 0xFF000000 >> 24)) % 0xFF; + + (addr & 0xFF000000 >> 24) + + ap->src_port) % 0xFF; addr = (long)ap->dst.s_addr; hash = ( hash + (addr & 0x000000FF) + (addr & 0x0000FF00 >> 8) + (addr & 0x00FF0000 >> 16) - + (addr & 0xFF000000 >> 24)) % 0xFF; + + (addr & 0xFF000000 >> 24) + + ap->dst_port) % 0xFF; return hash; } diff --git a/addr_hash.h b/addr_hash.h index 4d666e9..214d3a2 100644 --- a/addr_hash.h +++ b/addr_hash.h @@ -12,7 +12,9 @@ #include "hash.h" typedef struct { + unsigned short int src_port; struct in_addr src; + unsigned short int dst_port; struct in_addr dst; } addr_pair; diff --git a/iftop.c b/iftop.c index ceed8c4..44a2a34 100644 --- a/iftop.c +++ b/iftop.c @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include #include #include @@ -47,8 +49,8 @@ static void finish(int sig) { -/* Only need ethernet and IP headers. */ -#define CAPTURE_LENGTH 48 +/* Only need ethernet and IP headers (48) + first 2 bytes of tcp/udp header */ +#define CAPTURE_LENGTH 68 void init_history() { history = addr_hash_create(); @@ -118,6 +120,39 @@ int in_filter_net(struct in_addr addr) { return ret; } +/** + * Creates an addr_pair from an ip (and tcp/udp) header, swapping src and dst + * if required + */ +void assign_addr_pair(addr_pair* ap, struct ip* iptr, int flip) { + unsigned short int src_port = 0; + unsigned short int dst_port = 0; + + /* Does this protocol use ports? */ + if(iptr->ip_p == SOL_TCP || iptr->ip_p == SOL_UDP) { + /* We take a slight liberty here by treating UDP the same as TCP */ + + /* Find the TCP/UDP header */ + struct tcphdr* thdr = ((void*)iptr) + iptr->ip_hl * 4; + src_port = ntohs(thdr->source); + dst_port = ntohs(thdr->dest); + } + + if(flip == 0) { + ap->src = iptr->ip_src; + ap->src_port = src_port; + ap->dst = iptr->ip_dst; + ap->dst_port = dst_port; + } + else { + ap->src = iptr->ip_dst; + ap->src_port = dst_port; + ap->dst = iptr->ip_src; + ap->dst_port = src_port; + } + +} + static void handle_ip_packet(struct ip* iptr, int hw_dir) { int direction = 0; /* incoming */ @@ -131,28 +166,27 @@ static void handle_ip_packet(struct ip* iptr, int hw_dir) */ if(hw_dir == 1) { /* Packet leaving this interface. */ - ap.src = iptr->ip_src; - ap.dst = iptr->ip_dst; + assign_addr_pair(&ap, iptr, 0); direction = 1; } else if(hw_dir == 0) { /* Packet incoming */ - ap.src = iptr->ip_dst; - ap.dst = iptr->ip_src; + assign_addr_pair(&ap, iptr, 1); + direction = 0; } - /* - * This packet is not from or to this interface, or the h/ware - * layer did not give the direction away. Therefore assume - * it was picked up in promisc mode, and account it as incoming. - */ + /* + * This packet is not from or to this interface, or the h/ware + * layer did not give the direction away. Therefore assume + * it was picked up in promisc mode, and account it as incoming. + */ else if(iptr->ip_src.s_addr < iptr->ip_dst.s_addr) { - ap.src = iptr->ip_src; - ap.dst = iptr->ip_dst; + assign_addr_pair(&ap, iptr, 0); + direction = 0; } else { - ap.src = iptr->ip_dst; - ap.dst = iptr->ip_src; + assign_addr_pair(&ap, iptr, 0); + direction = 0; } } else { @@ -161,14 +195,13 @@ static void handle_ip_packet(struct ip* iptr, int hw_dir) */ if(in_filter_net(iptr->ip_src) & !in_filter_net(iptr->ip_dst)) { /* out of network */ - ap.src = iptr->ip_src; - ap.dst = iptr->ip_dst; + assign_addr_pair(&ap, iptr, 0); direction = 1; } else if(in_filter_net(iptr->ip_dst) & !in_filter_net(iptr->ip_src)) { /* into network */ - ap.src = iptr->ip_dst; - ap.dst = iptr->ip_src; + assign_addr_pair(&ap, iptr, 1); + direction = 0; } else { /* drop packet */ diff --git a/options.c b/options.c index f452ce5..5f1fcd3 100644 --- a/options.c +++ b/options.c @@ -18,7 +18,7 @@ options_t options; -char optstr[] = "+i:f:n:dhpb"; +char optstr[] = "+i:f:n:dhpbP"; /* Global options. */ @@ -66,6 +66,7 @@ static void set_defaults() { options.dnsresolution = 1; options.promiscuous = 0; options.showbars = 1; + options.showports = OPTION_PORTS_OFF; options.aggregate = OPTION_AGGREGATE_OFF; } @@ -120,6 +121,7 @@ static void usage(FILE *fp) { " -f filter code use filter code to select packets to count\n" " (default: none, but only IP packets are counted)\n" " -n net/mask show traffic flows in/out of network\n" +" -P show ports as well as hosts\n" "\n" "iftop, version " IFTOP_VERSION " copyright (c) 2002 Paul Warren \n" ); @@ -153,6 +155,10 @@ void options_read(int argc, char **argv) { options.promiscuous = 1; break; + case 'P': + options.showports = OPTION_PORTS_ON; + break; + case 'n': set_net_filter(optarg); break; diff --git a/options.h b/options.h index a244b67..e589b38 100644 --- a/options.h +++ b/options.h @@ -16,6 +16,13 @@ typedef enum { OPTION_AGGREGATE_DEST } option_aggregate_t; +typedef enum { + OPTION_PORTS_OFF, + OPTION_PORTS_AGGSRC, + OPTION_PORTS_AGGDEST, + OPTION_PORTS_ON +} option_port_t; + typedef struct { /* interface on which to listen */ char *interface; @@ -30,6 +37,7 @@ typedef struct { int dnsresolution; int promiscuous; int showbars; + option_port_t showports; option_aggregate_t aggregate; } options_t; diff --git a/ui.c b/ui.c index d8507d1..c65b9f4 100644 --- a/ui.c +++ b/ui.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "addr_hash.h" #include "iftop.h" @@ -199,15 +200,25 @@ void analyse_data() { ap = *(addr_pair*)n->key; + /* Aggregate hosts, if required */ if(options.aggregate == OPTION_AGGREGATE_SRC) { ap.dst.s_addr = 0; } else if(options.aggregate == OPTION_AGGREGATE_DEST) { ap.src.s_addr = 0; } + + /* Aggregate ports, if required */ + if(options.showports == OPTION_PORTS_AGGSRC || options.showports == OPTION_PORTS_OFF) { + ap.src_port = 0; + } + if(options.showports == OPTION_PORTS_AGGDEST || options.showports == OPTION_PORTS_OFF) { + ap.dst_port = 0; + } + if(hash_find(screen_hash, &ap, (void**)&screen_line) == HASH_STATUS_KEY_NOT_FOUND) { - screen_line = xcalloc(1, sizeof *screen_line); + screen_line = xcalloc(1, sizeof *screen_line); hash_insert(screen_hash, &ap, screen_line); screen_line->ap = ap; } @@ -233,6 +244,9 @@ void analyse_data() { } hash_delete_all(screen_hash); + /** + * Calculate peaks and totals + */ for(i = 0; i < HISTORY_LENGTH; i++) { int j; int ii = (HISTORY_LENGTH + history_pos - i) % HISTORY_LENGTH; @@ -257,6 +271,48 @@ void analyse_data() { } +void sprint_host(char * line, struct in_addr* addr, unsigned int port, int L) { + char hostname[HOSTNAME_LENGTH]; + char service[10]; + struct servent* sent; + int left; + if(addr->s_addr == 0) { + sprintf(hostname, " * "); + } + else { + if (options.dnsresolution) + resolve(addr, hostname, L); + else + strcpy(hostname, inet_ntoa(*addr)); + } + left = strlen(hostname); + + //TODO: Replace this with in-memory hash for speed. + //sent = getservbyport(port, "tcp"); + if(port != 0) { + sent = NULL; + if(sent == NULL) { + snprintf(service, 10, ":%d", port); + } + else { + snprintf(service, 10, ":%s", sent->s_name); + } + } + else { + service[0] = '\0'; + } + + + sprintf(line, "%-*s", L, hostname); + if(left > (L - strlen(service))) { + left = L - strlen(service); + if(left < 0) { + left = 0; + } + } + sprintf(line + left, "%-*s", L-left, service); +} + void ui_print() { sorted_list_node* nn = NULL; char hostname[HOSTNAME_LENGTH]; @@ -313,22 +369,16 @@ void ui_print() { host_pair_line* screen_line = (host_pair_line*)nn->data; if(y < LINES - 4) { + fprintf(stderr, "Drawing at %d \r\n", y); L = (COLS - 8 * HISTORY_DIVISIONS - 4) / 2; if(L > sizeof hostname) { L = sizeof hostname; } - if(screen_line->ap.src.s_addr == 0) { - sprintf(hostname, " * "); - } - else { - if (options.dnsresolution) - resolve(&(screen_line->ap.src), hostname, L); - else - strcpy(hostname, inet_ntoa(screen_line->ap.src)); - } - sprintf(line, "%-*s", L, hostname); + sprint_host(line, &(screen_line->ap.src), screen_line->ap.src_port, L); + + //sprintf(line, "%-*s", L, hostname); mvaddstr(y, x, line); x += L; @@ -336,16 +386,9 @@ void ui_print() { mvaddstr(y+1, x, " <= "); x += 4; - if(screen_line->ap.dst.s_addr == 0) { - sprintf(hostname, " * "); - } - else { - if (options.dnsresolution) - resolve(&screen_line->ap.dst, hostname, L); - else - strcpy(hostname, inet_ntoa(screen_line->ap.dst)); - } - sprintf(line, "%-*s", L, hostname); + + sprint_host(line, &(screen_line->ap.dst), screen_line->ap.dst_port, L); + mvaddstr(y, x, line); draw_line_totals(y, screen_line); @@ -413,6 +456,7 @@ void ui_init() { screen_list_init(); screen_hash = addr_hash_create(); + } void ui_loop() { @@ -448,6 +492,13 @@ void ui_loop() { ? OPTION_AGGREGATE_OFF : OPTION_AGGREGATE_DEST; break; + case 'P': + options.showports = + (options.showports == OPTION_PORTS_OFF) + ? OPTION_PORTS_ON + : OPTION_PORTS_OFF; + // Don't tick here, otherwise we get a bogus display + break; } tick(0); }