e939712b3d8a2935096c86edf2ec4200e2639d32
[rrq/rrqmisc.git] / socket-sniff / socket-sniff.c
1 #include <arpa/inet.h>
2 #include <fcntl.h>
3 #include <linux/if_ether.h>
4 #include <linux/in.h>
5 #include <stddef.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <sys/socket.h>
10 #include <sys/stat.h>
11 #include <sys/time.h>
12 #include <sys/types.h>
13 #include <unistd.h>
14
15 #include <hashvector.h>
16
17 // Seconds between outputs
18 static int DELAY = 5;
19
20 // Byte count fade-out between displays
21 static int FADE = 10000;
22
23 // Number of top usage to report
24 static int WORST = 20;
25
26 // Number of characters for text format IP holdings
27 #define IPBUFMAX 40
28
29 typedef struct _Count {
30     struct _Count *next;
31     int ignore;
32     int last;
33     int accum;
34     int total;
35     char ip[ IPBUFMAX ];
36 } Count;
37
38 static void die(char *m) {
39     fprintf( stderr, "%s\n", m );
40     exit( 1 );
41 }
42
43 // Return pointer to the key for an item
44 static void *Countp_itemkey(void *item) {
45     return ((Count*) item)->ip;
46 }
47
48 // Return 1 if the item has the key, or 0 otherwise.
49 static int Countp_haskey(void *item,void *key) {
50     return memcmp( key, Countp_itemkey( item ), IPBUFMAX ) == 0;
51 }
52
53 // Returns the hashcode for a key
54 static unsigned long Countp_hashcode(void *key) {
55     return hashvector_hashcode( key, IPBUFMAX );
56 }
57
58 static hashvector TBL = {
59     .table = { 256, 0 },
60     .fill = 0,
61     .holes = 0,
62     .keyhashcode = Countp_hashcode,
63     .itemkey = Countp_itemkey,
64     .haskey = Countp_haskey
65 };
66
67 static char buffer[ IPBUFMAX ];
68
69 /*============================================================
70  * Reading ignore lines.
71  */
72 // Return pointer to the key for an item
73 static void *charp_itemkey(void *item) {
74     return item;
75 }
76
77 // Return 1 if the item has the key, or 0 otherwise.
78 static int charp_haskey(void *item,void *key) {
79     return strcmp( key, item ) == 0;
80 }
81
82 // Returns the hashcode for a key
83 static unsigned long charp_hashcode(void *key) {
84     return hashvector_hashcode( key, strlen( (const char *) key ) );
85 }
86
87 static hashvector IGN = {
88     .table = { 256, 0 },
89     .fill = 0,
90     .holes = 0,
91     .keyhashcode = charp_hashcode,
92     .itemkey = charp_itemkey,
93     .haskey = charp_haskey
94 };
95
96 static void read_ignore_file(char *filename) {
97     #define RDBLKSZ 1000000
98     static char block[ RDBLKSZ ];
99     static char *cur = block;
100     static char *end = block;
101     int fd = open( filename, O_RDONLY );
102     if ( fd < 0 ) {
103         die( "Cannot load the ignare file." );
104     }
105     for ( ;; ) {
106         char *p = cur;
107         size_t n;
108         for ( ;; ) { // move p to end of line
109             while ( p < end && *p != '\n' ) {
110                 p++;
111             }
112             if ( p < end ) {
113                 break;
114             }
115             if ( cur != block && cur != end ) {
116                 memmove( cur, block, end - cur );
117                 end -= cur - block;
118                 cur = block;
119                 p = end;
120             }
121             n = RDBLKSZ - ( end - cur );
122             n = read( fd, end, n );
123             if ( n <= 0 ) {
124                 return; // All done
125             }
126             end += n;
127         }
128         // Line from cur to '\n' at p < end.
129         char *ip = calloc( 1, p - cur + 1 );
130         memcpy( ip, cur, p - cur );
131         cur = p + 1;
132         hashvector_add( &IGN, ip );
133     }
134 }
135
136 /*============================================================*/
137
138 static int Countp_compare(const void *ax, const void *bx) {
139     Count *a = (Count*) ax;
140     Count *b = (Count*) bx;
141     if ( b->ignore ) {
142         return 1;
143     }
144     if ( a->ignore ) {
145         return -1;
146     }
147     int x = a->total - b->total;
148     if ( x ) {
149         return x;
150     }
151     return a->last - b->last;
152 }
153
154 static int Countp_fade_and_print(unsigned long index,void *x,void *d) {
155     if ( x ) {
156         Count *item = (Count *) x;
157         item->last = item->accum;
158         item->total += item->last - FADE;
159         item->accum = 0;
160         if ( item->total <= 0 ) {
161             item->total = 0;
162         } else if ( index < WORST && item->ignore == 0 ) {
163             fprintf( stdout, "... %s %d %d\n",
164                      item->ip, item->total, item->last );
165         }
166     }
167     return 0;
168 }
169
170 static int Countp_reclaim(pvector *pv,unsigned long ix,void *item,void *data) {
171     return 0;
172 }
173
174
175 // ip points to [ IPBUFMAX ] of ip address in text
176 static void add_show_table(char *ip,size_t length) {
177     static time_t show = 0;
178     Count *item;
179     int i = hashvector_find( &TBL, ip, (void**) &item );
180     if ( i == 0 ) {
181         item = (Count *) calloc( 1, sizeof( Count ) );
182         memcpy( item->ip, ip, strlen( ip ) );
183         item->accum = length;
184         hashvector_add( &TBL, item );
185         item->ignore = hashvector_find( &IGN, ip, 0 );
186         for ( i = strlen( ip )-1; i > 1; i-- ) {
187             if ( ip[i] == '.' || ip[i] == ':' ) {
188                 item->ignore |= hashvector_find( &IGN, ip, 0 );
189             }
190             ip[i] = 0;
191         }
192         //fprintf( stdout, "add %s\n", ip );
193     } else {
194         item->accum += length;
195     }
196     struct timeval now;
197     if ( gettimeofday( &now, 0 ) ) {
198         perror( "gettimeofday" );
199         exit( 1 );
200     }
201     if ( now.tv_sec < show ) {
202         return;
203     }
204     if ( now.tv_sec - show > DELAY ) {
205         show = now.tv_sec;
206     }
207     show += DELAY; // Time for next output
208     pvector ordered = { 0, 0 };
209     hashvector_contents( &TBL, &ordered );
210     pvector_qsort( &ordered, Countp_compare );
211     pvector_iterate( &ordered, Countp_fade_and_print, 0 );
212     pvector_resize( &ordered, 0, Countp_reclaim, 0 );
213     fprintf( stdout, "==%ld/%ld/%ld\n", TBL.fill, TBL.holes, TBL.table.size );
214 }
215
216 static char *ipv4_address(char *b) {
217     memset( buffer, 0, sizeof( buffer ) );
218     sprintf( buffer, "%hhu.%hhu.%hhu.%hhu", b[0], b[1], b[2], b[3] );
219     return buffer;
220 }
221
222 static char *ipv6_address(short *b) {
223     memset( buffer, 0, sizeof( buffer ) );
224     sprintf( buffer, "%x:%x:%x:%x:%x:%x:%x:%x",
225              ntohs(b[0]), ntohs(b[1]), ntohs(b[2]), ntohs(b[3]),
226              ntohs(b[4]), ntohs(b[5]), ntohs(b[6]), ntohs(b[7]) );
227     return buffer;
228 }
229
230 int main(int argc,char **argv) {
231     static char packet[ 2048 ];
232     int ARG = 1;
233     // Check for -fN to set FADE
234     if ( ARG < argc && strncmp( argv[ ARG ], "-d", 2 ) == 0 ) {
235         if ( sscanf( argv[ ARG ]+2, "%d", &DELAY ) != 1 ) {
236             die( "Missing/bad delay value" );
237         }
238         fprintf( stdout, "Delay is %d seconds between reports\n", DELAY );
239         ARG++;
240     }
241     if ( ARG < argc && strncmp( argv[ ARG ], "-f", 2 ) == 0 ) {
242         if ( sscanf( argv[ ARG ]+2, "%d", &FADE ) != 1 ) {
243             die( "Missing/bad fade value" );
244         }
245         fprintf( stdout, "Fading %d bytes before reports\n", FADE );
246         ARG++;
247     }
248     if ( ARG < argc && strncmp( argv[ ARG ], "-n", 2 ) == 0 ) {
249         if ( sscanf( argv[ ARG ]+2, "%d", &WORST ) != 1 ) {
250             die( "Missing/bad number to display" );
251         }
252         fprintf( stdout, "Displaying at most %d lines in reports\n", WORST );
253         ARG++;
254     }
255     if ( ARG < argc && strncmp( argv[ ARG ], "-i", 2 ) == 0 ) {
256         char *filename = argv[ ARG ] + 2;
257         if ( (*filename) == 0 ) {
258             die( "Missing/bad ignore filename" );
259         }
260         read_ignore_file( filename );
261         fprintf( stdout, "ignoring ip prefixes from %s\n", filename );
262         ARG++;
263     }
264     if ( ARG >= argc ) {
265         die( "Please tell which interface to sniff" );
266     }
267     setbuf( stdout, 0 );
268     int N;
269     char *iface = argv[ ARG ];
270     int fd = socket( AF_PACKET, SOCK_RAW, htons( ETH_P_ALL ) );
271     char flags[4] = { 1,0,0,0 };
272     if ( fd < 0 ) {
273         perror( "what?" );
274         die( "socket" );
275     }
276     if ( fd < 0 ) {
277         die( "socket" );
278     }
279     N = strlen(iface);
280     if ( setsockopt( fd, SOL_SOCKET, SO_BINDTODEVICE, iface, N ) ) {
281         die( "setsockopt bind to device" );
282     }
283     if ( setsockopt( fd, SOL_SOCKET, SO_BROADCAST, &flags, 4 ) ) {
284         die( "setsockopt broadcast" );
285     }
286     while ( ( N = read( fd, packet, 2048 ) ) > 0 ) {
287         if ( N < 54 ) {
288             continue;
289         }
290         int code = ntohs( *((short*)(packet+12)) );
291         if ( code == 0x0800 ) {
292             // 14+12=src  14+16=dst
293             char *p = ipv4_address( packet+30 );
294             if ( ( strncmp( p, "127.", 4 ) != 0 ) ) {
295                 add_show_table( p, N );
296             }
297         } else if ( code == 0x86dd ) {
298             // 14+8=src 14+24=dst
299             char *p = ipv6_address( (short*)(packet+38) );
300             if ( ( strncmp( p, "ff02:0:0:0:0:", 13 ) != 0 ) &&
301                  ( strncmp( p, "0:0:0:0:0:0:0:1", 15 ) != 0 ) ) {
302                 add_show_table( p, N );
303             }
304         } else if ( code == 0x0800 ) {
305             // ignore
306         } else if ( code == 0x8100 ) {
307             // ignore
308         } else {
309             // funny code
310         }
311     }
312     return 0;
313 }