#include <arpa/inet.h>
+#include <fcntl.h>
#include <linux/if_ether.h>
#include <linux/in.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
+#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
-#include <htable.h>
+
+#include <HashVector.h>
+#include <stringitem.h>
// Seconds between outputs
-#define DELAY 5
+static int DELAY = 5;
// Byte count fade-out between displays
-#define FADE 10000
+static int FADE = 10000;
// Number of top usage to report
-#define WORST 20
+static int WORST = 20;
+
+// Drop-out age
+static int OLD = 600;
// Number of characters for text format IP holdings
#define IPBUFMAX 40
+// Count record for IP -> length mapping
typedef struct _Count {
- struct _Count *next;
- int ignore;
- int last;
- int accum;
- int total;
- char ip[ IPBUFMAX ];
+ struct _Count *next; // Next Count in time order
+ struct _Count *prev; // Previous Count in time order
+ struct timeval when; // Last update time for this Count record
+ int ignore; // Flag to leave out from reports
+ int last; // The saved accumulation from the last report period
+ int accum; // Current accumulation
+ int total; // The total accumulation (reduced by fading)
+ char ip[ IPBUFMAX ]; // The IP concerned, in ascii
} Count;
+// Print message and exit
static void die(char *m) {
fprintf( stderr, "%s\n", m );
exit( 1 );
}
-static int Count_hashcode(htable *table,unsigned char *key) {
- int value = 0;
- int i = 0;
- while ( *key ) {
- value += *(key++) + (i++);
- value += i;
- }
- return value;
+// Returns the hashcode for a key
+static unsigned long Countp_hashcode(void *this,void *key) {
+ return HashVector_hashcode( key, IPBUFMAX );
}
-static htable TBL = HTABLEINIT( Count, ip, Count_hashcode );
-static Count *last_add;
-static char buffer[ IPBUFMAX ];
+// Return pointer a key for an item (could be temporary allocation)
+static void *Countp_itemkey(void *this,void *item) {
+ return ((Count*) item)->ip;
+}
+
+// Return 1 if the item has the key, or 0 otherwise.
+static int Countp_haskey(void *this,void *item,void *key) {
+ return memcmp( key, Countp_itemkey( this, item ), IPBUFMAX ) == 0;
+}
+
+#if 0
+// Releasing a key does nothing
+static void Countp_releasekey(void *this,void *item) {
+}
+#endif
+
+static ItemKeyFun Countp_itemkeyfun = {
+ .hashcode = Countp_hashcode,
+ .haskey = Countp_haskey,
+ .itemkey = Countp_itemkey,
+ //.releasekey = Countp_releasekey,
+ //.tostring = Countp_tostring
+};
-#include "ignores.i"
+// The HashVector of seen IP
+static HashVector TBL = {
+ .table = { Nibble_index_levels, 16, 0 },
+ .fill = 0,
+ .holes = 0,
+ .type = &Countp_itemkeyfun,
+};
+// The Count records in time order
static struct {
- Count *table[ WORST ];
- int fill;
- int lowest;
-} worst;
+ Count *head;
+ Count *tail;
+} trail;
-static int Countp_compare(const void *ax, const void *bx) {
- Count *a = *(Count**) ax;
- Count *b = *(Count**) bx;
- int x = b->total - a->total;
- if ( x != 0 ) {
- if ( a->last == 0 ) {
- return ( b->last == 0 )? x : -1;
+// Temporary buffer for IP addresses in ascii
+static char buffer[ IPBUFMAX ];
+
+/*============================================================
+ * Reading ignore lines.
+ */
+#if 0
+// Return pointer to the key for an item
+static void *charp_itemkey(void *this,void *item) {
+ return item;
+}
+
+// Return 1 if the item has the key, or 0 otherwise.
+static int charp_haskey(void *this,void *item,void *key) {
+ return strcmp( key, item ) == 0;
+}
+
+// Returns the hashcode for a key
+static unsigned long charp_hashcode(void *this,void *key) {
+ return HashVector_hashcode( key, strlen( (const char *) key ) );
+}
+#endif
+
+static HashVector IGN = {
+ .table = { 256, 0 },
+ .fill = 0,
+ .holes = 0,
+ .type = &stringitem
+};
+
+static void read_ignore_file(char *filename) {
+ #define RDBLKSZ 1000000
+ static char block[ RDBLKSZ ];
+ static char *cur = block;
+ static char *end = block;
+ int fd = open( filename, O_RDONLY );
+ if ( fd < 0 ) {
+ die( "Cannot load the ignare file." );
+ }
+ for ( ;; ) {
+ char *p = cur;
+ size_t n;
+ for ( ;; ) { // move p to end of line
+ while ( p < end && *p != '\n' ) {
+ p++;
+ }
+ if ( p < end ) {
+ break;
+ }
+ if ( cur != block && cur != end ) {
+ memmove( cur, block, end - cur );
+ end -= cur - block;
+ cur = block;
+ p = end;
+ }
+ n = RDBLKSZ - ( end - cur );
+ n = read( fd, end, n );
+ if ( n <= 0 ) {
+ return; // All done
+ }
+ end += n;
}
+ // Line from cur to '\n' at p < end.
+ char *ip = calloc( 1, p - cur + 1 );
+ memcpy( ip, cur, p - cur );
+ cur = p + 1;
+ HashVector_add( &IGN, ip );
}
- x = b->last - a->last;
- return x;
}
-static void add_worst_ordered(Count *item) {
- if ( worst.fill < WORST ) {
- worst.table[ worst.fill++ ] = item;
- if ( worst.fill == WORST ) {
- qsort( worst.table, worst.fill, sizeof( Count* ), Countp_compare );
- }
- return;
+/*============================================================*/
+
+static int Countp_compare(const void *ax, const void *bx) {
+ Count *a = (Count*) ax;
+ Count *b = (Count*) bx;
+ if ( b->ignore ) {
+ return 1;
}
- Count **repl = bsearch(
- &item, worst.table, worst.fill, sizeof( Count* ), Countp_compare );
- if ( repl == 0 ) {
- return;
+ if ( a->ignore ) {
+ return -1;
+ }
+ int x = a->total - b->total;
+ if ( x ) {
+ return x;
}
- int size = (char*) &worst.table[ worst.fill - 1 ] - (char*) repl;
- if ( size > 0 ) {
- memmove( repl + 1, repl, size );
+ return a->last - b->last;
+}
+
+static int Countp_fade_and_print(VectorIndex index,void *x,void *d) {
+ if ( x ) {
+ Count *item = (Count *) x;
+ item->last = item->accum;
+ item->total += item->last - FADE;
+ item->accum = 0;
+ if ( item->total <= 0 ) {
+ item->total = 0;
+ } else if ( index < WORST && item->ignore == 0 ) {
+ fprintf( stdout, "... %s %d %d\n",
+ item->ip, item->total, item->last );
+ }
}
- *repl = item;
+ return 0;
+}
+
+static int Countp_reclaim(Vector *pv,unsigned long ix,void *item,void *data) {
+ return 0;
}
+
+// ip points to [ IPBUFMAX ] of ip address in text
static void add_show_table(char *ip,size_t length) {
static time_t show = 0;
- Count *item;
- int i = htfind( &TBL, ip, (unsigned char **) &item );
- if ( i == 0 ) {
- item = (Count *) calloc( 1, sizeof( Count ) );
- memcpy( item->ip, ip, strlen( ip ) );
- item->accum = length;
- item->ignore = ignored( ip );
- htadd( &TBL, (unsigned char *) item );
- item->next = last_add;
- last_add = item;
- } else {
- item->accum += length;
- }
+ Count *item = HashVector_find( &TBL, ip );
struct timeval now;
if ( gettimeofday( &now, 0 ) ) {
perror( "gettimeofday" );
exit( 1 );
}
+ if ( item == 0 ) {
+ item = (Count *) calloc( 1, sizeof( Count ) );
+ memcpy( item->ip, ip, strlen( ip ) );
+ HashVector_add( &TBL, item );
+ item->ignore = (HashVector_find( &IGN, ip ) != 0);
+ int i;
+ for ( i = strlen( ip )-1; i > 1; i-- ) {
+ if ( ip[i] == '.' || ip[i] == ':' ) {
+ item->ignore |= (HashVector_find( &IGN, ip ) != 0);
+ }
+ ip[i] = 0;
+ }
+ fprintf( stdout, "add %s\n", item->ip );
+ } else {
+ // Unlink item from the trail
+ if ( item->next ) {
+ item->next->prev = item->prev;
+ }
+ if ( item->prev ) {
+ item->prev->next = item->next;
+ }
+ if ( trail.head == item ) {
+ trail.head = item->next;
+ }
+ if ( trail.tail == item ) {
+ trail.tail = item->prev;
+ }
+ item->prev = item->next = 0;
+ }
+ item->accum += length;
+ item->when = now;
+ // Link in item to the trail end
+ if ( trail.head == 0 ) {
+ trail.head = item;
+ } else {
+ trail.tail->next = item;
+ item->prev = trail.tail;
+ }
+ trail.tail = item;
+ // Drop counters older than an hour
+ while ( trail.head->when.tv_sec + OLD < item->when.tv_sec ) {
+ Count *old = trail.head;
+ trail.head = old->next;
+ if ( trail.head ) {
+ trail.head->prev = 0;
+ }
+ fprintf( stdout, "drop %s\n", old->ip );
+ HashVector_delete( &TBL, old );
+ free( old );
+ }
if ( now.tv_sec < show ) {
return;
}
if ( now.tv_sec - show > DELAY ) {
show = now.tv_sec;
}
- show += 5; // Time for next output
- // collate entries; Keep the X worst entries, but reduce all a bit
- worst.fill = 0;
- item = last_add;
- for ( ; item; item = item->next ) {
- item->last = item->accum;
- item->total += item->last - FADE;
- if ( item->total < 0 ) {
- item->total = 0;
- }
- item->accum = 0;
- if ( item->ignore == 0 ) {
- add_worst_ordered( item );
- }
- }
- fprintf( stdout, "\e[2J" );
- for ( i = 0; i < worst.fill; i++ ) {
- item = worst.table[ i ];
- if ( item->total && item->last ) {
- fprintf( stdout, "... %s %d %d\n",
- item->ip, item->total, item->last );
- }
- }
- fprintf( stdout, "==%d/%d/%d\n", TBL.fill, TBL.holes,TBL.size );
+ show += DELAY; // Time for next output
+ Vector ordered = { Nibble_index_levels, 0, 0 };
+ HashVector_contents( &TBL, Nibble_index_levels, &ordered );
+ Vector_qsort( &ordered, Countp_compare );
+ Vector_iterate( &ordered, 0, Countp_fade_and_print, 0 );
+ Vector_resize( &ordered, 0, Countp_reclaim, 0 );
+ fprintf( stdout, "==%ld/%ld/%ld\n", TBL.fill, TBL.holes, TBL.table.size );
}
static char *ipv4_address(char *b) {
int main(int argc,char **argv) {
static char packet[ 2048 ];
- if ( argc != 2 ) {
+ int ARG = 1;
+ // Check for -fN to set FADE
+ if ( ARG < argc && strncmp( argv[ ARG ], "-d", 2 ) == 0 ) {
+ if ( sscanf( argv[ ARG ]+2, "%d", &DELAY ) != 1 ) {
+ die( "Missing/bad delay value" );
+ }
+ fprintf( stdout, "Delay is %d seconds between reports\n", DELAY );
+ ARG++;
+ }
+ if ( ARG < argc && strncmp( argv[ ARG ], "-f", 2 ) == 0 ) {
+ if ( sscanf( argv[ ARG ]+2, "%d", &FADE ) != 1 ) {
+ die( "Missing/bad fade value" );
+ }
+ fprintf( stdout, "Fading %d bytes before reports\n", FADE );
+ ARG++;
+ }
+ if ( ARG < argc && strncmp( argv[ ARG ], "-n", 2 ) == 0 ) {
+ if ( sscanf( argv[ ARG ]+2, "%d", &WORST ) != 1 ) {
+ die( "Missing/bad number to display" );
+ }
+ fprintf( stdout, "Displaying at most %d lines in reports\n", WORST );
+ ARG++;
+ }
+ if ( ARG < argc && strncmp( argv[ ARG ], "-a", 2 ) == 0 ) {
+ if ( sscanf( argv[ ARG ]+2, "%d", &OLD ) != 1 ) {
+ die( "Missing/bad drop-out age (seconds)" );
+ }
+ fprintf( stdout, "Displaying at most %d lines in reports\n", WORST );
+ ARG++;
+ }
+ if ( ARG < argc && strncmp( argv[ ARG ], "-i", 2 ) == 0 ) {
+ char *filename = argv[ ARG ] + 2;
+ if ( (*filename) == 0 ) {
+ die( "Missing/bad ignore filename" );
+ }
+ read_ignore_file( filename );
+ fprintf( stdout, "ignoring ip prefixes from %s\n", filename );
+ ARG++;
+ }
+ if ( ARG >= argc ) {
die( "Please tell which interface to sniff" );
}
setbuf( stdout, 0 );
int N;
- char *iface = argv[ argc - 1 ];
+ char *iface = argv[ ARG ];
int fd = socket( AF_PACKET, SOCK_RAW, htons( ETH_P_ALL ) );
char flags[4] = { 1,0,0,0 };
if ( fd < 0 ) {
int code = ntohs( *((short*)(packet+12)) );
if ( code == 0x0800 ) {
// 14+12=src 14+16=dst
- add_show_table( ipv4_address( packet+30 ), N );
+ char *p = ipv4_address( packet+30 );
+ if ( ( strncmp( p, "127.", 4 ) != 0 ) ) {
+ add_show_table( p, N );
+ }
} else if ( code == 0x86dd ) {
// 14+8=src 14+24=dst
- add_show_table( ipv6_address( (short*)(packet+38) ), N );
- } else if ( code == 0x0800 ) {
- // ignore
+ char *p = ipv6_address( (short*)(packet+38) );
+ if ( ( strncmp( p, "ff02:0:0:0:0:", 13 ) != 0 ) &&
+ ( strncmp( p, "0:0:0:0:0:0:0:1", 15 ) != 0 ) ) {
+ add_show_table( p, N );
+ }
} else if ( code == 0x8100 ) {
- // ignore
+ // ignore VLAN
} else {
// funny code
}