2 * Copyright (C) 2012 by Darren Reed.
4 * See the IPFILTER.LICENCE file for details on licencing.
6 #if defined(KERNEL) || defined(_KERNEL)
12 #include <sys/errno.h>
13 #include <sys/types.h>
14 #include <sys/param.h>
16 #if !defined(_KERNEL) && !defined(__KERNEL__)
24 # include <sys/systm.h>
25 # if defined(NetBSD) && (__NetBSD_Version__ >= 104000000)
26 # include <sys/proc.h>
30 # include <sys/protosw.h>
31 #include <sys/socket.h>
32 #if defined(_KERNEL) && !defined(__SVR4)
33 # include <sys/mbuf.h>
36 # include <sys/filio.h>
37 # include <sys/byteorder.h>
39 # include <sys/dditypes.h>
41 # include <sys/stream.h>
42 # include <sys/kmem.h>
44 #if defined(__FreeBSD_version)
45 # include <sys/malloc.h>
49 #include <netinet/in.h>
51 #include "netinet/ip_compat.h"
52 #include "netinet/ip_fil.h"
53 #include "netinet/ip_nat.h"
54 #include "netinet/ip_lookup.h"
55 #include "netinet/ip_dstlist.h"
66 static const char rcsid[] = "@(#)$Id: ip_dstlist.c,v 1.13.2.12 2012/07/20 08:40:19 darren_r Exp $";
69 typedef struct ipf_dstl_softc_s {
70 ippool_dst_t *dstlist[LOOKUP_POOL_SZ];
71 ippool_dst_t **tails[LOOKUP_POOL_SZ];
72 ipf_dstl_stat_t stats;
76 static void *ipf_dstlist_soft_create __P((ipf_main_softc_t *));
77 static void ipf_dstlist_soft_destroy __P((ipf_main_softc_t *, void *));
78 static int ipf_dstlist_soft_init __P((ipf_main_softc_t *, void *));
79 static void ipf_dstlist_soft_fini __P((ipf_main_softc_t *, void *));
80 static int ipf_dstlist_addr_find __P((ipf_main_softc_t *, void *, int,
82 static size_t ipf_dstlist_flush __P((ipf_main_softc_t *, void *,
84 static int ipf_dstlist_iter_deref __P((ipf_main_softc_t *, void *, int, int,
86 static int ipf_dstlist_iter_next __P((ipf_main_softc_t *, void *, ipftoken_t *,
88 static int ipf_dstlist_node_add __P((ipf_main_softc_t *, void *,
89 iplookupop_t *, int));
90 static int ipf_dstlist_node_del __P((ipf_main_softc_t *, void *,
91 iplookupop_t *, int));
92 static int ipf_dstlist_stats_get __P((ipf_main_softc_t *, void *,
94 static int ipf_dstlist_table_add __P((ipf_main_softc_t *, void *,
96 static int ipf_dstlist_table_del __P((ipf_main_softc_t *, void *,
98 static int ipf_dstlist_table_deref __P((ipf_main_softc_t *, void *, void *));
99 static void *ipf_dstlist_table_find __P((void *, int, char *));
100 static void ipf_dstlist_table_free __P((ipf_dstl_softc_t *, ippool_dst_t *));
101 static void ipf_dstlist_table_remove __P((ipf_main_softc_t *,
102 ipf_dstl_softc_t *, ippool_dst_t *));
103 static void ipf_dstlist_table_clearnodes __P((ipf_dstl_softc_t *,
105 static ipf_dstnode_t *ipf_dstlist_select __P((fr_info_t *, ippool_dst_t *));
106 static void *ipf_dstlist_select_ref __P((void *, int, char *));
107 static void ipf_dstlist_node_free __P((ipf_dstl_softc_t *, ippool_dst_t *, ipf_dstnode_t *));
108 static int ipf_dstlist_node_deref __P((void *, ipf_dstnode_t *));
109 static void ipf_dstlist_expire __P((ipf_main_softc_t *, void *));
110 static void ipf_dstlist_sync __P((ipf_main_softc_t *, void *));
112 ipf_lookup_t ipf_dstlist_backend = {
114 ipf_dstlist_soft_create,
115 ipf_dstlist_soft_destroy,
116 ipf_dstlist_soft_init,
117 ipf_dstlist_soft_fini,
118 ipf_dstlist_addr_find,
120 ipf_dstlist_iter_deref,
121 ipf_dstlist_iter_next,
122 ipf_dstlist_node_add,
123 ipf_dstlist_node_del,
124 ipf_dstlist_stats_get,
125 ipf_dstlist_table_add,
126 ipf_dstlist_table_del,
127 ipf_dstlist_table_deref,
128 ipf_dstlist_table_find,
129 ipf_dstlist_select_ref,
130 ipf_dstlist_select_node,
136 /* ------------------------------------------------------------------------ */
137 /* Function: ipf_dstlist_soft_create */
138 /* Returns: int - 0 = success, else error */
139 /* Parameters: softc(I) - pointer to soft context main structure */
141 /* Allocating a chunk of memory filled with 0's is enough for the current */
142 /* soft context used with destination lists. */
143 /* ------------------------------------------------------------------------ */
145 ipf_dstlist_soft_create(softc)
146 ipf_main_softc_t *softc;
148 ipf_dstl_softc_t *softd;
151 KMALLOC(softd, ipf_dstl_softc_t *);
157 bzero((char *)softd, sizeof(*softd));
158 for (i = 0; i <= IPL_LOGMAX; i++)
159 softd->tails[i] = &softd->dstlist[i];
165 /* ------------------------------------------------------------------------ */
166 /* Function: ipf_dstlist_soft_destroy */
168 /* Parameters: softc(I) - pointer to soft context main structure */
169 /* arg(I) - pointer to local context to use */
171 /* For destination lists, the only thing we have to do when destroying the */
172 /* soft context is free it! */
173 /* ------------------------------------------------------------------------ */
175 ipf_dstlist_soft_destroy(softc, arg)
176 ipf_main_softc_t *softc;
179 ipf_dstl_softc_t *softd = arg;
185 /* ------------------------------------------------------------------------ */
186 /* Function: ipf_dstlist_soft_init */
187 /* Returns: int - 0 = success, else error */
188 /* Parameters: softc(I) - pointer to soft context main structure */
189 /* arg(I) - pointer to local context to use */
191 /* There is currently no soft context for destination list management. */
192 /* ------------------------------------------------------------------------ */
194 ipf_dstlist_soft_init(softc, arg)
195 ipf_main_softc_t *softc;
202 /* ------------------------------------------------------------------------ */
203 /* Function: ipf_dstlist_soft_fini */
205 /* Parameters: softc(I) - pointer to soft context main structure */
206 /* arg(I) - pointer to local context to use */
208 /* There is currently no soft context for destination list management. */
209 /* ------------------------------------------------------------------------ */
211 ipf_dstlist_soft_fini(softc, arg)
212 ipf_main_softc_t *softc;
215 ipf_dstl_softc_t *softd = arg;
218 for (i = -1; i <= IPL_LOGMAX; i++) {
219 while (softd->dstlist[i + 1] != NULL) {
220 ipf_dstlist_table_remove(softc, softd,
221 softd->dstlist[i + 1]);
225 ASSERT(softd->stats.ipls_numderefnodes == 0);
229 /* ------------------------------------------------------------------------ */
230 /* Function: ipf_dstlist_addr_find */
231 /* Returns: int - 0 = success, else error */
232 /* Parameters: softc(I) - pointer to soft context main structure */
233 /* arg1(I) - pointer to local context to use */
234 /* arg2(I) - pointer to local context to use */
235 /* arg3(I) - pointer to local context to use */
236 /* arg4(I) - pointer to local context to use */
238 /* There is currently no such thing as searching a destination list for an */
239 /* address so this function becomes a no-op. Its presence is required as */
240 /* ipf_lookup_res_name() stores the "addr_find" function pointer in the */
241 /* pointer passed in to it as funcptr, although it could be a generic null- */
242 /* op function rather than a specific one. */
243 /* ------------------------------------------------------------------------ */
246 ipf_dstlist_addr_find(softc, arg1, arg2, arg3, arg4)
247 ipf_main_softc_t *softc;
256 /* ------------------------------------------------------------------------ */
257 /* Function: ipf_dstlist_flush */
258 /* Returns: int - number of objects deleted */
259 /* Parameters: softc(I) - pointer to soft context main structure */
260 /* arg(I) - pointer to local context to use */
261 /* fop(I) - pointer to lookup flush operation data */
263 /* Flush all of the destination tables that match the data passed in with */
264 /* the iplookupflush_t. There are two ways to match objects: the device for */
265 /* which they are to be used with and their name. */
266 /* ------------------------------------------------------------------------ */
268 ipf_dstlist_flush(softc, arg, fop)
269 ipf_main_softc_t *softc;
271 iplookupflush_t *fop;
273 ipf_dstl_softc_t *softd = arg;
274 ippool_dst_t *node, *next;
277 for (n = 0, i = -1; i <= IPL_LOGMAX; i++) {
278 if (fop->iplf_unit != IPLT_ALL && fop->iplf_unit != i)
280 for (node = softd->dstlist[i + 1]; node != NULL; node = next) {
281 next = node->ipld_next;
283 if ((*fop->iplf_name != '\0') &&
284 strncmp(fop->iplf_name, node->ipld_name,
288 ipf_dstlist_table_remove(softc, softd, node);
296 /* ------------------------------------------------------------------------ */
297 /* Function: ipf_dstlist_iter_deref */
298 /* Returns: int - 0 = success, else error */
299 /* Parameters: softc(I) - pointer to soft context main structure */
300 /* arg(I) - pointer to local context to use */
301 /* otype(I) - type of data structure to iterate through */
302 /* unit(I) - device we are working with */
303 /* data(I) - address of object in kernel space */
305 /* This function is called when the iteration token is being free'd and is */
306 /* responsible for dropping the reference count of the structure it points */
308 /* ------------------------------------------------------------------------ */
310 ipf_dstlist_iter_deref(softc, arg, otype, unit, data)
311 ipf_main_softc_t *softc;
321 if (unit < -1 || unit > IPL_LOGMAX) {
328 case IPFLOOKUPITER_LIST :
329 ipf_dstlist_table_deref(softc, arg, (ippool_dst_t *)data);
332 case IPFLOOKUPITER_NODE :
333 ipf_dstlist_node_deref(arg, (ipf_dstnode_t *)data);
341 /* ------------------------------------------------------------------------ */
342 /* Function: ipf_dstlist_iter_next */
343 /* Returns: int - 0 = success, else error */
344 /* Parameters: softc(I) - pointer to soft context main structure */
345 /* arg(I) - pointer to local context to use */
346 /* op(I) - pointer to lookup operation data */
347 /* uid(I) - uid of process doing the ioctl */
349 /* This function is responsible for either selecting the next destination */
350 /* list or node on a destination list to be returned as a user process */
351 /* iterates through the list of destination lists or nodes. */
352 /* ------------------------------------------------------------------------ */
354 ipf_dstlist_iter_next(softc, arg, token, iter)
355 ipf_main_softc_t *softc;
358 ipflookupiter_t *iter;
360 ipf_dstnode_t zn, *nextnode = NULL, *node = NULL;
361 ippool_dst_t zero, *next = NULL, *dsttab = NULL;
362 ipf_dstl_softc_t *softd = arg;
366 switch (iter->ili_otype)
368 case IPFLOOKUPITER_LIST :
369 dsttab = token->ipt_data;
370 if (dsttab == NULL) {
371 next = softd->dstlist[(int)iter->ili_unit + 1];
373 next = dsttab->ipld_next;
377 ATOMIC_INC32(next->ipld_ref);
378 token->ipt_data = next;
379 hint = next->ipld_next;
381 bzero((char *)&zero, sizeof(zero));
383 token->ipt_data = NULL;
388 case IPFLOOKUPITER_NODE :
389 node = token->ipt_data;
391 dsttab = ipf_dstlist_table_find(arg, iter->ili_unit,
393 if (dsttab == NULL) {
398 if (dsttab->ipld_dests == NULL)
401 nextnode = *dsttab->ipld_dests;
405 nextnode = node->ipfd_next;
408 if (nextnode != NULL) {
409 MUTEX_ENTER(&nextnode->ipfd_lock);
410 nextnode->ipfd_ref++;
411 MUTEX_EXIT(&nextnode->ipfd_lock);
412 token->ipt_data = nextnode;
413 hint = nextnode->ipfd_next;
415 bzero((char *)&zn, sizeof(zn));
417 token->ipt_data = NULL;
430 switch (iter->ili_otype)
432 case IPFLOOKUPITER_LIST :
434 ipf_dstlist_table_deref(softc, arg, dsttab);
435 err = COPYOUT(next, iter->ili_data, sizeof(*next));
442 case IPFLOOKUPITER_NODE :
444 ipf_dstlist_node_deref(arg, node);
445 err = COPYOUT(nextnode, iter->ili_data, sizeof(*nextnode));
454 ipf_token_mark_complete(token);
460 /* ------------------------------------------------------------------------ */
461 /* Function: ipf_dstlist_node_add */
462 /* Returns: int - 0 = success, else error */
463 /* Parameters: softc(I) - pointer to soft context main structure */
464 /* arg(I) - pointer to local context to use */
465 /* op(I) - pointer to lookup operation data */
466 /* uid(I) - uid of process doing the ioctl */
467 /* Locks: WRITE(ipf_poolrw) */
469 /* Add a new node to a destination list. To do this, we only copy in the */
470 /* frdest_t structure because that contains the only data required from the */
471 /* application to create a new node. The frdest_t doesn't contain the name */
472 /* itself. When loading filter rules, fd_name is a 'pointer' to the name. */
473 /* In this case, the 'pointer' does not work, instead it is the length of */
474 /* the name and the name is immediately following the frdest_t structure. */
475 /* fd_name must include the trailing \0, so it should be strlen(str) + 1. */
476 /* For simple sanity checking, an upper bound on the size of fd_name is */
478 /* ------------------------------------------------------------------------ */
480 ipf_dstlist_node_add(softc, arg, op, uid)
481 ipf_main_softc_t *softc;
486 ipf_dstl_softc_t *softd = arg;
487 ipf_dstnode_t *node, **nodes;
492 if (op->iplo_size < sizeof(frdest_t)) {
497 err = COPYIN(op->iplo_struct, &dest, sizeof(dest));
503 d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name);
509 switch (dest.fd_addr.adf_family)
519 if (dest.fd_name < -1 || dest.fd_name > 128) {
524 KMALLOCS(node, ipf_dstnode_t *, sizeof(*node) + dest.fd_name);
526 softd->stats.ipls_nomem++;
530 bzero((char *)node, sizeof(*node) + dest.fd_name);
532 bcopy(&dest, &node->ipfd_dest, sizeof(dest));
533 node->ipfd_size = sizeof(*node) + dest.fd_name;
535 if (dest.fd_name > 0) {
537 * fd_name starts out as the length of the string to copy
538 * in (including \0) and ends up being the offset from
541 err = COPYIN((char *)op->iplo_struct + sizeof(dest),
542 node->ipfd_names, dest.fd_name);
545 KFREES(node, node->ipfd_size);
548 node->ipfd_dest.fd_name = 0;
550 node->ipfd_dest.fd_name = -1;
553 if (d->ipld_nodes == d->ipld_maxnodes) {
554 KMALLOCS(nodes, ipf_dstnode_t **,
555 sizeof(*nodes) * (d->ipld_maxnodes + 1));
557 softd->stats.ipls_nomem++;
559 KFREES(node, node->ipfd_size);
562 if (d->ipld_dests != NULL) {
563 bcopy(d->ipld_dests, nodes,
564 sizeof(*nodes) * d->ipld_maxnodes);
565 KFREES(d->ipld_dests, sizeof(*nodes) * d->ipld_nodes);
566 nodes[0]->ipfd_pnext = nodes;
568 d->ipld_dests = nodes;
571 d->ipld_dests[d->ipld_nodes] = node;
574 if (d->ipld_nodes == 1) {
575 node->ipfd_pnext = d->ipld_dests;
576 } else if (d->ipld_nodes > 1) {
577 node->ipfd_pnext = &d->ipld_dests[d->ipld_nodes - 2]->ipfd_next;
579 *node->ipfd_pnext = node;
581 MUTEX_INIT(&node->ipfd_lock, "ipf dst node lock");
582 node->ipfd_uid = uid;
584 if (node->ipfd_dest.fd_name == 0)
585 (void) ipf_resolvedest(softc, node->ipfd_names,
586 &node->ipfd_dest, AF_INET);
588 if (node->ipfd_dest.fd_name == 0 &&
589 node->ipfd_dest.fd_ptr == (void *)-1)
590 (void) ipf_resolvedest(softc, node->ipfd_names,
591 &node->ipfd_dest, AF_INET6);
594 softd->stats.ipls_numnodes++;
600 /* ------------------------------------------------------------------------ */
601 /* Function: ipf_dstlist_node_deref */
602 /* Returns: int - 0 = success, else error */
603 /* Parameters: arg(I) - pointer to local context to use */
604 /* node(I) - pointer to destionation node to free */
606 /* Dereference the use count by one. If it drops to zero then we can assume */
607 /* that it has been removed from any lists/tables and is ripe for freeing. */
608 /* The pointer to context is required for the purpose of maintaining */
610 /* ------------------------------------------------------------------------ */
612 ipf_dstlist_node_deref(arg, node)
616 ipf_dstl_softc_t *softd = arg;
619 MUTEX_ENTER(&node->ipfd_lock);
620 ref = --node->ipfd_ref;
621 MUTEX_EXIT(&node->ipfd_lock);
626 if ((node->ipfd_flags & IPDST_DELETE) != 0)
627 softd->stats.ipls_numderefnodes--;
628 MUTEX_DESTROY(&node->ipfd_lock);
629 KFREES(node, node->ipfd_size);
630 softd->stats.ipls_numnodes--;
636 /* ------------------------------------------------------------------------ */
637 /* Function: ipf_dstlist_node_del */
638 /* Returns: int - 0 = success, else error */
639 /* Parameters: softc(I) - pointer to soft context main structure */
640 /* arg(I) - pointer to local context to use */
641 /* op(I) - pointer to lookup operation data */
642 /* uid(I) - uid of process doing the ioctl */
644 /* Look for a matching destination node on the named table and free it if */
645 /* found. Because the name embedded in the frdest_t is variable in length, */
646 /* it is necessary to allocate some memory locally, to complete this op. */
647 /* ------------------------------------------------------------------------ */
649 ipf_dstlist_node_del(softc, arg, op, uid)
650 ipf_main_softc_t *softc;
655 ipf_dstl_softc_t *softd = arg;
662 d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name);
668 err = COPYIN(op->iplo_struct, &frd, sizeof(frd));
674 size = sizeof(*temp) + frd.fd_name;
675 KMALLOCS(temp, frdest_t *, size);
677 softd->stats.ipls_nomem++;
682 err = COPYIN(op->iplo_struct, temp, size);
688 MUTEX_ENTER(&d->ipld_lock);
689 for (node = *d->ipld_dests; node != NULL; node = node->ipfd_next) {
690 if ((uid != 0) && (node->ipfd_uid != uid))
692 if (node->ipfd_size != size)
694 if (!bcmp(&node->ipfd_dest.fd_ip6, &frd.fd_ip6,
695 size - offsetof(frdest_t, fd_ip6))) {
696 ipf_dstlist_node_free(softd, d, node);
697 MUTEX_EXIT(&d->ipld_lock);
702 MUTEX_EXIT(&d->ipld_lock);
709 /* ------------------------------------------------------------------------ */
710 /* Function: ipf_dstlist_node_free */
712 /* Parameters: softd(I) - pointer to the destination list context */
713 /* d(I) - pointer to destination list */
714 /* node(I) - pointer to node to free */
715 /* Locks: MUTEX(ipld_lock) or WRITE(ipf_poolrw) */
717 /* Free the destination node by first removing it from any lists and then */
718 /* checking if this was the last reference held to the object. While the */
719 /* array of pointers to nodes is compacted, its size isn't reduced (by way */
720 /* of allocating a new smaller one and copying) because the belief is that */
721 /* it is likely the array will again reach that size. */
722 /* ------------------------------------------------------------------------ */
724 ipf_dstlist_node_free(softd, d, node)
725 ipf_dstl_softc_t *softd;
732 * Compact the array of pointers to nodes.
734 for (i = 0; i < d->ipld_nodes; i++)
735 if (d->ipld_dests[i] == node)
737 if (d->ipld_nodes - i > 1) {
738 bcopy(&d->ipld_dests[i + 1], &d->ipld_dests[i],
739 sizeof(*d->ipld_dests) * (d->ipld_nodes - i - 1));
743 if (node->ipfd_pnext != NULL)
744 *node->ipfd_pnext = node->ipfd_next;
745 if (node->ipfd_next != NULL)
746 node->ipfd_next->ipfd_pnext = node->ipfd_pnext;
747 node->ipfd_pnext = NULL;
748 node->ipfd_next = NULL;
750 if ((node->ipfd_flags & IPDST_DELETE) == 0) {
751 softd->stats.ipls_numderefnodes++;
752 node->ipfd_flags |= IPDST_DELETE;
755 ipf_dstlist_node_deref(softd, node);
759 /* ------------------------------------------------------------------------ */
760 /* Function: ipf_dstlist_stats_get */
761 /* Returns: int - 0 = success, else error */
762 /* Parameters: softc(I) - pointer to soft context main structure */
763 /* arg(I) - pointer to local context to use */
764 /* op(I) - pointer to lookup operation data */
766 /* Return the current statistics for destination lists. This may be for all */
767 /* of them or just information pertaining to a particular table. */
768 /* ------------------------------------------------------------------------ */
771 ipf_dstlist_stats_get(softc, arg, op)
772 ipf_main_softc_t *softc;
776 ipf_dstl_softc_t *softd = arg;
777 ipf_dstl_stat_t stats;
778 int unit, i, err = 0;
780 if (op->iplo_size != sizeof(ipf_dstl_stat_t)) {
785 stats = softd->stats;
786 unit = op->iplo_unit;
787 if (unit == IPL_LOGALL) {
788 for (i = 0; i <= IPL_LOGMAX; i++)
789 stats.ipls_list[i] = softd->dstlist[i];
790 } else if (unit >= 0 && unit <= IPL_LOGMAX) {
793 if (op->iplo_name[0] != '\0')
794 ptr = ipf_dstlist_table_find(softd, unit,
797 ptr = softd->dstlist[unit + 1];
798 stats.ipls_list[unit] = ptr;
805 err = COPYOUT(&stats, op->iplo_struct, sizeof(stats));
815 /* ------------------------------------------------------------------------ */
816 /* Function: ipf_dstlist_table_add */
817 /* Returns: int - 0 = success, else error */
818 /* Parameters: softc(I) - pointer to soft context main structure */
819 /* arg(I) - pointer to local context to use */
820 /* op(I) - pointer to lookup operation data */
822 /* Add a new destination table to the list of those available for the given */
823 /* device. Because we seldom operate on these objects (find/add/delete), */
824 /* they are just kept in a simple linked list. */
825 /* ------------------------------------------------------------------------ */
827 ipf_dstlist_table_add(softc, arg, op)
828 ipf_main_softc_t *softc;
832 ipf_dstl_softc_t *softd = arg;
833 ippool_dst_t user, *d, *new;
836 d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name);
842 err = COPYIN(op->iplo_struct, &user, sizeof(user));
848 KMALLOC(new, ippool_dst_t *);
850 softd->stats.ipls_nomem++;
854 bzero((char *)new, sizeof(*new));
856 MUTEX_INIT(&new->ipld_lock, "ipf dst table lock");
858 strncpy(new->ipld_name, op->iplo_name, FR_GROUPLEN);
859 unit = op->iplo_unit;
860 new->ipld_unit = unit;
861 new->ipld_policy = user.ipld_policy;
862 new->ipld_seed = ipf_random();
865 new->ipld_pnext = softd->tails[unit + 1];
866 *softd->tails[unit + 1] = new;
867 softd->tails[unit + 1] = &new->ipld_next;
868 softd->stats.ipls_numlists++;
874 /* ------------------------------------------------------------------------ */
875 /* Function: ipf_dstlist_table_del */
876 /* Returns: int - 0 = success, else error */
877 /* Parameters: softc(I) - pointer to soft context main structure */
878 /* arg(I) - pointer to local context to use */
879 /* op(I) - pointer to lookup operation data */
881 /* Find a named destinstion list table and delete it. If there are other */
882 /* references to it, the caller isn't told. */
883 /* ------------------------------------------------------------------------ */
885 ipf_dstlist_table_del(softc, arg, op)
886 ipf_main_softc_t *softc;
892 d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name);
898 if (d->ipld_dests != NULL) {
903 ipf_dstlist_table_remove(softc, arg, d);
909 /* ------------------------------------------------------------------------ */
910 /* Function: ipf_dstlist_table_remove */
912 /* Parameters: softc(I) - pointer to soft context main structure */
913 /* softd(I) - pointer to the destination list context */
914 /* d(I) - pointer to destination list */
916 /* Remove a given destination list from existance. While the IPDST_DELETE */
917 /* flag is set every time we call this function and the reference count is */
918 /* non-zero, the "numdereflists" counter is always incremented because the */
919 /* decision about whether it will be freed or not is not made here. This */
920 /* means that the only action the code can take here is to treat it as if */
921 /* it will become a detached. */
922 /* ------------------------------------------------------------------------ */
924 ipf_dstlist_table_remove(softc, softd, d)
925 ipf_main_softc_t *softc;
926 ipf_dstl_softc_t *softd;
930 if (softd->tails[d->ipld_unit + 1] == &d->ipld_next)
931 softd->tails[d->ipld_unit + 1] = d->ipld_pnext;
933 if (d->ipld_pnext != NULL)
934 *d->ipld_pnext = d->ipld_next;
935 if (d->ipld_next != NULL)
936 d->ipld_next->ipld_pnext = d->ipld_pnext;
937 d->ipld_pnext = NULL;
940 ipf_dstlist_table_clearnodes(softd, d);
942 softd->stats.ipls_numdereflists++;
943 d->ipld_flags |= IPDST_DELETE;
945 ipf_dstlist_table_deref(softc, softd, d);
949 /* ------------------------------------------------------------------------ */
950 /* Function: ipf_dstlist_table_free */
952 /* Parameters: softd(I) - pointer to the destination list context */
953 /* d(I) - pointer to destination list */
955 /* Free up a destination list data structure and any other memory that was */
956 /* directly allocated as part of creating it. Individual destination list */
957 /* nodes are not freed. It is assumed the caller will have already emptied */
958 /* the destination list. */
959 /* ------------------------------------------------------------------------ */
961 ipf_dstlist_table_free(softd, d)
962 ipf_dstl_softc_t *softd;
965 MUTEX_DESTROY(&d->ipld_lock);
967 if ((d->ipld_flags & IPDST_DELETE) != 0)
968 softd->stats.ipls_numdereflists--;
969 softd->stats.ipls_numlists--;
971 if (d->ipld_dests != NULL) {
972 KFREES(d->ipld_dests,
973 d->ipld_maxnodes * sizeof(*d->ipld_dests));
980 /* ------------------------------------------------------------------------ */
981 /* Function: ipf_dstlist_table_deref */
982 /* Returns: int - 0 = success, else error */
983 /* Parameters: softc(I) - pointer to soft context main structure */
984 /* arg(I) - pointer to local context to use */
985 /* op(I) - pointer to lookup operation data */
987 /* Drops the reference count on a destination list table object and free's */
988 /* it if 0 has been reached. */
989 /* ------------------------------------------------------------------------ */
991 ipf_dstlist_table_deref(softc, arg, table)
992 ipf_main_softc_t *softc;
996 ippool_dst_t *d = table;
1002 ipf_dstlist_table_free(arg, d);
1008 /* ------------------------------------------------------------------------ */
1009 /* Function: ipf_dstlist_table_clearnodes */
1011 /* Parameters: softd(I) - pointer to the destination list context */
1012 /* dst(I) - pointer to destination list */
1014 /* Free all of the destination nodes attached to the given table. */
1015 /* ------------------------------------------------------------------------ */
1017 ipf_dstlist_table_clearnodes(softd, dst)
1018 ipf_dstl_softc_t *softd;
1021 ipf_dstnode_t *node;
1023 if (dst->ipld_dests == NULL)
1026 while ((node = *dst->ipld_dests) != NULL) {
1027 ipf_dstlist_node_free(softd, dst, node);
1032 /* ------------------------------------------------------------------------ */
1033 /* Function: ipf_dstlist_table_find */
1034 /* Returns: int - 0 = success, else error */
1035 /* Parameters: arg(I) - pointer to local context to use */
1036 /* unit(I) - device we are working with */
1037 /* name(I) - destination table name to find */
1039 /* Return a pointer to a destination table that matches the unit+name that */
1041 /* ------------------------------------------------------------------------ */
1043 ipf_dstlist_table_find(arg, unit, name)
1048 ipf_dstl_softc_t *softd = arg;
1051 for (d = softd->dstlist[unit + 1]; d != NULL; d = d->ipld_next) {
1052 if ((d->ipld_unit == unit) &&
1053 !strncmp(d->ipld_name, name, FR_GROUPLEN)) {
1062 /* ------------------------------------------------------------------------ */
1063 /* Function: ipf_dstlist_select_ref */
1064 /* Returns: void * - NULL = failure, else pointer to table */
1065 /* Parameters: arg(I) - pointer to local context to use */
1066 /* unit(I) - device we are working with */
1067 /* name(I) - destination table name to find */
1069 /* Attempt to find a destination table that matches the name passed in and */
1070 /* if successful, bump up the reference count on it because we intend to */
1071 /* store the pointer to it somewhere else. */
1072 /* ------------------------------------------------------------------------ */
1074 ipf_dstlist_select_ref(arg, unit, name)
1081 d = ipf_dstlist_table_find(arg, unit, name);
1083 MUTEX_ENTER(&d->ipld_lock);
1085 MUTEX_EXIT(&d->ipld_lock);
1091 /* ------------------------------------------------------------------------ */
1092 /* Function: ipf_dstlist_select */
1093 /* Returns: void * - NULL = failure, else pointer to table */
1094 /* Parameters: fin(I) - pointer to packet information */
1095 /* d(I) - pointer to destination list */
1097 /* Find the next node in the destination list to be used according to the */
1098 /* defined policy. Of these, "connection" is the most expensive policy to */
1099 /* implement as it always looks for the node with the least number of */
1100 /* connections associated with it. */
1102 /* The hashes exclude the port numbers so that all protocols map to the */
1103 /* same destination. Otherwise, someone doing a ping would target a */
1104 /* different server than their TCP connection, etc. MD-5 is used to */
1105 /* transform the addressese into something random that the other end could */
1106 /* not easily guess and use in an attack. ipld_seed introduces an unknown */
1107 /* into the hash calculation to increase the difficult of an attacker */
1108 /* guessing the bucket. */
1110 /* One final comment: mixing different address families in a single pool */
1111 /* will currently result in failures as the address family of the node is */
1112 /* only matched up with that in the packet as the last step. While this can */
1113 /* be coded around for the weighted connection and round-robin models, it */
1114 /* cannot be supported for the hash/random models as they do not search and */
1115 /* nor is the algorithm conducive to searching. */
1116 /* ------------------------------------------------------------------------ */
1117 static ipf_dstnode_t *
1118 ipf_dstlist_select(fin, d)
1122 ipf_dstnode_t *node, *sel;
1129 if (d->ipld_dests == NULL || *d->ipld_dests == NULL)
1132 family = fin->fin_family;
1134 MUTEX_ENTER(&d->ipld_lock);
1136 switch (d->ipld_policy)
1138 case IPLDP_ROUNDROBIN:
1139 sel = d->ipld_selected;
1141 sel = *d->ipld_dests;
1143 sel = sel->ipfd_next;
1145 sel = *d->ipld_dests;
1149 case IPLDP_CONNECTION:
1150 if (d->ipld_selected == NULL) {
1151 sel = *d->ipld_dests;
1155 sel = d->ipld_selected;
1156 connects = 0x7fffffff;
1157 node = sel->ipfd_next;
1159 node = *d->ipld_dests;
1160 while (node != d->ipld_selected) {
1161 if (node->ipfd_states == 0) {
1165 if (node->ipfd_states < connects) {
1167 connects = node->ipfd_states;
1169 node = node->ipfd_next;
1171 node = *d->ipld_dests;
1176 x = ipf_random() % d->ipld_nodes;
1177 sel = d->ipld_dests[x];
1182 MD5Update(&ctx, (u_char *)&d->ipld_seed, sizeof(d->ipld_seed));
1183 MD5Update(&ctx, (u_char *)&fin->fin_src6,
1184 sizeof(fin->fin_src6));
1185 MD5Update(&ctx, (u_char *)&fin->fin_dst6,
1186 sizeof(fin->fin_dst6));
1187 MD5Final((u_char *)hash, &ctx);
1188 x = hash[0] % d->ipld_nodes;
1189 sel = d->ipld_dests[x];
1192 case IPLDP_SRCHASH :
1194 MD5Update(&ctx, (u_char *)&d->ipld_seed, sizeof(d->ipld_seed));
1195 MD5Update(&ctx, (u_char *)&fin->fin_src6,
1196 sizeof(fin->fin_src6));
1197 MD5Final((u_char *)hash, &ctx);
1198 x = hash[0] % d->ipld_nodes;
1199 sel = d->ipld_dests[x];
1202 case IPLDP_DSTHASH :
1204 MD5Update(&ctx, (u_char *)&d->ipld_seed, sizeof(d->ipld_seed));
1205 MD5Update(&ctx, (u_char *)&fin->fin_dst6,
1206 sizeof(fin->fin_dst6));
1207 MD5Final((u_char *)hash, &ctx);
1208 x = hash[0] % d->ipld_nodes;
1209 sel = d->ipld_dests[x];
1217 if (sel->ipfd_dest.fd_addr.adf_family != family)
1219 d->ipld_selected = sel;
1221 MUTEX_EXIT(&d->ipld_lock);
1227 /* ------------------------------------------------------------------------ */
1228 /* Function: ipf_dstlist_select_node */
1229 /* Returns: int - -1 == failure, 0 == success */
1230 /* Parameters: fin(I) - pointer to packet information */
1231 /* group(I) - destination pool to search */
1232 /* addr(I) - pointer to store selected address */
1233 /* pfdp(O) - pointer to storage for selected destination node */
1235 /* This function is only responsible for obtaining the next IP address for */
1236 /* use and storing it in the caller's address space (addr). "addr" is only */
1237 /* used for storage if pfdp is NULL. No permanent reference is currently */
1238 /* kept on the node. */
1239 /* ------------------------------------------------------------------------ */
1241 ipf_dstlist_select_node(fin, group, addr, pfdp)
1248 ipf_main_softc_t *softc = fin->fin_main_soft;
1250 ippool_dst_t *d = group;
1251 ipf_dstnode_t *node;
1254 READ_ENTER(&softc->ipf_poolrw);
1256 node = ipf_dstlist_select(fin, d);
1258 RWLOCK_EXIT(&softc->ipf_poolrw);
1263 bcopy(&node->ipfd_dest, pfdp, sizeof(*pfdp));
1265 if (fin->fin_family == AF_INET) {
1266 addr[0] = node->ipfd_dest.fd_addr.adf_addr.i6[0];
1267 } else if (fin->fin_family == AF_INET6) {
1268 addr[0] = node->ipfd_dest.fd_addr.adf_addr.i6[0];
1269 addr[1] = node->ipfd_dest.fd_addr.adf_addr.i6[1];
1270 addr[2] = node->ipfd_dest.fd_addr.adf_addr.i6[2];
1271 addr[3] = node->ipfd_dest.fd_addr.adf_addr.i6[3];
1275 fdp = &node->ipfd_dest;
1276 if (fdp->fd_ptr == NULL)
1277 fdp->fd_ptr = fin->fin_ifp;
1279 MUTEX_ENTER(&node->ipfd_lock);
1280 node->ipfd_states++;
1281 MUTEX_EXIT(&node->ipfd_lock);
1283 RWLOCK_EXIT(&softc->ipf_poolrw);
1289 /* ------------------------------------------------------------------------ */
1290 /* Function: ipf_dstlist_expire */
1292 /* Parameters: softc(I) - pointer to soft context main structure */
1293 /* arg(I) - pointer to local context to use */
1295 /* There are currently no objects to expire in destination lists. */
1296 /* ------------------------------------------------------------------------ */
1298 ipf_dstlist_expire(softc, arg)
1299 ipf_main_softc_t *softc;
1306 /* ------------------------------------------------------------------------ */
1307 /* Function: ipf_dstlist_sync */
1309 /* Parameters: softc(I) - pointer to soft context main structure */
1310 /* arg(I) - pointer to local context to use */
1312 /* When a network interface appears or disappears, we need to revalidate */
1313 /* all of the network interface names that have been configured as a target */
1314 /* in a destination list. */
1315 /* ------------------------------------------------------------------------ */
1317 ipf_dstlist_sync(softc, arg)
1318 ipf_main_softc_t *softc;
1321 ipf_dstl_softc_t *softd = arg;
1322 ipf_dstnode_t *node;
1327 for (i = 0; i < IPL_LOGMAX; i++) {
1328 for (list = softd->dstlist[i]; list != NULL;
1329 list = list->ipld_next) {
1330 for (j = 0; j < list->ipld_maxnodes; j++) {
1331 node = list->ipld_dests[j];
1334 if (node->ipfd_dest.fd_name == -1)
1336 (void) ipf_resolvedest(softc,