package BGPmon::Filter;
use strict;
use warnings;
use constant FALSE => 0;
use constant TRUE => 1;
use BGPmon::Translator::XFB2PerlHash;
use BGPmon::Filter::Prefix;
use BGPmon::Filter::Address;
use BGPmon::CPM::PList::Manager;
use Net::IP;
use Regexp::IPv6 qw($IPv6_re);
use Data::Dumper;
use threads;
use threads::shared;

BEGIN{
	require Exporter;
	our $VERSION = '1.092';
	our $AUTOLOAD;
	our @ISA = qw(Exporter);
	our @EXPORT_OK = qw(init parse_xml_msg parse_config_file 
		parse_database_config toString filterReset get_error_msg get_error_code
		matches  printFilters get_num_IPv4_prefs get_num_IPv6_prefs 
		get_num_ASes get_num_ip_addrs get_tot_num_filters);
}


my $progName = $0;


# Variables to hold error codes and messages
my %error_code;
my %error_msg;

use constant NO_ERROR_CODE => 0;
use constant NO_ERROR_MSG => 'No Error. Relax with some tea.';

#Error codes for opening the configuration file.
use constant UNOPANABLE_CONFIG_FILE => 520;
use constant UNOPANABLE_CONFIG_FILE_MSG => 'Invalid filename given for config
file.';
#Error codes for parsing the configuration file.
use constant INVALID_IPV4_CONFIG => 530;
use constant INVALID_IPV4_CONFIG_MSG => "Invalid IPv4 given in config file.";
use constant INVALID_IPV6_CONFIG => 531;
use constant INVALID_IPV6_CONFIG_MSG => "Invalid IPv6 given in config file.";
use constant INVALID_AS_CONFIG => 532;
use constant INVALID_AS_CONFIG_MSG => "Invalid AS given in config file.";
use constant UNKNOWN_CONFIG => 533;
use constant UNKNOWN_CONFIG_MSG => "Invalid line in config file.";

#Error codes for parsing the XML file.
use constant NO_MSG_GIVEN => 540;
use constant NO_MSG_GIVEN_MSG => "No XML message was given.";

#Variable to handle locking
my $lock = 1;
share($lock);



# Variables to hold the prefixes we'd like to filter
my @v6prefixes = ();
my @v4prefixes = ();
my %v4prefHash = ();
my $v4Count = 0;
my %asNumbers = ();
my @addresses = ();
my $prefixFilename;

# Variables to hold the prefixes we've found in the latest message that was parsed.
my @v4 = ();
my @v6 = ();
my @as = ();


=head1 NAME

BGPmon::Filter

This module provides information of if a BGP message matches a set of 
IPv4 or IPv6 prefixes, or if it matches a specific autonymous system number.
=cut

=head1 SYNOPSIS

use BGPmon::Filter;

if(BGPmon::Filter::init()){

	my $err_code = BGPmon::Filter::get_error_code('init');
	
	my $err_msg = BGPmon::Filter::get_error_msg('init');
	
	print "$err_code : $err_msg\n";
	
	exit 1;
}
if(BGPmon::Filter::parse_config_file('config_file.txt')){
	
	my $err_code = BGPmon::Filter::get_error_code('parse_config_file');
	
	my $err_msg = BGPmon::Filter::get_error_msg('parse_config_file');
	
	print "$err_code : $err_msg\n";
	
	exit 1;
}
my $xml4msg = '<BGP_MESSAGE length="00001140" version="0.4" xmlns="urn:ietf:pa

rams:xml:ns:xfb-0.4" type_value="2" type="UPDATE"><BGPMON_SEQ id="127893688" se

q_num="1541418969"/><TIME timestamp="1346459370" datetime="2012-09-01T00:29:30Z

" precision_time="0"/><PEERING as_num_len="2"><SRC_ADDR><ADDRESS>187.16.217.154

</ADDRESS><AFI value="1">IPV4</AFI></SRC_ADDR><SRC_PORT>179</SRC_PORT><SRC_AS>5

