http://www.perlmonks.org?node_id=61057
Category: Networking Code
Author/Contact Info Mark Thomas mark@ackers.net
Description: Juniper (www.juniper.net) is a provider of high-end routing equipment, even the lower-end juniper equipment (m20) can out-preform cisco's high-end 12k GSR's.

I needed a way to easily audit my juniper configurations on a ever expanding juniper-core based network, thus this was born.

Writing the template configuration file:
Writing the template configuration is a little on the complex side, and it takes a little bit of explaining, so I put the configuration readme at juniper-audit-readme.txt

Overall this code is in beta, and I know there are many things I could do much better, and I intend on doing so, any suggestions and comments would also be great!
#!/usr/bin/perl

use strict;
use Getopt::Std;
use Net::Telnet;

my %globhash;
my %subhash;
my %args = ();

getopts("fksdu:p:i:c:h", \%args);

my @ips;
my $IP_FILE;
my $CONF_FILE;
my $debug = 0;
my $USER = '';
my $PASS = '';

$debug = 1 if $args{d};
$USER = $args{u} if $args{u};
$PASS = $args{p} if $args{p};
$IP_FILE = $args{i};
$CONF_FILE = $args{c};

if ($args{h}) {
        &showhelp;
}

die "usage: $0 [-dupicskhf] -u <user> -p <pass> -i <ipfile> -c <conf f
+ile>" unless ($IP_FILE && $CONF_FILE);

open(IPS, "$IP_FILE");
while(<IPS>) {
        chomp;
        push @ips,$_;
}
close(IPS);
foreach my $ip (@ips) {
        doaudit($ip);
        unlink "$ip.tmp" if !$args{k};
} 

sub showhelp {
print <<EOF
        -u <user>:      username of the juniper routers

        -p <pass>:      password of the juniper routers

        -i <ipfile>:    file of ip juniper ip address's

        -c <config>:    the audit configuration file (see README)

        -d :            debugging information (mucho text)

        -s :            show matched information also 
                        (Not just the not matched)

        -h :            this help screen ;)

        -k :            keep a copy of the juniper's config file in <h
+ostname>.tmp

        -f :            show stuff that was left over that was in the 
+juniper.conf
                        but not in the audit configuration file (stuff
+ that was in
                        the juniper.conf but not in the template
EOF
}

sub getconfig {
        my($host) = @_;
        my $t;
        my $CMD = 'show configuration | no-more';
        $t = new Net::Telnet (Timeout => 10);
        $t->open("$host");
        $t->login($USER, $PASS);
        my @lines = $t->cmd($CMD);
        open(TMP, ">$host.tmp");
        foreach my $line (@lines) {
                print TMP "$line";
        }
        close(TMP);
}

sub getintnames {
        # usage getintnames(IP, TYPE(OC-3));
        # returns array
        my (@cardz,$slotcounter,$slot,$cardtype,$pic,$ports);
        my($ip,$type) = @_;
        my $t;
        my $CMD = 'show chassis fpc pic-status';
        $t = new Net::Telnet (Timeout => 10);
        $t->open("$ip");
        $t->login($USER, $PASS);
        my @lines = $t->cmd($CMD);
        foreach my $fpc (@lines) {
                if ($fpc =~ /Slot (\d+?).*$/) {
                        $slotcounter = 1;
                        $slot = $1;
                        next;
                }
                if ($slotcounter eq 1) {
                        my $knownthing = 0;
                        if ($fpc =~ /PIC (\d+?)\s+(.*?)\,.*$/) {
                                $cardtype = $2;
                                $pic = $1;
                                        if($cardtype =~ /$type/) {
                                                $ports = $cardtype;
                                                $ports =~ /^(\d)x.*/;
                                                $ports = $1;
                                                $knownthing  = 1;
                                                for (my $i = 0; $i < $
+ports; $i++) {
                                                        my $card = "so
+-$slot/$pic/$i";
                                                        push @cardz,$c
+ard;
                                                }
                                        }
                        }
                }
        }
        return @cardz;
}

