Commit 3004ea06 by pdw

iftop

0 parents
No preview for this file type
No preview for this file type
# Makefile:
#CC = gcc
CFLAGS += -g -Wall -I/usr/local/include
LDFLAGS += -g
LDLIBS += -L/usr/local/lib -lpcap -lpthread -lcurses
SRCS = iftop.c \
addr_hash.c \
hash.c \
ns_hash.c \
resolver.c \
ui.c \
sorted_list.c
OBJS = $(SRCS:.c=.o)
HDRS = addr_hash.h
# If you do not have makedepend, you will need to remove references to depend
# and nodepend below.
iftop: depend $(OBJS) Makefile
$(CC) $(LDFLAGS) -o $@ $(OBJS) $(LDLIBS)
%.o: %.c Makefile
$(CC) $(CFLAGS) -c -o $@ $<
clean: nodepend
rm -f *~ *.o core iftop
tags :
etags *.c *.h
depend:
makedepend -- $(CFLAGS) -- $(SRCS)
touch depend
nodepend:
makedepend -- --
rm -f depend
# DO NOT DELETE
/* hash table */
#include <stdio.h>
#include <stdlib.h>
#include "addr_hash.h"
#include "hash.h"
#define hash_table_size 256
int compare(void* a, void* b) {
addr_pair* aa = (addr_pair*)a;
addr_pair* bb = (addr_pair*)b;
return (aa->src.s_addr == bb->src.s_addr
&& aa->dst.s_addr == bb->dst.s_addr);
}
int hash(void* key) {
int hash;
long addr;
addr_pair* ap = (addr_pair*)key;
addr = (long)ap->src.s_addr;
hash = ((addr & 0x000000FF)
+ (addr & 0x0000FF00 >> 8)
+ (addr & 0x00FF0000 >> 16)
+ (addr & 0xFF000000 >> 24)) % 0xFF;
addr = (long)ap->dst.s_addr;
hash += ((addr & 0x000000FF)
+ (addr & 0x0000FF00 >> 8)
+ (addr & 0x00FF0000 >> 16)
+ (addr & 0xFF000000 >> 24)) % 0xFF;
return hash;
}
void* copy_key(void* orig) {
addr_pair* copy = malloc(sizeof(addr_pair));
if(copy == NULL) {
printf("Out of memory\n");
exit(1);
}
*copy = *(addr_pair*)orig;
return copy;
}
void delete_key(void* key) {
free(key);
}
/*
* Allocate and return a hash
*/
hash_type* addr_hash_create() {
hash_type* hash_table;
if ((hash_table = calloc(hash_table_size, sizeof(hash_type*))) == 0) {
fprintf (stderr, "out of memory (hashTable)\n");
exit(1);
}
hash_table->size = hash_table_size;
hash_table->compare = &compare;
hash_table->hash = &hash;
hash_table->delete_key = &delete_key;
hash_table->copy_key = &copy_key;
hash_initialise(hash_table);
return hash_table;
}
/*
* addr_hash.h:
*
*/
#ifndef __ADDR_HASH_H_ /* include guard */
#define __ADDR_HASH_H_
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "hash.h"
typedef struct {
struct in_addr src;
struct in_addr dst;
} addr_pair;
typedef addr_pair key_type; /* index into hash table */
hash_type* addr_hash_create();
#endif /* __ADDR_HASH_H_ */
/* hash table */
#include <stdio.h>
#include <stdlib.h>
#include "hash.h"
hash_status_enum hash_insert(hash_type* hash_table, void* key, void* rec) {
hash_node_type *p, *p0;
int bucket;
/************************************************
* allocate node for data and insert in table *
************************************************/
/* insert node at beginning of list */
bucket = hash_table->hash(key);
if ((p = malloc(sizeof(hash_node_type))) == 0)
return HASH_STATUS_MEM_EXHAUSTED;
p0 = hash_table->table[bucket];
hash_table->table[bucket] = p;
p->next = p0;
p->key = hash_table->copy_key(key);
p->rec = rec;
return HASH_STATUS_OK;
}
hash_status_enum hash_delete(hash_type* hash_table, void* key) {
hash_node_type *p0, *p;
int bucket;
/********************************************
* delete node containing data from table *
********************************************/
/* find node */
p0 = 0;
bucket = hash_table->hash(key);
p = hash_table->table[bucket];
while (p && !hash_table->compare(p->key, key)) {
p0 = p;
p = p->next;
}
if (!p) return HASH_STATUS_KEY_NOT_FOUND;
/* p designates node to delete, remove it from list */
if (p0)
/* not first node, p0 points to previous node */
p0->next = p->next;
else
/* first node on chain */
hash_table->table[bucket] = p->next;
hash_table->delete_key(p->key);
free (p);
return HASH_STATUS_OK;
}
hash_status_enum hash_find(hash_type* hash_table, void* key, void **rec) {
hash_node_type *p;
/*******************************
* find node containing data *
*******************************/
p = hash_table->table[hash_table->hash(key)];
while (p && !hash_table->compare(p->key, key))
p = p->next;
if (!p) return HASH_STATUS_KEY_NOT_FOUND;
*rec = p->rec;
return HASH_STATUS_OK;
}
hash_status_enum hash_next_item(hash_type* hash_table, hash_node_type** ppnode) {
int i;
if(*ppnode != NULL) {
if((*ppnode)->next != NULL) {
*ppnode = (*ppnode)->next;
return HASH_STATUS_OK;
}
i = hash_table->hash((*ppnode)->key) + 1;
}
else {
/* first node */
i = 0;
}
while(i < hash_table->size && hash_table->table[i] == NULL) {
i++;
}
if(i == hash_table->size) {
*ppnode = NULL;
return HASH_STATUS_KEY_NOT_FOUND;
}
*ppnode = hash_table->table[i];
return HASH_STATUS_OK;
}
/*
* Allocate and return a hash
*/
hash_status_enum hash_initialise(hash_type* hash_table) {
if ((hash_table->table = calloc(hash_table->size, sizeof(hash_node_type *))) == 0) {
fprintf (stderr, "out of memory (hash_table)\n");
return HASH_STATUS_MEM_EXHAUSTED;
}
return HASH_STATUS_OK;
}
hash_status_enum hash_destroy(hash_type* hash_table) {
free(hash_table->table);
return HASH_STATUS_OK;
}
/*
* addr_hash.h:
*
*/
#ifndef __HASH_H_ /* include guard */
#define __HASH_H_
/* implementation independent declarations */
typedef enum {
HASH_STATUS_OK,
HASH_STATUS_MEM_EXHAUSTED,
HASH_STATUS_KEY_NOT_FOUND
} hash_status_enum;
typedef struct node_tag {
struct node_tag *next; /* next node */
void* key; /* key */
void* rec; /* user data */
} hash_node_type;
typedef struct {
int (*compare) (void*, void*);
int (*hash) (void*);
void* (*copy_key) (void*);
void (*delete_key) (void*);
hash_node_type** table;
int size;
} hash_type;
hash_status_enum hash_initialise(hash_type*);
hash_status_enum hash_destroy(hash_type*);
hash_status_enum hash_insert(hash_type*, void* key, void *rec);
hash_status_enum hash_delete(hash_type* hash_table, void* key);
hash_status_enum hash_find(hash_type* hash_table, void* key, void** rec);
hash_status_enum hash_next_item(hash_type* hash_table, hash_node_type** ppnode);
#endif /* __HASH_H_ */
/*
* iftop.c:
*
*/
#include <pcap.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <net/ethernet.h>
#include <netinet/ip.h>
#include <pthread.h>
#include <curses.h>
#include <signal.h>
#include <string.h>
#include "iftop.h"
#include "addr_hash.h"
#include "resolver.h"
#include "ui.h"
hash_type* history;
time_t last_timestamp;
int history_pos = 0;
int history_len = 1;
pthread_mutex_t tick_mutex;
#define CAPTURE_LENGTH 512
void init_history() {
history = addr_hash_create();
last_timestamp = time(NULL);
}
history_type* history_create() {
history_type* h;
if ((h = calloc(1, sizeof(history_type))) == 0) {
fprintf (stderr, "out of memory (history_type)\n");
exit(1);
}
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);
}
else {
d->recv[history_pos] = 0;
d->sent[history_pos] = 0;
}
n = next;
}
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);
}
static void handle_packet(char* args, const struct pcap_pkthdr* pkthdr,const char* packet)
{
struct ether_header *eptr;
eptr = (struct ether_header*)packet;
tick();
if(ntohs(eptr->ether_type) == ETHERTYPE_IP) {
struct ip* iptr = (struct ip*)(packet + sizeof(struct ether_header));
history_type* ht;
addr_pair ap;
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;
}
/* Add the address to be resolved */
resolve(&iptr->ip_dst, NULL, 0);
if(hash_find(history, &ap, (void**)&ht) == HASH_STATUS_KEY_NOT_FOUND) {
ht = history_create();
hash_insert(history, &ap, ht);
}
/* Update record */
ht->last_write = history_pos;
if(iptr->ip_src.s_addr < iptr->ip_dst.s_addr) {
ht->recv[history_pos] += ntohs(iptr->ip_len);
}
else {
ht->sent[history_pos] += ntohs(iptr->ip_len);
}
}
fflush(stdout);
}
/*
* packet capture thread
*/
void packet_loop(void* ptr) {
char errbuf[PCAP_ERRBUF_SIZE];
char* device;
pcap_t* pd;
resolver_initialise();
device = pcap_lookupdev(errbuf);
printf("Device: %s\n",device);
pd = pcap_open_live(device,CAPTURE_LENGTH,1,1000,errbuf);
if(pd == NULL) {
printf("pcap_open_live(): %s\n",errbuf);
exit(1);
}
printf("Begin loop\n");
init_history();
pcap_loop(pd,0,(pcap_handler)handle_packet,NULL);
printf("end loop\n");
}
static void finish(int sig)
{
ui_finish();
exit(0);
}
int main(int argc, char **argv) {
pthread_t thread;
(void) signal(SIGINT, finish); /* arrange interrupts to terminate */
pthread_mutex_init(&tick_mutex, NULL);
pthread_create(&thread, NULL, (void*)&packet_loop, NULL);
ui_loop();
return 0;
}
/*
* iftop.h:
*
*/
#ifndef __IFTOP_H_ /* include guard */
#define __IFTOP_H_
/* 5 * 60 / 3 */
#define HISTORY_LENGTH 100
#define RESOLUTION 3
typedef struct {
long recv[HISTORY_LENGTH];
long sent[HISTORY_LENGTH];
int last_write;
} history_type;
void tick();
#endif /* __IFTOP_H_ */
/* hash table */
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "ns_hash.h"
#include "hash.h"
#define hash_table_size 256
int ns_hash_compare(void* a, void* b) {
struct in_addr* aa = (struct in_addr*)a;
struct in_addr* bb = (struct in_addr*)b;
return (aa->s_addr == bb->s_addr);
}
int ns_hash_hash(void* key) {
int hash;
long addr;
addr = (long)((struct in_addr*)key)->s_addr;
hash = ((addr & 0x000000FF)
+ (addr & 0x0000FF00 >> 8)
+ (addr & 0x00FF0000 >> 16)
+ (addr & 0xFF000000 >> 24)) % 0xFF;
return hash;
}
void* ns_hash_copy_key(void* orig) {
struct in_addr* copy = malloc(sizeof(struct in_addr));
if(copy == NULL) {
printf("Out of memory\n");
exit(1);
}
*copy = *(struct in_addr*)orig;
return copy;
}
void ns_hash_delete_key(void* key) {
free(key);
}
/*
* Allocate and return a hash
*/
hash_type* ns_hash_create() {
hash_type* hash_table;
if ((hash_table = calloc(hash_table_size, sizeof(hash_type*))) == 0) {
fprintf (stderr, "out of memory (hashTable)\n");
exit(1);
}
hash_table->size = hash_table_size;
hash_table->compare = &ns_hash_compare;
hash_table->hash = &ns_hash_hash;
hash_table->delete_key = &ns_hash_delete_key;
hash_table->copy_key = &ns_hash_copy_key;
hash_initialise(hash_table);
return hash_table;
}
/*
* ns_hash.h:
*
* Copyright (c) 2002 DecisionSoft Ltd.
*
*/
#ifndef __NS_HASH_H_ /* include guard */
#define __NS_HASH_H_
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "hash.h"
hash_type* ns_hash_create();
#endif /* __NS_HASH_H_ */
/*
* resolver.c:
*
*/
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <errno.h>
#include <string.h>
#include "ns_hash.h"
#define RESOLVE_QUEUE_LENGTH 20
struct in_addr resolve_queue[RESOLVE_QUEUE_LENGTH];
pthread_cond_t resolver_queue_cond;
pthread_mutex_t resolver_queue_mutex;
pthread_mutex_t resolver_queue_access_mutex;
hash_type* ns_hash;
int head;
int tail;
void resolver_worker(void* ptr) {
struct timespec delay;
delay.tv_sec = 0;
delay.tv_nsec = 500;
while(1) {
/* Wait until we are told that an address has been added to the
* queue
*/
pthread_cond_wait(&resolver_queue_cond, &resolver_queue_mutex);
/* Keep resolving until the queue is empty */
pthread_mutex_lock(&resolver_queue_access_mutex);
while(head != tail) {
struct in_addr addr = resolve_queue[tail];
struct hostent hostbuf, *hp;
size_t hstbuflen;
char *tmphstbuf;
int res;
int herr;
/* mutex always locked at this point */
tail = (tail + 1) % RESOLVE_QUEUE_LENGTH;
pthread_mutex_unlock(&resolver_queue_access_mutex);
hstbuflen = 1024;
/* Allocate buffer, remember to free it to avoid memory leakage. */
tmphstbuf = malloc (hstbuflen);
while ((res = gethostbyaddr_r (&addr, sizeof(addr), AF_INET,
&hostbuf, tmphstbuf, hstbuflen,
&hp, &herr)) == ERANGE) {
/* Enlarge the buffer. */
hstbuflen *= 2;
tmphstbuf = realloc (tmphstbuf, hstbuflen);
}
/*
* Store the result in ns_hash
*/
pthread_mutex_lock(&resolver_queue_access_mutex);
/* 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);
free(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);
}
free(tmphstbuf);
}
pthread_mutex_unlock(&resolver_queue_access_mutex);
}
}
void resolver_initialise() {
pthread_t thread;
head = tail = 0;
ns_hash = ns_hash_create();
pthread_mutex_init(&resolver_queue_mutex, NULL);
pthread_mutex_init(&resolver_queue_access_mutex, NULL);
pthread_cond_init(&resolver_queue_cond, NULL);
pthread_create(&thread, NULL, (void*)&resolver_worker, NULL);
}
void resolve(struct in_addr* addr, char* result, int buflen) {
char* hostname;
pthread_mutex_lock(&resolver_queue_access_mutex);
if(hash_find(ns_hash, addr, (void**)&hostname) == HASH_STATUS_OK) {
/* Found => already resolved, or on the queue */
}
else {
hostname = strdup(inet_ntoa(*addr));
hash_insert(ns_hash, addr, hostname);
if(((head + 1) % RESOLVE_QUEUE_LENGTH) == tail) {
/* queue full */
}
else {
resolve_queue[head] = *addr;
head = (head + 1) % RESOLVE_QUEUE_LENGTH;
pthread_cond_signal(&resolver_queue_cond);
}
}
pthread_mutex_unlock(&resolver_queue_access_mutex);
if(result != NULL && buflen > 1) {
strncpy(result, hostname, buflen - 1);
result[buflen - 1] = '\0';
}
}
/*
* resolver.h:
*
*/
#ifndef __RESOLVER_H_ /* include guard */
#define __RESOLVER_H_
#include <netinet/in.h>
void resolver_initialise();
void resolve(struct in_addr* addr, char* result, int buflen);
#endif /* __RESOLVER_H_ */
/*
* sorted_list.c:
*
*/
#include <stdlib.h>
#include <stdio.h>
#include "sorted_list.h"
void sorted_list_insert(sorted_list_type* list, void* item) {
sorted_list_node *node, *p;
p = &(list->root);
while(p->next != NULL && list->compare(item, p->next->data) > 0) {
p = p->next;
}
node = (sorted_list_node*)malloc(sizeof(sorted_list_node));
if(node == NULL) {
fprintf(stderr,"Out of memory\n");
exit(1);
}
node->next = p->next;
node->data = item;
p->next = node;
}
sorted_list_node* sorted_list_next_item(sorted_list_type* list, sorted_list_node* prev) {
if(prev == NULL) {
return list->root.next;
}
else {
return prev->next;
}
}
void sorted_list_destroy(sorted_list_type* list) {
sorted_list_node *p, *n;
p = list->root.next;
while(p != NULL) {
n = p->next;
free(p);
p = n;
}
list->root.next = NULL;
}
void sorted_list_initialise(sorted_list_type* list) {
list->root.next = NULL;
}
/*
* sorted_list.h:
*
*/
#ifndef __SORTED_LIST_H_ /* include guard */
#define __SORTED_LIST_H_
typedef struct sorted_list_node_tag {
struct sorted_list_node_tag* next;
void* data;
} sorted_list_node;
typedef struct {
sorted_list_node root;
int (*compare)(void*, void*);
} sorted_list_type;
void sorted_list_initialise(sorted_list_type* list);
void sorted_list_insert(sorted_list_type* list, void* item);
sorted_list_node* sorted_list_next_item(sorted_list_type* list, sorted_list_node* prev);
void sorted_list_destroy(sorted_list_type* list);
#endif /* __SORTED_LIST_H_ */
/*
* ui.c:
*
*/
#include <curses.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>
#include "addr_hash.h"
#include "iftop.h"
#include "resolver.h"
#include "sorted_list.h"
#define HOSTNAME_LENGTH 20
#define HISTORY_DIVISIONS 3
int history_divs[HISTORY_DIVISIONS] = {3, 20, 40};
typedef struct host_pair_line_tag {
addr_pair* ap;
long recv[HISTORY_DIVISIONS];
long sent[HISTORY_DIVISIONS];
} host_pair_line;
extern hash_type* history;
extern int history_pos;
extern int history_len;
int screen_line_compare(void* a, void* b) {
host_pair_line* aa = (host_pair_line*)a;
host_pair_line* bb = (host_pair_line*)b;
return(aa->recv[0] + aa->sent[0] < bb->recv[0] + bb->sent[0]);
}
void readable_size(float n, char* buf, int bsize) {
if(n >= 102400) {
snprintf(buf, bsize, " %4.1fM", n / (1024 * 1024));
}
else if(n >= 1024) {
snprintf(buf, bsize, " %4.1fK", n / 1024);
}
else {
snprintf(buf, bsize, " %4.0fb", n );
}
}
void ui_print() {
hash_node_type* n = NULL;
sorted_list_node* nn = NULL;
char hostname[HOSTNAME_LENGTH];
char line[80]; // FIXME
int y = 2;
sorted_list_type screen_list;
screen_list.compare = &screen_line_compare;
sorted_list_initialise(&screen_list);
erase();
while(hash_next_item(history, &n) == HASH_STATUS_OK) {
history_type* d = (history_type*)n->rec;
host_pair_line* screen_line;
int i;
screen_line = (host_pair_line*)calloc(1,sizeof(host_pair_line));
screen_line->ap = (addr_pair*)n->key;
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]) {
screen_line->recv[j] += d->recv[ii];
screen_line->sent[j] += d->sent[ii];
}
}
}
sorted_list_insert(&screen_list, screen_line);
}
while((nn = sorted_list_next_item(&screen_list, nn)) != NULL) {
int x = 0;
int j;
host_pair_line* screen_line = (host_pair_line*)nn->data;
resolve(&screen_line->ap->src, hostname, HOSTNAME_LENGTH);
sprintf(line, "%s ", hostname);
mvaddstr(y, x, line);
x += 20;
resolve(&screen_line->ap->dst, hostname, HOSTNAME_LENGTH);
sprintf(line ,"=> %10s ", hostname);
mvaddstr(y, x, line);
x += 24;
for(j = 0; j < HISTORY_DIVISIONS; j++) {
int t;
if(history_len < history_divs[j]) {
t = history_len * RESOLUTION;
}
else {
t = history_divs[j] * RESOLUTION;
}
readable_size(screen_line->sent[j] / t, line, 10);
mvaddstr(y, x, line);
x += strlen(line);