3175</SRC_AS><DST_ADDR><ADDRESS>200.160.6.217</ADDRESS><AFI value="1">IPV4</AFI

></DST_ADDR><DST_PORT>179</DST_PORT><DST_AS>6447</DST_AS><BGPID>0.0.0.0</BGPID>

</PEERING><ASCII_MSG length="31"><MARKER length="16">FFFFFFFFFFFFFFFFFFFFFFFFFF

FFFFFF</MARKER><UPDATE withdrawn_len="8" path_attr_len="0"><WITHDRAWN count="2"

><PREFIX label="WITH"><ADDRESS>150.196.29.0/24</ADDRESS><AFI value="1">IPV4

</AFI><SAFI value="1">UNICAST </SAFI></PREFIX><PREFIX label="WITH"><ADDRESS>

205.94.224.0/20</ADDRESS><AFI value="1">IPV4</AFI><SAFI value="1">UNICAST

</SAFI></PREFIX></WITHDRAWN><PATH_ATTRIBUTES count="0"/><NLRI count="0"/>

</UPDATE></ASCII_MSG><OCTET_MSG><OCTETS length="31">FFFFFFFFFFFFFFFFFFFFFFFFFFF

FFFFF001F0200081896C41D14CD5EE00000</OCTETS></OCTET_MSG></BGP_MESSAGE>';


if(BGPmon::Filter::matches($xml4msg)){
	
	print "Matches!\n";
	
	print BGPmon::Filter::toString(); #This will print out the parsed info

}

else{
	
	print "Does not match.\n";

}

=head1 EXPORT

init parse_xml_msg parse_config_file toString filterReset get_error_msg get_error_code matches printFilters



=head1 SUBROUTINES/METHODS

=head2 init

Will initilialize the module an its state variables.  This only needs
to be called once.

=cut
sub init{
	lock($lock);
	my $fname = 'init';

	$error_code{$fname} = NO_ERROR_CODE;
	$error_msg{$fname} = NO_ERROR_MSG;

	return 0;
}


=head2 filterReset

Resets the module's state values.

=cut
sub filterReset{
	lock($lock);
	my $fname = 'filterReset';

	foreach(@v6prefixes){
		$_ = undef;
	}
	foreach(@v4prefixes){
		$_ = undef;
	}
	foreach(@addresses){
		$_ = undef;
	}
	foreach(@v4){
		$_ = undef;
	}
	foreach(@v6){
		$_ = undef;
	}
	foreach(@as){
		$_ = undef;
	}
	@v4prefixes = ();
	%v4prefHash = ();
	$v4Count = 0;
	@v6prefixes = ();
	%asNumbers = ();
	@addresses = ();
	@v4 = ();
	@v6 = ();
	@as = ();


	$prefixFilename = undef;
	%error_code = ();
	%error_msg = ();

	$error_code{$fname} = NO_ERROR_CODE;
	$error_msg{$fname} = NO_ERROR_MSG;

	return 0;
}

=head2 get_error_msg

Will return the error message of the given function name.

Input:  A string that contains the function name where an error occured.

Output: The message which represents the error stored from that function.

=cut
sub get_error_msg{
	lock($lock);
	my $str = shift;
	my $fname = 'get_error_msg';
	my $toReturn = $error_msg{$str};
	return $toReturn;
}

=head2 get_error_code

Will return the error code of the given function name.

Input:  A string that represents the function name where an error occured.

Output: The code which represents the error stored from that function.

=cut
sub get_error_code{
	lock($lock);
	my $str = shift;
	my $fname = 'get_error_code';
	my $toReturn = $error_code{$str};
	return $toReturn;
}

#comment
#Will check to see if the string passed in is of proper IPv6 format.
#cut
sub is_IPv6{
	my $str = shift;
	my $fname = 'is_IPv6';
	if(!($str =~ /^$IPv6_re$/)){
		return FALSE;
	}
	return TRUE;
}




