#!/opt/local/bin/perl
##
## IP Filter UCD-SNMP pass module
##
## Allows read IP Filter's tables (In, Out, AccIn, AccOut),
## fetching rules, hits and bytes (for accounting tables only).
##
## Author: Yaroslav Terletsky <ts@polynet.lviv.ua>
## Date: $ Tue Dec  1 10:24:08 EET 1998 $
## Version: 1.1a

# Put this file in /usr/local/bin/ipf-mod.pl and then add the following 
# line to your snmpd.conf file (without the # at the front):
#
#   pass .1.3.6.1.4.1.2021.13.2 /usr/local/bin/ipf-mod.pl

# enterprises.ucdavis.ucdExperimental.ipFilter	= .1.3.6.1.4.1.2021.13.2
# ipfInTable.ipfInEntry.ipfInIndex		integer	= 1.1.1
# ipfInTable.ipfInEntry.ipfInRule		string	= 1.1.2
# ipfInTable.ipfInEntry.ipfInHits		counter	= 1.1.3
# ipfOutTable.ipfOutEntry.ipfOutIndex		integer	= 1.2.1
# ipfOutTable.ipfOutEntry.ipfOutRule		string	= 1.2.2
# ipfOutTable.ipfOutEntry.ipfOutHits		counter	= 1.2.3
# ipfAccInTable.ipfAccInEntry.ipfAccInIndex	integer	= 1.3.1
# ipfAccInTable.ipfAccInEntry.ipfAccInRule	string	= 1.3.2
# ipfAccInTable.ipfAccInEntry.ipfAccInHits	counter	= 1.3.3
# ipfAccInTable.ipfAccInEntry.ipfAccInBytes	counter	= 1.3.4
# ipfAccOutTable.ipfAccOutEntry.ipfAccOutIndex	integer	= 1.4.1
# ipfAccOutTable.ipfAccOutEntry.ipfAccOutRule	string	= 1.4.2
# ipfAccOutTable.ipfAccOutEntry.ipfAccOutHits	counter	= 1.4.3
# ipfAccOutTable.ipfAccOutEntry.ipfAccOutBytes	counter	= 1.4.4

# variables types
%type = ('1.1.1', 'integer', '1.1.2', 'string', '1.1.3', 'counter',
	 '2.1.1', 'integer', '2.1.2', 'string', '2.1.3', 'counter',
	 '3.1.1', 'integer', '3.1.2', 'string', '3.1.3', 'counter',
	 '3.1.4', 'counter',
	 '4.1.1', 'integer', '4.1.2', 'string', '4.1.3', 'counter',
	 '4.1.4', 'counter');

# getnext sequence
%next = ('1.1.1', '1.1.2', '1.1.2', '1.1.3', '1.1.3', '2.1.1',
	 '2.1.1', '2.1.2', '2.1.2', '2.1.3', '2.1.3', '3.1.1',
	 '3.1.1', '3.1.2', '3.1.2', '3.1.3', '3.1.3', '3.1.4',
	 '3.1.4', '4.1.1',
	 '4.1.1', '4.1.2', '4.1.2', '4.1.3', '4.1.3', '4.1.4');

# ipfilter's commands to fetch needed information
$ipfstat_comm="/sbin/ipfstat";
$ipf_in="$ipfstat_comm -ih 2>/dev/null";
$ipf_out="$ipfstat_comm -oh 2>/dev/null";
$ipf_acc_in="$ipfstat_comm -aih 2>/dev/null";
$ipf_acc_out="$ipfstat_comm -aoh 2>/dev/null";

$OID=$ARGV[0];
$IPF_OID='.1.3.6.1.4.1.2021.13.2';
$IPF_OID_NO_DOTS='\.1\.3\.6\.1\.4\.1\.2021\.13\.2';

# exit if OID is not one of IPF-MIB's
exit if $OID !~ /^$IPF_OID_NO_DOTS(\D|$)/;

# get table, entry, column and row numbers
$tecr = $OID;
$tecr =~ s/^$IPF_OID_NO_DOTS(\D|$)//;
($table, $entry, $col, $row, $rest) = split(/\./, $tecr);

# parse 'get' request
if($g) {
	# exit if OID is wrong specified
	if(!defined $table or !defined $entry or !defined $col or !defined $row or defined $rest) {
		print "[1] NO-SUCH NAME\n" if $d;
		exit;
	}

	# get the OID's value
	$value = &get_value($table, $entry, $col, $row);
	print "value=$value\n" if $d;

	# exit if OID does not exist
	print "[2] NO-SUCH NAME\n" if $d and !defined $value;
	exit if !defined $value;

	# set ObjectID and reply with response
	$tec = "$table.$entry.$col";
	$ObjectID = "${IPF_OID}.${tec}.${row}";
	&response;
}

