Help language development. Donate to The Perl Foundation

IP::Addr zef:vrurg last updated on 2021-10-06

README.md
NAME
====

IP::Addr - dealing with IPv4/IPv6 addresses

SYNOPSIS
========

    my $cidr = IP::Addr.new( "192.0.2.2/27" );
    say $cidr;
    say $cidr.ip;
    for $cidr.network.each -> $ip {
        say $ip;
    }

    my $cidr6 = IP::Addr.new( "600d::f00d/123" );
    say $cidr6;
    say $cidr6.ip;

DESCRIPTION
===========

This module provides functionality for working with IPv4/IPv6 addresses.

Module Structure
----------------

The main interface is provided by `IP::Addr` class. Typical class usage is demonstrated in the synopsis. The class is a frontend which tries to determine version of the IP address provided to its constructor and creates corresponding handler object available on its `handler` attribute. All methods of the handler are available on `IP::Addr` object via `handles` trait.

For example, for an arbitrary `$ip` object of the class `$ip.ip` call is actually same as calling `$ip.handler.ip`. Method `ip` is a universal one for both v4 and v6 addresses and therefore it is not necessary to care about what exact kind of object we're currently dealing with.

Similarly, version-dependant methods are available too but only when corresponding handler is active. For example:

    say IP::Addr.new( "192.0.2.2/27" ).broadcast;

is a valid call; while

    say IP::Addr.new( "2001:db8::/123" ).broadcast;

will fail with "no method" exception because `broadcast` is not available for IPv6 addresses.

Glossary
--------

### Address forms

Besides of its version each IP address has one more characteristic property: form. It is defined by handler's attribute of the same name: `$!form`. Particular forms are defined by IP-FORM enum defined in `IP::Addr::Common`:

  * *ip*

    Simple IP address. It has prefix length of 32 bits.

  * *cidr*

    CIDR form is an IP address with *prefix* defined.

        192.0.2.3/24
        192.0.2.3/255.255.255.0
        2001::/64

  * *range*

    A range is defined by its first and last IP addresses. It has a *selected* or *current* IP address which could be changed as a result of increase, decrease, or iteration operations.

        192.0.2.3-192.0.2.20
        2001::1-2001::1f

### Ranged forms

CIDR and ranges are *ranged forms* contrary to a single IP form.

### N-tets

As it is known IP addresses are represented by groups of integers called *octets* for IPv4 and *hextets* for IPv6 (there are variations but I chose these two). *N-tets* is used as a general term for both of them.

METHODS
=======

Constructor
-----------

Class constructor `new` simply re-dispatches its arguments to method `set`.

`set`
-----

  * *`$ip.set( Str $source )` / `$ip.set( Str :$source! )`*

    `$source` is a string representation of an IP address in either plain or CIDR notation, or of a range of addresses in a form *<first ip>-<last ip> *. Formats of valid IP addresses are defined by corresponding standards.

  * *`$ip.set( [ :v4 | :v6 ], *%params )`*

    Creates a handler object of specified version of handler (v4 or v6) by passing named `%params` to its constructor.

    For example, the following statement:

        $ip.set( :v4, ip => 3221225986, prefix-len => 24 )

    Is equivalent to:

        $ip.set( "192.0.2.2/24" )

    **Note:** all calls to `set` method result in creation of a new handler object. I.e.:

        my $old-handler = $ip.handler;
        $ip.set( ... );
        say $old-handler === $ip.handler; # False

`Str`
-----

Returns a valid string representation of the IP object.

`gist`
------

Same as `Str` method.

`each`
------

Returns an iterable object which would iterate over IPs contained in the current object starting with selected address (i.e. the one returned by `ip` method). For ranged forms to iterate over the whole range (from the `first-ip` to the `last-ip`) use of method `first` is recommended:

    .say for IP::Addr.new( "192.0.2.3/29" ).each;           # 192.0.2.3/29\n192.0.2.4/29 ...
    .say for IP::Addr.new( "192.0.2.3/29" ).first.each;     # 192.0.2.0/29\n192.0.2.1/29 ...

`Supply`
--------

Similar to `each` method but returns a `Supply` object. Same rule about starting with selected IP and use of `first` method apply.

HANDLER METHODS
===============

This sections documents methods common for both IPv4 and IPv6 handlers.

Notes
-----

For all methods accepting another IP address as a parameter and where descriptions has a statement *"same version only"* `X::IPAddr::TypeCheck` exception would be thrown if object with a handler of different version is passed.

Address method parameters could either be `IP::Addr` instances or string representations.

`set`
-----

Configures the handler object.