my $prefix_rgx = "\\d+\\.\\d+\\.\\d+\\.\\d+\/\\d+";
my $ip_rgx = "\\d+\\.\\d+\\.\\d+\\.\\d+";
sub ipv4_chkip($) {
	#checking to see if it's a prefix or an IPv4 address
        my ($ip) = $_[0] =~ /($prefix_rgx)/o;


        #print "Is a prefix\n" if $ip;
        #print "Not a prefix\n" unless $ip;
        if($ip){#if a prefix

                my @parts = split /\//, $ip;
                my $addr = $parts[0];
                my $mask = $parts[1];
                # Check that bytes are in range
                for (split /\./, $addr ) {
                        return undef if $_ < 0 or $_ > 255;
                }
                return undef if $mask < 0 or $mask > 32;
                return $ip;
        }

        else{#if an address
                ($ip) = $_[0] =~ /($ip_rgx)/o;
                #print "Is an IP address\n" if $ip;
                #print "Not an address\n" unless $ip;
                if($ip){
                        # Check that bytes are in range
                        for (split /\./, $ip ) {
                                return undef if $_ < 0 or $_ > 255;
                        }
                        return $ip;
                 }
        }

        return undef;
}


sub addV4prefixToHash{
	my $prefix = shift;
	my $prefObj = shift;

	my @pieces = split('/',$prefix);
	my @ips = split /\./, $pieces[0];


	#firstOct->{secondOct->{thirdOctet->{fourthOctet->{prefixObject
	if(!exists $v4prefHash{$ips[0]}) {
		$v4prefHash{$ips[0]} = {};
	}
	if(!exists $v4prefHash{$ips[0]}->{$ips[1]}){
		$v4prefHash{$ips[0]}->{$ips[1]} = {};
	}
	if(!exists $v4prefHash{$ips[0]}->{$ips[1]}->{$ips[2]}){
		$v4prefHash{$ips[0]}->{$ips[1]}->{$ips[2]} = {};
	}
	if(!exists $v4prefHash{$ips[0]}->{$ips[1]}->{$ips[2]}->{$ips[3]}){
		$v4prefHash{$ips[0]}->{$ips[1]}->{$ips[2]}->{$ips[3]} = [];
	}


	push($v4prefHash{$ips[0]}->{$ips[1]}->{$ips[2]}->{$ips[3]}, $prefObj);
	$v4Count += 1;
}


sub getChildren{
	my $hashRef = shift;

	my @toReturn = ();

	foreach my $key(keys % $hashRef){
		my $item = $hashRef->{$key};
		#print "$item\n";
		if(ref($item) eq 'HASH'){ #still going through the hashes
			push(@toReturn, @{getChildren($item)});
		}
		elsif(ref($item) eq 'ARRAY'){
			my @toAdd = @{$item};
			#print Dumper @toAdd;
			push(@toReturn, @toAdd);
		}
	}
	return \@toReturn;
}

sub getV4comparisons{
	my $prefix = shift;
	my @pieces = split /\//, $prefix;
	my $ip = $pieces[0];
	my @ips = split /\./, $ip;

	if(!exists $v4prefHash{$ips[0]}) { 
		return getChildren(\%v4prefHash);
	}
	elsif(!exists $v4prefHash{$ips[0]}->{$ips[1]}) {
		return getChildren($v4prefHash{$ips[0]});
	}
	elsif(!exists $v4prefHash{$ips[0]}->{$ips[1]}->{$ips[2]}) {
		return getChildren($v4prefHash{$ips[0]}->{$ips[1]});
	}
	elsif(!exists $v4prefHash{$ips[0]}->{$ips[1]}->{$ips[2]}->{$ips[3]}) {
		return getChildren($v4prefHash{$ips[0]}->{$ips[1]}->{$ips[2]});
	}
	elsif(exists $v4prefHash{$ips[0]}->{$ips[1]}->{$ips[2]}->{$ips[3]}){
		my $toReturn = $v4prefHash{$ips[0]}->{$ips[1]}->{$ips[2]}->{$ips[3]}; #returns the array of prefixes with these four octets 
		return $toReturn;
	}
	else{
		#TODO make lookup error
		return undef;
	}
}




