How to wrap your brain around CIDR

By  

apple cider

Did someone say cider?

flickr/dipfan

If you think about the underlying binary values, CIDR -- the classless inter-domain routing method of expressing IP address ranges -- makes a lot of sense. The trick is understanding that the boundary between the network portion of an address and the host portion sits where the netmask in binary turns from a string of 1's into a string of 0's. That's easy to see with a CIDR that ends in 16 (8, 24, or even 32).

192.168.0.0/16
11000000 10101000 00000000 00000000
11111111 11111111 00000000 00000000
                 ^

The host addresses, stretching from 192.168.0.1 through 192.168.255.254 seem pretty obvious. Remember that the 192.168.0.0 and 192.168.0.255 addresses are reserved for the network and broadcast addresses.

11000000 10101000 00000000 00000001
11000000 10100100 11111111 11111110
                 ^

Once you stray from netmasks that align on byte boundaries, however, it's harder to whip up a list of the included hosts. Change the prefix size from 16 to 20 and the host addresses aren't nearly so obvious.

192.168.0.0/20
11000000 10101000 0000 0000 00000000
11111111 11111111 1111 0000 00000000
                      ^

With a prefix size of 20, we clearly have a much smaller network. In fact it's 1/16th the size of 192.168.0.0/16. The host addresses range from 192.168.0.1 through 192.168.15.254.

11000000 10101000 0000 0000 00000001
11000000 10100100 0000 1111 11111110
                      ^

Fortunately, there are calculators on the web that we can use to help with these conversions and answer questions such as "How big is a network with a prefix size of 26?" or "What is the range of IP addresses for a network with a CIDR of 10.12.192.0/19?". Would you have anticipated 8,192 of them? Some will even provide a list of the available IP addresses. Check out http://magic-cookie.co.uk/cgi-bin/iplist-cgi.pl.

You can also find or build scripts to calculate address ranges for you. The Perl script below will print the network prefaces for a given CIDR, though you have to be sure that what you provide is a valid CIDR. In other words, the address you provide should be the initial address in the block -- like 192.168.0.0/16. It will then print a list of IP address prefaces. Any IP address that start with any of the prefaces in the list are part of the 192.168.0.0/20 network.

showRange.pl 192.168.0.0/20
192.168.0.
192.168.1.
192.168.2.
192.168.3.
192.168.4.
192.168.5.
192.168.6.
192.168.7.
192.168.8.
192.168.9.
192.168.10.
192.168.11.
192.168.12.
192.168.13.
192.168.14.
192.168.15.

The script uses the "when" construct that represents the new form of switch statement available in some versions of Perl. Depending on the particular prefix size (32 down to 8), it determines how many prefaces are needed and prints the list using a for statement.

It stops at 8 since I don't believe that networks larger than what people often still refer to as a "class A" network are ever used. And, of course, it has little to do when the prefix size is 8, 16.24 or 32.

use feature "switch";

$_=$ARGV[0];

($byte1,$byte2,$byte3,$byte4,$mask)=/(\d+).(\d+).(\d+).(\d+)\/(\d+)/;