**Note** In the following subsection `$handler` notation is used instead of `$ip.handler`.

  * *`$handler.set( Str:D $source )` / `$handler.set( Str:D :$source! )`*

    Handler attributes are set by parsing a string representation of IP address.

  * *`$handler.set( Int:D :$ip!, Int:D :$!prefix-len )`*

    Configures handler object as *CIDR* form.

  * *`$handler.set( Int:D :$first!, Int:D :$!last, Int :$ip? )`*

    Configures handler object as *range* form. Optional named parameter `:ip` defines selected IP within range (i.e. the one returned by `ip` method). If omitted then range initial selected IP is its first address.

  * *`$handler.set( Int:D $ip! )`*

    Configures a single IP form.

  * *`$handler.set( Int:D @n-tets )`*

    Configures a single IP form from integer octets for IPv4 or hextets for IPv6. Number of elements in @n-tets array is defined by the method of the same name.

`addr-len`
----------

Returns number of bits for correponding IP version (*32* for IPv4 and *128* for IPv6).

`n-tets`
--------

Returns number of n-tets for the current handler (*4* for IPv4 and *8* for IPv6).

`version`
---------

Returns integer version of the IP object (*4* or *6*).

`ip-classes`
------------

Returns list of hashes of reserved IP ranges. Each hash has two keys:

  * *net*

    `IP::Addr` object representing the reserved range.

  * *info*

    Another hash containing information about the scope of the reserved range in very brief text description:

      * *scope*

        Scopes are defined by `SCOPE` enum in `IP::Addr::Common`. Currently the following scopes are known:

          * `undetermined`

          * `mpublic`

          * `software`

          * `private`

          * `host`

          * `subnet`

          * `documentation`

          * `internet`

          * `routing`

          * `link`

        Of those `routing` and `link` are specific to IPv6. `public` isn't officially recognized but used to represent anything not been reserved. `undetermined` is also a special value to be returned if a requested range of IP addresses overlaps with a reserved range but isn't fully contained in it.

      * *description*

        Textual information about the reserved block.

`ip`
----

Returns a new `IP::Addr` object representing just an IP address of the current `IP::Addr` object.

    my $ip = IP::Addr.new( "192.0.2.3/24" );
    say $ip;            # 192.0.2.3/24
    say $ip.ip;         # 192.0.2.3
    say $ip.ip.WHO;     # IP::Addr

`prefix`
--------

Returns CIDR representation of any form of IP address. For example:

    say IP::Addr.new( "192.0.2.3" ).prefix;     # 192.0.2.3/32

For IPv4 handler additional named parameter `:mask` can be used to use network mask instead of prefix length:

    say IP::Addr.new( "192.0.2.3/24" ).prefix( :mask );     # 192.0.2.3/255.255.255.0

`first-ip`
----------

`IP::Addr` object for the first IP address in the range. For single IP will be the same as `ip` method.

`last-ip`
---------

`IP::Addr` object for the last IP address in the range. For single IP will be the same as `ip` method.

`network`
---------

`IP::Addr` object of the network current IP object belongs to. Makes real sense for CIDR form only though can be used with any other form too.

`mask`
------

Returns string representation of the current IP object mask. Doesn't make much sense for IPv6 addresses because there is officially no such thing for them.

`wildcard`
----------

Returns string representation of current IP object wildcard. Doesn't make much sense for IPv6 addresses because there is officially no such thing for them.

`size`
------

Returns number of IP addresses contained in the current `IP::Addr` object. For example:

    say IP::Addr.new( "192.0.0.0/13" ).size;            # 524288
    say IP::Addr.new( "192.0.2.3-192.0.2.23" );         # 21

`int-ip`
--------

Returns integer value of the IP address.

`int-first-ip`
--------------

Returns integer value of the first IP address in the current IP object.

`int-last-ip`
-------------

Returns integer value of the last IP address in the current IP object.

`int-mask`
----------

Returns integer value of current IP object mask. Though not defined as such for IPv6 addresses but could be useful in some address arithmetics.

`int-wildcard`
--------------

Returns integer value of current IP object wildcard. Though not defined as such for IPv6 addresses but could be useful in some address arithmetics.

`inc` / `succ`
--------------

Increments IP address by *1*. For ranged IP forms result of this operation won't be greater than the last IP of the range:

    my $ip = IP::Addr.new( "192.0.2.255/24" );
    say $ip.inc;                # 192.0.2.255/24

Rerturns current `IP::Addr` object.

`dec` / `pred`
--------------

Decrements IP address by *1*. For ranged IP forms result of this operation won't be less than the first IP of the range:

    my $ip = IP::Addr.new( "192.0.2.0/24" );
    say $ip.dec;                # 192.0.2.0/24

Returns current `IP::Addr` object.

`add( Int:D $count )`
---------------------

Shifts IP of the current object by `$count` positions higher. `$count` could be negative. For ranged IP form the result of the operation won't leave the range boundaries:

    my $ip = IP::Addr.new( "192.0.2.255/24" );
    say $ip.add( 10 );              # 192.0.2.255/24
    say $ip.add( -300 );            # 192.0.2.0/24

Returns current `IP::Addr` object.

`eq( $addr )`
-------------

Same version only.

