Gheek.net

November 13, 2008

Cisco IPS support for rancid

Filed under: Cisco, perl, rancid — Tags: , , , — lancevermilion @ 1:43 pm

In order to get rancid to collect the config from an IPS module you will need to make sure you have the correct login creds in the rancid users “.cloginrc”, add the type of ips to “rancid-fe” and you also need to create the “ipsrancid” script.

Sample addition for your “.cloginrc”. Make sure this is above anything else so no other wildcards in the “.cloginrc” get caught.

add autoenable ips-primary 1
add user ips-primary rancid
add password ips-primary <>
add method ips-primary ssh

Changes required for “rancid-fe”.

'ips' => 'ipsrancid',

Create the “ipsrancid” script as “<rancid_home>/bin/ipsrancid”. Make sure you “chmod 755 <rancid_home>/bin/ipsrancid” and “chown <rancid_user>:<rancid_user_group> <rancid_home>/bin/ipsrancid”.

#!/usr/bin/perl
##
## Copyright (C) 1997-2004 by Terrapin Communications, Inc.
## All rights reserved.
##
## This software may be freely copied, modified and redistributed
## without fee for non-commerical purposes provided that this license
## remains intact and unmodified with any RANCID distribution.
##
## There is no warranty or other guarantee of fitness of this software.
## It is provided solely "as is". The author(s) disclaim(s) all
## responsibility and liability with respect to this software's usage
## or its effect upon hardware, computer systems, other software, or
## anything else.
##
## Except where noted otherwise, rancid was written by and is maintained by
## Henry Kilmer, John Heasley, Andrew Partan, Pete Whiting, and Austin Schutz.
##
#
# hacked version of Hank's rancid - this one tries to deal with Hitachi's.
#
# Modified again by Lance Vermilion (11/13/08)
# Modified from htrancid by Jeremy M. Guthrie
# Created on 5/4/2007
#
# This is meant to try handle Cisco's IPS V5.X line and on
#
# RANCID - Really Awesome New Cisco confIg Differ
#
# usage: ipsrancid [-d] [-l] [-f filename | $host]
use Getopt::Std;
getopts('dfl');
$log = $opt_l;
$debug = $opt_d;
$file = $opt_f;
$host = $ARGV[0];
$clean_run = 0;
$found_end = 0;
$timeo = 90; # clogin timeout in seconds

my(@commandtable, %commands, @commands);# command lists
my(%filter_pwds); # password filtering mode

