Net::CIDR - Manipulate IPv4/IPv6 netblocks in CIDR notation
use Net::CIDR;
use Net::CIDR ':all';
my $var;
if ($var = Net::CIDR::cidrvalidate($var))
{
// ... do something
}
print join("\n",
Net::CIDR::range2cidr("192.168.0.0-192.168.255.255",
"10.0.0.0-10.3.255.255"))
. "\n";
#
# Output from above:
#
# 192.168.0.0/16
# 10.0.0.0/14
print join("\n",
Net::CIDR::range2cidr(
"dead:beef::-dead:beef:ffff:ffff:ffff:ffff:ffff:ffff"))
. "\n";
#
# Output from above:
#
# dead:beef::/32
print join("\n",
Net::CIDR::range2cidr("192.168.1.0-192.168.2.255"))
. "\n";
#
# Output from above:
#
# 192.168.1.0/24
# 192.168.2.0/24
print join("\n", Net::CIDR::cidr2range("192.168.0.0/16")) . "\n";
#
# Output from above:
#
# 192.168.0.0-192.168.255.255
print join("\n", Net::CIDR::cidr2range("dead::beef::/46")) . "\n";
#
# Output from above:
#
# dead:beef::-dead:beef:3:ffff:ffff:ffff:ffff:ffff
@list=("192.168.0.0/24");
@list=Net::CIDR::cidradd("192.168.1.0-192.168.1.255", @list);
print join("\n", @list) . "\n";
#
# Output from above:
#
# 192.168.0.0/23
print join("\n", Net::CIDR::cidr2octets("192.168.0.0/22")) . "\n";
#
# Output from above:
#
# 192.168.0
# 192.168.1
# 192.168.2
# 192.168.3
print join("\n", Net::CIDR::cidr2octets("dead::beef::/46")) . "\n";
#
# Output from above:
#
# dead:beef:0000
# dead:beef:0001
# dead:beef:0002
# dead:beef:0003
@list=("192.168.0.0/24");
print Net::CIDR::cidrlookup("192.168.0.12", @list);
#
# Output from above:
#
# 1
@list = Net::CIDR::addr2cidr("192.168.0.31");
print join("\n", @list);
#
# Output from above:
#
# 192.168.0.31/32
# 192.168.0.30/31
# 192.168.0.28/30
# 192.168.0.24/29
# 192.168.0.16/28
# 192.168.0.0/27
# 192.168.0.0/26
# 192.168.0.0/25
# 192.168.0.0/24
# 192.168.0.0/23
# [and so on]
print Net::CIDR::addrandmask2cidr("195.149.50.61", "255.255.255.248")."\n";
#
# Output from above:
#
# 195.149.50.56/29
The Net::CIDR package contains functions that manipulate lists of IP
netblocks expressed in CIDR notation.
The Net::CIDR functions handle both IPv4 and IPv6 addresses.
The cidrvalidate() function, described below, checks that its argument
is a single, valid IP address or a CIDR. The remaining functions
expect that
their parameters consist of validated IPs or CIDRs. See cidrvalidate()
and BUGS, below, for more information.
Each element in the @range_list is a string ``start-finish'', where
``start'' is the first IP address and ``finish'' is the last IP address.
range2cidr() converts each range into an equivalent CIDR netblock.
It returns a list of netblocks except in the case where it is given
only one parameter and is called in scalar context.
For example:
@a=Net::CIDR::range2cidr("192.168.0.0-192.168.255.255");
The result is a one-element array, with $a[0] being ``192.168.0.0/16''.
range2cidr() processes each ``start-finish'' element in @range_list separately.
But if invoked like so:
$a=Net::CIDR::range2cidr("192.168.0.0-192.168.255.255");
The result is a scalar ``192.168.0.0/16''.
Where each element cannot be expressed as a single CIDR netblock
range2cidr() will generate as many CIDR netblocks as are necessary to cover
the full range of IP addresses. Example:
@a=Net::CIDR::range2cidr("192.168.1.0-192.168.2.255");
The result is a two element array: (``192.168.1.0/24'',``192.168.2.0/24'');
@a=Net::CIDR::range2cidr(
"d08c:43::-d08c:43:ffff:ffff:ffff:ffff:ffff:ffff");
The result is an one element array: (``d08c:43::/32'') that reflects this
IPv6 netblock in CIDR notation.
range2cidr() does not merge adjacent or overlapping netblocks in
@range_list.
The cidr2range() functions converts a netblock list in CIDR notation
to a list of ``start-finish'' IP address ranges:
@a=Net::CIDR::cidr2range("10.0.0.0/14", "192.168.0.0/24");
The result is a two-element array:
(``10.0.0.0-10.3.255.255'', ``192.168.0.0-192.168.0.255'').
@a=Net::CIDR::cidr2range("d08c:43::/32");
The result is a one-element array:
(``d08c:43::-d08c:43:ffff:ffff:ffff:ffff:ffff:ffff'').
cidr2range() does not merge adjacent or overlapping netblocks in
@cidr_list.
The addr2cidr function takes an IP address and returns a list of all
the CIDR netblocks it might belong to:
@a=Net::CIDR::addr2cidr('192.168.0.31');
The result is a thirtythree-element array:
('192.168.0.31/32', '192.168.0.30/31', '192.168.0.28/30', '192.168.0.24/29',
[and so on])
consisting of all the possible subnets containing this address from
0.0.0.0/0 to address/32.
Any addresses supplied to addr2cidr after the first will be ignored.
It works similarly for IPv6 addresses, returning a list of one hundred
and twenty nine elements.
The addrandmask2cidr function takes an IP address and a netmask, and
returns the CIDR range whose size fits the netmask and which contains
the address. It is an error to supply one parameter in IPv4-ish
format and the other in IPv6-ish format, and it is an error to supply
a netmask which does not consist solely of 1 bits followed by 0 bits.
For example, '255.255.248.192' is an invalid netmask, as is
'255.255.255.32' because both contain 0 bits in between 1 bits.
Technically speaking both of those *are* valid netmasks, but a) you'd
have to be insane to use them, and b) there's no corresponding CIDR
range.
cidr2octets() takes @cidr_list and returns a list of leading octets
representing those netblocks. Example:
@octet_list=Net::CIDR::cidr2octets("10.0.0.0/14", "192.168.0.0/24");
The result is the following five-element array:
(``10.0'', ``10.1'', ``10.2'', ``10.3'', ``192.168.0'').
For IPv6 addresses, the hexadecimal words in the resulting list are
zero-padded:
@octet_list=Net::CIDR::cidr2octets("::dead:beef:0:0/110");
The result is a four-element array:
(``0000:0000:0000:0000:dead:beef:0000'',
``0000:0000:0000:0000:dead:beef:0001'',
``0000:0000:0000:0000:dead:beef:0002'',
``0000:0000:0000:0000:dead:beef:0003'').
Prefixes of IPv6 CIDR blocks should be even multiples of 16 bits, otherwise
they can potentially expand out to a 32,768-element array, each!
The cidradd() functions allows a CIDR list to be built one CIDR netblock
at a time, merging adjacent and overlapping ranges.
$block is a single netblock, expressed as either ``start-finish'', or
``address/prefix''.
Example:
@cidr_list=Net::CIDR::range2cidr("192.168.0.0-192.168.0.255");
@cidr_list=Net::CIDR::cidradd("10.0.0.0/8", @cidr_list);
@cidr_list=Net::CIDR::cidradd("192.168.1.0-192.168.1.255", @cidr_list);
The result is a two-element array: (``10.0.0.0/8'', ``192.168.0.0/23'').
IPv6 addresses are handled in an analogous fashion.
Search for $ip in @cidr_list. $ip can be a single IP address, or a
netblock in CIDR or start-finish notation.
lookup() returns 1 if $ip overlaps any netblock in @cidr_list, 0 if not.
Validate whether $ip is a valid IPv4 or IPv6 address, or a CIDR.
Returns its argument or undef.
Spaces are removed, and IPv6 hexadecimal address are converted to lowercase.
$ip with less than four octets gets filled out with additional octets, and
the modified value gets returned. This turns ``192.168/16'' into a proper
``192.168.0.0/16''.
If $ip contains a ``/'', it must be a valid CIDR, otherwise it must be a valid
IPv4 or an IPv6 address.
A technically invalid CIDR, such as ``192.168.0.1/24'' fails validation, returning
undef.
Garbage in, garbage out.
Always use cidrvalidate() before doing anything with untrusted input.
Otherwise,
``slightly'' invalid input will work (extraneous whitespace
is generally OK),
but the functions will croak if you're totally off the wall.
Sam Varshavchik <sam@email-scan.com>
With some contributions from David Cantrell <david@cantrell.org.uk>
|