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