7480b3c0efafe79c89f84a37b4eed5d19aa16af3
[zone] / net.lisp
1 ;;; -*-lisp-*-
2 ;;;
3 ;;; Network (numbering) tools
4 ;;;
5 ;;; (c) 2006 Straylight/Edgeware
6 ;;;
7
8 ;;;----- Licensing notice ---------------------------------------------------
9 ;;;
10 ;;; This program is free software; you can redistribute it and/or modify
11 ;;; it under the terms of the GNU General Public License as published by
12 ;;; the Free Software Foundation; either version 2 of the License, or
13 ;;; (at your option) any later version.
14 ;;;
15 ;;; This program is distributed in the hope that it will be useful,
16 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ;;; GNU General Public License for more details.
19 ;;;
20 ;;; You should have received a copy of the GNU General Public License
21 ;;; along with this program; if not, write to the Free Software Foundation,
22 ;;; Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23
24 (in-package #:net)
25
26 ;;;--------------------------------------------------------------------------
27 ;;; Various random utilities.
28
29 (declaim (inline mask))
30 (defun mask (n)
31 "Return 2^N - 1: i.e., a mask of N set bits."
32 (1- (ash 1 n)))
33
34 (defun find-first-bit-transition
35 (mask &optional (low 0) (high (integer-length mask)))
36 "Find the first (lowest bit-position) transition in MASK within the bounds.
37
38 The LOW bound is inclusive; the high bound is exclusive. A transition is
39 a change from zero to one, or vice-versa. The return value is the
40 upper (exclusive) bound on the initial run, and the lower (inclusive)
41 bound on the new run.
42
43 If there is no transition within the bounds, then return HIGH."
44
45 ;; Arrange that the initial run is ones.
46 (unless (logbitp low mask) (setf mask (lognot mask)))
47
48 ;; Now, note that MASK + 2^LOW is identical to MASK in all bit positions
49 ;; except for (a) the run of one bits starting at LOW, and (b) the zero bit
50 ;; just above it. So MASK xor (MASK + 2^LOW) is zero except for these
51 ;; bits; so all we need now is to find the position of its most significant
52 ;; set bit.
53 (let ((pos (1- (integer-length (logxor mask (+ mask (ash 1 low)))))))
54 (if (<= low pos high) pos high)))
55
56 (defun count-low-zero-bits (n)
57 "Return the number of low-order zero bits in the integer N."
58 (cond ((zerop n) nil)
59 ((oddp n) 0)
60 (t (find-first-bit-transition n))))
61
62 (declaim (inline round-down))
63 (defun round-down (n step)
64 "Return the largest multiple of STEP not greater than N."
65 (* step (floor n step)))
66
67 (declaim (inline round-up))
68 (defun round-up (n step)
69 "Return the smallest multiple of STEP not less than N."
70 (* step (ceiling n step)))
71
72 (defgeneric extract-class-name (object)
73 (:documentation "Turn OBJECT into a class name.")
74 (:method ((instance standard-object))
75 (extract-class-name (class-of instance)))
76 (:method ((class standard-class))
77 (class-name class))
78 (:method ((name symbol))
79 name))
80
81 (defclass savable-object ()
82 ())
83 (defmethod make-load-form ((object savable-object) &optional environment)
84 (make-load-form-saving-slots object :environment environment))
85
86 ;;;--------------------------------------------------------------------------
87 ;;; Parsing primitives for addresses.
88
89 (defun parse-partial-address
90 (str
91 &key (start 0) (end nil) (delim #\.)
92 (width 8) (radix 10) (min 1) (max 32) (shiftp t)
93 (what "address"))
94 "Parse a partial address from STR, which should be a sequence of integers
95 in the given RADIX, separated by the DELIM character, with each integer
96 N_i in the interval 0 <= N_i < 2^WIDTH. If the sequence is N_1, N_2, ...,
97 N_k, then the basic partial address BPA is the sum
98
99 SUM_{1<=i<=k} 2^{WIDTH (k-i)} N_i
100
101 If SHIFTP is true (the default) then let OFFSET be the smallest multiple
102 of WIDTH not less than MAX - k WIDTH; otherwise, let OFFSET be zero. The
103 partial address PA is BPA 2^SHIFT.
104
105 The return values are: PA, OFFSET, k WIDTH + OFFSET; i.e., the partial
106 address, and (inclusive) lower and (exclusive) upper bounds on the bits
107 specified by STR."
108
109 (setf-default end (length str))
110 (let ((addr 0) (nbits 0) (limit (ash 1 width)))
111 (when (< start end)
112 (loop
113 (when (>= nbits max)
114 (error "Too many elements in ~A" what))
115 (let* ((pos (position delim str :start start :end end))
116 (w (parse-integer str :radix radix
117 :start start :end (or pos end))))
118 (unless (and (<= 0 w) (< w limit))
119 (error "Element out of range in ~A" what))
120 (setf addr (logior (ash addr width) w))
121 (incf nbits width)
122 (unless pos (return))
123 (setf start (1+ pos)))))
124 (when (< nbits min)
125 (error "Not enough elements in ~A" what))
126 (if shiftp
127 (let* ((top (round-up max width))
128 (shift (- top nbits)))
129 (values (ash addr shift) shift top))
130 (values addr 0 nbits))))
131
132 ;;;--------------------------------------------------------------------------
133 ;;; Simple messing about with IP addresses.
134
135 (export 'ipaddr)
136 (export 'ipaddr-addr)
137 (defclass ipaddr (savable-object)
138 ()
139 (:documentation
140 "Base class for IP addresses."))
141
142 (export 'ipaddr-family)
143 (defgeneric ipaddr-family (addr))
144
145 (export 'family-addrclass)
146 (defgeneric family-addrclass (family)
147 (:method ((af symbol)) nil))
148
149 (export 'ipaddr-width)
150 (defgeneric ipaddr-width (class)
151 (:method ((object t)) (ipaddr-width (extract-class-name object))))
152
153 (export 'ipaddr-comparable-p)
154 (defgeneric ipaddr-comparable-p (addr-a addr-b)
155 (:method ((addr-a ipaddr) (addr-b ipaddr))
156 (eq (class-of addr-a) (class-of addr-b))))
157
158 (defun guess-address-class (str &key (start 0) (end nil))
159 (cond ((position #\: str :start start :end end) 'ip6addr)
160 (t 'ip4addr)))
161
162 (defgeneric parse-partial-ipaddr (class str &key start end min max)
163 (:method ((object t) str &rest keywords)
164 (apply #'parse-partial-ipaddr (extract-class-name object) str keywords)))
165
166 (export 'string-ipaddr)
167 (defun string-ipaddr (str &key (start 0) (end nil))
168 "Parse STR into an address; guess what kind is intended by the user.
169
170 STR may be anything at all: it's converted as if by `stringify'.
171 The START and END arguments may be used to parse out a substring."
172 (setf str (stringify str))
173 (let* ((class (guess-address-class str :start start :end end))
174 (width (ipaddr-width class)))
175 (make-instance class :addr
176 (parse-partial-ipaddr class str
177 :start start :end end
178 :min width :max width))))
179
180 (export 'integer-ipaddr)
181 (defgeneric integer-ipaddr (int like)
182 (:documentation "Convert INT into an address of type indicated by LIKE.
183
184 Specifically, if LIKE is an address object, then use its type; if it's
185 a class, then use it directly; if it's a symbol, then use the class it
186 names.")
187 (:method (int (like t)) (integer-ipaddr int (class-of like)))
188 (:method (int (like symbol))
189 (make-instance (or (family-addrclass like) like) :addr int))
190 (:method (int (like standard-class)) (make-instance like :addr int)))
191
192 (export 'ipaddr-string)
193 (defgeneric ipaddr-string (ip)
194 (:documentation
195 "Transform the address IP into a string in dotted-quad form."))
196
197 (defmethod print-object ((addr ipaddr) stream)
198 (print-unreadable-object (addr stream :type t)
199 (write-string (ipaddr-string addr) stream)))
200
201 (export 'ipaddrp)
202 (defun ipaddrp (ip)
203 "Answer true if IP is a valid IP address in integer form."
204 (typep ip 'ipaddr))
205
206 (defun ipaddr (ip &optional like)
207 "Convert IP to an IP address, of type similar to LIKE.
208
209 If it's an IP address, just return it unchanged; If it's an integer,
210 capture it; otherwise convert by `string-ipaddr'."
211 (typecase ip
212 (ipaddr ip)
213 (integer (integer-ipaddr ip like))
214 (t (string-ipaddr ip))))
215
216 (export 'ipaddr-rrtype)
217 (defgeneric ipaddr-rrtype (addr)
218 (:documentation "Return the proper resource record type for ADDR."))
219
220 ;;;--------------------------------------------------------------------------
221 ;;; Netmasks.
222
223 (export 'integer-netmask)
224 (defun integer-netmask (n i)
225 "Given an integer I, return an N-bit netmask with its I top bits set."
226 (- (ash 1 n) (ash 1 (- n i))))
227
228 (export 'ipmask-cidl-slash)
229 (defun ipmask-cidl-slash (width mask)
230 "Given a netmask MASK, try to compute a prefix length.
231
232 Return an integer N such that (integer-netmask WIDTH N) = MASK, or nil if
233 this is impossible."
234 (let* ((low (logxor mask (mask width)))
235 (bits (integer-length low)))
236 (and (= low (mask bits)) (- width bits))))
237
238 (export 'ipmask)
239 (defgeneric ipmask (addr mask)
240 (:documentation "Convert MASK into a suitable netmask for ADDR.")
241 (:method ((addr ipaddr) (mask null))
242 (mask (ipaddr-width addr)))
243 (:method ((addr ipaddr) (mask integer))
244 (let ((w (ipaddr-width addr)))
245 (if (<= 0 mask w)
246 (integer-netmask w mask)
247 (error "Prefix length out of range.")))))
248
249 (export 'mask-ipaddr)
250 (defun mask-ipaddr (addr mask)
251 "Apply the MASK to the ADDR, returning the base address."
252 (integer-ipaddr (logand mask (ipaddr-addr addr)) addr))
253
254 ;;;--------------------------------------------------------------------------
255 ;;; Networks: pairing an address and netmask.
256
257 (export 'ipnet)
258 (export 'ipnet-net)
259 (export 'ipnet-mask)
260 (defclass ipnet (savable-object)
261 ()
262 (:documentation "Base class for IP networks."))
263
264 (export 'ipnet-family)
265 (defgeneric ipnet-family (ipn)
266 (:method ((ipn ipnet)) (ipaddr-family (ipnet-net ipn))))
267
268 (export 'ipnet-addr)
269 (defun ipnet-addr (ipn)
270 "Return the base network address of IPN as a raw integer."
271 (ipaddr-addr (ipnet-net ipn)))
272
273 (export 'ipaddr-ipnet)
274 (defgeneric ipaddr-ipnet (addr mask)
275 (:documentation "Construct an `ipnet' object given a base ADDR and MASK."))
276
277 (export 'make-ipnet)
278 (defun make-ipnet (net mask)
279 "Construct an IP-network object given the NET and MASK; these are
280 transformed as though by `ipaddr' and `ipmask'."
281 (let* ((net (ipaddr net))
282 (mask (ipmask net mask)))
283 (ipaddr-ipnet (mask-ipaddr net mask) mask)))
284
285 (export 'with-ipnet)
286 (defmacro with-ipnet ((net addr mask) ipn &body body)
287 "Evaluate the BODY with components of IPN in scope.
288
289 The NET is bound to the underlying network base address, as an `ipaddr';
290 ADDR is bound to the integer value of this address; and MASK is bound to
291 the netmask, again as an integer. Any (or all) of these may be nil if not
292 wanted."
293 (with-gensyms tmp
294 `(let ((,tmp ,ipn))
295 (let (,@(and net `((,net (ipnet-net ,tmp))))
296 ,@(and addr `((,addr (ipnet-addr ,tmp))))
297 ,@(and mask `((,mask (ipnet-mask ,tmp)))))
298 ,@body))))
299
300 (export 'ipnet-width)
301 (defun ipnet-width (ipn)
302 "Return the underlying bit width of the addressing system."
303 (ipaddr-width (ipnet-net ipn)))
304
305 (export 'ipnet-string)
306 (defun ipnet-string (ipn)
307 "Convert IPN to a string."
308 (with-ipnet (net nil mask) ipn
309 (format nil "~A/~A"
310 (ipaddr-string net)
311 (or (ipmask-cidl-slash (ipnet-width ipn) mask)
312 (ipaddr-string (make-instance (class-of net) :addr mask))))))
313
314 (defmethod print-object ((ipn ipnet) stream)
315 (print-unreadable-object (ipn stream :type t)
316 (write-string (ipnet-string ipn) stream)))
317
318 (defun parse-subnet (class width max str &key (start 0) (end nil))
319 "Parse a subnet description from a (substring of) STR."
320 (setf-default end (length str))
321 (let ((sl (position #\/ str :start start :end end)))
322 (multiple-value-bind (addr lo hi)
323 (parse-partial-ipaddr class str :max max
324 :start start :end (or sl end))
325 (let* ((present (integer-netmask hi (- hi lo)))
326 (mask (cond ((not sl)
327 present)
328 ((every #'digit-char-p (subseq str (1+ sl) end))
329 (let ((length (parse-integer str
330 :start (1+ sl)
331 :end end)))
332 (unless (>= length (- width max))
333 (error "Mask doesn't reach subnet boundary"))
334 (integer-netmask max (- length (- width max)))))
335 (t
336 (parse-partial-ipaddr class str :max max
337 :start (1+ sl) :end end)))))
338 (unless (zerop (logandc2 mask present))
339 (error "Mask selects bits not present in base address"))
340 (values addr mask)))))
341
342 (export 'ipnet-subnet)
343 (defun ipnet-subnet (base-ipn sub-net sub-mask)
344 "Construct a subnet of IPN, using the NET and MASK.
345
346 The NET must either be zero or agree with IPN at all positions indicated
347 by their respective masks."
348 (with-ipnet (base-net base-addr base-mask) base-ipn
349 (let* ((sub-net (ipaddr sub-net (ipnet-net base-ipn)))
350 (sub-addr (ipaddr-addr sub-net))
351 (sub-mask (ipmask sub-net sub-mask))
352 (common (logand base-mask sub-mask))
353 (base-overlap (logand base-addr common))
354 (sub-overlap (logand sub-addr common))
355 (full-mask (logior base-mask sub-mask)))
356 (unless (or (zerop sub-overlap)
357 (= sub-overlap base-overlap))
358 (error "Subnet doesn't match base network"))
359 (ipaddr-ipnet (integer-ipaddr (logand full-mask
360 (logior base-addr sub-addr))
361 base-net)
362 full-mask))))
363
364 (export 'string-ipnet)
365 (defun string-ipnet (str &key (start 0) (end nil))
366 "Parse an IP-network from the string STR."
367 (setf str (stringify str))
368 (setf-default end (length str))
369 (let ((addr-class (guess-address-class str :start start :end end)))
370 (multiple-value-bind (addr mask)
371 (let ((width (ipaddr-width addr-class)))
372 (parse-subnet addr-class width width str
373 :start start :end end))
374 (make-ipnet (make-instance addr-class :addr addr)
375 (make-instance addr-class :addr mask)))))
376
377 (export 'string-subipnet)
378 (defun string-subipnet (ipn str &key (start 0) (end nil))
379 "Parse an IP subnet from a parent net IPN and a suffix string STR."
380 (setf str (stringify str))
381 (let* ((addr-class (extract-class-name (ipnet-net ipn)))
382 (width (ipaddr-width addr-class))
383 (max (- width
384 (or (ipmask-cidl-slash width (ipnet-mask ipn))
385 (error "Base network has complex netmask")))))
386 (multiple-value-bind (addr mask)
387 (parse-subnet addr-class width max str :start start :end end)
388 (ipnet-subnet ipn
389 (make-instance addr-class :addr addr)
390 (make-instance addr-class :addr mask)))))
391
392 (defun ipnet (net)
393 "Construct an IP-network object from the given argument.
394
395 A number of forms are acceptable:
396
397 * ADDR -- a single address, equivalent to (ADDR . N).
398 * (NET . MASK|nil) -- a single-object representation.
399 * IPNET -- return an equivalent (`equal', not necessarily `eql')
400 version."
401 (typecase net
402 (ipnet net)
403 ((or string symbol) (string-ipnet net))
404 (t (apply #'make-ipnet (pairify net nil)))))
405
406 (export 'ipnet-broadcast)
407 (defgeneric ipnet-broadcast (ipn)
408 (:documentation "Return the broadcast address for the network IPN.
409
410 Returns nil if there isn't one."))
411
412 (export 'ipnet-hosts)
413 (defun ipnet-hosts (ipn)
414 "Return the number of available addresses in network IPN."
415 (ash 1 (- (ipnet-width ipn) (logcount (ipnet-mask ipn)))))
416
417 (defstruct host-map
418 "An internal object used by `ipnet-index-host' and `ipnet-host-index'.
419
420 Our objective is to be able to convert between flat host indices and a
421 possibly crazy non-flat host space. We record the underlying IPNET for
422 convenience, and a list of byte-specifications for the runs of zero bits
423 in the netmask, in ascending order."
424 ipnet
425 bytes)
426
427 (export 'ipnet-host-map)
428 (defun ipnet-host-map (ipn)
429 "Work out how to enumerate the variable portion of IPN.
430
431 Returns an object which can be passed to `ipnet-index-host' and
432 `ipnet-host-index'."
433 (let* ((mask (ipnet-mask ipn)) (bytes nil) (i 0)
434 (len (integer-length mask)) (width (ipnet-width ipn)))
435 (when (logbitp i mask) (setf i (find-first-bit-transition mask i)))
436 (loop
437 (unless (< i len) (return))
438 (let ((next (find-first-bit-transition mask i width)))
439 (push (byte (- next i) i) bytes)
440 (setf i (find-first-bit-transition mask next width))))
441 (when (< len width) (push (byte (- width len) len) bytes))
442 (make-host-map :ipnet ipn :bytes (nreverse bytes))))
443
444 (export 'ipnet-index-host)
445 (defun ipnet-index-host (map host)
446 "Convert a HOST index to its address."
447 (let* ((ipn (host-map-ipnet map))
448 (addr (logand (ipnet-addr ipn) (ipnet-mask ipn))))
449 (dolist (byte (host-map-bytes map))
450 (setf (ldb byte addr) host
451 host (ash host (- (byte-size byte)))))
452 (unless (zerop host)
453 (error "Host index out of range."))
454 (integer-ipaddr addr (ipnet-net ipn))))
455
456 (export 'ipnet-host-index)
457 (defun ipnet-host-index (map addr)
458 "Convert an ADDR into a host index."
459 (let ((addr (ipaddr-addr addr))
460 (host 0) (offset 0))
461 (dolist (byte (host-map-bytes map))
462 (setf host (logior host
463 (ash (ldb byte addr) offset))
464 offset (+ offset (byte-size byte))))
465 host))
466
467 (export 'ipnet-index-bounds)
468 (defun ipnet-index-bounds (map start end)
469 "Return host-index bounds corresponding to the given bit-position bounds."
470 (flet ((hack (frob-map good-byte tweak-addr)
471 (dolist (byte (funcall frob-map (host-map-bytes map)))
472 (let* ((low (byte-position byte))
473 (high (+ low (byte-size byte)))
474 (good (funcall good-byte low high)))
475 (when good
476 (return-from hack
477 (ipnet-host-index map
478 (ipaddr (funcall tweak-addr
479 (ash 1 good))
480 (ipnet-net
481 (host-map-ipnet map))))))))
482 (error "No variable bits in range.")))
483 (values (hack #'identity
484 (lambda (low high)
485 (and (< start high) (max start low)))
486 #'identity)
487 (hack #'reverse
488 (lambda (low high)
489 (and (>= end low) (min end high)))
490 #'1-))))
491
492 (export 'ipnet-host)
493 (defun ipnet-host (ipn host)
494 "Return the address of the given HOST in network IPN.
495
496 This works even with a non-contiguous netmask."
497 (ipnet-index-host (ipnet-host-map ipn) host))
498
499 (export 'ipaddr-networkp)
500 (defun ipaddr-networkp (ip ipn)
501 "Returns true if numeric address IP is within network IPN."
502 (with-ipnet (nil addr mask) ipn
503 (= addr (logand ip mask))))
504
505 (export 'ipnet-subnetp)
506 (defun ipnet-subnetp (ipn subn)
507 "Returns true if SUBN is a (non-strict) subnet of IPN."
508 (with-ipnet (net addr mask) ipn
509 (with-ipnet (subnet subaddr submask) subn
510 (and (ipaddr-comparable-p net subnet)
511 (= addr (logand subaddr mask))
512 (= submask (logior mask submask))))))
513
514 (export 'ipnet-overlapp)
515 (defun ipnet-overlapp (ipn-a ipn-b)
516 "Returns true if IPN-A and IPN-B have any addresses in common."
517 (with-ipnet (net-a addr-a mask-a) ipn-a
518 (with-ipnet (net-b addr-b mask-b) ipn-b
519
520 ;; In the case of an overlap, we explicitly construct a common
521 ;; address. If this fails, we know that the networks don't overlap
522 ;; after all.
523 (flet ((narrow (addr-a mask-a addr-b mask-b)
524 ;; Narrow network A towards B, by setting bits in A's base
525 ;; address towards which A is indifferent, but B is not;
526 ;; return the resulting base address. This address is still
527 ;; within network A, since we only set bits to which A is
528 ;; indifferent.
529 (logior addr-a (logand addr-b (logandc2 mask-a mask-b)))))
530
531 (and (ipaddr-comparable-p net-a net-b)
532 (= (narrow addr-a mask-a addr-b mask-b)
533 (narrow addr-b mask-b addr-a mask-a)))))))
534
535 (export 'ipnet-changeable-bits)
536 (defun ipnet-changeable-bits (width mask)
537 "Work out the number of changeable bits in a network, given its MASK.
538
539 This is a conservative estimate in the case of noncontiguous masks. The
540 WIDTH is the total width of an address."
541
542 ;; We bisect the address. If the low-order bits are changeable then we
543 ;; recurse on them; otherwise we look at the high-order bits. A mask M of
544 ;; width W is changeable if it's not all-ones, i.e., if M /= 2^W. If the
545 ;; top half is changeable then we don't need to look at the bottom half.
546 (labels ((recurse (width mask offset)
547 (if (= width 1)
548 (if (zerop mask) (1+ offset) offset)
549 (let* ((lowwidth (floor width 2))
550 (highwidth (- width lowwidth))
551 (highmask (ash mask (- lowwidth))))
552 (if (logbitp highwidth (1+ highmask))
553 (recurse lowwidth
554 (logand mask (mask lowwidth))
555 offset)
556 (recurse highwidth highmask (+ offset lowwidth)))))))
557 (recurse width mask 0)))
558
559 ;;;--------------------------------------------------------------------------
560 ;;; Reverse lookups.
561
562 (export 'reverse-domain-component-width)
563 (defgeneric reverse-domain-component-width (ipaddr)
564 (:documentation "Return the component width for splitting IPADDR."))
565
566 (export 'reverse-domain-component-radix)
567 (defgeneric reverse-domain-radix (ipaddr)
568 (:documentation "Return the radix for representing IPADDR components."))
569
570 (export 'reverse-domain-component-suffix)
571 (defgeneric reverse-domain-suffix (ipaddr)
572 (:documentation "Return the reverse-lookup domain suffix for IPADDR."))
573
574 (export 'reverse-domain-fragment)
575 (defgeneric reverse-domain-fragment (ipaddr start end &key partialp)
576 (:documentation
577 "Return a portion of an IPADDR's reverse-resolution domain name.
578
579 Specifically, return the portion of the name which covers the bits of an
580 IPADDR between bits START (inclusive) and END (exclusive). Address
581 components which are only partially within the given bounds are included
582 unless PARTIALP is nil.")
583 (:method ((ipaddr ipaddr) start end &key (partialp t))
584
585 (let ((addr (ipaddr-addr ipaddr))
586 (comp-width (reverse-domain-component-width ipaddr))
587 (radix (reverse-domain-radix ipaddr)))
588
589 (with-output-to-string (out)
590 (do ((i (funcall (if partialp #'round-down #'round-up)
591 start comp-width)
592 (+ i comp-width))
593 (limit (funcall (if partialp #'round-up #'round-down)
594 end comp-width))
595 (sep nil t))
596 ((>= i limit))
597 (format out "~:[~;.~]~(~vR~)"
598 sep radix (ldb (byte comp-width i) addr)))))))
599
600 (export 'reverse-domain)
601 (defgeneric reverse-domain (ipaddr-or-ipn &optional prefix-len)
602 (:documentation "Return a reverse-resolution domain name for IPADDR-OR-IPN.
603
604 If PREFIX-LEN is nil then it defaults to the length of the network's fixed
605 prefix.")
606 (:method ((ipn ipnet) &optional prefix-len)
607 (let* ((addr (ipnet-net ipn))
608 (mask (ipnet-mask ipn))
609 (width (ipaddr-width addr)))
610 (concatenate 'string
611 (reverse-domain-fragment
612 addr
613 (if prefix-len
614 (- width prefix-len)
615 (ipnet-changeable-bits width mask))
616 width
617 :partialp nil)
618 "."
619 (reverse-domain-suffix addr))))
620 (:method ((addr ipaddr) &optional prefix-len)
621 (let* ((width (ipaddr-width addr)))
622 (reverse-domain (make-ipnet addr (mask width))
623 (or prefix-len width)))))
624
625 ;;;--------------------------------------------------------------------------
626 ;;; Network names and specifiers.
627
628 (export 'net)
629 (export 'net-name)
630 (export 'net-ipnets)
631 (defclass net ()
632 ((name :type string :initarg :name :reader net-name)
633 (ipnets :type list :initarg :ipnets :initform nil :accessor net-ipnets)
634 (next :type unsigned-byte :initform 1 :accessor net-next)))
635
636 (defmethod print-object ((net net) stream)
637 (print-unreadable-object (net stream :type t)
638 (format stream "~A~@[ = ~{~A~^, ~}~]"
639 (net-name net)
640 (mapcar #'ipnet-string (net-ipnets net)))))
641
642 (defvar *networks* (make-hash-table :test #'equal)
643 "The table of known networks.")
644
645 (export 'net-find)
646 (defun net-find (name)
647 "Find a network by NAME."
648 (gethash (string-downcase (stringify name)) *networks*))
649 (defun (setf net-find) (net name)
650 "Make NAME map to NET."
651 (setf (gethash (string-downcase (stringify name)) *networks*) net))
652
653 (export 'net-must-find)
654 (defun net-must-find (name)
655 (or (net-find name)
656 (error "Unknown network ~A." name)))
657
658 (defun net-ipnet (net family)
659 (find family (net-ipnets net) :key #'ipnet-family))
660 (defun (setf net-ipnet) (ipnet net family)
661 (assert (eq (ipnet-family ipnet) family))
662 (let ((ipns (net-ipnets net)))
663 (if (find family ipns :key #'ipnet-family)
664 (nsubstitute ipnet family ipns :key #'ipnet-family)
665 (setf (net-ipnets net) (cons ipnet ipns)))))
666
667 (defun process-net-form (name addr subnets)
668 "Unpack a net-form.
669
670 A net-form looks like (NAME ADDR [SUBNET ...]) where:
671
672 * NAME is the name for the network.
673
674 * ADDR is the subnet address (acceptable to `string-subipnet'); at
675 top-level, this is a plain network address (acceptable to
676 `string-ipnet'). Alternatively (for compatibility) the ADDR for a
677 non-top-level network can be an integer number of addresses to
678 allocate to this subnet; the subnet's base address is implicitly just
679 past the previous subnet's limit address (or, for the first subnet,
680 it's the parent network's base address). This won't work at all well
681 if your subnets have crazy netmasks.
682
683 * The SUBNETs are further net-forms, of the same form, whose addresses
684 are interpreted relative to the parent network's address.
685
686 The return value is a list of items of the form (NAME . IPNET)."
687
688 (labels ((process-subnets (subnets parent)
689 (let ((finger (ipnet-addr parent))
690 (list nil))
691 (dolist (subnet subnets list)
692 (destructuring-bind (name addr &rest subs) subnet
693 (let ((net (etypecase addr
694 (integer
695 (when (or (> (count-low-zero-bits addr)
696 (count-low-zero-bits finger))
697 (not (zerop (logand addr
698 (1- addr)))))
699 (error "Bad subnet size for ~A." name))
700 (make-ipnet
701 (ipaddr finger (ipnet-net parent))
702 (ipaddr (- (ash 1 (ipnet-width parent))
703 addr)
704 (ipnet-net parent))))
705 ((or string symbol)
706 (string-subipnet parent addr)))))
707
708 (unless (ipnet-subnetp parent net)
709 (error "Network `~A' (~A) falls outside parent ~A."
710 name (ipnet-string net) (ipnet-string parent)))
711
712 (dolist (entry list nil)
713 (let ((ipn (cdr entry)))
714 (when (ipnet-overlapp ipn net)
715 (error "Network `~A' (~A) overlaps `~A' (~A)."
716 name (ipnet-string net)
717 (car entry) (ipnet-string ipn)))))
718
719 (setf finger
720 (1+ (logior
721 (ipnet-addr net)
722 (logxor (ipnet-mask net)
723 (1- (ash 1 (ipnet-width net)))))))
724
725 (when name
726 (push (cons name net) list))
727
728 (when subs
729 (setf list (nconc (process-subnets subs net)
730 list)))))))))
731
732 (let* ((top (string-ipnet addr))
733 (list (nreverse (process-subnets subnets top))))
734 (when name (push (cons name top) list))
735 list)))
736
737 (export 'net-create)
738 (defun net-create (name net)
739 "Construct a new network called NAME and add it to the map.
740
741 The NET describes the new network, in a form acceptable to the `ipnet'
742 function. A named network may have multiple addresses with different
743 families: each `net-create' call adds a new family, or modifies the net's
744 address in an existing family."
745 (let ((ipn (ipnet net))
746 (net (net-find name)))
747 (if net
748 (progn (setf (net-ipnet net (ipnet-family ipn)) ipn) net)
749 (setf (net-find name)
750 (make-instance 'net
751 :name (string-downcase (stringify name))
752 :ipnets (list ipn))))))
753
754 (export 'defnet)
755 (defmacro defnet (name net &rest subnets)
756 "Main network definition macro.
757
758 None of the arguments is evaluated."
759 `(progn
760 ,@(mapcar (lambda (item)
761 (let ((name (car item)) (ipn (cdr item)))
762 `(net-create ',name ',ipn)))
763 (process-net-form name net subnets))
764 ',name))
765
766 (export 'net-parse-to-ipnets)
767 (defun net-parse-to-ipnets (form &optional (family t))
768 (flet ((hack (form family)
769 (let* ((form (if (and (consp form)
770 (endp (cdr form)))
771 (car form)
772 form))
773 (net (net-find form))
774 (ipns (if net (net-ipnets net)
775 (list (ipnet form)))))
776 (if (eq family t) ipns
777 (remove family ipns
778 :key #'ipnet-family
779 :test-not #'eq)))))
780 (let* ((ipns (if (and (listp form)
781 (every (lambda (clause)
782 (and (listp clause)
783 (symbolp (car clause))
784 (or (eq (car clause) t)
785 (family-addrclass
786 (car clause)))))
787 form))
788 (mappend (lambda (clause)
789 (hack (cdr clause) (car clause)))
790 form)
791 (hack form family)))
792 (merged (reduce (lambda (ipns ipn)
793 (if (find (ipnet-family ipn) ipns
794 :key #'ipnet-family)
795 ipns
796 (cons ipn ipns)))
797 ipns
798 :initial-value nil)))
799 (or merged (error "No matching addresses.")))))
800
801 (export 'net-host)
802 (defun net-host (net-form host &optional (family t))
803 "Return the given HOST on the NET, as an anonymous `host' object.
804
805 HOST may be an index (in range, of course), or one of the keywords:
806
807 :next next host, as by net-next-host
808 :net network base address
809 :broadcast network broadcast address
810
811 If FAMILY is not `t', then only return an address with that family;
812 otherwise return all available addresses."
813 (flet ((hosts (ipns host)
814 (mapcar (lambda (ipn) (ipnet-host ipn host))
815 (remove host ipns :key #'ipnet-hosts :test-not #'<))))
816 (let* ((net (and (typep net-form '(or string symbol))
817 (net-find net-form)))
818 (ipns (net-parse-to-ipnets net-form family))
819 (addrs (case host
820 (:next
821 (if net
822 (prog1 (hosts ipns (net-next net))
823 (incf (net-next net)))
824 (error "Can't use `:next' without a named net.")))
825 (:net (mapcar #'ipnet-net ipns))
826 (:broadcast (remove nil (mapcar #'ipnet-broadcast ipns)))
827 (t (hosts ipns host)))))
828 (unless addrs
829 (error "No networks have that address."))
830 (make-instance 'host :addrs addrs))))
831
832 ;;;--------------------------------------------------------------------------
833 ;;; Host names and specifiers.
834
835 (export 'host)
836 (export 'host-name)
837 (export 'host-addrs)
838 (defclass host ()
839 ((name :type (or string null) :initform nil
840 :initarg :name :reader host-name)
841 (addrs :type list :initarg :addrs :initform nil :accessor host-addrs)))
842
843 (defmethod print-object ((host host) stream)
844 (print-unreadable-object (host stream :type t)
845 (format stream "~:[<anonymous>~;~@*~A~]~@[ = ~{~A~^, ~}~]"
846 (host-name host)
847 (mapcar #'ipaddr-string (host-addrs host)))))
848
849 (defvar *hosts* (make-hash-table :test #'equal)
850 "The table of known hostnames.")
851
852 (export 'host-find)
853 (defun host-find (name)
854 "Find a host by NAME."
855 (gethash (string-downcase (stringify name)) *hosts*))
856 (defun (setf host-find) (addr name)
857 "Make NAME map to ADDR (must be an ipaddr in integer form)."
858 (setf (gethash (string-downcase (stringify name)) *hosts*) addr))
859
860 (defun merge-addresses (addrs-a addrs-b)
861 (append (remove-if (lambda (addr)
862 (member (ipaddr-family addr) addrs-b
863 :key #'ipaddr-family))
864 addrs-a)
865 addrs-b))
866
867 (export 'host-parse)
868 (defun host-parse (addr &optional (family t))
869 "Convert the ADDR into a (possibly anonymous) `host' object.
870
871 The ADDR can be one of a number of different things.
872
873 HOST a host name defined using `defhost'
874
875 (NET INDEX) a particular host in a network
876
877 IPADDR an address form acceptable to `ipnet'
878
879 ((FAMILY . ADDR) ...) the above, restricted to a particular address
880 FAMILY (i.e., one of the keywords `:ipv4',
881 etc.)"
882
883 (labels ((filter-addresses (addrs family)
884 (make-instance 'host
885 :addrs (if (eq family t) addrs
886 (remove family addrs
887 :key #'ipaddr-family
888 :test-not #'eq))))
889 (host-addresses (host family)
890 (if (eq family t) host
891 (filter-addresses (host-addrs host) family)))
892 (hack (addr family)
893 (let* ((form (listify addr))
894 (indic (car form))
895 (host (and (null (cdr form))
896 (host-find indic))))
897 (cond (host
898 (host-addresses host family))
899 ((and (consp (cdr form))
900 (endp (cddr form)))
901 (net-host (car form) (cadr form) family))
902 (t
903 (filter-addresses (list (ipaddr indic)) family))))))
904 (let ((host (cond
905 ((not (eq family t))
906 (hack addr family))
907 ((and (listp addr)
908 (every (lambda (clause)
909 (and (listp clause)
910 (symbolp (car clause))
911 (or (eq (car clause) t)
912 (family-addrclass (car clause)))))
913 addr))
914 (make-instance 'host
915 :addrs (reduce #'merge-addresses
916 (mapcar
917 (lambda (clause)
918 (host-addrs
919 (hack (cdr clause)
920 (car clause))))
921 (reverse addr))
922 :initial-value nil)))
923 (t
924 (hack addr t)))))
925 (unless (host-addrs host)
926 (error "No matching addresses."))
927 host)))
928
929 (export 'host-create)
930 (defun host-create (name addr)
931 "Make host NAME map to ADDR (anything acceptable to `host-parse')."
932 (let ((existing (host-find name))
933 (new (host-parse addr)))
934 (if (not existing)
935 (setf (host-find name)
936 (make-instance 'host
937 :name (string-downcase (stringify name))
938 :addrs (host-addrs new)))
939 (progn
940 (setf (host-addrs existing)
941 (merge-addresses (host-addrs existing) (host-addrs new)))
942 existing))))
943
944 (export 'defhost)
945 (defmacro defhost (name addr)
946 "Main host definition macro. Neither NAME nor ADDR is evaluated."
947 `(progn
948 (host-create ',name ',addr)
949 ',name))
950
951 ;;;----- That's all, folks --------------------------------------------------