4 * Copyright (C) 1993-2001, 2003 by Darren Reed.
6 * See the IPFILTER.LICENCE file for details on licencing.
8 #if defined(KERNEL) || defined(_KERNEL)
15 # define _PROTO_NET_H_
17 #include <sys/errno.h>
18 #include <sys/types.h>
19 #include <sys/param.h>
21 #if !defined(_KERNEL) && !defined(__KERNEL__)
32 # include <sys/systm.h>
33 # if defined(NetBSD) && (__NetBSD_Version__ >= 104000000)
34 # include <sys/proc.h>
39 # include <sys/protosw.h>
41 #include <sys/socket.h>
42 #if defined(_KERNEL) && (!defined(__SVR4) && !defined(__svr4__))
43 # include <sys/mbuf.h>
45 #if defined(__SVR4) || defined(__svr4__)
46 # include <sys/filio.h>
47 # include <sys/byteorder.h>
49 # include <sys/dditypes.h>
51 # include <sys/stream.h>
52 # include <sys/kmem.h>
54 #if defined(__FreeBSD_version) && (__FreeBSD_version >= 300000)
55 # include <sys/malloc.h>
58 #if (defined(__osf__) || defined(__hpux) || defined(__sgi)) && defined(_KERNEL)
60 # include <net/radix.h>
62 # include "radix_ipf_local.h"
66 #include <netinet/in.h>
68 #include "netinet/ip_compat.h"
69 #include "netinet/ip_fil.h"
70 #include "netinet/ip_pool.h"
72 #if defined(IPFILTER_LOOKUP) && defined(_KERNEL) && \
73 ((BSD >= 198911) && !defined(__osf__) && \
74 !defined(__hpux) && !defined(__sgi))
75 static int rn_freenode __P((struct radix_node *, void *));
81 static const char sccsid[] = "@(#)ip_fil.c 2.41 6/5/96 (C) 1993-2000 Darren Reed";
82 static const char rcsid[] = "@(#)Id: ip_pool.c,v 2.55.2.12 2005/02/01 04:04:46 darrenr Exp";
85 #ifdef IPFILTER_LOOKUP
87 # ifndef RADIX_NODE_HEAD_LOCK
88 # define RADIX_NODE_HEAD_LOCK(x) ;
90 # ifndef RADIX_NODE_HEAD_UNLOCK
91 # define RADIX_NODE_HEAD_UNLOCK(x) ;
94 ip_pool_stat_t ipoolstat;
95 ipfrwlock_t ip_poolrw;
98 * Binary tree routines from Sedgewick and enhanced to do ranges of addresses.
99 * NOTE: Insertion *MUST* be from greatest range to least for it to work!
100 * These should be replaced, eventually, by something else - most notably a
101 * interval searching method. The important feature is to be able to find
104 * So why not use a radix tree for this? As the first line implies, it
105 * has been written to work with a _range_ of addresses. A range is not
106 * necessarily a match with any given netmask so what we end up dealing
107 * with is an interval tree. Implementations of these are hard to find
108 * and the one herein is far from bug free.
110 * Sigh, in the end I became convinced that the bugs the code contained did
111 * not make it worthwhile not using radix trees. For now the radix tree from
112 * 4.4 BSD is used, but this is not viewed as a long term solution.
114 ip_pool_t *ip_pool_list[IPL_LOGSIZE] = { NULL, NULL, NULL, NULL,
115 NULL, NULL, NULL, NULL };
119 void treeprint __P((ip_pool_t *));
131 RWLOCK_INIT(&ip_poolrw, "poolrw");
134 bzero((char *)&a, sizeof(a));
135 bzero((char *)&b, sizeof(b));
136 bzero((char *)&ip, sizeof(ip));
137 bzero((char *)&op, sizeof(op));
138 strcpy(op.iplo_name, "0");
140 if (ip_pool_create(&op) == 0)
141 ipo = ip_pool_find(0, "0");
143 a.adf_addr.in4.s_addr = 0x0a010203;
144 b.adf_addr.in4.s_addr = 0xffffffff;
145 ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 1);
146 ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 1);
148 a.adf_addr.in4.s_addr = 0x0a000000;
149 b.adf_addr.in4.s_addr = 0xff000000;
150 ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 0);
151 ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 0);
153 a.adf_addr.in4.s_addr = 0x0a010100;
154 b.adf_addr.in4.s_addr = 0xffffff00;
155 ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 1);
156 ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 1);
158 a.adf_addr.in4.s_addr = 0x0a010200;
159 b.adf_addr.in4.s_addr = 0xffffff00;
160 ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 0);
161 ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 0);
163 a.adf_addr.in4.s_addr = 0x0a010000;
164 b.adf_addr.in4.s_addr = 0xffff0000;
165 ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 1);
166 ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 1);
168 a.adf_addr.in4.s_addr = 0x0a01020f;
169 b.adf_addr.in4.s_addr = 0xffffffff;
170 ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 1);
171 ip_pool_insert(ipo, &a.adf_addr, &b.adf_addr, 1);
175 ip.in4.s_addr = 0x0a00aabb;
176 printf("search(%#x) = %d (0)\n", ip.in4.s_addr,
177 ip_pool_search(ipo, 4, &ip));
179 ip.in4.s_addr = 0x0a000001;
180 printf("search(%#x) = %d (0)\n", ip.in4.s_addr,
181 ip_pool_search(ipo, 4, &ip));
183 ip.in4.s_addr = 0x0a000101;
184 printf("search(%#x) = %d (0)\n", ip.in4.s_addr,
185 ip_pool_search(ipo, 4, &ip));
187 ip.in4.s_addr = 0x0a010001;
188 printf("search(%#x) = %d (1)\n", ip.in4.s_addr,
189 ip_pool_search(ipo, 4, &ip));
191 ip.in4.s_addr = 0x0a010101;
192 printf("search(%#x) = %d (1)\n", ip.in4.s_addr,
193 ip_pool_search(ipo, 4, &ip));
195 ip.in4.s_addr = 0x0a010201;
196 printf("search(%#x) = %d (0)\n", ip.in4.s_addr,
197 ip_pool_search(ipo, 4, &ip));
199 ip.in4.s_addr = 0x0a010203;
200 printf("search(%#x) = %d (1)\n", ip.in4.s_addr,
201 ip_pool_search(ipo, 4, &ip));
203 ip.in4.s_addr = 0x0a01020f;
204 printf("search(%#x) = %d (1)\n", ip.in4.s_addr,
205 ip_pool_search(ipo, 4, &ip));
207 ip.in4.s_addr = 0x0b00aabb;
208 printf("search(%#x) = %d (-1)\n", ip.in4.s_addr,
209 ip_pool_search(ipo, 4, &ip));
227 for (c = ipo->ipo_list; c != NULL; c = c->ipn_next)
228 printf("Node %p(%s) (%#x/%#x) = %d hits %lu\n",
229 c, c->ipn_name, c->ipn_addr.adf_addr.in4.s_addr,
230 c->ipn_mask.adf_addr.in4.s_addr,
231 c->ipn_info, c->ipn_hits);
233 #endif /* TEST_POOL */
236 /* ------------------------------------------------------------------------ */
237 /* Function: ip_pool_init */
238 /* Returns: int - 0 = success, else error */
240 /* Initialise the routing table data structures where required. */
241 /* ------------------------------------------------------------------------ */
245 bzero((char *)&ipoolstat, sizeof(ipoolstat));
247 #if (!defined(_KERNEL) || (BSD < 199306))
254 /* ------------------------------------------------------------------------ */
255 /* Function: ip_pool_fini */
256 /* Returns: int - 0 = success, else error */
257 /* Locks: WRITE(ipf_global) */
259 /* Clean up all the pool data structures allocated and call the cleanup */
260 /* function for the radix tree that supports the pools. ip_pool_destroy() is*/
261 /* used to delete the pools one by one to ensure they're properly freed up. */
262 /* ------------------------------------------------------------------------ */
269 ASSERT(rw_read_locked(&ipf_global.ipf_lk) == 0);
271 for (i = 0; i <= IPL_LOGMAX; i++) {
272 for (q = ip_pool_list[i]; (p = q) != NULL; ) {
274 (void)strncpy(op.iplo_name, p->ipo_name,
275 sizeof(op.iplo_name));
277 (void) ip_pool_destroy(&op);
281 #if (!defined(_KERNEL) || (BSD < 199306))
287 /* ------------------------------------------------------------------------ */
288 /* Function: ip_pool_statistics */
289 /* Returns: int - 0 = success, else error */
290 /* Parameters: op(I) - pointer to lookup operation arguments */
292 /* Copy the current statistics out into user space, collecting pool list */
293 /* pointers as appropriate for later use. */
294 /* ------------------------------------------------------------------------ */
295 int ip_pool_statistics(op)
298 ip_pool_stat_t stats;
299 int unit, i, err = 0;
301 if (op->iplo_size != sizeof(ipoolstat))
304 bcopy((char *)&ipoolstat, (char *)&stats, sizeof(stats));
305 unit = op->iplo_unit;
306 if (unit == IPL_LOGALL) {
307 for (i = 0; i < IPL_LOGSIZE; i++)
308 stats.ipls_list[i] = ip_pool_list[i];
309 } else if (unit >= 0 && unit < IPL_LOGSIZE) {
310 if (op->iplo_name[0] != '\0')
311 stats.ipls_list[unit] = ip_pool_find(unit,
314 stats.ipls_list[unit] = ip_pool_list[unit];
318 err = COPYOUT(&stats, op->iplo_struct, sizeof(stats));
324 /* ------------------------------------------------------------------------ */
325 /* Function: ip_pool_find */
326 /* Returns: int - 0 = success, else error */
327 /* Parameters: ipo(I) - pointer to the pool getting the new node. */
329 /* Find a matching pool inside the collection of pools for a particular */
330 /* device, indicated by the unit number. */
331 /* ------------------------------------------------------------------------ */
332 void *ip_pool_find(unit, name)
338 for (p = ip_pool_list[unit]; p != NULL; p = p->ipo_next)
339 if (strncmp(p->ipo_name, name, sizeof(p->ipo_name)) == 0)
345 /* ------------------------------------------------------------------------ */
346 /* Function: ip_pool_findeq */
347 /* Returns: int - 0 = success, else error */
348 /* Parameters: ipo(I) - pointer to the pool getting the new node. */
349 /* addr(I) - pointer to address information to delete */
352 /* Searches for an exact match of an entry in the pool. */
353 /* ------------------------------------------------------------------------ */
354 ip_pool_node_t *ip_pool_findeq(ipo, addr, mask)
356 addrfamily_t *addr, *mask;
358 struct radix_node *n;
364 RADIX_NODE_HEAD_LOCK(ipo->ipo_head);
365 n = ipo->ipo_head->rnh_lookup(addr, mask, ipo->ipo_head);
366 RADIX_NODE_HEAD_UNLOCK(ipo->ipo_head);
368 return (ip_pool_node_t *)n;
372 /* ------------------------------------------------------------------------ */
373 /* Function: ip_pool_search */
374 /* Returns: int - 0 == +ve match, -1 == error, 1 == -ve/no match */
375 /* Parameters: tptr(I) - pointer to the pool to search */
376 /* version(I) - IP protocol version (4 or 6) */
377 /* dptr(I) - pointer to address information */
379 /* Search the pool for a given address and return a search result. */
380 /* ------------------------------------------------------------------------ */
381 int ip_pool_search(tptr, version, dptr)
386 struct radix_node *rn;
399 addr = (i6addr_t *)dptr;
400 bzero(&v, sizeof(v));
401 v.adf_len = offsetof(addrfamily_t, adf_addr);
404 v.adf_len += sizeof(addr->in4);
405 v.adf_addr.in4 = addr->in4;
407 } else if (version == 6) {
408 v.adf_len += sizeof(addr->in6);
409 v.adf_addr.in6 = addr->in6;
414 READ_ENTER(&ip_poolrw);
416 RADIX_NODE_HEAD_LOCK(ipo->ipo_head);
417 rn = ipo->ipo_head->rnh_matchaddr(&v, ipo->ipo_head);
418 RADIX_NODE_HEAD_UNLOCK(ipo->ipo_head);
420 if ((rn != NULL) && ((rn->rn_flags & RNF_ROOT) == 0)) {
421 m = (ip_pool_node_t *)rn;
426 RWLOCK_EXIT(&ip_poolrw);
431 /* ------------------------------------------------------------------------ */
432 /* Function: ip_pool_insert */
433 /* Returns: int - 0 = success, else error */
434 /* Parameters: ipo(I) - pointer to the pool getting the new node. */
435 /* addr(I) - address being added as a node */
436 /* mask(I) - netmask to with the node being added */
437 /* info(I) - extra information to store in this node. */
438 /* Locks: WRITE(ip_poolrw) */
440 /* Add another node to the pool given by ipo. The three parameters passed */
441 /* in (addr, mask, info) shold all be stored in the node. */
442 /* ------------------------------------------------------------------------ */
443 int ip_pool_insert(ipo, addr, mask, info)
445 i6addr_t *addr, *mask;
448 struct radix_node *rn;
451 ASSERT(rw_read_locked(&ip_poolrw.ipf_lk) == 0);
453 KMALLOC(x, ip_pool_node_t *);
458 bzero(x, sizeof(*x));
461 (void)strncpy(x->ipn_name, ipo->ipo_name, sizeof(x->ipn_name));
463 bcopy(addr, &x->ipn_addr.adf_addr, sizeof(*addr));
464 x->ipn_addr.adf_len = sizeof(x->ipn_addr);
465 bcopy(mask, &x->ipn_mask.adf_addr, sizeof(*mask));
466 x->ipn_mask.adf_len = sizeof(x->ipn_mask);
468 RADIX_NODE_HEAD_LOCK(ipo->ipo_head);
469 rn = ipo->ipo_head->rnh_addaddr(&x->ipn_addr, &x->ipn_mask,
470 ipo->ipo_head, x->ipn_nodes);
471 RADIX_NODE_HEAD_UNLOCK(ipo->ipo_head);
473 printf("Added %p at %p\n", x, rn);
481 x->ipn_next = ipo->ipo_list;
482 x->ipn_pnext = &ipo->ipo_list;
483 if (ipo->ipo_list != NULL)
484 ipo->ipo_list->ipn_pnext = &x->ipn_next;
487 ipoolstat.ipls_nodes++;
493 /* ------------------------------------------------------------------------ */
494 /* Function: ip_pool_create */
495 /* Returns: int - 0 = success, else error */
496 /* Parameters: op(I) - pointer to iplookup struct with call details */
497 /* Locks: WRITE(ip_poolrw) */
499 /* Creates a new group according to the paramters passed in via the */
500 /* iplookupop structure. Does not check to see if the group already exists */
501 /* when being inserted - assume this has already been done. If the pool is */
502 /* marked as being anonymous, give it a new, unique, identifier. Call any */
503 /* other functions required to initialise the structure. */
504 /* ------------------------------------------------------------------------ */
505 int ip_pool_create(op)
508 char name[FR_GROUPLEN];
512 ASSERT(rw_read_locked(&ip_poolrw.ipf_lk) == 0);
514 KMALLOC(h, ip_pool_t *);
517 bzero(h, sizeof(*h));
519 if (rn_inithead((void **)&h->ipo_head,
520 offsetof(addrfamily_t, adf_addr) << 3) == 0) {
525 unit = op->iplo_unit;
527 if ((op->iplo_arg & IPOOL_ANON) != 0) {
530 poolnum = IPOOL_ANON;
532 #if defined(SNPRINTF) && defined(_KERNEL)
533 SNPRINTF(name, sizeof(name), "%x", poolnum);
535 (void)sprintf(name, "%x", poolnum);
538 for (p = ip_pool_list[unit]; p != NULL; ) {
539 if (strncmp(name, p->ipo_name,
540 sizeof(p->ipo_name)) == 0) {
542 #if defined(SNPRINTF) && defined(_KERNEL)
543 SNPRINTF(name, sizeof(name), "%x", poolnum);
545 (void)sprintf(name, "%x", poolnum);
547 p = ip_pool_list[unit];
552 (void)strncpy(h->ipo_name, name, sizeof(h->ipo_name));
554 (void) strncpy(h->ipo_name, op->iplo_name, sizeof(h->ipo_name));
560 h->ipo_next = ip_pool_list[unit];
561 if (ip_pool_list[unit] != NULL)
562 ip_pool_list[unit]->ipo_pnext = &h->ipo_next;
563 h->ipo_pnext = &ip_pool_list[unit];
564 ip_pool_list[unit] = h;
566 ipoolstat.ipls_pools++;
572 /* ------------------------------------------------------------------------ */
573 /* Function: ip_pool_remove */
574 /* Returns: int - 0 = success, else error */
575 /* Parameters: ipo(I) - pointer to the pool to remove the node from. */
576 /* ipe(I) - address being deleted as a node */
577 /* Locks: WRITE(ip_poolrw) */
579 /* Add another node to the pool given by ipo. The three parameters passed */
580 /* in (addr, mask, info) shold all be stored in the node. */
581 /* ------------------------------------------------------------------------ */
582 int ip_pool_remove(ipo, ipe)
586 ip_pool_node_t **ipp, *n;
588 ASSERT(rw_read_locked(&ip_poolrw.ipf_lk) == 0);
590 for (ipp = &ipo->ipo_list; (n = *ipp) != NULL; ipp = &n->ipn_next) {
592 *n->ipn_pnext = n->ipn_next;
594 n->ipn_next->ipn_pnext = n->ipn_pnext;
602 RADIX_NODE_HEAD_LOCK(ipo->ipo_head);
603 ipo->ipo_head->rnh_deladdr(&n->ipn_addr, &n->ipn_mask,
605 RADIX_NODE_HEAD_UNLOCK(ipo->ipo_head);
608 ipoolstat.ipls_nodes--;
614 /* ------------------------------------------------------------------------ */
615 /* Function: ip_pool_destroy */
616 /* Returns: int - 0 = success, else error */
617 /* Parameters: op(I) - information about the pool to remove */
618 /* Locks: WRITE(ip_poolrw) or WRITE(ipf_global) */
620 /* Search for a pool using paramters passed in and if it's not otherwise */
623 /* NOTE: Because this function is called out of ipldetach() where ip_poolrw */
624 /* may not be initialised, we can't use an ASSERT to enforce the locking */
625 /* assertion that one of the two (ip_poolrw,ipf_global) is held. */
626 /* ------------------------------------------------------------------------ */
627 int ip_pool_destroy(op)
632 ipo = ip_pool_find(op->iplo_unit, op->iplo_name);
636 if (ipo->ipo_ref != 1)
644 /* ------------------------------------------------------------------------ */
645 /* Function: ip_pool_flush */
646 /* Returns: int - number of pools deleted */
647 /* Parameters: fp(I) - which pool(s) to flush */
648 /* Locks: WRITE(ip_poolrw) or WRITE(ipf_global) */
650 /* Free all pools associated with the device that matches the unit number */
651 /* passed in with operation. */
653 /* NOTE: Because this function is called out of ipldetach() where ip_poolrw */
654 /* may not be initialised, we can't use an ASSERT to enforce the locking */
655 /* assertion that one of the two (ip_poolrw,ipf_global) is held. */
656 /* ------------------------------------------------------------------------ */
657 int ip_pool_flush(fp)
660 int i, num = 0, unit, err;
664 unit = fp->iplf_unit;
666 for (i = 0; i <= IPL_LOGMAX; i++) {
667 if (unit != IPLT_ALL && i != unit)
669 for (q = ip_pool_list[i]; (p = q) != NULL; ) {
671 (void)strncpy(op.iplo_name, p->ipo_name,
672 sizeof(op.iplo_name));
674 err = ip_pool_destroy(&op);
685 /* ------------------------------------------------------------------------ */
686 /* Function: ip_pool_free */
688 /* Parameters: ipo(I) - pointer to pool structure */
689 /* Locks: WRITE(ip_poolrw) or WRITE(ipf_global) */
691 /* Deletes the pool strucutre passed in from the list of pools and deletes */
692 /* all of the address information stored in it, including any tree data */
693 /* structures also allocated. */
695 /* NOTE: Because this function is called out of ipldetach() where ip_poolrw */
696 /* may not be initialised, we can't use an ASSERT to enforce the locking */
697 /* assertion that one of the two (ip_poolrw,ipf_global) is held. */
698 /* ------------------------------------------------------------------------ */
699 void ip_pool_free(ipo)
704 RADIX_NODE_HEAD_LOCK(ipo->ipo_head);
705 while ((n = ipo->ipo_list) != NULL) {
706 ipo->ipo_head->rnh_deladdr(&n->ipn_addr, &n->ipn_mask,
709 *n->ipn_pnext = n->ipn_next;
711 n->ipn_next->ipn_pnext = n->ipn_pnext;
715 ipoolstat.ipls_nodes--;
717 RADIX_NODE_HEAD_UNLOCK(ipo->ipo_head);
719 ipo->ipo_list = NULL;
720 if (ipo->ipo_next != NULL)
721 ipo->ipo_next->ipo_pnext = ipo->ipo_pnext;
722 *ipo->ipo_pnext = ipo->ipo_next;
723 rn_freehead(ipo->ipo_head);
726 ipoolstat.ipls_pools--;
730 /* ------------------------------------------------------------------------ */
731 /* Function: ip_pool_deref */
733 /* Parameters: ipo(I) - pointer to pool structure */
734 /* Locks: WRITE(ip_poolrw) */
736 /* Drop the number of known references to this pool structure by one and if */
737 /* we arrive at zero known references, free it. */
738 /* ------------------------------------------------------------------------ */
739 void ip_pool_deref(ipo)
743 ASSERT(rw_read_locked(&ip_poolrw.ipf_lk) == 0);
746 if (ipo->ipo_ref == 0)
751 # if defined(_KERNEL) && ((BSD >= 198911) && !defined(__osf__) && \
752 !defined(__hpux) && !defined(__sgi))
754 rn_freenode(struct radix_node *n, void *p)
756 struct radix_node_head *rnh = p;
757 struct radix_node *d;
759 d = rnh->rnh_deladdr(n->rn_key, NULL, rnh);
761 FreeS(d, max_keylen + 2 * sizeof (*d));
769 struct radix_node_head *rnh;
772 RADIX_NODE_HEAD_LOCK(rnh);
773 (*rnh->rnh_walktree)(rnh, rn_freenode, rnh);
775 rnh->rnh_addaddr = NULL;
776 rnh->rnh_deladdr = NULL;
777 rnh->rnh_matchaddr = NULL;
778 rnh->rnh_lookup = NULL;
779 rnh->rnh_walktree = NULL;
780 RADIX_NODE_HEAD_UNLOCK(rnh);
786 #endif /* IPFILTER_LOOKUP */