=head2 parse_config_file

Will parse the wanted IPv4 and IPv6 prefixes from a configuration file as well
as any autonymous system numbers.  These will be stored until 
BGPmon::Filter::filterReset() is called.  This will also aggregate addresses
where possible and setup a mult-layer hash lookup system for faster retrieval

Input: A string with the location of the configuration file to parse

Output: 0 if there is no error
        1 if an error occured


=cut
sub parse_config_file{
	lock($lock);
	$prefixFilename = shift;
	my $fname = 'parse_config_file';
	my $file;
	my $lineNum = 0;
	if(!open($file, $prefixFilename)){
		$error_code{$fname} = UNOPANABLE_CONFIG_FILE;
		$error_msg{$fname} = UNOPANABLE_CONFIG_FILE_MSG;
		return 1;
	}

	while(my $line = <$file>){
		$lineNum ++;
		chomp $line;

		# Remove any trailing white space.
		$line =~ s/^s+//g;

		# If the line starts with a #, skip it.
		next if ($line =~ /^s*#/);

		# Skipping the line if it's blank
		next if ($line eq "");


		# Splitting the line up
		my @lineArray = split ' ', $line;
		my $lineLength = scalar(@lineArray);
		if($lineLength < 1){
			$error_code{$fname} = UNKNOWN_CONFIG;
			$error_msg{$fname} = UNKNOWN_CONFIG_MSG;
			return 1;
		}


		# if this line is an AS number
		if($lineArray[0] =~ /[aA][sS]/){
			if($lineArray[1] > 0 and $lineArray[1] < 65536){
				my $temp = $lineArray[1];
				$asNumbers{$temp} = 1;
			}
			else{
				$error_code{$fname} = INVALID_AS_CONFIG;
				$error_msg{$fname} = INVALID_AS_CONFIG_MSG;
				return 1;
			}
		}
		# if this line is an IPv4 number
		elsif($lineArray[0] =~ /[iI][pP][vV][4]/){
			#Making sure whatever is next is either a valid prefix or IPv4 address
			my $ipcheck = ipv4_chkip($lineArray[1]);
			unless($ipcheck) {
				$error_code{$fname} = INVALID_IPV4_CONFIG;
				$error_msg{$fname} = INVALID_IPV4_CONFIG_MSG.':'.$line;
				return 1;
			}
			#Decerning if we have a prefix or IPv4 address
			if($ipcheck =~ /\//){ #prefix
				if(!exists($lineArray[2]) or !$lineArray[2] =~ m/[mMlL][sS]/){
					$error_code{$fname} = INVALID_IPV4_CONFIG;
					$error_msg{$fname} = INVALID_IPV4_CONFIG_MSG.':'.$line;
					return 1;
					#print "Skippnig $line\n";
					#next;
				}
				# Adding prefix to the list since it's okay
				my $moreSpecific = $lineArray[2] =~ m/[mM][sS]/;
				my $temp = new BGPmon::Filter::Prefix(4, $lineArray[1], $moreSpecific); 
				push(@v4prefixes, $temp);
			}
			else{#IPv4 address
				my $temp = new BGPmon::Filter::Address(4,$lineArray[1]);
				push(@addresses,$temp);
			}
		}

		# if this line is an IPv6 number
		elsif($lineArray[0] =~ /[iI][pP][vV][6]/){
			# Ensuring that it's a valid IPv6 number and prefix
			my @ipv6Addr = split(/\//, $lineArray[1]);
			my $address = $ipv6Addr[0];
			my $prefix = $ipv6Addr[1];

			# Making sure the IPv6 is valid - in any form.
			if(!is_IPv6($ipv6Addr[0])){
				$error_code{$fname} = INVALID_IPV6_CONFIG;
				$error_msg{$fname} = INVALID_IPV6_CONFIG_MSG;
				return 1;
			}

			# Making sure the prefix is valid
			if($prefix < 0 or $prefix > 128){
				$error_code{$fname} = INVALID_IPV6_CONFIG;
				$error_msg{$fname} = INVALID_IPV6_CONFIG_MSG;
				return 1;
			}

			# Adding prefix to the list
			if(!defined($lineArray[2])){ 
				$error_code{$fname} = INVALID_IPV6_CONFIG;
				$error_msg{$fname} = INVALID_IPV6_CONFIG_MSG;
			}
			if(!$lineArray[2] =~ m/[mMlL][sS]/){
				$error_code{$fname} = INVALID_IPV6_CONFIG;
				$error_msg{$fname} = INVALID_IPV6_CONFIG_MSG;
				return 1;
			}
			my $moreSpecific = $lineArray[2] =~ m/[mM][sS]/;
			my $temp = new BGPmon::Filter::Prefix(6, $lineArray[1], $moreSpecific);
			push(@v6prefixes, $temp);
		}

		# if we don't know what this line is
		else{
			$error_code{$fname} = UNKNOWN_CONFIG;
			$error_msg{$fname} = UNKNOWN_CONFIG_MSG;
			return 1;
		}



	}

	#closing the file
	close($file);
	condense_prefs(); #aggregates where possible
	optimize_prefs(); #puts them in the multilayer hash for faster lookups

	$error_code{$fname} = NO_ERROR_CODE;
	$error_msg{$fname} = NO_ERROR_MSG;

	return 0;
}



sub parse_database_config{
	my $listName = shift;
	my $fname = 'parse_database_config';
	lock($lock);


	#getting a list of prefixes
	my @prefs = BGPmon::CPM::PList::Manager::export2CSV('',$listName);
	my $size = scalar(@prefs);
	if($size == 0){
		#TODO create an error message that has the list size at zero
	}

	#resetting the module
	filterReset();

	#creating new prefixes
	foreach(@prefs){
		my $newPref = $_->prefix();
		my $moreSpec = $_->watch_more_specifics();
		if(is_IPv6($newPref)){
			my $temp = new BGPmon::Filter::Prefix(6, $newPref, $moreSpec);
            push(@v6prefixes, $temp);
		}
		else{
			my $temp = new BGPmon::Filter::Prefix(4, $newPref, $moreSpec);
			push(@v4prefixes, $temp);
		}
	}


	#optimizing
	condense_prefs(); #aggregates where possible
	optimize_prefs(); #puts them in the multilayer hash for faster lookups

	$error_code{$fname} = NO_ERROR_CODE;
	$error_msg{$fname} = NO_ERROR_MSG;

	return 0;

}

#puts the prefixes for IPv4 into a multi-layer hash lookup
sub optimize_prefs{
	foreach(@v4prefixes){
		my $temp = $_;
		addV4prefixToHash($temp->prefix(), $temp);
	}

	#TODO put in error codes

	return 0;
}

=head2 get_num_IPv4_prefs

Will count the number of IPv4 prefixes it has parsed from the configuration
file and return the integer

Input: None
Output : Integer
=cut
sub get_num_IPv4_prefs{
	my $toReturn = scalar(@v4prefixes);
	return $toReturn;
}

=head2 get_num_IPv6_prefs

Will count the number of IPv6 prefixes it has parsed from the configuration
file and return the integer

Input: None
Output : Integer
=cut
sub get_num_IPv6_prefs{
	my $toReturn = scalar(@v6prefixes);
	return $toReturn;
}


=head2 get_num_ASes

Will count the number of ASes it has parsed from the configuration
file and return the integer

Input: None
Output : Integer
=cut
sub get_num_ASes{
	my $toReturn = scalar(keys %asNumbers);
	return $toReturn;
}



=head2 get_num_ip_addrs

Will count the number of IPv4 addresses it has parsed from the configuration
file and return the integer

Input: None
Output : Integer
=cut
sub get_num_ip_addrs{
	my $toReturn = scalar(@addresses);
	return $toReturn;
}

=head2 get_total_num_filters

Will tally all filters the module will look for per message and return
the interger

Input: None
Output : Integer

=cut
sub get_total_num_filters{
	lock($lock);
	my $toReturn = 0;
	$toReturn += scalar(@v4prefixes);
	$toReturn += scalar(@v6prefixes);
	$toReturn += scalar(%asNumbers);
	$toReturn += scalar(@addresses);
	return $toReturn;
}


#condense__prefs
#
#Will try to aggregate IPv4 and IPv6 prefixes where possible.  This is used to reduce
#overhead that the filter script may experience later.  Please note that the
#parse_config_file must be ran beforehand.
#
#Input: None
#
#Output: 0 if there is no error
#        1 if an error occured
#
#
sub condense_prefs{
	
	##Starting with IPv4
	#Will continuously agg. addresses where it can until none are left
	my $found = TRUE;
	while($found){
		$found = FALSE;
		for( my $i = 0; $i < scalar(@v4prefixes); $i++){
			my $currPref = $v4prefixes[$i];
			for(my $k = $i+1; $k < scalar(@v4prefixes); $k++){
				my $thisPref = $v4prefixes[$k];
				next if not $currPref->matchSpecific($thisPref);

				if($currPref->canAggregateWith($thisPref)){
					my $newPref = $currPref->getAggregate($thisPref);
					splice @v4prefixes, $k, 1;
					splice @v4prefixes, $i, 1;
					my $np = new BGPmon::Filter::Prefix(4, 
						$newPref, $currPref->{moreSpecific}); 
					push(@v4prefixes, $np);
					$found = TRUE;
				}
			}
		}
	}

	##Starting with IPv6
	#Will continuously agg. addresses where it can until none are left
	$found = TRUE;
	while($found){
		$found = FALSE;
		for( my $i = 0; $i < scalar(@v6prefixes); $i++){
			my $currPref = $v6prefixes[$i];
			for(my $k = $i+1; $k < scalar(@v6prefixes); $k++){
				my $thisPref = $v6prefixes[$k];
				next if not $currPref->matchSpecific($thisPref);

				if($currPref->canAggregateWith($thisPref)){
					my $newPref = $currPref->getAggregate($thisPref);
					splice @v6prefixes, $k, 1;
					splice @v6prefixes, $i, 1;
					my $np = new BGPmon::Filter::Prefix(6, 
						$newPref, $currPref->{moreSpecific}); 
					push(@v6prefixes, $np);
					$found = TRUE;
				}
			}
		}
	}

	return 0;
}



=head2 printFilters

Will print to standard output the filters currently set for the module.
For example, if your prefix file looked like

#IPv4

ipv4 192.168.1.0/24 ls

#IPv6

ipv6 ::0/32 ms

#AS

as   1

This will print 

ipv4 192.168.1.0/24 ls

ipv6 ::0/32 ms

as   1

=cut

sub printFilters{
	lock($lock);
	foreach(@v4prefixes){
		my $temp = $_->toString();
		print "$temp\n";
	}
	foreach(@v6prefixes){
		my $temp = $_->toString();
		print "$temp\n";
	}
	foreach(keys %asNumbers){
		print "$_\n";
	}
	foreach(@addresses){
		my $temp = $_->toString();
		print "$temp\n";
	}
}



=head2 toString

Will return a string that prints the most recently filtered prefixes and
autonymous system numbers in human-readable format.

E.g., 
IPv4 prefixes pulled from the message:
192.168.1.0/24
IPv6 prefixes pulled from the message:
(none)
AS numbers pulled from the message:
12345

=cut
sub toString{
	lock($lock);
	my $fname = 'toString';
	my $toReturn = "";

	#Adding v4's
	$toReturn .= "IPv4 prefixes pulled from the message:\n";
	if(scalar @v4 == 0){
		$toReturn .= "(none)\n";
	}
	else{
		foreach(@v4){
			$toReturn .= "$_\n";
		}
	}

	#Adding v6's
	#
	$toReturn .= "IPv6 prefixes pulled from the message:\n";
	if(scalar @v6 == 0){
		$toReturn .= "(none)\n";
	}
	else{
		foreach(@v6){
			$toReturn .= "$_\n";
		}
	}
	#Adding AS's
	#
	$toReturn .= "AS numbers pulled from the message:\n";
	if(scalar @as == 0){
		$toReturn .= "(none)\n";
	}
	else{
		foreach(@as){
			$toReturn .= "$_\n";
		}
	}

	return $toReturn;

}

#comment
#
#Will reset the most recently filtered prefixes and AS numbers, parse the 
#message that was sent to it, and store a unique set of prefixes and 
#AS numbers.
#
#cut
sub parse_xml_msg{
	lock($lock);
	my $fname = 'parse_xml_msg';
	my $xmlMsg = shift;

	if(!defined($xmlMsg)){
		$error_code{$fname} = NO_MSG_GIVEN;
		$error_msg{$fname} = NO_MSG_GIVEN_MSG;
		return undef;
	}

	# A list of all the prefixes and AS's found during searching.
	my @v4s = ();
	my @v6s = ();
	my @ases = ();

	# The translation of the message
	my $hash = BGPmon::Translator::XFB2PerlHash::translate_msg($xmlMsg);

	#Checking the withdrawn part of the message
	my $hashRes1 = BGPmon::Translator::XFB2PerlHash::get_content('/BGP_MESSAGE/ASCII_MSG/UPDATE/WITHDRAWN/PREFIX/');
	if(defined($hashRes1)){
		foreach(@$hashRes1){
			push(@v4s, $_->{'ADDRESS'}->{'content'});
		}
	}

	#Checking the address parts in NLRI place
	my $hashRes2 = BGPmon::Translator::XFB2PerlHash::get_content('/BGP_MESSAGE/ASCII_MSG/UPDATE/NLRI/PREFIX/ADDRESS/content');
	push(@v4s, $hashRes2) if defined $hashRes2;

	#Checking the address part of MP_REACH_NLRI but skipping the Next Hop addresses
	my $hashRes = BGPmon::Translator::XFB2PerlHash::get_content('/BGP_MESSAGE/ASCII_MSG/UPDATE/PATH_ATTRIBUTES/ATTRIBUTE/MP_REACH_NLRI/NLRI/PREFIX/ADDRESS/content');
	if(defined($hashRes)){
		my @parts = split(/\//, $hashRes);
		if(is_IPv6($parts[0])){
			push(@v6s, $hashRes);
		}
		else{
			push(@v4s, $hashRes);
		}
	}


	#Checking the address part of MP_UNREACH_NLRI
	my $hashRes7 = BGPmon::Translator::XFB2PerlHash::get_content('/BGP_MESSAGE/ASCII_MSG/UPDATE/PATH_ATTRIBUTES/ATTRIBUTE/MP_UNREACH_NLRI/WITHDRAWN/PREFIX/ADDRESS/content');
	if(defined($hashRes7)){
		my @parts = split(/\//, $hashRes7);
		if(is_IPv6($parts[0])){
			push(@v6s, $hashRes7);
		}
		else{
			push(@v4s, $hashRes7);
		}
	}



	#Checking for AS numbers in the AS_Path attribute
	my $hashRes5 = BGPmon::Translator::XFB2PerlHash::get_content('/BGP_MESSAGE/ASCII_MSG/UPDATE/PATH_ATTRIBUTES/ATTRIBUTE/AS_PATH/AS_SEG/AS/');
	if(defined($hashRes5)){
		my $size = scalar(@$hashRes5);
		my $wantedAS = @$hashRes5[$size-1];
		my $finalAS = $wantedAS->{'content'};
		push(@ases, $finalAS) if defined $finalAS;
	}


	@v4 = ();
	@v6 = ();
	@as = ();

	if(scalar @v4s > 0){
		$v4[0] = $v4s[0];
		my $i = 0;
		@v4s = sort(@v4s);
		foreach my $item(@v4s){
			unless($item eq $v4[$i]){
				push(@v4, $item);
				$i++;
			}
		}
	}

	if(scalar @v6s > 0){
		$v6[0] = $v6s[0];
		my $i = 0;
		@v6s = sort(@v6s);
		foreach my $item(@v6s){
			unless($item eq $v6[$i]){
				push(@v6, $item);
				$i++;
			}
		}
	}

	if(scalar @ases > 0){
		$as[0] = $ases[0];
		my $i = 0;
		@ases = sort(@ases);
		foreach my $item(@ases){
			unless($item eq $as[$i]){
				push(@as, $item);
				$i++;
			}
		}
	}

	return 0; # successful message parsing
}


=head2 matches

Will check to see if the BGPmon message passed to it has maching prefix or AS 
fields that were given earlier to the module.  

Input:  A BGPmon message in XML format

Output: 1 if there was at least one matching filed.
        0 if no matches were found.

=cut
sub matches{
	lock($lock);
	my $xmlMsg = shift;
	my $fname = 'matches';

	if(!defined($xmlMsg)){
		$error_code{$fname} = NO_MSG_GIVEN;
		$error_msg{$fname} = NO_MSG_GIVEN_MSG;
		return undef;
	}

	parse_xml_msg($xmlMsg);

	# Checking to see if any of these AS numbers are ones we're looking for.
	if(scalar @as > 0){
		foreach(@as){
			return TRUE if defined $asNumbers{$_};
		}
	}



	# Checking to see if any of the v4 prefixes are matches.
	if(scalar @v4 > 0){
		foreach(@v4){
			my $ipPrefAddr = $_;
			my @toCheck = @{getV4comparisons($_)};
			#print Dumper @toCheck;
			foreach(@toCheck){
				my $v4Prefix = $_;
				#print "IP: $v4Prefix\n";
				return TRUE if $v4Prefix->matches($ipPrefAddr);
			}

		}
	}

	#Checking to see if any of the v4 addresses are a match
	if(scalar @v4 > 0){
		foreach(@addresses){
			my $addr = $_;
			foreach(@v4){
				my $tempPrefix = $_;
				if($addr->matches($tempPrefix)){
					return TRUE;
				}
			}

		}
	}

	# Checking to see if any of the v6 addresses are matches.
	if(scalar @v6 > 0){
		foreach (@v6prefixes){
			my $v6Prefix = $_;
			#Seeing if we need to keep on to the message
			foreach(@v6){
				my $ipPrefAddr = $_;
				if($v6Prefix->matches($ipPrefAddr)){
					return TRUE;
				}
			}
		}
	}

	return FALSE;
}





1;
__END__


=head1 ERROR CODES AND MESSAGES

The following error codes and messages are defined:
	
	0:   There isn't an error.
	     'No Error. Relax with some tea.'
	520: The name of the configuration file given doesn't exists or
	     cannot be opened.
	     'Invalid filename given for config file.'
	530: An IPv4 address given in the configuration file has on octet 
	     out of range, is syntactly incorrect, or is otherwise invalid.
	     'Invalid IPv4 given in config file.'
	531: An IPv6 address given in teh configuration file has a value
	     out of range, is syntactly incorrect, or is otherwise invalid.
	     'Invalid IPv6 given in config file.'
	532: An Autonymous System number given in the configuration file
	     is out of range or otherwise invalid.
	     'Invalid AS given in config file.'
	533: An unknown configuration was found in the configuration file.
	     'Invalid line in config file.'
	540: A message was not passed to the BGPmon::Filter::matches method.
	     'No XML message was given.'


=cut

=head1 AUTHOR

M. Lawrence Weikum C<< <mweikum at rams.colostate.edu> >>

=cut

=head1 BUGS

Please report any bugs or feature requeues to 
 C<bgpmon at netsec.colostate.edu> or through the web interface
 at L<http://bgpmon.netsec.colostate.edu>.

=cut

=head1 SUPPORT

You can find documentation on this module with the perldoc command.

	perldoc BGPmon::Filter

=cut


=head1 LICENSE AND COPYRIGHT

Copyright (c) 2012 Colorado State University

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.\

File: Filter.pm
Authors: M. Lawrence Weikum
Date: 3 March 2013
=cut