Returns *True* if current object is equal to `$addr`.

`lt( $addr )`
-------------

Same version only.

Returns true if the current object is less than `$addr`.

`gt( $addr )`
-------------

Same version only.

Returns true if the current object is greater than `$addr`

`cmp( $addr )`
--------------

Same version only.

Returns one of three `Order` values: `Less`, `Same`, or `More`. For ranges comparison is performed by the selected IP.

`contains( $addr )`
-------------------

Same version only.

Returns *True* if current object contains `$addr`. Wether the `$addr` object is the same (e.g. `eq` method would return *True*) then it is also considered as contained.

`overlaps( $addr )`
-------------------

Same version only.

Returns *True* if the current object and `$addr` have at least one common IP address. Useful for starting iteration:

`first`
-------

Returns a new `IP::Addr` object whose IP address is the first IP of the current object. Useful for starting iteration.

    my $ip = IP::Addr.new( "192.0.2.12/24" );
    for $ip.first.each { say $_ }       # 192.0.2.0\n192.0.2.1\n...

See `each` method.

`next`
------

Returns a new `IP::Addr` object successive to the current object or *Nil* if current is the last IP of the range.

`prev`
------

Returns a new `IP::Addr` object preceding the current object or *Nil* if current is the first IP of the range.

`next-network`
--------------

Returns a new `IP::Addr` object containing network successive to the network of the current object. Valid for CIDR form only. Flips over begining/end of IP range:

    $ip = IP::Addr.new( "255.255.255.13/24" );
    say $ip.next-network;               # 0.0.0.0/24

`prev-network`
--------------

Returns a new `IP::Addr` object containing network preceding the network of the current object. Valid for CIDR form only. Flips over the begining of IP range:

    $ip = IP::Addr.new( "0.0.0.2/24" );
    say $ip.prev-network;               # 255.255.255.0/24

`next-range`
------------

Returns a new `IP::Addr` object containing range of the same length successive to the current object. Valid for range form only.

`prev-range`
------------

Returns a new `IP::Addr` object containing range of the same length preceding the current object. Valid for range form only.

`to-int( @n-tets )`
-------------------

Converts an array of n-tets to integer value corresponding to the current handler's IP version.

`to-n-tets( Int:D $addr )`
--------------------------

Splits integer represnation of IP address into n-tets corresponding to the current handler's IP version.

`info`
------

Returns a hash with information about the current IP object. The hash contains two keys: `scope` and `description`. See `ip-classes` method for more information.

`Str`
-----

Stringifies current IP object with regard to its form.

OPERATORS
=========

For all supported operators where both operands are addresses at least one of them has to be a `IP::Addr` object. The other one could be a string representation of an address.

`prefix/postfix ++` and `prefix/postfix --`
-------------------------------------------

Standard Raku operatios working by calling `succ`/`pred` methods on `IP::Addr` object.

`infix + ( $addr, $int )`
-------------------------

Adds an integer value to the address. The resulting address will never get out of network/range boundaries for objects of corresponding forms. A new `IP::Addr` object is returned.

    $ip2 = $ip1 + 3;

`infix - ( $addr, $int )`
-------------------------

Deducts an integer value from the address. The resulting address will never get out of network/range boundaries for objects of corresponding forms. A new `IP::Addr` object is returned.

    $ip2 = $ip1 - 3;

`infix cmp ( $addr1, $addr2 )`
------------------------------

Compares two IP addresses.

    given $ip1 cmp $ip2 {
        when Less { say "smaller" }
        when Same { say "same" }
        when More { say "bigger" }
    }

`infix eqv / infix == ( $addr1, $addr2 )`
-----------------------------------------

Checks if two addresses are equal. See handler's `eq` method.

`infix < ( $addr1, $addr2 )`
----------------------------

*True* if `$addr1` is less than `$addr2`. See handler's `lt` method.

`infix <= / infix ≤ ( $addr1, $addr2 )`
---------------------------------------

*True* if `$addr1` is less than or equal to `$addr2`.

`infix > ( $addr1, $addr2 )`
----------------------------

*True* if `$addr1` is greater than `$addr2`. See handler's `gt` method.

`infix >= / infix ≥ ( $addr1, $addr2 )`
---------------------------------------

*True* `$addr1` greater than or equal to `$addr2`.

`infix (cont) / infix ⊇ ( $addr1, $addr2 )`
-------------------------------------------

*True* if `$addr1` object contains `$addr2`. See handler's `contains` method.

`infix ⊆ ( $addr1, $addr2 )`
----------------------------

*True* if `$addr1` is contained by `$addr2`.

CAVEATS
=======

The author doesn't use IPv6 in his setups. All the functionality provided here is developed using information from corresponding Wikipedia pages. Therefore, "here be dragons"©. Please, report back any issue encountered!

SEE ALSO
========

[ChangeLog](ChangeLog.md)

AUTHOR
======

Vadim Belman <[email protected]>