Canonicalize CIDR
You are encouraged to solve this task according to the task description, using any language you may know.
Implement a function or program that, given a range of IPv4 addresses in CIDR notation (dotted-decimal/network-bits), will return/output the same range in canonical form. That is, the IP address portion of the output CIDR block must not contain any set (1) bits in the host part of the address.
Example: given 87.70.141.1/22, your code should output 87.70.140.0/22.
Perl
<lang perl>#!/usr/bin/env perl use v5.16; use Socket qw(inet_aton inet_ntoa);
- canonicalize a CIDR block: make sure none of the host bits are set
if (!@ARGV) {
chomp(@ARGV = <>);
}
for (@ARGV) {
# dotted-decimal / bits in network part my ($dotted, $size) = split m#/#;
# get IP as binary string my $binary = sprintf "%032b", unpack('N', inet_aton $dotted);
# Replace the host part with all zeroes substr($binary, $size) = 0 x (32 - $size);
# Convert back to dotted-decimal $dotted = inet_ntoa(pack 'B32', $binary);
# And output say "$dotted/$size";
}</lang>
- Output:
$ canonicalize_cidr.pl 87.70.141.1/22 87.70.140.0/22
Python
<lang python>#!/usr/bin/env python
- canonicalize a CIDR block specification:
- make sure none of the host bits are set
import sys from socket import inet_aton, inet_ntoa from struct import pack, unpack
args = sys.argv[1:] if len(args) == 0:
args = sys.stdin.readlines()
for cidr in args:
# IP in dotted-decimal / bits in network part dotted, size_str = cidr.split('/') size = int(size_str)
numeric = unpack('!I', inet_aton(dotted))[0] # IP as an integer binary = f'{numeric:#034b}' # then as a padded binary string prefix = binary[:size + 2] # just the network part # (34 and +2 are to account # for leading '0b')
canon_binary = prefix + '0' * (32 - size) # replace host part with all zeroes canon_numeric = int(canon_binary, 2) # convert back to integer canon_dotted = inet_ntoa(pack('!I', (canon_numeric))) # and then to dotted-decimal print(f'{canon_dotted}/{size}') # output result</lang>
- Output:
$ canonicalize_cidr.py 87.70.141.1/22 87.70.140.0/22
Raku
<lang perl6>#!/usr/bin/env raku
- canonicalize a CIDR block: make sure none of the host bits are set
if (!@*ARGS) {
@*ARGS = $*IN.lines;
}
for @*ARGS -> $cidr {
# dotted-decimal / bits in network part my ($dotted, $size) = $cidr.split('/');
# get IP as binary string my $binary = $dotted.split('.').map({ .fmt("%08b") }).join;
# Replace the host part with all zeroes $binary.substr-rw($size) = 0 x (32 - $size);
# Convert back to dotted-decimal my $canon = $binary.comb.batch(8).map({ .join.parse-base(2) }).join('.');
# And output say "$canon/$size";
}</lang>
- Output:
$ canonicalize_cidr.raku 87.70.141.1/22 87.70.140.0/22
Ruby
<lang ruby>#!/usr/bin/env ruby
- canonicalize a CIDR block: make sure none of the host bits are set
if ARGV.length == 0 then
ARGV = $stdin.readlines.map(&:chomp)
end
ARGV.each do |cidr|
# dotted-decimal / bits in network part dotted, size_str = cidr.split('/') size = size_str.to_i
# get IP as binary string binary = dotted.split('.').map { |o| "%08b" % o }.join
# Replace the host part with all zeroes binary[size .. -1] = '0' * (32 - size)
# Convert back to dotted-decimal canon = binary.chars.each_slice(8).map { |a| a.join.to_i(2) }.join('.')
# And output puts "#{canon}/#{size}";
end</lang>
- Output:
$ canonicalize_cidr.rb 87.70.141.1/22 87.70.140.0/22