3 #include <linux/if_ether.h>
9 #include <sys/socket.h>
12 #include <sys/types.h>
15 #include <hashvector.h>
17 // Seconds between outputs
20 // Byte count fade-out between displays
21 static int FADE = 10000;
23 // Number of top usage to report
24 static int WORST = 20;
29 // Number of characters for text format IP holdings
32 typedef struct _Count {
43 static void die(char *m) {
44 fprintf( stderr, "%s\n", m );
48 // Return pointer to the key for an item
49 static void *Countp_itemkey(void *item) {
50 return ((Count*) item)->ip;
53 // Return 1 if the item has the key, or 0 otherwise.
54 static int Countp_haskey(void *item,void *key) {
55 return memcmp( key, Countp_itemkey( item ), IPBUFMAX ) == 0;
58 // Returns the hashcode for a key
59 static unsigned long Countp_hashcode(void *key) {
60 return hashvector_hashcode( key, IPBUFMAX );
63 static hashvector TBL = {
67 .keyhashcode = Countp_hashcode,
68 .itemkey = Countp_itemkey,
69 .haskey = Countp_haskey
77 static char buffer[ IPBUFMAX ];
79 /*============================================================
80 * Reading ignore lines.
82 // Return pointer to the key for an item
83 static void *charp_itemkey(void *item) {
87 // Return 1 if the item has the key, or 0 otherwise.
88 static int charp_haskey(void *item,void *key) {
89 return strcmp( key, item ) == 0;
92 // Returns the hashcode for a key
93 static unsigned long charp_hashcode(void *key) {
94 return hashvector_hashcode( key, strlen( (const char *) key ) );
97 static hashvector IGN = {
101 .keyhashcode = charp_hashcode,
102 .itemkey = charp_itemkey,
103 .haskey = charp_haskey
106 static void read_ignore_file(char *filename) {
107 #define RDBLKSZ 1000000
108 static char block[ RDBLKSZ ];
109 static char *cur = block;
110 static char *end = block;
111 int fd = open( filename, O_RDONLY );
113 die( "Cannot load the ignare file." );
118 for ( ;; ) { // move p to end of line
119 while ( p < end && *p != '\n' ) {
125 if ( cur != block && cur != end ) {
126 memmove( cur, block, end - cur );
131 n = RDBLKSZ - ( end - cur );
132 n = read( fd, end, n );
138 // Line from cur to '\n' at p < end.
139 char *ip = calloc( 1, p - cur + 1 );
140 memcpy( ip, cur, p - cur );
142 hashvector_add( &IGN, ip );
146 /*============================================================*/
148 static int Countp_compare(const void *ax, const void *bx) {
149 Count *a = (Count*) ax;
150 Count *b = (Count*) bx;
157 int x = a->total - b->total;
161 return a->last - b->last;
164 static int Countp_fade_and_print(unsigned long index,void *x,void *d) {
166 Count *item = (Count *) x;
167 item->last = item->accum;
168 item->total += item->last - FADE;
170 if ( item->total <= 0 ) {
172 } else if ( index < WORST && item->ignore == 0 ) {
173 fprintf( stdout, "... %s %d %d\n",
174 item->ip, item->total, item->last );
180 static int Countp_reclaim(pvector *pv,unsigned long ix,void *item,void *data) {
185 // ip points to [ IPBUFMAX ] of ip address in text
186 static void add_show_table(char *ip,size_t length) {
187 static time_t show = 0;
189 int i = hashvector_find( &TBL, ip, (void**) &item );
191 if ( gettimeofday( &now, 0 ) ) {
192 perror( "gettimeofday" );
196 item = (Count *) calloc( 1, sizeof( Count ) );
197 memcpy( item->ip, ip, strlen( ip ) );
198 hashvector_add( &TBL, item );
199 item->ignore = hashvector_find( &IGN, ip, 0 );
200 for ( i = strlen( ip )-1; i > 1; i-- ) {
201 if ( ip[i] == '.' || ip[i] == ':' ) {
202 item->ignore |= hashvector_find( &IGN, ip, 0 );
206 fprintf( stdout, "add %s\n", item->ip );
208 // Unlink item from the trail
210 item->next->prev = item->prev;
213 item->prev->next = item->next;
215 if ( trail.head == item ) {
216 trail.head = item->next;
218 if ( trail.tail == item ) {
219 trail.tail = item->prev;
221 item->prev = item->next = 0;
223 item->accum += length;
225 // Link in item to the trail end
226 if ( trail.head == 0 ) {
229 trail.tail->next = item;
230 item->prev = trail.tail;
233 // Drop counters older than an hour
234 while ( trail.head->when.tv_sec + OLD < item->when.tv_sec ) {
235 Count *old = trail.head;
236 trail.head = old->next;
238 trail.head->prev = 0;
240 fprintf( stdout, "drop %s\n", old->ip );
241 hashvector_delete( &TBL, old );
244 if ( now.tv_sec < show ) {
247 if ( now.tv_sec - show > DELAY ) {
250 show += DELAY; // Time for next output
251 pvector ordered = { 0, 0 };
252 hashvector_contents( &TBL, &ordered );
253 pvector_qsort( &ordered, Countp_compare );
254 pvector_iterate( &ordered, Countp_fade_and_print, 0 );
255 pvector_resize( &ordered, 0, Countp_reclaim, 0 );
256 fprintf( stdout, "==%ld/%ld/%ld\n", TBL.fill, TBL.holes, TBL.table.size );
259 static char *ipv4_address(char *b) {
260 memset( buffer, 0, sizeof( buffer ) );
261 sprintf( buffer, "%hhu.%hhu.%hhu.%hhu", b[0], b[1], b[2], b[3] );
265 static char *ipv6_address(short *b) {
266 memset( buffer, 0, sizeof( buffer ) );
267 sprintf( buffer, "%x:%x:%x:%x:%x:%x:%x:%x",
268 ntohs(b[0]), ntohs(b[1]), ntohs(b[2]), ntohs(b[3]),
269 ntohs(b[4]), ntohs(b[5]), ntohs(b[6]), ntohs(b[7]) );
273 int main(int argc,char **argv) {
274 static char packet[ 2048 ];
276 // Check for -fN to set FADE
277 if ( ARG < argc && strncmp( argv[ ARG ], "-d", 2 ) == 0 ) {
278 if ( sscanf( argv[ ARG ]+2, "%d", &DELAY ) != 1 ) {
279 die( "Missing/bad delay value" );
281 fprintf( stdout, "Delay is %d seconds between reports\n", DELAY );
284 if ( ARG < argc && strncmp( argv[ ARG ], "-f", 2 ) == 0 ) {
285 if ( sscanf( argv[ ARG ]+2, "%d", &FADE ) != 1 ) {
286 die( "Missing/bad fade value" );
288 fprintf( stdout, "Fading %d bytes before reports\n", FADE );
291 if ( ARG < argc && strncmp( argv[ ARG ], "-n", 2 ) == 0 ) {
292 if ( sscanf( argv[ ARG ]+2, "%d", &WORST ) != 1 ) {
293 die( "Missing/bad number to display" );
295 fprintf( stdout, "Displaying at most %d lines in reports\n", WORST );
298 if ( ARG < argc && strncmp( argv[ ARG ], "-a", 2 ) == 0 ) {
299 if ( sscanf( argv[ ARG ]+2, "%d", &OLD ) != 1 ) {
300 die( "Missing/bad drop-out age (seconds)" );
302 fprintf( stdout, "Displaying at most %d lines in reports\n", WORST );
305 if ( ARG < argc && strncmp( argv[ ARG ], "-i", 2 ) == 0 ) {
306 char *filename = argv[ ARG ] + 2;
307 if ( (*filename) == 0 ) {
308 die( "Missing/bad ignore filename" );
310 read_ignore_file( filename );
311 fprintf( stdout, "ignoring ip prefixes from %s\n", filename );
315 die( "Please tell which interface to sniff" );
319 char *iface = argv[ ARG ];
320 int fd = socket( AF_PACKET, SOCK_RAW, htons( ETH_P_ALL ) );
321 char flags[4] = { 1,0,0,0 };
330 if ( setsockopt( fd, SOL_SOCKET, SO_BINDTODEVICE, iface, N ) ) {
331 die( "setsockopt bind to device" );
333 if ( setsockopt( fd, SOL_SOCKET, SO_BROADCAST, &flags, 4 ) ) {
334 die( "setsockopt broadcast" );
336 while ( ( N = read( fd, packet, 2048 ) ) > 0 ) {
340 int code = ntohs( *((short*)(packet+12)) );
341 if ( code == 0x0800 ) {
342 // 14+12=src 14+16=dst
343 char *p = ipv4_address( packet+30 );
344 if ( ( strncmp( p, "127.", 4 ) != 0 ) ) {
345 add_show_table( p, N );
347 } else if ( code == 0x86dd ) {
348 // 14+8=src 14+24=dst
349 char *p = ipv6_address( (short*)(packet+38) );
350 if ( ( strncmp( p, "ff02:0:0:0:0:", 13 ) != 0 ) &&
351 ( strncmp( p, "0:0:0:0:0:0:0:1", 15 ) != 0 ) ) {
352 add_show_table( p, N );
354 } else if ( code == 0x8100 ) {