1 /********************************************************************************
2
3 Program: dns_flood_detector.c
4 Author: Dennis Opacki <dopacki@adotout.com>
5 Date: Tue Mar 18 16:46:53 EST 2003
6 Purpose: Monitor DNS servers for abusive usage levels
7 and alarm to syslog
8
9 compile with:
10 gcc -o dns_flood_detector -lpcap -lpthread -lm dns_flood_detector.c
11
12 command-line options:
13
14 -i ifname specify interface to listen on (default lets pcap pick)
15 -t n alarm when more than n queries per second are observed
16 (default 40)
17 -a n wait for n seconds before alarming again on same source
18 (default 90)
19 -w n calculate statistics every n seconds
20 (default 10)
21 -x n use n buckets
22 (default 50)
23 -m n mark overall query rate every n seconds
24 (default disabled)
25 -A addr filter for specific address
26 -M mask netmask for filter (in conjunction with -A)
27 -Q monitor any addresses (default is to filter only for
28 primary addresses on chosen interface)
29 -b run in foreground in "bindsnap" mode
30 -d run in background in "daemon" mode
31 -D dump dns packets (implies -b)
32 -v detailed information (use twice for more detail)
33 -s send source IP stats to collector as JSON
34 -z N.N.N.N address to send stats to (default 226.1.1.2)
35 -p N UDP port to send stats to (default 2000)
36
37 -h usage info
38
39 Copyright (C) 2003 Dennis Opacki
40
41 This program is free software; you can redistribute it and/or modify
42 it under the terms of the GNU General Public License as published by
43 the Free Software Foundation; either version 2 of the License, or
44 (at your option) any later version.
45
46 This program is distributed in the hope that it will be useful,
47 but WITHOUT ANY WARRANTY; without even the implied warranty of
48 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
49 GNU General Public License for more details.
50
51 You should have received a copy of the GNU General Public License
52 along with this program; if not, write to the Free Software
53 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
54
55 --- new in v1.05 ---
56 8/18/2003 - FreeBSD target - Jim Westfall <jwestfall@surrealistic.net>
57 8/18/2003 - Moved to getopt(3) for compatibility <dopacki@adotout.com>
58 8/19/2003 - Added OSX/BSDI make targets - <dopacki@adotout.com>
59 Added ability to specify inteface - <dopacki@adotout.com>
60
61 --- new in v1.06 ---
62 8/20/2003 - Added Solaris9 make target - <dopacki@adotout.com>
63 8/26/2003 - Fixed tcp qdcount bug - <dopacki@adotout.com>
64
65 --- new in v1.07 ---
66 8/27/2003 - Fixed alarm reset bug - <dopacki@adotout.com>
67 8/28/2003 - Added malloc_fail function - <dopacki@adotout.com>
68 8/28/2003 - Added mutex thread locking - <dopacki@adotout.com>
69 8/30/2003 - Fixed wierd qtype segfault - <jwestfall@surrealistic.net>
70 <dopacki@adotout.com>
71
72 --- new in v1.08 ---
73 9/02/2003 - Added -v -v output in daemon mode - <dopacki@adotout.com>
74
75 --- new in v1.09 ---
76 10/19/2003 - Added stdout flushing to bindsnap mode - <dopacki@adotout.com>
77 10/19/2003 - Changed logging priority to LOG_NOTICE - <dopacki@adotout.com>
78 10/19/2003 - Fixed low traffic verbose logging bugs - <dopacki@adotout.com>
79
80 --- new in v1.10 ---
81 10/22/2003 - Added 'mark status' option via '-m' - <dopacki@adotout.com>
82 10/23/2003 - Code cleanup in verbose syslogging - <dopacki@adotout.com>
83
84 --- new in v1.11 ---
85 06/14/2005 - added A6, AAAA, ANY qtypes - <jwestfall@surrealistic.net>
86 examine all packets with >= 1 qdcount - <jwestfall@surrealistic.net>
87 stop processing packet if invalid dns char - <jwestfall@surrealistic.net>
88 fix tcp parsing - <jwestfall@surrealistic.net>
89 add option_D to dump packets - <jwestfall@surrealistic.net>
90
91 --- new in v1.12 ---
92 03/03/2006 - added address filtering options - <erikm@buh.org>
93 fix segfault using argv[0] after getopt - <erikm@buh.org>
94 fix rounding from float/int conversions, use unsigned more consistently - <erikm@buh.org>
95 clean up to work with -Wall - <erikm@buh.org>
96 show fractional qps rates for totals - <erikm@buh.org>
97 store addresses raw, instead of as text (speedup/reduce memory usage) - <erikm@buh.org>
98 fix crash on long syslog messages - <jwestfall@surrealistic.net>
99
100 --- new in v1.2 ---
101 05/10/2012 - added sending of source-IP telemetry to a network collector - <dopacki@adotout.com>
102
103 ********************************************************************************/
104
105 #include <pcap.h>
106 #include <stdio.h>
107 #include <stdlib.h>
108 #include <fcntl.h>
109 #include <errno.h>
110 #include <netinet/in_systm.h>
111 #include <netinet/in.h>
112 #include <netinet/ip.h>
113 #include <netinet/tcp.h>
114 #include <netinet/udp.h>
115 #include <arpa/inet.h>
116 #ifdef __bsdi__
117 #include <net/if_ethernet.h>
118 #else
119 #ifdef __sun__
120 #include <sys/ethernet.h>
121 #else
122 #include <net/ethernet.h>
123 #endif
124 #endif
125 #include <pthread.h>
126 #include <unistd.h>
127 #include <time.h>
128 #include <math.h>
129 #include <signal.h>
130 #include <syslog.h>
131 #include <string.h>
132 #include <sys/stat.h>
133 #include "dns_flood_detector.h"
134 #include <sys/types.h>
135 #include <sys/socket.h>
136 #include <sys/select.h>
137
138 // global variables and their defaults
139 pthread_mutex_t stats_lock;
140 struct bucket **bb;
141 int option_t = 60;
142 int option_a = 90;
143 int option_w = 20;
144 int option_x = 10000;
145 int option_m = 0;
146 int option_b = 0;
147 int option_d = 0;
148 int option_D = 0;
149 int option_v = 0;
150 int option_h = 0;
151 int option_Q = 0;
152 int option_A = 0;
153 int option_M = 0;
154 int option_s = 0;
155 int totals = 0;
156
157 static char *target_ip = NULL;
158 int target_port = DEFAULT_PORT;
159 int mcast_ttl = 10;
160 char hostname[HOST_NAME_MAX];
161
162 char VERSION[] = "1.2";
163
164 // 255.255.255.255 is invalid as a src IP address; we'll use it to mark empty buckets
165 #define BCAST 0xffFFffFF
166
167 // this is our statistics thread
run_stats()168 void *run_stats () {
169 // prepare multicast socket
170 struct sockaddr_in addr;
171 int sock;
172 sock = socket(AF_INET, SOCK_DGRAM,0);
173
174 if (sock<0) {
175 perror("can't set up socket");
176 exit(1);
177 }
178
179 // is it harmful to set this on non-multicast sockets?
180 setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, (char *)&mcast_ttl, sizeof(mcast_ttl));
181
182 bzero((char*)&addr, sizeof(addr));
183 addr.sin_family = AF_INET;
184 addr.sin_addr.s_addr = inet_addr(target_ip);
185 addr.sin_port = htons(target_port);
186
187 // get our hostname
188 gethostname(hostname, HOST_NAME_MAX-1);
189
190 while (1) {
191
192 // check statistical stuff
193 pthread_mutex_lock(&stats_lock);
194 calculate_averages();
195 saddr_stats(sock,addr,hostname);
196 pthread_mutex_unlock(&stats_lock);
197 sleep (option_w);
198 }
199 }
200
201 // report saddr stats
saddr_stats(int sock,struct sockaddr_in addr,char * hostname)202 int saddr_stats(int sock, struct sockaddr_in addr, char *hostname) {
203 u_int i;
204 int addrlen;
205 char buff[MAXMESSAGE];
206 int buffhead = 0;
207 char st_time[10];
208 time_t now = time(0);
209 struct tm *raw_time = localtime(&now);
210 addrlen = sizeof(addr);
211 snprintf(st_time, 9, "%02d:%02d:%02d",raw_time->tm_hour,raw_time->tm_min,raw_time->tm_sec);
212
213 // prepare jason structure for multicast datagrams
214 char head[MAXHEAD];
215 char *tail = "}}";
216 snprintf(head,MAXHEAD,"{\"hostname\":\"%s\",\"type\":\"source\",\"data\":{",hostname);
217 int netsize = MAXMESSAGE - strlen(head) - strlen(tail);
218 if (netsize<=0) exit(EXIT_FAILURE); // this should never ever happen
219
220 int avail = netsize;
221 int dlen = 0;
222 char datalet[MAXDATALET];
223
224 // copy the initial json header into the buffer
225 bzero(buff,sizeof(buff));
226 memcpy(buff,head,strlen(head));
227 buffhead = buffhead + strlen(head);
228
229 // report all source address stats, cleaning up afterward
230 for (i=0; ( (i < option_x) && ( bb[i]->ip_addr.s_addr != 0 ) ); i++) {
231
232 if ( bb[i]->ip_addr.s_addr != BCAST ) {
233
234 // prepare a datalet
235 snprintf(datalet,MAXDATALET,"\"%s\":%d,",inet_ntoa(bb[i]->ip_addr),bb[i]->udp_count+bb[i]->tcp_count);
236 dlen = strlen(datalet);
237
238 // see if the current datagram has room for the datalet
239 if ( avail > dlen ) {
240
241 // append this datalet to the current datagram (minus null terminator)
242 avail = avail - dlen;
243 memcpy(buff+buffhead,datalet,dlen);
244 buffhead = buffhead + dlen;
245 }
246 // no room in current dgram
247 else {
248
249 // remove trailing comma from the buffer so we can close it out
250 buffhead = buffhead - 1;
251
252 // add the tail
253 strncpy(buff+buffhead,tail, strlen(tail));
254
255 // send the transmission if option_s is set
256 if (option_s > 0 ) {
257 sendto(sock,buff,strlen(buff)+1,0,(struct sockaddr *) &addr, addrlen);
258 microsleep(10);
259 }
260
261 // init next datagram
262 bzero(buff,sizeof(buff));
263 memcpy(buff,head,strlen(head));
264 buffhead = strlen(head);
265 avail = netsize;
266
267 // append this datalet to the current datagram (minus null terminatin)
268 avail = avail - dlen;
269 memcpy(buff+buffhead,datalet,dlen);
270 buffhead = buffhead + dlen;
271 }
272 }
273
274 scour_bucket(i);
275 }
276
277 // transmit final buffer contents if needed
278 if ( ( option_b == 0) && (buffhead>strlen(head)) ) {
279
280 // remove trailing comma
281 buffhead = buffhead - 1;
282
283 // add the tail
284 strncpy(buff+buffhead,tail,strlen(tail));
285
286 // send the multicast transmission
287 sendto(sock,buff,strlen(buff)+1,0,(struct sockaddr *) &addr, addrlen);
288 microsleep(10);
289
290 bzero(buff,sizeof(buff));
291 }
292 return 1;
293 }
294
295
296 // calculate the running average within each bucket
calculate_averages()297 int calculate_averages() {
298 u_int i,j,delta,cursize,qps;
299 int newsize;
300 float qpsf;
301 char st_time[10];
302 time_t now = time(0);
303 u_int types[] = {1,2,5,6,12,15,28,38,252,255,0};
304 char *target;
305 char *names[] = {"A","NS","CNAME","SOA","PTR","MX","AAAA","A6","AXFR","ANY",""};
306 struct tm *raw_time = localtime(&now);
307 snprintf(st_time, 9, "%02d:%02d:%02d",raw_time->tm_hour,raw_time->tm_min,raw_time->tm_sec);
308
309 for (i=0; i<option_x; i++) {
310
311 // only process valid buckets
312 if ( bb[i]->ip_addr.s_addr != BCAST) {
313 delta = now - bb[i]->first_packet;
314
315 // let's try to avoid a divide-by-zero, shall we?
316 if (delta > 1 ) {
317
318 // round our average and save it in the bucket
319 bb[i]->qps = (u_int)ceil( (bb[i]->tcp_count + bb[i]->udp_count) / (float)delta);
320
321 // handle threshold crossing
322 if ( bb[i]->qps > option_t ) {
323
324 // display detail to either syslog or stdout
325 if ( option_b ) {
326 if ( ! option_v ) {
327 printf("[%s] source [%s] - %u qps\n",st_time,inet_ntoa(bb[i]->ip_addr),bb[i]->qps);
328 fflush(stdout);
329 }
330 else {
331 printf("[%s] source [%s] - %u qps tcp : %u qps udp ",st_time,inet_ntoa(bb[i]->ip_addr),
332 (u_int)ceil( ((float)bb[i]->tcp_count/delta)),
333 (u_int)ceil( ((float)bb[i]->udp_count/delta)));
334
335 if ( option_v >1 ) {
336 for (j=0;types[j];j++) {
337 qps = (u_int)ceil((float)bb[i]->qstats[types[j]]/delta);
338 if (qps){
339 printf("[%u qps %s] ",qps,names[j]);
340 }
341 }
342 }
343 printf("\n");
344 fflush(stdout);
345 }
346 }
347 else {
348 // if running in background, use alarm reset timer
349 if ((now-bb[i]->alarm_set)>option_a) {
350
351 // display appropriate level of detail via syslog
352 if ( ! option_v ) {
353 syslog(LOG_NOTICE,"source [%s] - %u qps\n",inet_ntoa(bb[i]->ip_addr),bb[i]->qps);
354 }
355 else if (option_v > 1) {
356 target = (char *)malloc(sizeof(char)*MAXSYSLOG);
357 newsize = MAXSYSLOG;
358 cursize = snprintf(target,newsize,"source [%s] - %u tcp qps : %u udp qps ",inet_ntoa(bb[i]->ip_addr),
359 (u_int)ceil( ((float)bb[i]->tcp_count/delta)),
360 (u_int)ceil( ((float)bb[i]->udp_count/delta)));
361 newsize-=cursize;
362
363 for (j=0;types[j];j++ ) {
364 qps = (u_int)ceil(((float)bb[i]->qstats[types[j]]/delta));
365 if ( ( qps > 0) && ( newsize > 1 ) ) {
366 cursize = snprintf(target+(MAXSYSLOG-newsize),newsize,"[%u qps %s] ",qps,names[j]);
367 newsize-=cursize;
368 }
369 }
370 if (newsize <= 0 ) {
371 target[MAXSYSLOG-1]='\0';
372 }
373 syslog(LOG_NOTICE,"%s",target);
374 free(target);
375 }
376 else {
377 syslog(LOG_NOTICE,"source [%s] - %u tcp qps - %u udp qps\n",inet_ntoa(bb[i]->ip_addr),
378 (u_int)ceil( ((float)bb[i]->tcp_count/delta)),
379 (u_int)ceil( ((float)bb[i]->udp_count/delta)));
380 }
381
382 // reset alarm
383 bb[i]->alarm_set = now;
384 }
385 }
386 }
387 }
388 }
389 }
390
391 // 'mark stats' if required and it is time
392 delta = (u_int)(now - bb[totals]->first_packet);
393 if ( (option_m > 0)&&(delta > 1)&&(delta >= option_m) ) {
394
395 // handle bindsnap mode
396 if (option_b) {
397 printf("[%s] totals - %3.2f qps tcp : %3.2f qps udp ",st_time, ((float)bb[totals]->tcp_count/delta),((float)bb[totals]->udp_count/delta));
398 if (option_v) {
399 for (j=0;types[j];j++) {
400 qpsf = ((float)bb[totals]->qstats[types[j]]/delta);
401 if (qpsf > 0){
402 printf("[%3.2f qps %s] ",qpsf,names[j]);
403 }
404 }
405 }
406 printf("\n");
407 fflush(stdout);
408 }
409 else {
410 // agonizing high verbosity code
411 if (option_v) {
412 target = (char *)malloc(sizeof(char)*MAXSYSLOG);
413 newsize = MAXSYSLOG;
414 cursize = snprintf(target,newsize,"[totals] - %3.2f tcp qps : %3.2f udp qps ",
415 ((float)bb[totals]->tcp_count/delta),
416 ((float)bb[totals]->udp_count/delta));
417 newsize-=cursize;
418
419 for (j=0;types[j];j++ ) {
420 qpsf = ((float)bb[totals]->qstats[types[j]]/delta);
421 if ( ( qpsf > 0) && ( newsize > 1 ) ) {
422 cursize = snprintf(target+(MAXSYSLOG-newsize),newsize,"[%3.2f qps %s] ",qpsf,names[j]);
423 newsize-=cursize;
424 }
425 }
426 if (newsize <= 0 ) {
427 target[MAXSYSLOG-1]='\0';
428 }
429 syslog(LOG_NOTICE,"%s",target);
430 free(target);
431 }
432 else {
433 syslog(LOG_NOTICE,"[totals] - %3.2f tcp qps : %3.2f udp qps\n",
434 ((float)bb[totals]->tcp_count/delta),
435 ((float)bb[totals]->udp_count/delta));
436 }
437 }
438 scour_bucket(totals);
439 }
440 return 1;
441 }
442
valid_dns_char(char c)443 int valid_dns_char(char c) {
444
445 if((c >= '0' && c <= '9')
446 || (c >= 'a' && c <= 'z')
447 || (c >= 'A' && c <= 'Z')
448 || (c == '-')
449 || (c == '_')) // is valid for SRV records.
450 return 1;
451
452 return 0;
453 }
454
455 // purge and initialize all buckets
init_buckets()456 void init_buckets() {
457 u_int i;
458
459 // create bucket brigade (final bucket is for totals)
460 pthread_mutex_lock(&stats_lock);
461 if ( ( bb = malloc( sizeof(struct bucket *) * (option_x+1)) ) == NULL ) malloc_fail("bb", sizeof(struct bucket *) * (option_x+1));
462 for (i=0; i <=option_x; i++ ) {
463 if ( ( bb[i] = (struct bucket *)malloc( sizeof(struct bucket) ) ) == NULL) malloc_fail("bb[i]", sizeof(struct bucket) );
464 scour_bucket(i);
465 }
466 pthread_mutex_unlock(&stats_lock);
467 }
468
469 // clean out a bucket while avoiding obvious memory leak
scour_bucket(int i)470 int scour_bucket( int i ) {
471 int j;
472
473 bb[i]->ip_addr.s_addr=BCAST;
474 bb[i]->tcp_count=0;
475 bb[i]->udp_count=0;
476 bb[i]->qps=0;
477 bb[i]->first_packet=time(0);
478 bb[i]->last_packet=(time_t)0;
479 bb[i]->alarm_set=(time_t)0;
480
481 for (j=0;j<256;j++) {
482 bb[i]->qstats[j]=0;
483 }
484
485 return 1;
486 }
487
488 // add a packet to a bucket
add_to_bucket(struct in_addr * ip_src,int ip_proto,int num_queries,u_int8_t qtype)489 int add_to_bucket ( struct in_addr *ip_src, int ip_proto, int num_queries, u_int8_t qtype) {
490 int bucket = 0;
491
492 // get the bucket to put packet in
493 pthread_mutex_lock(&stats_lock);
494 bucket = find_bucket(ip_src);
495
496 // set bucket fields
497 bb[bucket]->last_packet = time(0);
498 if (ip_proto == 6 ) {
499 bb[bucket]->tcp_count+=num_queries;
500 bb[totals]->tcp_count+=num_queries;
501 }
502 else {
503 bb[bucket]->udp_count+=num_queries;
504 bb[totals]->udp_count+=num_queries;
505 }
506
507 bb[bucket]->qstats[qtype]+=num_queries;
508 bb[totals]->qstats[qtype]+=num_queries;
509 pthread_mutex_unlock(&stats_lock);
510
511 return 1;
512 }
513
514 // figure out where to put this packet
find_bucket(struct in_addr * ip_src)515 int find_bucket(struct in_addr *ip_src) {
516 int i, bucket=0;
517 time_t oldest=0;
518
519 // look for an existing bucket for this IP
520 for (i=0; i< option_x; i++ ){
521 // ip field of bucket seems to match the ip we are checking
522 if (bb[i]->ip_addr.s_addr == ip_src->s_addr) {
523 return i;
524 }
525 }
526
527 // look for unused buckets
528 for (i=0; i< option_x; i++ ) {
529
530 // found an unused one - clean it, init it, and return it
531 if ( bb[i]->ip_addr.s_addr == BCAST ) {
532 scour_bucket(i);
533 bb[i]->ip_addr.s_addr = ip_src->s_addr;
534 return i;
535 }
536
537 // find the most stagnant bucket in case we need it
538 // avoids another loop through the buckets
539 // TODO - should we autoflush buckets after some idle time,
540 // or after alarming? fixes the case where
541 // alarms are unlikely to reappear even if a client
542 // resumes flooding if there isn't bucket contention
543 // churning them out and resetting the timer for the rate
544 // calculation...
545
546 if ( ( bb[i]->last_packet != 0 ) && ((oldest==0)||( bb[i]->last_packet < oldest))) {
547 oldest = bb[i]->last_packet;
548 bucket = i;
549 }
550 }
551
552 // use the most stagnant bucket since all are in use
553 // clean it, init it, and return it
554 scour_bucket(bucket);
555 bb[i]->ip_addr.s_addr = ip_src->s_addr;
556
557 return bucket;
558 }
559
560 // handle all packets we throw at it
handle_IP(u_char * args,const struct pcap_pkthdr * pkthdr,const u_char * packet)561 void handle_IP(u_char *args, const struct pcap_pkthdr* pkthdr,const u_char* packet){
562 const struct ip* ip;
563 const struct my_dns *dns;
564 const struct tcphdr *tcp;
565 const struct udphdr *udp;
566 u_int length = pkthdr->len;
567 u_int caplen = pkthdr->caplen;
568 u_int hlen,off,version;
569 unsigned char dname[NS_MAXDNAME]="";
570 struct in_addr ip_src;
571 unsigned char *data;
572 u_int len,dpos;
573 u_int8_t qtype,tlen;
574
575 // skip the ethernet header
576 length -= sizeof(struct ether_header);
577
578 // make sure packet is a valid length
579 if (length < sizeof(struct ip)) {
580 return;
581 }
582
583 // snap off the ip portion
584 ip = (struct ip*)(packet + sizeof(struct ether_header));
585
586 // get utility params for sanity checking
587 len = ntohs(ip->ip_len);
588 hlen = ip->ip_hl;
589 version = ip->ip_v;
590
591 // let's not do ipv6 just yet
592 if(version != 4) {
593 return;
594 }
595
596 // make sure we have a sane header length
597 if(hlen < 5 ) {
598 return;
599 }
600
601 // do we have the everything we are supposed to?
602 if(length < len) {
603 return;
604 }
605
606 // make sure we are only processing the first fragment
607 off = ntohs(ip->ip_off);
608 if((off & 0x1fff) == 0 ) {
609
610 // get the source ip
611 ip_src.s_addr = ip->ip_src.s_addr;
612
613 // process udp packets
614 if ( ip->ip_p == 17 ) {
615 udp = (struct udphdr *) ( (char *) packet + sizeof(struct ether_header)+ sizeof (struct ip) );
616
617 // try to make sure it is safe to cast packet into dns structure
618 if ( (sizeof(struct my_dns)+sizeof(struct ether_header)+sizeof(struct ip)+sizeof(struct udphdr)) >= caplen ) {
619 return;
620 }
621 else {
622 // populate dns header
623 dns = (struct my_dns *) ( (char *) packet + sizeof(struct ether_header) + sizeof (struct ip) + sizeof (struct udphdr) );
624 data = (unsigned char *) packet +sizeof(struct ether_header) + sizeof (struct ip) + sizeof (struct udphdr) + sizeof(struct my_dns);
625 }
626 }
627
628 // process tcp packets
629 else if ( ip->ip_p == 6 ) {
630 tcp = (struct tcphdr *) ( (char *) packet + sizeof(struct ether_header)+ sizeof (struct ip) );
631
632 // ignore packets without push flag set
633 if (! (tcp->th_flags & TH_PUSH)) return;
634
635 // try to make sure it is safe to cast packet into dns structure
636 if ( (sizeof(struct my_dns)+sizeof(struct ether_header)+sizeof(struct ip)+(tcp->th_off * sizeof(u_int32_t)) + sizeof(u_int16_t)) >= caplen ) {
637 return;
638 }
639 else {
640 // populate dns header
641 // tcp dns lookups also include a 16bit length field = dns header + data.
642 dns = (struct my_dns *) ( (char *) packet + sizeof(struct ether_header)+ sizeof (struct ip) + (tcp->th_off * sizeof(u_int32_t) + sizeof(u_int16_t)));
643 data = (unsigned char *) packet + sizeof(struct ether_header) + sizeof (struct ip) + (tcp->th_off * sizeof(u_int32_t)) + sizeof(struct my_dns) + sizeof(u_int16_t);
644 }
645 }
646
647 // hmm.. not tcp, not udp.. move on.
648 else {
649 return;
650 }
651
652 // we only want queries, not responses
653 if ( dns->dns_flags1 & 0x80 ) {
654 return;
655 }
656
657 // ignore packets with no questions
658 if (ntohs(dns->dns_qdcount) == 0) {
659 return;
660 }
661
662 // get the domain name and query type
663 tlen=dpos=0;
664 for (;(*data)&&((void *)data<((void *)packet+caplen-1)); data++) {
665 if (!tlen) tlen=*data;
666 for (;(tlen&&((void *)data<((void *)packet+caplen-1)));tlen--){
667 data++;
668 // bail on an invalid dns char
669 if(!valid_dns_char(*data)) {
670 return;
671 }
672 if (dpos<NS_MAXDNAME) dname[dpos++] = *data;
673 }
674 if (dpos<NS_MAXDNAME) dname[dpos++] = '.';
675 }
676 dname[dpos]='\0';
677
678 // be careful not to walk past the end of the captured data
679 if ( (void *)data < ((void *)packet+caplen-3) ) {
680 data+=2;
681 qtype = *data;
682 }
683 else {
684 return;
685 }
686
687 if( option_D ) {
688 printf("src: %-15s proto: %s qtype: 0x%02x domain: %s\n", (inet_ntoa(ip_src)),
689 (ip->ip_p == 17 ? "udp" : "tcp"), qtype, dname);
690 }
691
692 // add packet to bucket array
693 if (ntohs(dns->dns_qdcount)&&qtype) {
694 add_to_bucket( &ip_src, ip->ip_p, 1, qtype );
695 }
696 }
697 return;
698 }
699
700 // main logic
701 // some pcap code borrowed from http://www.cet.nau.edu/~mc8/Socket/Tutorials/section1.html
main(int argc,char ** argv)702 int main(int argc,char **argv){
703 char *dev = NULL;
704 pthread_t thread;
705 char errbuf[PCAP_ERRBUF_SIZE];
706 pcap_t* descr;
707 struct bpf_program fp; /* hold compiled program */
708 bpf_u_int32 maskp=0; /* subnet mask */
709 bpf_u_int32 netp=0; /* ip */
710 char *filter = NULL;
711 char *dst_addr = NULL;
712 char *dst_mask = NULL;
713 struct sigaction sa;
714 struct in_addr addr,tmpaddr;
715 u_int f_size;
716 char *name = NULL;
717 u_int c = 0;
718
719 if ( ( name = (char *)strdup(argv[0]) ) == NULL) malloc_fail("name", strlen(argv[0]) );
720
721 // loop through command line options and get options
722 while(1) {
723 c = getopt(argc, argv,"i:t:a:w:x:m:A:M:QbdDvsh");
724
725 if (c==-1) break;
726 switch(c) {
727 case 0:
728 break;
729 case 'i':
730 if (optarg) {
731 if ( ( dev = (char *)strdup(optarg) ) == NULL) malloc_fail("dev", strlen(optarg) );
732 }
733 break;
734 case 't':
735 if (optarg) {
736 if ( abs (atoi(optarg)) > 0) {
737 option_t = abs( atoi(optarg));
738 }
739 }
740 break;
741 case 'a':
742 if (optarg) {
743 if ( abs (atoi(optarg)) > 10) {
744 option_a = abs( atoi(optarg));
745 }
746 }
747 break;
748 case 'w':
749 if (optarg) {
750 if ( abs (atoi(optarg)) > 1) {
751 option_w = abs( atoi(optarg));
752 }
753 }
754 break;
755 case 'x':
756 if (optarg) {
757 if ( abs (atoi(optarg)) > 10) {
758 option_x = abs( atoi(optarg));
759 }
760 }
761 break;
762 case 'm':
763 if (optarg) {
764 if ( abs (atoi(optarg)) > 0) {
765 option_m = abs( atoi(optarg));
766 }
767 }
768 break;
769 case 'M':
770 if (optarg && (dst_mask == NULL) ) {
771 if ( inet_aton(optarg, &tmpaddr) ) {
772 if ( ( dst_mask = (char *)strdup(optarg) ) == NULL) malloc_fail("filter mask", strlen(optarg) );
773 option_M=1;
774 } else {
775 fprintf(stderr,"Invalid filter mask \"%s\"\n",optarg);
776 option_h = 1;
777 }
778 }
779 break;
780 case 'A':
781 if (optarg && (dst_addr == NULL) ) {
782 if ( inet_aton(optarg, &tmpaddr) ) {
783 if ( ( dst_addr = (char *)strdup(optarg) ) == NULL) malloc_fail("dest filter", strlen(optarg) );
784 option_A=1;
785 } else {
786 fprintf(stderr,"Invalid filter address \"%s\"\n",optarg);
787 option_h = 1;
788 }
789 }
790 break;
791 case 'Q':
792 option_Q = 1;
793 break;
794 case 'b':
795 option_b = 1;
796 break;
797 case 'd':
798 option_d = 1;
799 break;
800 case 'D':
801 option_D = 1;
802 break;
803 case 'v':
804 option_v++;
805 break;
806 case 's':
807 option_s = 1;
808 break;
809 case 'h':
810 option_h = 1;
811 break;
812 case 'z':
813 target_ip = optarg;
814 break;
815 case 'p':
816 target_port = atoi(optarg);
817 break;
818 default:
819 break;
820 }
821 }
822
823 // display usage info if needed
824 if (optind<argc) option_h = 1;
825
826 if (option_h) {
827 fprintf(stderr,"dns_flood_detector, version %s\n",VERSION);
828 fprintf(stderr,"Usage: %s [OPTION]\n\n",name);
829 fprintf(stderr,"-i IFNAME specify device name to listen on\n");
830 fprintf(stderr,"-t N alarm at >N queries per second\n");
831 fprintf(stderr,"-a N reset alarm after N seconds\n");
832 fprintf(stderr,"-w N calculate stats every N seconds\n");
833 fprintf(stderr,"-x N create N buckets\n");
834 fprintf(stderr,"-m N report overall stats every N seconds\n");
835 fprintf(stderr,"-A addr filter for specific address\n");
836 fprintf(stderr,"-M mask netmask for filter (in conjunction with -A)\n");
837 fprintf(stderr,"-Q don't filter by local interface address\n");
838 fprintf(stderr,"-b run in foreground in bindsnap mode\n");
839 fprintf(stderr,"-d run in background in daemon mode\n");
840 fprintf(stderr,"-D dump dns packets (implies -b)\n");
841 fprintf(stderr,"-v verbose output - use again for more verbosity\n");
842 fprintf(stderr,"-s send source-IP data to network collector as JSON\n");
843 fprintf(stderr,"-z addr address to send stats to (default 226.1.1.2)\n");
844 fprintf(stderr,"-p N UDP port to send stats to (default 2000)\n");
845 fprintf(stderr,"-h display this usage information\n");
846 exit(1);
847 }
848
849 if ( target_ip == NULL ) {
850 target_ip = DEFAULT_IP;
851 }
852
853 // if dumping packets, force option_b and disable option_d
854 if( option_D ) {
855 if( ! option_b )
856 option_b = 1;
857
858 if( option_d )
859 option_d = 0;
860 }
861
862 if ( ( option_Q ) && ( option_A ) ) {
863 fprintf(stderr,"%s couldn't start\n",name);
864 fprintf(stderr,"You can't specify both -A (address filter) and -Q (no filter)\n");
865 exit(1);
866 }
867
868 if ( ( ! option_d ) && ( ! option_b ) ) {
869 fprintf(stderr,"%s couldn't start\n",name);
870 fprintf(stderr,"You must specify either -d (daemon) or -b (bindsnap)\n");
871 exit(1);
872 }
873 free(name);
874
875 // set up for daemonized operation unless running in bindsnap mode
876 if ( ! option_b ) {
877 openlog("dns_flood_detector",LOG_PID|LOG_CONS,LOG_DAEMON);
878 syslog(LOG_NOTICE,"dns_flood_detector starting");
879
880 // daemonize unless running in bindsnap mode
881 daemonize();
882
883 // set up signal handlers
884 sa.sa_handler=exit;
885 sa.sa_flags=0;
886 if(sigaction(SIGTERM,&sa,NULL)) {
887 syslog(LOG_ERR,"Unable to set signal handler: %s. Exiting.",strerror(errno));
888 }
889 }
890
891 // find a valid device to open
892 if(dev == NULL && ( (dev=pcap_lookupdev(errbuf)) == NULL ) ){
893 fprintf(stderr,"unable to bind to valid device\n");
894 exit(1);
895 }
896
897 /* restrict to queries to primary local address? */
898 if (option_Q) {
899 f_size = strlen("port 53 ");
900 if ( ( filter = (char *) malloc ( f_size+1) ) == NULL ) malloc_fail( "filter", f_size+1 );
901 snprintf( filter, f_size, "port 53");
902 } else {
903 if (! option_A) {
904 // get network address and netmask for device
905 pcap_lookupnet(dev,&netp,&maskp,errbuf);
906
907 // set up filter with local network
908 addr.s_addr = (unsigned long int)netp;
909 if ( ( dst_addr = (char *)malloc( strlen((char *)inet_ntoa(addr))+1) ) == NULL ) malloc_fail("dest_addr", strlen((char *)inet_ntoa(addr))+1 );
910 strncpy(dst_addr,(char*)inet_ntoa(addr),strlen((char *)inet_ntoa(addr)));
911 dst_addr[strlen((char *)inet_ntoa(addr))]='\0';
912
913 addr.s_addr = (unsigned long int)maskp;
914
915 if (!option_M) {
916 if ( ( dst_mask = (char *)malloc( strlen((char *)inet_ntoa(addr))+1) ) == NULL ) malloc_fail("dest_mask", strlen((char *)inet_ntoa(addr))+1 );
917 strncpy(dst_mask,(char*)inet_ntoa(addr),strlen((char *)inet_ntoa(addr)));
918 dst_mask[strlen((char *)inet_ntoa(addr))]='\0';
919 }
920 } else {
921 // we're using an address from -A
922 if (!option_M) {
923 // if no mask was specified, then use just a host mask
924 if ( ( dst_mask = (char *)malloc(16) ) == NULL ) malloc_fail("dest_mask", 16);
925 strncpy(dst_mask,"255.255.255.255",15);
926 }
927 }
928
929 f_size = strlen("port 53 and dst net mask ")+ strlen(dst_mask)+ strlen(dst_addr);
930 if ( ( filter = (char *) malloc ( f_size+1) ) == NULL ) malloc_fail( "filter", f_size+1 );
931 snprintf( filter, f_size, "port 53 and dst net %s mask %s", dst_addr, dst_mask);
932
933 free (dst_mask);
934 free (dst_addr);
935 }
936
937 if ( option_b && option_v ) {
938 printf("using filter \"%s\" on dev %s\n", filter, dev);
939 }
940
941 // open device for reading only local traffic
942 descr = pcap_open_live(dev,1500,0,1,errbuf);
943 if(descr == NULL) {
944 fprintf(stderr,"unable to open device %s\n",dev);
945 exit(1);
946 }
947
948 // compile filter
949 if(pcap_compile(descr,&fp,filter,0,netp) == -1) {
950 fprintf(stderr,"error compiling filter: %s\n",pcap_geterr(descr));
951 exit(1);
952 }
953
954 // set filter
955 if(pcap_setfilter(descr,&fp) == -1){
956 fprintf(stderr,"error setting filter: %s\n",pcap_geterr(descr));
957 exit(1);
958 }
959
960 // initialize buckets and mark overall stats bucket
961 init_buckets();
962 totals = option_x;
963
964 // create mutex lock
965 if (pthread_mutex_init(&stats_lock, NULL) < 0) {
966 exit(1);
967 }
968
969 // launch watcher thread
970 if (pthread_create (&thread, NULL, run_stats, (void *)0)) {
971 exit(1);
972 }
973
974 // main pcap loop
975 pcap_loop(descr,-1,&handle_IP,NULL);
976
977 // done
978 closelog();
979 return 0;
980 }
981
982 // daemonize the process
daemonize(void)983 int daemonize(void) {
984 pid_t pid;
985 int fd;
986 int a;
987
988 fd=open("/dev/null",O_RDWR);
989 if(fd<0) {
990 syslog(LOG_ERR,"Failed to open /dev/null: %s. Exiting.",strerror(errno));
991 exit(1);
992 }
993
994 dup2(fd,0);
995 dup2(fd,1);
996 dup2(fd,2);
997
998 if((pid=fork())<0) {
999 syslog(LOG_ERR,"Fork failed: %s. Exiting.",strerror(errno));
1000 exit(1);
1001 }
1002 else if (pid!=0) {
1003 exit(0);
1004 }
1005
1006 setsid();
1007 a = chdir("/");
1008 umask(0);
1009 return 0;
1010 }
1011
malloc_fail(char * var,int size)1012 int malloc_fail( char * var, int size ) {
1013 // print error to stderr if running in bindsnap mode
1014 if (option_b) {
1015 fprintf(stderr, "our OS wouldn't let me malloc %d bytes for a new %s. giving up", size, var);
1016 }
1017 else {
1018 syslog(LOG_ERR, "our OS wouldn't let me malloc %d bytes for a new %s. giving up", size, var);
1019 }
1020 exit(1);
1021 }
1022
microsleep(unsigned int usec)1023 int microsleep(unsigned int usec) {
1024 struct timeval timeout;
1025 timeout.tv_sec = usec / 1000000;
1026 timeout.tv_usec = usec % 1000000;
1027
1028 while ((select(0, (fd_set *) 0, (fd_set *) 0,(fd_set *) 0, &timeout) < 0) && (errno == EINTR));
1029 return(0);
1030 }
1031
1032