Previous section   Next section

Recipe 5.3 Converting Different Mask Formats

5.3.1 Problem

You want to convert between the three different formats that Cisco routers use to present mask information: standard netmask, ACL wildcards, and CIDR bit numbers.

5.3.2 Solution

The following Perl script converts from any of these formats to 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

Example 5-1 contains the Perl code for the mask-cvt script.

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; }
}

5.3.3 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 zeros 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 zeros, and all of the zeros to ones.

The conversion to or from 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, the bit count can be found by counting zeros. The program actually has only 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 note that the CIDR bit count format makes sense only if all of the ones in a netmask are on the left, and all of the zeros are on the right. Then the bit count number simply represents the location of the transition from ones to zeros, which in turn represents the division point between the network and host portions of the address. The program includes a check to ensure that the netmask pattern is valid, with no zeros to the left of any ones in the pattern.


  Previous section   Next section
Top