for ($mask) {
    when (/^32$/) { print "$byte1.$byte2.$byte3.$byte4\n"; }
    when (/^31$/) { print "$byte1.$byte2.$byte3.$byte4\n";
	for ($add = 1; $add < 2; $add++) {
	    $byte4++;print "$byte1.$byte2.$byte3.$byte4\n";
	    }
	}
    when (/^30$/) { print "$byte1.$byte2.$byte3.$byte4\n";
	for ($add = 1; $add < 4; $add++) {
	    $byte4++;print "$byte1.$byte2.$byte3.$byte4\n";
	    }
	}
    when (/^29$/) { print "$byte1.$byte2.$byte3.$byte4\n";
	for ($add = 1; $add < 8; $add++) {
	    $byte4++;print "$byte1.$byte2.$byte3.$byte4\n";
	    }
	}
    when (/^28$/) { print "$byte1.$byte2.$byte3.$byte4\n";
	for ($add = 1; $add < 16; $add++) {
	    $byte4++;print "$byte1.$byte2.$byte3.$byte4\n";
	    }
	}
    when (/^27$/) { print "$byte1.$byte2.$byte3.$byte4\n";
	for ($add = 1; $add < 32; $add++) {
	    $byte4++;print "$byte1.$byte2.$byte3.$byte4\n";
	    }
	}
    when (/^26$/) { print "$byte1.$byte2.$byte3.$byte4\n";
	for ($add = 1; $add < 64; $add++) {
	    $byte4++;print "$byte1.$byte2.$byte3.$byte4\n";
	    }
	}
    when (/^25$/) { print "$byte1.$byte2.$byte3.$byte4\n";
	for ($add = 1; $add < 128; $add++) {
	    $byte4++;print "$byte1.$byte2.$byte3.$byte4\n";
	    }
	}
    when (/^24$/) { print "$byte1.$byte2.$byte3.\n"; }
    when (/^23$/) { print "$byte1.$byte2.$byte3.\n";
	$byte3=$byte3+1;
	print "$byte1.$byte2.$byte3.\n";
	}
    when (/^22$/) { print "$byte1.$byte2.$byte3.\n";
	for ($add = 1; $add < 4; $add++) {
	    $byte3++;print "$byte1.$byte2.$byte3.\n";
	    }
	}
    when (/^21$/) { print "$byte1.$byte2.$byte3.\n";
	for ($add = 1; $add < 8; $add++) {
	    $byte3++;print "$byte1.$byte2.$byte3.\n";
	    }
	}
    when (/^20$/) { print "$byte1.$byte2.$byte3.\n";
	for ($add = 1; $add < 16; $add++) {
	    $byte3++;print "$byte1.$byte2.$byte3.\n";
	    }
	}
    when (/^19$/) { print "$byte1.$byte2.$byte3.\n";
	for ($add = 1; $add < 32; $add++) {
	    $byte3++;print "$byte1.$byte2.$byte3.\n";
	    }
	}
    when (/^18$/) { print "$byte1.$byte2.$byte3.\n";
	for ($add = 1; $add < 64; $add++) {
	    $byte3++;print "$byte1.$byte2.$byte3.\n";
	    }
	}
    when (/^17$/) { print "$byte1.$byte2.$byte3.\n";
	for ($add = 1; $add < 128; $add++) {
	    $byte3++;print "$byte1.$byte2.$byte3.\n";
	    }
	}
    when (/^16$/) { print "$byte1.$byte2.\n"; }
    when (/^15$/) { print "$byte1.$byte2.\n";
	$byte2=$byte2+1;print "$byte1.$byte2.\n";
	}
    when (/^14$/) { print "$byte1.$byte2.\n";
	for ($add = 1; $add < 4; $add++) {
	    $byte2++;print "$byte1.$byte2.\n";
	    }
	}
    when (/^13$/) { print "$byte1.$byte2.\n";
	for ($add = 1; $add < 8; $add++) {
	    $byte2++;print "$byte1.$byte2.\n";
	    }
	}
    when (/^12$/) { print "$byte1.$byte2.\n";
	for ($add = 1; $add < 16; $add++) {
	    $byte2++;print "$byte1.$byte2.\n";
	    }
	}
    when (/^11$/) { print "$byte1.$byte2.\n";
	for ($add = 1; $add < 32; $add++) {
	    $byte2++;print "$byte1.$byte2.\n";
	    }
	}
    when (/^10$/) { print "$byte1.$byte2.\n";
	for ($add = 1; $add < 64; $add++) {
	    $byte2++;print "$byte1.$byte2.\n";
	    }
	}
    when (/^9$/) { print "$byte1.$byte2.\n";
	for ($add = 1; $add < 128; $add++) {
	    $byte2++;print "$byte1.$byte2.\n";
	    }
	}
    when (/^8$/)  { print "$byte1.\n"; }
}

There are undoubtedly more concise ways to do the same thing, but the process that I followed is pretty clear in this script and it works as long as the argument you provide is a valid CIDR.

Join us:
Facebook

Twitter

Pinterest

Tumblr

LinkedIn

Google+

Answers - Powered by ITworld

Join us:
Facebook

Twitter

Pinterest

Tumblr

LinkedIn

Google+

Ask a Question
randomness