---
name: "Net::CIDR"
section: "3pm"
platform: "linux"
package: "libnet-cidr-perl"
version: "0.25-1"
release: "Debian 13 (Trixie)"
platforms: ["linux"]
url: "https://new.carta.tech/Net::CIDR"
---

# Net::CIDR(3pm)

Manipulate IPv4/IPv6 netblocks in CIDR notation

## NAME

Net::CIDR - Manipulate IPv4/IPv6 netblocks in CIDR notation

## SYNOPSIS

```
    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
```

## DESCRIPTION

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.

### @cidr\_list=Net::CIDR::range2cidr(@range\_list);

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`.

### @range\_list=Net::CIDR::cidr2range(@cidr\_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`.

### @netblock\_list = Net::CIDR::addr2cidr($address);

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.

### $cidr=Net::CIDR::addrandmask2cidr($address, $netmask);

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.

### @octet\_list=Net::CIDR::cidr2octets(@cidr\_list);

**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!

### @cidr\_list=Net::CIDR::cidradd($block, @cidr\_list);

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.

### $found=Net::CIDR::cidrlookup($ip, @cidr\_list);

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.

### $ip=Net::CIDR::cidrvalidate($ip);

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.

## BUGS

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.

## AUTHOR

Sam Varshavchik \<sam@email-scan.com\>

With some contributions from David Cantrell \<david@cantrell.org.uk\> and brian d foy \<briandfoy@pobox.com\>.
