Commit a248b604 authored by pdw's avatar pdw

Added -n option.

parent 94a696b7
......@@ -32,8 +32,9 @@ CFLAGS += -g -Wall "-DIFTOP_VERSION=\"$(VERSION)\""
LDFLAGS += -g
LDLIBS += -lpcap -lpthread -lcurses -lm
SRCS = iftop.c addr_hash.c hash.c ns_hash.c resolver.c ui.c util.c sorted_list.c
HDRS = addr_hash.h hash.h iftop.h ns_hash.h resolver.h sorted_list.h ui.h
SRCS = iftop.c addr_hash.c hash.c ns_hash.c resolver.c ui.c util.c sorted_list.c\
options.c
HDRS = addr_hash.h hash.h iftop.h ns_hash.h resolver.h sorted_list.h ui.h options.h
TXTS = README CHANGES INSTALL TODO iftop.8 COPYING
OBJS = $(SRCS:.c=.o)
......
......@@ -12,9 +12,6 @@ $Id$
* Aggregate traffic by source host / dest host rather than just by host pair.
* For promiscuous mode, give a way to define which packets are regarded as
`inside' and which `outside' the network.
* Cummulative byte counters.
* Configurable sort criteria.
......
......@@ -31,6 +31,10 @@ in a confusing display. You may wish to suppress display of DNS traffic by
using filter code such as \fBnot port domain\fP, or switch it off entirely,
by using the \fB-d\fP option or by pressing \fBR\fP when the program is running.
By default, \fBiftop\fP shows all IP packets that pass through the filter, and the direction of the packet is determined according to the direction the packet is moving across the interface. Using the \fB-n\fP option it is possible to get \fBiftop\fP to show packets entering and leaving a given network. For example, \fBiftop -n 10.0.0.0/255.0.0.0\fP will analyse packets flowing in and out of the 10.* network.
\fBiftop\fP must be run as root.
Some other filter ideas:
.TP
\fBnot ether host ff:ff:ff:ff:ff:ff\fP
......@@ -43,6 +47,7 @@ Count web traffic only, unless it is being directed through a local web cache.
How much bandwith are users wasting trying to figure out why the network is
slow?
.SH OPTIONS
.TP
......@@ -62,7 +67,11 @@ Listen to packets on \fIinterface\fP.
\fB-f\fP \fIfilter code\fP
Use \fIfilter code\fP to select the packets to count. Only IP packets are ever
counted, so the specified code is evaluated as \fB(\fP\fIfilter code\fP\fB) and ip\fP.
.TP
\fB-n\fP \fInet/mask\fP
Specifies a network for traffic analysis. If specified, iftop will only
include packets flowing in to or out of the given network, and packet direction
is determined relative to the network boundary, rather than to the interface.
.SH SEE ALSO
.BR tcpdump (8),
.BR pcap (3),
......
......@@ -15,29 +15,25 @@
#include <curses.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include "iftop.h"
#include "addr_hash.h"
#include "resolver.h"
#include "ui.h"
#include "options.h"
/* Global options. */
char *interface = "eth0";
char *filtercode = NULL;
unsigned char if_hw_addr[6]; /* ethernet address of interface. */
extern options_t options;
hash_type* history;
history_type history_totals;
time_t last_timestamp;
int history_pos = 0;
int history_len = 1;
pthread_mutex_t tick_mutex;
/* Open non-promiscuous by default since this is intended to be run on a
* router. */
int promiscuous = 0;
sig_atomic_t foad;
static void finish(int sig) {
......@@ -52,6 +48,7 @@ static void finish(int sig) {
void init_history() {
history = addr_hash_create();
last_timestamp = time(NULL);
memset(&history_totals, 0, sizeof history_totals);
}
history_type* history_create() {
......@@ -72,7 +69,7 @@ void history_rotate() {
if(d->last_write == history_pos) {
addr_pair key = *(addr_pair*)(n->key);
hash_delete(history, &key);
free(d);
free(d);
}
else {
d->recv[history_pos] = 0;
......@@ -80,6 +77,10 @@ void history_rotate() {
}
n = next;
}
history_totals.sent[history_pos] = 0;
history_totals.recv[history_pos] = 0;
if(history_len < HISTORY_LENGTH) {
history_len++;
}
......@@ -102,52 +103,81 @@ void tick() {
pthread_mutex_unlock(&tick_mutex);
}
int in_filter_net(struct in_addr addr) {
int ret;
ret = ((addr.s_addr & options.netfiltermask.s_addr) == options.netfilternet.s_addr);
return ret;
}
static void handle_packet(char* args, const struct pcap_pkthdr* pkthdr,const char* packet)
{
struct ether_header *eptr;
int direction = 0; /* incoming */
eptr = (struct ether_header*)packet;
tick();
if(ntohs(eptr->ether_type) == ETHERTYPE_IP) {
struct ip* iptr;
history_type* ht;
addr_pair ap;
int promisc = 0;
iptr = (struct ip*)(packet + sizeof(struct ether_header)); /* alignment? */
if(memcmp(eptr->ether_shost, if_hw_addr, 6) == 0 ) {
/* Packet leaving this interface. */
ap.src = iptr->ip_src;
ap.dst = iptr->ip_dst;
}
else if(memcmp(eptr->ether_dhost, if_hw_addr, 6) == 0 || memcmp("\xFF\xFF\xFF\xFF\xFF\xFF", eptr->ether_dhost, 6) == 0) {
ap.src = iptr->ip_dst;
ap.dst = iptr->ip_src;
}
/*
* This packet is not from or to this interface. Therefore assume
* it was picked up in promisc mode.
*/
else if(iptr->ip_src.s_addr < iptr->ip_dst.s_addr) {
ap.src = iptr->ip_src;
ap.dst = iptr->ip_dst;
promisc = 1;
if(options.netfilter == 0) {
/*
* Net filter is off, so assign direction based on MAC address
*/
if(memcmp(eptr->ether_shost, if_hw_addr, 6) == 0 ) {
/* Packet leaving this interface. */
ap.src = iptr->ip_src;
ap.dst = iptr->ip_dst;
direction = 1;
}
else if(memcmp(eptr->ether_dhost, if_hw_addr, 6) == 0 || memcmp("\xFF\xFF\xFF\xFF\xFF\xFF", eptr->ether_dhost, 6) == 0) {
ap.src = iptr->ip_dst;
ap.dst = iptr->ip_src;
}
/*
* This packet is not from or to this interface. 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;
}
else {
ap.src = iptr->ip_dst;
ap.dst = iptr->ip_src;
}
}
else {
ap.src = iptr->ip_dst;
ap.dst = iptr->ip_src;
promisc = 1;
/*
* Net filter on, assign direction according to netmask
*/
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;
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;
}
else {
/* drop packet */
return ;
}
}
/* Add the address to be resolved */
/* Add the addresses to be resolved */
resolve(&iptr->ip_dst, NULL, 0);
resolve(&iptr->ip_src, NULL, 0);
if(hash_find(history, &ap, (void**)&ht) == HASH_STATUS_KEY_NOT_FOUND) {
ht = history_create();
ht->promisc = promisc;
hash_insert(history, &ap, ht);
}
......@@ -160,6 +190,14 @@ static void handle_packet(char* args, const struct pcap_pkthdr* pkthdr,const cha
ht->recv[history_pos] += ntohs(iptr->ip_len);
}
if(direction == 0) {
/* incoming */
history_totals.recv[history_pos] += ntohs(iptr->ip_len);
}
else {
history_totals.sent[history_pos] += ntohs(iptr->ip_len);
}
}
}
......@@ -181,7 +219,7 @@ void packet_loop(void* ptr) {
foad = 1;
return;
}
strncpy(ifr.ifr_name, interface, IFNAMSIZ);
strncpy(ifr.ifr_name, options.interface, IFNAMSIZ);
ifr.ifr_hwaddr.sa_family = AF_UNSPEC;
if (ioctl(s, SIOCGIFHWADDR, &ifr) == -1) {
perror("ioctl(SIOCGIFHWADDR)");
......@@ -197,15 +235,15 @@ void packet_loop(void* ptr) {
resolver_initialise();
pd = pcap_open_live(interface, CAPTURE_LENGTH, promiscuous, 1000, errbuf);
pd = pcap_open_live(options.interface, CAPTURE_LENGTH, options.promiscuous, 1000, errbuf);
if(pd == NULL) {
fprintf(stderr, "pcap_open_live(%s): %s\n", interface, errbuf);
fprintf(stderr, "pcap_open_live(%s): %s\n", options.interface, errbuf);
foad = 1;
return;
}
if (filtercode) {
str = xmalloc(strlen(filtercode) + sizeof "() and ip");
sprintf(str, "(%s) and ip", filtercode);
if (options.filtercode) {
str = xmalloc(strlen(options.filtercode) + sizeof "() and ip");
sprintf(str, "(%s) and ip", options.filtercode);
}
if (pcap_compile(pd, &F, str, 1, 0) == -1) {
fprintf(stderr, "pcap_compile(%s): %s\n", str, pcap_geterr(pd));
......@@ -217,76 +255,21 @@ void packet_loop(void* ptr) {
foad = 1;
return;
}
if (filtercode)
if (options.filtercode)
xfree(str);
printf("Begin loop\n");
pcap_loop(pd,0,(pcap_handler)handle_packet,NULL);
printf("end loop\n");
}
/* usage:
* Print usage information. */
void usage(FILE *fp) {
fprintf(fp,
"iftop: display bandwidth usage on an interface by host\n"
"\n"
"Synopsis: iftop -h | [-d] [-p] [-i interface] [-f filter code]\n"
"\n"
" -h display this message\n"
" -d don't do hostname lookups\n"
" -p run in promiscuous mode (show traffic between other\n"
" hosts on the same network segment)\n"
" -i interface listen on named interface (default: eth0)\n"
" -f filter code use filter code to select packets to count\n"
" (default: none, but only IP packets are counted)\n"
"\n"
"iftop, version " IFTOP_VERSION "copyright (c) 2002 Paul Warren <pdw@ex-parrot.com>\n"
);
}
/* main:
* Entry point. See usage(). */
char optstr[] = "+i:f:dhp";
int main(int argc, char **argv) {
pthread_t thread;
struct sigaction sa = {0};
extern int dnsresolution; /* in ui.c */
int opt;
opterr = 0;
while ((opt = getopt(argc, argv, optstr)) != -1) {
switch (opt) {
case 'h':
usage(stdout);
return 0;
case 'd':
dnsresolution = 0;
break;
case 'i':
interface = optarg;
break;
case 'f':
filtercode = optarg;
break;
case 'p':
promiscuous = 1;
break;
case '?':
fprintf(stderr, "iftop: unknown option -%c\n", optopt);
usage(stderr);
return 1;
case ':':
fprintf(stderr, "iftop: option -%c requires an argument\n", optopt);
usage(stderr);
return 1;
}
}
options_read(argc, argv);
sa.sa_handler = finish;
sigaction(SIGINT, &sa, NULL);
......
......@@ -14,7 +14,6 @@ typedef struct {
long recv[HISTORY_LENGTH];
long sent[HISTORY_LENGTH];
int last_write;
int promisc;
} history_type;
void tick();
......
/*
* options.c:
*
*
*/
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include "options.h"
options_t options;
char optstr[] = "+i:f:n:dhp";
/* Global options. */
void set_defaults() {
options.interface = "eth0";
options.filtercode = NULL;
options.netfilter = 0;
inet_aton("10.0.1.0", &options.netfilternet);
inet_aton("255.255.255.0", &options.netfiltermask);
options.dnsresolution = 1;
options.promiscuous = 0;
}
void die(char *msg) {
fprintf(stderr, msg);
exit(1);
}
void set_net_filter(char* arg) {
char* mask;
mask = strstr(arg, "/");
if(mask == NULL) {
die("Could not parse net/mask\n");
}
*mask = '\0';
mask++;
if(inet_aton(arg, &options.netfilternet) == 0) {
die("Invalid network address\n");
}
if(inet_aton(mask, &options.netfiltermask) == 0) {
die("Invalid network mask\n");
}
options.netfilter = 1;
}
/* usage:
* Print usage information. */
void usage(FILE *fp) {
fprintf(fp,
"iftop: display bandwidth usage on an interface by host\n"
"\n"
"Synopsis: iftop -h | [-d] [-p] [-i interface] [-f filter code]\n"
"\n"
" -h display this message\n"
" -d don't do hostname lookups\n"
" -p run in promiscuous mode (show traffic between other\n"
" hosts on the same network segment)\n"
" -i interface listen on named interface (default: eth0)\n"
" -f filter code use filter code to select packets to count\n"
" (default: none, but only IP packets are counted)\n"
" -n network/netmask show traffic flows in/out of network\n"
"\n"
"iftop, version " IFTOP_VERSION "copyright (c) 2002 Paul Warren <pdw@ex-parrot.com>\n"
);
}
void options_read(int argc, char **argv) {
int opt;
set_defaults();
opterr = 0;
while ((opt = getopt(argc, argv, optstr)) != -1) {
switch (opt) {
case 'h':
usage(stdout);
exit(0);
case 'd':
options.dnsresolution = 0;
break;
case 'i':
options.interface = optarg;
break;
case 'f':
options.filtercode = optarg;
break;
case 'p':
options.promiscuous = 1;
break;
case 'n':
set_net_filter(optarg);
break;
case '?':
fprintf(stderr, "iftop: unknown option -%c\n", optopt);
usage(stderr);
exit(1);
case ':':
fprintf(stderr, "iftop: option -%c requires an argument\n", optopt);
usage(stderr);
exit(1);
}
}
}
/*
* options.h:
*
*/
#ifndef __OPTIONS_H_ /* include guard */
#define __OPTIONS_H_
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
typedef struct {
/* interface to listen on */
char *interface;
/* pcap filter code */
char *filtercode;
/* Cross network filter */
int netfilter;
struct in_addr netfilternet;
struct in_addr netfiltermask;
int dnsresolution;
int promiscuous;
} options_t;
#endif /* __OPTIONS_H_ */
......@@ -74,20 +74,15 @@ void resolver_worker(void* ptr) {
/* Check for errors. */
if (res || hp == NULL) {
/* failed */
//printf("[ Did not resolve %s ]\n", inet_ntoa(addr));
/* Leave the unresolved IP in the hash */
}
else {
/* success */
char* hostname;
//printf("[ Resolved: %s ]\n", hp->h_name);
if(hash_find(ns_hash, &addr, (void**)&hostname) == HASH_STATUS_OK) {
hash_delete(ns_hash, &addr);
xfree(hostname);
}
else {
//printf("[ Warning: Could not find hash entry for key: %s ]\n", inet_ntoa(addr));
}
hostname = strdup(hp->h_name);
hash_insert(ns_hash, &addr, (void*)hostname);
......
......@@ -15,6 +15,7 @@
#include "iftop.h"
#include "resolver.h"
#include "sorted_list.h"
#include "options.h"
#define HOSTNAME_LENGTH 256
......@@ -36,6 +37,8 @@ extern hash_type* history;
extern int history_pos;
extern int history_len;
extern options_t options ;
void ui_finish();
int screen_line_compare(void* a, void* b) {
......@@ -136,8 +139,8 @@ void draw_totals(host_pair_line* totals) {
draw_line_totals(y, totals);
}
extern history_type history_totals;
int dnsresolution = 1;
void ui_print() {
hash_node_type* n = NULL;
......@@ -150,7 +153,6 @@ void ui_print() {
int i;
sorted_list_type screen_list;
host_pair_line totals;
history_type history_totals;
if (!line || lcols != COLS) {
xfree(line);
......@@ -170,12 +172,11 @@ void ui_print() {
attron(A_REVERSE);
addstr(" R ");
attroff(A_REVERSE);
addstr(dnsresolution ? " name resolution off "
addstr(options.dnsresolution ? " name resolution off "
: " name resolution on ");
draw_bar_scale();
memset(&totals, 0, sizeof totals);
memset(&history_totals, 0, sizeof history_totals);
while(hash_next_item(history, &n) == HASH_STATUS_OK) {
history_type* d = (history_type*)n->rec;
......@@ -195,34 +196,26 @@ void ui_print() {
for(j = 0; j < HISTORY_DIVISIONS; j++) {
if(i < history_divs[j]) {
screen_line->recv[j] += d->recv[ii];
totals.recv[j] += d->recv[ii];
screen_line->sent[j] += d->sent[ii];
if(d->promisc == 1) {
/* Treat all promisc traffic as incoming */
totals.recv[j] += d->sent[ii];
}
else {
totals.sent[j] += d->sent[ii];
}
}
}
history_totals.recv[ii] += d->recv[ii];
if(d->promisc == 1) {
/* Treat all promisc traffic as incoming */
history_totals.recv[ii] += d->sent[ii];
}
else {
history_totals.sent[ii] += d->sent[ii];
}
}
sorted_list_insert(&screen_list, screen_line);
}
for(i = 0; i < HISTORY_LENGTH; i++) {
int j;
int ii = (HISTORY_LENGTH + history_pos - i) % HISTORY_LENGTH;
for(j = 0; j < HISTORY_DIVISIONS; j++) {
if(i < history_divs[j]) {
totals.recv[j] += history_totals.recv[ii];
totals.sent[j] += history_totals.sent[ii];
}
}
if(history_totals.recv[i] > peakrecv) {
peakrecv = history_totals.recv[i];
}
......@@ -253,7 +246,7 @@ void ui_print() {
L = sizeof hostname;
}
if (dnsresolution)
if (options.dnsresolution)
resolve(&screen_line->ap->src, hostname, L);
else
strcpy(hostname, inet_ntoa(screen_line->ap->src));
......@@ -265,7 +258,7 @@ void ui_print() {
mvaddstr(y+1, x, " <= ");
x += 4;
if (dnsresolution)
if (options.dnsresolution)
resolve(&screen_line->ap->dst, hostname, L);
else
strcpy(hostname, inet_ntoa(screen_line->ap->dst));
......@@ -348,7 +341,7 @@ void ui_loop() {
break;
case 'R':
dnsresolution = !dnsresolution;
options.dnsresolution = !options.dnsresolution;
break;
}
tick();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment