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