=========================================
``inet`` - an IP address mangling library
=========================================
``inet`` is meant to make it fun to do IP address calculations.
::
local inet = require 'inet'
-- get first address of the 3rd /64 in a /56
inet('2001:db8::/56') / 64 * 3 + 1 -- returns inet('2001:db8:0:3::1/64')
-- get last /64 in a /56
inet('2001:db8::/56') * 1 / 64 * -1 -- returns inet('2001:db8:0:ff::/64')
Dependencies
============
- Lua_ version 5.2 or 5.3
- LPeg_ - Parsing Expression Grammars For Lua
API
===
``inet`` module
---------------
======================= =====================================================
API Description
======================= =====================================================
``inet(...)`` Parse address and build ``inet4`` or ``inet6`` table
``inet.is(foo)`` is ``foo`` an ``inet*`` table
``inet.is4(foo)`` is ``foo`` an ``inet4`` table
``inet.is6(foo)`` is ``foo`` an ``inet6`` table
``inet.set4()`` get new empty IPv4 set
``inet.set6()`` get new empty IPv6 set
``inet.mixed_networks`` IPv6 mixed notation set
``inet.version`` API version (currently ``1``)
======================= =====================================================
IPv6 mixed notation configuration
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
``inet.mixed_networks`` can be used to configure which IPv6 networks
should use mixed notation, ie. last 32 bits formatted as IPv4,
as per `RFC 5952`_ section 5.
Initially the set only contains the well-known ``::ffff:0.0.0.0/96`` network.
Common ``inet*`` API
--------------------
================= ======================================
Operator Description
================= ======================================
``+`` Addition
``-`` Subtract
``/`` Change mask (absolute)
``^`` Change mask (relative)
``*`` Move network
``<`` is less than
``<=`` is less than or equal
``==`` equals
``>=`` is greater or equal
``>`` is greater than
``~=`` not equals
``#`` number of network bits
``:contains()`` contains
``:network()`` extract network part of address
``tostring(net)`` convert to network
``:ipstring()`` ip as string without prefix
``:cidrstring()`` format CIDR notation
``:netmask()`` generate netmask as address
``:flip()`` flip the least significant network bit
================= ======================================
Additional ``inet6`` methods
-----------------------------
inet6 has these additional methods:
================ =====================================
Operator Description
================ =====================================
``:ipstring4()`` string formatted in mixed notation
``:ipstring6()`` string formatted in standard notation
================ =====================================
``set`` API
-----------
================== =================================
API Description
================== =================================
``set:list()`` list networks in set
``set:add()`` add network to set
``set:remove()`` remove network from set
``set:contains()`` is network contained in set?
``set:flush()`` empty the set
================== =================================
Creating
--------
There is a multitude of different ways to create ``inet*`` instances.
::
-- IPv4
inet('192.0.2.0') -- returns inet('192.0.2.0/32')
inet('192.0.2.0', 24) -- returns inet('192.0.2.0/24')
inet({192,0,2,0}, 24) -- returns inet('192.0.2.0/24')
inet(3221225985, 32) -- returns inet('192.0.2.1')
-- IPv6
inet('2001:db8::') -- returns inet('2001:db8::/128')
inet('2001:db8::', 56) -- returns inet('2001:db8::/56')
-- its possible to wrap inet instances
inet(inet('192.0.2.0/24')) -- returns inet('192.0.2.0/24')
inet(inet('2001:db8::')) -- returns inet('2001:db8::')
-- when wrapped additional mask takes precedence
inet(inet('192.0.2.0/32'), 24) -- returns inet('192.0.2.0/24')
inet(inet('2001:db8::/128'), 64) -- returns inet('2001:db8::/64')
-- various error examples
inet('192.0.2.0/24', 32) -- returns nil, 'multiple masks supplied'
inet('2001:db8::/64', 56) -- returns nil, 'multiple masks supplied'
inet('foobar') -- returns nil, 'parse error'
inet('foo::bar') -- returns nil, 'parse error'
inet('192.0.2.0', 33) -- returns nil, 'invalid mask'
inet('2001:db8::', 129) -- returns nil, 'invalid mask'
Mangling
--------
All of the ``inet*`` mangling operators and methods returns a new instance, and does
not modify the original instance.
``foo + bar``
~~~~~~~~~~~~~
Addition
::
inet('2001:db8::/64') + 5 -- returns inet('2001:db8::5/64')
inet('::ffff:0.0.0.0/96') + inet('192.0.2.24') -- XXXreturns inet('192.0.2.24')
``foo - bar``
~~~~~~~~~~~~~
Subtract
::
inet('2001:db8::5/64') - 5 -- returns inet('2001:db8::/64')
inet('2001:db8::5/64') - inet('2001:db8::') -- XXXreturns 5
``foo / bar``
~~~~~~~~~~~~~
Change mask (absolute)
::
inet('2001:db8::/32') / 64 -- returns inet('2001:db8::/64')
inet('2001:db8::1/32') / 64 -- returns inet('2001:db8::1/64')
``foo ^ bar``
~~~~~~~~~~~~~
Change mask (relative)
::
inet('2001:db8::/64') ^ -8 -- returns inet('2001:db8::/56')
inet('2001:db8::2/48') ^ 8 -- returns inet('2001:db8::2/56')
``foo * bar``
~~~~~~~~~~~~~
Move network
::
inet('2001:db8::/64') * 1 -- returns inet('2001:db8:0:1::/64')
inet('2001:db8:1::/64') * -16 -- returns inet('2001:db8:0:fff0::/64')
``foo:network()``
~~~~~~~~~~~~~~~~~
Reset the host bits.
::
inet('192.0.2.4/24'):network() -- returns inet('192.0.2.0/24')
``foo:netmask()``
~~~~~~~~~~~~~~~~~
Build an IP address mask with the netmask of ``foo``.
::
inet('192.0.2.0/24'):netmask() -- returns inet('255.255.255.0')
``foo:flip()``
~~~~~~~~~~~~~~
Flip the least significant network bit, to find the complimentary network.
::
inet('192.0.2.0/26'):flip() -- returns inet('192.0.2.64/26')
inet('192.0.2.64/26'):flip() -- returns inet('192.0.2.0/26')
inet('192.0.2.0/25'):flip() -- returns inet('192.0.2.128/25')
Tests
-----
``<``, ``<=``, ``>=`` and ``>``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Compares ``inet`` instances according to the sort order.
::
inet('192.0.2.0/26') < inet('192.0.2.64/26') -- returns true
inet('192.0.2.0/24') < inet('192.0.2.0/26') -- returns true
inet('192.0.2.0/26') < inet('192.0.2.1/26') -- returns true
``==`` and ``~=``
~~~~~~~~~~~~~~~~~
Checks if two ``inet`` instances are of the same family, address and mask, or not.
::
inet('192.0.2.0/24') == inet('192.0.2.0/24') -- returns true
inet('192.0.2.0/24') ~= inet('192.0.2.0/24') -- returns false
inet('192.0.2.0/24') == inet('192.0.2.0/26') -- returns false
inet('192.0.2.0/24') == inet('192.0.2.1/24') -- returns false
inet('192.0.2.0/24') == inet('2001:db8::') -- returns false
``#foo``
~~~~~~~~
Returns the amount of significant network bits.
::
#inet('192.0.2.0/24') -- returns 24
#inet('2001:db8::/48') -- returns 48
``foo:contains(bar)``
~~~~~~~~~~~~~~~~~~~~~~
``:contains()`` tests for subnet inclusion. It considers only the network parts of the two addresses, ignoring any host part, and determine whether one network part is a subnet of the other.
::
inet('192.0.2.0/24'):contains(inet('192.0.2.64/26')) -- returns true
inet('192.0.2.0/24'):contains(inet('192.0.2.0/26')) -- returns true
inet('192.0.2.0/24'):contains(inet('192.0.2.0/24')) -- returns false
inet('192.0.2.64/26'):contains(inet('192.0.2.0/24')) -- returns false
Text representation
-------------------
``inet6`` implements `RFC 5952`_ providing a standardized textual representation of IPv6 addresses.
``tostring(foo)``
~~~~~~~~~~~~~~~~~
String representation of ``foo``. If ``foo`` represents a host address, then just the address is returned, otherwise CIDR notation is used.
::
tostring(inet('192.0.2.0/24')) -- returns '192.0.2.0/24'
tostring(inet('192.0.2.0/32')) -- returns '192.0.2.0'
For IPv6, if the network is contained by ``inet.mixed_networks``, then mixed notation is used.
``foo:cidrstring(foo)``
~~~~~~~~~~~~~~~~~~~~~~~
Like ``tostring(foo)``, but always return the address in CIDR notation, as specified in `RFC 4632`_.
::
inet('192.0.2.0/32'):cidrstring() -- returns '192.0.2.0/32'
``foo:ipstring()``
~~~~~~~~~~~~~~~~~~
Like ``tostring(foo)``, but always just return the IP address.
::
inet('192.0.2.0/24'):ipstring() -- returns '192.0.2.0'
``foo:ipstring4()``
~~~~~~~~~~~~~~~~~~~
Like ``foo:ipstring()``, but always uses mixed notation.
``foo:ipstring6()``
~~~~~~~~~~~~~~~~~~~
Like ``tostring(foo)``, but never uses mixed notation.
Sets
----
``set:list()``
~~~~~~~~~~~~~~
list networks in set
``set:add(foo)``
~~~~~~~~~~~~~~~~
add network to set
``set:remove(foo)``
~~~~~~~~~~~~~~~~~~~
remove network from set
``set:contains(foo)``
~~~~~~~~~~~~~~~~~~~~~
is network contained in set?
``set:flush()``
~~~~~~~~~~~~~~~
Empties the set.
History
=======
* ``inet`` was brewed in Labitat_ in late 2014.
* Since then it has been battle-tested in production at the danish ISP Fiberby_.
* In July 2019 ``inet`` was finally polished up and released to the world.
License
=======
This project is licensed under `GNU Lesser General Public License version 3`_ or later.
.. _Labitat: https://labitat.dk/
.. _Fiberby: https://peeringdb.com/asn/42541
.. _Lua: http://www.lua.org/
.. _LPeg: http://www.inf.puc-rio.br/~roberto/lpeg/
.. _RFC 4632: https://tools.ietf.org/html/rfc4632
.. _RFC 5952: https://tools.ietf.org/html/rfc5952
.. _GNU Lesser General Public License version 3: https://www.gnu.org/licenses/lgpl-3.0.en.html