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