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);
697 MUTEX_ENTER(&d->ipld_lock);
698 for (node = *d->ipld_dests; node != NULL; node = node->ipfd_next) {
699 if ((uid != 0) && (node->ipfd_uid != uid))
701 if (node->ipfd_size != size)
703 if (!bcmp(&node->ipfd_dest.fd_ip6, &frd.fd_ip6,
704 size - offsetof(frdest_t, fd_ip6))) {
705 ipf_dstlist_node_free(softd, d, node);
706 MUTEX_EXIT(&d->ipld_lock);
711 MUTEX_EXIT(&d->ipld_lock);
718 /* ------------------------------------------------------------------------ */
719 /* Function: ipf_dstlist_node_free */
721 /* Parameters: softd(I) - pointer to the destination list context */
722 /* d(I) - pointer to destination list */
723 /* node(I) - pointer to node to free */
724 /* Locks: MUTEX(ipld_lock) or WRITE(ipf_poolrw) */
726 /* Free the destination node by first removing it from any lists and then */
727 /* checking if this was the last reference held to the object. While the */
728 /* array of pointers to nodes is compacted, its size isn't reduced (by way */
729 /* of allocating a new smaller one and copying) because the belief is that */
730 /* it is likely the array will again reach that size. */
731 /* ------------------------------------------------------------------------ */
733 ipf_dstlist_node_free(softd, d, node)
734 ipf_dstl_softc_t *softd;
741 * Compact the array of pointers to nodes.
743 for (i = 0; i < d->ipld_nodes; i++)
744 if (d->ipld_dests[i] == node)
746 if (d->ipld_nodes - i > 1) {
747 bcopy(&d->ipld_dests[i + 1], &d->ipld_dests[i],
748 sizeof(*d->ipld_dests) * (d->ipld_nodes - i - 1));
752 if (node->ipfd_pnext != NULL)
753 *node->ipfd_pnext = node->ipfd_next;
754 if (node->ipfd_next != NULL)
755 node->ipfd_next->ipfd_pnext = node->ipfd_pnext;
756 node->ipfd_pnext = NULL;
757 node->ipfd_next = NULL;
759 if ((node->ipfd_flags & IPDST_DELETE) == 0) {
760 softd->stats.ipls_numderefnodes++;
761 node->ipfd_flags |= IPDST_DELETE;
764 ipf_dstlist_node_deref(softd, node);
768 /* ------------------------------------------------------------------------ */
769 /* Function: ipf_dstlist_stats_get */
770 /* Returns: int - 0 = success, else error */
771 /* Parameters: softc(I) - pointer to soft context main structure */
772 /* arg(I) - pointer to local context to use */
773 /* op(I) - pointer to lookup operation data */
775 /* Return the current statistics for destination lists. This may be for all */
776 /* of them or just information pertaining to a particular table. */
777 /* ------------------------------------------------------------------------ */
780 ipf_dstlist_stats_get(softc, arg, op)
781 ipf_main_softc_t *softc;
785 ipf_dstl_softc_t *softd = arg;
786 ipf_dstl_stat_t stats;
787 int unit, i, err = 0;
789 if (op->iplo_size != sizeof(ipf_dstl_stat_t)) {
794 stats = softd->stats;
795 unit = op->iplo_unit;
796 if (unit == IPL_LOGALL) {
797 for (i = 0; i <= IPL_LOGMAX; i++)
798 stats.ipls_list[i] = softd->dstlist[i];
799 } else if (unit >= 0 && unit <= IPL_LOGMAX) {
802 if (op->iplo_name[0] != '\0')
803 ptr = ipf_dstlist_table_find(softd, unit,
806 ptr = softd->dstlist[unit + 1];
807 stats.ipls_list[unit] = ptr;
814 err = COPYOUT(&stats, op->iplo_struct, sizeof(stats));
824 /* ------------------------------------------------------------------------ */
825 /* Function: ipf_dstlist_table_add */
826 /* Returns: int - 0 = success, else error */
827 /* Parameters: softc(I) - pointer to soft context main structure */
828 /* arg(I) - pointer to local context to use */
829 /* op(I) - pointer to lookup operation data */
831 /* Add a new destination table to the list of those available for the given */
832 /* device. Because we seldom operate on these objects (find/add/delete), */
833 /* they are just kept in a simple linked list. */
834 /* ------------------------------------------------------------------------ */
836 ipf_dstlist_table_add(softc, arg, op)
837 ipf_main_softc_t *softc;
841 ipf_dstl_softc_t *softd = arg;
842 ippool_dst_t user, *d, *new;
845 d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name);
851 err = COPYIN(op->iplo_struct, &user, sizeof(user));
857 KMALLOC(new, ippool_dst_t *);
859 softd->stats.ipls_nomem++;
863 bzero((char *)new, sizeof(*new));
865 MUTEX_INIT(&new->ipld_lock, "ipf dst table lock");
867 strncpy(new->ipld_name, op->iplo_name, FR_GROUPLEN);
868 unit = op->iplo_unit;
869 new->ipld_unit = unit;
870 new->ipld_policy = user.ipld_policy;
871 new->ipld_seed = ipf_random();
874 new->ipld_pnext = softd->tails[unit + 1];
875 *softd->tails[unit + 1] = new;
876 softd->tails[unit + 1] = &new->ipld_next;
877 softd->stats.ipls_numlists++;
883 /* ------------------------------------------------------------------------ */
884 /* Function: ipf_dstlist_table_del */
885 /* Returns: int - 0 = success, else error */
886 /* Parameters: softc(I) - pointer to soft context main structure */
887 /* arg(I) - pointer to local context to use */
888 /* op(I) - pointer to lookup operation data */
890 /* Find a named destinstion list table and delete it. If there are other */
891 /* references to it, the caller isn't told. */
892 /* ------------------------------------------------------------------------ */
894 ipf_dstlist_table_del(softc, arg, op)
895 ipf_main_softc_t *softc;
901 d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name);
907 if (d->ipld_dests != NULL) {
912 ipf_dstlist_table_remove(softc, arg, d);
918 /* ------------------------------------------------------------------------ */
919 /* Function: ipf_dstlist_table_remove */
921 /* Parameters: softc(I) - pointer to soft context main structure */
922 /* softd(I) - pointer to the destination list context */
923 /* d(I) - pointer to destination list */
925 /* Remove a given destination list from existance. While the IPDST_DELETE */
926 /* flag is set every time we call this function and the reference count is */
927 /* non-zero, the "numdereflists" counter is always incremented because the */
928 /* decision about whether it will be freed or not is not made here. This */
929 /* means that the only action the code can take here is to treat it as if */
930 /* it will become a detached. */
931 /* ------------------------------------------------------------------------ */
933 ipf_dstlist_table_remove(softc, softd, d)
934 ipf_main_softc_t *softc;
935 ipf_dstl_softc_t *softd;
939 if (softd->tails[d->ipld_unit + 1] == &d->ipld_next)
940 softd->tails[d->ipld_unit + 1] = d->ipld_pnext;
942 if (d->ipld_pnext != NULL)
943 *d->ipld_pnext = d->ipld_next;
944 if (d->ipld_next != NULL)
945 d->ipld_next->ipld_pnext = d->ipld_pnext;
946 d->ipld_pnext = NULL;
949 ipf_dstlist_table_clearnodes(softd, d);
951 softd->stats.ipls_numdereflists++;
952 d->ipld_flags |= IPDST_DELETE;
954 ipf_dstlist_table_deref(softc, softd, d);
958 /* ------------------------------------------------------------------------ */
959 /* Function: ipf_dstlist_table_free */
961 /* Parameters: softd(I) - pointer to the destination list context */
962 /* d(I) - pointer to destination list */
964 /* Free up a destination list data structure and any other memory that was */
965 /* directly allocated as part of creating it. Individual destination list */
966 /* nodes are not freed. It is assumed the caller will have already emptied */
967 /* the destination list. */
968 /* ------------------------------------------------------------------------ */
970 ipf_dstlist_table_free(softd, d)
971 ipf_dstl_softc_t *softd;
974 MUTEX_DESTROY(&d->ipld_lock);
976 if ((d->ipld_flags & IPDST_DELETE) != 0)
977 softd->stats.ipls_numdereflists--;
978 softd->stats.ipls_numlists--;
980 if (d->ipld_dests != NULL) {
981 KFREES(d->ipld_dests,
982 d->ipld_maxnodes * sizeof(*d->ipld_dests));
989 /* ------------------------------------------------------------------------ */
990 /* Function: ipf_dstlist_table_deref */
991 /* Returns: int - 0 = success, else error */
992 /* Parameters: softc(I) - pointer to soft context main structure */
993 /* arg(I) - pointer to local context to use */
994 /* op(I) - pointer to lookup operation data */
996 /* Drops the reference count on a destination list table object and free's */
997 /* it if 0 has been reached. */
998 /* ------------------------------------------------------------------------ */
1000 ipf_dstlist_table_deref(softc, arg, table)
1001 ipf_main_softc_t *softc;
1005 ippool_dst_t *d = table;
1008 if (d->ipld_ref > 0)
1011 ipf_dstlist_table_free(arg, d);
1017 /* ------------------------------------------------------------------------ */
1018 /* Function: ipf_dstlist_table_clearnodes */
1020 /* Parameters: softd(I) - pointer to the destination list context */
1021 /* dst(I) - pointer to destination list */
1023 /* Free all of the destination nodes attached to the given table. */
1024 /* ------------------------------------------------------------------------ */
1026 ipf_dstlist_table_clearnodes(softd, dst)
1027 ipf_dstl_softc_t *softd;
1030 ipf_dstnode_t *node;
1032 if (dst->ipld_dests == NULL)
1035 while ((node = *dst->ipld_dests) != NULL) {
1036 ipf_dstlist_node_free(softd, dst, node);
1041 /* ------------------------------------------------------------------------ */
1042 /* Function: ipf_dstlist_table_find */
1043 /* Returns: int - 0 = success, else error */
1044 /* Parameters: arg(I) - pointer to local context to use */
1045 /* unit(I) - device we are working with */
1046 /* name(I) - destination table name to find */
1048 /* Return a pointer to a destination table that matches the unit+name that */
1050 /* ------------------------------------------------------------------------ */
1052 ipf_dstlist_table_find(arg, unit, name)
1057 ipf_dstl_softc_t *softd = arg;
1060 for (d = softd->dstlist[unit + 1]; d != NULL; d = d->ipld_next) {
1061 if ((d->ipld_unit == unit) &&
1062 !strncmp(d->ipld_name, name, FR_GROUPLEN)) {
1071 /* ------------------------------------------------------------------------ */
1072 /* Function: ipf_dstlist_select_ref */
1073 /* Returns: void * - NULL = failure, else pointer to table */
1074 /* Parameters: arg(I) - pointer to local context to use */
1075 /* unit(I) - device we are working with */
1076 /* name(I) - destination table name to find */
1078 /* Attempt to find a destination table that matches the name passed in and */
1079 /* if successful, bump up the reference count on it because we intend to */
1080 /* store the pointer to it somewhere else. */
1081 /* ------------------------------------------------------------------------ */
1083 ipf_dstlist_select_ref(arg, unit, name)
1090 d = ipf_dstlist_table_find(arg, unit, name);
1092 MUTEX_ENTER(&d->ipld_lock);
1094 MUTEX_EXIT(&d->ipld_lock);
1100 /* ------------------------------------------------------------------------ */
1101 /* Function: ipf_dstlist_select */
1102 /* Returns: void * - NULL = failure, else pointer to table */
1103 /* Parameters: fin(I) - pointer to packet information */
1104 /* d(I) - pointer to destination list */
1106 /* Find the next node in the destination list to be used according to the */
1107 /* defined policy. Of these, "connection" is the most expensive policy to */
1108 /* implement as it always looks for the node with the least number of */
1109 /* connections associated with it. */
1111 /* The hashes exclude the port numbers so that all protocols map to the */
1112 /* same destination. Otherwise, someone doing a ping would target a */
1113 /* different server than their TCP connection, etc. MD-5 is used to */
1114 /* transform the addressese into something random that the other end could */
1115 /* not easily guess and use in an attack. ipld_seed introduces an unknown */
1116 /* into the hash calculation to increase the difficult of an attacker */
1117 /* guessing the bucket. */
1119 /* One final comment: mixing different address families in a single pool */
1120 /* will currently result in failures as the address family of the node is */
1121 /* only matched up with that in the packet as the last step. While this can */
1122 /* be coded around for the weighted connection and round-robin models, it */
1123 /* cannot be supported for the hash/random models as they do not search and */
1124 /* nor is the algorithm conducive to searching. */
1125 /* ------------------------------------------------------------------------ */
1126 static ipf_dstnode_t *
1127 ipf_dstlist_select(fin, d)
1131 ipf_dstnode_t *node, *sel;
1138 if (d == NULL || d->ipld_dests == NULL || *d->ipld_dests == NULL)
1141 family = fin->fin_family;
1143 MUTEX_ENTER(&d->ipld_lock);
1145 switch (d->ipld_policy)
1147 case IPLDP_ROUNDROBIN:
1148 sel = d->ipld_selected;
1150 sel = *d->ipld_dests;
1152 sel = sel->ipfd_next;
1154 sel = *d->ipld_dests;
1158 case IPLDP_CONNECTION:
1159 if (d->ipld_selected == NULL) {
1160 sel = *d->ipld_dests;
1164 sel = d->ipld_selected;
1165 connects = 0x7fffffff;
1166 node = sel->ipfd_next;
1168 node = *d->ipld_dests;
1169 while (node != d->ipld_selected) {
1170 if (node->ipfd_states == 0) {
1174 if (node->ipfd_states < connects) {
1176 connects = node->ipfd_states;
1178 node = node->ipfd_next;
1180 node = *d->ipld_dests;
1185 x = ipf_random() % d->ipld_nodes;
1186 sel = d->ipld_dests[x];
1191 MD5Update(&ctx, (u_char *)&d->ipld_seed, sizeof(d->ipld_seed));
1192 MD5Update(&ctx, (u_char *)&fin->fin_src6,
1193 sizeof(fin->fin_src6));
1194 MD5Update(&ctx, (u_char *)&fin->fin_dst6,
1195 sizeof(fin->fin_dst6));
1196 MD5Final((u_char *)hash, &ctx);
1197 x = ntohl(hash[0]) % d->ipld_nodes;
1198 sel = d->ipld_dests[x];
1201 case IPLDP_SRCHASH :
1203 MD5Update(&ctx, (u_char *)&d->ipld_seed, sizeof(d->ipld_seed));
1204 MD5Update(&ctx, (u_char *)&fin->fin_src6,
1205 sizeof(fin->fin_src6));
1206 MD5Final((u_char *)hash, &ctx);
1207 x = ntohl(hash[0]) % d->ipld_nodes;
1208 sel = d->ipld_dests[x];
1211 case IPLDP_DSTHASH :
1213 MD5Update(&ctx, (u_char *)&d->ipld_seed, sizeof(d->ipld_seed));
1214 MD5Update(&ctx, (u_char *)&fin->fin_dst6,
1215 sizeof(fin->fin_dst6));
1216 MD5Final((u_char *)hash, &ctx);
1217 x = ntohl(hash[0]) % d->ipld_nodes;
1218 sel = d->ipld_dests[x];
1226 if (sel && sel->ipfd_dest.fd_addr.adf_family != family)
1228 d->ipld_selected = sel;
1230 MUTEX_EXIT(&d->ipld_lock);
1236 /* ------------------------------------------------------------------------ */
1237 /* Function: ipf_dstlist_select_node */
1238 /* Returns: int - -1 == failure, 0 == success */
1239 /* Parameters: fin(I) - pointer to packet information */
1240 /* group(I) - destination pool to search */
1241 /* addr(I) - pointer to store selected address */
1242 /* pfdp(O) - pointer to storage for selected destination node */
1244 /* This function is only responsible for obtaining the next IP address for */
1245 /* use and storing it in the caller's address space (addr). "addr" is only */
1246 /* used for storage if pfdp is NULL. No permanent reference is currently */
1247 /* kept on the node. */
1248 /* ------------------------------------------------------------------------ */
1250 ipf_dstlist_select_node(fin, group, addr, pfdp)
1257 ipf_main_softc_t *softc = fin->fin_main_soft;
1259 ippool_dst_t *d = group;
1260 ipf_dstnode_t *node;
1263 READ_ENTER(&softc->ipf_poolrw);
1265 node = ipf_dstlist_select(fin, d);
1267 RWLOCK_EXIT(&softc->ipf_poolrw);
1272 bcopy(&node->ipfd_dest, pfdp, sizeof(*pfdp));
1274 if (fin->fin_family == AF_INET) {
1275 addr[0] = node->ipfd_dest.fd_addr.adf_addr.i6[0];
1276 } else if (fin->fin_family == AF_INET6) {
1277 addr[0] = node->ipfd_dest.fd_addr.adf_addr.i6[0];
1278 addr[1] = node->ipfd_dest.fd_addr.adf_addr.i6[1];
1279 addr[2] = node->ipfd_dest.fd_addr.adf_addr.i6[2];
1280 addr[3] = node->ipfd_dest.fd_addr.adf_addr.i6[3];
1284 fdp = &node->ipfd_dest;
1285 if (fdp->fd_ptr == NULL)
1286 fdp->fd_ptr = fin->fin_ifp;
1288 MUTEX_ENTER(&node->ipfd_lock);
1289 node->ipfd_states++;
1290 MUTEX_EXIT(&node->ipfd_lock);
1292 RWLOCK_EXIT(&softc->ipf_poolrw);
1298 /* ------------------------------------------------------------------------ */
1299 /* Function: ipf_dstlist_expire */
1301 /* Parameters: softc(I) - pointer to soft context main structure */
1302 /* arg(I) - pointer to local context to use */
1304 /* There are currently no objects to expire in destination lists. */
1305 /* ------------------------------------------------------------------------ */
1307 ipf_dstlist_expire(softc, arg)
1308 ipf_main_softc_t *softc;
1315 /* ------------------------------------------------------------------------ */
1316 /* Function: ipf_dstlist_sync */
1318 /* Parameters: softc(I) - pointer to soft context main structure */
1319 /* arg(I) - pointer to local context to use */
1321 /* When a network interface appears or disappears, we need to revalidate */
1322 /* all of the network interface names that have been configured as a target */
1323 /* in a destination list. */
1324 /* ------------------------------------------------------------------------ */
1326 ipf_dstlist_sync(softc, arg)
1327 ipf_main_softc_t *softc;
1330 ipf_dstl_softc_t *softd = arg;
1331 ipf_dstnode_t *node;
1336 for (i = 0; i < IPL_LOGMAX; i++) {
1337 for (list = softd->dstlist[i]; list != NULL;
1338 list = list->ipld_next) {
1339 for (j = 0; j < list->ipld_maxnodes; j++) {
1340 node = list->ipld_dests[j];
1343 if (node->ipfd_dest.fd_name == -1)
1345 (void) ipf_resolvedest(softc,