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