Home | History | Annotate | Line # | Download | only in netinet
      1 /*	$NetBSD: ip_dstlist.c,v 1.8 2014/06/28 07:59:26 darrenr Exp $	*/
      2 
      3 /*
      4  * Copyright (C) 2012 by Darren Reed.
      5  *
      6  * See the IPFILTER.LICENCE file for details on licencing.
      7  */
      8 #if defined(KERNEL) || defined(_KERNEL)
      9 # undef KERNEL
     10 # undef _KERNEL
     11 # define        KERNEL	1
     12 # define        _KERNEL	1
     13 #endif
     14 #if defined(__osf__)
     15 # define _PROTO_NET_H_
     16 #endif
     17 #include <sys/errno.h>
     18 #include <sys/types.h>
     19 #include <sys/param.h>
     20 #include <sys/file.h>
     21 #if !defined(_KERNEL) && !defined(__KERNEL__)
     22 # include <stdio.h>
     23 # include <stdlib.h>
     24 # include <string.h>
     25 # define _KERNEL
     26 # ifdef __OpenBSD__
     27 struct file;
     28 # endif
     29 # include <sys/uio.h>
     30 # undef _KERNEL
     31 #else
     32 # include <sys/systm.h>
     33 # if defined(NetBSD) && (__NetBSD_Version__ >= 104000000)
     34 #  include <sys/proc.h>
     35 # endif
     36 #endif
     37 #include <sys/time.h>
     38 #if !defined(linux)
     39 # include <sys/protosw.h>
     40 #endif
     41 #include <sys/socket.h>
     42 #if defined(_KERNEL) && (!defined(__SVR4) && !defined(__svr4__))
     43 # include <sys/mbuf.h>
     44 #endif
     45 #if defined(__SVR4) || defined(__svr4__)
     46 # include <sys/filio.h>
     47 # include <sys/byteorder.h>
     48 # ifdef _KERNEL
     49 #  include <sys/dditypes.h>
     50 # endif
     51 # include <sys/stream.h>
     52 # include <sys/kmem.h>
     53 #endif
     54 #if defined(__FreeBSD_version) && (__FreeBSD_version >= 300000)
     55 # include <sys/malloc.h>
     56 #endif
     57 
     58 #include <net/if.h>
     59 #include <netinet/in.h>
     60 
     61 #include "netinet/ip_compat.h"
     62 #include "netinet/ip_fil.h"
     63 #include "netinet/ip_nat.h"
     64 #include "netinet/ip_lookup.h"
     65 #include "netinet/ip_dstlist.h"
     66 
     67 /* END OF INCLUDES */
     68 
     69 #ifdef HAS_SYS_MD5_H
     70 # include <sys/md5.h>
     71 #else
     72 # include "md5.h"
     73 #endif
     74 
     75 __KERNEL_RCSID(0, "Id: ip_dstlist.c,v 1.1.1.2 2012/07/22 13:45:11 darrenr Exp");
     76 
     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;
     81 } ipf_dstl_softc_t;
     82 
     83 
     84 static void *ipf_dstlist_soft_create(ipf_main_softc_t *);
     85 static void ipf_dstlist_soft_destroy(ipf_main_softc_t *, void *);
     86 static int ipf_dstlist_soft_init(ipf_main_softc_t *, void *);
     87 static void ipf_dstlist_soft_fini(ipf_main_softc_t *, void *);
     88 static int ipf_dstlist_addr_find(ipf_main_softc_t *, void *, int,
     89 				      void *, u_int);
     90 static size_t ipf_dstlist_flush(ipf_main_softc_t *, void *,
     91 				     iplookupflush_t *);
     92 static void ipf_dstlist_table_free(ipf_dstl_softc_t *, ippool_dst_t *);
     93 static int ipf_dstlist_iter_deref(ipf_main_softc_t *, void *, int, int,
     94 				       void *);
     95 static int ipf_dstlist_iter_next(ipf_main_softc_t *, void *, ipftoken_t *,
     96 				      ipflookupiter_t *);
     97 static int ipf_dstlist_node_add(ipf_main_softc_t *, void *,
     98 				     iplookupop_t *, int);
     99 static int ipf_dstlist_node_del(ipf_main_softc_t *, void *,
    100 				     iplookupop_t *, int);
    101 static int ipf_dstlist_stats_get(ipf_main_softc_t *, void *,
    102 				      iplookupop_t *);
    103 static int ipf_dstlist_table_add(ipf_main_softc_t *, void *,
    104 				      iplookupop_t *);
    105 static int ipf_dstlist_table_del(ipf_main_softc_t *, void *,
    106 				      iplookupop_t *);
    107 static int ipf_dstlist_table_deref(ipf_main_softc_t *, void *, void *);
    108 static void *ipf_dstlist_table_find(void *, int, char *);
    109 static void ipf_dstlist_table_remove(ipf_main_softc_t *,
    110 					  ipf_dstl_softc_t *, ippool_dst_t *);
    111 static void ipf_dstlist_table_clearnodes(ipf_dstl_softc_t *,
    112 					      ippool_dst_t *);
    113 static ipf_dstnode_t *ipf_dstlist_select(fr_info_t *, ippool_dst_t *);
    114 static void *ipf_dstlist_select_ref(void *, int, char *);
    115 static void ipf_dstlist_node_free(ipf_dstl_softc_t *, ippool_dst_t *, ipf_dstnode_t *);
    116 static int ipf_dstlist_node_deref(void *, ipf_dstnode_t *);
    117 static void ipf_dstlist_expire(ipf_main_softc_t *, void *);
    118 static void ipf_dstlist_sync(ipf_main_softc_t *, void *);
    119 
    120 ipf_lookup_t ipf_dstlist_backend = {
    121 	IPLT_DSTLIST,
    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,
    127 	ipf_dstlist_flush,
    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,
    139 	ipf_dstlist_expire,
    140 	ipf_dstlist_sync
    141 };
    142 
    143 
    144 /* ------------------------------------------------------------------------ */
    145 /* Function:    ipf_dstlist_soft_create                                     */
    146 /* Returns:     int - 0 = success, else error                               */
    147 /* Parameters:  softc(I) - pointer to soft context main structure           */
    148 /*                                                                          */
    149 /* Allocating a chunk of memory filled with 0's is enough for the current   */
    150 /* soft context used with destination lists.                                */
    151 /* ------------------------------------------------------------------------ */
    152 static void *
    153 ipf_dstlist_soft_create(ipf_main_softc_t *softc)
    154 {
    155 	ipf_dstl_softc_t *softd;
    156 	int i;
    157 
    158 	KMALLOC(softd, ipf_dstl_softc_t *);
    159 	if (softd == NULL) {
    160 		IPFERROR(120028);
    161 		return NULL;
    162 	}
    163 
    164 	bzero((char *)softd, sizeof(*softd));
    165 	for (i = 0; i <= IPL_LOGMAX; i++)
    166 		softd->tails[i] = &softd->dstlist[i];
    167 
    168 	return softd;
    169 }
    170 
    171 
    172 /* ------------------------------------------------------------------------ */
    173 /* Function:    ipf_dstlist_soft_destroy                                    */
    174 /* Returns:     Nil                                                         */
    175 /* Parameters:  softc(I) - pointer to soft context main structure           */
    176 /*              arg(I)   - pointer to local context to use                  */
    177 /*                                                                          */
    178 /* For destination lists, the only thing we have to do when destroying the  */
    179 /* soft context is free it!                                                 */
    180 /* ------------------------------------------------------------------------ */
    181 static void
    182 ipf_dstlist_soft_destroy(ipf_main_softc_t *softc, void *arg)
    183 {
    184 	ipf_dstl_softc_t *softd = arg;
    185 
    186 	KFREE(softd);
    187 }
    188 
    189 
    190 /* ------------------------------------------------------------------------ */
    191 /* Function:    ipf_dstlist_soft_init                                       */
    192 /* Returns:     int - 0 = success, else error                               */
    193 /* Parameters:  softc(I) - pointer to soft context main structure           */
    194 /*              arg(I)   - pointer to local context to use                  */
    195 /*                                                                          */
    196 /* There is currently no soft context for destination list management.      */
    197 /* ------------------------------------------------------------------------ */
    198 static int
    199 ipf_dstlist_soft_init(ipf_main_softc_t *softc, void *arg)
    200 {
    201 	return 0;
    202 }
    203 
    204 
    205 /* ------------------------------------------------------------------------ */
    206 /* Function:    ipf_dstlist_soft_fini                                       */
    207 /* Returns:     Nil                                                         */
    208 /* Parameters:  softc(I) - pointer to soft context main structure           */
    209 /*              arg(I)   - pointer to local context to use                  */
    210 /*                                                                          */
    211 /* There is currently no soft context for destination list management.      */
    212 /* ------------------------------------------------------------------------ */
    213 static void
    214 ipf_dstlist_soft_fini(ipf_main_softc_t *softc, void *arg)
    215 {
    216 	ipf_dstl_softc_t *softd = arg;
    217 	int i;
    218 
    219 	for (i = -1; i <= IPL_LOGMAX; i++) {
    220 		while (softd->dstlist[i + 1] != NULL) {
    221 			ipf_dstlist_table_remove(softc, softd,
    222 						 softd->dstlist[i + 1]);
    223 		}
    224 	}
    225 
    226 	ASSERT(softd->stats.ipls_numderefnodes == 0);
    227 }
    228 
    229 
    230 /* ------------------------------------------------------------------------ */
    231 /* Function:    ipf_dstlist_addr_find                                       */
    232 /* Returns:     int - 0 = success, else error                               */
    233 /* Parameters:  softc(I) - pointer to soft context main structure           */
    234 /*              arg1(I)  - pointer to local context to use                  */
    235 /*              arg2(I)  - pointer to local context to use                  */
    236 /*              arg3(I)  - pointer to local context to use                  */
    237 /*              arg4(I)  - pointer to local context to use                  */
    238 /*                                                                          */
    239 /* There is currently no such thing as searching a destination list for an  */
    240 /* address so this function becomes a no-op. Its presence is required as    */
    241 /* ipf_lookup_res_name() stores the "addr_find" function pointer in the     */
    242 /* pointer passed in to it as funcptr, although it could be a generic null- */
    243 /* op function rather than a specific one.                                  */
    244 /* ------------------------------------------------------------------------ */
    245 /*ARGSUSED*/
    246 static int
    247 ipf_dstlist_addr_find(ipf_main_softc_t *softc, void *arg1, int arg2, void *arg3,
    248     u_int arg4)
    249 {
    250 	return -1;
    251 }
    252 
    253 
    254 /* ------------------------------------------------------------------------ */
    255 /* Function:    ipf_dstlist_flush                                           */
    256 /* Returns:     int      - number of objects deleted                        */
    257 /* Parameters:  softc(I) - pointer to soft context main structure           */
    258 /*              arg(I)   - pointer to local context to use                  */
    259 /*              fop(I)   - pointer to lookup flush operation data           */
    260 /*                                                                          */
    261 /* Flush all of the destination tables that match the data passed in with   */
    262 /* the iplookupflush_t. There are two ways to match objects: the device for */
    263 /* which they are to be used with and their name.                           */
    264 /* ------------------------------------------------------------------------ */
    265 static size_t
    266 ipf_dstlist_flush(ipf_main_softc_t *softc, void *arg, iplookupflush_t *fop)
    267 {
    268 	ipf_dstl_softc_t *softd = arg;
    269 	ippool_dst_t *node, *next;
    270 	int n, i;
    271 
    272 	for (n = 0, i = -1; i <= IPL_LOGMAX; i++) {
    273 		if (fop->iplf_unit != IPLT_ALL && fop->iplf_unit != i)
    274 			continue;
    275 		for (node = softd->dstlist[i + 1]; node != NULL; node = next) {
    276 			next = node->ipld_next;
    277 
    278 			if ((*fop->iplf_name != '\0') &&
    279 			    strncmp(fop->iplf_name, node->ipld_name,
    280 				    FR_GROUPLEN))
    281 				continue;
    282 
    283 			ipf_dstlist_table_remove(softc, softd, node);
    284 			n++;
    285 		}
    286 	}
    287 	return n;
    288 }
    289 
    290 
    291 /* ------------------------------------------------------------------------ */
    292 /* Function:    ipf_dstlist_iter_deref                                      */
    293 /* Returns:     int      - 0 = success, else error                          */
    294 /* Parameters:  softc(I) - pointer to soft context main structure           */
    295 /*              arg(I)   - pointer to local context to use                  */
    296 /*              otype(I) - type of data structure to iterate through        */
    297 /*              unit(I)  - device we are working with                       */
    298 /*              data(I)  - address of object in kernel space                */
    299 /*                                                                          */
    300 /* This function is called when the iteration token is being free'd and is  */
    301 /* responsible for dropping the reference count of the structure it points  */
    302 /* to.                                                                      */
    303 /* ------------------------------------------------------------------------ */
    304 static int
    305 ipf_dstlist_iter_deref(ipf_main_softc_t *softc, void *arg, int otype, int unit,
    306     void *data)
    307 {
    308 	if (data == NULL) {
    309 		IPFERROR(120001);
    310 		return EINVAL;
    311 	}
    312 
    313 	if (unit < -1 || unit > IPL_LOGMAX) {
    314 		IPFERROR(120002);
    315 		return EINVAL;
    316 	}
    317 
    318 	switch (otype)
    319 	{
    320 	case IPFLOOKUPITER_LIST :
    321 		ipf_dstlist_table_deref(softc, arg, (ippool_dst_t *)data);
    322 		break;
    323 
    324 	case IPFLOOKUPITER_NODE :
    325 		ipf_dstlist_node_deref(arg, (ipf_dstnode_t *)data);
    326 		break;
    327 	}
    328 
    329 	return 0;
    330 }
    331 
    332 
    333 /* ------------------------------------------------------------------------ */
    334 /* Function:    ipf_dstlist_iter_next                                       */
    335 /* Returns:     int - 0 = success, else error                               */
    336 /* Parameters:  softc(I) - pointer to soft context main structure           */
    337 /*              arg(I)   - pointer to local context to use                  */
    338 /*              op(I)    - pointer to lookup operation data                 */
    339 /*              uid(I)   - uid of process doing the ioctl                   */
    340 /*                                                                          */
    341 /* This function is responsible for either selecting the next destination   */
    342 /* list or node on a destination list to be returned as a user process      */
    343 /* iterates through the list of destination lists or nodes.                 */
    344 /* ------------------------------------------------------------------------ */
    345 static int
    346 ipf_dstlist_iter_next(ipf_main_softc_t *softc, void *arg, ipftoken_t *token,
    347     ipflookupiter_t *iter)
    348 {
    349 	ipf_dstnode_t zn, *nextnode = NULL, *node = NULL;
    350 	ippool_dst_t zero, *next = NULL, *dsttab = NULL;
    351 	ipf_dstl_softc_t *softd = arg;
    352 	int err = 0;
    353 	void *hint;
    354 
    355 	switch (iter->ili_otype)
    356 	{
    357 	case IPFLOOKUPITER_LIST :
    358 		dsttab = token->ipt_data;
    359 		if (dsttab == NULL) {
    360 			next = softd->dstlist[(int)iter->ili_unit + 1];
    361 		} else {
    362 			next = dsttab->ipld_next;
    363 		}
    364 
    365 		if (next != NULL) {
    366 			ATOMIC_INC32(next->ipld_ref);
    367 			token->ipt_data = next;
    368 			hint = next->ipld_next;
    369 		} else {
    370 			bzero((char *)&zero, sizeof(zero));
    371 			next = &zero;
    372 			token->ipt_data = NULL;
    373 			hint = NULL;
    374 		}
    375 		break;
    376 
    377 	case IPFLOOKUPITER_NODE :
    378 		node = token->ipt_data;
    379 		if (node == NULL) {
    380 			dsttab = ipf_dstlist_table_find(arg, iter->ili_unit,
    381 							iter->ili_name);
    382 			if (dsttab == NULL) {
    383 				IPFERROR(120004);
    384 				err = ESRCH;
    385 				nextnode = NULL;
    386 			} else {
    387 				if (dsttab->ipld_dests == NULL)
    388 					nextnode = NULL;
    389 				else
    390 					nextnode = *dsttab->ipld_dests;
    391 				dsttab = NULL;
    392 			}
    393 		} else {
    394 			nextnode = node->ipfd_next;
    395 		}
    396 
    397 		if (nextnode != NULL) {
    398 			MUTEX_ENTER(&nextnode->ipfd_lock);
    399 			nextnode->ipfd_ref++;
    400 			MUTEX_EXIT(&nextnode->ipfd_lock);
    401 			token->ipt_data = nextnode;
    402 			hint = nextnode->ipfd_next;
    403 		} else {
    404 			bzero((char *)&zn, sizeof(zn));
    405 			nextnode = &zn;
    406 			token->ipt_data = NULL;
    407 			hint = NULL;
    408 		}
    409 		break;
    410 	default :
    411 		IPFERROR(120003);
    412 		err = EINVAL;
    413 		break;
    414 	}
    415 
    416 	if (err != 0)
    417 		return err;
    418 
    419 	switch (iter->ili_otype)
    420 	{
    421 	case IPFLOOKUPITER_LIST :
    422 		if (dsttab != NULL)
    423 			ipf_dstlist_table_deref(softc, arg, dsttab);
    424 		err = COPYOUT(next, iter->ili_data, sizeof(*next));
    425 		if (err != 0) {
    426 			IPFERROR(120005);
    427 			err = EFAULT;
    428 		}
    429 		break;
    430 
    431 	case IPFLOOKUPITER_NODE :
    432 		if (node != NULL)
    433 			ipf_dstlist_node_deref(arg, node);
    434 		err = COPYOUT(nextnode, iter->ili_data, sizeof(*nextnode));
    435 		if (err != 0) {
    436 			IPFERROR(120006);
    437 			err = EFAULT;
    438 		}
    439 		break;
    440 	}
    441 
    442 	if (hint == NULL)
    443 		ipf_token_mark_complete(token);
    444 
    445 	return err;
    446 }
    447 
    448 
    449 /* ------------------------------------------------------------------------ */
    450 /* Function:    ipf_dstlist_node_add                                        */
    451 /* Returns:     int - 0 = success, else error                               */
    452 /* Parameters:  softc(I) - pointer to soft context main structure           */
    453 /*              arg(I)   - pointer to local context to use                  */
    454 /*              op(I)    - pointer to lookup operation data                 */
    455 /*              uid(I)   - uid of process doing the ioctl                   */
    456 /* Locks:       WRITE(ipf_poolrw)                                           */
    457 /*                                                                          */
    458 /* Add a new node to a destination list. To do this, we only copy in the    */
    459 /* frdest_t structure because that contains the only data required from the */
    460 /* application to create a new node. The frdest_t doesn't contain the name  */
    461 /* itself. When loading filter rules, fd_name is a 'pointer' to the name.   */
    462 /* In this case, the 'pointer' does not work, instead it is the length of   */
    463 /* the name and the name is immediately following the frdest_t structure.   */
    464 /* fd_name must include the trailing \0, so it should be strlen(str) + 1.   */
    465 /* For simple sanity checking, an upper bound on the size of fd_name is     */
    466 /* imposed - 128.                                                          */
    467 /* ------------------------------------------------------------------------ */
    468 static int
    469 ipf_dstlist_node_add(ipf_main_softc_t *softc, void *arg, iplookupop_t *op,
    470     int uid)
    471 {
    472 	ipf_dstl_softc_t *softd = arg;
    473 	ipf_dstnode_t *node, **nodes;
    474 	ippool_dst_t *d;
    475 	frdest_t dest;
    476 	int err;
    477 
    478 	if (op->iplo_size < sizeof(frdest_t)) {
    479 		IPFERROR(120007);
    480 		return EINVAL;
    481 	}
    482 
    483 	err = COPYIN(op->iplo_struct, &dest, sizeof(dest));
    484 	if (err != 0) {
    485 		IPFERROR(120009);
    486 		return EFAULT;
    487 	}
    488 
    489 	d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name);
    490 	if (d == NULL) {
    491 		IPFERROR(120010);
    492 		return ESRCH;
    493 	}
    494 
    495 	switch (dest.fd_addr.adf_family)
    496 	{
    497 	case AF_INET :
    498 	case AF_INET6 :
    499 		break;
    500 	default :
    501 		IPFERROR(120019);
    502 		return EINVAL;
    503 	}
    504 
    505 	if (dest.fd_name < -1 || dest.fd_name > 128) {
    506 		IPFERROR(120018);
    507 		return EINVAL;
    508 	}
    509 
    510 	KMALLOCS(node, ipf_dstnode_t *, sizeof(*node) + dest.fd_name);
    511 	if (node == NULL) {
    512 		softd->stats.ipls_nomem++;
    513 		IPFERROR(120008);
    514 		return ENOMEM;
    515 	}
    516 	bzero((char *)node, sizeof(*node) + dest.fd_name);
    517 
    518 	bcopy(&dest, &node->ipfd_dest, sizeof(dest));
    519 	node->ipfd_size = sizeof(*node) + dest.fd_name;
    520 
    521 	if (dest.fd_name > 0) {
    522 		/*
    523 		 * fd_name starts out as the length of the string to copy
    524 		 * in (including \0) and ends up being the offset from
    525 		 * fd_names (0).
    526 		 */
    527 		err = COPYIN((char *)op->iplo_struct + sizeof(dest),
    528 			     node->ipfd_names, dest.fd_name);
    529 		if (err != 0) {
    530 			IPFERROR(120017);
    531 			KFREES(node, node->ipfd_size);
    532 			return EFAULT;
    533 		}
    534 		node->ipfd_dest.fd_name = 0;
    535 	} else {
    536 		node->ipfd_dest.fd_name = -1;
    537 	}
    538 
    539 	if (d->ipld_nodes == d->ipld_maxnodes) {
    540 		KMALLOCS(nodes, ipf_dstnode_t **,
    541 			 sizeof(*nodes) * (d->ipld_maxnodes + 1));
    542 		if (nodes == NULL) {
    543 			softd->stats.ipls_nomem++;
    544 			IPFERROR(120022);
    545 			KFREES(node, node->ipfd_size);
    546 			return ENOMEM;
    547 		}
    548 		if (d->ipld_dests != NULL) {
    549 			bcopy(d->ipld_dests, nodes,
    550 			      sizeof(*nodes) * d->ipld_maxnodes);
    551 			KFREES(d->ipld_dests, sizeof(*nodes) * d->ipld_nodes);
    552 			nodes[0]->ipfd_pnext = nodes;
    553 		}
    554 		d->ipld_dests = nodes;
    555 		d->ipld_maxnodes++;
    556 	}
    557 	d->ipld_dests[d->ipld_nodes] = node;
    558 	d->ipld_nodes++;
    559 
    560 	if (d->ipld_nodes == 1) {
    561 		node->ipfd_pnext = d->ipld_dests;
    562 	} else if (d->ipld_nodes > 1) {
    563 		node->ipfd_pnext = &d->ipld_dests[d->ipld_nodes - 2]->ipfd_next;
    564 	}
    565 	*node->ipfd_pnext = node;
    566 
    567 	MUTEX_INIT(&node->ipfd_lock, "ipf dst node lock");
    568 	node->ipfd_uid = uid;
    569 	node->ipfd_ref = 1;
    570 	if (node->ipfd_dest.fd_name == 0)
    571 		(void) ipf_resolvedest(softc, node->ipfd_names,
    572 				       &node->ipfd_dest, AF_INET);
    573 #ifdef USE_INET6
    574 	if (node->ipfd_dest.fd_name == 0 &&
    575 	    node->ipfd_dest.fd_ptr == (void *)-1)
    576 		(void) ipf_resolvedest(softc, node->ipfd_names,
    577 				       &node->ipfd_dest, AF_INET6);
    578 #endif
    579 
    580 	softd->stats.ipls_numnodes++;
    581 
    582 	return 0;
    583 }
    584 
    585 
    586 /* ------------------------------------------------------------------------ */
    587 /* Function:    ipf_dstlist_node_deref                                      */
    588 /* Returns:     int - 0 = success, else error                               */
    589 /* Parameters:  arg(I)  - pointer to local context to use                   */
    590 /*              node(I) - pointer to destionation node to free              */
    591 /*                                                                          */
    592 /* Dereference the use count by one. If it drops to zero then we can assume */
    593 /* that it has been removed from any lists/tables and is ripe for freeing.  */
    594 /* The pointer to context is required for the purpose of maintaining        */
    595 /* statistics.                                                              */
    596 /* ------------------------------------------------------------------------ */
    597 static int
    598 ipf_dstlist_node_deref(void *arg, ipf_dstnode_t *node)
    599 {
    600 	ipf_dstl_softc_t *softd = arg;
    601 	int ref;
    602 
    603 	MUTEX_ENTER(&node->ipfd_lock);
    604 	ref = --node->ipfd_ref;
    605 	MUTEX_EXIT(&node->ipfd_lock);
    606 
    607 	if (ref > 0)
    608 		return 0;
    609 
    610 	if ((node->ipfd_flags & IPDST_DELETE) != 0)
    611 		softd->stats.ipls_numderefnodes--;
    612 	MUTEX_DESTROY(&node->ipfd_lock);
    613 	KFREES(node, node->ipfd_size);
    614 	softd->stats.ipls_numnodes--;
    615 
    616 	return 0;
    617 }
    618 
    619 
    620 /* ------------------------------------------------------------------------ */
    621 /* Function:    ipf_dstlist_node_del                                        */
    622 /* Returns:     int      - 0 = success, else error                          */
    623 /* Parameters:  softc(I) - pointer to soft context main structure           */
    624 /*              arg(I)   - pointer to local context to use                  */
    625 /*              op(I)    - pointer to lookup operation data                 */
    626 /*              uid(I)   - uid of process doing the ioctl                   */
    627 /*                                                                          */
    628 /* Look for a matching destination node on the named table and free it if   */
    629 /* found. Because the name embedded in the frdest_t is variable in length,  */
    630 /* it is necessary to allocate some memory locally, to complete this op.    */
    631 /* ------------------------------------------------------------------------ */
    632 static int
    633 ipf_dstlist_node_del(ipf_main_softc_t *softc, void *arg, iplookupop_t *op,
    634     int uid)
    635 {
    636 	ipf_dstl_softc_t *softd = arg;
    637 	ipf_dstnode_t *node;
    638 	frdest_t frd, *temp;
    639 	ippool_dst_t *d;
    640 	size_t size;
    641 	int err;
    642 
    643 	d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name);
    644 	if (d == NULL) {
    645 		IPFERROR(120012);
    646 		return ESRCH;
    647 	}
    648 
    649 	err = COPYIN(op->iplo_struct, &frd, sizeof(frd));
    650 	if (err != 0) {
    651 		IPFERROR(120011);
    652 		return EFAULT;
    653 	}
    654 
    655 	size = sizeof(*temp) + frd.fd_name;
    656 	KMALLOCS(temp, frdest_t *, size);
    657 	if (temp == NULL) {
    658 		softd->stats.ipls_nomem++;
    659 		IPFERROR(120026);
    660 		return ENOMEM;
    661 	}
    662 
    663 	err = COPYIN(op->iplo_struct, temp, size);
    664 	if (err != 0) {
    665 		IPFERROR(120027);
    666 		return EFAULT;
    667 	}
    668 
    669 	MUTEX_ENTER(&d->ipld_lock);
    670 	for (node = *d->ipld_dests; node != NULL; node = node->ipfd_next) {
    671 		if ((uid != 0) && (node->ipfd_uid != uid))
    672 			continue;
    673 		if (node->ipfd_size != size)
    674 			continue;
    675 		if (!bcmp(&node->ipfd_dest.fd_ip6, &frd.fd_ip6,
    676 			  size - offsetof(frdest_t, fd_ip6))) {
    677 			ipf_dstlist_node_free(softd, d, node);
    678 			MUTEX_EXIT(&d->ipld_lock);
    679 			KFREES(temp, size);
    680 			return 0;
    681 		}
    682 	}
    683 	MUTEX_EXIT(&d->ipld_lock);
    684 	KFREES(temp, size);
    685 
    686 	return ESRCH;
    687 }
    688 
    689 
    690 /* ------------------------------------------------------------------------ */
    691 /* Function:    ipf_dstlist_node_free                                       */
    692 /* Returns:     Nil                                                         */
    693 /* Parameters:  softd(I) - pointer to the destination list context          */
    694 /*              d(I)     - pointer to destination list                      */
    695 /*              node(I)  - pointer to node to free                          */
    696 /* Locks:       MUTEX(ipld_lock) or WRITE(ipf_poolrw)                       */
    697 /*                                                                          */
    698 /* Free the destination node by first removing it from any lists and then   */
    699 /* checking if this was the last reference held to the object. While the    */
    700 /* array of pointers to nodes is compacted, its size isn't reduced (by way  */
    701 /* of allocating a new smaller one and copying) because the belief is that  */
    702 /* it is likely the array will again reach that size.                       */
    703 /* ------------------------------------------------------------------------ */
    704 static void
    705 ipf_dstlist_node_free(ipf_dstl_softc_t *softd, ippool_dst_t *d,
    706     ipf_dstnode_t *node)
    707 {
    708 	int i;
    709 
    710 	/*
    711 	 * Compact the array of pointers to nodes.
    712 	 */
    713 	for (i = 0; i < d->ipld_nodes; i++)
    714 		if (d->ipld_dests[i] == node)
    715 			break;
    716 	if (d->ipld_nodes - i > 1) {
    717 		bcopy(&d->ipld_dests[i + 1], &d->ipld_dests[i],
    718 		      sizeof(*d->ipld_dests) * (d->ipld_nodes - i - 1));
    719 	}
    720 	d->ipld_nodes--;
    721 
    722 	if (node->ipfd_pnext != NULL)
    723 		*node->ipfd_pnext = node->ipfd_next;
    724 	if (node->ipfd_next != NULL)
    725 		node->ipfd_next->ipfd_pnext = node->ipfd_pnext;
    726 	node->ipfd_pnext = NULL;
    727 	node->ipfd_next = NULL;
    728 
    729 	if ((node->ipfd_flags & IPDST_DELETE) == 0) {
    730 		softd->stats.ipls_numderefnodes++;
    731 		node->ipfd_flags |= IPDST_DELETE;
    732 	}
    733 
    734 	ipf_dstlist_node_deref(softd, node);
    735 }
    736 
    737 
    738 /* ------------------------------------------------------------------------ */
    739 /* Function:    ipf_dstlist_stats_get                                       */
    740 /* Returns:     int - 0 = success, else error                               */
    741 /* Parameters:  softc(I) - pointer to soft context main structure           */
    742 /*              arg(I)   - pointer to local context to use                  */
    743 /*              op(I)    - pointer to lookup operation data                 */
    744 /*                                                                          */
    745 /* Return the current statistics for destination lists. This may be for all */
    746 /* of them or just information pertaining to a particular table.            */
    747 /* ------------------------------------------------------------------------ */
    748 /*ARGSUSED*/
    749 static int
    750 ipf_dstlist_stats_get(ipf_main_softc_t *softc, void *arg, iplookupop_t *op)
    751 {
    752 	ipf_dstl_softc_t *softd = arg;
    753 	ipf_dstl_stat_t stats;
    754 	int unit, i, err = 0;
    755 
    756 	if (op->iplo_size != sizeof(ipf_dstl_stat_t)) {
    757 		IPFERROR(120023);
    758 		return EINVAL;
    759 	}
    760 
    761 	stats = softd->stats;
    762 	unit = op->iplo_unit;
    763 	if (unit == IPL_LOGALL) {
    764 		for (i = 0; i <= IPL_LOGMAX; i++)
    765 			stats.ipls_list[i] = softd->dstlist[i];
    766 	} else if (unit >= 0 && unit <= IPL_LOGMAX) {
    767 		void *ptr;
    768 
    769 		if (op->iplo_name[0] != '\0')
    770 			ptr = ipf_dstlist_table_find(softd, unit,
    771 						     op->iplo_name);
    772 		else
    773 			ptr = softd->dstlist[unit + 1];
    774 		stats.ipls_list[unit] = ptr;
    775 	} else {
    776 		IPFERROR(120024);
    777 		err = EINVAL;
    778 	}
    779 
    780 	if (err == 0) {
    781 		err = COPYOUT(&stats, op->iplo_struct, sizeof(stats));
    782 		if (err != 0) {
    783 			IPFERROR(120025);
    784 			return EFAULT;
    785 		}
    786 	}
    787 	return 0;
    788 }
    789 
    790 
    791 /* ------------------------------------------------------------------------ */
    792 /* Function:    ipf_dstlist_table_add                                       */
    793 /* Returns:     int      - 0 = success, else error                          */
    794 /* Parameters:  softc(I) - pointer to soft context main structure           */
    795 /*              arg(I)   - pointer to local context to use                  */
    796 /*              op(I)    - pointer to lookup operation data                 */
    797 /*                                                                          */
    798 /* Add a new destination table to the list of those available for the given */
    799 /* device. Because we seldom operate on these objects (find/add/delete),    */
    800 /* they are just kept in a simple linked list.                              */
    801 /* ------------------------------------------------------------------------ */
    802 static int
    803 ipf_dstlist_table_add(ipf_main_softc_t *softc, void *arg, iplookupop_t *op)
    804 {
    805 	ipf_dstl_softc_t *softd = arg;
    806 	ippool_dst_t user, *d, *new;
    807 	int unit, err;
    808 
    809 	d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name);
    810 	if (d != NULL) {
    811 		IPFERROR(120013);
    812 		return EEXIST;
    813 	}
    814 
    815 	err = COPYIN(op->iplo_struct, &user, sizeof(user));
    816 	if (err != 0) {
    817 		IPFERROR(120021);
    818 		return EFAULT;
    819 	}
    820 
    821 	KMALLOC(new, ippool_dst_t *);
    822 	if (new == NULL) {
    823 		softd->stats.ipls_nomem++;
    824 		IPFERROR(120014);
    825 		return ENOMEM;
    826 	}
    827 	bzero((char *)new, sizeof(*new));
    828 
    829 	MUTEX_INIT(&new->ipld_lock, "ipf dst table lock");
    830 
    831 	strncpy(new->ipld_name, op->iplo_name, FR_GROUPLEN);
    832 	unit = op->iplo_unit;
    833 	new->ipld_unit = unit;
    834 	new->ipld_policy = user.ipld_policy;
    835 	new->ipld_seed = ipf_random();
    836 	new->ipld_ref = 1;
    837 
    838 	new->ipld_pnext = softd->tails[unit + 1];
    839 	*softd->tails[unit + 1] = new;
    840 	softd->tails[unit + 1] = &new->ipld_next;
    841 	softd->stats.ipls_numlists++;
    842 
    843 	return 0;
    844 }
    845 
    846 
    847 /* ------------------------------------------------------------------------ */
    848 /* Function:    ipf_dstlist_table_del                                       */
    849 /* Returns:     int - 0 = success, else error                               */
    850 /* Parameters:  softc(I) - pointer to soft context main structure           */
    851 /*              arg(I)   - pointer to local context to use                  */
    852 /*              op(I)    - pointer to lookup operation data                 */
    853 /*                                                                          */
    854 /* Find a named destinstion list table and delete it. If there are other    */
    855 /* references to it, the caller isn't told.                                 */
    856 /* ------------------------------------------------------------------------ */
    857 static int
    858 ipf_dstlist_table_del(ipf_main_softc_t *softc, void *arg, iplookupop_t *op)
    859 {
    860 	ippool_dst_t *d;
    861 
    862 	d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name);
    863 	if (d == NULL) {
    864 		IPFERROR(120015);
    865 		return ESRCH;
    866 	}
    867 
    868 	if (d->ipld_dests != NULL) {
    869 		IPFERROR(120016);
    870 		return EBUSY;
    871 	}
    872 
    873 	ipf_dstlist_table_remove(softc, arg, d);
    874 
    875 	return 0;
    876 }
    877 
    878 
    879 /* ------------------------------------------------------------------------ */
    880 /* Function:    ipf_dstlist_table_remove                                    */
    881 /* Returns:     Nil                                                         */
    882 /* Parameters:  softc(I) - pointer to soft context main structure           */
    883 /*              softd(I) - pointer to the destination list context          */
    884 /*              d(I)     - pointer to destination list                      */
    885 /*                                                                          */
    886 /* Remove a given destination list from existance. While the IPDST_DELETE   */
    887 /* flag is set every time we call this function and the reference count is  */
    888 /* non-zero, the "numdereflists" counter is always incremented because the  */
    889 /* decision about whether it will be freed or not is not made here. This    */
    890 /* means that the only action the code can take here is to treat it as if   */
    891 /* it will become a detached.                                               */
    892 /* ------------------------------------------------------------------------ */
    893 static void
    894 ipf_dstlist_table_remove(ipf_main_softc_t *softc, ipf_dstl_softc_t *softd,
    895     ippool_dst_t *d)
    896 {
    897 
    898 	if (softd->tails[d->ipld_unit + 1] == &d->ipld_next)
    899 		softd->tails[d->ipld_unit + 1] = d->ipld_pnext;
    900 
    901 	if (d->ipld_pnext != NULL)
    902 		*d->ipld_pnext = d->ipld_next;
    903 	if (d->ipld_next != NULL)
    904 		d->ipld_next->ipld_pnext = d->ipld_pnext;
    905 	d->ipld_pnext = NULL;
    906 	d->ipld_next = NULL;
    907 
    908 	ipf_dstlist_table_clearnodes(softd, d);
    909 
    910 	softd->stats.ipls_numdereflists++;
    911 	d->ipld_flags |= IPDST_DELETE;
    912 
    913 	ipf_dstlist_table_deref(softc, softd, d);
    914 }
    915 
    916 
    917 /* ------------------------------------------------------------------------ */
    918 /* Function:    ipf_dstlist_table_free                                      */
    919 /* Returns:     Nil                                                         */
    920 /* Parameters:  softd(I) - pointer to the destination list context          */
    921 /*              d(I)   - pointer to destination list                        */
    922 /*                                                                          */
    923 /* Free up a destination list data structure and any other memory that was  */
    924 /* directly allocated as part of creating it. Individual destination list   */
    925 /* nodes are not freed. It is assumed the caller will have already emptied  */
    926 /* the destination list.                                                    */
    927 /* ------------------------------------------------------------------------ */
    928 static void
    929 ipf_dstlist_table_free(ipf_dstl_softc_t *softd, ippool_dst_t *d)
    930 {
    931 	MUTEX_DESTROY(&d->ipld_lock);
    932 
    933 	if ((d->ipld_flags & IPDST_DELETE) != 0)
    934 		softd->stats.ipls_numdereflists--;
    935 	softd->stats.ipls_numlists--;
    936 
    937 	if (d->ipld_dests != NULL) {
    938 		KFREES(d->ipld_dests,
    939 		       d->ipld_maxnodes * sizeof(*d->ipld_dests));
    940 	}
    941 
    942 	KFREE(d);
    943 }
    944 
    945 
    946 /* ------------------------------------------------------------------------ */
    947 /* Function:    ipf_dstlist_table_deref                                     */
    948 /* Returns:     int - 0 = success, else error                               */
    949 /* Parameters:  softc(I) - pointer to soft context main structure           */
    950 /*              arg(I)   - pointer to local context to use                  */
    951 /*              op(I)    - pointer to lookup operation data                 */
    952 /*                                                                          */
    953 /* Drops the reference count on a destination list table object and free's  */
    954 /* it if 0 has been reached.                                                */
    955 /* ------------------------------------------------------------------------ */
    956 static int
    957 ipf_dstlist_table_deref(ipf_main_softc_t *softc, void *arg, void *table)
    958 {
    959 	ippool_dst_t *d = table;
    960 
    961 	d->ipld_ref--;
    962 	if (d->ipld_ref > 0)
    963 		return d->ipld_ref;
    964 
    965 	ipf_dstlist_table_free(arg, d);
    966 
    967 	return 0;
    968 }
    969 
    970 
    971 /* ------------------------------------------------------------------------ */
    972 /* Function:    ipf_dstlist_table_clearnodes                                */
    973 /* Returns:     Nil                                                         */
    974 /* Parameters:  softd(I) - pointer to the destination list context          */
    975 /*              dst(I)   - pointer to destination list                      */
    976 /*                                                                          */
    977 /* Free all of the destination nodes attached to the given table.           */
    978 /* ------------------------------------------------------------------------ */
    979 static void
    980 ipf_dstlist_table_clearnodes(ipf_dstl_softc_t *softd, ippool_dst_t *dst)
    981 {
    982 	ipf_dstnode_t *node;
    983 
    984 	if (dst->ipld_dests == NULL)
    985 		return;
    986 
    987 	while ((node = *dst->ipld_dests) != NULL) {
    988 		ipf_dstlist_node_free(softd, dst, node);
    989 	}
    990 }
    991 
    992 
    993 /* ------------------------------------------------------------------------ */
    994 /* Function:    ipf_dstlist_table_find                                      */
    995 /* Returns:     int      - 0 = success, else error                          */
    996 /* Parameters:  arg(I)   - pointer to local context to use                  */
    997 /*              unit(I)  - device we are working with                       */
    998 /*              name(I)  - destination table name to find                   */
    999 /*                                                                          */
   1000 /* Return a pointer to a destination table that matches the unit+name that  */
   1001 /* is passed in.                                                            */
   1002 /* ------------------------------------------------------------------------ */
   1003 static void *
   1004 ipf_dstlist_table_find(void *arg, int unit, char *name)
   1005 {
   1006 	ipf_dstl_softc_t *softd = arg;
   1007 	ippool_dst_t *d;
   1008 
   1009 	for (d = softd->dstlist[unit + 1]; d != NULL; d = d->ipld_next) {
   1010 		if ((d->ipld_unit == unit) &&
   1011 		    !strncmp(d->ipld_name, name, FR_GROUPLEN)) {
   1012 			return d;
   1013 		}
   1014 	}
   1015 
   1016 	return NULL;
   1017 }
   1018 
   1019 
   1020 /* ------------------------------------------------------------------------ */
   1021 /* Function:    ipf_dstlist_select_ref                                      */
   1022 /* Returns:     void *   - NULL = failure, else pointer to table            */
   1023 /* Parameters:  arg(I)   - pointer to local context to use                  */
   1024 /*              unit(I)  - device we are working with                       */
   1025 /*              name(I)  - destination table name to find                   */
   1026 /*                                                                          */
   1027 /* Attempt to find a destination table that matches the name passed in and  */
   1028 /* if successful, bump up the reference count on it because we intend to    */
   1029 /* store the pointer to it somewhere else.                                  */
   1030 /* ------------------------------------------------------------------------ */
   1031 static void *
   1032 ipf_dstlist_select_ref(void *arg, int unit, char *name)
   1033 {
   1034 	ippool_dst_t *d;
   1035 
   1036 	d = ipf_dstlist_table_find(arg, unit, name);
   1037 	if (d != NULL) {
   1038 		MUTEX_ENTER(&d->ipld_lock);
   1039 		d->ipld_ref++;
   1040 		MUTEX_EXIT(&d->ipld_lock);
   1041 	}
   1042 	return d;
   1043 }
   1044 
   1045 
   1046 /* ------------------------------------------------------------------------ */
   1047 /* Function:    ipf_dstlist_select                                          */
   1048 /* Returns:     void * - NULL = failure, else pointer to table              */
   1049 /* Parameters:  fin(I) - pointer to packet information                      */
   1050 /*              d(I)   - pointer to destination list                        */
   1051 /*                                                                          */
   1052 /* Find the next node in the destination list to be used according to the   */
   1053 /* defined policy. Of these, "connection" is the most expensive policy to   */
   1054 /* implement as it always looks for the node with the least number of       */
   1055 /* connections associated with it.                                          */
   1056 /*                                                                          */
   1057 /* The hashes exclude the port numbers so that all protocols map to the     */
   1058 /* same destination. Otherwise, someone doing a ping would target a         */
   1059 /* different server than their TCP connection, etc. MD-5 is used to         */
   1060 /* transform the addressese into something random that the other end could  */
   1061 /* not easily guess and use in an attack. ipld_seed introduces an unknown   */
   1062 /* into the hash calculation to increase the difficult of an attacker       */
   1063 /* guessing the bucket.                                                     */
   1064 /*                                                                          */
   1065 /* One final comment: mixing different address families in a single pool    */
   1066 /* will currently result in failures as the address family of the node is   */
   1067 /* only matched up with that in the packet as the last step. While this can */
   1068 /* be coded around for the weighted connection and round-robin models, it   */
   1069 /* cannot be supported for the hash/random models as they do not search and */
   1070 /* nor is the algorithm conducive to searching.                             */
   1071 /* ------------------------------------------------------------------------ */
   1072 static ipf_dstnode_t *
   1073 ipf_dstlist_select(fr_info_t *fin, ippool_dst_t *d)
   1074 {
   1075 	ipf_dstnode_t *node, *sel;
   1076 	int connects;
   1077 	union {
   1078 	    u_32_t hash[4];
   1079 	    unsigned char bytes[16];
   1080 	} h;
   1081 	MD5_CTX ctx;
   1082 	int family;
   1083 	int x;
   1084 
   1085 	if (d == NULL || d->ipld_dests == NULL || *d->ipld_dests == NULL)
   1086 		return NULL;
   1087 
   1088 	family = fin->fin_family;
   1089 
   1090 	MUTEX_ENTER(&d->ipld_lock);
   1091 
   1092 	switch (d->ipld_policy)
   1093 	{
   1094 	case IPLDP_ROUNDROBIN:
   1095 		sel = d->ipld_selected;
   1096 		if (sel == NULL) {
   1097 			sel = *d->ipld_dests;
   1098 		} else {
   1099 			sel = sel->ipfd_next;
   1100 			if (sel == NULL)
   1101 				sel = *d->ipld_dests;
   1102 		}
   1103 		break;
   1104 
   1105 	case IPLDP_CONNECTION:
   1106 		if (d->ipld_selected == NULL) {
   1107 			sel = *d->ipld_dests;
   1108 			break;
   1109 		}
   1110 
   1111 		sel = d->ipld_selected;
   1112 		connects = 0x7fffffff;
   1113 		node = sel->ipfd_next;
   1114 		if (node == NULL)
   1115 			node = *d->ipld_dests;
   1116 		while (node != d->ipld_selected) {
   1117 			if (node->ipfd_states == 0) {
   1118 				sel = node;
   1119 				break;
   1120 			}
   1121 			if (node->ipfd_states < connects) {
   1122 				sel = node;
   1123 				connects = node->ipfd_states;
   1124 			}
   1125 			node = node->ipfd_next;
   1126 			if (node == NULL)
   1127 				node = *d->ipld_dests;
   1128 		}
   1129 		break;
   1130 
   1131 	case IPLDP_RANDOM :
   1132 		x = ipf_random() % d->ipld_nodes;
   1133 		sel = d->ipld_dests[x];
   1134 		break;
   1135 
   1136 	case IPLDP_HASHED :
   1137 		MD5Init(&ctx);
   1138 		MD5Update(&ctx, (u_char *)&d->ipld_seed, sizeof(d->ipld_seed));
   1139 		MD5Update(&ctx, (u_char *)&fin->fin_src6,
   1140 			  sizeof(fin->fin_src6));
   1141 		MD5Update(&ctx, (u_char *)&fin->fin_dst6,
   1142 			  sizeof(fin->fin_dst6));
   1143 		MD5Final(h.bytes, &ctx);
   1144 		x = ntohl(h.hash[0]) % d->ipld_nodes;
   1145 		sel = d->ipld_dests[x];
   1146 		break;
   1147 
   1148 	case IPLDP_SRCHASH :
   1149 		MD5Init(&ctx);
   1150 		MD5Update(&ctx, (u_char *)&d->ipld_seed, sizeof(d->ipld_seed));
   1151 		MD5Update(&ctx, (u_char *)&fin->fin_src6,
   1152 			  sizeof(fin->fin_src6));
   1153 		MD5Final(h.bytes, &ctx);
   1154 		x = ntohl(h.hash[0]) % d->ipld_nodes;
   1155 		sel = d->ipld_dests[x];
   1156 		break;
   1157 
   1158 	case IPLDP_DSTHASH :
   1159 		MD5Init(&ctx);
   1160 		MD5Update(&ctx, (u_char *)&d->ipld_seed, sizeof(d->ipld_seed));
   1161 		MD5Update(&ctx, (u_char *)&fin->fin_dst6,
   1162 			  sizeof(fin->fin_dst6));
   1163 		MD5Final(h.bytes, &ctx);
   1164 		x = ntohl(h.hash[0]) % d->ipld_nodes;
   1165 		sel = d->ipld_dests[x];
   1166 		break;
   1167 
   1168 	default :
   1169 		sel = NULL;
   1170 		break;
   1171 	}
   1172 
   1173 	if (sel && sel->ipfd_dest.fd_addr.adf_family != family)
   1174 		sel = NULL;
   1175 	d->ipld_selected = sel;
   1176 
   1177 	MUTEX_EXIT(&d->ipld_lock);
   1178 
   1179 	return sel;
   1180 }
   1181 
   1182 
   1183 /* ------------------------------------------------------------------------ */
   1184 /* Function:    ipf_dstlist_select_node                                     */
   1185 /* Returns:     int      - -1 == failure, 0 == success                      */
   1186 /* Parameters:  fin(I)   - pointer to packet information                    */
   1187 /*              group(I) - destination pool to search                       */
   1188 /*              addr(I)  - pointer to store selected address                */
   1189 /*              pfdp(O)  - pointer to storage for selected destination node */
   1190 /*                                                                          */
   1191 /* This function is only responsible for obtaining the next IP address for  */
   1192 /* use and storing it in the caller's address space (addr). "addr" is only  */
   1193 /* used for storage if pfdp is NULL. No permanent reference is currently    */
   1194 /* kept on the node.                                                        */
   1195 /* ------------------------------------------------------------------------ */
   1196 int
   1197 ipf_dstlist_select_node(fr_info_t *fin, void *group, u_32_t *addr,
   1198     frdest_t *pfdp)
   1199 {
   1200 #ifdef USE_MUTEXES
   1201 	ipf_main_softc_t *softc = fin->fin_main_soft;
   1202 #endif
   1203 	ippool_dst_t *d = group;
   1204 	ipf_dstnode_t *node;
   1205 	frdest_t *fdp;
   1206 
   1207 	READ_ENTER(&softc->ipf_poolrw);
   1208 
   1209 	node = ipf_dstlist_select(fin, d);
   1210 	if (node == NULL) {
   1211 		RWLOCK_EXIT(&softc->ipf_poolrw);
   1212 		return -1;
   1213 	}
   1214 
   1215 	if (pfdp != NULL) {
   1216 		bcopy(&node->ipfd_dest, pfdp, sizeof(*pfdp));
   1217 	} else {
   1218 		if (fin->fin_family == AF_INET) {
   1219 			addr[0] = node->ipfd_dest.fd_addr.adf_addr.i6[0];
   1220 		} else if (fin->fin_family == AF_INET6) {
   1221 			addr[0] = node->ipfd_dest.fd_addr.adf_addr.i6[0];
   1222 			addr[1] = node->ipfd_dest.fd_addr.adf_addr.i6[1];
   1223 			addr[2] = node->ipfd_dest.fd_addr.adf_addr.i6[2];
   1224 			addr[3] = node->ipfd_dest.fd_addr.adf_addr.i6[3];
   1225 		}
   1226 	}
   1227 
   1228 	fdp = &node->ipfd_dest;
   1229 	if (fdp->fd_ptr == NULL)
   1230 		fdp->fd_ptr = fin->fin_ifp;
   1231 
   1232 	MUTEX_ENTER(&node->ipfd_lock);
   1233 	node->ipfd_states++;
   1234 	MUTEX_EXIT(&node->ipfd_lock);
   1235 
   1236 	RWLOCK_EXIT(&softc->ipf_poolrw);
   1237 
   1238 	return 0;
   1239 }
   1240 
   1241 
   1242 /* ------------------------------------------------------------------------ */
   1243 /* Function:    ipf_dstlist_expire                                          */
   1244 /* Returns:     Nil                                                         */
   1245 /* Parameters:  softc(I) - pointer to soft context main structure           */
   1246 /*              arg(I)   - pointer to local context to use                  */
   1247 /*                                                                          */
   1248 /* There are currently no objects to expire in destination lists.           */
   1249 /* ------------------------------------------------------------------------ */
   1250 static void
   1251 ipf_dstlist_expire(ipf_main_softc_t *softc, void *arg)
   1252 {
   1253 	return;
   1254 }
   1255 
   1256 
   1257 /* ------------------------------------------------------------------------ */
   1258 /* Function:    ipf_dstlist_sync                                            */
   1259 /* Returns:     Nil                                                         */
   1260 /* Parameters:  softc(I) - pointer to soft context main structure           */
   1261 /*              arg(I)   - pointer to local context to use                  */
   1262 /*                                                                          */
   1263 /* When a network interface appears or disappears, we need to revalidate    */
   1264 /* all of the network interface names that have been configured as a target */
   1265 /* in a destination list.                                                   */
   1266 /* ------------------------------------------------------------------------ */
   1267 void
   1268 ipf_dstlist_sync(ipf_main_softc_t *softc, void *arg)
   1269 {
   1270 	ipf_dstl_softc_t *softd = arg;
   1271 	ipf_dstnode_t *node;
   1272 	ippool_dst_t *list;
   1273 	int i;
   1274 	int j;
   1275 
   1276 	for (i = 0; i < IPL_LOGMAX; i++) {
   1277 		for (list = softd->dstlist[i]; list != NULL;
   1278 		     list = list->ipld_next) {
   1279 			for (j = 0; j < list->ipld_maxnodes; j++) {
   1280 				node = list->ipld_dests[j];
   1281 				if (node == NULL)
   1282 					continue;
   1283 				if (node->ipfd_dest.fd_name == -1)
   1284 					continue;
   1285 				(void) ipf_resolvedest(softc,
   1286 						       node->ipfd_names,
   1287 						       &node->ipfd_dest,
   1288 						       AF_INET);
   1289 			}
   1290 		}
   1291 	}
   1292 }
   1293