diff options
author | pdw <> | 2007-03-03 16:01:48 +0000 |
---|---|---|
committer | pdw <> | 2007-03-03 16:01:48 +0000 |
commit | 6458ed04f5ce51a4b0dc9059e8ddeff05e56a721 (patch) | |
tree | bd99c53cb248642b18075154baae4f3d4ff28722 /iftop-dump.c | |
parent | 86013fabd68b173dc9021829913ebcc9ce6e9722 (diff) | |
download | iftop-6458ed04f5ce51a4b0dc9059e8ddeff05e56a721.zip |
Created iftop-dump
Diffstat (limited to 'iftop-dump.c')
-rw-r--r-- | iftop-dump.c | 466 |
1 files changed, 466 insertions, 0 deletions
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; +} |