NETADDR is a Common Lisp library for manipulating IP addresses, subnets, ranges, and sets. It is inspired by its namesake library in Python, netaddr. NETADDR supports/provides:
- IPv4 and IPv6 addresses, subnets, and ranges.
- Shorthand syntax for the above with a reader macro
#I. See the IP Syntax section for details. - Helper lookup functions for reserved space, e.g.,
PRIVATE?,RESERVED?, andPUBLIC?. - An
IP-SETdata structure for working with sets of addresses, subnets, and ranges. SeeMAKE-IP-SET. - Set operations on the above like union, intersection, difference, and symmetric difference.
- Membership checks of IPs against subnets, ranges, and sets using
CONTAINS?.
┌ ─ ─ ─ ┐
┌─────── IP+ ────────┐
│ └ ─ ─ ─ ┘ │
│ │
│ │
│ │
▼ ▼
┌ ─ ─ ─ ┐ ┌──────────┐
IP-LIKE ◀─ ─ set of─ ─│ IP-SET │
└ ─ ─ ─ ┘ └──────────┘
│
├──────┐
┌────────────┘ ▼
│ ┌ ─ ─ ─ ┐
│ ┌─ IP-PAIR ──┐
▼ ▼ └ ─ ─ ─ ┘ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│IP-ADDRESS│ │IP-NETWORK│ │ IP-RANGE │
└──────────┘ └──────────┘ └──────────┘
Users of this library will only instantiate the leaf classes in the tree above,
using their respective MAKE-* functions, or in the case of the three that
inherit from IP-LIKE, the short-hand #I notation. IP-SETs are comprised of
a set of IP-LIKEs. Most operations will expect either IP-LIKEs as arguments
and/or IP+s. For example, CONTAINS? takes an IP+ as its first argument and
an IP-LIKE as its second argument because:
- An
IP-ADDRESSCONTAINS?itself. - An
IP-NETWORKand anIP-RANGECONTAINS?themselves, any subset of those networks or ranges, and anyIP-ADDRESSthat is a member of the network or range. - An
IP-SETCONTAINS?any of its memberIP-LIKEs, and so on.
There are two equality operators for IP+ subclasses:
IP-EQUAL(aliased toIP=)IP-EQUALP
Similar to Common Lisp's EQUAL and EQUALP, IP-EQUAL is more specific than
IP-EQUALP. The former considers different classes to always be unequal, while
the latter allows comparisons across all leaf classes described in the Class
Hierarchy. For example:
NETADDR> (ip-equal #I("1.1.1.1") #I("1.1.1.1/32"))
NIL
NETADDR> (ip-equalp #I("1.1.1.1") #I("1.1.1.1/32"))
T
NETADDR> (ip-equalp #I("1.1.1.1") #I("1.1.1.1/31"))
NIL
NETADDR> (ip-equal #I("1.0.0.0/8") #I("1.0.0.0-1.255.255.255"))
NIL
NETADDR> (ip-equalp #I("1.0.0.0/8") #I("1.0.0.0-1.255.255.255"))
T
NETADDR> (ip-equal (make-ip-set (list #I("1.1.1.1"))) (make-ip-set (list #I("1.1.1.1/32"))))
NIL
NETADDR> (ip-equalp (make-ip-set (list #I("1.1.1.1"))) (make-ip-set (list #I("1.1.1.1/32"))))
T
IP-EQUAL always returns NIL if classes are different. However, IP-EQUALP
returns T if the underlying object refers to the same set of IP addresses,
regardless of the concrete object type. In general, if you are comparing
individual IP-LIKEs, you'll want to use IP-EQUAL. If you are comparing
IP-SETs, which may contain a mixture of classes internally, or IP-NETWORKs
and IP-RANGEs, you'll want to use IP-EQUALP.
NETADDR provides a shorthand syntax for defining IP-LIKEs from strings with
the reader macro #I that can be enabled by first calling ENABLE-IP-SYNTAX.
If a single argument is provided, a single object is returned. If multiple
arguments are provided, a list of objects is returned. Example usage is shown
below:
NETADDR> #I("1.2.3.4")
#<IP-ADDRESS 1.2.3.4>
NETADDR> #I("192.168.1.0/24")
#<IP-NETWORK 192.168.1.0/24>
NETADDR> #I("::-ffff::")
#<IP-RANGE ::-ffff::>
NETADDR> #I("0.0.0.0" "1.1.1.1")
(#<IP-ADDRESS 0.0.0.0> #<IP-ADDRESS 1.1.1.1>)
NETADDR> (multiple-value-bind (x y z) (values "1.1.1.1" "::/96" "10.20.30.40-11.20.30.40")
#I(x y z))
(#<IP-ADDRESS 1.1.1.1> #<IP-NETWORK ::/96> #<IP-RANGE 10.20.30.40-11.20.30.40>)