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