.\" Automatically generated by Pod::Man 4.10 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "CIDR 3pm" .TH CIDR 3pm "2019-02-28" "perl v5.28.1" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" Net::CIDR \- Manipulate IPv4/IPv6 netblocks in CIDR notation .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 1 \& use Net::CIDR; \& \& use Net::CIDR \*(Aq:all\*(Aq; \& \& print join("\en", \& Net::CIDR::range2cidr("192.168.0.0\-192.168.255.255", \& "10.0.0.0\-10.3.255.255")) \& . "\en"; \& # \& # Output from above: \& # \& # 192.168.0.0/16 \& # 10.0.0.0/14 \& \& print join("\en", \& Net::CIDR::range2cidr( \& "dead:beef::\-dead:beef:ffff:ffff:ffff:ffff:ffff:ffff")) \& . "\en"; \& \& # \& # Output from above: \& # \& # dead:beef::/32 \& \& print join("\en", \& Net::CIDR::range2cidr("192.168.1.0\-192.168.2.255")) \& . "\en"; \& # \& # Output from above: \& # \& # 192.168.1.0/24 \& # 192.168.2.0/24 \& \& print join("\en", Net::CIDR::cidr2range("192.168.0.0/16")) . "\en"; \& # \& # Output from above: \& # \& # 192.168.0.0\-192.168.255.255 \& \& print join("\en", Net::CIDR::cidr2range("dead::beef::/46")) . "\en"; \& # \& # 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("\en", @list) . "\en"; \& # \& # Output from above: \& # \& # 192.168.0.0/23 \& \& print join("\en", Net::CIDR::cidr2octets("192.168.0.0/22")) . "\en"; \& # \& # Output from above: \& # \& # 192.168.0 \& # 192.168.1 \& # 192.168.2 \& # 192.168.3 \& \& print join("\en", Net::CIDR::cidr2octets("dead::beef::/46")) . "\en"; \& # \& # 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("\en", @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")."\en"; \& # \& # Output from above: \& # \& # 195.149.50.56/29 .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" The Net::CIDR package contains functions that manipulate lists of \s-1IP\s0 netblocks expressed in \s-1CIDR\s0 notation. The Net::CIDR functions handle both IPv4 and IPv6 addresses. .ie n .SS "@cidr_list=Net::CIDR::range2cidr(@range_list);" .el .SS "\f(CW@cidr_list\fP=Net::CIDR::range2cidr(@range_list);" .IX Subsection "@cidr_list=Net::CIDR::range2cidr(@range_list);" Each element in the \f(CW@range_list\fR is a string \*(L"start-finish\*(R", where \&\*(L"start\*(R" is the first \s-1IP\s0 address and \*(L"finish\*(R" is the last \s-1IP\s0 address. \&\fBrange2cidr()\fR converts each range into an equivalent \s-1CIDR\s0 netblock. It returns a list of netblocks except in the case where it is given only one parameter and is called in scalar context. .PP For example: .PP .Vb 1 \& @a=Net::CIDR::range2cidr("192.168.0.0\-192.168.255.255"); .Ve .PP The result is a one-element array, with \f(CW$a\fR[0] being \*(L"192.168.0.0/16\*(R". \&\fBrange2cidr()\fR processes each \*(L"start-finish\*(R" element in \f(CW@range_list\fR separately. But if invoked like so: .PP .Vb 1 \& $a=Net::CIDR::range2cidr("192.168.0.0\-192.168.255.255"); .Ve .PP The result is a scalar \*(L"192.168.0.0/16\*(R". .PP Where each element cannot be expressed as a single \s-1CIDR\s0 netblock \&\fBrange2cidr()\fR will generate as many \s-1CIDR\s0 netblocks as are necessary to cover the full range of \s-1IP\s0 addresses. Example: .PP .Vb 1 \& @a=Net::CIDR::range2cidr("192.168.1.0\-192.168.2.255"); .Ve .PP The result is a two element array: (\*(L"192.168.1.0/24\*(R",\*(L"192.168.2.0/24\*(R"); .PP .Vb 2 \& @a=Net::CIDR::range2cidr( \& "d08c:43::\-d08c:43:ffff:ffff:ffff:ffff:ffff:ffff"); .Ve .PP The result is an one element array: (\*(L"d08c:43::/32\*(R") that reflects this IPv6 netblock in \s-1CIDR\s0 notation. .PP \&\fBrange2cidr()\fR does not merge adjacent or overlapping netblocks in \&\f(CW@range_list\fR. .ie n .SS "@range_list=Net::CIDR::cidr2range(@cidr_list);" .el .SS "\f(CW@range_list\fP=Net::CIDR::cidr2range(@cidr_list);" .IX Subsection "@range_list=Net::CIDR::cidr2range(@cidr_list);" The \fBcidr2range()\fR functions converts a netblock list in \s-1CIDR\s0 notation to a list of \*(L"start-finish\*(R" \s-1IP\s0 address ranges: .PP .Vb 1 \& @a=Net::CIDR::cidr2range("10.0.0.0/14", "192.168.0.0/24"); .Ve .PP The result is a two-element array: (\*(L"10.0.0.0\-10.3.255.255\*(R", \*(L"192.168.0.0\-192.168.0.255\*(R"). .PP .Vb 1 \& @a=Net::CIDR::cidr2range("d08c:43::/32"); .Ve .PP The result is a one-element array: (\*(L"d08c:43::\-d08c:43:ffff:ffff:ffff:ffff:ffff:ffff\*(R"). .PP \&\fBcidr2range()\fR does not merge adjacent or overlapping netblocks in \&\f(CW@cidr_list\fR. .ie n .SS "@netblock_list = Net::CIDR::addr2cidr($address);" .el .SS "\f(CW@netblock_list\fP = Net::CIDR::addr2cidr($address);" .IX Subsection "@netblock_list = Net::CIDR::addr2cidr($address);" The addr2cidr function takes an \s-1IP\s0 address and returns a list of all the \s-1CIDR\s0 netblocks it might belong to: .PP .Vb 1 \& @a=Net::CIDR::addr2cidr(\*(Aq192.168.0.31\*(Aq); .Ve .PP 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. .PP 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. .ie n .SS "$cidr=Net::CIDR::addrandmask2cidr($address, $netmask);" .el .SS "\f(CW$cidr\fP=Net::CIDR::addrandmask2cidr($address, \f(CW$netmask\fP);" .IX Subsection "$cidr=Net::CIDR::addrandmask2cidr($address, $netmask);" The addrandmask2cidr function takes an \s-1IP\s0 address and a netmask, and returns the \s-1CIDR\s0 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. .PP 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 \s-1CIDR\s0 range. .ie n .SS "@octet_list=Net::CIDR::cidr2octets(@cidr_list);" .el .SS "\f(CW@octet_list\fP=Net::CIDR::cidr2octets(@cidr_list);" .IX Subsection "@octet_list=Net::CIDR::cidr2octets(@cidr_list);" \&\fBcidr2octets()\fR takes \f(CW@cidr_list\fR and returns a list of leading octets representing those netblocks. Example: .PP .Vb 1 \& @octet_list=Net::CIDR::cidr2octets("10.0.0.0/14", "192.168.0.0/24"); .Ve .PP The result is the following five-element array: (\*(L"10.0\*(R", \*(L"10.1\*(R", \*(L"10.2\*(R", \*(L"10.3\*(R", \*(L"192.168.0\*(R"). .PP For IPv6 addresses, the hexadecimal words in the resulting list are zero-padded: .PP .Vb 1 \& @octet_list=Net::CIDR::cidr2octets("::dead:beef:0:0/110"); .Ve .PP The result is a four-element array: (\*(L"0000:0000:0000:0000:dead:beef:0000\*(R", \&\*(L"0000:0000:0000:0000:dead:beef:0001\*(R", \&\*(L"0000:0000:0000:0000:dead:beef:0002\*(R", \&\*(L"0000:0000:0000:0000:dead:beef:0003\*(R"). Prefixes of IPv6 \s-1CIDR\s0 blocks should be even multiples of 16 bits, otherwise they can potentially expand out to a 32,768\-element array, each! .ie n .SS "@cidr_list=Net::CIDR::cidradd($block, @cidr_list);" .el .SS "\f(CW@cidr_list\fP=Net::CIDR::cidradd($block, \f(CW@cidr_list\fP);" .IX Subsection "@cidr_list=Net::CIDR::cidradd($block, @cidr_list);" The \fBcidradd()\fR functions allows a \s-1CIDR\s0 list to be built one \s-1CIDR\s0 netblock at a time, merging adjacent and overlapping ranges. \&\f(CW$block\fR is a single netblock, expressed as either \*(L"start-finish\*(R", or \&\*(L"address/prefix\*(R". Example: .PP .Vb 3 \& @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); .Ve .PP The result is a two-element array: (\*(L"10.0.0.0/8\*(R", \*(L"192.168.0.0/23\*(R"). IPv6 addresses are handled in an analogous fashion. .ie n .SS "$found=Net::CIDR::cidrlookup($ip, @cidr_list);" .el .SS "\f(CW$found\fP=Net::CIDR::cidrlookup($ip, \f(CW@cidr_list\fP);" .IX Subsection "$found=Net::CIDR::cidrlookup($ip, @cidr_list);" Search for \f(CW$ip\fR in \f(CW@cidr_list\fR. \f(CW$ip\fR can be a single \s-1IP\s0 address, or a netblock in \s-1CIDR\s0 or start-finish notation. \&\fBlookup()\fR returns 1 if \f(CW$ip\fR overlaps any netblock in \f(CW@cidr_list\fR, 0 if not. .ie n .SS "$ip=Net::CIDR::cidrvalidate($ip);" .el .SS "\f(CW$ip\fP=Net::CIDR::cidrvalidate($ip);" .IX Subsection "$ip=Net::CIDR::cidrvalidate($ip);" Validate whether \f(CW$ip\fR is a valid IPv4 or IPv6 address, or a \s-1CIDR.\s0 Returns its argument or undef. Spaces are removed, and IPv6 hexadecimal address are converted to lowercase. .PP \&\f(CW$ip\fR with less than four octets gets filled out with additional octets, and the modified value gets returned. This turns \*(L"192.168/16\*(R" into a proper \&\*(L"192.168.0.0/16\*(R". .PP If \f(CW$ip\fR contains a \*(L"/\*(R", it must be a valid \s-1CIDR,\s0 otherwise it must be a valid IPv4 or an IPv6 address. .PP A technically invalid \s-1CIDR,\s0 such as \*(L"192.168.0.1/24\*(R" fails validation, returning undef. .SH "BUGS" .IX Header "BUGS" Garbage in, garbage out. Always use \fBcidrvalidate()\fR before doing anything with untrusted input. Otherwise, \&\*(L"slightly\*(R" invalid input will work (extraneous whitespace is generally \s-1OK\s0), but the functions will croak if you're totally off the wall. .SH "AUTHOR" .IX Header "AUTHOR" Sam Varshavchik .PP With some contributions from David Cantrell