less output
[rrq/rrqmisc.git] / socket-sniff / socket-sniff.c
1 #include <arpa/inet.h>
2 #include <linux/if_ether.h>
3 #include <linux/in.h>
4 #include <stddef.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <sys/socket.h>
9 #include <sys/time.h>
10 #include <sys/types.h>
11 #include <unistd.h>
12 #include <htable.h>
13
14 // Seconds between outputs
15 #define DELAY 5
16
17 // Byte count fade-out between displays
18 #define FADE 10000
19
20 // Number of top usage to report
21 #define WORST 20
22
23 // Number of characters for text format IP holdings
24 #define IPBUFMAX 40
25
26 typedef struct _Count {
27     struct _Count *next;
28     int ignore;
29     int last;
30     int accum;
31     int total;
32     char ip[ IPBUFMAX ];
33 } Count;
34
35 static void die(char *m) {
36     fprintf( stderr, "%s\n", m );
37     exit( 1 );
38 }
39
40 static int Count_hashcode(htable *table,unsigned char *key) {
41     int value = 0;
42     int i = 0;
43     while ( *key ) {
44         value += *(key++) + (i++);
45         value += i;
46     }
47     return value;
48 }
49
50 static htable TBL = HTABLEINIT( Count, ip, Count_hashcode );
51 static Count *last_add;
52 static char buffer[ IPBUFMAX ];
53
54 #include "ignores.i"
55
56 static struct {
57     Count *table[ WORST ];
58     int fill;
59     int lowest;
60 } worst;
61
62 static int Countp_compare(const void *ax, const void *bx) {
63     Count *a = *(Count**) ax;
64     Count *b = *(Count**) bx;
65     int x = b->total - a->total;
66     if ( x != 0 ) {
67         if ( a->last == 0 ) {
68             return ( b->last == 0 )? x : -1;
69         }
70     }
71     x = b->last - a->last;
72     return x;
73 }
74
75 static void add_worst_ordered(Count *item) {
76     if ( worst.fill < WORST ) {
77         worst.table[ worst.fill++ ] = item;
78         if ( worst.fill == WORST ) {
79             qsort( worst.table, worst.fill, sizeof( Count* ), Countp_compare );
80         }
81         return;
82     }
83     Count **repl = bsearch(
84         &item, worst.table, worst.fill, sizeof( Count* ), Countp_compare );
85     if ( repl == 0 ) {
86         return;
87     }
88     int size = (char*) &worst.table[ worst.fill - 1 ] - (char*) repl;
89     if ( size > 0 ) {
90         memmove( repl + 1, repl, size );
91     }
92     *repl = item;
93 }
94
95 static void add_show_table(char *ip,size_t length) {
96     static time_t show = 0;
97     Count *item;
98     int i = htfind( &TBL, ip, (unsigned char **) &item );
99     if ( i == 0 ) {
100         item = (Count *) calloc( 1, sizeof( Count ) );
101         memcpy( item->ip, ip, strlen( ip ) );
102         item->accum = length;
103         item->ignore = ignored( ip );
104         htadd( &TBL, (unsigned char *) item );
105         item->next = last_add;
106         last_add = item;
107     } else {
108         item->accum += length;
109     }
110     struct timeval now;
111     if ( gettimeofday( &now, 0 ) ) {
112         perror( "gettimeofday" );
113         exit( 1 );
114     }
115     if ( now.tv_sec < show ) {
116         return;
117     }
118     if ( now.tv_sec - show > DELAY ) {
119         show = now.tv_sec;
120     }
121     show += 5; // Time for next output
122     // collate entries; Keep the X worst entries, but reduce all a bit
123     worst.fill = 0;
124     item = last_add;
125     for ( ; item; item = item->next ) {
126         item->last = item->accum;
127         item->total += item->last - FADE;
128         if ( item->total < 0 ) {
129             item->total = 0;
130         }
131         item->accum = 0;
132         if ( item->ignore == 0 ) {
133             add_worst_ordered( item );
134         }
135     }
136     //fprintf( stdout, "\e[2J" );
137     for ( i = 0; i < worst.fill; i++ ) {
138         item = worst.table[ i ];
139         if ( item->total && item->last ) {
140             fprintf( stdout, "... %s %d %d\n",
141                      item->ip, item->total, item->last );
142         }
143     }
144     fprintf( stdout, "==%d/%d/%d\n", TBL.fill, TBL.holes,TBL.size );
145 }
146
147 static char *ipv4_address(char *b) {
148     memset( buffer, 0, sizeof( buffer ) );
149     sprintf( buffer, "%hhu.%hhu.%hhu.%hhu", b[0], b[1], b[2], b[3] );
150     return buffer;
151 }
152
153 static char *ipv6_address(short *b) {
154     memset( buffer, 0, sizeof( buffer ) );
155     sprintf( buffer, "%x:%x:%x:%x:%x:%x:%x:%x",
156              ntohs(b[0]), ntohs(b[1]), ntohs(b[2]), ntohs(b[3]),
157              ntohs(b[4]), ntohs(b[5]), ntohs(b[6]), ntohs(b[7]) );
158     return buffer;
159 }
160
161 int main(int argc,char **argv) {
162     static char packet[ 2048 ];
163     if ( argc != 2 ) {
164         die( "Please tell which interface to sniff" );
165     }
166     setbuf( stdout, 0 );
167     int N;
168     char *iface = argv[ argc - 1 ];
169     int fd = socket( AF_PACKET, SOCK_RAW, htons( ETH_P_ALL ) );
170     char flags[4] = { 1,0,0,0 };
171     if ( fd < 0 ) {
172         perror( "what?" );
173         die( "socket" );
174     }
175     if ( fd < 0 ) {
176         die( "socket" );
177     }
178     N = strlen(iface);
179     if ( setsockopt( fd, SOL_SOCKET, SO_BINDTODEVICE, iface, N ) ) {
180         die( "setsockopt bind to device" );
181     }
182     if ( setsockopt( fd, SOL_SOCKET, SO_BROADCAST, &flags, 4 ) ) {
183         die( "setsockopt broadcast" );
184     }
185     while ( ( N = read( fd, packet, 2048 ) ) > 0 ) {
186         if ( N < 54 ) {
187             continue;
188         }
189         int code = ntohs( *((short*)(packet+12)) );
190         if ( code == 0x0800 ) {
191             // 14+12=src  14+16=dst
192             add_show_table( ipv4_address( packet+30 ), N );
193         } else if ( code == 0x86dd ) {
194             // 14+8=src 14+24=dst
195             add_show_table( ipv6_address( (short*)(packet+38) ), N );
196         } else if ( code == 0x0800 ) {
197             // ignore
198         } else if ( code == 0x8100 ) {
199             // ignore
200         } else {
201             // funny code
202         }
203     }
204     return 0;
205 }