xref: /illumos-gate/usr/src/uts/common/inet/ipf/ip_pool.c (revision dd51520e127b452179a2ce4ea3bd8dee949f9afe)
1 /*
2  * Copyright (C) 1993-2001, 2003 by Darren Reed.
3  *
4  * See the IPFILTER.LICENCE file for details on licencing.
5  *
6  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
7  */
8 
9 #if defined(KERNEL) || defined(_KERNEL)
10 # undef KERNEL
11 # undef _KERNEL
12 # define        KERNEL	1
13 # define        _KERNEL	1
14 #endif
15 #if defined(__osf__)
16 # define _PROTO_NET_H_
17 #endif
18 #include <sys/errno.h>
19 #include <sys/types.h>
20 #include <sys/param.h>
21 #include <sys/file.h>
22 #if !defined(_KERNEL) && !defined(__KERNEL__)
23 # include <stdio.h>
24 # include <stdlib.h>
25 # include <string.h>
26 # define _KERNEL
27 # ifdef __OpenBSD__
28 struct file;
29 # endif
30 # include <sys/uio.h>
31 # undef _KERNEL
32 #else
33 # include <sys/systm.h>
34 # if defined(NetBSD) && (__NetBSD_Version__ >= 104000000)
35 #  include <sys/proc.h>
36 # endif
37 #endif
38 #include <sys/time.h>
39 #if !defined(linux)
40 # include <sys/protosw.h>
41 #endif
42 #include <sys/socket.h>
43 #if defined(_KERNEL) && (!defined(__SVR4) && !defined(__svr4__))
44 # include <sys/mbuf.h>
45 #endif
46 #if defined(__SVR4) || defined(__svr4__)
47 # include <sys/filio.h>
48 # include <sys/byteorder.h>
49 # ifdef _KERNEL
50 #  include <sys/dditypes.h>
51 # endif
52 # include <sys/stream.h>
53 # include <sys/kmem.h>
54 #endif
55 #if defined(__FreeBSD_version) && (__FreeBSD_version >= 300000)
56 # include <sys/malloc.h>
57 #endif
58 
59 #if defined(_KERNEL) && (defined(__osf__) || defined(AIX) || \
60      defined(__hpux) || defined(__sgi))
61 # ifdef __osf__
62 #  include <net/radix.h>
63 # endif
64 # include "radix_ipf_local.h"
65 # define _RADIX_H_
66 #endif
67 #include <net/if.h>
68 #include <netinet/in.h>
69 
70 #include "netinet/ipf_stack.h"
71 #include "netinet/ip_compat.h"
72 #include "netinet/ip_fil.h"
73 #include "netinet/ip_pool.h"
74 
75 #if defined(IPFILTER_LOOKUP) && defined(_KERNEL) && \
76       ((BSD >= 198911) && !defined(__osf__) && \
77       !defined(__hpux) && !defined(__sgi))
78 static int rn_freenode __P((struct radix_node *, void *));
79 #endif
80 
81 /* END OF INCLUDES */
82 
83 #if !defined(lint)
84 static const char sccsid[] = "@(#)ip_fil.c	2.41 6/5/96 (C) 1993-2000 Darren Reed";
85 static const char rcsid[] = "@(#)$Id: ip_pool.c,v 2.55.2.14 2005/06/12 07:18:26 darrenr Exp $";
86 #endif
87 
88 #ifdef IPFILTER_LOOKUP
89 
90 /*
91  * Binary tree routines from Sedgewick and enhanced to do ranges of addresses.
92  * NOTE: Insertion *MUST* be from greatest range to least for it to work!
93  * These should be replaced, eventually, by something else - most notably a
94  * interval searching method.  The important feature is to be able to find
95  * the best match.
96  *
97  * So why not use a radix tree for this?  As the first line implies, it
98  * has been written to work with a _range_ of addresses.  A range is not
99  * necessarily a match with any given netmask so what we end up dealing
100  * with is an interval tree.  Implementations of these are hard to find
101  * and the one herein is far from bug free.
102  *
103  * Sigh, in the end I became convinced that the bugs the code contained did
104  * not make it worthwhile not using radix trees.  For now the radix tree from
105  * 4.4 BSD is used, but this is not viewed as a long term solution.
106  */
107 
108 #ifdef TEST_POOL
109 void treeprint __P((ip_pool_t *));
110 
111 int
112 main(argc, argv)
113 	int argc;
114 	char *argv[];
115 {
116 	addrfamily_t a, b;
117 	iplookupop_t op;
118 	ip_pool_t *ipo;
119 	i6addr_t ip;
120 	fr_info_t fin;
121 
122 	RWLOCK_INIT(&ifs->ifs_ip_poolrw, "poolrw");
123 	ip_pool_init(ifs);
124 
125 	bzero((char *)&a, sizeof(a));
126 	bzero((char *)&b, sizeof(b));
127 	bzero((char *)&ip, sizeof(ip));
128 	bzero((char *)&op, sizeof(op));
129 	strcpy(op.iplo_name, "0");
130 
131 	if (ip_pool_create(&op, ifs) == 0)
132 		ipo = ip_pool_find(0, "0", ifs);
133 
134 	a.adf_addr.in4.s_addr = 0x0a010203;
135 	b.adf_addr.in4.s_addr = 0xffffffff;
136 	ip_pool_insert(ipo, &a, &b, 1, ifs);
137 	ip_pool_insert(ipo, &a, &b, 1, ifs);
138 
139 	a.adf_addr.in4.s_addr = 0x0a000000;
140 	b.adf_addr.in4.s_addr = 0xff000000;
141 	ip_pool_insert(ipo, &a, &b, 0, ifs);
142 	ip_pool_insert(ipo, &a, &b, 0, ifs);
143 
144 	a.adf_addr.in4.s_addr = 0x0a010100;
145 	b.adf_addr.in4.s_addr = 0xffffff00;
146 	ip_pool_insert(ipo, &a, &b, 1, ifs);
147 	ip_pool_insert(ipo, &a, &b, 1, ifs);
148 
149 	a.adf_addr.in4.s_addr = 0x0a010200;
150 	b.adf_addr.in4.s_addr = 0xffffff00;
151 	ip_pool_insert(ipo, &a, &b, 0, ifs);
152 	ip_pool_insert(ipo, &a, &b, 0, ifs);
153 
154 	a.adf_addr.in4.s_addr = 0x0a010000;
155 	b.adf_addr.in4.s_addr = 0xffff0000;
156 	ip_pool_insert(ipo, &a, &b, 1, ifs);
157 	ip_pool_insert(ipo, &a, &b, 1, ifs);
158 
159 	a.adf_addr.in4.s_addr = 0x0a01020f;
160 	b.adf_addr.in4.s_addr = 0xffffffff;
161 	ip_pool_insert(ipo, &a, &b, 1, ifs);
162 	ip_pool_insert(ipo, &a, &b, 1, ifs);
163 #ifdef	DEBUG_POOL
164 treeprint(ipo);
165 #endif
166 	fin.fin_plen = 20;
167 	ip.in4.s_addr = 0x0a00aabb;
168 	printf("search(%#x) = %d (0)\n", ip.in4.s_addr,
169 		ip_pool_search(ipo, 4, &ip, &fin, ifs));
170 
171 	ip.in4.s_addr = 0x0a000001;
172 	printf("search(%#x) = %d (0)\n", ip.in4.s_addr,
173 		ip_pool_search(ipo, 4, &ip, &fin, ifs));
174 
175 	ip.in4.s_addr = 0x0a000101;
176 	printf("search(%#x) = %d (0)\n", ip.in4.s_addr,
177 		ip_pool_search(ipo, 4, &ip, &fin, ifs));
178 
179 	ip.in4.s_addr = 0x0a010001;
180 	printf("search(%#x) = %d (1)\n", ip.in4.s_addr,
181 		ip_pool_search(ipo, 4, &ip, &fin, ifs));
182 
183 	ip.in4.s_addr = 0x0a010101;
184 	printf("search(%#x) = %d (1)\n", ip.in4.s_addr,
185 		ip_pool_search(ipo, 4, &ip, &fin, ifs));
186 
187 	ip.in4.s_addr = 0x0a010201;
188 	printf("search(%#x) = %d (0)\n", ip.in4.s_addr,
189 		ip_pool_search(ipo, 4, &ip, &fin, ifs));
190 
191 	ip.in4.s_addr = 0x0a010203;
192 	printf("search(%#x) = %d (1)\n", ip.in4.s_addr,
193 		ip_pool_search(ipo, 4, &ip, &fin, ifs));
194 
195 	ip.in4.s_addr = 0x0a01020f;
196 	printf("search(%#x) = %d (1)\n", ip.in4.s_addr,
197 		ip_pool_search(ipo, 4, &ip, &fin, ifs));
198 
199 	ip.in4.s_addr = 0x0b00aabb;
200 	printf("search(%#x) = %d (-1)\n", ip.in4.s_addr,
201 		ip_pool_search(ipo, 4, &ip, &fin, ifs));
202 
203 #ifdef	DEBUG_POOL
204 treeprint(ipo);
205 #endif
206 
207 	ip_pool_fini(ifs);
208 
209 	return 0;
210 }
211 
212 
213 void
214 treeprint(ipo)
215 ip_pool_t *ipo;
216 {
217 	ip_pool_node_t *c;
218 
219 	for (c = ipo->ipo_list; c != NULL; c = c->ipn_next)
220 		printf("Node %p(%s) (%#x/%#x) = %d hits %lu\n",
221 			c, c->ipn_name, c->ipn_addr.adf_addr.in4.s_addr,
222 			c->ipn_mask.adf_addr.in4.s_addr,
223 			c->ipn_info, c->ipn_hits);
224 }
225 #endif /* TEST_POOL */
226 
227 
228 /* ------------------------------------------------------------------------ */
229 /* Function:    ip_pool_init                                                */
230 /* Returns:     int     - 0 = success, else error                           */
231 /*                                                                          */
232 /* Initialise the routing table data structures where required.             */
233 /* ------------------------------------------------------------------------ */
234 int ip_pool_init(ifs)
235 ipf_stack_t *ifs;
236 {
237 
238 	bzero(&ifs->ifs_ipoolstat, sizeof (ip_pool_stat_t));
239 
240 #if !defined(_KERNEL) || ((BSD < 199306) && (SOLARIS2 < 10))
241 	rn_init();
242 #endif
243 	return 0;
244 }
245 
246 
247 /* ------------------------------------------------------------------------ */
248 /* Function:    ip_pool_fini                                                */
249 /* Returns:     int     - 0 = success, else error                           */
250 /* Locks:       WRITE(ipf_global)                                           */
251 /*                                                                          */
252 /* Clean up all the pool data structures allocated and call the cleanup     */
253 /* function for the radix tree that supports the pools. ip_pool_destroy() is*/
254 /* used to delete the pools one by one to ensure they're properly freed up. */
255 /* ------------------------------------------------------------------------ */
256 void ip_pool_fini(ifs)
257 ipf_stack_t *ifs;
258 {
259 	ip_pool_t *p, *q;
260 	iplookupop_t op;
261 	int i;
262 
263 	ASSERT(rw_read_locked(&ifs->ifs_ipf_global.ipf_lk) == 0);
264 
265 	for (i = 0; i <= IPL_LOGMAX; i++) {
266 		for (q = ifs->ifs_ip_pool_list[i]; (p = q) != NULL; ) {
267 			op.iplo_unit = i;
268 			(void)strncpy(op.iplo_name, p->ipo_name,
269 				sizeof(op.iplo_name));
270 			q = p->ipo_next;
271 			(void) ip_pool_destroy(&op, ifs);
272 		}
273 	}
274 
275 #if !defined(_KERNEL) || ((BSD < 199306) && (SOLARIS2 < 10))
276 	rn_fini();
277 #endif
278 }
279 
280 
281 /* ------------------------------------------------------------------------ */
282 /* Function:    ip_pool_statistics                                          */
283 /* Returns:     int     - 0 = success, else error                           */
284 /* Parameters:  op(I)   - pointer to lookup operation arguments             */
285 /*                                                                          */
286 /* Copy the current statistics out into user space, collecting pool list    */
287 /* pointers as appropriate for later use.                                   */
288 /* ------------------------------------------------------------------------ */
289 int ip_pool_statistics(op, ifs)
290 iplookupop_t *op;
291 ipf_stack_t *ifs;
292 {
293 	ip_pool_stat_t stats;
294 	int unit, i, err = 0;
295 
296 	if (op->iplo_size != sizeof(ip_pool_stat_t))
297 		return EINVAL;
298 
299 	bcopy((char *)&ifs->ifs_ipoolstat, (char *)&stats, sizeof(stats));
300 	unit = op->iplo_unit;
301 	if (unit == IPL_LOGALL) {
302 		for (i = 0; i < IPL_LOGSIZE; i++)
303 			stats.ipls_list[i] = ifs->ifs_ip_pool_list[i];
304 	} else if (unit >= 0 && unit < IPL_LOGSIZE) {
305 		if (op->iplo_name[0] != '\0')
306 			stats.ipls_list[unit] = ip_pool_find(unit,
307 							     op->iplo_name, ifs);
308 		else
309 			stats.ipls_list[unit] = ifs->ifs_ip_pool_list[unit];
310 	} else
311 		err = EINVAL;
312 	if (err == 0)
313 		err = COPYOUT(&stats, op->iplo_struct, sizeof(stats));
314 	return err;
315 }
316 
317 
318 
319 /* ------------------------------------------------------------------------ */
320 /* Function:    ip_pool_find                                                */
321 /* Returns:     int     - 0 = success, else error                           */
322 /* Parameters:  ipo(I)  - pointer to the pool getting the new node.         */
323 /*                                                                          */
324 /* Find a matching pool inside the collection of pools for a particular     */
325 /* device, indicated by the unit number.                                    */
326 /* ------------------------------------------------------------------------ */
327 void *ip_pool_find(unit, name, ifs)
328 int unit;
329 char *name;
330 ipf_stack_t *ifs;
331 {
332 	ip_pool_t *p;
333 
334 	for (p = ifs->ifs_ip_pool_list[unit]; p != NULL; p = p->ipo_next)
335 		if (strncmp(p->ipo_name, name, sizeof(p->ipo_name)) == 0)
336 			break;
337 	return p;
338 }
339 
340 
341 /* ------------------------------------------------------------------------ */
342 /* Function:    ip_pool_findeq                                              */
343 /* Returns:     int     - 0 = success, else error                           */
344 /* Parameters:  ipo(I)  - pointer to the pool getting the new node.         */
345 /*              addr(I) - pointer to address information to delete          */
346 /*              mask(I) -                                                   */
347 /*                                                                          */
348 /* Searches for an exact match of an entry in the pool.                     */
349 /* ------------------------------------------------------------------------ */
350 ip_pool_node_t *ip_pool_findeq(ipo, addr, mask)
351 ip_pool_t *ipo;
352 addrfamily_t *addr, *mask;
353 {
354 	struct radix_node *n;
355 	SPL_INT(s);
356 
357 	SPL_NET(s);
358 	n = ipo->ipo_head->rnh_lookup(addr, mask, ipo->ipo_head);
359 	SPL_X(s);
360 	return (ip_pool_node_t *)n;
361 }
362 
363 
364 /* ------------------------------------------------------------------------ */
365 /* Function:    ip_pool_search                                              */
366 /* Returns:     int     - 0 == +ve match, -1 == error, 1 == -ve/no match    */
367 /* Parameters:  tptr(I)    - pointer to the pool to search                  */
368 /*              version(I) - IP protocol version (4 or 6)                   */
369 /*              dptr(I)    - pointer to address information                 */
370 /*		fin	   - pointer to packet information		    */
371 /*		ifs	   - ipf stack instance				    */
372 /*                                                                          */
373 /* Search the pool for a given address and return a search result.          */
374 /* ------------------------------------------------------------------------ */
375 int ip_pool_search(tptr, version, dptr, fin, ifs)
376 void *tptr;
377 int version;
378 void *dptr;
379 fr_info_t *fin;
380 ipf_stack_t *ifs;
381 {
382 	struct radix_node *rn;
383 	ip_pool_node_t *m;
384 	i6addr_t *addr;
385 	addrfamily_t v;
386 	ip_pool_t *ipo;
387 	int rv;
388 
389 	ipo = tptr;
390 	if (ipo == NULL)
391 		return -1;
392 
393 	rv = 1;
394 	m = NULL;
395 	addr = (i6addr_t *)dptr;
396 	bzero(&v, sizeof(v));
397 	v.adf_len = offsetof(addrfamily_t, adf_addr);
398 
399 	if (version == 4) {
400 		v.adf_len += sizeof(addr->in4);
401 		v.adf_addr.in4 = addr->in4;
402 #ifdef USE_INET6
403 	} else if (version == 6) {
404 		v.adf_len += sizeof(addr->in6);
405 		v.adf_addr.in6 = addr->in6;
406 #endif
407 	} else
408 		return -1;
409 
410 	READ_ENTER(&ifs->ifs_ip_poolrw);
411 
412 	rn = ipo->ipo_head->rnh_matchaddr(&v, ipo->ipo_head);
413 
414 	if ((rn != NULL) && ((rn->rn_flags & RNF_ROOT) == 0)) {
415 		m = (ip_pool_node_t *)rn;
416 		ipo->ipo_hits++;
417 		m->ipn_hits++;
418 		m->ipn_bytes += fin->fin_plen;
419 		rv = m->ipn_info;
420 	}
421 	RWLOCK_EXIT(&ifs->ifs_ip_poolrw);
422 	return rv;
423 }
424 
425 
426 /* ------------------------------------------------------------------------ */
427 /* Function:    ip_pool_insert                                              */
428 /* Returns:     int     - 0 = success, else error                           */
429 /* Parameters:  ipo(I)  - pointer to the pool getting the new node.         */
430 /*              addr(I) - IPv4/6 address being added as a node              */
431 /*              mask(I) - IPv4/6 netmask to with the node being added       */
432 /*              info(I) - extra information to store in this node.          */
433 /* Locks:       WRITE(ip_poolrw)                                            */
434 /*                                                                          */
435 /* Add another node to the pool given by ipo.  The three parameters passed  */
436 /* in (addr, mask, info) shold all be stored in the node.                   */
437 /* ------------------------------------------------------------------------ */
438 int ip_pool_insert(ipo, addr, mask, info, ifs)
439 ip_pool_t *ipo;
440 addrfamily_t *addr, *mask;
441 int info;
442 ipf_stack_t *ifs;
443 {
444 	struct radix_node *rn;
445 	ip_pool_node_t *x;
446 
447 	ASSERT(rw_read_locked(&ifs->ifs_ip_poolrw.ipf_lk) == 0);
448 
449 	KMALLOC(x, ip_pool_node_t *);
450 	if (x == NULL) {
451 		return ENOMEM;
452 	}
453 
454 	bzero(x, sizeof(*x));
455 
456 	x->ipn_info = info;
457 	(void)strncpy(x->ipn_name, ipo->ipo_name, sizeof(x->ipn_name));
458 
459 	bcopy(addr, &x->ipn_addr, sizeof(*addr));
460 	x->ipn_addr.adf_len = sizeof(x->ipn_addr);
461 	bcopy(mask, &x->ipn_mask, sizeof(*mask));
462 	x->ipn_mask.adf_len = sizeof(x->ipn_mask);
463 
464 	rn = ipo->ipo_head->rnh_addaddr(&x->ipn_addr, &x->ipn_mask,
465 					ipo->ipo_head, x->ipn_nodes);
466 #ifdef	DEBUG_POOL
467 	printf("Added %p at %p\n", x, rn);
468 #endif
469 
470 	if (rn == NULL) {
471 		KFREE(x);
472 		return ENOMEM;
473 	}
474 
475 	x->ipn_ref = 1;
476 	x->ipn_next = ipo->ipo_list;
477 	x->ipn_pnext = &ipo->ipo_list;
478 	if (ipo->ipo_list != NULL)
479 		ipo->ipo_list->ipn_pnext = &x->ipn_next;
480 	ipo->ipo_list = x;
481 
482 	ifs->ifs_ipoolstat.ipls_nodes++;
483 
484 	return 0;
485 }
486 
487 
488 /* ------------------------------------------------------------------------ */
489 /* Function:    ip_pool_create                                              */
490 /* Returns:     int     - 0 = success, else error                           */
491 /* Parameters:  op(I) - pointer to iplookup struct with call details        */
492 /* Locks:       WRITE(ip_poolrw)                                            */
493 /*                                                                          */
494 /* Creates a new group according to the paramters passed in via the         */
495 /* iplookupop structure.  Does not check to see if the group already exists */
496 /* when being inserted - assume this has already been done.  If the pool is */
497 /* marked as being anonymous, give it a new, unique, identifier.  Call any  */
498 /* other functions required to initialise the structure.                    */
499 /* ------------------------------------------------------------------------ */
500 int ip_pool_create(op, ifs)
501 iplookupop_t *op;
502 ipf_stack_t *ifs;
503 {
504 	char name[FR_GROUPLEN];
505 	int poolnum, unit;
506 	ip_pool_t *h;
507 
508 	ASSERT(rw_read_locked(&ifs->ifs_ip_poolrw.ipf_lk) == 0);
509 
510 	KMALLOC(h, ip_pool_t *);
511 	if (h == NULL)
512 		return ENOMEM;
513 	bzero(h, sizeof(*h));
514 
515 	if (rn_inithead((void **)&h->ipo_head,
516 			offsetof(addrfamily_t, adf_addr) << 3) == 0) {
517 		KFREE(h);
518 		return ENOMEM;
519 	}
520 
521 	unit = op->iplo_unit;
522 
523 	if ((op->iplo_arg & IPOOL_ANON) != 0) {
524 		ip_pool_t *p;
525 
526 		poolnum = IPOOL_ANON;
527 
528 #if defined(SNPRINTF) && defined(_KERNEL)
529 		(void)SNPRINTF(name, sizeof(name), "%x", poolnum);
530 #else
531 		(void)sprintf(name, "%x", poolnum);
532 #endif
533 
534 		for (p = ifs->ifs_ip_pool_list[unit]; p != NULL; ) {
535 			if (strncmp(name, p->ipo_name,
536 				    sizeof(p->ipo_name)) == 0) {
537 				poolnum++;
538 #if defined(SNPRINTF) && defined(_KERNEL)
539 				(void)SNPRINTF(name, sizeof(name), "%x", poolnum);
540 #else
541 				(void)sprintf(name, "%x", poolnum);
542 #endif
543 				p = ifs->ifs_ip_pool_list[unit];
544 			} else
545 				p = p->ipo_next;
546 		}
547 
548 		(void)strncpy(h->ipo_name, name, sizeof(h->ipo_name));
549 	} else {
550 		(void) strncpy(h->ipo_name, op->iplo_name, sizeof(h->ipo_name));
551 	}
552 
553 	h->ipo_ref = 1;
554 	h->ipo_list = NULL;
555 	h->ipo_unit = unit;
556 	h->ipo_next = ifs->ifs_ip_pool_list[unit];
557 	if (ifs->ifs_ip_pool_list[unit] != NULL)
558 		ifs->ifs_ip_pool_list[unit]->ipo_pnext = &h->ipo_next;
559 	h->ipo_pnext = &ifs->ifs_ip_pool_list[unit];
560 	ifs->ifs_ip_pool_list[unit] = h;
561 
562 	ifs->ifs_ipoolstat.ipls_pools++;
563 
564 	return 0;
565 }
566 
567 
568 /* ------------------------------------------------------------------------ */
569 /* Function:    ip_pool_remove                                              */
570 /* Returns:     int    - 0 = success, else error                            */
571 /* Parameters:  ipo(I) - pointer to the pool to remove the node from.       */
572 /*              ipe(I) - address being deleted as a node                    */
573 /* Locks:       WRITE(ip_poolrw)                                            */
574 /*                                                                          */
575 /* Add another node to the pool given by ipo.  The three parameters passed  */
576 /* in (addr, mask, info) shold all be stored in the node.                   */
577 /* ------------------------------------------------------------------------ */
578 int ip_pool_remove(ipo, ipe, ifs)
579 ip_pool_t *ipo;
580 ip_pool_node_t *ipe;
581 ipf_stack_t *ifs;
582 {
583 	ip_pool_node_t **ipp, *n;
584 
585 	ASSERT(rw_read_locked(&ifs->ifs_ip_poolrw.ipf_lk) == 0);
586 
587 	for (ipp = &ipo->ipo_list; (n = *ipp) != NULL; ipp = &n->ipn_next) {
588 		if (ipe == n) {
589 			*n->ipn_pnext = n->ipn_next;
590 			if (n->ipn_next)
591 				n->ipn_next->ipn_pnext = n->ipn_pnext;
592 			break;
593 		}
594 	}
595 
596 	if (n == NULL)
597 		return ENOENT;
598 
599 	ipo->ipo_head->rnh_deladdr(&n->ipn_addr, &n->ipn_mask,
600 				   ipo->ipo_head);
601 	KFREE(n);
602 
603 	ifs->ifs_ipoolstat.ipls_nodes--;
604 
605 	return 0;
606 }
607 
608 
609 /* ------------------------------------------------------------------------ */
610 /* Function:    ip_pool_destroy                                             */
611 /* Returns:     int    - 0 = success, else error                            */
612 /* Parameters:  op(I)  -  information about the pool to remove              */
613 /* Locks:       WRITE(ip_poolrw) or WRITE(ipf_global)                       */
614 /*                                                                          */
615 /* Search for a pool using paramters passed in and if it's not otherwise    */
616 /* busy, free it.                                                           */
617 /*                                                                          */
618 /* NOTE: Because this function is called out of ipldetach() where ip_poolrw */
619 /* may not be initialised, we can't use an ASSERT to enforce the locking    */
620 /* assertion that one of the two (ip_poolrw,ipf_global) is held.            */
621 /* ------------------------------------------------------------------------ */
622 int ip_pool_destroy(op, ifs)
623 iplookupop_t *op;
624 ipf_stack_t *ifs;
625 {
626 	ip_pool_t *ipo;
627 
628 	ipo = ip_pool_find(op->iplo_unit, op->iplo_name, ifs);
629 	if (ipo == NULL)
630 		return ESRCH;
631 
632 	if (ipo->ipo_ref != 1)
633 		return EBUSY;
634 
635 	ip_pool_free(ipo, ifs);
636 	return 0;
637 }
638 
639 
640 /* ------------------------------------------------------------------------ */
641 /* Function:    ip_pool_flush                                               */
642 /* Returns:     int    - number of pools deleted                            */
643 /* Parameters:  fp(I)  - which pool(s) to flush                             */
644 /* Locks:       WRITE(ip_poolrw) or WRITE(ipf_global)                       */
645 /*                                                                          */
646 /* Free all pools associated with the device that matches the unit number   */
647 /* passed in with operation.                                                */
648 /*                                                                          */
649 /* NOTE: Because this function is called out of ipldetach() where ip_poolrw */
650 /* may not be initialised, we can't use an ASSERT to enforce the locking    */
651 /* assertion that one of the two (ip_poolrw,ipf_global) is held.            */
652 /* ------------------------------------------------------------------------ */
653 int ip_pool_flush(fp, ifs)
654 iplookupflush_t *fp;
655 ipf_stack_t *ifs;
656 {
657 	int i, num = 0, unit, err;
658 	ip_pool_t *p, *q;
659 	iplookupop_t op;
660 
661 	unit = fp->iplf_unit;
662 
663 	for (i = 0; i <= IPL_LOGMAX; i++) {
664 		if (unit != IPLT_ALL && i != unit)
665 			continue;
666 		for (q = ifs->ifs_ip_pool_list[i]; (p = q) != NULL; ) {
667 			op.iplo_unit = i;
668 			(void)strncpy(op.iplo_name, p->ipo_name,
669 				sizeof(op.iplo_name));
670 			q = p->ipo_next;
671 			err = ip_pool_destroy(&op, ifs);
672 			if (err == 0)
673 				num++;
674 			else
675 				break;
676 		}
677 	}
678 	return num;
679 }
680 
681 
682 /* ------------------------------------------------------------------------ */
683 /* Function:    ip_pool_free                                                */
684 /* Returns:     void                                                        */
685 /* Parameters:  ipo(I) -  pointer to pool structure                         */
686 /* Locks:       WRITE(ip_poolrw) or WRITE(ipf_global)                       */
687 /*                                                                          */
688 /* Deletes the pool strucutre passed in from the list of pools and deletes  */
689 /* all of the address information stored in it, including any tree data     */
690 /* structures also allocated.                                               */
691 /*                                                                          */
692 /* NOTE: Because this function is called out of ipldetach() where ip_poolrw */
693 /* may not be initialised, we can't use an ASSERT to enforce the locking    */
694 /* assertion that one of the two (ip_poolrw,ipf_global) is held.            */
695 /* ------------------------------------------------------------------------ */
696 void ip_pool_free(ipo, ifs)
697 ip_pool_t *ipo;
698 ipf_stack_t *ifs;
699 {
700 	ip_pool_node_t *n;
701 
702 	while ((n = ipo->ipo_list) != NULL) {
703 		ipo->ipo_head->rnh_deladdr(&n->ipn_addr, &n->ipn_mask,
704 					   ipo->ipo_head);
705 
706 		*n->ipn_pnext = n->ipn_next;
707 		if (n->ipn_next)
708 			n->ipn_next->ipn_pnext = n->ipn_pnext;
709 
710 		KFREE(n);
711 
712 		ifs->ifs_ipoolstat.ipls_nodes--;
713 	}
714 
715 	ipo->ipo_list = NULL;
716 	if (ipo->ipo_next != NULL)
717 		ipo->ipo_next->ipo_pnext = ipo->ipo_pnext;
718 	*ipo->ipo_pnext = ipo->ipo_next;
719 	rn_freehead(ipo->ipo_head);
720 	KFREE(ipo);
721 
722 	ifs->ifs_ipoolstat.ipls_pools--;
723 }
724 
725 
726 /* ------------------------------------------------------------------------ */
727 /* Function:    ip_pool_deref                                               */
728 /* Returns:     void                                                        */
729 /* Parameters:  ipo(I) -  pointer to pool structure                         */
730 /* Locks:       WRITE(ip_poolrw)                                            */
731 /*                                                                          */
732 /* Drop the number of known references to this pool structure by one and if */
733 /* we arrive at zero known references, free it.                             */
734 /* ------------------------------------------------------------------------ */
735 void ip_pool_deref(ipo, ifs)
736 ip_pool_t *ipo;
737 ipf_stack_t *ifs;
738 {
739 
740 	ASSERT(rw_read_locked(&ifs->ifs_ip_poolrw.ipf_lk) == 0);
741 
742 	ipo->ipo_ref--;
743 	if (ipo->ipo_ref == 0)
744 		ip_pool_free(ipo, ifs);
745 }
746 
747 
748 
749 void ip_pool_node_deref(ipn, ifs)
750 ip_pool_node_t *ipn;
751 ipf_stack_t *ifs;
752 {
753 
754 	ipn->ipn_ref--;
755 
756 	if (ipn->ipn_ref == 0) {
757 		KFREE(ipn);
758 		ifs->ifs_ipoolstat.ipls_nodes--;
759 	}
760 }
761 
762 
763 int ip_pool_getnext(token, ilp, ifs)
764 ipftoken_t *token;
765 ipflookupiter_t *ilp;
766 ipf_stack_t *ifs;
767 {
768 	ip_pool_node_t *node, zn, *nextnode;
769 	ip_pool_t *ipo, zp, *nextipo;
770 	int err;
771 
772 	err = 0;
773 	node = NULL;
774 	nextnode = NULL;
775 	ipo = NULL;
776 	nextipo = NULL;
777 
778 	READ_ENTER(&ifs->ifs_ip_poolrw);
779 
780 	/*
781 	 * Get "previous" entry from the token and find the next entry.
782 	 *
783 	 * If we found an entry, add a reference to it and update the token.
784 	 * Otherwise, zero out data to be returned and NULL out token.
785 	 */
786 	switch (ilp->ili_otype)
787 	{
788 	case IPFLOOKUPITER_LIST :
789 		ipo = token->ipt_data;
790 		if (ipo == NULL) {
791 			nextipo = ifs->ifs_ip_pool_list[(int)ilp->ili_unit];
792 		} else {
793 			nextipo = ipo->ipo_next;
794 		}
795 		if (nextipo != NULL) {
796 			ATOMIC_INC(nextipo->ipo_ref);
797 			token->ipt_data = nextipo;
798 		} else {
799 			bzero((char *)&zp, sizeof(zp));
800 			nextipo = &zp;
801 			token->ipt_data = NULL;
802 		}
803 		break;
804 
805 	case IPFLOOKUPITER_NODE :
806 		node = token->ipt_data;
807 		if (node == NULL) {
808 			ipo = ip_pool_find(ilp->ili_unit, ilp->ili_name, ifs);
809 			if (ipo == NULL)
810 				err = ESRCH;
811 			else {
812 				nextnode = ipo->ipo_list;
813 				ipo = NULL;
814 			}
815 		} else {
816 			nextnode = node->ipn_next;
817 		}
818 		if (nextnode != NULL) {
819 			ATOMIC_INC(nextnode->ipn_ref);
820 			token->ipt_data = nextnode;
821 		} else {
822 			bzero((char *)&zn, sizeof(zn));
823 			nextnode = &zn;
824 			token->ipt_data = NULL;
825 		}
826 		break;
827 
828 	default :
829 		err = EINVAL;
830 		break;
831 	}
832 
833 	/*
834 	 * Now that we have ref, it's save to give up lock.
835 	 */
836 	RWLOCK_EXIT(&ifs->ifs_ip_poolrw);
837 
838 	if (err != 0)
839 		return err;
840 
841 	/*
842 	 * Copy out the data and update the references and token as needed.
843 	 */
844 	switch (ilp->ili_otype)
845 	{
846 	case IPFLOOKUPITER_LIST :
847 		err = COPYOUT(nextipo, ilp->ili_data, sizeof(*nextipo));
848 		if (err != 0)
849 			err = EFAULT;
850 		if (token->ipt_data == NULL) {
851 			ipf_freetoken(token, ifs);
852 		} else {
853 			if (ipo != NULL) {
854 				WRITE_ENTER(&ifs->ifs_ip_poolrw);
855 				ip_pool_deref(ipo, ifs);
856 				RWLOCK_EXIT(&ifs->ifs_ip_poolrw);
857 			}
858 			if (nextipo->ipo_next == NULL)
859 				ipf_freetoken(token, ifs);
860 		}
861 		break;
862 
863 	case IPFLOOKUPITER_NODE :
864 		err = COPYOUT(nextnode, ilp->ili_data, sizeof(*nextnode));
865 		if (err != 0)
866 			err = EFAULT;
867 		if (token->ipt_data == NULL) {
868 			ipf_freetoken(token, ifs);
869 		} else {
870 			if (node != NULL) {
871 				WRITE_ENTER(&ifs->ifs_ip_poolrw);
872 				ip_pool_node_deref(node, ifs);
873 				RWLOCK_EXIT(&ifs->ifs_ip_poolrw);
874 			}
875 			if (nextnode->ipn_next == NULL)
876 				ipf_freetoken(token, ifs);
877 		}
878 		break;
879 	}
880 
881 	return err;
882 }
883 
884 
885 void ip_pool_iterderef(otype, unit, data, ifs)
886 u_int otype;
887 int unit;
888 void *data;
889 ipf_stack_t *ifs;
890 {
891 
892 	if (data == NULL)
893 		return;
894 
895 	if (unit < 0 || unit > IPL_LOGMAX)
896 		return;
897 
898 	switch (otype)
899 	{
900 	case IPFLOOKUPITER_LIST :
901 		WRITE_ENTER(&ifs->ifs_ip_poolrw);
902 		ip_pool_deref((ip_pool_t *)data, ifs);
903 		RWLOCK_EXIT(&ifs->ifs_ip_poolrw);
904 		break;
905 
906 	case IPFLOOKUPITER_NODE :
907 		WRITE_ENTER(&ifs->ifs_ip_poolrw);
908 		ip_pool_node_deref((ip_pool_node_t *)data, ifs);
909 		RWLOCK_EXIT(&ifs->ifs_ip_poolrw);
910 		break;
911 	default :
912 		break;
913 	}
914 }
915 
916 
917 # if defined(_KERNEL) && ((BSD >= 198911) && !defined(__osf__) && \
918       !defined(__hpux) && !defined(__sgi))
919 static int
920 rn_freenode(struct radix_node *n, void *p, ipf_stack_t *ifs)
921 {
922 	struct radix_node_head *rnh = p;
923 	struct radix_node *d;
924 
925 	d = rnh->rnh_deladdr(n->rn_key, NULL, rnh);
926 	if (d != NULL) {
927 		FreeS(d, ifs->ifs_max_keylen + 2 * sizeof (*d));
928 	}
929 	return 0;
930 }
931 
932 
933 void
934 rn_freehead(rnh)
935       struct radix_node_head *rnh;
936 {
937 
938 	(*rnh->rnh_walktree)(rnh, rn_freenode, rnh);
939 
940 	rnh->rnh_addaddr = NULL;
941 	rnh->rnh_deladdr = NULL;
942 	rnh->rnh_matchaddr = NULL;
943 	rnh->rnh_lookup = NULL;
944 	rnh->rnh_walktree = NULL;
945 
946 	Free(rnh);
947 }
948 # endif
949 
950 #endif /* IPFILTER_LOOKUP */
951