sub parsit {
        my (@matched,@body);
        my ($host,$where) = @_;

        open(FILE, "$host.tmp");
        my $inside = "";
        my %hash = ();  
                        
        while (<FILE>) {
                chomp;
                push @body,$_;
        }
        close(FILE); 
        while (my $line = shift @body) {
                if($line =~ /^([^\{]+)\{/) {
                        my $start = $1;
                        $start =~ s/^\s+//g;
                        $start =~ s/\s+$//g;
                        if($inside) {
                                $inside .= ":$start";

                        }
                        else {
                                $inside = $start;
                        }
                        next;
                }
                elsif ($line =~ /\}/) {
                        push @{$hash{$inside}},undef;
                        if ($inside =~ /\w+(\:\w+)+/) {
                                my @tmp = split ':', $inside;
                                $#tmp--;
                                $inside = join ':', @tmp;
                        }
                        else {
                                $inside = "";
                                next;
                        }
                }
                else {
                        $line =~ s/^\s+//g;
                        $line =~ s/\s+$//g;
                        push @{$hash{$inside}},$line;
                }
        }
        foreach my $thingy ( keys %hash ) {
                foreach my $blah (@{ $hash{$thingy} }) {
                        $blah =~ s/^\s+//g;
                        push @matched,$blah if $thingy eq $where; 
                }
        }

        return @matched;
}





sub doaudit {
        my($host) = @_;
        my (@all);
        getconfig($host);
        open(FILE, "$CONF_FILE");

        while(<FILE>) {
                chomp;
                s/^\#.*$//g; # i want comments damnit
                s/\n$//g;
                s/\s+$//g;
                s/^\s+//g;
                print "DEBUG: while(<FILE>) {: pushing $_ into \@all\n
+"
                        if $debug eq 1;
                push @all, $_;
        }
        close(FILE);
        print "\n\n" 
                if $debug eq 1;
        my $startgroup = 0;
        my $brackets = 0;
        my $globalcounter = 0;
        my $subcounter = 0;
        my $group;
        my $sub;
        my %globhash = ();
        my %subhash = ();
        foreach my $line (@all) {
                if ($line =~ /^GROUP: (\S+?)\s+\{$/) {
                        $group = $1;
                        $startgroup = 1;
                        print "DEBUG: \$line matched GROUP: $group\n"
                                if $debug eq 1;
                        next;
                }
                if ($startgroup eq 1) {
                        if ($line =~ /GLOBAL {$/) {
                                print "DEBUG: Matched GLOBAL for \"$li
+ne\"\n"
                                        if $debug eq 1;
                                $globalcounter = 1;
                                next;
                        }
                        if ($globalcounter eq 1) {
                                print "DEBUG: \$globalcounter ='s 1\n"
                                        if $debug eq 1;
                                if ($line =~ /}/) {
                                        print "DEBUG: \$line matched }
+\n"
                                                if $debug eq 1;
                                        $globalcounter = 0;
                                        next;
                                }
                                else {
                                        print "DEBUG: \$line isnt }\n"
                                                if $debug eq 1;
                                        if ($globhash{$group} eq "") {
                                                $globhash{$group} = "$
+line";
                                                print "DEBUG globhash 
+\$globhash{$group} = \"$line\"\n"
                                                        if $debug eq 1
+;
                                                        next;
                                        }
                                        else {
                                                $globhash{$group} .= "
+!:!$line";
                                                print "DEBUG globhash 
+\$globhash\{$group\} = \"!:!$line\"\n"
                                                        if $debug eq 1
+;
                                                        next;
                                        }
                                }
                        }
                        if ($line =~ /SUB: (.*?)\s+{$/) {
                                $sub = $1;
                                $subcounter = 1;
                                print "DEBUG \$line matched SUB: $line
+\n"
                                        if $debug eq 1;
                                        next;
                        }
                        if ($subcounter eq 1) {
                                print "DEBUG \$subcounter ='s 1: $line
+\n"
                                        if $debug eq 1;
                                if ($line =~ /}/) {
                                        $subcounter = 0;
                                        print "DEBUG found } so \$subc
+ounter ='s 0 now\n"
                                                if $debug eq 1;
                                        next;
                                }
                                else {
                                        my $both = "$group:$sub";
                                        if ($subhash{$both} eq "") {

                                                $subhash{$both} = "$li
+ne";
                                                print "DEBUG subhash \
+{$both\} = \"$line\"\n"
                                                        if $debug eq 1
+;
                                                next;
                                        }
                                        else {
                                                $subhash{$both} .= "!:
+!$line";
                                                print "DEBUG subhash \
+{$both\} = \"!:!$line\"\n"
                                                        if $debug eq 1
+;
                                                next;
                                        }

                                }
                        }
                }
        }
        my $type;
        print "\n$host audit\n----------------------------\n";
        print "GLOBALS\n";
        while(my($k,$v) = each %globhash) {  
                my @stuff = split(/!:!/,$v);
                my $worde;
                my @matched = parsit($host,$k);
                for (my $j=0; $j < @stuff; $j++) {
                        $stuff[$j] =~ /(.*?):(.*?)$/;
                        $type = $1;
                        $worde = $2;
                        my $knownthing = 0;
                        for (my $i=0; $i < @matched; $i++) {
                                $worde =~ s/^\s+//g;
                                $matched[$i] =~ s/^\s+//g;
                                $matched[$i] =~ s/\s+$//g;
                                if ($type eq "EXACT") {
                                        if ($worde eq $matched[$i]) {
                                                print "\tMATCHED EXACT
+: $k -- $matched[$i]\n" if $args{s};
                                                splice(@matched, $i, 1
+);
                                                splice(@stuff, $j, 1);
                                                $j--;
                                                $i--;
                                                $knownthing = 1;
                                        }
                                }
                                if ($type eq "INCLUDE") {
                                        if ($matched[$i] =~ /$worde/) 
+{
                                                print "\tMATCHED REGEX
+: $k -- $matched[$i]\n" if $args{s};
                                                splice(@matched, $i, 1
+);
                                                splice(@stuff, $j, 1);
                                                $j--;
                                                $i--;
                                                $knownthing = 1;
                                        }
                                }
                        }
                }
                if ($args{f}) {
                        print "\tNOT IN TEMPLATE:\n";
                        foreach my $crap (@matched) {
                                next if $crap =~ /^\s+$/;
                                print "\t\t$crap\n";
                        }
                }
                print "\tNOT IN HOSTS JUNIPER.CONF:\n";
                foreach my $crap (@stuff) {
                        next if $crap =~ /^\s+$/;
                        print "\t\t$crap\n";
                }

        }
        print "\nSUBS\n";
        my @stuff;
        while(my($k,$v) = each %subhash) {
                my ($intmatch) = 0;
                my ($worde);
                my ($ktmp);
                my $haveint = 0;
                @stuff = split(/!:!/,$v);
                if ($k =~ /MATCH-INTERFACES (.*?)($|:(.*))/) {
                        my $devtype = $1;
                        my $whatelse = $2;
                        my @intnames = getintnames($host,$devtype);
                        foreach my $int (@intnames) {
                                $ktmp = $k;
                                $ktmp =~ s/MATCH-INTERFACES.*?($|:)/$i
+nt:/g;
                                getsubs(\@stuff,$host,$ktmp);
                        }
                        next;
                } 
                else {
                        $ktmp = $k;
                        getsubs(\@stuff,$host,$ktmp);
                        next;
                }
                sub getsubs {
                        my ($stuff,$host,$k) = @_;
                        my @matched = parsit($host,$k);
                        for (my $j=0; $j < @$stuff; $j++) {
                                $stuff->[$j] =~ /(.*?):(.*?)$/;
                                $type = $1;
                                $worde = $2;
                                my $knownthing = 0;
                                for (my $i=0; $i < @matched; $i++) {
                                        $worde =~ s/^\s+//g;
                                        $matched[$i] =~ s/^\s+//g;
                                        $matched[$i] =~ s/\s+$//g;
                                        if ($type eq "EXACT") {
                                                if ($worde eq $matched
+[$i]) {
                                                        print "\t\t\tM
+ATCHED EXACT: $k -- $matched[$i]\n" if $args{s};
                                                        splice(@matche
+d, $i, 1);
                                                        $i--;
                                                        $knownthing = 
+1;
                                                }
                                        }
                                        if ($type eq "INCLUDE") {
                                                if ($matched[$i] =~ /$
+worde/) {
                                                        print "\t\t\tM
+ATCHED REGEX: $k -- $matched[$i]\n" if $args{s};
                                                        splice(@matche
+d, $i, 1);
                                                        $i--;
                                                        $knownthing = 
+1;
                                                } 
                                        }
                                }
                                print "\tNOT IN JUNIPER.CONF: $k -- $w
+orde\n" if !$knownthing;
                        }
                        if ($args{f}) {
                                foreach my $crap (@matched) {
                                        next if $crap =~ /^\s+$/;
                                        next if $crap eq "";
                                        print "\t\tNOT IN TEMPLATE $k 
+-- $crap\n";
                                }
                        }
                }
        }
}