/* * iftop.c: * */ #include #include #include #include #include #include #include #include #include #include #include #include #include "iftop.h" #include "addr_hash.h" #include "resolver.h" #include "ui.h" #include "options.h" 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; sig_atomic_t foad; static void finish(int sig) { foad = sig; } /* Only need ethernet and IP headers. */ #define CAPTURE_LENGTH 48 void init_history() { history = addr_hash_create(); last_timestamp = time(NULL); memset(&history_totals, 0, sizeof history_totals); } history_type* history_create() { history_type* h; h = xcalloc(1, sizeof *h); return h; } void history_rotate() { hash_node_type* n = NULL; history_pos = (history_pos + 1) % HISTORY_LENGTH; hash_next_item(history, &n); while(n != NULL) { hash_node_type* next = n; history_type* d = (history_type*)n->rec; hash_next_item(history, &next); if(d->last_write == history_pos) { addr_pair key = *(addr_pair*)(n->key); hash_delete(history, &key); free(d); } else { d->recv[history_pos] = 0; d->sent[history_pos] = 0; } n = next; } history_totals.sent[history_pos] = 0; history_totals.recv[history_pos] = 0; if(history_len < HISTORY_LENGTH) { history_len++; } } void tick() { time_t t; pthread_mutex_lock(&tick_mutex); t = time(NULL); if(t - last_timestamp >= RESOLUTION) { //printf("TICKING\n"); ui_print(); history_rotate(); last_timestamp = t; } 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 len; iptr = (struct ip*)(packet + sizeof(struct ether_header)); /* alignment? */ 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 { /* * 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 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(); hash_insert(history, &ap, ht); } len = ntohs(iptr->ip_len); /* Update record */ ht->last_write = history_pos; if(iptr->ip_src.s_addr == ap.src.s_addr) { ht->sent[history_pos] += len; ht->total_sent += len; } else { ht->recv[history_pos] += len; ht->total_recv += len; } if(direction == 0) { /* incoming */ history_totals.recv[history_pos] += ntohs(iptr->ip_len); history_totals.total_recv += len; } else { history_totals.sent[history_pos] += ntohs(iptr->ip_len); history_totals.total_sent += len; } } } /* packet_loop: * Worker function for packet capture thread. */ void packet_loop(void* ptr) { char errbuf[PCAP_ERRBUF_SIZE]; char* str = "ip"; pcap_t* pd; struct bpf_program F; int s; struct ifreq ifr = {0}; /* First, get the address of the interface. If it isn't an ethernet * interface whose address we can obtain, there's not a lot we can do. */ s = socket(PF_INET, SOCK_DGRAM, 0); /* any sort of IP socket will do */ if (s == -1) { perror("socket"); foad = 1; return; } strncpy(ifr.ifr_name, options.interface, IFNAMSIZ); ifr.ifr_hwaddr.sa_family = AF_UNSPEC; if (ioctl(s, SIOCGIFHWADDR, &ifr) == -1) { perror("ioctl(SIOCGIFHWADDR)"); foad = 1; return; } close(s); memcpy(if_hw_addr, ifr.ifr_hwaddr.sa_data, 6); fprintf(stderr, "MAC address is:"); for (s = 0; s < 6; ++s) fprintf(stderr, "%c%02x", s ? ':' : ' ', (unsigned int)if_hw_addr[s]); fprintf(stderr, "\n"); resolver_initialise(); pd = pcap_open_live(options.interface, CAPTURE_LENGTH, options.promiscuous, 1000, errbuf); if(pd == NULL) { fprintf(stderr, "pcap_open_live(%s): %s\n", options.interface, errbuf); foad = 1; return; } 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)); foad = 1; return; } if (pcap_setfilter(pd, &F) == -1) { fprintf(stderr, "pcap_setfilter: %s\n", pcap_geterr(pd)); foad = 1; return; } if (options.filtercode) xfree(str); printf("Begin loop\n"); pcap_loop(pd,0,(pcap_handler)handle_packet,NULL); printf("end loop\n"); } /* main: * Entry point. See usage(). */ int main(int argc, char **argv) { pthread_t thread; struct sigaction sa = {0}; options_read(argc, argv); sa.sa_handler = finish; sigaction(SIGINT, &sa, NULL); pthread_mutex_init(&tick_mutex, NULL); init_history(); pthread_create(&thread, NULL, (void*)&packet_loop, NULL); ui_loop(); pthread_cancel(thread); ui_finish(); return 0; }