#!/usr/local/bin/perl # mark fullmer Sep 4 1992 # maf+@osu.edu # EDIT THIS FILE FOR YOUR MACHINE! require "/home/arpmon/paths.pl"; # Don't start if the process id in $pidfile exists $ret = open(PIDFILE, "< $pidfile"); if ($ret) { $checkpid = ; if (kill(0, $checkpid)) { die "Program allready running!\n"; } } open (LOG, ">>$logfile") || die "Can't open $logfile\n"; ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(time); $mon += 1; print LOG "arpmon started on $mon/$mday/$year $hour:$min:$sec\n"; undef $ret; undef $chekkpid; undef $PIDFILE; # make ourselves a daemon -- ??? $SIG{'TTOU'} = 'IGNORE'; $SIG{'TTIN'} = 'IGNORE'; $SIG{'TSTP'} = 'IGNORE'; open (FD, "+>+ $pidfile") || die "Can't open $pidfile for write"; print PIDFILE "$$\n"; close(PIDFILE); # read our current database file...can't use DBM because 4k is just too # small for key/value pairs (machines doing proxy arp would cause problems) open (DBFILE, "<$dbasefile"); # if it doesn't exist, so what. while () { chop; ($ipaddr, @enets) = split(' '); $val = join(' ', @enets); $netlist{$ipaddr} = $val; } close DBFILE; # when we receive a SIGHUP, dump our internal database to $dbasefile # and continue. $SIG{'HUP'} = 'sighandler'; # when we receive a SIGQUIT, dump our internal database to $dbasefile # and exit $SIG{'QUIT'} = 'sighandler'; # sub sighandler { # handle SIGHUP and SIGQUIT local ($sig) = @_; # either way, we dump our data open(DBFILE, ">$dbasefile"); foreach $key (keys(%netlist)) { print DBFILE "$key $netlist{$key}\n"; } close DBFILE; # signal to the report generator we're done dumping if ( -e $lockfile ) { unlink ($lockfile); } ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(time); $mon += 1; print LOG "arpmon dumped $dbasefile on $mon/$mday/$year $hour:$min:$sec\n"; if ($sig eq 'QUIT') { print LOG "arpmon Quit $mon/$mday/$year $hour:$min:$sec\n"; exit 0; } } # start up tcpdump to watch for arp requests and replies # doing $tcpdump -qenlp arp and broadcast| will limit you to only requests # which also allows the interface not to be in promiscous mode (-p) open (TCPDUMP, "$tcpdump -qenl arp |") || die "Can't popen tcpdump\n"; while() { ($time, $src, $dst, $len, $arp, $op, $addr1, $op2, $addr2) = split(' '); # if ($len != 60) { # print LOG "Got arp packet wrong size: $_\n"; #next; # } # if ($arp ne 'arp'){ # print LOG "Received non arp packet: $_\n"; #next; # } if ($op eq 'who-has') { $type = 1; $ip = $addr2; $enet = $src; $toaddr = $addr1; # ip if ($dst != 'ff:ff:ff:ff:ff:ff') { print LOG "request not to broadcast $_\n"; } } elsif ($op eq 'reply') { $type = 2; $ip = $addr1; $enet = $addr2; $toaddr = $dst; # hardware address if ($src != $addr2) { print LOG "Proxy ARP detected $_\n"; } } else { print LOG "Unknown output from tcpdump: $_\n"; next; } # ADDCODE also check length, dst not FFFF* on requests, tell ne src on replies $curtime = time; $didupdate = 0; # each entry in netlist may have more than 1 hw address stored in it # we'll have to do a linear search through all of them. # if only perl4 did nested arrays and structs :( @temp = split(' ', $netlist{$ip}); foreach $cur (@temp){ ($atype, $nseen, $ltime, $ftime, $hw, $laddr) = split('-', $cur); next if (($atype != $type) || ($hw ne $enet)); # at this point we found the hw address, so we had seen # it before, update that record and set a flag that we did # so. $nseen ++; # #times seen $ltime = $curtime; # last time we saw it $laddr = $toaddr; $didupdate ++; # we made an update $cur = join('-', $type, $nseen, $ltime, $ftime, $hw, $laddr); last; } # foreach # if we didn't make an update, then we need to add this entry to the list. if (! $didupdate) { $cur = join ('-', $type, '1', $curtime, $curtime, $enet, $toaddr); $index = $#temp + 1; $temp[$index] = $cur; } # if # update the associative array $netlist{$ip} = join(' ', @temp); # again and again... } # while