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