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		return EFAULT;
694	}
695
696	MUTEX_ENTER(&d->ipld_lock);
697	for (node = *d->ipld_dests; node != NULL; node = node->ipfd_next) {
698		if ((uid != 0) && (node->ipfd_uid != uid))
699			continue;
700		if (node->ipfd_size != size)
701			continue;
702		if (!bcmp(&node->ipfd_dest.fd_ip6, &frd.fd_ip6,
703			  size - offsetof(frdest_t, fd_ip6))) {
704			ipf_dstlist_node_free(softd, d, node);
705			MUTEX_EXIT(&d->ipld_lock);
706			KFREES(temp, size);
707			return 0;
708		}
709	}
710	MUTEX_EXIT(&d->ipld_lock);
711	KFREES(temp, size);
712
713	return ESRCH;
714}
715
716
717/* ------------------------------------------------------------------------ */
718/* Function:    ipf_dstlist_node_free                                       */
719/* Returns:     Nil                                                         */
720/* Parameters:  softd(I) - pointer to the destination list context          */
721/*              d(I)     - pointer to destination list                      */
722/*              node(I)  - pointer to node to free                          */
723/* Locks:       MUTEX(ipld_lock) or WRITE(ipf_poolrw)                       */
724/*                                                                          */
725/* Free the destination node by first removing it from any lists and then   */
726/* checking if this was the last reference held to the object. While the    */
727/* array of pointers to nodes is compacted, its size isn't reduced (by way  */
728/* of allocating a new smaller one and copying) because the belief is that  */
729/* it is likely the array will again reach that size.                       */
730/* ------------------------------------------------------------------------ */
731static void
732ipf_dstlist_node_free(softd, d, node)
733	ipf_dstl_softc_t *softd;
734	ippool_dst_t *d;
735	ipf_dstnode_t *node;
736{
737	int i;
738
739	/*
740	 * Compact the array of pointers to nodes.
741	 */
742	for (i = 0; i < d->ipld_nodes; i++)
743		if (d->ipld_dests[i] == node)
744			break;
745	if (d->ipld_nodes - i > 1) {
746		bcopy(&d->ipld_dests[i + 1], &d->ipld_dests[i],
747		      sizeof(*d->ipld_dests) * (d->ipld_nodes - i - 1));
748	}
749	d->ipld_nodes--;
750
751	if (node->ipfd_pnext != NULL)
752		*node->ipfd_pnext = node->ipfd_next;
753	if (node->ipfd_next != NULL)
754		node->ipfd_next->ipfd_pnext = node->ipfd_pnext;
755	node->ipfd_pnext = NULL;
756	node->ipfd_next = NULL;
757
758	if ((node->ipfd_flags & IPDST_DELETE) == 0) {
759		softd->stats.ipls_numderefnodes++;
760		node->ipfd_flags |= IPDST_DELETE;
761	}
762
763	ipf_dstlist_node_deref(softd, node);
764}
765
766
767/* ------------------------------------------------------------------------ */
768/* Function:    ipf_dstlist_stats_get                                       */
769/* Returns:     int - 0 = success, else error                               */
770/* Parameters:  softc(I) - pointer to soft context main structure           */
771/*              arg(I)   - pointer to local context to use                  */
772/*              op(I)    - pointer to lookup operation data                 */
773/*                                                                          */
774/* Return the current statistics for destination lists. This may be for all */
775/* of them or just information pertaining to a particular table.            */
776/* ------------------------------------------------------------------------ */
777/*ARGSUSED*/
778static int
779ipf_dstlist_stats_get(softc, arg, op)
780	ipf_main_softc_t *softc;
781	void *arg;
782	iplookupop_t *op;
783{
784	ipf_dstl_softc_t *softd = arg;
785	ipf_dstl_stat_t stats;
786	int unit, i, err = 0;
787
788	if (op->iplo_size != sizeof(ipf_dstl_stat_t)) {
789		IPFERROR(120023);
790		return EINVAL;
791	}
792
793	stats = softd->stats;
794	unit = op->iplo_unit;
795	if (unit == IPL_LOGALL) {
796		for (i = 0; i <= IPL_LOGMAX; i++)
797			stats.ipls_list[i] = softd->dstlist[i];
798	} else if (unit >= 0 && unit <= IPL_LOGMAX) {
799		void *ptr;
800
801		if (op->iplo_name[0] != '\0')
802			ptr = ipf_dstlist_table_find(softd, unit,
803						     op->iplo_name);
804		else
805			ptr = softd->dstlist[unit + 1];
806		stats.ipls_list[unit] = ptr;
807	} else {
808		IPFERROR(120024);
809		err = EINVAL;
810	}
811
812	if (err == 0) {
813		err = COPYOUT(&stats, op->iplo_struct, sizeof(stats));
814		if (err != 0) {
815			IPFERROR(120025);
816			return EFAULT;
817		}
818	}
819	return 0;
820}
821
822
823/* ------------------------------------------------------------------------ */
824/* Function:    ipf_dstlist_table_add                                       */
825/* Returns:     int      - 0 = success, else error                          */
826/* Parameters:  softc(I) - pointer to soft context main structure           */
827/*              arg(I)   - pointer to local context to use                  */
828/*              op(I)    - pointer to lookup operation data                 */
829/*                                                                          */
830/* Add a new destination table to the list of those available for the given */
831/* device. Because we seldom operate on these objects (find/add/delete),    */
832/* they are just kept in a simple linked list.                              */
833/* ------------------------------------------------------------------------ */
834static int
835ipf_dstlist_table_add(softc, arg, op)
836	ipf_main_softc_t *softc;
837	void *arg;
838	iplookupop_t *op;
839{
840	ipf_dstl_softc_t *softd = arg;
841	ippool_dst_t user, *d, *new;
842	int unit, err;
843
844	d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name);
845	if (d != NULL) {
846		IPFERROR(120013);
847		return EEXIST;
848	}
849
850	err = COPYIN(op->iplo_struct, &user, sizeof(user));
851	if (err != 0) {
852		IPFERROR(120021);
853		return EFAULT;
854	}
855
856	KMALLOC(new, ippool_dst_t *);
857	if (new == NULL) {
858		softd->stats.ipls_nomem++;
859		IPFERROR(120014);
860		return ENOMEM;
861	}
862	bzero((char *)new, sizeof(*new));
863
864	MUTEX_INIT(&new->ipld_lock, "ipf dst table lock");
865
866	strncpy(new->ipld_name, op->iplo_name, FR_GROUPLEN);
867	unit = op->iplo_unit;
868	new->ipld_unit = unit;
869	new->ipld_policy = user.ipld_policy;
870	new->ipld_seed = ipf_random();
871	new->ipld_ref = 1;
872
873	new->ipld_pnext = softd->tails[unit + 1];
874	*softd->tails[unit + 1] = new;
875	softd->tails[unit + 1] = &new->ipld_next;
876	softd->stats.ipls_numlists++;
877
878	return 0;
879}
880
881
882/* ------------------------------------------------------------------------ */
883/* Function:    ipf_dstlist_table_del                                       */
884/* Returns:     int - 0 = success, else error                               */
885/* Parameters:  softc(I) - pointer to soft context main structure           */
886/*              arg(I)   - pointer to local context to use                  */
887/*              op(I)    - pointer to lookup operation data                 */
888/*                                                                          */
889/* Find a named destinstion list table and delete it. If there are other    */
890/* references to it, the caller isn't told.                                 */
891/* ------------------------------------------------------------------------ */
892static int
893ipf_dstlist_table_del(softc, arg, op)
894	ipf_main_softc_t *softc;
895	void *arg;
896	iplookupop_t *op;
897{
898	ippool_dst_t *d;
899
900	d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name);
901	if (d == NULL) {
902		IPFERROR(120015);
903		return ESRCH;
904	}
905
906	if (d->ipld_dests != NULL) {
907		IPFERROR(120016);
908		return EBUSY;
909	}
910
911	ipf_dstlist_table_remove(softc, arg, d);
912
913	return 0;
914}
915
916
917/* ------------------------------------------------------------------------ */
918/* Function:    ipf_dstlist_table_remove                                    */
919/* Returns:     Nil                                                         */
920/* Parameters:  softc(I) - pointer to soft context main structure           */
921/*              softd(I) - pointer to the destination list context          */
922/*              d(I)     - pointer to destination list                      */
923/*                                                                          */
924/* Remove a given destination list from existance. While the IPDST_DELETE   */
925/* flag is set every time we call this function and the reference count is  */
926/* non-zero, the "numdereflists" counter is always incremented because the  */
927/* decision about whether it will be freed or not is not made here. This    */
928/* means that the only action the code can take here is to treat it as if   */
929/* it will become a detached.                                               */
930/* ------------------------------------------------------------------------ */
931static void
932ipf_dstlist_table_remove(softc, softd, d)
933	ipf_main_softc_t *softc;
934	ipf_dstl_softc_t *softd;
935	ippool_dst_t *d;
936{
937
938	if (softd->tails[d->ipld_unit + 1] == &d->ipld_next)
939		softd->tails[d->ipld_unit + 1] = d->ipld_pnext;
940
941	if (d->ipld_pnext != NULL)
942		*d->ipld_pnext = d->ipld_next;
943	if (d->ipld_next != NULL)
944		d->ipld_next->ipld_pnext = d->ipld_pnext;
945	d->ipld_pnext = NULL;
946	d->ipld_next = NULL;
947
948	ipf_dstlist_table_clearnodes(softd, d);
949
950	softd->stats.ipls_numdereflists++;
951	d->ipld_flags |= IPDST_DELETE;
952
953	ipf_dstlist_table_deref(softc, softd, d);
954}
955
956
957/* ------------------------------------------------------------------------ */
958/* Function:    ipf_dstlist_table_free                                      */
959/* Returns:     Nil                                                         */
960/* Parameters:  softd(I) - pointer to the destination list context          */
961/*              d(I)   - pointer to destination list                        */
962/*                                                                          */
963/* Free up a destination list data structure and any other memory that was  */
964/* directly allocated as part of creating it. Individual destination list   */
965/* nodes are not freed. It is assumed the caller will have already emptied  */
966/* the destination list.                                                    */
967/* ------------------------------------------------------------------------ */
968static void
969ipf_dstlist_table_free(softd, d)
970	ipf_dstl_softc_t *softd;
971	ippool_dst_t *d;
972{
973	MUTEX_DESTROY(&d->ipld_lock);
974
975	if ((d->ipld_flags & IPDST_DELETE) != 0)
976		softd->stats.ipls_numdereflists--;
977	softd->stats.ipls_numlists--;
978
979	if (d->ipld_dests != NULL) {
980		KFREES(d->ipld_dests,
981		       d->ipld_maxnodes * sizeof(*d->ipld_dests));
982	}
983
984	KFREE(d);
985}
986
987
988/* ------------------------------------------------------------------------ */
989/* Function:    ipf_dstlist_table_deref                                     */
990/* Returns:     int - 0 = success, else error                               */
991/* Parameters:  softc(I) - pointer to soft context main structure           */
992/*              arg(I)   - pointer to local context to use                  */
993/*              op(I)    - pointer to lookup operation data                 */
994/*                                                                          */
995/* Drops the reference count on a destination list table object and free's  */
996/* it if 0 has been reached.                                                */
997/* ------------------------------------------------------------------------ */
998static int
999ipf_dstlist_table_deref(softc, arg, table)
1000	ipf_main_softc_t *softc;
1001	void *arg;
1002	void *table;
1003{
1004	ippool_dst_t *d = table;
1005
1006	d->ipld_ref--;
1007	if (d->ipld_ref > 0)
1008		return d->ipld_ref;
1009
1010	ipf_dstlist_table_free(arg, d);
1011
1012	return 0;
1013}
1014
1015
1016/* ------------------------------------------------------------------------ */
1017/* Function:    ipf_dstlist_table_clearnodes                                */
1018/* Returns:     Nil                                                         */
1019/* Parameters:  softd(I) - pointer to the destination list context          */
1020/*              dst(I)   - pointer to destination list                      */
1021/*                                                                          */
1022/* Free all of the destination nodes attached to the given table.           */
1023/* ------------------------------------------------------------------------ */
1024static void
1025ipf_dstlist_table_clearnodes(softd, dst)
1026	ipf_dstl_softc_t *softd;
1027	ippool_dst_t *dst;
1028{
1029	ipf_dstnode_t *node;
1030
1031	if (dst->ipld_dests == NULL)
1032		return;
1033
1034	while ((node = *dst->ipld_dests) != NULL) {
1035		ipf_dstlist_node_free(softd, dst, node);
1036	}
1037}
1038
1039
1040/* ------------------------------------------------------------------------ */
1041/* Function:    ipf_dstlist_table_find                                      */
1042/* Returns:     int      - 0 = success, else error                          */
1043/* Parameters:  arg(I)   - pointer to local context to use                  */
1044/*              unit(I)  - device we are working with                       */
1045/*              name(I)  - destination table name to find                   */
1046/*                                                                          */
1047/* Return a pointer to a destination table that matches the unit+name that  */
1048/* is passed in.                                                            */
1049/* ------------------------------------------------------------------------ */
1050static void *
1051ipf_dstlist_table_find(arg, unit, name)
1052	void *arg;
1053	int unit;
1054	char *name;
1055{
1056	ipf_dstl_softc_t *softd = arg;
1057	ippool_dst_t *d;
1058
1059	for (d = softd->dstlist[unit + 1]; d != NULL; d = d->ipld_next) {
1060		if ((d->ipld_unit == unit) &&
1061		    !strncmp(d->ipld_name, name, FR_GROUPLEN)) {
1062			return d;
1063		}
1064	}
1065
1066	return NULL;
1067}
1068
1069
1070/* ------------------------------------------------------------------------ */
1071/* Function:    ipf_dstlist_select_ref                                      */
1072/* Returns:     void *   - NULL = failure, else pointer to table            */
1073/* Parameters:  arg(I)   - pointer to local context to use                  */
1074/*              unit(I)  - device we are working with                       */
1075/*              name(I)  - destination table name to find                   */
1076/*                                                                          */
1077/* Attempt to find a destination table that matches the name passed in and  */
1078/* if successful, bump up the reference count on it because we intend to    */
1079/* store the pointer to it somewhere else.                                  */
1080/* ------------------------------------------------------------------------ */
1081static void *
1082ipf_dstlist_select_ref(arg, unit, name)
1083	void *arg;
1084	int unit;
1085	char *name;
1086{
1087	ippool_dst_t *d;
1088
1089	d = ipf_dstlist_table_find(arg, unit, name);
1090	if (d != NULL) {
1091		MUTEX_ENTER(&d->ipld_lock);
1092		d->ipld_ref++;
1093		MUTEX_EXIT(&d->ipld_lock);
1094	}
1095	return d;
1096}
1097
1098
1099/* ------------------------------------------------------------------------ */
1100/* Function:    ipf_dstlist_select                                          */
1101/* Returns:     void * - NULL = failure, else pointer to table              */
1102/* Parameters:  fin(I) - pointer to packet information                      */
1103/*              d(I)   - pointer to destination list                        */
1104/*                                                                          */
1105/* Find the next node in the destination list to be used according to the   */
1106/* defined policy. Of these, "connection" is the most expensive policy to   */
1107/* implement as it always looks for the node with the least number of       */
1108/* connections associated with it.                                          */
1109/*                                                                          */
1110/* The hashes exclude the port numbers so that all protocols map to the     */
1111/* same destination. Otherwise, someone doing a ping would target a         */
1112/* different server than their TCP connection, etc. MD-5 is used to         */
1113/* transform the addressese into something random that the other end could  */
1114/* not easily guess and use in an attack. ipld_seed introduces an unknown   */
1115/* into the hash calculation to increase the difficult of an attacker       */
1116/* guessing the bucket.                                                     */
1117/*                                                                          */
1118/* One final comment: mixing different address families in a single pool    */
1119/* will currently result in failures as the address family of the node is   */
1120/* only matched up with that in the packet as the last step. While this can */
1121/* be coded around for the weighted connection and round-robin models, it   */
1122/* cannot be supported for the hash/random models as they do not search and */
1123/* nor is the algorithm conducive to searching.                             */
1124/* ------------------------------------------------------------------------ */
1125static ipf_dstnode_t *
1126ipf_dstlist_select(fin, d)
1127	fr_info_t *fin;
1128	ippool_dst_t *d;
1129{
1130	ipf_dstnode_t *node, *sel;
1131	int connects;
1132	u_32_t hash[4];
1133	MD5_CTX ctx;
1134	int family;
1135	int x;
1136
1137	if (d->ipld_dests == NULL || *d->ipld_dests == NULL)
1138		return NULL;
1139
1140	family = fin->fin_family;
1141
1142	MUTEX_ENTER(&d->ipld_lock);
1143
1144	switch (d->ipld_policy)
1145	{
1146	case IPLDP_ROUNDROBIN:
1147		sel = d->ipld_selected;
1148		if (sel == NULL) {
1149			sel = *d->ipld_dests;
1150		} else {
1151			sel = sel->ipfd_next;
1152			if (sel == NULL)
1153				sel = *d->ipld_dests;
1154		}
1155		break;
1156
1157	case IPLDP_CONNECTION:
1158		if (d->ipld_selected == NULL) {
1159			sel = *d->ipld_dests;
1160			break;
1161		}
1162
1163		sel = d->ipld_selected;
1164		connects = 0x7fffffff;
1165		node = sel->ipfd_next;
1166		if (node == NULL)
1167			node = *d->ipld_dests;
1168		while (node != d->ipld_selected) {
1169			if (node->ipfd_states == 0) {
1170				sel = node;
1171				break;
1172			}
1173			if (node->ipfd_states < connects) {
1174				sel = node;
1175				connects = node->ipfd_states;
1176			}
1177			node = node->ipfd_next;
1178			if (node == NULL)
1179				node = *d->ipld_dests;
1180		}
1181		break;
1182
1183	case IPLDP_RANDOM :
1184		x = ipf_random() % d->ipld_nodes;
1185		sel = d->ipld_dests[x];
1186		break;
1187
1188	case IPLDP_HASHED :
1189		MD5Init(&ctx);
1190		MD5Update(&ctx, (u_char *)&d->ipld_seed, sizeof(d->ipld_seed));
1191		MD5Update(&ctx, (u_char *)&fin->fin_src6,
1192			  sizeof(fin->fin_src6));
1193		MD5Update(&ctx, (u_char *)&fin->fin_dst6,
1194			  sizeof(fin->fin_dst6));
1195		MD5Final((u_char *)hash, &ctx);
1196		x = hash[0] % d->ipld_nodes;
1197		sel = d->ipld_dests[x];
1198		break;
1199
1200	case IPLDP_SRCHASH :
1201		MD5Init(&ctx);
1202		MD5Update(&ctx, (u_char *)&d->ipld_seed, sizeof(d->ipld_seed));
1203		MD5Update(&ctx, (u_char *)&fin->fin_src6,
1204			  sizeof(fin->fin_src6));
1205		MD5Final((u_char *)hash, &ctx);
1206		x = hash[0] % d->ipld_nodes;
1207		sel = d->ipld_dests[x];
1208		break;
1209
1210	case IPLDP_DSTHASH :
1211		MD5Init(&ctx);
1212		MD5Update(&ctx, (u_char *)&d->ipld_seed, sizeof(d->ipld_seed));
1213		MD5Update(&ctx, (u_char *)&fin->fin_dst6,
1214			  sizeof(fin->fin_dst6));
1215		MD5Final((u_char *)hash, &ctx);
1216		x = hash[0] % d->ipld_nodes;
1217		sel = d->ipld_dests[x];
1218		break;
1219
1220	default :
1221		sel = NULL;
1222		break;
1223	}
1224
1225	if (sel->ipfd_dest.fd_addr.adf_family != family)
1226		sel = NULL;
1227	d->ipld_selected = sel;
1228
1229	MUTEX_EXIT(&d->ipld_lock);
1230
1231	return sel;
1232}
1233
1234
1235/* ------------------------------------------------------------------------ */
1236/* Function:    ipf_dstlist_select_node                                     */
1237/* Returns:     int      - -1 == failure, 0 == success                      */
1238/* Parameters:  fin(I)   - pointer to packet information                    */
1239/*              group(I) - destination pool to search                       */
1240/*              addr(I)  - pointer to store selected address                */
1241/*              pfdp(O)  - pointer to storage for selected destination node */
1242/*                                                                          */
1243/* This function is only responsible for obtaining the next IP address for  */
1244/* use and storing it in the caller's address space (addr). "addr" is only  */
1245/* used for storage if pfdp is NULL. No permanent reference is currently    */
1246/* kept on the node.                                                        */
1247/* ------------------------------------------------------------------------ */
1248int
1249ipf_dstlist_select_node(fin, group, addr, pfdp)
1250	fr_info_t *fin;
1251	void *group;
1252	u_32_t *addr;
1253	frdest_t *pfdp;
1254{
1255#ifdef USE_MUTEXES
1256	ipf_main_softc_t *softc = fin->fin_main_soft;
1257#endif
1258	ippool_dst_t *d = group;
1259	ipf_dstnode_t *node;
1260	frdest_t *fdp;
1261
1262	READ_ENTER(&softc->ipf_poolrw);
1263
1264	node = ipf_dstlist_select(fin, d);
1265	if (node == NULL) {
1266		RWLOCK_EXIT(&softc->ipf_poolrw);
1267		return -1;
1268	}
1269
1270	if (pfdp != NULL) {
1271		bcopy(&node->ipfd_dest, pfdp, sizeof(*pfdp));
1272	} else {
1273		if (fin->fin_family == AF_INET) {
1274			addr[0] = node->ipfd_dest.fd_addr.adf_addr.i6[0];
1275		} else if (fin->fin_family == AF_INET6) {
1276			addr[0] = node->ipfd_dest.fd_addr.adf_addr.i6[0];
1277			addr[1] = node->ipfd_dest.fd_addr.adf_addr.i6[1];
1278			addr[2] = node->ipfd_dest.fd_addr.adf_addr.i6[2];
1279			addr[3] = node->ipfd_dest.fd_addr.adf_addr.i6[3];
1280		}
1281	}
1282
1283	fdp = &node->ipfd_dest;
1284	if (fdp->fd_ptr == NULL)
1285		fdp->fd_ptr = fin->fin_ifp;
1286
1287	MUTEX_ENTER(&node->ipfd_lock);
1288	node->ipfd_states++;
1289	MUTEX_EXIT(&node->ipfd_lock);
1290
1291	RWLOCK_EXIT(&softc->ipf_poolrw);
1292
1293	return 0;
1294}
1295
1296
1297/* ------------------------------------------------------------------------ */
1298/* Function:    ipf_dstlist_expire                                          */
1299/* Returns:     Nil                                                         */
1300/* Parameters:  softc(I) - pointer to soft context main structure           */
1301/*              arg(I)   - pointer to local context to use                  */
1302/*                                                                          */
1303/* There are currently no objects to expire in destination lists.           */
1304/* ------------------------------------------------------------------------ */
1305static void
1306ipf_dstlist_expire(softc, arg)
1307	ipf_main_softc_t *softc;
1308	void *arg;
1309{
1310	return;
1311}
1312
1313
1314/* ------------------------------------------------------------------------ */
1315/* Function:    ipf_dstlist_sync                                            */
1316/* Returns:     Nil                                                         */
1317/* Parameters:  softc(I) - pointer to soft context main structure           */
1318/*              arg(I)   - pointer to local context to use                  */
1319/*                                                                          */
1320/* When a network interface appears or disappears, we need to revalidate    */
1321/* all of the network interface names that have been configured as a target */
1322/* in a destination list.                                                   */
1323/* ------------------------------------------------------------------------ */
1324void
1325ipf_dstlist_sync(softc, arg)
1326	ipf_main_softc_t *softc;
1327	void *arg;
1328{
1329	ipf_dstl_softc_t *softd = arg;
1330	ipf_dstnode_t *node;
1331	ippool_dst_t *list;
1332	int i;
1333	int j;
1334
1335	for (i = 0; i < IPL_LOGMAX; i++) {
1336		for (list = softd->dstlist[i]; list != NULL;
1337		     list = list->ipld_next) {
1338			for (j = 0; j < list->ipld_maxnodes; j++) {
1339				node = list->ipld_dests[j];
1340				if (node == NULL)
1341					continue;
1342				if (node->ipfd_dest.fd_name == -1)
1343					continue;
1344				(void) ipf_resolvedest(softc,
1345						       node->ipfd_names,
1346						       &node->ipfd_dest,
1347						       AF_INET);
1348			}
1349		}
1350	}
1351}
1352