Imported Upstream version 1.20
This commit is contained in:
parent
912e105ee9
commit
63b4b0fa48
17
README
17
README
|
@ -1,4 +1,4 @@
|
||||||
DNS FLood Detector 1.12
|
DNS FLood Detector 1.2
|
||||||
Dennis Opacki
|
Dennis Opacki
|
||||||
dopacki@adotout.com
|
dopacki@adotout.com
|
||||||
|
|
||||||
|
@ -21,6 +21,16 @@ By default, it will count dns queries directed to any address in the same
|
||||||
network as the primary IP address on the interface being watched; the -A,
|
network as the primary IP address on the interface being watched; the -A,
|
||||||
-M, and -Q options can be used to modify this behaviour.
|
-M, and -Q options can be used to modify this behaviour.
|
||||||
|
|
||||||
|
As of version 1.2, DNS Flood Detector can now send source IP request
|
||||||
|
data to a network-based collector as JSON. This lets you gather near
|
||||||
|
real-time information about who is using your DNS servers, and from
|
||||||
|
where. I've included a sample application called dns_flood_collector.pl,
|
||||||
|
which you can use to receive and report these data. The output of this
|
||||||
|
program can be easily fed into a graphing tool, such as Caida's
|
||||||
|
plot-latlong:
|
||||||
|
|
||||||
|
http://www.caida.org/tools/visualization/plot-latlong/
|
||||||
|
|
||||||
How do I build it?
|
How do I build it?
|
||||||
|
|
||||||
Execute ./configure.pl to select the appropriate make target. Then simply
|
Execute ./configure.pl to select the appropriate make target. Then simply
|
||||||
|
@ -41,7 +51,7 @@ What platforms does it work on?
|
||||||
|
|
||||||
Linux, BSDI, FreeBSD, Mac OSX, Solaris
|
Linux, BSDI, FreeBSD, Mac OSX, Solaris
|
||||||
|
|
||||||
Will it run under Windows {95,98,NT,2000,XP}?
|
Will it run under Windows {95,98,NT,2000,XP,2003,2008 or Win7}?
|
||||||
|
|
||||||
Maybe. I haven't tried. If it doesn't, feel free to submit a fix.
|
Maybe. I haven't tried. If it doesn't, feel free to submit a fix.
|
||||||
|
|
||||||
|
@ -62,6 +72,9 @@ Usage: ./dns_flood_detector [OPTION]
|
||||||
-d run in background in daemon mode
|
-d run in background in daemon mode
|
||||||
-D dump dns packets (implies -b)
|
-D dump dns packets (implies -b)
|
||||||
-v verbose output - use again for more verbosity
|
-v verbose output - use again for more verbosity
|
||||||
|
-s send source IP stats to collector as JSON
|
||||||
|
-z N.N.N.N address to send stats to (default 226.1.1.2)
|
||||||
|
-p N UDP port to send stats to (default 2000)
|
||||||
-h display this usage information
|
-h display this usage information
|
||||||
|
|
||||||
Sample Output:
|
Sample Output:
|
||||||
|
|
157
dns_flood_collector.pl
Executable file
157
dns_flood_collector.pl
Executable file
|
@ -0,0 +1,157 @@
|
||||||
|
#!/usr/bin/perl
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use threads;
|
||||||
|
use threads::shared;
|
||||||
|
use Sys::Syslog;
|
||||||
|
use Data::Dumper;
|
||||||
|
use Getopt::Long;
|
||||||
|
use POSIX;
|
||||||
|
use IO::Socket::Multicast;
|
||||||
|
use JSON;
|
||||||
|
|
||||||
|
# Native Maxmind library - http://www.maxmind.com/download/geoip/api/perl/
|
||||||
|
# requires: http://www.maxmind.com/app/c
|
||||||
|
use Geo::IP;
|
||||||
|
|
||||||
|
# set these to the same port and multicast (or unicast) address as the detector
|
||||||
|
use constant GROUP => '226.1.1.2';
|
||||||
|
use constant PORT => '2000';
|
||||||
|
|
||||||
|
my %ipc_source :shared;
|
||||||
|
my %ipc_customer :shared;
|
||||||
|
my $time_to_die :shared = 0;
|
||||||
|
my $debug;
|
||||||
|
my $foreground=0;
|
||||||
|
|
||||||
|
# determines how often you want to aggregage and write-out stats dumps
|
||||||
|
my $interval = 60;
|
||||||
|
|
||||||
|
# you can get the binary format GeoLiteCity.dat from Maxmind
|
||||||
|
# http://www.maxmind.com/app/geolitecity
|
||||||
|
my $gi = Geo::IP->open("/usr/local/GeoLiteCity.dat",GEOIP_MEMORY_CACHE | GEOIP_CHECK_CACHE);
|
||||||
|
|
||||||
|
# adjust this to the path where you want to keep the
|
||||||
|
sub PATH {'/tmp/'}
|
||||||
|
|
||||||
|
$|=1;
|
||||||
|
|
||||||
|
GetOptions(
|
||||||
|
"debug" => \$debug,
|
||||||
|
"foreground" => \$foreground,
|
||||||
|
"interval=s" => \$interval,
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
main();
|
||||||
|
exit();
|
||||||
|
|
||||||
|
sub main() {
|
||||||
|
|
||||||
|
# daemonize unless running in foreground
|
||||||
|
unless ($foreground){
|
||||||
|
daemonize();
|
||||||
|
}
|
||||||
|
|
||||||
|
# prepare data acquisition thread
|
||||||
|
threads->new(\&get_data);
|
||||||
|
|
||||||
|
while (! $time_to_die ) {
|
||||||
|
|
||||||
|
# record time started to help evenly space runs
|
||||||
|
my $start_run = time();
|
||||||
|
my $next_run = $start_run + $interval;
|
||||||
|
|
||||||
|
# de-serialize latest copy of source address structure
|
||||||
|
# execute this in a isolated scope so that lock goes out of scope
|
||||||
|
{
|
||||||
|
my $source_distance;
|
||||||
|
|
||||||
|
# lock data structure to prevent other thread from updating it
|
||||||
|
lock(%ipc_source);
|
||||||
|
|
||||||
|
# open coordinates file for graph generation
|
||||||
|
open(CRDS, ">".PATH."/coords.txt.tmp");
|
||||||
|
|
||||||
|
# calculate great circle distance between each source IP and local POP
|
||||||
|
foreach my $key (keys %ipc_source) {
|
||||||
|
|
||||||
|
eval {
|
||||||
|
my $r = $gi->record_by_addr($key);
|
||||||
|
|
||||||
|
# write raw entry to coordinates file
|
||||||
|
print CRDS $key.",".$ipc_source{$key}.",".$r->latitude.",".$r->longitude."\n";
|
||||||
|
};
|
||||||
|
if ($@) {
|
||||||
|
print CRDS $key.",".$ipc_source{$key}.",0,0\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# close coordinate file
|
||||||
|
close CRDS;
|
||||||
|
system("mv ".PATH."/coords.txt.tmp ".PATH."/coords.txt");
|
||||||
|
|
||||||
|
# clean out structure for next sample period
|
||||||
|
%ipc_source = ();
|
||||||
|
}
|
||||||
|
|
||||||
|
# sleep to make the interval
|
||||||
|
while((my $time_left = ($next_run - time())) > 0) {
|
||||||
|
sleep($time_left);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
threads->join();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
# fetch data from UDP multicast
|
||||||
|
sub get_data() {
|
||||||
|
|
||||||
|
# set up our multicast listener
|
||||||
|
# note: this will receive unicast fine too
|
||||||
|
my $sock = IO::Socket::Multicast->new(LocalPort=>PORT,ReuseAddr=>1);
|
||||||
|
$sock->mcast_add(GROUP) || die "Couldn't set group: $!\n";
|
||||||
|
|
||||||
|
|
||||||
|
while ( ! $time_to_die ) {
|
||||||
|
my $data;
|
||||||
|
next unless $sock->recv($data,1500);
|
||||||
|
|
||||||
|
# decode JSON
|
||||||
|
eval {
|
||||||
|
my $obj = decode_json $data;
|
||||||
|
print Dumper $obj;
|
||||||
|
foreach my $ip (keys %{$obj->{data}}) {
|
||||||
|
my $count = $obj->{data}->{$ip};
|
||||||
|
lock(%ipc_source);
|
||||||
|
$ipc_source{$ip}+=$count;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
# done!
|
||||||
|
threads->exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
# daemonize application
|
||||||
|
sub daemonize {
|
||||||
|
|
||||||
|
chdir '/' or die "Can't chdir to /: $!";
|
||||||
|
open STDIN, '/dev/null' or die "Can't read /dev/null: $!";
|
||||||
|
open STDOUT, '>/dev/null';
|
||||||
|
|
||||||
|
# fork and exit parent
|
||||||
|
my $pid = fork();
|
||||||
|
exit if $pid;
|
||||||
|
die "Couldn't fork: $!" unless defined ($pid);
|
||||||
|
POSIX::setsid() || die ("$0 can't start a new session: $!");
|
||||||
|
open STDERR, '>&STDOUT' or die "Can't dup stdout: $!";
|
||||||
|
|
||||||
|
# signal handlers
|
||||||
|
$SIG{KILL} = \&handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub handler {
|
||||||
|
$time_to_die = 1;
|
||||||
|
}
|
|
@ -30,6 +30,10 @@
|
||||||
-d run in background in "daemon" mode
|
-d run in background in "daemon" mode
|
||||||
-D dump dns packets (implies -b)
|
-D dump dns packets (implies -b)
|
||||||
-v detailed information (use twice for more detail)
|
-v detailed information (use twice for more detail)
|
||||||
|
-s send source IP stats to collector as JSON
|
||||||
|
-z N.N.N.N address to send stats to (default 226.1.1.2)
|
||||||
|
-p N UDP port to send stats to (default 2000)
|
||||||
|
|
||||||
-h usage info
|
-h usage info
|
||||||
|
|
||||||
Copyright (C) 2003 Dennis Opacki
|
Copyright (C) 2003 Dennis Opacki
|
||||||
|
@ -93,6 +97,9 @@
|
||||||
store addresses raw, instead of as text (speedup/reduce memory usage) - <erikm@buh.org>
|
store addresses raw, instead of as text (speedup/reduce memory usage) - <erikm@buh.org>
|
||||||
fix crash on long syslog messages - <jwestfall@surrealistic.net>
|
fix crash on long syslog messages - <jwestfall@surrealistic.net>
|
||||||
|
|
||||||
|
--- new in v1.2 ---
|
||||||
|
05/10/2012 - added sending of source-IP telemetry to a network collector - <dopacki@adotout.com>
|
||||||
|
|
||||||
********************************************************************************/
|
********************************************************************************/
|
||||||
|
|
||||||
#include <pcap.h>
|
#include <pcap.h>
|
||||||
|
@ -124,14 +131,17 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include "dns_flood_detector.h"
|
#include "dns_flood_detector.h"
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/select.h>
|
||||||
|
|
||||||
// global variables and their defaults
|
// global variables and their defaults
|
||||||
pthread_mutex_t stats_lock;
|
pthread_mutex_t stats_lock;
|
||||||
struct bucket **bb;
|
struct bucket **bb;
|
||||||
int option_t = 60;
|
int option_t = 60;
|
||||||
int option_a = 90;
|
int option_a = 90;
|
||||||
int option_w = 10;
|
int option_w = 20;
|
||||||
int option_x = 50;
|
int option_x = 10000;
|
||||||
int option_m = 0;
|
int option_m = 0;
|
||||||
int option_b = 0;
|
int option_b = 0;
|
||||||
int option_d = 0;
|
int option_d = 0;
|
||||||
|
@ -141,25 +151,148 @@ int option_h = 0;
|
||||||
int option_Q = 0;
|
int option_Q = 0;
|
||||||
int option_A = 0;
|
int option_A = 0;
|
||||||
int option_M = 0;
|
int option_M = 0;
|
||||||
|
int option_s = 0;
|
||||||
int totals = 0;
|
int totals = 0;
|
||||||
char VERSION[] = "1.12";
|
|
||||||
|
static char *target_ip = NULL;
|
||||||
|
int target_port = DEFAULT_PORT;
|
||||||
|
int mcast_ttl = 10;
|
||||||
|
char hostname[HOST_NAME_MAX];
|
||||||
|
|
||||||
|
char VERSION[] = "1.2";
|
||||||
|
|
||||||
// 255.255.255.255 is invalid as a src IP address; we'll use it to mark empty buckets
|
// 255.255.255.255 is invalid as a src IP address; we'll use it to mark empty buckets
|
||||||
#define BCAST 0xffFFffFF
|
#define BCAST 0xffFFffFF
|
||||||
|
|
||||||
// this is our statistics thread
|
// this is our statistics thread
|
||||||
void *run_stats () {
|
void *run_stats () {
|
||||||
|
// prepare multicast socket
|
||||||
|
struct sockaddr_in addr;
|
||||||
|
int sock;
|
||||||
|
sock = socket(AF_INET, SOCK_DGRAM,0);
|
||||||
|
|
||||||
|
if (sock<0) {
|
||||||
|
perror("can't set up socket");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// is it harmful to set this on non-multicast sockets?
|
||||||
|
setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, (char *)&mcast_ttl, sizeof(mcast_ttl));
|
||||||
|
|
||||||
|
bzero((char*)&addr, sizeof(addr));
|
||||||
|
addr.sin_family = AF_INET;
|
||||||
|
addr.sin_addr.s_addr = inet_addr(target_ip);
|
||||||
|
addr.sin_port = htons(target_port);
|
||||||
|
|
||||||
|
// get our hostname
|
||||||
|
gethostname(hostname, HOST_NAME_MAX-1);
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
|
|
||||||
// check statistical stuff
|
// check statistical stuff
|
||||||
pthread_mutex_lock(&stats_lock);
|
pthread_mutex_lock(&stats_lock);
|
||||||
calculate_averages();
|
calculate_averages();
|
||||||
|
saddr_stats(sock,addr,hostname);
|
||||||
pthread_mutex_unlock(&stats_lock);
|
pthread_mutex_unlock(&stats_lock);
|
||||||
|
|
||||||
sleep (option_w);
|
sleep (option_w);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// report saddr stats
|
||||||
|
int saddr_stats(int sock, struct sockaddr_in addr, char *hostname) {
|
||||||
|
u_int i;
|
||||||
|
int addrlen;
|
||||||
|
char buff[MAXMESSAGE];
|
||||||
|
int buffhead = 0;
|
||||||
|
char st_time[10];
|
||||||
|
time_t now = time(0);
|
||||||
|
struct tm *raw_time = localtime(&now);
|
||||||
|
addrlen = sizeof(addr);
|
||||||
|
snprintf(st_time, 9, "%02d:%02d:%02d",raw_time->tm_hour,raw_time->tm_min,raw_time->tm_sec);
|
||||||
|
|
||||||
|
// prepare jason structure for multicast datagrams
|
||||||
|
char head[MAXHEAD];
|
||||||
|
char *tail = "}}";
|
||||||
|
snprintf(head,MAXHEAD,"{\"hostname\":\"%s\",\"type\":\"source\",\"data\":{",hostname);
|
||||||
|
int netsize = MAXMESSAGE - strlen(head) - strlen(tail);
|
||||||
|
if (netsize<=0) exit(EXIT_FAILURE); // this should never ever happen
|
||||||
|
|
||||||
|
int avail = netsize;
|
||||||
|
int dlen = 0;
|
||||||
|
char datalet[MAXDATALET];
|
||||||
|
|
||||||
|
// copy the initial json header into the buffer
|
||||||
|
bzero(buff,sizeof(buff));
|
||||||
|
memcpy(buff,head,strlen(head));
|
||||||
|
buffhead = buffhead + strlen(head);
|
||||||
|
|
||||||
|
// report all source address stats, cleaning up afterward
|
||||||
|
for (i=0; ( (i < option_x) && ( bb[i]->ip_addr.s_addr != 0 ) ); i++) {
|
||||||
|
|
||||||
|
if ( bb[i]->ip_addr.s_addr != BCAST ) {
|
||||||
|
|
||||||
|
// prepare a datalet
|
||||||
|
snprintf(datalet,MAXDATALET,"\"%s\":%d,",inet_ntoa(bb[i]->ip_addr),bb[i]->udp_count+bb[i]->tcp_count);
|
||||||
|
dlen = strlen(datalet);
|
||||||
|
|
||||||
|
// see if the current datagram has room for the datalet
|
||||||
|
if ( avail > dlen ) {
|
||||||
|
|
||||||
|
// append this datalet to the current datagram (minus null terminator)
|
||||||
|
avail = avail - dlen;
|
||||||
|
memcpy(buff+buffhead,datalet,dlen);
|
||||||
|
buffhead = buffhead + dlen;
|
||||||
|
}
|
||||||
|
// no room in current dgram
|
||||||
|
else {
|
||||||
|
|
||||||
|
// remove trailing comma from the buffer so we can close it out
|
||||||
|
buffhead = buffhead - 1;
|
||||||
|
|
||||||
|
// add the tail
|
||||||
|
strncpy(buff+buffhead,tail, strlen(tail));
|
||||||
|
|
||||||
|
// send the transmission if option_s is set
|
||||||
|
if (option_s > 0 ) {
|
||||||
|
sendto(sock,buff,strlen(buff)+1,0,(struct sockaddr *) &addr, addrlen);
|
||||||
|
microsleep(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// init next datagram
|
||||||
|
bzero(buff,sizeof(buff));
|
||||||
|
memcpy(buff,head,strlen(head));
|
||||||
|
buffhead = strlen(head);
|
||||||
|
avail = netsize;
|
||||||
|
|
||||||
|
// append this datalet to the current datagram (minus null terminatin)
|
||||||
|
avail = avail - dlen;
|
||||||
|
memcpy(buff+buffhead,datalet,dlen);
|
||||||
|
buffhead = buffhead + dlen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scour_bucket(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// transmit final buffer contents if needed
|
||||||
|
if ( ( option_b == 0) && (buffhead>strlen(head)) ) {
|
||||||
|
|
||||||
|
// remove trailing comma
|
||||||
|
buffhead = buffhead - 1;
|
||||||
|
|
||||||
|
// add the tail
|
||||||
|
strncpy(buff+buffhead,tail,strlen(tail));
|
||||||
|
|
||||||
|
// send the multicast transmission
|
||||||
|
sendto(sock,buff,strlen(buff)+1,0,(struct sockaddr *) &addr, addrlen);
|
||||||
|
microsleep(10);
|
||||||
|
|
||||||
|
bzero(buff,sizeof(buff));
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// calculate the running average within each bucket
|
// calculate the running average within each bucket
|
||||||
int calculate_averages() {
|
int calculate_averages() {
|
||||||
u_int i,j,delta,cursize,qps;
|
u_int i,j,delta,cursize,qps;
|
||||||
|
@ -188,7 +321,6 @@ int calculate_averages() {
|
||||||
// handle threshold crossing
|
// handle threshold crossing
|
||||||
if ( bb[i]->qps > option_t ) {
|
if ( bb[i]->qps > option_t ) {
|
||||||
|
|
||||||
|
|
||||||
// display detail to either syslog or stdout
|
// display detail to either syslog or stdout
|
||||||
if ( option_b ) {
|
if ( option_b ) {
|
||||||
if ( ! option_v ) {
|
if ( ! option_v ) {
|
||||||
|
@ -198,8 +330,8 @@ int calculate_averages() {
|
||||||
else {
|
else {
|
||||||
printf("[%s] source [%s] - %u qps tcp : %u qps udp ",st_time,inet_ntoa(bb[i]->ip_addr),
|
printf("[%s] source [%s] - %u qps tcp : %u qps udp ",st_time,inet_ntoa(bb[i]->ip_addr),
|
||||||
(u_int)ceil( ((float)bb[i]->tcp_count/delta)),
|
(u_int)ceil( ((float)bb[i]->tcp_count/delta)),
|
||||||
(u_int)ceil( ((float)bb[i]->udp_count/delta))
|
(u_int)ceil( ((float)bb[i]->udp_count/delta)));
|
||||||
);
|
|
||||||
if ( option_v >1 ) {
|
if ( option_v >1 ) {
|
||||||
for (j=0;types[j];j++) {
|
for (j=0;types[j];j++) {
|
||||||
qps = (u_int)ceil((float)bb[i]->qstats[types[j]]/delta);
|
qps = (u_int)ceil((float)bb[i]->qstats[types[j]]/delta);
|
||||||
|
@ -225,8 +357,7 @@ int calculate_averages() {
|
||||||
newsize = MAXSYSLOG;
|
newsize = MAXSYSLOG;
|
||||||
cursize = snprintf(target,newsize,"source [%s] - %u tcp qps : %u udp qps ",inet_ntoa(bb[i]->ip_addr),
|
cursize = snprintf(target,newsize,"source [%s] - %u tcp qps : %u udp qps ",inet_ntoa(bb[i]->ip_addr),
|
||||||
(u_int)ceil( ((float)bb[i]->tcp_count/delta)),
|
(u_int)ceil( ((float)bb[i]->tcp_count/delta)),
|
||||||
(u_int)ceil( ((float)bb[i]->udp_count/delta))
|
(u_int)ceil( ((float)bb[i]->udp_count/delta)));
|
||||||
);
|
|
||||||
newsize-=cursize;
|
newsize-=cursize;
|
||||||
|
|
||||||
for (j=0;types[j];j++ ) {
|
for (j=0;types[j];j++ ) {
|
||||||
|
@ -245,8 +376,7 @@ int calculate_averages() {
|
||||||
else {
|
else {
|
||||||
syslog(LOG_NOTICE,"source [%s] - %u tcp qps - %u udp qps\n",inet_ntoa(bb[i]->ip_addr),
|
syslog(LOG_NOTICE,"source [%s] - %u tcp qps - %u udp qps\n",inet_ntoa(bb[i]->ip_addr),
|
||||||
(u_int)ceil( ((float)bb[i]->tcp_count/delta)),
|
(u_int)ceil( ((float)bb[i]->tcp_count/delta)),
|
||||||
(u_int)ceil( ((float)bb[i]->udp_count/delta))
|
(u_int)ceil( ((float)bb[i]->udp_count/delta)));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset alarm
|
// reset alarm
|
||||||
|
@ -283,8 +413,7 @@ int calculate_averages() {
|
||||||
newsize = MAXSYSLOG;
|
newsize = MAXSYSLOG;
|
||||||
cursize = snprintf(target,newsize,"[totals] - %3.2f tcp qps : %3.2f udp qps ",
|
cursize = snprintf(target,newsize,"[totals] - %3.2f tcp qps : %3.2f udp qps ",
|
||||||
((float)bb[totals]->tcp_count/delta),
|
((float)bb[totals]->tcp_count/delta),
|
||||||
((float)bb[totals]->udp_count/delta)
|
((float)bb[totals]->udp_count/delta));
|
||||||
);
|
|
||||||
newsize-=cursize;
|
newsize-=cursize;
|
||||||
|
|
||||||
for (j=0;types[j];j++ ) {
|
for (j=0;types[j];j++ ) {
|
||||||
|
@ -303,13 +432,11 @@ int calculate_averages() {
|
||||||
else {
|
else {
|
||||||
syslog(LOG_NOTICE,"[totals] - %3.2f tcp qps : %3.2f udp qps\n",
|
syslog(LOG_NOTICE,"[totals] - %3.2f tcp qps : %3.2f udp qps\n",
|
||||||
((float)bb[totals]->tcp_count/delta),
|
((float)bb[totals]->tcp_count/delta),
|
||||||
((float)bb[totals]->udp_count/delta)
|
((float)bb[totals]->udp_count/delta));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
scour_bucket(totals);
|
scour_bucket(totals);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -324,6 +451,7 @@ int valid_dns_char(char c) {
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// purge and initialize all buckets
|
// purge and initialize all buckets
|
||||||
void init_buckets() {
|
void init_buckets() {
|
||||||
u_int i;
|
u_int i;
|
||||||
|
@ -353,6 +481,7 @@ int scour_bucket( int i ) {
|
||||||
for (j=0;j<256;j++) {
|
for (j=0;j<256;j++) {
|
||||||
bb[i]->qstats[j]=0;
|
bb[i]->qstats[j]=0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -413,6 +542,7 @@ int find_bucket(struct in_addr *ip_src) {
|
||||||
// resumes flooding if there isn't bucket contention
|
// resumes flooding if there isn't bucket contention
|
||||||
// churning them out and resetting the timer for the rate
|
// churning them out and resetting the timer for the rate
|
||||||
// calculation...
|
// calculation...
|
||||||
|
|
||||||
if ( ( bb[i]->last_packet != 0 ) && ((oldest==0)||( bb[i]->last_packet < oldest))) {
|
if ( ( bb[i]->last_packet != 0 ) && ((oldest==0)||( bb[i]->last_packet < oldest))) {
|
||||||
oldest = bb[i]->last_packet;
|
oldest = bb[i]->last_packet;
|
||||||
bucket = i;
|
bucket = i;
|
||||||
|
@ -491,7 +621,7 @@ void handle_IP(u_char *args, const struct pcap_pkthdr* pkthdr,const u_char* pack
|
||||||
else {
|
else {
|
||||||
// populate dns header
|
// populate dns header
|
||||||
dns = (struct my_dns *) ( (char *) packet + sizeof(struct ether_header) + sizeof (struct ip) + sizeof (struct udphdr) );
|
dns = (struct my_dns *) ( (char *) packet + sizeof(struct ether_header) + sizeof (struct ip) + sizeof (struct udphdr) );
|
||||||
data = (char *) packet +sizeof(struct ether_header) + sizeof (struct ip) + sizeof (struct udphdr) + sizeof(struct my_dns);
|
data = (unsigned char *) packet +sizeof(struct ether_header) + sizeof (struct ip) + sizeof (struct udphdr) + sizeof(struct my_dns);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -500,7 +630,7 @@ void handle_IP(u_char *args, const struct pcap_pkthdr* pkthdr,const u_char* pack
|
||||||
tcp = (struct tcphdr *) ( (char *) packet + sizeof(struct ether_header)+ sizeof (struct ip) );
|
tcp = (struct tcphdr *) ( (char *) packet + sizeof(struct ether_header)+ sizeof (struct ip) );
|
||||||
|
|
||||||
// ignore packets without push flag set
|
// ignore packets without push flag set
|
||||||
if (! tcp->th_flags & TH_PUSH) return;
|
if (! (tcp->th_flags & TH_PUSH)) return;
|
||||||
|
|
||||||
// try to make sure it is safe to cast packet into dns structure
|
// try to make sure it is safe to cast packet into dns structure
|
||||||
if ( (sizeof(struct my_dns)+sizeof(struct ether_header)+sizeof(struct ip)+(tcp->th_off * sizeof(u_int32_t)) + sizeof(u_int16_t)) >= caplen ) {
|
if ( (sizeof(struct my_dns)+sizeof(struct ether_header)+sizeof(struct ip)+(tcp->th_off * sizeof(u_int32_t)) + sizeof(u_int16_t)) >= caplen ) {
|
||||||
|
@ -510,7 +640,7 @@ void handle_IP(u_char *args, const struct pcap_pkthdr* pkthdr,const u_char* pack
|
||||||
// populate dns header
|
// populate dns header
|
||||||
// tcp dns lookups also include a 16bit length field = dns header + data.
|
// tcp dns lookups also include a 16bit length field = dns header + data.
|
||||||
dns = (struct my_dns *) ( (char *) packet + sizeof(struct ether_header)+ sizeof (struct ip) + (tcp->th_off * sizeof(u_int32_t) + sizeof(u_int16_t)));
|
dns = (struct my_dns *) ( (char *) packet + sizeof(struct ether_header)+ sizeof (struct ip) + (tcp->th_off * sizeof(u_int32_t) + sizeof(u_int16_t)));
|
||||||
data = (char *) packet + sizeof(struct ether_header) + sizeof (struct ip) + (tcp->th_off * sizeof(u_int32_t)) + sizeof(struct my_dns) + sizeof(u_int16_t);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -569,7 +699,7 @@ void handle_IP(u_char *args, const struct pcap_pkthdr* pkthdr,const u_char* pack
|
||||||
|
|
||||||
// main logic
|
// main logic
|
||||||
// some pcap code borrowed from http://www.cet.nau.edu/~mc8/Socket/Tutorials/section1.html
|
// some pcap code borrowed from http://www.cet.nau.edu/~mc8/Socket/Tutorials/section1.html
|
||||||
int main(int argc,char **argv){
|
int main(int argc,char **argv){
|
||||||
char *dev = NULL;
|
char *dev = NULL;
|
||||||
pthread_t thread;
|
pthread_t thread;
|
||||||
char errbuf[PCAP_ERRBUF_SIZE];
|
char errbuf[PCAP_ERRBUF_SIZE];
|
||||||
|
@ -583,14 +713,14 @@ int main(int argc,char **argv){
|
||||||
struct sigaction sa;
|
struct sigaction sa;
|
||||||
struct in_addr addr,tmpaddr;
|
struct in_addr addr,tmpaddr;
|
||||||
u_int f_size;
|
u_int f_size;
|
||||||
char *args = NULL;
|
|
||||||
char *name = NULL;
|
char *name = NULL;
|
||||||
u_int c = 0;
|
u_int c = 0;
|
||||||
|
|
||||||
if ( ( name = (char *)strdup(argv[0]) ) == NULL) malloc_fail("name", strlen(argv[0]) );
|
if ( ( name = (char *)strdup(argv[0]) ) == NULL) malloc_fail("name", strlen(argv[0]) );
|
||||||
|
|
||||||
// loop through command line options and get options
|
// loop through command line options and get options
|
||||||
while(1) {
|
while(1) {
|
||||||
c = getopt(argc, argv,"i:t:a:w:x:m:A:M:QbdDvh");
|
c = getopt(argc, argv,"i:t:a:w:x:m:A:M:QbdDvsh");
|
||||||
|
|
||||||
if (c==-1) break;
|
if (c==-1) break;
|
||||||
switch(c) {
|
switch(c) {
|
||||||
|
@ -673,8 +803,18 @@ int main(int argc,char **argv){
|
||||||
case 'v':
|
case 'v':
|
||||||
option_v++;
|
option_v++;
|
||||||
break;
|
break;
|
||||||
|
case 's':
|
||||||
|
option_s = 1;
|
||||||
|
break;
|
||||||
case 'h':
|
case 'h':
|
||||||
option_h = 1;
|
option_h = 1;
|
||||||
|
break;
|
||||||
|
case 'z':
|
||||||
|
target_ip = optarg;
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
target_port = atoi(optarg);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -682,6 +822,7 @@ int main(int argc,char **argv){
|
||||||
|
|
||||||
// display usage info if needed
|
// display usage info if needed
|
||||||
if (optind<argc) option_h = 1;
|
if (optind<argc) option_h = 1;
|
||||||
|
|
||||||
if (option_h) {
|
if (option_h) {
|
||||||
fprintf(stderr,"dns_flood_detector, version %s\n",VERSION);
|
fprintf(stderr,"dns_flood_detector, version %s\n",VERSION);
|
||||||
fprintf(stderr,"Usage: %s [OPTION]\n\n",name);
|
fprintf(stderr,"Usage: %s [OPTION]\n\n",name);
|
||||||
|
@ -698,10 +839,17 @@ int main(int argc,char **argv){
|
||||||
fprintf(stderr,"-d run in background in daemon mode\n");
|
fprintf(stderr,"-d run in background in daemon mode\n");
|
||||||
fprintf(stderr,"-D dump dns packets (implies -b)\n");
|
fprintf(stderr,"-D dump dns packets (implies -b)\n");
|
||||||
fprintf(stderr,"-v verbose output - use again for more verbosity\n");
|
fprintf(stderr,"-v verbose output - use again for more verbosity\n");
|
||||||
|
fprintf(stderr,"-s send source-IP data to network collector as JSON\n");
|
||||||
|
fprintf(stderr,"-z addr address to send stats to (default 226.1.1.2)\n");
|
||||||
|
fprintf(stderr,"-p N UDP port to send stats to (default 2000)\n");
|
||||||
fprintf(stderr,"-h display this usage information\n");
|
fprintf(stderr,"-h display this usage information\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( target_ip == NULL ) {
|
||||||
|
target_ip = DEFAULT_IP;
|
||||||
|
}
|
||||||
|
|
||||||
// if dumping packets, force option_b and disable option_d
|
// if dumping packets, force option_b and disable option_d
|
||||||
if( option_D ) {
|
if( option_D ) {
|
||||||
if( ! option_b )
|
if( ! option_b )
|
||||||
|
@ -709,7 +857,6 @@ int main(int argc,char **argv){
|
||||||
|
|
||||||
if( option_d )
|
if( option_d )
|
||||||
option_d = 0;
|
option_d = 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ( option_Q ) && ( option_A ) ) {
|
if ( ( option_Q ) && ( option_A ) ) {
|
||||||
|
@ -717,12 +864,14 @@ int main(int argc,char **argv){
|
||||||
fprintf(stderr,"You can't specify both -A (address filter) and -Q (no filter)\n");
|
fprintf(stderr,"You can't specify both -A (address filter) and -Q (no filter)\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ( ! option_d ) && ( ! option_b ) ) {
|
if ( ( ! option_d ) && ( ! option_b ) ) {
|
||||||
fprintf(stderr,"%s couldn't start\n",name);
|
fprintf(stderr,"%s couldn't start\n",name);
|
||||||
fprintf(stderr,"You must specify either -d (daemon) or -b (bindsnap)\n");
|
fprintf(stderr,"You must specify either -d (daemon) or -b (bindsnap)\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
free(name);
|
free(name);
|
||||||
|
|
||||||
// set up for daemonized operation unless running in bindsnap mode
|
// set up for daemonized operation unless running in bindsnap mode
|
||||||
if ( ! option_b ) {
|
if ( ! option_b ) {
|
||||||
openlog("dns_flood_detector",LOG_PID|LOG_CONS,LOG_DAEMON);
|
openlog("dns_flood_detector",LOG_PID|LOG_CONS,LOG_DAEMON);
|
||||||
|
@ -735,8 +884,7 @@ int main(int argc,char **argv){
|
||||||
sa.sa_handler=exit;
|
sa.sa_handler=exit;
|
||||||
sa.sa_flags=0;
|
sa.sa_flags=0;
|
||||||
if(sigaction(SIGTERM,&sa,NULL)) {
|
if(sigaction(SIGTERM,&sa,NULL)) {
|
||||||
syslog(LOG_ERR,"Unable to set signal handler: %s. Exiting.",
|
syslog(LOG_ERR,"Unable to set signal handler: %s. Exiting.",strerror(errno));
|
||||||
strerror(errno));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -763,6 +911,7 @@ int main(int argc,char **argv){
|
||||||
dst_addr[strlen((char *)inet_ntoa(addr))]='\0';
|
dst_addr[strlen((char *)inet_ntoa(addr))]='\0';
|
||||||
|
|
||||||
addr.s_addr = (unsigned long int)maskp;
|
addr.s_addr = (unsigned long int)maskp;
|
||||||
|
|
||||||
if (!option_M) {
|
if (!option_M) {
|
||||||
if ( ( dst_mask = (char *)malloc( strlen((char *)inet_ntoa(addr))+1) ) == NULL ) malloc_fail("dest_mask", strlen((char *)inet_ntoa(addr))+1 );
|
if ( ( dst_mask = (char *)malloc( strlen((char *)inet_ntoa(addr))+1) ) == NULL ) malloc_fail("dest_mask", strlen((char *)inet_ntoa(addr))+1 );
|
||||||
strncpy(dst_mask,(char*)inet_ntoa(addr),strlen((char *)inet_ntoa(addr)));
|
strncpy(dst_mask,(char*)inet_ntoa(addr),strlen((char *)inet_ntoa(addr)));
|
||||||
|
@ -788,6 +937,7 @@ int main(int argc,char **argv){
|
||||||
if ( option_b && option_v ) {
|
if ( option_b && option_v ) {
|
||||||
printf("using filter \"%s\" on dev %s\n", filter, dev);
|
printf("using filter \"%s\" on dev %s\n", filter, dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
// open device for reading only local traffic
|
// open device for reading only local traffic
|
||||||
descr = pcap_open_live(dev,1500,0,1,errbuf);
|
descr = pcap_open_live(dev,1500,0,1,errbuf);
|
||||||
if(descr == NULL) {
|
if(descr == NULL) {
|
||||||
|
@ -822,7 +972,7 @@ int main(int argc,char **argv){
|
||||||
}
|
}
|
||||||
|
|
||||||
// main pcap loop
|
// main pcap loop
|
||||||
pcap_loop(descr,-1,handle_IP,args);
|
pcap_loop(descr,-1,&handle_IP,NULL);
|
||||||
|
|
||||||
// done
|
// done
|
||||||
closelog();
|
closelog();
|
||||||
|
@ -833,6 +983,7 @@ int main(int argc,char **argv){
|
||||||
int daemonize(void) {
|
int daemonize(void) {
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
int fd;
|
int fd;
|
||||||
|
int a;
|
||||||
|
|
||||||
fd=open("/dev/null",O_RDWR);
|
fd=open("/dev/null",O_RDWR);
|
||||||
if(fd<0) {
|
if(fd<0) {
|
||||||
|
@ -853,7 +1004,7 @@ int daemonize(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
setsid();
|
setsid();
|
||||||
chdir("/");
|
a = chdir("/");
|
||||||
umask(0);
|
umask(0);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -868,3 +1019,13 @@ int malloc_fail( char * var, int size ) {
|
||||||
}
|
}
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int microsleep(unsigned int usec) {
|
||||||
|
struct timeval timeout;
|
||||||
|
timeout.tv_sec = usec / 1000000;
|
||||||
|
timeout.tv_usec = usec % 1000000;
|
||||||
|
|
||||||
|
while ((select(0, (fd_set *) 0, (fd_set *) 0,(fd_set *) 0, &timeout) < 0) && (errno == EINTR));
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,13 @@
|
||||||
#endif
|
#endif
|
||||||
#define NS_MAXDNAME 1025
|
#define NS_MAXDNAME 1025
|
||||||
#define MAXSYSLOG 192
|
#define MAXSYSLOG 192
|
||||||
|
#define MAXMESSAGE 1200
|
||||||
|
#define MAXDATALET 64
|
||||||
|
#define MAXHEAD 300
|
||||||
|
#define MAX_TIME_LEN 20
|
||||||
|
#define DEFAULT_PORT 2000
|
||||||
|
#define DEFAULT_IP "226.1.1.2"
|
||||||
|
#define HOST_NAME_MAX 254
|
||||||
|
|
||||||
// evil Solaris hack
|
// evil Solaris hack
|
||||||
#ifdef __sun__
|
#ifdef __sun__
|
||||||
|
@ -41,10 +48,12 @@ typedef uint32_t u_int32_t;
|
||||||
// prototypes
|
// prototypes
|
||||||
void handle_IP(u_char *args,const struct pcap_pkthdr* pkthdr,const u_char* packet);
|
void handle_IP(u_char *args,const struct pcap_pkthdr* pkthdr,const u_char* packet);
|
||||||
int calculate_averages();
|
int calculate_averages();
|
||||||
|
int saddr_stats(int sock, struct sockaddr_in addr, char *hostname);
|
||||||
int scour_bucket(int i);
|
int scour_bucket(int i);
|
||||||
int find_bucket(struct in_addr *ip_src);
|
int find_bucket(struct in_addr *ip_src);
|
||||||
int daemonize(void);
|
int daemonize(void);
|
||||||
int malloc_fail(char * var, int size);
|
int malloc_fail(char * var, int size);
|
||||||
|
int microsleep(unsigned int usec);
|
||||||
|
|
||||||
// data structures
|
// data structures
|
||||||
struct my_dns {
|
struct my_dns {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
CFLAGS+=-Wall -O -g -I/usr/local/include -I/usr/include
|
CFLAGS+=-Wall -O -g -I/usr/local/include -I/usr/include
|
||||||
LDLIBS=-L/usr/local/lib -lpcap -lpthread -lm
|
LDLIBS=-lpcap -lpthread -lm
|
||||||
|
|
||||||
all: dns_flood_detector
|
all: dns_flood_detector
|
||||||
strip dns_flood_detector
|
strip dns_flood_detector
|
||||||
|
|
Loading…
Reference in a new issue