Added support for displaying ports.
This commit is contained in:
4
CHANGES
4
CHANGES
@@ -1,6 +1,10 @@
|
|||||||
Change log for iftop
|
Change log for iftop
|
||||||
$Id$
|
$Id$
|
||||||
|
|
||||||
|
0.8
|
||||||
|
|
||||||
|
Added support for displaying port numbers
|
||||||
|
|
||||||
0.7 02/10/02
|
0.7 02/10/02
|
||||||
|
|
||||||
Fixed missing sll.h file.
|
Fixed missing sll.h file.
|
||||||
|
|||||||
6
TODO
6
TODO
@@ -1,12 +1,12 @@
|
|||||||
Things to do for iftop
|
Things to do for iftop
|
||||||
$Id$
|
$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?
|
* IP types other than v4?
|
||||||
|
|
||||||
* Include ports in display.
|
|
||||||
|
|
||||||
* Which average to use for the bar graph? Show several and peaks? Colours?
|
* 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
|
* Single keypress firewalling of troublesome connections, a la top(1)'s K
|
||||||
|
|||||||
10
addr_hash.c
10
addr_hash.c
@@ -12,7 +12,9 @@ int compare(void* a, void* b) {
|
|||||||
addr_pair* aa = (addr_pair*)a;
|
addr_pair* aa = (addr_pair*)a;
|
||||||
addr_pair* bb = (addr_pair*)b;
|
addr_pair* bb = (addr_pair*)b;
|
||||||
return (aa->src.s_addr == bb->src.s_addr
|
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) {
|
int hash(void* key) {
|
||||||
@@ -25,13 +27,15 @@ int hash(void* key) {
|
|||||||
hash = ((addr & 0x000000FF)
|
hash = ((addr & 0x000000FF)
|
||||||
+ (addr & 0x0000FF00 >> 8)
|
+ (addr & 0x0000FF00 >> 8)
|
||||||
+ (addr & 0x00FF0000 >> 16)
|
+ (addr & 0x00FF0000 >> 16)
|
||||||
+ (addr & 0xFF000000 >> 24)) % 0xFF;
|
+ (addr & 0xFF000000 >> 24)
|
||||||
|
+ ap->src_port) % 0xFF;
|
||||||
|
|
||||||
addr = (long)ap->dst.s_addr;
|
addr = (long)ap->dst.s_addr;
|
||||||
hash = ( hash + (addr & 0x000000FF)
|
hash = ( hash + (addr & 0x000000FF)
|
||||||
+ (addr & 0x0000FF00 >> 8)
|
+ (addr & 0x0000FF00 >> 8)
|
||||||
+ (addr & 0x00FF0000 >> 16)
|
+ (addr & 0x00FF0000 >> 16)
|
||||||
+ (addr & 0xFF000000 >> 24)) % 0xFF;
|
+ (addr & 0xFF000000 >> 24)
|
||||||
|
+ ap->dst_port) % 0xFF;
|
||||||
|
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,9 @@
|
|||||||
#include "hash.h"
|
#include "hash.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
unsigned short int src_port;
|
||||||
struct in_addr src;
|
struct in_addr src;
|
||||||
|
unsigned short int dst_port;
|
||||||
struct in_addr dst;
|
struct in_addr dst;
|
||||||
} addr_pair;
|
} addr_pair;
|
||||||
|
|
||||||
|
|||||||
61
iftop.c
61
iftop.c
@@ -11,6 +11,8 @@
|
|||||||
#include <net/if.h>
|
#include <net/if.h>
|
||||||
#include <net/ethernet.h>
|
#include <net/ethernet.h>
|
||||||
#include <netinet/ip.h>
|
#include <netinet/ip.h>
|
||||||
|
#include <netinet/tcp.h>
|
||||||
|
#include <netinet/udp.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <curses.h>
|
#include <curses.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
@@ -47,8 +49,8 @@ static void finish(int sig) {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Only need ethernet and IP headers. */
|
/* Only need ethernet and IP headers (48) + first 2 bytes of tcp/udp header */
|
||||||
#define CAPTURE_LENGTH 48
|
#define CAPTURE_LENGTH 68
|
||||||
|
|
||||||
void init_history() {
|
void init_history() {
|
||||||
history = addr_hash_create();
|
history = addr_hash_create();
|
||||||
@@ -118,6 +120,39 @@ int in_filter_net(struct in_addr addr) {
|
|||||||
return ret;
|
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)
|
static void handle_ip_packet(struct ip* iptr, int hw_dir)
|
||||||
{
|
{
|
||||||
int direction = 0; /* incoming */
|
int direction = 0; /* incoming */
|
||||||
@@ -131,14 +166,13 @@ static void handle_ip_packet(struct ip* iptr, int hw_dir)
|
|||||||
*/
|
*/
|
||||||
if(hw_dir == 1) {
|
if(hw_dir == 1) {
|
||||||
/* Packet leaving this interface. */
|
/* Packet leaving this interface. */
|
||||||
ap.src = iptr->ip_src;
|
assign_addr_pair(&ap, iptr, 0);
|
||||||
ap.dst = iptr->ip_dst;
|
|
||||||
direction = 1;
|
direction = 1;
|
||||||
}
|
}
|
||||||
else if(hw_dir == 0) {
|
else if(hw_dir == 0) {
|
||||||
/* Packet incoming */
|
/* Packet incoming */
|
||||||
ap.src = iptr->ip_dst;
|
assign_addr_pair(&ap, iptr, 1);
|
||||||
ap.dst = iptr->ip_src;
|
direction = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -147,12 +181,12 @@ static void handle_ip_packet(struct ip* iptr, int hw_dir)
|
|||||||
* it was picked up in promisc mode, and account it as incoming.
|
* it was picked up in promisc mode, and account it as incoming.
|
||||||
*/
|
*/
|
||||||
else if(iptr->ip_src.s_addr < iptr->ip_dst.s_addr) {
|
else if(iptr->ip_src.s_addr < iptr->ip_dst.s_addr) {
|
||||||
ap.src = iptr->ip_src;
|
assign_addr_pair(&ap, iptr, 0);
|
||||||
ap.dst = iptr->ip_dst;
|
direction = 0;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ap.src = iptr->ip_dst;
|
assign_addr_pair(&ap, iptr, 0);
|
||||||
ap.dst = iptr->ip_src;
|
direction = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
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)) {
|
if(in_filter_net(iptr->ip_src) & !in_filter_net(iptr->ip_dst)) {
|
||||||
/* out of network */
|
/* out of network */
|
||||||
ap.src = iptr->ip_src;
|
assign_addr_pair(&ap, iptr, 0);
|
||||||
ap.dst = iptr->ip_dst;
|
|
||||||
direction = 1;
|
direction = 1;
|
||||||
}
|
}
|
||||||
else if(in_filter_net(iptr->ip_dst) & !in_filter_net(iptr->ip_src)) {
|
else if(in_filter_net(iptr->ip_dst) & !in_filter_net(iptr->ip_src)) {
|
||||||
/* into network */
|
/* into network */
|
||||||
ap.src = iptr->ip_dst;
|
assign_addr_pair(&ap, iptr, 1);
|
||||||
ap.dst = iptr->ip_src;
|
direction = 0;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* drop packet */
|
/* drop packet */
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
options_t options;
|
options_t options;
|
||||||
|
|
||||||
char optstr[] = "+i:f:n:dhpb";
|
char optstr[] = "+i:f:n:dhpbP";
|
||||||
|
|
||||||
/* Global options. */
|
/* Global options. */
|
||||||
|
|
||||||
@@ -66,6 +66,7 @@ static void set_defaults() {
|
|||||||
options.dnsresolution = 1;
|
options.dnsresolution = 1;
|
||||||
options.promiscuous = 0;
|
options.promiscuous = 0;
|
||||||
options.showbars = 1;
|
options.showbars = 1;
|
||||||
|
options.showports = OPTION_PORTS_OFF;
|
||||||
options.aggregate = OPTION_AGGREGATE_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"
|
" -f filter code use filter code to select packets to count\n"
|
||||||
" (default: none, but only IP packets are counted)\n"
|
" (default: none, but only IP packets are counted)\n"
|
||||||
" -n net/mask show traffic flows in/out of network\n"
|
" -n net/mask show traffic flows in/out of network\n"
|
||||||
|
" -P show ports as well as hosts\n"
|
||||||
"\n"
|
"\n"
|
||||||
"iftop, version " IFTOP_VERSION " copyright (c) 2002 Paul Warren <pdw@ex-parrot.com>\n"
|
"iftop, version " IFTOP_VERSION " copyright (c) 2002 Paul Warren <pdw@ex-parrot.com>\n"
|
||||||
);
|
);
|
||||||
@@ -153,6 +155,10 @@ void options_read(int argc, char **argv) {
|
|||||||
options.promiscuous = 1;
|
options.promiscuous = 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'P':
|
||||||
|
options.showports = OPTION_PORTS_ON;
|
||||||
|
break;
|
||||||
|
|
||||||
case 'n':
|
case 'n':
|
||||||
set_net_filter(optarg);
|
set_net_filter(optarg);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -16,6 +16,13 @@ typedef enum {
|
|||||||
OPTION_AGGREGATE_DEST
|
OPTION_AGGREGATE_DEST
|
||||||
} option_aggregate_t;
|
} option_aggregate_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
OPTION_PORTS_OFF,
|
||||||
|
OPTION_PORTS_AGGSRC,
|
||||||
|
OPTION_PORTS_AGGDEST,
|
||||||
|
OPTION_PORTS_ON
|
||||||
|
} option_port_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
/* interface on which to listen */
|
/* interface on which to listen */
|
||||||
char *interface;
|
char *interface;
|
||||||
@@ -30,6 +37,7 @@ typedef struct {
|
|||||||
int dnsresolution;
|
int dnsresolution;
|
||||||
int promiscuous;
|
int promiscuous;
|
||||||
int showbars;
|
int showbars;
|
||||||
|
option_port_t showports;
|
||||||
option_aggregate_t aggregate;
|
option_aggregate_t aggregate;
|
||||||
|
|
||||||
} options_t;
|
} options_t;
|
||||||
|
|||||||
91
ui.c
91
ui.c
@@ -10,6 +10,7 @@
|
|||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
|
||||||
#include "addr_hash.h"
|
#include "addr_hash.h"
|
||||||
#include "iftop.h"
|
#include "iftop.h"
|
||||||
@@ -199,6 +200,7 @@ void analyse_data() {
|
|||||||
|
|
||||||
ap = *(addr_pair*)n->key;
|
ap = *(addr_pair*)n->key;
|
||||||
|
|
||||||
|
/* Aggregate hosts, if required */
|
||||||
if(options.aggregate == OPTION_AGGREGATE_SRC) {
|
if(options.aggregate == OPTION_AGGREGATE_SRC) {
|
||||||
ap.dst.s_addr = 0;
|
ap.dst.s_addr = 0;
|
||||||
}
|
}
|
||||||
@@ -206,6 +208,15 @@ void analyse_data() {
|
|||||||
ap.src.s_addr = 0;
|
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) {
|
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);
|
hash_insert(screen_hash, &ap, screen_line);
|
||||||
@@ -233,6 +244,9 @@ void analyse_data() {
|
|||||||
}
|
}
|
||||||
hash_delete_all(screen_hash);
|
hash_delete_all(screen_hash);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate peaks and totals
|
||||||
|
*/
|
||||||
for(i = 0; i < HISTORY_LENGTH; i++) {
|
for(i = 0; i < HISTORY_LENGTH; i++) {
|
||||||
int j;
|
int j;
|
||||||
int ii = (HISTORY_LENGTH + history_pos - i) % HISTORY_LENGTH;
|
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() {
|
void ui_print() {
|
||||||
sorted_list_node* nn = NULL;
|
sorted_list_node* nn = NULL;
|
||||||
char hostname[HOSTNAME_LENGTH];
|
char hostname[HOSTNAME_LENGTH];
|
||||||
@@ -313,22 +369,16 @@ void ui_print() {
|
|||||||
host_pair_line* screen_line = (host_pair_line*)nn->data;
|
host_pair_line* screen_line = (host_pair_line*)nn->data;
|
||||||
|
|
||||||
if(y < LINES - 4) {
|
if(y < LINES - 4) {
|
||||||
|
fprintf(stderr, "Drawing at %d \r\n", y);
|
||||||
|
|
||||||
L = (COLS - 8 * HISTORY_DIVISIONS - 4) / 2;
|
L = (COLS - 8 * HISTORY_DIVISIONS - 4) / 2;
|
||||||
if(L > sizeof hostname) {
|
if(L > sizeof hostname) {
|
||||||
L = sizeof hostname;
|
L = sizeof hostname;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(screen_line->ap.src.s_addr == 0) {
|
sprint_host(line, &(screen_line->ap.src), screen_line->ap.src_port, L);
|
||||||
sprintf(hostname, " * ");
|
|
||||||
}
|
//sprintf(line, "%-*s", L, 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);
|
|
||||||
mvaddstr(y, x, line);
|
mvaddstr(y, x, line);
|
||||||
x += L;
|
x += L;
|
||||||
|
|
||||||
@@ -336,16 +386,9 @@ void ui_print() {
|
|||||||
mvaddstr(y+1, x, " <= ");
|
mvaddstr(y+1, x, " <= ");
|
||||||
|
|
||||||
x += 4;
|
x += 4;
|
||||||
if(screen_line->ap.dst.s_addr == 0) {
|
|
||||||
sprintf(hostname, " * ");
|
sprint_host(line, &(screen_line->ap.dst), screen_line->ap.dst_port, L);
|
||||||
}
|
|
||||||
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);
|
|
||||||
mvaddstr(y, x, line);
|
mvaddstr(y, x, line);
|
||||||
|
|
||||||
draw_line_totals(y, screen_line);
|
draw_line_totals(y, screen_line);
|
||||||
@@ -413,6 +456,7 @@ void ui_init() {
|
|||||||
|
|
||||||
screen_list_init();
|
screen_list_init();
|
||||||
screen_hash = addr_hash_create();
|
screen_hash = addr_hash_create();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ui_loop() {
|
void ui_loop() {
|
||||||
@@ -448,6 +492,13 @@ void ui_loop() {
|
|||||||
? OPTION_AGGREGATE_OFF
|
? OPTION_AGGREGATE_OFF
|
||||||
: OPTION_AGGREGATE_DEST;
|
: OPTION_AGGREGATE_DEST;
|
||||||
break;
|
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);
|
tick(0);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user