;;; along with this program; if not, write to the Free Software Foundation,
;;; Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-;;;--------------------------------------------------------------------------
-;;; Packaging.
-
-(defpackage #:net
- (:use #:common-lisp #:mdw.base #:mdw.str #:collect #:net-sys)
- (:export #:ipaddr #:string-ipaddr #:ipaddr-byte #:ipaddr-string #:ipaddrp
- #:integer-netmask #:ipmask #:ipmask-cidl-slash #:make-ipnet
- #:string-ipnet #:ipnet #:ipnet-net #:ipnet-mask #:with-ipnet
- #:ipnet-pretty #:ipnet-string #:ipnet-broadcast #:ipnet-hosts
- #:ipnet-host #:ipaddr-networkp #:ipnet-subnetp
- #:ipnet-changeable-bytes
- #:host-find #:host-create #:defhost #:parse-ipaddr
- #:resolve-hostname #:canonify-hostname
- #:net #:net-find #:net-get-as-ipnet #:net-create #:defnet
- #:net-next-host #:net-host))
-
(in-package #:net)
;;;--------------------------------------------------------------------------
"The type of unsigned 32-bit values."
'(unsigned-byte 32))
+(export 'ipaddr)
(deftype ipaddr ()
"The type of IP (version 4) addresses."
'u32)
;;;--------------------------------------------------------------------------
;;; Simple messing with IP addresses.
+(export 'string-ipaddr)
(defun string-ipaddr (str &key (start 0) (end nil))
- "Parse STR as an IP address in dotted-quad form and return the integer
- equivalent. STR may be anything at all: it's converted as if by
- `stringify'. The START and END arguments may be used to parse out a
- substring."
+ "Parse STR into an address.
+
+ STR may be anything at all: it's converted as if by `stringify'.
+ The START and END arguments may be used to parse out a substring."
(setf str (stringify str))
(setf-default end (length str))
(let ((addr 0) (noct 0))
(error "Wrong number of octets in IP address"))
addr))
+(export 'ipaddr-byte)
(defun ipaddr-byte (ip n)
"Return byte N (from most significant downwards) of an IP address."
(assert (<= 0 n 3))
(logand #xff (ash ip (* -8 (- 3 n)))))
+(export 'ipaddr-string)
(defun ipaddr-string (ip)
"Transform the address IP into a string in dotted-quad form."
(check-type ip ipaddr)
(dotimes (i 4)
(collect (ipaddr-byte ip i))))))
+(export 'ipaddrp)
(defun ipaddrp (ip)
"Answer true if IP is a valid IP address in integer form."
(typep ip 'ipaddr))
(defun ipaddr (ip)
- "Convert IP to an IP address. If it's an integer, return it unchanged;
- otherwise convert by `string-ipaddr'."
+ "Convert IP to an IP address.
+
+ If it's an integer, return it unchanged; otherwise convert by
+ `string-ipaddr'."
(typecase ip
(ipaddr ip)
(t (string-ipaddr ip))))
;;;--------------------------------------------------------------------------
;;; Netmasks.
+(export 'integer-netmask)
(defun integer-netmask (i)
"Given an integer I, return a netmask with its I top bits set."
(- (ash 1 32) (ash 1 (- 32 i))))
+(export 'ipmask)
(defun ipmask (ip)
"Transform IP into a netmask. If it's a small integer then it's converted
by `integer-netmask'; if nil, then all-bits-set; otherwise convert using
((integer 0 32) (integer-netmask ip))
(t (ipaddr ip))))
+(export 'ipmask-cidl-slash)
(defun ipmask-cidl-slash (mask)
- "Given a netmask MASK, return an integer N such that (integer-netmask N) =
- MASK, or nil if this is impossible."
+ "Given a netmask MASK, try to compute a prefix length.
+
+ Return an integer N such that (integer-netmask N) = MASK, or nil if this
+ is impossible."
(dotimes (i 33)
(when (= mask (integer-netmask i))
(return i))))
;;;--------------------------------------------------------------------------
;;; Networks: pairing an address and netmask.
+(export 'make-ipnet)
(defun make-ipnet (net mask)
"Construct an IP-network object given the NET and MASK; these are
transformed as though by `ipaddr' and `ipmask'."
(mask (ipmask mask)))
(cons (logand net mask) mask)))
+(export 'string-ipnet)
(defun string-ipnet (str &key (start 0) (end nil))
"Parse an IP-network from the string STR."
(setf str (stringify str))
(make-ipnet (parse-ipaddr (subseq str start end))
(integer-netmask 32)))))
+(export 'ipnet)
(defun ipnet (net)
"Construct an IP-network object from the given argument. A number of forms
are acceptable:
(cond ((or (stringp net) (symbolp net)) (string-ipnet net))
(t (apply #'make-ipnet (pairify net 32)))))
+(export 'ipnet-net)
(defun ipnet-net (ipn)
"Return the base network address of IPN."
(car ipn))
+(export 'ipnet-mask)
(defun ipnet-mask (ipn)
"Return the netmask of IPN."
(cdr ipn))
+(export 'with-ipnet)
(defmacro with-ipnet ((net mask) ipn &body body)
- "Evaluate BODY with NET and MASK bound to the base address and netmask of
- IPN. Either NET or MASK (or, less usefully, both) may be nil if not
- wanted."
+ "Evaluate the BODY with components of IPN in scope.
+
+ The NET is bound to the underlying network base address and MASK is bound
+ to the netmask, again as an integer. Either (or both) of these may be nil
+ if not wanted."
(with-gensyms tmp
`(let ((,tmp ,ipn))
(let (,@(and net `((,net (ipnet-net ,tmp))))
,@(and mask `((,mask (ipnet-mask ,tmp)))))
,@body))))
+(export 'ipnet-pretty)
(defun ipnet-pretty (ipn)
"Convert IPN to a pretty cons-cell form."
(with-ipnet (net mask) ipn
(cons (ipaddr-string net)
(or (ipmask-cidl-slash mask) (ipaddr-string mask)))))
+(export 'ipnet-string)
(defun ipnet-string (ipn)
"Convert IPN to a string."
(with-ipnet (net mask) ipn
(ipaddr-string net)
(or (ipmask-cidl-slash mask) (ipaddr-string mask)))))
+(export 'ipnet-broadcast)
(defun ipnet-broadcast (ipn)
"Return the broadcast address for the network IPN."
(with-ipnet (net mask) ipn
(logior net (logxor (mask 32) mask))))
+(export 'ipnet-hosts)
(defun ipnet-hosts (ipn)
"Return the number of available addresses in network IPN."
(ash 1 (- 32 (logcount (ipnet-mask ipn)))))
+(export 'ipnet-host)
(defun ipnet-host (ipn host)
- "Return the address of the given HOST in network IPN. This works even with
- a non-contiguous netmask."
+ "Return the address of the given HOST in network IPN.
+
+ This works even with a non-contiguous netmask."
(check-type host u32)
(with-ipnet (net mask) ipn
(let ((i 0) (m 1) (a net) (h host))
(setf m (ash m 1))
(incf i)))))
+(export 'ipaddr-networkp)
(defun ipaddr-networkp (ip ipn)
"Returns true if address IP is within network IPN."
(with-ipnet (net mask) ipn
(= net (logand ip mask))))
+(export 'ipnet-subnetp)
(defun ipnet-subnetp (ipn subn)
"Returns true if SUBN is a (non-strict) subnet of IPN."
(with-ipnet (net mask) ipn
(and (= net (logand subnet mask))
(= submask (logior mask submask))))))
+(export 'ipnet-changeable-bytes)
(defun ipnet-changeable-bytes (mask)
"Answers how many low-order bytes of MASK are (entirely or partially)
changeable. This is used when constructing reverse zones."
;;;--------------------------------------------------------------------------
;;; Host names and specifiers.
+(export 'parse-ipaddr)
(defun parse-ipaddr (addr)
- "Convert the string ADDR into an IP address: tries all sorts of things:
+ "Convert the string ADDR into an IP address.
+
+ Tries all sorts of things:
(NET [INDEX]) index a network: NET is a network name defined by
defnet; INDEX is an index or one of the special
(defvar *hosts* (make-hash-table :test #'equal)
"The table of known hostnames.")
+(export 'host-find)
(defun host-find (name)
"Find a host by NAME."
(gethash (string-downcase (stringify name)) *hosts*))
-
(defun (setf host-find) (addr name)
"Make NAME map to ADDR (must be an ipaddr in integer form)."
(setf (gethash (string-downcase (stringify name)) *hosts*) addr))
+(export 'host-create)
(defun host-create (name addr)
"Make host NAME map to ADDR (anything acceptable to parse-ipaddr)."
(setf (host-find name) (parse-ipaddr addr)))
+(export 'defhost)
(defmacro defhost (name addr)
"Main host definition macro. Neither NAME nor ADDR is evaluated."
`(progn
;;;--------------------------------------------------------------------------
;;; Network names and specifiers.
+(export 'net)
(defstruct (net (:predicate netp))
"A network structure. Slots:
(defvar *networks* (make-hash-table :test #'equal)
"The table of known networks.")
+(export 'net-find)
(defun net-find (name)
"Find a network by NAME."
(gethash (string-downcase (stringify name)) *networks*))
-
(defun (setf net-find) (net name)
"Make NAME map to NET."
(setf (gethash (string-downcase (stringify name)) *networks*) net))
+(export 'net-get-as-ipnet)
(defun net-get-as-ipnet (form)
- "Transform FORM into an ipnet. FORM may be a network name, or something
-acceptable to the ipnet function."
+ "Transform FORM into an ipnet.
+
+ FORM may be a network name, or something acceptable to the ipnet
+ function."
(let ((net (net-find form)))
(if net (net-ipnet net)
(ipnet form))))
(defun process-net-form (root addr subnets)
- "Unpack a net-form. The return value is a list of entries, each of which
- is a list of the form (NAME ADDR MASK). The first entry is merely repeats
- the given ROOT and ADDR arguments (unpacking ADDR into separate network
- address and mask). The SUBNETS are then processed: they are a list of
- items of the form (NAME NUM-HOSTS . SUBNETS), where NAME names the subnet,
- NUM-HOSTS is the number of hosts in it, and SUBNETS are its sub-subnets in
- the same form. An error is signalled if a net's subnets use up more hosts
- than the net has to start with."
+ "Unpack a net-form.
+
+ The return value is a list of entries, each of which is a list of the form
+ (NAME ADDR MASK). The first entry is merely repeats the given ROOT and
+ ADDR arguments (unpacking ADDR into separate network address and mask).
+ The SUBNETS are then processed: they are a list of items of the form (NAME
+ NUM-HOSTS . SUBNETS), where NAME names the subnet, NUM-HOSTS is the number
+ of hosts in it, and SUBNETS are its sub-subnets in the same form. An
+ error is signalled if a net's subnets use up more hosts than the net has
+ to start with."
+
(labels ((frob (subnets limit finger)
(when subnets
(destructuring-bind (name size &rest subs) (car subnets)
(cons (list root net mask)
(frob subnets (+ net (ipnet-hosts ipn) 1) net))))))
+(export 'net-create)
(defun net-create (name net)
- "Construct a new network called NAME and add it to the map. The ARGS
- describe the new network, in a form acceptable to the ipnet function."
+ "Construct a new network called NAME and add it to the map.
+
+ The ARGS describe the new network, in a form acceptable to the `ipnet'
+ function."
(let ((ipn (ipnet net)))
(setf (net-find name)
(make-net :name (string-downcase (stringify name))
:hosts (ipnet-hosts ipn)
:next 1))))
+(export 'defnet)
(defmacro defnet (name net &rest subnets)
- "Main network definition macro. None of the arguments is evaluated."
+ "Main network definition macro.
+
+ None of the arguments is evaluated."
`(progn
,@(loop for (name addr mask) in (process-net-form name net subnets)
collect `(net-create ',name '(,addr . ,mask)))
',name))
+(export 'net-next-host)
(defun net-next-host (net)
"Given a NET, return the IP address (as integer) of the next available
address in the network."
(incf (net-next net))
(net-host net next)))
+(export 'net-host)
(defun net-host (net host)
- "Return the given HOST on the NEXT. HOST may be an index (in range, of
- course), or one of the keywords:
+ "Return the given HOST on the NEXT.
+
+ HOST may be an index (in range, of course), or one of the keywords:
:NEXT next host, as by net-next-host
:NET network base address