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