eval 'exec perl -x $0 ${1+"$@"}' # -*-perl-*-
  if 0;
#!perl -w
#
# ======================================================================
# This file is Copyright 1998,1999 by the Purdue Research Foundation and
# may only be used under license.  For terms of the license, see the
# file named COPYRIGHT included with this software release.
# AAFID is a trademark of the Purdue Research Foundation.
# All rights reserved.
# ======================================================================
#
# AAFID2 system
# Agent name:        ARPWatcher
# Agent description: Keeps an eye on the ARP cache of the host to detect ARP poison.
# Author:            Tom Daniels
#
# History of modifications:
# 
# Known bugs:
# - Since the agent just polls the ARP cache, a quick attack may be easily missed.
# - Longer Term Denial of service would be caught though...
# - Works on Solaris, may not work on other UN*Xs due to differences in netstat command.
# 
# Things to do:
# 
# Future work:
# 
# Suggestions made by people:
# 
# Generated automatically by ../bin/makeagent.pl on Tue Sep  7 23:55:40 1999.
# 
###### AUTOMATICALLY GENERATED FILE --- DO NOT EDIT ##########
#

# 11 "ARPWatcher.aas"
package ARPWatcher;

# Version number
# 13 "ARPWatcher.aas"
$VERSION=eval {1.0}; $VERSION=$VERSION;

# Agent parameters
%PARAMETERS=(
             Description        => "Keeps an eye on the ARP cache of the host to detect ARP poison.",
             CheckPeriod        => eval {10},
# 106 "ARPWatcher.aas"
             MyARPHistory => {},
             MyEnforced => {},
             MyCommand => "/usr/bin/netstat -pn",
             MyStatus => 0,
             MyMessage => "",
             FiltersNeeded => {  }
            );

# Package loading
use AAFID::Agent;
use AAFID::Log;
use AAFID::Common;


use vars qw (
             @ISA
             $VERSION
             %PARAMETERS
            );

# Define the superclass.
 @ISA=qw(AAFID::Agent);

=head1 ARPWatcher



=cut

# Preamble code


# The Check function.
sub Check {
  my $self=checkref(shift);
# 21 "ARPWatcher.aas"
	my $a = "";
	my @fields = ();
	my @myp= ();
	my @hashv = ();
	my $CurMessage = $Params{MyMessage};
	my $curstat = $Params{MyStatus};
	
open(NETSTATPROC, $Params{MyCommand}."|") ;

do {
	$a = <NETSTATPROC>;
   } until ($a =~ /---/);


while (defined($a= <NETSTATPROC>) && $a)
	{
	@fields = split(/\s+/,$a);
#  Handle flags which are sometimes in the 3rd column.
# just copy over them with the hw address which is then in the 4th column
	if (!($fields[3] =~ /:/))
		{
		$fields[3] = $fields[4];
		}
	@myp = split(/\./,$fields[1]);
#	print $myp[0] . "\n";
# Throw out multicast mappings. (might be interesting later when
# we know what to do with them.
	if ($myp[0] < 224)
		{
## Here, in fields  0 - interface, 1 - IP address, 3 - Phys address
## We have the mappings we need to process.
#		print join(", ", @fields) ."\n";
		
		if (! exists $Params{MyARPHistory}{$fields[1]} )
			{
			$Params{MyARPHistory}->{$fields[1]}= [ [$fields[3], $fields[0], time]];
			}
		else
			{
			$hashv =$Params{MyARPHistory}{$fields[1]};
			if ($hashv->[0][0] ne $fields[3])

# Stored Hardware address is not same as currently found one.
				{
## add current mapping to the list contained in the hash		
##				print  $hashv->[0][0] ."--".$fields[3] ."\n";
				
				unshift(@$hashv, [$fields[3],$fields[0],time]);
## Raise an error!					
						
				$curstat += 1;
			$CurMessage = $CurMessage . "IP:".$fields[1]." mapped to $hashv->[1][0] at $hashv->[1][2]  but has changed to $fields[3], "; 

				$Params{MyARPHistory}->{$fields[1]}=  $hashv;
				}	
			}
		
		

		}
	}
 	
close(NETSTATPROC);

if ($curstat >10) {$curstat = 10;}

$Params{MyStatus} = $curstat;
$Params{MyMessage} = $CurMessage;
if ($curstat >0)  
	{
	return ($curstat, "Possible ARP poison -".$CurMessage);
	}
return ($curstat, "");
}
# Agent commands

sub command_CLEARSTATUS {
  my $self=checkref(shift);
  my ($message, %p)=@_;
  if (1) {
    
# 97 "ARPWatcher.aas"
	$Params{MyStatus} = 0;
	$Params{MyMessage} = "";
    # By default, return undef
    return undef;
  }
}

sub command_GET_MAPPINGS {
  my $self=checkref(shift);
  my ($message, %p)=@_;
  if (exists($p{AnIP}) && 1) {
    my $AnIP = $p{AnIP};
# 101 "ARPWatcher.aas"

	my $foo = $Params{MyARPHistory}->{$AnIP};
	return {Mapping => $foo};
    # By default, return undef
    return undef;
  }
}

# End of entity marker
_EndOfEntity;
