I’m needing to work out the IPv6 reverse zone for a given IPv6 CIDR prefix, that is a prefix with number of bits in the network on the end after a forward slash. e.g.:
2001:ba8:1f1:f004::/64 → 4.0.0.f.1.f.1.0.8.a.b.0.1.0.0.2.ip6.arpa
4:2::/32 → 2.0.0.0.4.0.0.0.ip6.arpa
2001:ba8:1f1:400::/56 → 0.0.4.0.1.f.1.0.8.a.b.0.1.0.0.2.ip6.arpa
I had a quick look for a module that does it, but couldn’t find one, so I hacked this subroutine together:
#!/usr/bin/perl
use strict;
use warnings;
foreach my $cidr (qw(2001:ba8:1f1:f004::/64 4:2::/32 2001:ba8:1f1:400::/56)) {
print "$cidr reverses to ", v6_cidr_to_reverse($cidr), "\n";
}
sub v6_cidr_to_reverse
{
my ($cidr) = @_;
my ($prefix, $net_bits) = split(/\//, $cidr);
# Bust the v6 address apart into the colon-delimited sections.
my @sections = split(/:/, $prefix);
my @v6_array;
# Iterate through them backwards.
foreach my $section (reverse @sections) {
# Split each section up backwards into a list, so e.g. '2001' becomes
# ('1', '0', '0', '2').
my @digits = reverse(split(//, $section));
my $str = $digits[0];
my $i;
# Join the digits we have together with dots.
for ($i = 1; $i <= $#digits; $i++) {
$str .= ('.' . $digits[$i]);
}
# If there were fewer than four digits then make the remainder up with
# zeroes.
while ($i < 4) {
$str .= '.' . '0';
$i++;
}
push(@v6_array, $str);
}
# Join the sections together with dots and add the reverse zone TLD on the
# end.
my $revstr = join('.', @v6_array) . '.ip6.arpa';
my $have_bits = 16 * scalar @v6_array;
if ($have_bits > $net_bits) {
# They gave us a prefix like 2001:ba8:1f1:400::/56 which specifies 64
# bits, not 56, so we have to chop off the first few nibbles.
# Multiply by 2 because we need to remove the dots as well!
my $remainder = ($have_bits - $net_bits) / 4 * 2;
$revstr =~ s/^[0-9a-f\.]{$remainder}//;
} elsif ($net_bits > $have_bits) {
# They didn't specify a big enough prefix, e.g.
# 2001:ba8:1f1::/56 which specifies 48 bits, not 56, so
# we have to front pad with zeroes.
my $remainder = ($net_bits - $have_bits) / 4;
$revstr = '0.' x $remainder . $revstr;
}
return $revstr;
}
Is there a more elegant way? Is there a module I can replace this with?
Must support:
Arbitrary prefix length
Use of ‘::’ anywhere legal in the address
$ perl -MNet::IP -e’print Net::IP->new($ARGV[0])->reverse_ip’ 2001:ba8:1f1:f004::/64
4.0.0.f.1.f.1.0.8.a.b.0.1.0.0.2.ip6.arpa.
Hah, brilliant, even works for ones like 2001:ba8:1f1:400::/56. Don’t know how I missed that!