2 * Copyright (C) 2012 by Darren Reed.
4 * See the IPFILTER.LICENCE file for details on licencing.
6 #if defined(KERNEL) || defined(_KERNEL)
13 # define _PROTO_NET_H_
15 #include <sys/errno.h>
16 #include <sys/types.h>
17 #include <sys/param.h>
19 #if !defined(_KERNEL) && !defined(__KERNEL__)
30 # include <sys/systm.h>
31 # if defined(NetBSD) && (__NetBSD_Version__ >= 104000000)
32 # include <sys/proc.h>
37 # include <sys/protosw.h>
39 #include <sys/socket.h>
40 #if defined(_KERNEL) && (!defined(__SVR4) && !defined(__svr4__))
41 # include <sys/mbuf.h>
43 #if defined(__SVR4) || defined(__svr4__)
44 # include <sys/filio.h>
45 # include <sys/byteorder.h>
47 # include <sys/dditypes.h>
49 # include <sys/stream.h>
50 # include <sys/kmem.h>
52 #if defined(__FreeBSD_version) && (__FreeBSD_version >= 300000)
53 # include <sys/malloc.h>
57 #include <netinet/in.h>
59 #include "netinet/ip_compat.h"
60 #include "netinet/ip_fil.h"
61 #include "netinet/ip_nat.h"
62 #include "netinet/ip_lookup.h"
63 #include "netinet/ip_dstlist.h"
74 static const char rcsid[] = "@(#)$Id: ip_dstlist.c,v 1.13.2.12 2012/07/20 08:40:19 darren_r Exp $";
77 typedef struct ipf_dstl_softc_s {
78 ippool_dst_t *dstlist[LOOKUP_POOL_SZ];
79 ippool_dst_t **tails[LOOKUP_POOL_SZ];
80 ipf_dstl_stat_t stats;
84 static void *ipf_dstlist_soft_create __P((ipf_main_softc_t *));
85 static void ipf_dstlist_soft_destroy __P((ipf_main_softc_t *, void *));
86 static int ipf_dstlist_soft_init __P((ipf_main_softc_t *, void *));
87 static void ipf_dstlist_soft_fini __P((ipf_main_softc_t *, void *));
88 static int ipf_dstlist_addr_find __P((ipf_main_softc_t *, void *, int,
90 static size_t ipf_dstlist_flush __P((ipf_main_softc_t *, void *,
92 static int ipf_dstlist_iter_deref __P((ipf_main_softc_t *, void *, int, int,
94 static int ipf_dstlist_iter_next __P((ipf_main_softc_t *, void *, ipftoken_t *,
96 static int ipf_dstlist_node_add __P((ipf_main_softc_t *, void *,
97 iplookupop_t *, int));
98 static int ipf_dstlist_node_del __P((ipf_main_softc_t *, void *,
99 iplookupop_t *, int));
100 static int ipf_dstlist_stats_get __P((ipf_main_softc_t *, void *,
102 static int ipf_dstlist_table_add __P((ipf_main_softc_t *, void *,
104 static int ipf_dstlist_table_del __P((ipf_main_softc_t *, void *,
106 static int ipf_dstlist_table_deref __P((ipf_main_softc_t *, void *, void *));
107 static void *ipf_dstlist_table_find __P((void *, int, char *));
108 static void ipf_dstlist_table_free __P((ipf_dstl_softc_t *, ippool_dst_t *));
109 static void ipf_dstlist_table_remove __P((ipf_main_softc_t *,
110 ipf_dstl_softc_t *, ippool_dst_t *));
111 static void ipf_dstlist_table_clearnodes __P((ipf_dstl_softc_t *,
113 static ipf_dstnode_t *ipf_dstlist_select __P((fr_info_t *, ippool_dst_t *));
114 static void *ipf_dstlist_select_ref __P((void *, int, char *));
115 static void ipf_dstlist_node_free __P((ipf_dstl_softc_t *, ippool_dst_t *, ipf_dstnode_t *));
116 static int ipf_dstlist_node_deref __P((void *, ipf_dstnode_t *));
117 static void ipf_dstlist_expire __P((ipf_main_softc_t *, void *));
118 static void ipf_dstlist_sync __P((ipf_main_softc_t *, void *));
120 ipf_lookup_t ipf_dstlist_backend = {
122 ipf_dstlist_soft_create,
123 ipf_dstlist_soft_destroy,
124 ipf_dstlist_soft_init,
125 ipf_dstlist_soft_fini,
126 ipf_dstlist_addr_find,
128 ipf_dstlist_iter_deref,
129 ipf_dstlist_iter_next,
130 ipf_dstlist_node_add,
131 ipf_dstlist_node_del,
132 ipf_dstlist_stats_get,
133 ipf_dstlist_table_add,
134 ipf_dstlist_table_del,
135 ipf_dstlist_table_deref,
136 ipf_dstlist_table_find,
137 ipf_dstlist_select_ref,
138 ipf_dstlist_select_node,
144 /* ------------------------------------------------------------------------ */
145 /* Function: ipf_dstlist_soft_create */
146 /* Returns: int - 0 = success, else error */
147 /* Parameters: softc(I) - pointer to soft context main structure */
149 /* Allocating a chunk of memory filled with 0's is enough for the current */
150 /* soft context used with destination lists. */
151 /* ------------------------------------------------------------------------ */
153 ipf_dstlist_soft_create(softc)
154 ipf_main_softc_t *softc;
156 ipf_dstl_softc_t *softd;
159 KMALLOC(softd, ipf_dstl_softc_t *);
165 bzero((char *)softd, sizeof(*softd));
166 for (i = 0; i <= IPL_LOGMAX; i++)
167 softd->tails[i] = &softd->dstlist[i];
173 /* ------------------------------------------------------------------------ */
174 /* Function: ipf_dstlist_soft_destroy */
176 /* Parameters: softc(I) - pointer to soft context main structure */
177 /* arg(I) - pointer to local context to use */
179 /* For destination lists, the only thing we have to do when destroying the */
180 /* soft context is free it! */
181 /* ------------------------------------------------------------------------ */
183 ipf_dstlist_soft_destroy(softc, arg)
184 ipf_main_softc_t *softc;
187 ipf_dstl_softc_t *softd = arg;
193 /* ------------------------------------------------------------------------ */
194 /* Function: ipf_dstlist_soft_init */
195 /* Returns: int - 0 = success, else error */
196 /* Parameters: softc(I) - pointer to soft context main structure */
197 /* arg(I) - pointer to local context to use */
199 /* There is currently no soft context for destination list management. */
200 /* ------------------------------------------------------------------------ */
202 ipf_dstlist_soft_init(softc, arg)
203 ipf_main_softc_t *softc;
210 /* ------------------------------------------------------------------------ */
211 /* Function: ipf_dstlist_soft_fini */
213 /* Parameters: softc(I) - pointer to soft context main structure */
214 /* arg(I) - pointer to local context to use */
216 /* There is currently no soft context for destination list management. */
217 /* ------------------------------------------------------------------------ */
219 ipf_dstlist_soft_fini(softc, arg)
220 ipf_main_softc_t *softc;
223 ipf_dstl_softc_t *softd = arg;
226 for (i = -1; i <= IPL_LOGMAX; i++) {
227 while (softd->dstlist[i + 1] != NULL) {
228 ipf_dstlist_table_remove(softc, softd,
229 softd->dstlist[i + 1]);
233 ASSERT(softd->stats.ipls_numderefnodes == 0);
237 /* ------------------------------------------------------------------------ */
238 /* Function: ipf_dstlist_addr_find */
239 /* Returns: int - 0 = success, else error */
240 /* Parameters: softc(I) - pointer to soft context main structure */
241 /* arg1(I) - pointer to local context to use */
242 /* arg2(I) - pointer to local context to use */
243 /* arg3(I) - pointer to local context to use */
244 /* arg4(I) - pointer to local context to use */
246 /* There is currently no such thing as searching a destination list for an */
247 /* address so this function becomes a no-op. Its presence is required as */
248 /* ipf_lookup_res_name() stores the "addr_find" function pointer in the */
249 /* pointer passed in to it as funcptr, although it could be a generic null- */
250 /* op function rather than a specific one. */
251 /* ------------------------------------------------------------------------ */
254 ipf_dstlist_addr_find(softc, arg1, arg2, arg3, arg4)
255 ipf_main_softc_t *softc;
264 /* ------------------------------------------------------------------------ */
265 /* Function: ipf_dstlist_flush */
266 /* Returns: int - number of objects deleted */
267 /* Parameters: softc(I) - pointer to soft context main structure */
268 /* arg(I) - pointer to local context to use */
269 /* fop(I) - pointer to lookup flush operation data */
271 /* Flush all of the destination tables that match the data passed in with */
272 /* the iplookupflush_t. There are two ways to match objects: the device for */
273 /* which they are to be used with and their name. */
274 /* ------------------------------------------------------------------------ */
276 ipf_dstlist_flush(softc, arg, fop)
277 ipf_main_softc_t *softc;
279 iplookupflush_t *fop;
281 ipf_dstl_softc_t *softd = arg;
282 ippool_dst_t *node, *next;
285 for (n = 0, i = -1; i <= IPL_LOGMAX; i++) {
286 if (fop->iplf_unit != IPLT_ALL && fop->iplf_unit != i)
288 for (node = softd->dstlist[i + 1]; node != NULL; node = next) {
289 next = node->ipld_next;
291 if ((*fop->iplf_name != '\0') &&
292 strncmp(fop->iplf_name, node->ipld_name,
296 ipf_dstlist_table_remove(softc, softd, node);
304 /* ------------------------------------------------------------------------ */
305 /* Function: ipf_dstlist_iter_deref */
306 /* Returns: int - 0 = success, else error */
307 /* Parameters: softc(I) - pointer to soft context main structure */
308 /* arg(I) - pointer to local context to use */
309 /* otype(I) - type of data structure to iterate through */
310 /* unit(I) - device we are working with */
311 /* data(I) - address of object in kernel space */
313 /* This function is called when the iteration token is being free'd and is */
314 /* responsible for dropping the reference count of the structure it points */
316 /* ------------------------------------------------------------------------ */
318 ipf_dstlist_iter_deref(softc, arg, otype, unit, data)
319 ipf_main_softc_t *softc;
329 if (unit < -1 || unit > IPL_LOGMAX) {
336 case IPFLOOKUPITER_LIST :
337 ipf_dstlist_table_deref(softc, arg, (ippool_dst_t *)data);
340 case IPFLOOKUPITER_NODE :
341 ipf_dstlist_node_deref(arg, (ipf_dstnode_t *)data);
349 /* ------------------------------------------------------------------------ */
350 /* Function: ipf_dstlist_iter_next */
351 /* Returns: int - 0 = success, else error */
352 /* Parameters: softc(I) - pointer to soft context main structure */
353 /* arg(I) - pointer to local context to use */
354 /* op(I) - pointer to lookup operation data */
355 /* uid(I) - uid of process doing the ioctl */
357 /* This function is responsible for either selecting the next destination */
358 /* list or node on a destination list to be returned as a user process */
359 /* iterates through the list of destination lists or nodes. */
360 /* ------------------------------------------------------------------------ */
362 ipf_dstlist_iter_next(softc, arg, token, iter)
363 ipf_main_softc_t *softc;
366 ipflookupiter_t *iter;
368 ipf_dstnode_t zn, *nextnode = NULL, *node = NULL;
369 ippool_dst_t zero, *next = NULL, *dsttab = NULL;
370 ipf_dstl_softc_t *softd = arg;
374 switch (iter->ili_otype)
376 case IPFLOOKUPITER_LIST :
377 dsttab = token->ipt_data;
378 if (dsttab == NULL) {
379 next = softd->dstlist[(int)iter->ili_unit + 1];
381 next = dsttab->ipld_next;
385 ATOMIC_INC32(next->ipld_ref);
386 token->ipt_data = next;
387 hint = next->ipld_next;
389 bzero((char *)&zero, sizeof(zero));
391 token->ipt_data = NULL;
396 case IPFLOOKUPITER_NODE :
397 node = token->ipt_data;
399 dsttab = ipf_dstlist_table_find(arg, iter->ili_unit,
401 if (dsttab == NULL) {
406 if (dsttab->ipld_dests == NULL)
409 nextnode = *dsttab->ipld_dests;
413 nextnode = node->ipfd_next;
416 if (nextnode != NULL) {
417 MUTEX_ENTER(&nextnode->ipfd_lock);
418 nextnode->ipfd_ref++;
419 MUTEX_EXIT(&nextnode->ipfd_lock);
420 token->ipt_data = nextnode;
421 hint = nextnode->ipfd_next;
423 bzero((char *)&zn, sizeof(zn));
425 token->ipt_data = NULL;
438 switch (iter->ili_otype)
440 case IPFLOOKUPITER_LIST :
442 ipf_dstlist_table_deref(softc, arg, dsttab);
443 err = COPYOUT(next, iter->ili_data, sizeof(*next));
450 case IPFLOOKUPITER_NODE :
452 ipf_dstlist_node_deref(arg, node);
453 err = COPYOUT(nextnode, iter->ili_data, sizeof(*nextnode));
462 ipf_token_mark_complete(token);
468 /* ------------------------------------------------------------------------ */
469 /* Function: ipf_dstlist_node_add */
470 /* Returns: int - 0 = success, else error */
471 /* Parameters: softc(I) - pointer to soft context main structure */
472 /* arg(I) - pointer to local context to use */
473 /* op(I) - pointer to lookup operation data */
474 /* uid(I) - uid of process doing the ioctl */
475 /* Locks: WRITE(ipf_poolrw) */
477 /* Add a new node to a destination list. To do this, we only copy in the */
478 /* frdest_t structure because that contains the only data required from the */
479 /* application to create a new node. The frdest_t doesn't contain the name */
480 /* itself. When loading filter rules, fd_name is a 'pointer' to the name. */
481 /* In this case, the 'pointer' does not work, instead it is the length of */
482 /* the name and the name is immediately following the frdest_t structure. */
483 /* fd_name must include the trailing \0, so it should be strlen(str) + 1. */
484 /* For simple sanity checking, an upper bound on the size of fd_name is */
486 /* ------------------------------------------------------------------------ */
488 ipf_dstlist_node_add(softc, arg, op, uid)
489 ipf_main_softc_t *softc;
494 ipf_dstl_softc_t *softd = arg;
495 ipf_dstnode_t *node, **nodes;
500 if (op->iplo_size < sizeof(frdest_t)) {
505 err = COPYIN(op->iplo_struct, &dest, sizeof(dest));
511 d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name);
517 switch (dest.fd_addr.adf_family)
527 if (dest.fd_name < -1 || dest.fd_name > 128) {
532 KMALLOCS(node, ipf_dstnode_t *, sizeof(*node) + dest.fd_name);
534 softd->stats.ipls_nomem++;
538 bzero((char *)node, sizeof(*node) + dest.fd_name);
540 bcopy(&dest, &node->ipfd_dest, sizeof(dest));
541 node->ipfd_size = sizeof(*node) + dest.fd_name;
543 if (dest.fd_name > 0) {
545 * fd_name starts out as the length of the string to copy
546 * in (including \0) and ends up being the offset from
549 err = COPYIN((char *)op->iplo_struct + sizeof(dest),
550 node->ipfd_names, dest.fd_name);
553 KFREES(node, node->ipfd_size);
556 node->ipfd_dest.fd_name = 0;
558 node->ipfd_dest.fd_name = -1;
561 if (d->ipld_nodes == d->ipld_maxnodes) {
562 KMALLOCS(nodes, ipf_dstnode_t **,
563 sizeof(*nodes) * (d->ipld_maxnodes + 1));
565 softd->stats.ipls_nomem++;
567 KFREES(node, node->ipfd_size);
570 if (d->ipld_dests != NULL) {
571 bcopy(d->ipld_dests, nodes,
572 sizeof(*nodes) * d->ipld_maxnodes);
573 KFREES(d->ipld_dests, sizeof(*nodes) * d->ipld_nodes);
574 nodes[0]->ipfd_pnext = nodes;
576 d->ipld_dests = nodes;
579 d->ipld_dests[d->ipld_nodes] = node;
582 if (d->ipld_nodes == 1) {
583 node->ipfd_pnext = d->ipld_dests;
584 } else if (d->ipld_nodes > 1) {
585 node->ipfd_pnext = &d->ipld_dests[d->ipld_nodes - 2]->ipfd_next;
587 *node->ipfd_pnext = node;
589 MUTEX_INIT(&node->ipfd_lock, "ipf dst node lock");
590 node->ipfd_uid = uid;
592 if (node->ipfd_dest.fd_name == 0)
593 (void) ipf_resolvedest(softc, node->ipfd_names,
594 &node->ipfd_dest, AF_INET);
596 if (node->ipfd_dest.fd_name == 0 &&
597 node->ipfd_dest.fd_ptr == (void *)-1)
598 (void) ipf_resolvedest(softc, node->ipfd_names,
599 &node->ipfd_dest, AF_INET6);
602 softd->stats.ipls_numnodes++;
608 /* ------------------------------------------------------------------------ */
609 /* Function: ipf_dstlist_node_deref */
610 /* Returns: int - 0 = success, else error */
611 /* Parameters: arg(I) - pointer to local context to use */
612 /* node(I) - pointer to destionation node to free */
614 /* Dereference the use count by one. If it drops to zero then we can assume */
615 /* that it has been removed from any lists/tables and is ripe for freeing. */
616 /* The pointer to context is required for the purpose of maintaining */
618 /* ------------------------------------------------------------------------ */
620 ipf_dstlist_node_deref(arg, node)
624 ipf_dstl_softc_t *softd = arg;
627 MUTEX_ENTER(&node->ipfd_lock);
628 ref = --node->ipfd_ref;
629 MUTEX_EXIT(&node->ipfd_lock);
634 if ((node->ipfd_flags & IPDST_DELETE) != 0)
635 softd->stats.ipls_numderefnodes--;
636 MUTEX_DESTROY(&node->ipfd_lock);
637 KFREES(node, node->ipfd_size);
638 softd->stats.ipls_numnodes--;
644 /* ------------------------------------------------------------------------ */
645 /* Function: ipf_dstlist_node_del */
646 /* Returns: int - 0 = success, else error */
647 /* Parameters: softc(I) - pointer to soft context main structure */
648 /* arg(I) - pointer to local context to use */
649 /* op(I) - pointer to lookup operation data */
650 /* uid(I) - uid of process doing the ioctl */
652 /* Look for a matching destination node on the named table and free it if */
653 /* found. Because the name embedded in the frdest_t is variable in length, */
654 /* it is necessary to allocate some memory locally, to complete this op. */
655 /* ------------------------------------------------------------------------ */
657 ipf_dstlist_node_del(softc, arg, op, uid)
658 ipf_main_softc_t *softc;
663 ipf_dstl_softc_t *softd = arg;
670 d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name);
676 err = COPYIN(op->iplo_struct, &frd, sizeof(frd));
682 size = sizeof(*temp) + frd.fd_name;
683 KMALLOCS(temp, frdest_t *, size);
685 softd->stats.ipls_nomem++;
690 err = COPYIN(op->iplo_struct, temp, size);
696 MUTEX_ENTER(&d->ipld_lock);
697 for (node = *d->ipld_dests; node != NULL; node = node->ipfd_next) {
698 if ((uid != 0) && (node->ipfd_uid != uid))
700 if (node->ipfd_size != size)
702 if (!bcmp(&node->ipfd_dest.fd_ip6, &frd.fd_ip6,
703 size - offsetof(frdest_t, fd_ip6))) {
704 ipf_dstlist_node_free(softd, d, node);
705 MUTEX_EXIT(&d->ipld_lock);
710 MUTEX_EXIT(&d->ipld_lock);
717 /* ------------------------------------------------------------------------ */
718 /* Function: ipf_dstlist_node_free */
720 /* Parameters: softd(I) - pointer to the destination list context */
721 /* d(I) - pointer to destination list */
722 /* node(I) - pointer to node to free */
723 /* Locks: MUTEX(ipld_lock) or WRITE(ipf_poolrw) */
725 /* Free the destination node by first removing it from any lists and then */
726 /* checking if this was the last reference held to the object. While the */
727 /* array of pointers to nodes is compacted, its size isn't reduced (by way */
728 /* of allocating a new smaller one and copying) because the belief is that */
729 /* it is likely the array will again reach that size. */
730 /* ------------------------------------------------------------------------ */
732 ipf_dstlist_node_free(softd, d, node)
733 ipf_dstl_softc_t *softd;
740 * Compact the array of pointers to nodes.
742 for (i = 0; i < d->ipld_nodes; i++)
743 if (d->ipld_dests[i] == node)
745 if (d->ipld_nodes - i > 1) {
746 bcopy(&d->ipld_dests[i + 1], &d->ipld_dests[i],
747 sizeof(*d->ipld_dests) * (d->ipld_nodes - i - 1));
751 if (node->ipfd_pnext != NULL)
752 *node->ipfd_pnext = node->ipfd_next;
753 if (node->ipfd_next != NULL)
754 node->ipfd_next->ipfd_pnext = node->ipfd_pnext;
755 node->ipfd_pnext = NULL;
756 node->ipfd_next = NULL;
758 if ((node->ipfd_flags & IPDST_DELETE) == 0) {
759 softd->stats.ipls_numderefnodes++;
760 node->ipfd_flags |= IPDST_DELETE;
763 ipf_dstlist_node_deref(softd, node);
767 /* ------------------------------------------------------------------------ */
768 /* Function: ipf_dstlist_stats_get */
769 /* Returns: int - 0 = success, else error */
770 /* Parameters: softc(I) - pointer to soft context main structure */
771 /* arg(I) - pointer to local context to use */
772 /* op(I) - pointer to lookup operation data */
774 /* Return the current statistics for destination lists. This may be for all */
775 /* of them or just information pertaining to a particular table. */
776 /* ------------------------------------------------------------------------ */
779 ipf_dstlist_stats_get(softc, arg, op)
780 ipf_main_softc_t *softc;
784 ipf_dstl_softc_t *softd = arg;
785 ipf_dstl_stat_t stats;
786 int unit, i, err = 0;
788 if (op->iplo_size != sizeof(ipf_dstl_stat_t)) {
793 stats = softd->stats;
794 unit = op->iplo_unit;
795 if (unit == IPL_LOGALL) {
796 for (i = 0; i <= IPL_LOGMAX; i++)
797 stats.ipls_list[i] = softd->dstlist[i];
798 } else if (unit >= 0 && unit <= IPL_LOGMAX) {
801 if (op->iplo_name[0] != '\0')
802 ptr = ipf_dstlist_table_find(softd, unit,
805 ptr = softd->dstlist[unit + 1];
806 stats.ipls_list[unit] = ptr;
813 err = COPYOUT(&stats, op->iplo_struct, sizeof(stats));
823 /* ------------------------------------------------------------------------ */
824 /* Function: ipf_dstlist_table_add */
825 /* Returns: int - 0 = success, else error */
826 /* Parameters: softc(I) - pointer to soft context main structure */
827 /* arg(I) - pointer to local context to use */
828 /* op(I) - pointer to lookup operation data */
830 /* Add a new destination table to the list of those available for the given */
831 /* device. Because we seldom operate on these objects (find/add/delete), */
832 /* they are just kept in a simple linked list. */
833 /* ------------------------------------------------------------------------ */
835 ipf_dstlist_table_add(softc, arg, op)
836 ipf_main_softc_t *softc;
840 ipf_dstl_softc_t *softd = arg;
841 ippool_dst_t user, *d, *new;
844 d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name);
850 err = COPYIN(op->iplo_struct, &user, sizeof(user));
856 KMALLOC(new, ippool_dst_t *);
858 softd->stats.ipls_nomem++;
862 bzero((char *)new, sizeof(*new));
864 MUTEX_INIT(&new->ipld_lock, "ipf dst table lock");
866 strncpy(new->ipld_name, op->iplo_name, FR_GROUPLEN);
867 unit = op->iplo_unit;
868 new->ipld_unit = unit;
869 new->ipld_policy = user.ipld_policy;
870 new->ipld_seed = ipf_random();
873 new->ipld_pnext = softd->tails[unit + 1];
874 *softd->tails[unit + 1] = new;
875 softd->tails[unit + 1] = &new->ipld_next;
876 softd->stats.ipls_numlists++;
882 /* ------------------------------------------------------------------------ */
883 /* Function: ipf_dstlist_table_del */
884 /* Returns: int - 0 = success, else error */
885 /* Parameters: softc(I) - pointer to soft context main structure */
886 /* arg(I) - pointer to local context to use */
887 /* op(I) - pointer to lookup operation data */
889 /* Find a named destinstion list table and delete it. If there are other */
890 /* references to it, the caller isn't told. */
891 /* ------------------------------------------------------------------------ */
893 ipf_dstlist_table_del(softc, arg, op)
894 ipf_main_softc_t *softc;
900 d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name);
906 if (d->ipld_dests != NULL) {
911 ipf_dstlist_table_remove(softc, arg, d);
917 /* ------------------------------------------------------------------------ */
918 /* Function: ipf_dstlist_table_remove */
920 /* Parameters: softc(I) - pointer to soft context main structure */
921 /* softd(I) - pointer to the destination list context */
922 /* d(I) - pointer to destination list */
924 /* Remove a given destination list from existance. While the IPDST_DELETE */
925 /* flag is set every time we call this function and the reference count is */
926 /* non-zero, the "numdereflists" counter is always incremented because the */
927 /* decision about whether it will be freed or not is not made here. This */
928 /* means that the only action the code can take here is to treat it as if */
929 /* it will become a detached. */
930 /* ------------------------------------------------------------------------ */
932 ipf_dstlist_table_remove(softc, softd, d)
933 ipf_main_softc_t *softc;
934 ipf_dstl_softc_t *softd;
938 if (softd->tails[d->ipld_unit + 1] == &d->ipld_next)
939 softd->tails[d->ipld_unit + 1] = d->ipld_pnext;
941 if (d->ipld_pnext != NULL)
942 *d->ipld_pnext = d->ipld_next;
943 if (d->ipld_next != NULL)
944 d->ipld_next->ipld_pnext = d->ipld_pnext;
945 d->ipld_pnext = NULL;
948 ipf_dstlist_table_clearnodes(softd, d);
950 softd->stats.ipls_numdereflists++;
951 d->ipld_flags |= IPDST_DELETE;
953 ipf_dstlist_table_deref(softc, softd, d);
957 /* ------------------------------------------------------------------------ */
958 /* Function: ipf_dstlist_table_free */
960 /* Parameters: softd(I) - pointer to the destination list context */
961 /* d(I) - pointer to destination list */
963 /* Free up a destination list data structure and any other memory that was */
964 /* directly allocated as part of creating it. Individual destination list */
965 /* nodes are not freed. It is assumed the caller will have already emptied */
966 /* the destination list. */
967 /* ------------------------------------------------------------------------ */
969 ipf_dstlist_table_free(softd, d)
970 ipf_dstl_softc_t *softd;
973 MUTEX_DESTROY(&d->ipld_lock);
975 if ((d->ipld_flags & IPDST_DELETE) != 0)
976 softd->stats.ipls_numdereflists--;
977 softd->stats.ipls_numlists--;
979 if (d->ipld_dests != NULL) {
980 KFREES(d->ipld_dests,
981 d->ipld_maxnodes * sizeof(*d->ipld_dests));
988 /* ------------------------------------------------------------------------ */
989 /* Function: ipf_dstlist_table_deref */
990 /* Returns: int - 0 = success, else error */
991 /* Parameters: softc(I) - pointer to soft context main structure */
992 /* arg(I) - pointer to local context to use */
993 /* op(I) - pointer to lookup operation data */
995 /* Drops the reference count on a destination list table object and free's */
996 /* it if 0 has been reached. */
997 /* ------------------------------------------------------------------------ */
999 ipf_dstlist_table_deref(softc, arg, table)
1000 ipf_main_softc_t *softc;
1004 ippool_dst_t *d = table;
1007 if (d->ipld_ref > 0)
1010 ipf_dstlist_table_free(arg, d);
1016 /* ------------------------------------------------------------------------ */
1017 /* Function: ipf_dstlist_table_clearnodes */
1019 /* Parameters: softd(I) - pointer to the destination list context */
1020 /* dst(I) - pointer to destination list */
1022 /* Free all of the destination nodes attached to the given table. */
1023 /* ------------------------------------------------------------------------ */
1025 ipf_dstlist_table_clearnodes(softd, dst)
1026 ipf_dstl_softc_t *softd;
1029 ipf_dstnode_t *node;
1031 if (dst->ipld_dests == NULL)
1034 while ((node = *dst->ipld_dests) != NULL) {
1035 ipf_dstlist_node_free(softd, dst, node);
1040 /* ------------------------------------------------------------------------ */
1041 /* Function: ipf_dstlist_table_find */
1042 /* Returns: int - 0 = success, else error */
1043 /* Parameters: arg(I) - pointer to local context to use */
1044 /* unit(I) - device we are working with */
1045 /* name(I) - destination table name to find */
1047 /* Return a pointer to a destination table that matches the unit+name that */
1049 /* ------------------------------------------------------------------------ */
1051 ipf_dstlist_table_find(arg, unit, name)
1056 ipf_dstl_softc_t *softd = arg;
1059 for (d = softd->dstlist[unit + 1]; d != NULL; d = d->ipld_next) {
1060 if ((d->ipld_unit == unit) &&
1061 !strncmp(d->ipld_name, name, FR_GROUPLEN)) {
1070 /* ------------------------------------------------------------------------ */
1071 /* Function: ipf_dstlist_select_ref */
1072 /* Returns: void * - NULL = failure, else pointer to table */
1073 /* Parameters: arg(I) - pointer to local context to use */
1074 /* unit(I) - device we are working with */
1075 /* name(I) - destination table name to find */
1077 /* Attempt to find a destination table that matches the name passed in and */
1078 /* if successful, bump up the reference count on it because we intend to */
1079 /* store the pointer to it somewhere else. */
1080 /* ------------------------------------------------------------------------ */
1082 ipf_dstlist_select_ref(arg, unit, name)
1089 d = ipf_dstlist_table_find(arg, unit, name);
1091 MUTEX_ENTER(&d->ipld_lock);
1093 MUTEX_EXIT(&d->ipld_lock);
1099 /* ------------------------------------------------------------------------ */
1100 /* Function: ipf_dstlist_select */
1101 /* Returns: void * - NULL = failure, else pointer to table */
1102 /* Parameters: fin(I) - pointer to packet information */
1103 /* d(I) - pointer to destination list */
1105 /* Find the next node in the destination list to be used according to the */
1106 /* defined policy. Of these, "connection" is the most expensive policy to */
1107 /* implement as it always looks for the node with the least number of */
1108 /* connections associated with it. */
1110 /* The hashes exclude the port numbers so that all protocols map to the */
1111 /* same destination. Otherwise, someone doing a ping would target a */
1112 /* different server than their TCP connection, etc. MD-5 is used to */
1113 /* transform the addressese into something random that the other end could */
1114 /* not easily guess and use in an attack. ipld_seed introduces an unknown */
1115 /* into the hash calculation to increase the difficult of an attacker */
1116 /* guessing the bucket. */
1118 /* One final comment: mixing different address families in a single pool */
1119 /* will currently result in failures as the address family of the node is */
1120 /* only matched up with that in the packet as the last step. While this can */
1121 /* be coded around for the weighted connection and round-robin models, it */
1122 /* cannot be supported for the hash/random models as they do not search and */
1123 /* nor is the algorithm conducive to searching. */
1124 /* ------------------------------------------------------------------------ */
1125 static ipf_dstnode_t *
1126 ipf_dstlist_select(fin, d)
1130 ipf_dstnode_t *node, *sel;
1137 if (d->ipld_dests == NULL || *d->ipld_dests == NULL)
1140 family = fin->fin_family;
1142 MUTEX_ENTER(&d->ipld_lock);
1144 switch (d->ipld_policy)
1146 case IPLDP_ROUNDROBIN:
1147 sel = d->ipld_selected;
1149 sel = *d->ipld_dests;
1151 sel = sel->ipfd_next;
1153 sel = *d->ipld_dests;
1157 case IPLDP_CONNECTION:
1158 if (d->ipld_selected == NULL) {
1159 sel = *d->ipld_dests;
1163 sel = d->ipld_selected;
1164 connects = 0x7fffffff;
1165 node = sel->ipfd_next;
1167 node = *d->ipld_dests;
1168 while (node != d->ipld_selected) {
1169 if (node->ipfd_states == 0) {
1173 if (node->ipfd_states < connects) {
1175 connects = node->ipfd_states;
1177 node = node->ipfd_next;
1179 node = *d->ipld_dests;
1184 x = ipf_random() % d->ipld_nodes;
1185 sel = d->ipld_dests[x];
1190 MD5Update(&ctx, (u_char *)&d->ipld_seed, sizeof(d->ipld_seed));
1191 MD5Update(&ctx, (u_char *)&fin->fin_src6,
1192 sizeof(fin->fin_src6));
1193 MD5Update(&ctx, (u_char *)&fin->fin_dst6,
1194 sizeof(fin->fin_dst6));
1195 MD5Final((u_char *)hash, &ctx);
1196 x = hash[0] % d->ipld_nodes;
1197 sel = d->ipld_dests[x];
1200 case IPLDP_SRCHASH :
1202 MD5Update(&ctx, (u_char *)&d->ipld_seed, sizeof(d->ipld_seed));
1203 MD5Update(&ctx, (u_char *)&fin->fin_src6,
1204 sizeof(fin->fin_src6));
1205 MD5Final((u_char *)hash, &ctx);
1206 x = hash[0] % d->ipld_nodes;
1207 sel = d->ipld_dests[x];
1210 case IPLDP_DSTHASH :
1212 MD5Update(&ctx, (u_char *)&d->ipld_seed, sizeof(d->ipld_seed));
1213 MD5Update(&ctx, (u_char *)&fin->fin_dst6,
1214 sizeof(fin->fin_dst6));
1215 MD5Final((u_char *)hash, &ctx);
1216 x = hash[0] % d->ipld_nodes;
1217 sel = d->ipld_dests[x];
1225 if (sel->ipfd_dest.fd_addr.adf_family != family)
1227 d->ipld_selected = sel;
1229 MUTEX_EXIT(&d->ipld_lock);
1235 /* ------------------------------------------------------------------------ */
1236 /* Function: ipf_dstlist_select_node */
1237 /* Returns: int - -1 == failure, 0 == success */
1238 /* Parameters: fin(I) - pointer to packet information */
1239 /* group(I) - destination pool to search */
1240 /* addr(I) - pointer to store selected address */
1241 /* pfdp(O) - pointer to storage for selected destination node */
1243 /* This function is only responsible for obtaining the next IP address for */
1244 /* use and storing it in the caller's address space (addr). "addr" is only */
1245 /* used for storage if pfdp is NULL. No permanent reference is currently */
1246 /* kept on the node. */
1247 /* ------------------------------------------------------------------------ */
1249 ipf_dstlist_select_node(fin, group, addr, pfdp)
1256 ipf_main_softc_t *softc = fin->fin_main_soft;
1258 ippool_dst_t *d = group;
1259 ipf_dstnode_t *node;
1262 READ_ENTER(&softc->ipf_poolrw);
1264 node = ipf_dstlist_select(fin, d);
1266 RWLOCK_EXIT(&softc->ipf_poolrw);
1271 bcopy(&node->ipfd_dest, pfdp, sizeof(*pfdp));
1273 if (fin->fin_family == AF_INET) {
1274 addr[0] = node->ipfd_dest.fd_addr.adf_addr.i6[0];
1275 } else if (fin->fin_family == AF_INET6) {
1276 addr[0] = node->ipfd_dest.fd_addr.adf_addr.i6[0];
1277 addr[1] = node->ipfd_dest.fd_addr.adf_addr.i6[1];
1278 addr[2] = node->ipfd_dest.fd_addr.adf_addr.i6[2];
1279 addr[3] = node->ipfd_dest.fd_addr.adf_addr.i6[3];
1283 fdp = &node->ipfd_dest;
1284 if (fdp->fd_ptr == NULL)
1285 fdp->fd_ptr = fin->fin_ifp;
1287 MUTEX_ENTER(&node->ipfd_lock);
1288 node->ipfd_states++;
1289 MUTEX_EXIT(&node->ipfd_lock);
1291 RWLOCK_EXIT(&softc->ipf_poolrw);
1297 /* ------------------------------------------------------------------------ */
1298 /* Function: ipf_dstlist_expire */
1300 /* Parameters: softc(I) - pointer to soft context main structure */
1301 /* arg(I) - pointer to local context to use */
1303 /* There are currently no objects to expire in destination lists. */
1304 /* ------------------------------------------------------------------------ */
1306 ipf_dstlist_expire(softc, arg)
1307 ipf_main_softc_t *softc;
1314 /* ------------------------------------------------------------------------ */
1315 /* Function: ipf_dstlist_sync */
1317 /* Parameters: softc(I) - pointer to soft context main structure */
1318 /* arg(I) - pointer to local context to use */
1320 /* When a network interface appears or disappears, we need to revalidate */
1321 /* all of the network interface names that have been configured as a target */
1322 /* in a destination list. */
1323 /* ------------------------------------------------------------------------ */
1325 ipf_dstlist_sync(softc, arg)
1326 ipf_main_softc_t *softc;
1329 ipf_dstl_softc_t *softd = arg;
1330 ipf_dstnode_t *node;
1335 for (i = 0; i < IPL_LOGMAX; i++) {
1336 for (list = softd->dstlist[i]; list != NULL;
1337 list = list->ipld_next) {
1338 for (j = 0; j < list->ipld_maxnodes; j++) {
1339 node = list->ipld_dests[j];
1342 if (node->ipfd_dest.fd_name == -1)
1344 (void) ipf_resolvedest(softc,