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