# parse 'get-next' request
if($n) {
	# set values if 0 or unspecified
	$table = 1, $a = 1 if !$table or !defined $table;
	$entry = 1, $a = 1 if !$entry or !defined $entry;
	$col = 1, $a = 1 if !$col or !defined $col;
	$row = 1, $a = 1 if !$row or !defined $row;

	if($a) {
		# get the OID's value
		$value = &get_value($table, $entry, $col, $row);
		print "value=$value\n" if $d;

		# set ObjectID and reply with response
		$tec = "$table.$entry.$col";
		$ObjectID = "${IPF_OID}.${tec}.${row}";
		&response;
	}

	# get next OID's value
	$row++;
	$value = &get_value($table, $entry, $col, $row);

	# choose new table/column if rows exceeded
	if(!defined $value) {
		$tec = "$table.$entry.$col";
		$tec = $next{$tec} if !$a;
		$table = $tec;
		$entry = $tec;
		$col = $tec;
		$table =~ s/\.\d\.\d$//;
		$entry =~ s/^\d\.(\d)\.\d$/$1/;
		$col =~ s/^\d\.\d\.//;
		$row = 1;

		# get the OID's value
		$value = &get_value($table, $entry, $col, $row);
		print "value=$value\n" if $d;
	}

	# set ObjectID and reply with response
	$tec = "$table.$entry.$col";
	$ObjectID = "${IPF_OID}.${tec}.${row}";
	&response;
}

##############################################################################

# fetch values from 'ipfInTable' and 'ipfOutTable' tables
sub fetch_hits_n_rules {
	local($row, $col, $ipf_output) = @_;
	local($asdf, $i, @ipf_lines, $length);

	# create an entry if no rule exists
	$ipf_output = "0 empty list for ipfilter" if !$ipf_output;

	@ipf_lines = split("\n", $ipf_output);
	$length = $#ipf_lines + 1;

	for($i = 1; $i < $length + 1; $i++) {
		$hits{$i} = $ipf_lines[$i-1];
		$hits{$i} =~ s/^(\d+).*$/$1/;
		$rule{$i} = $ipf_lines[$i-1];
		$rule{$i} =~ s/^\d+ //;
		if($i == $row) {
			return $i if $col == 1;
			return $rule{$i} if $col == 2;
			return $hits{$i} if $col == 3;
		}
	}
	# return undefined value
	undef $asdf;
	return $asdf;
}

# fetch values from 'ipfAccInTable' and 'ipfAccOutTable' tables
sub fetch_hits_bytes_n_rules {
	local($row, $col, $ipf_output) = @_;
	local($asdf, $i, @ipf_lines, $length);

	# create an entry if no rule exists
	$ipf_output = "0 0 empty list for ipacct" if !$ipf_output;

	@ipf_lines = split("\n", $ipf_output);
	$length = $#ipf_lines + 1;

	for($i = 1; $i < $length + 1; $i++) {
		$hits{$i} = $ipf_lines[$i-1];
		$hits{$i} =~ s/^(\d+) .*$/$1/;
		$bytes{$i} = $ipf_lines[$i-1];
		$bytes{$i} =~ s/^\d+ (\d+) .*/$1/;
		$rule{$i} = $ipf_lines[$i-1];
		$rule{$i} =~ s/^\d+ \d+ //;
		if($i == $row) {
			return $i if $col == 1;
			return $rule{$i} if $col == 2;
			return $hits{$i} if $col == 3;
			return $bytes{$i} if $col == 4;
		}
	}
	# return undefined value
	undef $asdf;
	return $asdf;
}

# get the values from ipfilter's tables
sub get_value {
	local($table, $entry, $col, $row) = @_;

	if($table == 1) {
		# fetch ipfInTable data
		$ipf_output = `$ipf_in`;
		$value = &fetch_hits_n_rules($row, $col, $ipf_output);
	} elsif($table == 2) {
		# fetch ipfOutTable data
		$ipf_output = `$ipf_out`;
		$value = &fetch_hits_n_rules($row, $col, $ipf_output);
	} elsif($table == 3) {
		# fetch ipfAccInTable data
		$ipf_output = `$ipf_acc_in`;
		$value = &fetch_hits_bytes_n_rules($row, $col, $ipf_output);
	} elsif($table == 4) {
		# fetch ipfAccOutTable data
		$ipf_output = `$ipf_acc_out`;
		$value = &fetch_hits_bytes_n_rules($row, $col, $ipf_output);
	}
	return $value;
}

# generate response to 'get' or 'get-next' request
sub response {
	# print ObjectID, its type and the value
	if(defined $ObjectID and defined $type{$tec} and defined $value) {
		print "$ObjectID\n";
		print "$type{$tec}\n";
		print "$value\n";
	}
	exit;
}
