ip_pool.c revision 3448:aaf16568054b
1/*
2 * Copyright (C) 1993-2001, 2003 by Darren Reed.
3 *
4 * See the IPFILTER.LICENCE file for details on licencing.
5 *
6 * Copyright 2007 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__
31struct 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))
81static int rn_freenode __P((struct radix_node *, void *));
82#endif
83
84/* END OF INCLUDES */
85
86#if !defined(lint)
87static const char sccsid[] = "@(#)ip_fil.c	2.41 6/5/96 (C) 1993-2000 Darren Reed";
88static 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
112void treeprint __P((ip_pool_t *));
113
114int
115main(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
166treeprint(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
205treeprint(ipo);
206#endif
207
208	ip_pool_fini(ifs);
209
210	return 0;
211}
212
213
214void
215treeprint(ipo)
216ip_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/* ------------------------------------------------------------------------ */
235int ip_pool_init(ifs)
236ipf_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/* ------------------------------------------------------------------------ */
257void ip_pool_fini(ifs)
258ipf_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/* ------------------------------------------------------------------------ */
290int ip_pool_statistics(op, ifs)
291iplookupop_t *op;
292ipf_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/* ------------------------------------------------------------------------ */
328void *ip_pool_find(unit, name, ifs)
329int unit;
330char *name;
331ipf_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/* ------------------------------------------------------------------------ */
351ip_pool_node_t *ip_pool_findeq(ipo, addr, mask)
352ip_pool_t *ipo;
353addrfamily_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/* ------------------------------------------------------------------------ */
374int ip_pool_search(tptr, version, dptr, ifs)
375void *tptr;
376int version;
377void *dptr;
378ipf_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/* ------------------------------------------------------------------------ */
435int ip_pool_insert(ipo, addr, mask, info, ifs)
436ip_pool_t *ipo;
437addrfamily_t *addr, *mask;
438int info;
439ipf_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/* ------------------------------------------------------------------------ */
497int ip_pool_create(op, ifs)
498iplookupop_t *op;
499ipf_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/* ------------------------------------------------------------------------ */
575int ip_pool_remove(ipo, ipe, ifs)
576ip_pool_t *ipo;
577ip_pool_node_t *ipe;
578ipf_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/* ------------------------------------------------------------------------ */
619int ip_pool_destroy(op, ifs)
620iplookupop_t *op;
621ipf_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/* ------------------------------------------------------------------------ */
650int ip_pool_flush(fp, ifs)
651iplookupflush_t *fp;
652ipf_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/* ------------------------------------------------------------------------ */
693void ip_pool_free(ipo, ifs)
694ip_pool_t *ipo;
695ipf_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/* ------------------------------------------------------------------------ */
732void ip_pool_deref(ipo, ifs)
733ip_pool_t *ipo;
734ipf_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
746void ip_pool_node_deref(ipn, ifs)
747ip_pool_node_t *ipn;
748ipf_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
760int ip_pool_getnext(token, ilp, ifs)
761ipftoken_t *token;
762ipflookupiter_t *ilp;
763ipf_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	switch (ilp->ili_otype)
778	{
779	case IPFLOOKUPITER_LIST :
780		ipo = token->ipt_data;
781		if (ipo == NULL) {
782			nextipo = ifs->ifs_ip_pool_list[(int)ilp->ili_unit];
783		} else {
784			nextipo = ipo->ipo_next;
785		}
786
787		if (nextipo != NULL) {
788			if (nextipo->ipo_next == NULL)
789				token->ipt_alive = 0;
790			else {
791				ATOMIC_INC(nextipo->ipo_ref);
792			}
793		} else {
794			bzero((char *)&zp, sizeof(zp));
795			nextipo = &zp;
796		}
797		break;
798
799	case IPFLOOKUPITER_NODE :
800		node = token->ipt_data;
801		if (node == NULL) {
802			ipo = ip_pool_find(ilp->ili_unit, ilp->ili_name, ifs);
803			if (ipo == NULL)
804				err = ESRCH;
805			else {
806				nextnode = ipo->ipo_list;
807				ipo = NULL;
808			}
809		} else {
810			nextnode = node->ipn_next;
811		}
812
813		if (nextnode != NULL) {
814			if (nextnode->ipn_next == NULL)
815				token->ipt_alive = 0;
816			else {
817				ATOMIC_INC(nextnode->ipn_ref);
818			}
819		} else {
820			bzero((char *)&zn, sizeof(zn));
821			nextnode = &zn;
822		}
823		break;
824	default :
825		err = EINVAL;
826		break;
827	}
828
829	RWLOCK_EXIT(&ifs->ifs_ip_poolrw);
830
831	if (err != 0)
832		return err;
833
834	switch (ilp->ili_otype)
835	{
836	case IPFLOOKUPITER_LIST :
837		if (ipo != NULL) {
838			WRITE_ENTER(&ifs->ifs_ip_poolrw);
839			ip_pool_deref(ipo, ifs);
840			RWLOCK_EXIT(&ifs->ifs_ip_poolrw);
841		}
842		token->ipt_data = nextipo;
843		err = COPYOUT(nextipo, ilp->ili_data, sizeof(*nextipo));
844		if (err != 0)
845			err = EFAULT;
846		break;
847
848	case IPFLOOKUPITER_NODE :
849		if (node != NULL) {
850			WRITE_ENTER(&ifs->ifs_ip_poolrw);
851			ip_pool_node_deref(node, ifs);
852			RWLOCK_EXIT(&ifs->ifs_ip_poolrw);
853		}
854		token->ipt_data = nextnode;
855		err = COPYOUT(nextnode, ilp->ili_data, sizeof(*nextnode));
856		if (err != 0)
857			err = EFAULT;
858		break;
859	}
860
861	return err;
862}
863
864
865void ip_pool_iterderef(otype, unit, data, ifs)
866u_int otype;
867int unit;
868void *data;
869ipf_stack_t *ifs;
870{
871
872	if (data == NULL)
873		return;
874
875	if (unit < 0 || unit > IPL_LOGMAX)
876		return;
877
878	switch (otype)
879	{
880	case IPFLOOKUPITER_LIST :
881		WRITE_ENTER(&ifs->ifs_ip_poolrw);
882		ip_pool_deref((ip_pool_t *)data, ifs);
883		RWLOCK_EXIT(&ifs->ifs_ip_poolrw);
884		break;
885
886	case IPFLOOKUPITER_NODE :
887		WRITE_ENTER(&ifs->ifs_ip_poolrw);
888		ip_pool_node_deref((ip_pool_node_t *)data, ifs);
889		RWLOCK_EXIT(&ifs->ifs_ip_poolrw);
890		break;
891	default :
892		break;
893	}
894}
895
896
897# if defined(_KERNEL) && ((BSD >= 198911) && !defined(__osf__) && \
898      !defined(__hpux) && !defined(__sgi))
899static int
900rn_freenode(struct radix_node *n, void *p, ipf_stack_t *ifs)
901{
902	struct radix_node_head *rnh = p;
903	struct radix_node *d;
904
905	d = rnh->rnh_deladdr(n->rn_key, NULL, rnh);
906	if (d != NULL) {
907		FreeS(d, ifs->ifs_max_keylen + 2 * sizeof (*d));
908	}
909	return 0;
910}
911
912
913void
914rn_freehead(rnh)
915      struct radix_node_head *rnh;
916{
917
918	(*rnh->rnh_walktree)(rnh, rn_freenode, rnh);
919
920	rnh->rnh_addaddr = NULL;
921	rnh->rnh_deladdr = NULL;
922	rnh->rnh_matchaddr = NULL;
923	rnh->rnh_lookup = NULL;
924	rnh->rnh_walktree = NULL;
925
926	Free(rnh);
927}
928# endif
929
930#endif /* IPFILTER_LOOKUP */
931