Converting Different Mask Formats
Problem
You want to convert between the three different formats that Cisco routers use for presenting mask information: standard netmask, ACL wildcards, and CIDR bit number format.
Solution
The following Perl script converts from any of these formatsnetmask, wildcard, or bit countto any other. The usage syntax is mask-cvt {n|w|b} {n|w|b} {nnn.nnn.nnn.nnn|/bits}, where the first argument specifies what the input format is and the second argument specifies the output format. In both cases, "n" is for netmask format, "w" is for wildcard format, and "b" is for CIDR bit format (with or without the leading slash, as in /24).
For example:
$ mask-cvt.pl n w 255.255.248.0
0.0.7.255
$ mask-cvt.pl n b 255.255.248.0
/21
$ mask-cvt.pl w n 0.3.255.255
255.252.0.0
$ mask-cvt.pl w b 0.3.255.255
/14
$ mask-cvt.pl b n /21
255.255.248.0
$ mask-cvt.pl b w /21
0.0.7.255
The Perl code follows in Example 5-1.
Example 5-1. mask-cvt.pl
#!bin/perl # # mask-cvt.pl -- a script to convert between the various # methods of masking IP addresses #
sub usage( ) { print "mask-cvt [nwb] [nwb] {nnn.nnn.nnn.nnn|bbb}\n"; print " where the first argument, [nwba], specifies the input \n"; print " format as one of netmask, wildcard or number of \n"; print " bits and the second argument, [nwb], specifies \n"; print " the output format\n"; exit( ); }
if($#ARGV != 2) { usage( ); }
# get the input format style $_ = @ARGV[0];
if(/[nN]/) { # incoming format netmask, what's the outgoing $_ = @ARGV[1]; if(/[nN]/) { # no conversion $output = @ARGV[2]; } elsif (/[wW]/) { # out is wildcard $output = do_subtract(@ARGV[2]); } elsif (/[bB]/) { # out is wildcard $output = do_bits(@ARGV[2]); } else { usage( ); } } elsif (/[wW]/) { # incoming format wildcard, what's the outgoing $_ = @ARGV[1]; if(/[wW]/) { # no conversion $output = @ARGV[2]; } elsif (/[nN]/) { # out is wildcard $output = do_subtract(@ARGV[2]); } elsif (/[bB]/) { # out is wildcard $output = do_bits(do_subtract(@ARGV[2])); } else { usage( ); } } elsif (/[bB]/) { # remove any leading "/" in the bit count $_ = @ARGV[2]; s/[-\/]//; $bits = $_; # incoming format is bit count, what's the outgoing $_ = @ARGV[1]; if(/[bB]/) { # no conversion $output = @ARGV[2]; print "no conversion\n"; } elsif (/[nN]/) { # out is netmask $output = cvt_bits_mask($bits); } elsif (/[wW]/) { # out is wildcard $output = do_subtract(cvt_bits_mask($bits)); } else { usage( ); } } else { usage( ); }
print "$output\n";
sub do_subtract( ) { local($ip) = @_;
# break up the bytes of the incoming IP address $_ = $ip; ($a, $b, $c, $d) = split(/\./);
if ($a > 255 || $b > 255 || $c > 255 || $d > 255 || /[^0-9.]/) { print "invalid input mask or wildcard\n"; exit( ); }
$a = 255 - $a; $b = 255 - $b; $c = 255 - $c; $d = 255 - $d;
return ($a . "." . $b . "." . $c . "." . $d); }
sub do_bits( ) { local($ip) = @_;
# break up the bytes of the incoming IP address $_ = $ip; @ip_bytes = split(/\./);
if ($ip_bytes[0] > 255 || $ip_bytes[1] > 255 || $ip_bytes[2] > 255 || $ip_bytes[3] > 255 || /[^0-9.]/ || $#ip_bytes != 3) { print "invalid input mask or wildcard\n"; exit( ); }
$bits = 0; for ($i=0; $i < 4 ; $i++) { if ($ip_bytes[$i] > 0 && $bits < 8*$i) { print "invalid mask for bit count format\n"; exit( ); } if ($ip_bytes[$i] == 255 ) { $bits += 8; } elsif ($ip_bytes[$i] == 254 ) { $bits += 7; } elsif ($ip_bytes[$i] == 252 ) { $bits += 6; } elsif ($ip_bytes[$i] == 248 ) { $bits += 5; } elsif ($ip_bytes[$i] == 240 ) { $bits += 4; } elsif ($ip_bytes[$i] == 224 ) { $bits += 3; } elsif ($ip_bytes[$i] == 192 ) { $bits += 2; } elsif ($ip_bytes[$i] == 128 ) { $bits += 1; } elsif ($ip_bytes[$i] != 0 ) { print "invalid mask for bit count format\n"; exit( ); } } return("/" . $bits); } sub cvt_bits_mask( ) { local($bits) = @_;
if ($bits <= 8 ) { $a = bits_to_dec($bits); $b=$c=$d=0; } else { $a=255; if ($bits <= 16 ) { $b = bits_to_dec($bits-8); $c=$d=0; } else { $b=255; if ($bits <= 24 ) { $c = bits_to_dec($bits-16); $d=0; } else { $c=255; if ($bits <= 32 ) { $d = bits_to_dec($bits-24); } else { print "invalid bit count\n"; exit( ); } } } } return ($a . "." . $b . "." . $c . "." . $d); }
sub bits_to_dec( ) { local($bits) = @_;
if($bits == 0 ) { return 0; } if($bits == 1 ) { return 128; } if($bits == 2 ) { return 192; } if($bits == 3 ) { return 224; } if($bits == 4 ) { return 240; } if($bits == 5 ) { return 248; } if($bits == 6 ) { return 252; } if($bits == 7 ) { return 254; } if($bits == 8 ) { return 255; } }
|
Discussion
This script performs several different functions. It converts from netmask format to either wildcard or bit count format, from wildcard to either netmask or bit count format, and from bit count to either netmask or wildcard format. Many experienced network engineers pride themselves on doing these conversions in their heads. But it is still relatively common to find router configurations in which the conversion has been done incorrectly.
The difference between netmask and wildcard formats is that netmask format uses ones in the bit pattern to represent bits that do not change, while wildcard format uses zeroes to represent these bits. So, for example, if you are constructing an access-list that looks at all of the devices in the subnet 192.168.1.0/24, the netmask would be 255.255.255.0, and the wildcard in the access-list would be 0.0.0.255.
The reason for the difference is that you will sometimes want to construct an access-list that doesn't care which subnet a device is on, but can be used to select a particular set of devices on that subnet. Access-lists don't look at subnets, they do pattern matching on addresses.
To convert from wildcard format to netmask format, or vice versa, the program simply subtracts each byte in the mask from the number 255, which is 8 bits of all ones. It should be relatively easy to see that this converts all of the ones in a binary pattern to zeroes, and all of the zeroes to ones.
The conversion to or CIDR bit count format is slightly more complicated in the program, but easier in concept. If the input is a netmask, the CIDR bit count is simply the number of ones in the bit pattern, counting from the left. Similarly, if the source is a wildcard, then the bit count can be found by counting zeroes. The program actually only has one subroutine for counting bits. If it needs to convert a wildcard pattern to a bit count, it converts it to netmask format first.
It is important to notice that the CIDR bit count format only makes sense if all of the ones in a netmask are on the left, and all of the zeroes on the right. Then this number simply represents the location of the transition from ones to zeroes, which in turn represents the division point between the network and host portions of the address. So the program includes a check to ensure that the netmask pattern is valid, with no zeroes to the left of any ones in the pattern.