]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - sys/contrib/ipfilter/netinet/ip_pool.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / sys / contrib / ipfilter / netinet / ip_pool.c
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
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
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 /* ------------------------------------------------------------------------ */
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 /* ------------------------------------------------------------------------ */
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 /* ------------------------------------------------------------------------ */
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 /* ------------------------------------------------------------------------ */
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 /* ------------------------------------------------------------------------ */
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 /* ------------------------------------------------------------------------ */
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 /* ------------------------------------------------------------------------ */
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 /* ------------------------------------------------------------------------ */
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 /* ------------------------------------------------------------------------ */
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 /* ------------------------------------------------------------------------ */
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 /* ------------------------------------------------------------------------ */
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 /* ------------------------------------------------------------------------ */
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 /* ------------------------------------------------------------------------ */
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 /* ------------------------------------------------------------------------ */
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 /* ------------------------------------------------------------------------ */
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 /* ------------------------------------------------------------------------ */
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 /* ------------------------------------------------------------------------ */
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 /* ------------------------------------------------------------------------ */
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
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
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 */