# This routine is used to print out the router configuration
sub ProcessHistory {

($new_hist_tag,$new_command,$command_string, @string) = (@_);
if ((($new_hist_tag ne $hist_tag) || ($new_command ne $command))
&& defined %history) {
print eval "$command \%history";
undef %history;
}
if (($new_hist_tag) && ($new_command) && ($command_string)) {
if ($history{$command_string}) {
$history{$command_string} = "$history{$command_string}@string";
} else {
$history{$command_string} = "@string";
}
} elsif (($new_hist_tag) && ($new_command)) {
$history{++$#history} = "@string";
} else {
print "@string";
}
$hist_tag = $new_hist_tag;
$command = $new_command;
1;
}

sub numerically { $a $b; }

# This is a sort routine that will sort numerically on the
# keys of a hash as if it were a normal array.
sub keynsort {
local(%lines) = @_;
local($i) = 0;
local(@sorted_lines);
foreach $key (sort numerically keys(%lines)) {
$sorted_lines[$i] = $lines{$key};
$i++;
}
@sorted_lines;
}

# This is a sort routine that will sort on the
# keys of a hash as if it were a normal array.
sub keysort {
local(%lines) = @_;
local($i) = 0;
local(@sorted_lines);
foreach $key (sort keys(%lines)) {
$sorted_lines[$i] = $lines{$key};
$i++;
}
@sorted_lines;
}

# This is a sort routine that will sort on the
# values of a hash as if it were a normal array.
sub valsort{
local(%lines) = @_;
local($i) = 0;
local(@sorted_lines);
foreach $key (sort values %lines) {
$sorted_lines[$i] = $key;
$i++;
}
@sorted_lines;
}

# This is a numerical sort routine (ascending).
sub numsort {
local(%lines) = @_;
local($i) = 0;
local(@sorted_lines);
foreach $num (sort {$a $b} keys %lines) {
$sorted_lines[$i] = $lines{$num};
$i++;
}
@sorted_lines;
}

# This is a sort routine that will sort on the
# ip address when the ip address is anywhere in
# the strings.
sub ipsort {
local(%lines) = @_;
local($i) = 0;
local(@sorted_lines);
foreach $addr (sort sortbyipaddr keys %lines) {
$sorted_lines[$i] = $lines{$addr};
$i++;
}
@sorted_lines;
}

# These two routines will sort based upon IP addresses
sub ipaddrval {
my(@a) = ($_[0] =~ m#^(\d+)\.(\d+)\.(\d+)\.(\d+)$#);
$a[3] + 256 * ($a[2] + 256 * ($a[1] +256 * $a[0]));
}
sub sortbyipaddr {
&ipaddrval($a) &ipaddrval($b);
}

# This routine parses "show config"
sub ShowConfig {
print STDERR " In ShowConfig: $_" if ($debug);

$firstexit=0;

while () {
tr/15//d;
tr/20//d;

#strip out the stupid spinning running-config progress thingy
s/Generating current config: \.*[\|\/\-\\]//gi;
$skipprocess=0;

#sometimes an 'exit' appears at the top of the config, we don't want them
if ( (/^exit/) && ( ! $firstexit ) ) {
$firstexit=1;
$skipprocess=1;
}

#remove spaces left over from lame spinning progress thingy
if ( /^\s+! ------------------------------/ ) {
s/^\s+!/!/g
}

if (/^(read-only-community) / && $filter_pwds >= 1) {
ProcessHistory("","","","!$1 \n"); next;
}
if (/^(read-write-community) / && $filter_pwds >= 1) {
ProcessHistory("","","","!$1 \n"); next;
}
if (/^(trap-community-name) / && $filter_pwds >= 1) {
ProcessHistory("","","","!$1 \n"); next;
}
if (/^(ntp-keys \d+ md5-key) / && $filter_pwds >= 1) {
ProcessHistory("","","","!$1 \n"); next;
}
if (/^(password) / && $filter_pwds >= 1) {
ProcessHistory("","","","!$1 \n"); next;
}

last if (/^$prompt/);
next if (/^(\s*|\s*$cmd\s*)$/);
if ( ! /^$prompt/) {
if ( ! $skipprocess ) {
print STDOUT " ShowConfig Data: $_" if ($debug);
ProcessHistory("","","","$_");
}
}
}
$clean_run=1;
print STDERR " Exiting ShowConfig: $_" if ($debug);
return(0);
}

# This routine parses single command's that return no required info
sub ShowVersion {
print STDERR " In ShowVersion: $_" if ($debug);
ProcessHistory("","","","!\n!IPS Show Version Start\n");

while () {
tr/15//d;

$skipprocess=0;

if ( /^Sensor up-time/ ) { $skipprocess=1; }
if ( ( /using.*bytes of available/i ) ) { $skipprocess=1; }

last if (/^$prompt/);
next if (/^(\s*|\s*$cmd\s*)$/);
if ( ! /^$prompt/) {
if ( ! $skipprocess ) {
print STDOUT " ShowVersion Data: $_" if ($debug);
ProcessHistory("","","","! $_");
}
}
}
ProcessHistory("","","","!\n!IPS Show Version End\n");
print STDERR " Exiting ShowVersion: $_" if ($debug);
return(0)
}

# This routine parses single command's that return no required info
sub ShowUsersAll {
print STDERR " In ShowUsersAll: $_" if ($debug);
ProcessHistory("","","","!\n!IPS User Database Start\n");

while () {
tr/15//d;

$skipprocess=0;

s/^ CLI ID //g;
s/^ //g;
s/^\* +[0-9]+ +//g;

last if (/^$prompt/);
next if (/^(\s*|\s*$cmd\s*)$/);
if ( ! /^$prompt/) {
if ( ! $skipprocess ) {
print STDOUT " ShowUsersAll Data: $_" if ($debug);
ProcessHistory("","","","!$_");
}
}
}
ProcessHistory("","","","!\n!IPS User Database End\n!\n!\n");
print STDERR " Exiting ShowUsersAll: $_" if ($debug);
return(0)
}

# dummy function
sub DoNothing {print STDOUT;}

# Main
@commandtable = (
{'show version' => 'ShowVersion'},
{'show users all' => 'ShowUsersAll'},
{'show configuration' => 'ShowConfig'}
);
# Use an array to preserve the order of the commands and a hash for mapping
# commands to the subroutine and track commands that have been completed.
@commands = map(keys(%$_), @commandtable);
%commands = map(%$_, @commandtable);

$cisco_cmds=join(";",@commands);
$cmds_regexp=join("|",@commands);

open(OUTPUT,">$host.new") || die "Can't open $host.new for writing: $!\n";
select(OUTPUT);
# make OUTPUT unbuffered if debugging
if ($debug) { $| = 1; }

# The IPS doesn't like the TERM of network so we must change it
if ( $ENV{TERM} eq 'network' ) {
$ENV{TERM} = 'vt100';
}
if ($file) {
print STDERR "opening file $host\n" if ($debug);
print STDOUT "opening file $host\n" if ($log);
open(INPUT,"<$host") || die "open failed for $host: $!\n";
} else {
print STDERR "executing clogin -t $timeo -c\"$cisco_cmds\" $host\n" if ($debug);
print STDOUT "executing clogin -t $timeo -c\"$cisco_cmds\" $host\n" if ($log);
if (defined($ENV{NOPIPE})) {
system "clogin -t $timeo -c \"$cisco_cmds\" $host $host.raw 2>&1" || die "clogin failed for $host: $!\n";
open(INPUT, "< $host.raw") || die "clogin failed for $host: $!\n";
} else {
open(INPUT,"clogin -t $timeo -c \"$cisco_cmds\" $host </dev/null |") || die "clogin failed for $host: $!\n";
}
}
# Change the TERM back to network
if ( $ENV{TERM} eq 'vt100' ) {
$ENV{TERM} = 'network';
}

# determine password filtering mode
if ($ENV{"FILTER_PWDS"} =~ /no/i) {
$filter_pwds = 0;
} elsif ($ENV{"FILTER_PWDS"} =~ /all/i) {
$filter_pwds = 2;
} else {
$filter_pwds = 1;
}

ProcessHistory("","","","!RANCID-CONTENT-TYPE: ipsrancid\n!\n");
TOP: while() {
tr/15//d;

#strip out the stupid spinning running-config progress thingy
s/Generating current config: \.*[\|\/\-\\]//gi;

if (/^.*logout$/) {
$clean_run=1;
last;
}
if (/^Error:/) {
print STDOUT ("$host clogin error: $_");
print STDERR ("$host clogin error: $_") if ($debug);
$clean_run=0;
last;
}
while (/($cmds_regexp)/) {
$cmd = $1;
if (!defined($prompt)) {
$prompt = ($_ =~ /^([^#]+#)/)[0];
$prompt =~ s/([][}{)(\\])/\\$1/g;
print STDERR ("PROMPT MATCH: $prompt\n") if ($debug);
}
print STDERR ("IPS COMMAND:$_") if ($debug);
if (! defined($commands{$cmd})) {
print STDERR "$host: found unexpected command - \"$cmd\"\n";
$clean_run = 0;
last TOP;
}
$rval = };
delete($commands{$cmd});
if ($rval == -1) {
$clean_run = 0;
last TOP;
}
}
}
print STDOUT "Done $logincmd: $_\n" if ($log);
# Flush History
ProcessHistory("","","","");
# Cleanup
close(INPUT);
close(OUTPUT);

if (defined($ENV{NOPIPE})) {
unlink("$host.raw") if (! $debug);
}

# check for completeness
if (scalar(%commands) || !$clean_run ) {
if (scalar(%commands)) {
printf(STDOUT "$host: missed cmd(s): %s\n", join(',', keys(%commands)));
printf(STDERR "$host: missed cmd(s): %s\n", join(',', keys(%commands))) if ($debug);
}
if (!$clean_run ) {
print STDOUT "$host: End of run not found\n";
print STDERR "$host: End of run not found\n" if ($debug);
system("/usr/bin/tail -1 $host.new");
}
unlink "$host.new" if (! $debug);
}

Advertisements

2 Comments »

  1. I have some problems when I create this script…
    Scalar found where operator expected at /usr/local/rancid/bin/ipsrancid line 69, near “$a $b”
    (Missing operator before $b?)
    Scalar found where operator expected at /usr/local/rancid/bin/ipsrancid line 115, near “$a $b”
    (Missing operator before $b?)
    syntax error at /usr/local/rancid/bin/ipsrancid line 69, near “$a $b”
    syntax error at /usr/local/rancid/bin/ipsrancid line 115, near “$a $b”
    syntax error at /usr/local/rancid/bin/ipsrancid line 339, near “= }”
    Unmatched right curly bracket at /usr/local/rancid/bin/ipsrancid line 346, at end of line
    syntax error at /usr/local/rancid/bin/ipsrancid line 346, near “}”
    Execution of /usr/local/rancid/bin/ipsrancid aborted due to compilation errors.

    Any ideas why i get this?

    Comment by Jose da silca — October 19, 2012 @ 10:28 am

    • I have not tested that code in a really long time. I know it worked when I used it. Also what version of RANCID are you using with it?

      I have looked at it and it seems to be missing a few pieces of the code. . That must have been an artifact that I moved that from my old install of WordPress on my private server to the wordpress.com blog. So it looks like some stuff was lost in translation. 😦

      The first few that are complaining about about $a and $b are easy fixes. The last few will require review of the code. Unfortunately I do not have time to do that now. I will want to modify it again to collect stuff at a later date I am sure because I have some new Cisco gear with the wonderful IPS.

      Sorry I can’t offer more help at the moment.

      Comment by lancevermilion — October 30, 2012 @ 11:13 am


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Blog at WordPress.com.

%d bloggers like this: