summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpdw <>2007-03-03 16:01:48 +0000
committerpdw <>2007-03-03 16:01:48 +0000
commit6458ed04f5ce51a4b0dc9059e8ddeff05e56a721 (patch)
treebd99c53cb248642b18075154baae4f3d4ff28722
parent86013fabd68b173dc9021829913ebcc9ce6e9722 (diff)
downloadiftop-6458ed04f5ce51a4b0dc9059e8ddeff05e56a721.zip
Created iftop-dump
-rw-r--r--Makefile.OLD33
-rw-r--r--Makefile.am10
-rw-r--r--configure.in2
-rw-r--r--counter_hash.c56
-rw-r--r--counter_hash.h23
-rw-r--r--iftop-dump.c466
-rw-r--r--iftop.h5
-rw-r--r--ui.h2
8 files changed, 579 insertions, 18 deletions
diff --git a/Makefile.OLD b/Makefile.OLD
index 2855d1c..e965a0c 100644
--- a/Makefile.OLD
+++ b/Makefile.OLD
@@ -22,8 +22,8 @@ CFLAGS += -I/usr/include/pcap -g
# Do you want to use curses or ncurses? Probably ncurses, unless curses
# is ncurses on your machine.
-CURSES = ncurses
-#CURSES = curses
+# CURSES = -lncurses
+#CURSES = -lcurses
#
# Name resolution. Sensible systems have gethostbyaddr_r, which is reentrant
@@ -66,7 +66,7 @@ CFLAGS += -DUSE_GETHOSTBYADDR_R
#
# Solaris needs a library to make sockets go and lacks inet_aton.
#
-LDLIBS += -lsocket -lnsl
+#LDLIBS += -lsocket -lnsl
CFLAGS += -DFAKE_INET_ATON
# PREFIX specifies the base directory for the installation.
@@ -83,23 +83,32 @@ MANDIR = man
# You shouldn't need to change anything below this point.
CFLAGS += -g -Wall "-DIFTOP_VERSION=\"$(VERSION)\""
LDFLAGS += -g #-pthread
-LDLIBS += -lpcap -l$(CURSES) -lm -lpthread
-
-SRCS = iftop.c addr_hash.c hash.c ns_hash.c resolver.c ui.c util.c sorted_list.c\
- options.c serv_hash.c threadprof.c edline.c screenfilter.c
+LDLIBS += -lpcap $(CURSES) -lm -lpthread
+
+COMMON_SRCS = addr_hash.c hash.c ns_hash.c util.c sorted_list.c\
+ options.c serv_hash.c threadprof.c screenfilter.c cfgfile.c stringmap.c addrs_ioctl.c
+IFTOP_ONLY_SRCS = iftop.c ui.c stringmap.c
+IFTOPDUMP_ONLY_SRCS = iftop-dump.c counter_hash.c
+SRCS = $(COMMON_SRCS) $(IFTOPDUMP_ONLY_SRCS) $(IFTOP_ONLY_SRCS)
+IFTOP_SRCS = $(IFTOP_ONLY_SRCS) $(COMMON_SRCS)
+IFTOPDUMP_SRCS = $(IFTOPDUMP_ONLY_SRCS) $(COMMON_SRCS)
HDRS = addr_hash.h hash.h iftop.h ns_hash.h resolver.h sorted_list.h ui.h options.h sll.h\
serv_hash.h threadprof.h ether.h ip.h tcp.h screenfilter.h token.h llc.h \
extract.h ethertype.h
TXTS = README CHANGES INSTALL TODO iftop.8 COPYING
SPECFILE = iftop.spec iftop.spec.in
-OBJS = $(SRCS:.c=.o)
+IFTOP_OBJS = $(IFTOP_SRCS:.c=.o)
+IFTOPDUMP_OBJS = $(IFTOPDUMP_SRCS:.c=.o)
+
+iftop: $(IFTOP_OBJS) Makefile
+ $(CC) $(LDFLAGS) -o $@ $(IFTOP_OBJS) $(LDLIBS)
-iftop: $(OBJS) Makefile
- $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LDLIBS)
+iftop-dump: $(IFTOPDUMP_OBJS) Makefile
+ $(CC) $(LDFLAGS) -o $@ $(IFTOPDUMP_OBJS) $(LDLIBS)
-integers.h: integers
- ./integers
+#integers.h: integers
+# ./integers
install: iftop
install -D iftop $(PREFIX)/$(BINDIR)/iftop
diff --git a/Makefile.am b/Makefile.am
index 9459c3c..2eeb45c 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -10,7 +10,7 @@
# $Id$
#
-sbin_PROGRAMS = iftop
+sbin_PROGRAMS = iftop iftop-dump
iftop_SOURCES = addr_hash.c edline.c hash.c iftop.c ns_hash.c \
options.c resolver.c screenfilter.c serv_hash.c \
@@ -18,6 +18,14 @@ iftop_SOURCES = addr_hash.c edline.c hash.c iftop.c ns_hash.c \
addrs_ioctl.c addrs_dlpi.c dlcommon.c \
stringmap.c cfgfile.c vector.c
+iftop_dump_SOURCES = counter_hash.c hash.c iftop-dump.c \
+ options.c \
+ util.c \
+ addrs_ioctl.c addrs_dlpi.c dlcommon.c \
+ stringmap.c cfgfile.c
+
+
+
noinst_HEADERS = addr_hash.h ether.h ethertype.h extract.h hash.h iftop.h \
integers.h ip.h llc.h ns_hash.h options.h resolver.h \
screenfilter.h serv_hash.h sll.h sorted_list.h tcp.h \
diff --git a/configure.in b/configure.in
index 128d4c6..ec0416d 100644
--- a/configure.in
+++ b/configure.in
@@ -29,7 +29,7 @@ AC_CONFIG_AUX_DIR(config)
AC_CANONICAL_SYSTEM
AM_CONFIG_HEADER(config.h)
-AM_INIT_AUTOMAKE(iftop, "0.17pre3")
+AM_INIT_AUTOMAKE(iftop, "0.17")
AC_DEFINE_UNQUOTED(IFTOP_VERSION, "$VERSION", [The iftop version number])
diff --git a/counter_hash.c b/counter_hash.c
new file mode 100644
index 0000000..d0a275b
--- /dev/null
+++ b/counter_hash.c
@@ -0,0 +1,56 @@
+/* hash table */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <netinet/in.h>
+#include "counter_hash.h"
+#include "hash.h"
+#include "iftop.h"
+
+#define hash_table_size 256
+
+int counter_hash_compare(void* a, void* b) {
+ return *(long*)a == *(long*)b;
+}
+
+int counter_hash_hash(void* key) {
+ int hash;
+ long addr;
+
+ addr = *(long*)key;
+
+ hash = ((addr & 0x000000FF)
+ + (addr & 0x0000FF00 >> 8)
+ + (addr & 0x00FF0000 >> 16)
+ + (addr & 0xFF000000 >> 24)
+ ) % 0xFF;
+
+ return hash;
+}
+
+void* counter_hash_copy_key(void* orig) {
+ struct in_addr* copy;
+ copy = xmalloc(sizeof *copy);
+ *copy = *(struct in_addr*)orig;
+ return copy;
+}
+
+void counter_hash_delete_key(void* key) {
+ free(key);
+}
+
+/*
+ * Allocate and return a hash
+ */
+hash_type* counter_hash_create() {
+ hash_type* hash_table;
+ hash_table = xcalloc(hash_table_size, sizeof *hash_table);
+ hash_table->size = hash_table_size;
+ hash_table->compare = &counter_hash_compare;
+ hash_table->hash = &counter_hash_hash;
+ hash_table->delete_key = &counter_hash_delete_key;
+ hash_table->copy_key = &counter_hash_copy_key;
+ hash_initialise(hash_table);
+ return hash_table;
+}
+
diff --git a/counter_hash.h b/counter_hash.h
new file mode 100644
index 0000000..9041238
--- /dev/null
+++ b/counter_hash.h
@@ -0,0 +1,23 @@
+/*
+ * addr_hash.h:
+ *
+ */
+
+#ifndef __ADDR_HASH_H_ /* include guard */
+#define __ADDR_HASH_H_
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include "hash.h"
+
+typedef struct {
+ unsigned long long sent;
+ unsigned long long recv;
+} counter_type;
+
+typedef counter_type key_type; /* index into hash table */
+
+hash_type* counter_hash_create(void);
+
+#endif /* __ADDR_HASH_H_ */
diff --git a/iftop-dump.c b/iftop-dump.c
new file mode 100644
index 0000000..c8a9fff
--- /dev/null
+++ b/iftop-dump.c
@@ -0,0 +1,466 @@
+/*
+ * iftop.c:
+ *
+ */
+
+#include "integers.h"
+
+#if defined(HAVE_PCAP_H)
+# include <pcap.h>
+#elif defined(HAVE_PCAP_PCAP_H)
+# include <pcap/pcap.h>
+#else
+# error No pcap.h
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+
+#include <pthread.h>
+#include <curses.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "iftop.h"
+#include "counter_hash.h"
+#include "resolver.h"
+#include "options.h"
+#ifdef DLT_LINUX_SLL
+#include "sll.h"
+#endif /* DLT_LINUX_SLL */
+#include "threadprof.h"
+#include "ether.h"
+#include "ip.h"
+#include "tcp.h"
+#include "token.h"
+#include "llc.h"
+#include "extract.h"
+#include "ethertype.h"
+#include "cfgfile.h"
+#include "ppp.h"
+
+
+/* ethernet address of interface. */
+int have_hw_addr = 0;
+unsigned char if_hw_addr[6];
+
+/* IP address of interface */
+int have_ip_addr = 0;
+struct in_addr if_ip_addr;
+
+extern options_t options;
+
+hash_type* counters;
+time_t last_timestamp;
+pthread_mutex_t tick_mutex;
+
+pcap_t* pd; /* pcap descriptor */
+struct bpf_program pcap_filter;
+pcap_handler packet_handler;
+
+
+
+sig_atomic_t foad;
+
+static void finish(int sig) {
+ foad = sig;
+}
+
+
+
+
+
+/* Only need ethernet (plus optional 4 byte VLAN) and IP headers (48) + first 2 bytes of tcp/udp header */
+#define CAPTURE_LENGTH 72
+
+void init_counters() {
+ counters = counter_hash_create();
+ last_timestamp = time(NULL);
+}
+
+counter_type* counter_create() {
+ counter_type* c;
+ c = xcalloc(1, sizeof *c);
+ return c;
+}
+
+void tick(int print) {
+ time_t t;
+ hash_node_type * hn = NULL;
+ counter_type* n;
+
+ pthread_mutex_lock(&tick_mutex);
+
+ t = time(NULL);
+ if(t - last_timestamp >= DUMP_RESOLUTION) {
+ last_timestamp = t;
+ while(hash_next_item(counters, &hn) == HASH_STATUS_OK) {
+ n = (counter_type*)hn->rec;
+ printf("%s %lld %lld\n",inet_ntoa(*(struct in_addr*)hn->key),n->sent,n->recv);
+ }
+ }
+ else {
+ }
+
+ 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;
+}
+
+int ip_addr_match(struct in_addr addr) {
+ return addr.s_addr == if_ip_addr.s_addr;
+}
+
+
+static void handle_ip_packet(struct ip* iptr, int hw_dir)
+{
+ int direction = 0; /* incoming */
+ /*
+ union {
+ void **void_pp;
+ } u_ht = { &ht };
+ */
+ counter_type * counter;
+ int len;
+ struct in_addr local_addr;
+
+ if(options.netfilter == 0) {
+ fprintf(stderr, "netfilter option must be specified for iftop-dump\n");
+ }
+ else {
+ /*
+ * Net filter on, assign direction according to netmask
+ */
+ if(in_filter_net(iptr->ip_src) && !in_filter_net(iptr->ip_dst)) {
+ /* out of network */
+ local_addr = iptr->ip_src;
+ direction = 1;
+ }
+ else if(in_filter_net(iptr->ip_dst) && !in_filter_net(iptr->ip_src)) {
+ /* into network */
+ local_addr = iptr->ip_dst;
+ direction = 0;
+ }
+ else {
+ /* drop packet */
+ return ;
+ }
+ }
+
+ if(hash_find(counters, &local_addr, (void**)&counter) == HASH_STATUS_KEY_NOT_FOUND) {
+ counter = counter_create();
+ hash_insert(counters, &local_addr, counter);
+ }
+
+ len = ntohs(iptr->ip_len);
+
+ if(direction == 0) {
+ /* incoming */
+ counter->recv += len;
+ }
+ else {
+ counter->sent += len;
+ }
+
+}
+
+static void handle_raw_packet(unsigned char* args, const struct pcap_pkthdr* pkthdr, const unsigned char* packet)
+{
+ handle_ip_packet((struct ip*)packet, -1);
+}
+
+static void handle_llc_packet(const struct llc* llc, int dir) {
+
+ struct ip* ip = (struct ip*)((void*)llc + sizeof(struct llc));
+
+ /* Taken from tcpdump/print-llc.c */
+ if(llc->ssap == LLCSAP_SNAP && llc->dsap == LLCSAP_SNAP
+ && llc->llcui == LLC_UI) {
+ u_int32_t orgcode;
+ register u_short et;
+ orgcode = EXTRACT_24BITS(&llc->llc_orgcode[0]);
+ et = EXTRACT_16BITS(&llc->llc_ethertype[0]);
+ switch(orgcode) {
+ case OUI_ENCAP_ETHER:
+ case OUI_CISCO_90:
+ handle_ip_packet(ip, dir);
+ break;
+ case OUI_APPLETALK:
+ if(et == ETHERTYPE_ATALK) {
+ handle_ip_packet(ip, dir);
+ }
+ break;
+ default:;
+ /* Not a lot we can do */
+ }
+ }
+}
+
+static void handle_tokenring_packet(unsigned char* args, const struct pcap_pkthdr* pkthdr, const unsigned char* packet)
+{
+ struct token_header *trp;
+ int dir = -1;
+ trp = (struct token_header *)packet;
+
+ if(IS_SOURCE_ROUTED(trp)) {
+ packet += RIF_LENGTH(trp);
+ }
+ packet += TOKEN_HDRLEN;
+
+ if(memcmp(trp->token_shost, if_hw_addr, 6) == 0 ) {
+ /* packet leaving this i/f */
+ dir = 1;
+ }
+ else if(memcmp(trp->token_dhost, if_hw_addr, 6) == 0 || memcmp("\xFF\xFF\xFF\xFF\xFF\xFF", trp->token_dhost, 6) == 0) {
+ /* packet entering this i/f */
+ dir = 0;
+ }
+
+ /* Only know how to deal with LLC encapsulated packets */
+ if(FRAME_TYPE(trp) == TOKEN_FC_LLC) {
+ handle_llc_packet((struct llc*)packet, dir);
+ }
+}
+
+static void handle_ppp_packet(unsigned char* args, const struct pcap_pkthdr* pkthdr, const unsigned char* packet)
+{
+ register u_int length = pkthdr->len;
+ register u_int caplen = pkthdr->caplen;
+ u_int proto;
+
+ if (caplen < 2)
+ return;
+
+ if(packet[0] == PPP_ADDRESS) {
+ if (caplen < 4)
+ return;
+
+ packet += 2;
+ length -= 2;
+
+ proto = EXTRACT_16BITS(packet);
+ packet += 2;
+ length -= 2;
+
+ if(proto == PPP_IP || proto == ETHERTYPE_IP) {
+ handle_ip_packet((struct ip*)packet, -1);
+ }
+ }
+}
+
+#ifdef DLT_LINUX_SLL
+static void handle_cooked_packet(unsigned char *args, const struct pcap_pkthdr * thdr, const unsigned char * packet)
+{
+ struct sll_header *sptr;
+ int dir = -1;
+ sptr = (struct sll_header *) packet;
+
+ switch (ntohs(sptr->sll_pkttype))
+ {
+ case LINUX_SLL_HOST:
+ /*entering this interface*/
+ dir = 0;
+ break;
+ case LINUX_SLL_OUTGOING:
+ /*leaving this interface */
+ dir=1;
+ break;
+ }
+ handle_ip_packet((struct ip*)(packet+SLL_HDR_LEN), dir);
+}
+#endif /* DLT_LINUX_SLL */
+
+static void handle_eth_packet(unsigned char* args, const struct pcap_pkthdr* pkthdr, const unsigned char* packet)
+{
+ struct ether_header *eptr;
+ int ether_type;
+ const unsigned char *payload;
+ eptr = (struct ether_header*)packet;
+ ether_type = ntohs(eptr->ether_type);
+ payload = packet + sizeof(struct ether_header);
+
+ tick(0);
+
+ if(ether_type == ETHERTYPE_8021Q) {
+ struct vlan_8021q_header* vptr;
+ vptr = (struct vlan_8021q_header*)payload;
+ ether_type = ntohs(vptr->ether_type);
+ payload += sizeof(struct vlan_8021q_header);
+ }
+
+ if(ether_type == ETHERTYPE_IP) {
+ struct ip* iptr;
+ int dir = -1;
+
+ /*
+ * Is a direction implied by the MAC addresses?
+ */
+ if(have_hw_addr && memcmp(eptr->ether_shost, if_hw_addr, 6) == 0 ) {
+ /* packet leaving this i/f */
+ dir = 1;
+ }
+ else if(have_hw_addr && memcmp(eptr->ether_dhost, if_hw_addr, 6) == 0 ) {
+ /* packet entering this i/f */
+ dir = 0;
+ }
+ else if (memcmp("\xFF\xFF\xFF\xFF\xFF\xFF", eptr->ether_dhost, 6) == 0) {
+ /* broadcast packet, count as incoming */
+ dir = 0;
+ }
+
+ iptr = (struct ip*)(payload); /* alignment? */
+ handle_ip_packet(iptr, dir);
+ }
+}
+
+
+/* set_filter_code:
+ * Install some filter code. Returns NULL on success or an error message on
+ * failure. */
+char *set_filter_code(const char *filter) {
+ char *x;
+ if (filter) {
+ x = xmalloc(strlen(filter) + sizeof "() and ip");
+ sprintf(x, "(%s) and ip", filter);
+ } else
+ x = xstrdup("ip");
+ if (pcap_compile(pd, &pcap_filter, x, 1, 0) == -1) {
+ xfree(x);
+ return pcap_geterr(pd);
+ }
+ xfree(x);
+ if (pcap_setfilter(pd, &pcap_filter) == -1)
+ return pcap_geterr(pd);
+ else
+ return NULL;
+}
+
+
+
+/*
+ * packet_init:
+ *
+ * performs pcap initialisation, called before ui is initialised
+ */
+void packet_init() {
+ char errbuf[PCAP_ERRBUF_SIZE];
+ char *m;
+ int s;
+ int i;
+ int dlt;
+ int result;
+
+#ifdef HAVE_DLPI
+ result = get_addrs_dlpi(options.interface, if_hw_addr, &if_ip_addr);
+#else
+ result = get_addrs_ioctl(options.interface, if_hw_addr, &if_ip_addr);
+#endif
+
+ if (result < 0) {
+ exit(1);
+ }
+
+ have_hw_addr = result & 1;
+ have_ip_addr = result & 2;
+
+ if(have_ip_addr) {
+ fprintf(stderr, "IP address is: %s\n", inet_ntoa(if_ip_addr));
+ }
+
+ if(have_hw_addr) {
+ fprintf(stderr, "MAC address is:");
+ for (i = 0; i < 6; ++i)
+ fprintf(stderr, "%c%02x", i ? ':' : ' ', (unsigned int)if_hw_addr[i]);
+ fprintf(stderr, "\n");
+ }
+
+ // exit(0);
+ /* resolver_initialise(); */
+
+ pd = pcap_open_live(options.interface, CAPTURE_LENGTH, options.promiscuous, 1000, errbuf);
+ // DEBUG: pd = pcap_open_offline("tcpdump.out", errbuf);
+ if(pd == NULL) {
+ fprintf(stderr, "pcap_open_live(%s): %s\n", options.interface, errbuf);
+ exit(1);
+ }
+ dlt = pcap_datalink(pd);
+ if(dlt == DLT_EN10MB) {
+ packet_handler = handle_eth_packet;
+ }
+ else if(dlt == DLT_RAW || dlt == DLT_NULL) {
+ packet_handler = handle_raw_packet;
+ }
+ else if(dlt == DLT_IEEE802) {
+ packet_handler = handle_tokenring_packet;
+ }
+ else if(dlt == DLT_PPP) {
+ packet_handler = handle_ppp_packet;
+ }
+/*
+ * SLL support not available in older libpcaps
+ */
+#ifdef DLT_LINUX_SLL
+ else if(dlt == DLT_LINUX_SLL) {
+ packet_handler = handle_cooked_packet;
+ }
+#endif
+ else {
+ fprintf(stderr, "Unsupported datalink type: %d\n"
+ "Please email pdw@ex-parrot.com, quoting the datalink type and what you were\n"
+ "trying to do at the time\n.", dlt);
+ exit(1);
+ }
+
+ if ((m = set_filter_code(options.filtercode))) {
+ fprintf(stderr, "set_filter_code: %s\n", m);
+ exit(1);
+ return;
+ }
+}
+
+/* packet_loop: */
+void packet_loop() {
+ pcap_loop(pd,-1,(pcap_handler)packet_handler,NULL);
+}
+
+
+/* main:
+ * Entry point. See usage(). */
+int main(int argc, char **argv) {
+ pthread_t thread;
+ struct sigaction sa = {};
+
+ /* TODO: tidy this up */
+ /* read command line options and config file */
+ config_init();
+ options_set_defaults();
+ options_read_args(argc, argv);
+ /* If a config was explicitly specified, whinge if it can't be found */
+ read_config(options.config_file, options.config_file_specified);
+ options_make();
+
+ /*
+ sa.sa_handler = finish;
+ sigaction(SIGINT, &sa, NULL);
+ */
+
+ pthread_mutex_init(&tick_mutex, NULL);
+
+ packet_init();
+
+ init_counters();
+
+ packet_loop();
+
+ return 0;
+}
diff --git a/iftop.h b/iftop.h
index 42947af..80d04cf 100644
--- a/iftop.h
+++ b/iftop.h
@@ -11,6 +11,7 @@
/* 40 / 2 */
#define HISTORY_LENGTH 20
#define RESOLUTION 2
+#define DUMP_RESOLUTION 15
typedef struct {
long recv[HISTORY_LENGTH];
@@ -28,10 +29,6 @@ void *xrealloc(void *w, size_t n);
char *xstrdup(const char *s);
void xfree(void *v);
-/* ui.c */
-void analyse_data(void);
-void ui_init(void);
-
/* options.c */
void options_read(int argc, char **argv);
diff --git a/ui.h b/ui.h
index d67c8c5..1ebf90c 100644
--- a/ui.h
+++ b/ui.h
@@ -11,5 +11,7 @@ void ui_print(void);
void ui_loop(void);
void ui_finish(void);
void ui_tick(int);
+void analyse_data(void);
+void ui_init(void);
#endif /* __UI_H_ */