3 #include <linux/if_ether.h>
9 #include <sys/socket.h>
12 #include <sys/types.h>
15 #include <HashVector.h>
16 #include <stringitem.h>
18 // Seconds between outputs
21 // Byte count fade-out between displays
22 static int FADE = 10000;
24 // Number of top usage to report
25 static int WORST = 20;
30 // Number of characters for text format IP holdings
33 // Count record for IP -> length mapping
34 typedef struct _Count {
35 struct _Count *next; // Next Count in time order
36 struct _Count *prev; // Previous Count in time order
37 struct timeval when; // Last update time for this Count record
38 int ignore; // Flag to leave out from reports
39 int last; // The saved accumulation from the last report period
40 int accum; // Current accumulation
41 int total; // The total accumulation (reduced by fading)
42 char ip[ IPBUFMAX ]; // The IP concerned, in ascii
45 // Print message and exit
46 static void die(char *m) {
47 fprintf( stderr, "%s\n", m );
51 // Returns the hashcode for a key
52 static unsigned long Countp_hashcode(void *this,void *key) {
53 return HashVector_hashcode( key, IPBUFMAX );
56 // Return pointer a key for an item (could be temporary allocation)
57 static void *Countp_itemkey(void *this,void *item) {
58 return ((Count*) item)->ip;
61 // Return 1 if the item has the key, or 0 otherwise.
62 static int Countp_haskey(void *this,void *item,void *key) {
63 return memcmp( key, Countp_itemkey( this, item ), IPBUFMAX ) == 0;
67 // Releasing a key does nothing
68 static void Countp_releasekey(void *this,void *item) {
72 static ItemKeyFun Countp_itemkeyfun = {
73 .hashcode = Countp_hashcode,
74 .haskey = Countp_haskey,
75 .itemkey = Countp_itemkey,
76 //.releasekey = Countp_releasekey,
77 //.tostring = Countp_tostring
80 // The HashVector of seen IP
81 static HashVector TBL = {
82 .table = { Nibble_index_levels, 16, 0 },
85 .type = &Countp_itemkeyfun,
88 // The Count records in time order
94 // Temporary buffer for IP addresses in ascii
95 static char buffer[ IPBUFMAX ];
97 /*============================================================
98 * Reading ignore lines.
101 // Return pointer to the key for an item
102 static void *charp_itemkey(void *this,void *item) {
106 // Return 1 if the item has the key, or 0 otherwise.
107 static int charp_haskey(void *this,void *item,void *key) {
108 return strcmp( key, item ) == 0;
111 // Returns the hashcode for a key
112 static unsigned long charp_hashcode(void *this,void *key) {
113 return HashVector_hashcode( key, strlen( (const char *) key ) );
117 static HashVector IGN = {
124 static void read_ignore_file(char *filename) {
125 #define RDBLKSZ 1000000
126 static char block[ RDBLKSZ ];
127 static char *cur = block;
128 static char *end = block;
129 int fd = open( filename, O_RDONLY );
131 die( "Cannot load the ignare file." );
136 for ( ;; ) { // move p to end of line
137 while ( p < end && *p != '\n' ) {
143 if ( cur != block && cur != end ) {
144 memmove( cur, block, end - cur );
149 n = RDBLKSZ - ( end - cur );
150 n = read( fd, end, n );
156 // Line from cur to '\n' at p < end.
157 char *ip = calloc( 1, p - cur + 1 );
158 memcpy( ip, cur, p - cur );
160 HashVector_add( &IGN, ip );
164 /*============================================================*/
166 static int Countp_compare(const void *ax, const void *bx) {
167 Count *a = (Count*) ax;
168 Count *b = (Count*) bx;
175 int x = a->total - b->total;
179 return a->last - b->last;
182 static int Countp_fade_and_print(VectorIndex index,void *x,void *d) {
184 Count *item = (Count *) x;
185 item->last = item->accum;
186 item->total += item->last - FADE;
188 if ( item->total <= 0 ) {
190 } else if ( index < WORST && item->ignore == 0 ) {
191 fprintf( stdout, "... %s %d %d\n",
192 item->ip, item->total, item->last );
198 static int Countp_reclaim(Vector *pv,unsigned long ix,void *item,void *data) {
203 // ip points to [ IPBUFMAX ] of ip address in text
204 static void add_show_table(char *ip,size_t length) {
205 static time_t show = 0;
206 Count *item = HashVector_find( &TBL, ip );
208 if ( gettimeofday( &now, 0 ) ) {
209 perror( "gettimeofday" );
213 item = (Count *) calloc( 1, sizeof( Count ) );
214 memcpy( item->ip, ip, strlen( ip ) );
215 HashVector_add( &TBL, item );
216 item->ignore = (HashVector_find( &IGN, ip ) != 0);
218 for ( i = strlen( ip )-1; i > 1; i-- ) {
219 if ( ip[i] == '.' || ip[i] == ':' ) {
220 item->ignore |= (HashVector_find( &IGN, ip ) != 0);
224 fprintf( stdout, "add %s\n", item->ip );
226 // Unlink item from the trail
228 item->next->prev = item->prev;
231 item->prev->next = item->next;
233 if ( trail.head == item ) {
234 trail.head = item->next;
236 if ( trail.tail == item ) {
237 trail.tail = item->prev;
239 item->prev = item->next = 0;
241 item->accum += length;
243 // Link in item to the trail end
244 if ( trail.head == 0 ) {
247 trail.tail->next = item;
248 item->prev = trail.tail;
251 // Drop counters older than an hour
252 while ( trail.head->when.tv_sec + OLD < item->when.tv_sec ) {
253 Count *old = trail.head;
254 trail.head = old->next;
256 trail.head->prev = 0;
258 fprintf( stdout, "drop %s\n", old->ip );
259 HashVector_delete( &TBL, old );
262 if ( now.tv_sec < show ) {
265 if ( now.tv_sec - show > DELAY ) {
268 show += DELAY; // Time for next output
269 Vector ordered = { Nibble_index_levels, 0, 0 };
270 HashVector_contents( &TBL, Nibble_index_levels, &ordered );
271 Vector_qsort( &ordered, Countp_compare );
272 Vector_iterate( &ordered, 0, Countp_fade_and_print, 0 );
273 Vector_resize( &ordered, 0, Countp_reclaim, 0 );
274 fprintf( stdout, "==%ld/%ld/%ld\n", TBL.fill, TBL.holes, TBL.table.size );
277 static char *ipv4_address(char *b) {
278 memset( buffer, 0, sizeof( buffer ) );
279 sprintf( buffer, "%hhu.%hhu.%hhu.%hhu", b[0], b[1], b[2], b[3] );
283 static char *ipv6_address(short *b) {
284 memset( buffer, 0, sizeof( buffer ) );
285 sprintf( buffer, "%x:%x:%x:%x:%x:%x:%x:%x",
286 ntohs(b[0]), ntohs(b[1]), ntohs(b[2]), ntohs(b[3]),
287 ntohs(b[4]), ntohs(b[5]), ntohs(b[6]), ntohs(b[7]) );
291 int main(int argc,char **argv) {
292 static char packet[ 2048 ];
294 // Check for -fN to set FADE
295 if ( ARG < argc && strncmp( argv[ ARG ], "-d", 2 ) == 0 ) {
296 if ( sscanf( argv[ ARG ]+2, "%d", &DELAY ) != 1 ) {
297 die( "Missing/bad delay value" );
299 fprintf( stdout, "Delay is %d seconds between reports\n", DELAY );
302 if ( ARG < argc && strncmp( argv[ ARG ], "-f", 2 ) == 0 ) {
303 if ( sscanf( argv[ ARG ]+2, "%d", &FADE ) != 1 ) {
304 die( "Missing/bad fade value" );
306 fprintf( stdout, "Fading %d bytes before reports\n", FADE );
309 if ( ARG < argc && strncmp( argv[ ARG ], "-n", 2 ) == 0 ) {
310 if ( sscanf( argv[ ARG ]+2, "%d", &WORST ) != 1 ) {
311 die( "Missing/bad number to display" );
313 fprintf( stdout, "Displaying at most %d lines in reports\n", WORST );
316 if ( ARG < argc && strncmp( argv[ ARG ], "-a", 2 ) == 0 ) {
317 if ( sscanf( argv[ ARG ]+2, "%d", &OLD ) != 1 ) {
318 die( "Missing/bad drop-out age (seconds)" );
320 fprintf( stdout, "Displaying at most %d lines in reports\n", WORST );
323 if ( ARG < argc && strncmp( argv[ ARG ], "-i", 2 ) == 0 ) {
324 char *filename = argv[ ARG ] + 2;
325 if ( (*filename) == 0 ) {
326 die( "Missing/bad ignore filename" );
328 read_ignore_file( filename );
329 fprintf( stdout, "ignoring ip prefixes from %s\n", filename );
333 die( "Please tell which interface to sniff" );
337 char *iface = argv[ ARG ];
338 int fd = socket( AF_PACKET, SOCK_RAW, htons( ETH_P_ALL ) );
339 char flags[4] = { 1,0,0,0 };
348 if ( setsockopt( fd, SOL_SOCKET, SO_BINDTODEVICE, iface, N ) ) {
349 die( "setsockopt bind to device" );
351 if ( setsockopt( fd, SOL_SOCKET, SO_BROADCAST, &flags, 4 ) ) {
352 die( "setsockopt broadcast" );
354 while ( ( N = read( fd, packet, 2048 ) ) > 0 ) {
358 int code = ntohs( *((short*)(packet+12)) );
359 if ( code == 0x0800 ) {
360 // 14+12=src 14+16=dst
361 char *p = ipv4_address( packet+30 );
362 if ( ( strncmp( p, "127.", 4 ) != 0 ) ) {
363 add_show_table( p, N );
365 } else if ( code == 0x86dd ) {
366 // 14+8=src 14+24=dst
367 char *p = ipv6_address( (short*)(packet+38) );
368 if ( ( strncmp( p, "ff02:0:0:0:0:", 13 ) != 0 ) &&
369 ( strncmp( p, "0:0:0:0:0:0:0:1", 15 ) != 0 ) ) {
370 add_show_table( p, N );
372 } else if ( code == 0x8100 ) {