[Netpass-devel] NetPass/bin rogue-dhcp-detect.pl,1.2,1.3
Brought to you by:
jeffmurphy
From: rcolantuoni <rco...@us...> - 2005-04-26 20:35:45
|
Update of /cvsroot/netpass/NetPass/bin In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv24744 Modified Files: rogue-dhcp-detect.pl Log Message: added pcap code and threading... still has errors on exit, due to _log calls in the threads perhaps... Index: rogue-dhcp-detect.pl =================================================================== RCS file: /cvsroot/netpass/NetPass/bin/rogue-dhcp-detect.pl,v retrieving revision 1.2 retrieving revision 1.3 diff -u -d -r1.2 -r1.3 --- rogue-dhcp-detect.pl 25 Apr 2005 20:03:35 -0000 1.2 +++ rogue-dhcp-detect.pl 26 Apr 2005 20:35:36 -0000 1.3 @@ -57,22 +57,20 @@ use threads; use Getopt::Std; use Pod::Usage; +use Net::Pcap; -use lib qw(../lib); +use lib qw(/opt/netpass/lib); use NetPass::LOG qw(_log _cont); use NetPass; use NetPass::Config; use FileHandle; -use IO::Select; BEGIN { use Config; $Config{useithreads} or die "Recompile Perl with threads to run this program."; } -#NetPass::LOG::init [ 'rogue-dhcp', 'local0' ]; #*STDOUT; - my %opts; getopts('U:Dqhc:', \%opts); @@ -80,11 +78,18 @@ my ($dbuser, $dbpass) = exists $opts{'U'} ? split('/', $opts{'U'}) : (undef, undef); +my $debug = exists $opts{'D'} ? 1 : 0; +my $quiet = exists $opts{'q'} ? 1 : 0; + +NetPass::LOG::init *STDOUT if $debug; +NetPass::LOG::init [ 'rogue-dhcp-sniff', 'local0' ] unless $debug; + + my $np = new NetPass( -cstr => exists $opts{'c'} ? $opts{'c'} : undef, -dbuser => $dbuser, -dbpass => $dbpass, - -debug => exists $opts{'D'} ? 1 : 0, - -quiet => exists $opts{'q'} ? 1 : 0, + -debug => $debug, + -quiet => $quiet, ); die "failed to create NetPass object" unless defined $np; @@ -102,13 +107,9 @@ } die "no interfaces to listen on" if($#interfaces<0); - -#exit; ###################### CONFIG VARS #################################### -my $debug = 1; -my $quiet = 0; my $allowed = { '128.205.1.32' => 'ccdhcp-resnet3', @@ -124,7 +125,7 @@ my $reportFrequency = 20; # how often (in minutes) to send the report of rogues found # file containing a map of the first half of a mac address to manufacturer -my $ouiFile = "../etc/oui.txt"; +my $ouiFile = "/opt/netpass/etc/oui.txt"; my $fhIfMap = {}; @@ -136,9 +137,6 @@ # unbuffer output $|=1; -# create a filehandle group -my $fhGroup = IO::Select->new(); - # when true, we exit my $programExit = 0; @@ -152,129 +150,85 @@ # use oui file for determining what manufacturer made this device my $ouiCache = loadOUI($ouiFile); -# for each VLAN, open a tcpdump filehandle and push it into the filehandle group -foreach my $interface (@interfaces) { - next if(!ifConfigured($interface)); - - # the -S in the tcpdump command is very important... without -S, tcpdump keeps track of - # all the connections it has seen so it can generate relative sequence numbers rather - # than absolute sequence numbers. over time, this will increase the address space used - # by tcpdump, simulating a memory leak. +my @threads = (); - my $fh = new FileHandle "/usr/sbin/tcpdump -Slne -i$interface udp src port 67 2>&1 |"; - if(defined($fh)) { - print "Listening to traffic on IF $interface\n" if(!$quiet); - $fhGroup->add($fh); - $fhIfMap->{$fh} = $interface; - } +# for each interface, spawn a thread and push it into the filehandle group +foreach my $interface (@interfaces) { + my $sniffer = pcapDescriptor($interface); + push @threads, new threads (\&threadEntry, $sniffer, $interface); } -while(!$programExit) { - - # check to see if any of the filehandles have input - if (my @fhs = $fhGroup->can_read(0)) { - - # @fhs is an array of the filehandle that have input waiting to be read - - # foreach filehandle that has input to be read, get the input and parse it - foreach my $fh (@fhs) { - - my $line = $fh->getline; - my ($srcEth, $dstEth, $srcIp, $dstIp) = ('','','',''); +#print "Parent thread waiting\n" if $debug; +#$threads[0]->join; +#print "Parent thread joined\n" if $debug; - ($srcEth, $dstEth, $srcIp, $dstIp) = $line =~ /(\w+\:\w+\:\w+\:\w+\:\w+\:\w+) (\w+\:\w+\:\w+\:\w+\:\w+\:\w+)*.+ (\d+\.\d+\.\d+\.\d+)\.bootps > (\d+\.\d+\.\d+\.\d+).+/; +# wait for all threads to finish +$_->join foreach @threads; - if(!$srcIp) { - # this filter catches bad matches - print "NOMATCH:\t$line" if($debug); +exit 0; - } elsif($srcIp =~ /\.25\d$/) { - # this filter catches dhcrelays - print "DHCRELAY:\t$srcIp - $srcEth\n" if($debug); +######################################################################## - } elsif( $allowed->{$srcIp} ) { - # this filter catches our exceptions - print "EXCEPTION:\t$srcIp - $srcEth\n" if($debug); +sub threadEntry { + my $sniffer = shift; + my $interface = shift; - } else { - # anything else, should be a rogue server - $roguesFound->{$srcEth}->{'ip'} = $srcIp; - $roguesFound->{$srcEth}->{'vlan'} = $fhIfMap->{$fh}; - $roguesFound->{$srcEth}->{'count'}++; - print "ROGUE:\t$srcIp - $srcEth - " . $fhIfMap->{$fh} . " - " . $roguesFound->{$srcEth}->{'count'} . "\n" if(!$quiet); - } - } + #my $tid = threads->tid(); + #print "$tid"; # causes segfault?? wtf! + if(ref($sniffer)) { + _log("DEBUG", "Thread [tid] - Listening on interface $interface\n"); +# Net::Pcap::loop($sniffer, -1, \&processPacket, 0); + Net::Pcap::close($sniffer); + _log("DEBUG", "Thread [tid] - Done Listening on interface $interface\n"); } else { - print "no line\n" if($debug); + _log("DEBUG", "Not Listening on interface $interface\n"); } - - # if it's time to report, report - if((time - $lastReport) >= $reportFrequency) { - sendReport(); - $lastReport = time; - $roguesFound = {}; - } - - # let's sleep for a while and wait for some input to queue up - sleep($checkFrequency); - -# $programExit = 1; - } -# if we're exiting, close all the filehandles -print "Stop Listening...\n" if(!$quiet); -foreach my $fh ( @{$fhGroup->handles} ) { - $fh->close; +sub processPacket { + my($user_data, $hdr, $pkt) = @_; + print "got one!\n"; + return; } -exit; - -######################################################################## - -sub ifConfigured { - my ($interface) = @_; - - return 0 if(!defined($interface)); - - # check that the interface exists on this machine - if(system("/sbin/ifconfig -s $interface > /dev/null 2>&1") == 0) { - return 1; - } - print "Interface $interface is not configured on this device\n" if(!$quiet); - return 0; - -} # end sub - -sub sendReport { - - my $msg = ''; - - foreach my $eth ( keys %$roguesFound ) { - my $ip = $roguesFound->{$eth}->{'ip'}; - my $vlan = $roguesFound->{$eth}->{'vlan'}; - my $count = $roguesFound->{$eth}->{'count'}; - - $eth = sprintf('%02s:%02s:%02s:%02s:%02s:%02s', split(':', $eth)); - - $msg .= "ip: $ip - eth: $eth - vlan: $vlan - requests: $count"; +sub pcapDescriptor { + my ($device) = @_; - my $lookup = sprintf('%02s:%02s:%02s', split(':', $eth)); + # promiscuous mode on + my $promisc = 1; + + my $snaplen = 96; - $msg .= " - manufacturer: " . $ouiCache->{$lookup} if($ouiCache->{$lookup}); - $msg .= "\n"; - } + my $timeout = 0; # timeout (ms) + my $optimize = 1; # optimize flag + + # dhcp + my $filter = "udp src port 67"; - if($msg ne '') { - $msg = "ROGUE DHCP SERVERS DETECTED:\n\n" . $msg; - print "$msg\n"; - } + my ($err, $net, $mask, $filterCompiled); + + if ( (Net::Pcap::lookupnet($device, \$net, \$mask, \$err) ) == -1 ) { + _log("DEBUG", "$err\n"); + return undef; + } + + # open the descriptor + my $descriptor = Net::Pcap::open_live($device, $snaplen, $promisc, $timeout, \$err); + $descriptor || die "Can't create packet descriptor. Error was $err"; + + if ( Net::Pcap::compile($descriptor, \$filterCompiled, $filter, $optimize, $net) == -1 ) { + die "Unable to compile filter string '$filter'\n"; + } - return 1; + # Make sure our sniffer only captures those bytes we want in + # our filter. + Net::Pcap::setfilter($descriptor, $filterCompiled); -} # end sub + # Return our pcap descriptor + return $descriptor; +} sub loadOUI { my ($filename) = @_; |