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