Parsing IP Access Lists with Cisco::Reconfig without the dreaded died at /cisco/reconfig.pm line 212 message


Since making a few posts about using Cisco::Reconfig, two of the top search results that brings people here is “died at cisco/reconfig.pm line 212″ or “died at cisco/reconfig.pm line 103″. I’ll show you what causes these as I demonstrate how to compare standard and extended ip access-lists with Cisco::Reconfig.


For the second message about line 102, that’s easy enough to fix. Search for line 103 in the Reconfig.pm module and comment out lines 103 through 106.

Essentially, change this:

                                # this really shouldn't happen.  But it does.
                               die unless $prev eq "!\n" || $prev =~ /^!.*<removed>$/;
                               die unless $indent == 0;
                               $ciscobug = 1;
                               $indent = $in;

To this:

                               # this really shouldn't happen.  But it does.
                               #die unless $prev eq "!\n" || $prev =~ /^!.*<removed>$/;
                               #die unless $indent == 0;
                               #$ciscobug = 1;
                               #$indent = $in;

I am not too familiar with the Cisco::Reconfig code, but I have been running scripts with this modification against 600+ routers for over a year without issues. If uncommented, it always crashes against our GSRs. It must be left over code from some old IOS configuration quirk (Cisco never changes stuff around from version to version!). Cisco’s anti-automation engineers will probably see this and the next IOS release will break all my scripts now. :)

The second error about line 212 always seems to happen when you’re searching for route-maps or ip access-lists (not regular access-lists). Basically, anything where you have a sub query such as:

my @EACL = $::MYCONFIG->get('ip access-list extended SOMEACL', 'permit')->alltext;

If the SOMEACL ip access-list doesn’t exist, Cisco::Reconfig bombs out with the error at line 212 because it doesn’t have any error handling for this situation. Line 212 has an explicit “die” if the two sequences aren’t true. Doesn’t even print a message – just crashes.

So, if you run into this, you can work around it by doing your own testing first. I’ll show you how I test for whether an ip access-list exists before deciding to try to get the following lines.

                                my $eaclline = $::MYCONFIG->get($aclcmd);
                                chomp ($eaclline);
                                if ($eaclline !~ /$aclcmd/)
                                {
                                        $::REPORTID = 14;
                                        $::MESSAGE = "CRITICAL: Missing '$aclcmd'!";
                                        InsertLogMessage();
                                        ClearERRs();
                                        next;
                                }

In the above code, $aclcmd is pulled from my database and basically would look like ‘ip access-list standard SSM’. All I care about is searching for that one line before doing a more complex search. If the output doesn’t match the $aclcmd variable, we report an error, insert it into the report table, and move on.

If it does exist, we can compare the contents of the ip access-list.

                                if ($deny == 1)
                                {
                                        my @EACLDt = $::MYCONFIG->get($aclcmd, 'deny')->alltext;
                                        foreach (@EACLDt)
                                        {
                                                my $line1 = $_;
                                                push @aclrestemp, split (/\n/, $line1);
                                        }
                                }
                                if ($permit == 1)
                                {
                                        my @EACLPt = $::MYCONFIG->get($aclcmd, 'permit')->alltext;
                                        foreach (@EACLPt)
                                        {
                                                my $line2 = $_;
                                                push @aclrestemp, split (/\n/, $line2);
                                        }
                                }

If you’re curious about the complete code for this function, here it is.

                my $aclsql = "SELECT extended,acl,name,contents FROM AccessLists WHERE chassisid = '$::CHASSIS' AND rtypeid = '$::TYPE'";
                my $aclqry = $dbh->prepare($aclsql);
                $aclqry->execute();

                while (@aclrow = $aclqry->fetchrow_array())
                {
                        my $isExtended  = $aclrow[0];
                        my $aclcmd      = $aclrow[1];
                        my $aclname     = $aclrow[2];
                        my $aclcontents = $aclrow[3];

                        my $deny = 0;
                        my $permit = 0;

                        my @aclarr;
                        my @acltmp = split('\n', $aclcontents);
                        for (@acltmp)
                        {
                                my $aclline = $_;
                                chomp ($aclline);
                                $aclline =~ s/^\s+|\s+$//g;
                                if ($aclline =~ /^deny/)
                                {
                                        $deny = 1;
                                }
                                if ($aclline =~ /^permit/)
                                {
                                        $permit = 1;
                                }
                                push @aclarr, $aclline;
                        }

                        my @aclrestemp;

                        if ($isExtended == 0)          ### regular accesss-list
                        {
                                @aclrestemp = $::MYCONFIG->get($aclcmd)->all;
                        }
                        if ($isExtended == 1)          ### ip access-list
                        {
                                my $eaclline = $::MYCONFIG->get($aclcmd);
                                chomp ($eaclline);
                                if ($eaclline !~ /$aclcmd/)
                                {
                                        $::REPORTID = 14;
                                        $::MESSAGE = "CRITICAL: Missing '$aclcmd'!";
                                        InsertLogMessage();
                                        ClearERRs();
                                        next;
                                }

                                if ($deny == 1)
                                {
                                        my @EACLDt = $::MYCONFIG->get($aclcmd, 'deny')->alltext;
                                        foreach (@EACLDt)
                                        {
                                                my $line1 = $_;
                                                push @aclrestemp, split (/\n/, $line1);
                                        }
                                }
                                if ($permit == 1)
                                {
                                        my @EACLPt = $::MYCONFIG->get($aclcmd, 'permit')->alltext;
                                        foreach (@EACLPt)
                                        {
                                                my $line2 = $_;
                                                push @aclrestemp, split (/\n/, $line2);
                                        }
                                }
                        }

                        my @aclres;
                        for (@aclrestemp)
                        {
                                my $restemp = $_;
                                $restemp =~ s/^\s+|\s+$//g;
                                chomp ($restemp);
                                push @aclres, $restemp;
                        }

                        # some quantum sexiness going on here
                        my @fACL = eigenstates(any(@aclres) ne all(@aclarr));
                        my @rACL = eigenstates(any(@aclarr) ne all(@aclres));

                        my $fcount = @fACL;
                        my $rcount = @rACL;

                        # If the first element is empty, don't try to insert stuff into the logs
                        if ($fACL[0] ne "")
                        {
                                for (@fACL)
                                {
                                        $::REPORTID = 14;
                                        $::MESSAGE = "$aclname extra: $_";
                                        InsertLogMessage();
                                }
                        }

                        if ($rACL[0] ne "")
                        {
                                for (@rACL)
                                {
                                        $::REPORTID = 14;
                                        $::MESSAGE = "$aclname missing: $_";
                                        InsertLogMessage();
                                }
                        }

                        ClearERRs();
                }
        }

Our AccessList table looks like this (click to enlarge):

Because we have some “Core” routers that are GSRs and some that are 7600s, the table has a chassisid and rtypeid. That allows us to have different ACLs for platforms with different functions.

The acl column is the actual command that is searched for in the $aclcmd variable. The name column is used when inserting an error message into our report table. Finally, the contents column is a text type and contains the official access-list used to audit against.

I hope this post helps those of you frustrated by the cryptic errors with Cisco::Reconfig. It is very useful, but it would be nice if it actually told you why it crashed. Even better would be if it had more enhanced error handling. Unfortunately, since it was last updated 3 years ago (Feb 2007), it seems to be abandoned. A shame for such a valuable module.

If anyone stumbles on this post and has a different error than the two identified, please leave a comment and I’ll see what I can do to help you out.

, ,

  1. No comments yet.
(will not be published)