Cisco-ACL
view release on metacpan or search on metacpan
lib/Cisco/ACL.pm view on Meta::CPAN
$rule_string = $action ? "permit" : "deny";
$rule_string .= " $protocol ";
if ($src_addr =~ /\//) {
$src_elem = parse_cidr($src_addr);
}
elsif ($src_addr =~ /any/) {
$src_elem = "any";
}
else {
$src_elem = "host $src_addr";
};
if ($dst_addr =~ /\//) {
$dst_elem = parse_cidr($dst_addr);
}
elsif ($dst_addr =~ /any/) {
$dst_elem = "any";
}
else {
$dst_elem = "host $dst_addr";
};
if ($src_port =~ /any/) {
$src_p_elem = "";
}
else {
$src_p_elem = $src_port;
};
if ($dst_port =~ /any/) {
$dst_p_elem = "";
}
else {
$dst_p_elem = $dst_port;
};
$rule_string .= "$src_elem $src_p_elem $dst_elem $dst_p_elem";
if( $established ) {
$rule_string .= " established";
}
$rule_string =~ s/\s+/ /g;
$rule_string =~ s/\s+$//;
return $rule_string;
};
#
#-------------------------------------------------------------------
#
sub breakout_addrs {
# Split on commas, return a list where every element is either a
# single address or a single cidr specification.
my @list = @_;
if ($list[0] =~ /any/) { return("any"); };
my (@elements,$addr,@endpoints,@octets1,@octets2,$start,$end,$i,
$number_of_endpoints,$number_of_octets,$done,$dec_start,$dec_end,@george,$remaining);
foreach $addr( @list ) {
if ($addr !~ /\-/) {
push @elements, $addr; # Not a range and we're returning single addresses and
# cidr notation as is, so nothing to do
}
else {
@endpoints = split(/\-/, $addr);
$number_of_endpoints = @endpoints;
if ($number_of_endpoints != 2) {
next; # something is screwey; probably something like
# 10.10.10.10-20-30. Silently shitcan it.
};
# Two cases left; x.x.x.x-y.y.y.y and x.x.x.x-y
#
@octets2 = split(/\./, $endpoints[1]);
$number_of_octets = @octets2;
if ($number_of_octets == 4) {
$dec_start = ip_to_decimal($endpoints[0]);
$dec_end = ip_to_decimal($endpoints[1]);
push @elements, ferment("$dec_start-$dec_end");
}
else {
@octets1 = split(/\./, $endpoints[0]);
my $newend = "$octets1[0].$octets1[1].$octets1[2].$octets2[0]";
$dec_start = ip_to_decimal($endpoints[0]);
$dec_end = ip_to_decimal($newend);
push @elements, ferment("$dec_start-$dec_end");
}
}
}
return(@elements);
}
#
#-------------------------------------------------------------------
#
sub breakout_ports {
my @list = @_;
my ($tidbit,@endpoints,$start,$end,$i,$number_of_endpoints,@elements);
foreach $tidbit( @list ) {
if ($tidbit =~ /\-/) {
@endpoints = split(/\-/, $tidbit);
$number_of_endpoints = @endpoints;
if ($number_of_endpoints != 2) {
next;
};
$start = $endpoints[0];
$end = $endpoints[1];
# flip range ends if they are backward
if ($start >= $end) {
($start, $end) = ($end, $start);
};
push @elements, "range $start $end";
}
else {
push @elements, "eq $tidbit";
}
};
return(@elements);
};
#
#-------------------------------------------------------------------
#
sub parse_cidr {
my $bob = $_[0];
my ($address, $block, $start, $end, $mask, $rev_mask);
($address, $block) = split(/\//, $bob);
($start, $end) = ip_to_endpoints($address, $block);
$mask = find_mask($block);
my $bin_mask = ip_to_bin($mask);
my @bits = split(//, $bin_mask);
foreach my $toggle_bait (@bits) {
if ($toggle_bait eq "1") {
$toggle_bait = "0";
}
else {
$toggle_bait = "1";
};
};
my $inv_bin = join "",@bits;
my $inv_mask = bin_to_ip($inv_bin);
return "$start $inv_mask ";
}
#
#-------------------------------------------------------------------
#
sub ferment {
# Ferment = "cidr-ize" the address range (ha ha, ok, I'll keep
# my day job.) Take the range given as xxxx-yyyy (it's decimal!!)
# and find the most concise way to express it in cidr notation.
# Return: The list of elements, or "" if the range given was ""
# Arguments: the range, the list of elements to add to.
my $range = shift(@_);
my @list_to_date = @_;
my ($start,$end,$difference,$i,$got_it,@working_list,
$trial_start,$trial_end,$dotted_start,$block_found,$remaining_range);
if ($range eq "") { return(@list_to_date) }; # an end condition
($start, $end) = split(/\-/, $range);
$difference = $end - $start;
if ($difference == 0) {
# The range is one address (i.e. start and end are the same);
# return it in dotted notation and we're at another end condition.
push @list_to_date, decimal_to_ip($start);
return(@list_to_date);
};
$got_it = 0;
for ($i = 1; $i < 31; $i++) {
# We'll only try to put 1 block per call of this subroutine
if ($got_it) { last };
# Using the cidr size for this loop iteration, calculate what
# the block of that size would be for the start address we
# have, then compare that to the range we're looking for.
#
($trial_start, $trial_end) = ip_to_endpoints(decimal_to_ip($start),$i); # dotted
$trial_start = ip_to_decimal($trial_start); # now decimal
$trial_end = ip_to_decimal($trial_end);
#
# Ok, now these are in decimal
#
if ($trial_start == $start) {
# Woo hoo, the start of the range is aligned with a cidr boundary.
# Is it the right one? We know it's the biggest possible,
# but it may be too big. If so, just move on to the next
# $i (i.e. next smaller sized block) and try again.
#
if ($trial_end > $end) { next; };
# otherwise, it's the money...
#
$got_it = 1;
$dotted_start = decimal_to_ip($start);
$block_found = "$dotted_start/$i";
$start += (($trial_end - $start) + 1);
#
# Ok, now we've reduced the range by the amount of space
# in the block we just found.
#
# The extra '+1' above means that the next start point
# will be one address beyond the end of the block we
# just found (otherwise we'd find a few individual addresses
# twice). However, it also means that for the final block,
# $start is > $end by 1. We have to check for that before
# returning the values; if we let it through we'll
# spin forever...
#
}
else {
next; # try the next smaller size block
}
} # for loop
# Ok, we're done trying cidr blocks. If we found one, return it
# and the remaining range. Otherwise, return 1 address and the
# remaining range.
if ($got_it) {
# We already calculated $block_found
$remaining_range = "$start-$end";
if ($start > $end) { $remaining_range = "" }
}
else {
$block_found = decimal_to_ip($start);
$start++;
$remaining_range = "$start-$end";
if ($start > $end) { $remaining_range = "" }
}
push @list_to_date, $block_found;
return(ferment($remaining_range,@list_to_date));
};
#
#-------------------------------------------------------------------
#
sub ip_to_endpoints {
#
# Various of these routings use strings for bit masks where
# it would undoubtedly be much more efficient to use real binary
# data, but... it's fast enough, and this was easier. :)
#
my($address,$cidr,$zeros,$ones,$bin_address);
$address = $_[0];
$bin_address = ip_to_bin($address);
$cidr = $_[1];
$zeros = "00000000000000000000000000000000";
$ones = "11111111111111111111111111111111";
for(my $i=0; $i<=($cidr-1); $i++) {
substr($zeros,$i,1) = substr($bin_address,$i,1);
substr($ones,$i,1) = substr($bin_address,$i,1)
};
return(bin_to_ip($zeros), bin_to_ip($ones));
};
###########################################################################
sub find_mask {
my($cidr,$bin,$i);
$cidr = $_[0];
$bin = "00000000000000000000000000000000";
for ($i=0; $i<=31; $i++) {
if ($i <= ($cidr-1)) {
substr($bin,$i,1) = "1"
}
}
my $mask = bin_to_ip($bin);
return($mask);
};
############################################################################
sub ip_to_decimal {
my($address, $i, $a, $b, $c, $d);
$address = shift(@_);
($a, $b, $c, $d) = split(/\./, $address);
$i = (256**3)*$a + (256**2)*$b + 256*$c + $d ;
return($i);
};
############################################################################
#
# Ok, so, it's a hack... sue me. :)
#
sub decimal_to_ip {
return bin_to_ip(decimal_to_bin($_[0]));
};
############################################################################
sub decimal_to_bin {
my($decimal,@bits,$i,$bin_string);
$decimal = $_[0];
@bits = "";
for ($i=0;$i<=31;$i++) {
$bits[$i] = "0";
( run in 0.609 second using v1.01-cache-2.11-cpan-ceb78f64989 )