hi Clayton, for testing purposes, I have separated the problematic part of the rule into a simple Single rule. As you can see, the rule calls the 'eval' action for resolving an IP address and then writes it to standard output with 'write' action:
type=single ptype=regexp pattern=neighbor \*?(\d+\.\d+\.\d+\.\d+) Down desc=BGP Neighbor $1 Down action=eval %hostname ( $line = `perl -MSocket -E "say scalar gethostbyaddr(inet_aton(\"$1\"), AF_INET)"`; \ if ($line !~ //) { "$$1"; } else { "$1"; } ); \ write - hostname: %hostname The code fragment provided for 'eval' action has two issues, and I'll try to explain them below. The first issue is related to interpretation of the command line perl -MSocket -E "say scalar gethostbyaddr(inet_aton(\"$1\"), AF_INET)" At first glance, this command line appears to work when one tries it with a specific IP address -- for example, when executing perl -MSocket -E "say scalar gethostbyaddr(inet_aton(\"127.0.0.1\"), AF_INET)" in a terminal window, the command line returns string "localhost" as expected. However, when sec calls the 'eval' action which essentially means executing this code as a Perl program, this code gets interpreted and backslashes in front of " symbols are removed. Therefore, the command line which gets forked from 'eval' action is actually perl -MSocket -E "say scalar gethostbyaddr(inet_aton("127.0.0.1"), AF_INET)" This command line gets interpreted by shell before it gets executed by separate Perl process, and as a result, the Perl process will see the following code: say scalar gethostbyaddr(inet_aton(127.0.0.1), AF_INET) However, since the parameter for inet_aton() is not a string, the function fails. For addressing this issue, you might use a pair of two backslashes for masking, for example: $line = `perl -MSocket -E "say scalar gethostbyaddr(inet_aton(\\"$1\\"), AF_INET)"` As an alternative, you could also enclose the entire say statement in apostrophes which disables interpretation for it: $line = `perl -MSocket -E 'say scalar gethostbyaddr(inet_aton("$1"), AF_INET)'` The other issue is related to the following code fragment: if ($line !~ //) { "$$1"; } else { "$1"; } Firstly, the regular expression // matches an empty string which can be found in any input string, and therefore the check ($line !~ //) is always false. As a result, the 'eval' action would always execute the else-branch and return an IP address. Also, the regular expression // does not contain any capture groups and therefore reference $$1 can't possibly return a value. For addressing this issue, the code fragment could be rewritten as: if ($line =~ /^(\S+)/) { "$$1"; } else { "$1"; } After introducing the changes above, the rule would return a hostname as it should. However, the rule is still sub-optimal, since it essentially first compiles and executes Perl code, in order to fork another process for compiling and executing another piece of Perl code. Below is a much more efficient alternative which loads perl Socket module when sec starts up, and resolves IP addresses to hostnames via fast 'lcall' action that avoids expensive code compilation before each execution: type=Single ptype=SubStr pattern=SEC_STARTUP context=SEC_INTERNAL_EVENT continue=TakeNext desc=Load the Socket module and terminate if it is not found action=eval %ret (require Socket); \ if %ret ( logonly Socket module loaded ) else ( eval %o exit(1) ) type=single ptype=regexp pattern=neighbor \*?(\d+\.\d+\.\d+\.\d+) Down desc=BGP Neighbor $1 Down action=lcall %hostname $1 -> ( sub { my $host = scalar gethostbyaddr(Socket::inet_aton($_[0]), Socket::AF_INET); if (!defined($host)) { $host = $_[0]; } return $host; } ); \ write - hostname: %hostname All the work is done by anonymous function sub { my $host = scalar gethostbyaddr(Socket::inet_aton($_[0]), Socket::AF_INET); if (!defined($host)) { $host = $_[0]; } return $host; } which resolves IP address parameter to hostname with gethostbyaddr(), and returns the IP address itself if it does not resolve. Hopefully these examples are helpful, risto Kontakt Clayton Dukes (<cdu...@logzilla.net>) kirjutas kuupäeval K, 28. august 2019 kell 08:25: > Hi folks, > > I have a user with a rule that is trying to use perl gethostbyaddr but it > doesn’t seem to be returning anything. > > Can someone point out what’s wrong here? > > I added the `write` statement at the end and the file just writes the IP, > not the reverse lookup hostname. > > > > # Tracks BGP tunnel downtime > > type=pair > > ptype=regexp > > continue=dontcont > > pattern=neighbor \*?(\d+\.\d+\.\d+\.\d+) Down > > desc=BGP Neighbor $1 Down > > action=eval %hostname ( $line = `perl -MSocket -E "say scalar > gethostbyaddr(inet_aton(\"$1\"), AF_INET)"`; \ > > if ($line !~ //) { "$$1"; } else { "$1"; } ); \ > > eval %storenumber ( if ("%hostname" =~ /lo/) { "$$1"; } else { "NA"; } > ); \ > > eval %tunnel ( if ("%hostname" =~ /lo(\d+)/) { "$$1"; } else { "NA"; } > ); \ > > eval %TS (time()); \ > > tcpsock 10.1.0.85:514 SEC BGP Neighbor status="Down" > hostname="%hostname" store="%storenumber" tunnel="%tunnel" > ECRule="01-bgp-flap-detection" ECRulenum="1"%{.nl}; \ > > write /var/log/debug.sec.log %hostname > > ptype2=regexp > > pattern2=neighbor \*?($1) Up > > desc2=BGP Neighbor $1 Up > > action2=eval %TT ( time() - %TS ); \ > > tcpsock 10.1.0.85:514 SEC BGP Neighbor status="Up" > hostname="%hostname" store="%storenumber" tunnel="%tunnel" downtime="%TT" > ECRule="01-bgp-flap-detection" ECRulenum="2"%{.nl} > > > > > > > > > _______________________________________________ > Simple-evcorr-users mailing list > Simple-evcorr-users@lists.sourceforge.net > https://lists.sourceforge.net/lists/listinfo/simple-evcorr-users >
_______________________________________________ Simple-evcorr-users mailing list Simple-evcorr-users@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/simple-evcorr-users