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