1254562Scy/*
2254562Scy * Copyright (C) 2012 by Darren Reed.
3254562Scy *
4254562Scy * See the IPFILTER.LICENCE file for details on licencing.
5254562Scy */
6254562Scy#if defined(KERNEL) || defined(_KERNEL)
7254562Scy# undef KERNEL
8254562Scy# undef _KERNEL
9254562Scy# define        KERNEL	1
10254562Scy# define        _KERNEL	1
11254562Scy#endif
12254562Scy#if defined(__osf__)
13254562Scy# define _PROTO_NET_H_
14254562Scy#endif
15254562Scy#include <sys/errno.h>
16254562Scy#include <sys/types.h>
17254562Scy#include <sys/param.h>
18254562Scy#include <sys/file.h>
19254562Scy#if !defined(_KERNEL) && !defined(__KERNEL__)
20254562Scy# include <stdio.h>
21254562Scy# include <stdlib.h>
22254562Scy# include <string.h>
23254562Scy# define _KERNEL
24254562Scy# ifdef __OpenBSD__
25254562Scystruct file;
26254562Scy# endif
27254562Scy# include <sys/uio.h>
28254562Scy# undef _KERNEL
29254562Scy#else
30254562Scy# include <sys/systm.h>
31254562Scy# if defined(NetBSD) && (__NetBSD_Version__ >= 104000000)
32254562Scy#  include <sys/proc.h>
33254562Scy# endif
34254562Scy#endif
35254562Scy#include <sys/time.h>
36254562Scy#if !defined(linux)
37254562Scy# include <sys/protosw.h>
38254562Scy#endif
39254562Scy#include <sys/socket.h>
40254562Scy#if defined(_KERNEL) && (!defined(__SVR4) && !defined(__svr4__))
41254562Scy# include <sys/mbuf.h>
42254562Scy#endif
43254562Scy#if defined(__SVR4) || defined(__svr4__)
44254562Scy# include <sys/filio.h>
45254562Scy# include <sys/byteorder.h>
46254562Scy# ifdef _KERNEL
47254562Scy#  include <sys/dditypes.h>
48254562Scy# endif
49254562Scy# include <sys/stream.h>
50254562Scy# include <sys/kmem.h>
51254562Scy#endif
52254562Scy#if defined(__FreeBSD_version) && (__FreeBSD_version >= 300000)
53254562Scy# include <sys/malloc.h>
54254562Scy#endif
55254562Scy
56254562Scy#include <net/if.h>
57254562Scy#include <netinet/in.h>
58254562Scy
59254562Scy#include "netinet/ip_compat.h"
60254562Scy#include "netinet/ip_fil.h"
61254562Scy#include "netinet/ip_nat.h"
62254562Scy#include "netinet/ip_lookup.h"
63254562Scy#include "netinet/ip_dstlist.h"
64254562Scy
65254562Scy/* END OF INCLUDES */
66254562Scy
67254562Scy#ifdef HAS_SYS_MD5_H
68254562Scy# include <sys/md5.h>
69254562Scy#else
70254562Scy# include "md5.h"
71254562Scy#endif
72254562Scy
73254562Scy#if !defined(lint)
74254562Scystatic const char rcsid[] = "@(#)$Id: ip_dstlist.c,v 1.13.2.12 2012/07/20 08:40:19 darren_r Exp $";
75254562Scy#endif
76254562Scy
77254562Scytypedef struct ipf_dstl_softc_s {
78254562Scy	ippool_dst_t	*dstlist[LOOKUP_POOL_SZ];
79254562Scy	ippool_dst_t	**tails[LOOKUP_POOL_SZ];
80254562Scy	ipf_dstl_stat_t	stats;
81254562Scy} ipf_dstl_softc_t;
82254562Scy
83254562Scy
84254562Scystatic void *ipf_dstlist_soft_create __P((ipf_main_softc_t *));
85254562Scystatic void ipf_dstlist_soft_destroy __P((ipf_main_softc_t *, void *));
86254562Scystatic int ipf_dstlist_soft_init __P((ipf_main_softc_t *, void *));
87254562Scystatic void ipf_dstlist_soft_fini __P((ipf_main_softc_t *, void *));
88254562Scystatic int ipf_dstlist_addr_find __P((ipf_main_softc_t *, void *, int,
89254562Scy				      void *, u_int));
90254562Scystatic size_t ipf_dstlist_flush __P((ipf_main_softc_t *, void *,
91254562Scy				     iplookupflush_t *));
92254562Scystatic int ipf_dstlist_iter_deref __P((ipf_main_softc_t *, void *, int, int,
93254562Scy				       void *));
94254562Scystatic int ipf_dstlist_iter_next __P((ipf_main_softc_t *, void *, ipftoken_t *,
95254562Scy				      ipflookupiter_t *));
96254562Scystatic int ipf_dstlist_node_add __P((ipf_main_softc_t *, void *,
97254562Scy				     iplookupop_t *, int));
98254562Scystatic int ipf_dstlist_node_del __P((ipf_main_softc_t *, void *,
99254562Scy				     iplookupop_t *, int));
100254562Scystatic int ipf_dstlist_stats_get __P((ipf_main_softc_t *, void *,
101254562Scy				      iplookupop_t *));
102254562Scystatic int ipf_dstlist_table_add __P((ipf_main_softc_t *, void *,
103254562Scy				      iplookupop_t *));
104254562Scystatic int ipf_dstlist_table_del __P((ipf_main_softc_t *, void *,
105254562Scy				      iplookupop_t *));
106254562Scystatic int ipf_dstlist_table_deref __P((ipf_main_softc_t *, void *, void *));
107254562Scystatic void *ipf_dstlist_table_find __P((void *, int, char *));
108254562Scystatic void ipf_dstlist_table_free __P((ipf_dstl_softc_t *, ippool_dst_t *));
109254562Scystatic void ipf_dstlist_table_remove __P((ipf_main_softc_t *,
110254562Scy					  ipf_dstl_softc_t *, ippool_dst_t *));
111254562Scystatic void ipf_dstlist_table_clearnodes __P((ipf_dstl_softc_t *,
112254562Scy					      ippool_dst_t *));
113254562Scystatic ipf_dstnode_t *ipf_dstlist_select __P((fr_info_t *, ippool_dst_t *));
114254562Scystatic void *ipf_dstlist_select_ref __P((void *, int, char *));
115254562Scystatic void ipf_dstlist_node_free __P((ipf_dstl_softc_t *, ippool_dst_t *, ipf_dstnode_t *));
116254562Scystatic int ipf_dstlist_node_deref __P((void *, ipf_dstnode_t *));
117254562Scystatic void ipf_dstlist_expire __P((ipf_main_softc_t *, void *));
118254562Scystatic void ipf_dstlist_sync __P((ipf_main_softc_t *, void *));
119254562Scy
120254562Scyipf_lookup_t ipf_dstlist_backend = {
121254562Scy	IPLT_DSTLIST,
122254562Scy	ipf_dstlist_soft_create,
123254562Scy	ipf_dstlist_soft_destroy,
124254562Scy	ipf_dstlist_soft_init,
125254562Scy	ipf_dstlist_soft_fini,
126254562Scy	ipf_dstlist_addr_find,
127254562Scy	ipf_dstlist_flush,
128254562Scy	ipf_dstlist_iter_deref,
129254562Scy	ipf_dstlist_iter_next,
130254562Scy	ipf_dstlist_node_add,
131254562Scy	ipf_dstlist_node_del,
132254562Scy	ipf_dstlist_stats_get,
133254562Scy	ipf_dstlist_table_add,
134254562Scy	ipf_dstlist_table_del,
135254562Scy	ipf_dstlist_table_deref,
136254562Scy	ipf_dstlist_table_find,
137254562Scy	ipf_dstlist_select_ref,
138254562Scy	ipf_dstlist_select_node,
139254562Scy	ipf_dstlist_expire,
140254562Scy	ipf_dstlist_sync
141254562Scy};
142254562Scy
143254562Scy
144254562Scy/* ------------------------------------------------------------------------ */
145254562Scy/* Function:    ipf_dstlist_soft_create                                     */
146254562Scy/* Returns:     int - 0 = success, else error                               */
147254562Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
148254562Scy/*                                                                          */
149254562Scy/* Allocating a chunk of memory filled with 0's is enough for the current   */
150254562Scy/* soft context used with destination lists.                                */
151254562Scy/* ------------------------------------------------------------------------ */
152254562Scystatic void *
153254562Scyipf_dstlist_soft_create(softc)
154254562Scy	ipf_main_softc_t *softc;
155254562Scy{
156254562Scy	ipf_dstl_softc_t *softd;
157254562Scy	int i;
158254562Scy
159254562Scy	KMALLOC(softd, ipf_dstl_softc_t *);
160254562Scy	if (softd == NULL) {
161254562Scy		IPFERROR(120028);
162254562Scy		return NULL;
163254562Scy	}
164254562Scy
165254562Scy	bzero((char *)softd, sizeof(*softd));
166254562Scy	for (i = 0; i <= IPL_LOGMAX; i++)
167254562Scy		softd->tails[i] = &softd->dstlist[i];
168254562Scy
169254562Scy	return softd;
170254562Scy}
171254562Scy
172254562Scy
173254562Scy/* ------------------------------------------------------------------------ */
174254562Scy/* Function:    ipf_dstlist_soft_destroy                                    */
175254562Scy/* Returns:     Nil                                                         */
176254562Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
177254562Scy/*              arg(I)   - pointer to local context to use                  */
178254562Scy/*                                                                          */
179254562Scy/* For destination lists, the only thing we have to do when destroying the  */
180254562Scy/* soft context is free it!                                                 */
181254562Scy/* ------------------------------------------------------------------------ */
182254562Scystatic void
183254562Scyipf_dstlist_soft_destroy(softc, arg)
184254562Scy	ipf_main_softc_t *softc;
185254562Scy	void *arg;
186254562Scy{
187254562Scy	ipf_dstl_softc_t *softd = arg;
188254562Scy
189254562Scy	KFREE(softd);
190254562Scy}
191254562Scy
192254562Scy
193254562Scy/* ------------------------------------------------------------------------ */
194254562Scy/* Function:    ipf_dstlist_soft_init                                       */
195254562Scy/* Returns:     int - 0 = success, else error                               */
196254562Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
197254562Scy/*              arg(I)   - pointer to local context to use                  */
198254562Scy/*                                                                          */
199254562Scy/* There is currently no soft context for destination list management.      */
200254562Scy/* ------------------------------------------------------------------------ */
201254562Scystatic int
202254562Scyipf_dstlist_soft_init(softc, arg)
203254562Scy	ipf_main_softc_t *softc;
204254562Scy	void *arg;
205254562Scy{
206254562Scy	return 0;
207254562Scy}
208254562Scy
209254562Scy
210254562Scy/* ------------------------------------------------------------------------ */
211254562Scy/* Function:    ipf_dstlist_soft_fini                                       */
212254562Scy/* Returns:     Nil                                                         */
213254562Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
214254562Scy/*              arg(I)   - pointer to local context to use                  */
215254562Scy/*                                                                          */
216254562Scy/* There is currently no soft context for destination list management.      */
217254562Scy/* ------------------------------------------------------------------------ */
218254562Scystatic void
219254562Scyipf_dstlist_soft_fini(softc, arg)
220254562Scy	ipf_main_softc_t *softc;
221254562Scy	void *arg;
222254562Scy{
223254562Scy	ipf_dstl_softc_t *softd = arg;
224254562Scy	int i;
225254562Scy
226254562Scy	for (i = -1; i <= IPL_LOGMAX; i++) {
227254562Scy		while (softd->dstlist[i + 1] != NULL) {
228254562Scy			ipf_dstlist_table_remove(softc, softd,
229254562Scy						 softd->dstlist[i + 1]);
230254562Scy		}
231254562Scy	}
232254562Scy
233254562Scy	ASSERT(softd->stats.ipls_numderefnodes == 0);
234254562Scy}
235254562Scy
236254562Scy
237254562Scy/* ------------------------------------------------------------------------ */
238254562Scy/* Function:    ipf_dstlist_addr_find                                       */
239254562Scy/* Returns:     int - 0 = success, else error                               */
240254562Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
241254562Scy/*              arg1(I)  - pointer to local context to use                  */
242254562Scy/*              arg2(I)  - pointer to local context to use                  */
243254562Scy/*              arg3(I)  - pointer to local context to use                  */
244254562Scy/*              arg4(I)  - pointer to local context to use                  */
245254562Scy/*                                                                          */
246254562Scy/* There is currently no such thing as searching a destination list for an  */
247254562Scy/* address so this function becomes a no-op. Its presence is required as    */
248254562Scy/* ipf_lookup_res_name() stores the "addr_find" function pointer in the     */
249254562Scy/* pointer passed in to it as funcptr, although it could be a generic null- */
250254562Scy/* op function rather than a specific one.                                  */
251254562Scy/* ------------------------------------------------------------------------ */
252254562Scy/*ARGSUSED*/
253254562Scystatic int
254254562Scyipf_dstlist_addr_find(softc, arg1, arg2, arg3, arg4)
255254562Scy	ipf_main_softc_t *softc;
256254562Scy	void *arg1, *arg3;
257254562Scy	int arg2;
258254562Scy	u_int arg4;
259254562Scy{
260254562Scy	return -1;
261254562Scy}
262254562Scy
263254562Scy
264254562Scy/* ------------------------------------------------------------------------ */
265254562Scy/* Function:    ipf_dstlist_flush                                           */
266254562Scy/* Returns:     int      - number of objects deleted                        */
267254562Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
268254562Scy/*              arg(I)   - pointer to local context to use                  */
269254562Scy/*              fop(I)   - pointer to lookup flush operation data           */
270254562Scy/*                                                                          */
271254562Scy/* Flush all of the destination tables that match the data passed in with   */
272254562Scy/* the iplookupflush_t. There are two ways to match objects: the device for */
273254562Scy/* which they are to be used with and their name.                           */
274254562Scy/* ------------------------------------------------------------------------ */
275254562Scystatic size_t
276254562Scyipf_dstlist_flush(softc, arg, fop)
277254562Scy	ipf_main_softc_t *softc;
278254562Scy	void *arg;
279254562Scy	iplookupflush_t *fop;
280254562Scy{
281254562Scy	ipf_dstl_softc_t *softd = arg;
282254562Scy	ippool_dst_t *node, *next;
283254562Scy	int n, i;
284254562Scy
285254562Scy	for (n = 0, i = -1; i <= IPL_LOGMAX; i++) {
286254562Scy		if (fop->iplf_unit != IPLT_ALL && fop->iplf_unit != i)
287254562Scy			continue;
288254562Scy		for (node = softd->dstlist[i + 1]; node != NULL; node = next) {
289254562Scy			next = node->ipld_next;
290254562Scy
291254562Scy			if ((*fop->iplf_name != '\0') &&
292254562Scy			    strncmp(fop->iplf_name, node->ipld_name,
293254562Scy				    FR_GROUPLEN))
294254562Scy				continue;
295254562Scy
296254562Scy			ipf_dstlist_table_remove(softc, softd, node);
297254562Scy			n++;
298254562Scy		}
299254562Scy	}
300254562Scy	return n;
301254562Scy}
302254562Scy
303254562Scy
304254562Scy/* ------------------------------------------------------------------------ */
305254562Scy/* Function:    ipf_dstlist_iter_deref                                      */
306254562Scy/* Returns:     int      - 0 = success, else error                          */
307254562Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
308254562Scy/*              arg(I)   - pointer to local context to use                  */
309254562Scy/*              otype(I) - type of data structure to iterate through        */
310254562Scy/*              unit(I)  - device we are working with                       */
311254562Scy/*              data(I)  - address of object in kernel space                */
312254562Scy/*                                                                          */
313254562Scy/* This function is called when the iteration token is being free'd and is  */
314254562Scy/* responsible for dropping the reference count of the structure it points  */
315254562Scy/* to.                                                                      */
316254562Scy/* ------------------------------------------------------------------------ */
317254562Scystatic int
318254562Scyipf_dstlist_iter_deref(softc, arg, otype, unit, data)
319254562Scy	ipf_main_softc_t *softc;
320254562Scy	void *arg;
321254562Scy	int otype, unit;
322254562Scy	void *data;
323254562Scy{
324254562Scy	if (data == NULL) {
325254562Scy		IPFERROR(120001);
326254562Scy		return EINVAL;
327254562Scy	}
328254562Scy
329254562Scy	if (unit < -1 || unit > IPL_LOGMAX) {
330254562Scy		IPFERROR(120002);
331254562Scy		return EINVAL;
332254562Scy	}
333254562Scy
334254562Scy	switch (otype)
335254562Scy	{
336254562Scy	case IPFLOOKUPITER_LIST :
337254562Scy		ipf_dstlist_table_deref(softc, arg, (ippool_dst_t *)data);
338254562Scy		break;
339254562Scy
340254562Scy	case IPFLOOKUPITER_NODE :
341254562Scy		ipf_dstlist_node_deref(arg, (ipf_dstnode_t *)data);
342254562Scy		break;
343254562Scy	}
344254562Scy
345254562Scy	return 0;
346254562Scy}
347254562Scy
348254562Scy
349254562Scy/* ------------------------------------------------------------------------ */
350254562Scy/* Function:    ipf_dstlist_iter_next                                       */
351254562Scy/* Returns:     int - 0 = success, else error                               */
352254562Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
353254562Scy/*              arg(I)   - pointer to local context to use                  */
354254562Scy/*              op(I)    - pointer to lookup operation data                 */
355254562Scy/*              uid(I)   - uid of process doing the ioctl                   */
356254562Scy/*                                                                          */
357254562Scy/* This function is responsible for either selecting the next destination   */
358254562Scy/* list or node on a destination list to be returned as a user process      */
359254562Scy/* iterates through the list of destination lists or nodes.                 */
360254562Scy/* ------------------------------------------------------------------------ */
361254562Scystatic int
362254562Scyipf_dstlist_iter_next(softc, arg, token, iter)
363254562Scy	ipf_main_softc_t *softc;
364254562Scy	void *arg;
365254562Scy	ipftoken_t *token;
366254562Scy	ipflookupiter_t *iter;
367254562Scy{
368254562Scy	ipf_dstnode_t zn, *nextnode = NULL, *node = NULL;
369254562Scy	ippool_dst_t zero, *next = NULL, *dsttab = NULL;
370254562Scy	ipf_dstl_softc_t *softd = arg;
371254562Scy	int err = 0;
372254562Scy	void *hint;
373254562Scy
374254562Scy	switch (iter->ili_otype)
375254562Scy	{
376254562Scy	case IPFLOOKUPITER_LIST :
377254562Scy		dsttab = token->ipt_data;
378254562Scy		if (dsttab == NULL) {
379254562Scy			next = softd->dstlist[(int)iter->ili_unit + 1];
380254562Scy		} else {
381254562Scy			next = dsttab->ipld_next;
382254562Scy		}
383254562Scy
384254562Scy		if (next != NULL) {
385254562Scy			ATOMIC_INC32(next->ipld_ref);
386254562Scy			token->ipt_data = next;
387254562Scy			hint = next->ipld_next;
388254562Scy		} else {
389254562Scy			bzero((char *)&zero, sizeof(zero));
390254562Scy			next = &zero;
391254562Scy			token->ipt_data = NULL;
392254562Scy			hint = NULL;
393254562Scy		}
394254562Scy		break;
395254562Scy
396254562Scy	case IPFLOOKUPITER_NODE :
397254562Scy		node = token->ipt_data;
398254562Scy		if (node == NULL) {
399254562Scy			dsttab = ipf_dstlist_table_find(arg, iter->ili_unit,
400254562Scy							iter->ili_name);
401254562Scy			if (dsttab == NULL) {
402254562Scy				IPFERROR(120004);
403254562Scy				err = ESRCH;
404254562Scy				nextnode = NULL;
405254562Scy			} else {
406254562Scy				if (dsttab->ipld_dests == NULL)
407254562Scy					nextnode = NULL;
408254562Scy				else
409254562Scy					nextnode = *dsttab->ipld_dests;
410254562Scy				dsttab = NULL;
411254562Scy			}
412254562Scy		} else {
413254562Scy			nextnode = node->ipfd_next;
414254562Scy		}
415254562Scy
416254562Scy		if (nextnode != NULL) {
417254562Scy			MUTEX_ENTER(&nextnode->ipfd_lock);
418254562Scy			nextnode->ipfd_ref++;
419254562Scy			MUTEX_EXIT(&nextnode->ipfd_lock);
420254562Scy			token->ipt_data = nextnode;
421254562Scy			hint = nextnode->ipfd_next;
422254562Scy		} else {
423254562Scy			bzero((char *)&zn, sizeof(zn));
424254562Scy			nextnode = &zn;
425254562Scy			token->ipt_data = NULL;
426254562Scy			hint = NULL;
427254562Scy		}
428254562Scy		break;
429254562Scy	default :
430254562Scy		IPFERROR(120003);
431254562Scy		err = EINVAL;
432254562Scy		break;
433254562Scy	}
434254562Scy
435254562Scy	if (err != 0)
436254562Scy		return err;
437254562Scy
438254562Scy	switch (iter->ili_otype)
439254562Scy	{
440254562Scy	case IPFLOOKUPITER_LIST :
441254562Scy		if (dsttab != NULL)
442254562Scy			ipf_dstlist_table_deref(softc, arg, dsttab);
443254562Scy		err = COPYOUT(next, iter->ili_data, sizeof(*next));
444254562Scy		if (err != 0) {
445254562Scy			IPFERROR(120005);
446254562Scy			err = EFAULT;
447254562Scy		}
448254562Scy		break;
449254562Scy
450254562Scy	case IPFLOOKUPITER_NODE :
451254562Scy		if (node != NULL)
452254562Scy			ipf_dstlist_node_deref(arg, node);
453254562Scy		err = COPYOUT(nextnode, iter->ili_data, sizeof(*nextnode));
454254562Scy		if (err != 0) {
455254562Scy			IPFERROR(120006);
456254562Scy			err = EFAULT;
457254562Scy		}
458254562Scy		break;
459254562Scy	}
460254562Scy
461254562Scy	if (hint == NULL)
462254562Scy		ipf_token_mark_complete(token);
463254562Scy
464254562Scy	return err;
465254562Scy}
466254562Scy
467254562Scy
468254562Scy/* ------------------------------------------------------------------------ */
469254562Scy/* Function:    ipf_dstlist_node_add                                        */
470254562Scy/* Returns:     int - 0 = success, else error                               */
471254562Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
472254562Scy/*              arg(I)   - pointer to local context to use                  */
473254562Scy/*              op(I)    - pointer to lookup operation data                 */
474254562Scy/*              uid(I)   - uid of process doing the ioctl                   */
475254562Scy/* Locks:       WRITE(ipf_poolrw)                                           */
476254562Scy/*                                                                          */
477254562Scy/* Add a new node to a destination list. To do this, we only copy in the    */
478254562Scy/* frdest_t structure because that contains the only data required from the */
479254562Scy/* application to create a new node. The frdest_t doesn't contain the name  */
480254562Scy/* itself. When loading filter rules, fd_name is a 'pointer' to the name.   */
481254562Scy/* In this case, the 'pointer' does not work, instead it is the length of   */
482254562Scy/* the name and the name is immediately following the frdest_t structure.   */
483254562Scy/* fd_name must include the trailing \0, so it should be strlen(str) + 1.   */
484254562Scy/* For simple sanity checking, an upper bound on the size of fd_name is     */
485254562Scy/* imposed - 128.                                                          */
486254562Scy/* ------------------------------------------------------------------------ */
487254562Scystatic int
488254562Scyipf_dstlist_node_add(softc, arg, op, uid)
489254562Scy	ipf_main_softc_t *softc;
490254562Scy	void *arg;
491254562Scy	iplookupop_t *op;
492254562Scy	int uid;
493254562Scy{
494254562Scy	ipf_dstl_softc_t *softd = arg;
495254562Scy	ipf_dstnode_t *node, **nodes;
496254562Scy	ippool_dst_t *d;
497254562Scy	frdest_t dest;
498254562Scy	int err;
499254562Scy
500254562Scy	if (op->iplo_size < sizeof(frdest_t)) {
501254562Scy		IPFERROR(120007);
502254562Scy		return EINVAL;
503254562Scy	}
504254562Scy
505254562Scy	err = COPYIN(op->iplo_struct, &dest, sizeof(dest));
506254562Scy	if (err != 0) {
507254562Scy		IPFERROR(120009);
508254562Scy		return EFAULT;
509254562Scy	}
510254562Scy
511254562Scy	d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name);
512254562Scy	if (d == NULL) {
513254562Scy		IPFERROR(120010);
514254562Scy		return ESRCH;
515254562Scy	}
516254562Scy
517254562Scy	switch (dest.fd_addr.adf_family)
518254562Scy	{
519254562Scy	case AF_INET :
520254562Scy	case AF_INET6 :
521254562Scy		break;
522254562Scy	default :
523254562Scy		IPFERROR(120019);
524254562Scy		return EINVAL;
525254562Scy	}
526254562Scy
527254562Scy	if (dest.fd_name < -1 || dest.fd_name > 128) {
528254562Scy		IPFERROR(120018);
529254562Scy		return EINVAL;
530254562Scy	}
531254562Scy
532254562Scy	KMALLOCS(node, ipf_dstnode_t *, sizeof(*node) + dest.fd_name);
533254562Scy	if (node == NULL) {
534254562Scy		softd->stats.ipls_nomem++;
535254562Scy		IPFERROR(120008);
536254562Scy		return ENOMEM;
537254562Scy	}
538254562Scy	bzero((char *)node, sizeof(*node) + dest.fd_name);
539254562Scy
540254562Scy	bcopy(&dest, &node->ipfd_dest, sizeof(dest));
541254562Scy	node->ipfd_size = sizeof(*node) + dest.fd_name;
542254562Scy
543254562Scy	if (dest.fd_name > 0) {
544254562Scy		/*
545254562Scy		 * fd_name starts out as the length of the string to copy
546254562Scy		 * in (including \0) and ends up being the offset from
547254562Scy		 * fd_names (0).
548254562Scy		 */
549254562Scy		err = COPYIN((char *)op->iplo_struct + sizeof(dest),
550254562Scy			     node->ipfd_names, dest.fd_name);
551254562Scy		if (err != 0) {
552254562Scy			IPFERROR(120017);
553254562Scy			KFREES(node, node->ipfd_size);
554254562Scy			return EFAULT;
555254562Scy		}
556254562Scy		node->ipfd_dest.fd_name = 0;
557254562Scy	} else {
558254562Scy		node->ipfd_dest.fd_name = -1;
559254562Scy	}
560254562Scy
561254562Scy	if (d->ipld_nodes == d->ipld_maxnodes) {
562254562Scy		KMALLOCS(nodes, ipf_dstnode_t **,
563254562Scy			 sizeof(*nodes) * (d->ipld_maxnodes + 1));
564254562Scy		if (nodes == NULL) {
565254562Scy			softd->stats.ipls_nomem++;
566254562Scy			IPFERROR(120022);
567254562Scy			KFREES(node, node->ipfd_size);
568254562Scy			return ENOMEM;
569254562Scy		}
570254562Scy		if (d->ipld_dests != NULL) {
571254562Scy			bcopy(d->ipld_dests, nodes,
572254562Scy			      sizeof(*nodes) * d->ipld_maxnodes);
573254562Scy			KFREES(d->ipld_dests, sizeof(*nodes) * d->ipld_nodes);
574254562Scy			nodes[0]->ipfd_pnext = nodes;
575254562Scy		}
576254562Scy		d->ipld_dests = nodes;
577254562Scy		d->ipld_maxnodes++;
578254562Scy	}
579254562Scy	d->ipld_dests[d->ipld_nodes] = node;
580254562Scy	d->ipld_nodes++;
581254562Scy
582254562Scy	if (d->ipld_nodes == 1) {
583254562Scy		node->ipfd_pnext = d->ipld_dests;
584254562Scy	} else if (d->ipld_nodes > 1) {
585254562Scy		node->ipfd_pnext = &d->ipld_dests[d->ipld_nodes - 2]->ipfd_next;
586254562Scy	}
587254562Scy	*node->ipfd_pnext = node;
588254562Scy
589254562Scy	MUTEX_INIT(&node->ipfd_lock, "ipf dst node lock");
590254562Scy	node->ipfd_uid = uid;
591254562Scy	node->ipfd_ref = 1;
592254562Scy	if (node->ipfd_dest.fd_name == 0)
593254562Scy		(void) ipf_resolvedest(softc, node->ipfd_names,
594254562Scy				       &node->ipfd_dest, AF_INET);
595254562Scy#ifdef USE_INET6
596254562Scy	if (node->ipfd_dest.fd_name == 0 &&
597254562Scy	    node->ipfd_dest.fd_ptr == (void *)-1)
598254562Scy		(void) ipf_resolvedest(softc, node->ipfd_names,
599254562Scy				       &node->ipfd_dest, AF_INET6);
600254562Scy#endif
601254562Scy
602254562Scy	softd->stats.ipls_numnodes++;
603254562Scy
604254562Scy	return 0;
605254562Scy}
606254562Scy
607254562Scy
608254562Scy/* ------------------------------------------------------------------------ */
609254562Scy/* Function:    ipf_dstlist_node_deref                                      */
610254562Scy/* Returns:     int - 0 = success, else error                               */
611254562Scy/* Parameters:  arg(I)  - pointer to local context to use                   */
612254562Scy/*              node(I) - pointer to destionation node to free              */
613254562Scy/*                                                                          */
614254562Scy/* Dereference the use count by one. If it drops to zero then we can assume */
615254562Scy/* that it has been removed from any lists/tables and is ripe for freeing.  */
616254562Scy/* The pointer to context is required for the purpose of maintaining        */
617254562Scy/* statistics.                                                              */
618254562Scy/* ------------------------------------------------------------------------ */
619254562Scystatic int
620254562Scyipf_dstlist_node_deref(arg, node)
621254562Scy	void *arg;
622254562Scy	ipf_dstnode_t *node;
623254562Scy{
624254562Scy	ipf_dstl_softc_t *softd = arg;
625254562Scy	int ref;
626254562Scy
627254562Scy	MUTEX_ENTER(&node->ipfd_lock);
628254562Scy	ref = --node->ipfd_ref;
629254562Scy	MUTEX_EXIT(&node->ipfd_lock);
630254562Scy
631254562Scy	if (ref > 0)
632254562Scy		return 0;
633254562Scy
634254562Scy	if ((node->ipfd_flags & IPDST_DELETE) != 0)
635254562Scy		softd->stats.ipls_numderefnodes--;
636254562Scy	MUTEX_DESTROY(&node->ipfd_lock);
637254562Scy	KFREES(node, node->ipfd_size);
638254562Scy	softd->stats.ipls_numnodes--;
639254562Scy
640254562Scy	return 0;
641254562Scy}
642254562Scy
643254562Scy
644254562Scy/* ------------------------------------------------------------------------ */
645254562Scy/* Function:    ipf_dstlist_node_del                                        */
646254562Scy/* Returns:     int      - 0 = success, else error                          */
647254562Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
648254562Scy/*              arg(I)   - pointer to local context to use                  */
649254562Scy/*              op(I)    - pointer to lookup operation data                 */
650254562Scy/*              uid(I)   - uid of process doing the ioctl                   */
651254562Scy/*                                                                          */
652254562Scy/* Look for a matching destination node on the named table and free it if   */
653254562Scy/* found. Because the name embedded in the frdest_t is variable in length,  */
654254562Scy/* it is necessary to allocate some memory locally, to complete this op.    */
655254562Scy/* ------------------------------------------------------------------------ */
656254562Scystatic int
657254562Scyipf_dstlist_node_del(softc, arg, op, uid)
658254562Scy	ipf_main_softc_t *softc;
659254562Scy	void *arg;
660254562Scy	iplookupop_t *op;
661254562Scy	int uid;
662254562Scy{
663254562Scy	ipf_dstl_softc_t *softd = arg;
664254562Scy	ipf_dstnode_t *node;
665254562Scy	frdest_t frd, *temp;
666254562Scy	ippool_dst_t *d;
667254562Scy	size_t size;
668254562Scy	int err;
669254562Scy
670254562Scy	d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name);
671254562Scy	if (d == NULL) {
672254562Scy		IPFERROR(120012);
673254562Scy		return ESRCH;
674254562Scy	}
675254562Scy
676254562Scy	err = COPYIN(op->iplo_struct, &frd, sizeof(frd));
677254562Scy	if (err != 0) {
678254562Scy		IPFERROR(120011);
679254562Scy		return EFAULT;
680254562Scy	}
681254562Scy
682254562Scy	size = sizeof(*temp) + frd.fd_name;
683254562Scy	KMALLOCS(temp, frdest_t *, size);
684254562Scy	if (temp == NULL) {
685254562Scy		softd->stats.ipls_nomem++;
686254562Scy		IPFERROR(120026);
687254562Scy		return ENOMEM;
688254562Scy	}
689254562Scy
690254562Scy	err = COPYIN(op->iplo_struct, temp, size);
691254562Scy	if (err != 0) {
692254562Scy		IPFERROR(120027);
693254562Scy		return EFAULT;
694254562Scy	}
695254562Scy
696254562Scy	MUTEX_ENTER(&d->ipld_lock);
697254562Scy	for (node = *d->ipld_dests; node != NULL; node = node->ipfd_next) {
698254562Scy		if ((uid != 0) && (node->ipfd_uid != uid))
699254562Scy			continue;
700254562Scy		if (node->ipfd_size != size)
701254562Scy			continue;
702254562Scy		if (!bcmp(&node->ipfd_dest.fd_ip6, &frd.fd_ip6,
703254562Scy			  size - offsetof(frdest_t, fd_ip6))) {
704254562Scy			ipf_dstlist_node_free(softd, d, node);
705254562Scy			MUTEX_EXIT(&d->ipld_lock);
706254562Scy			KFREES(temp, size);
707254562Scy			return 0;
708254562Scy		}
709254562Scy	}
710254562Scy	MUTEX_EXIT(&d->ipld_lock);
711254562Scy	KFREES(temp, size);
712254562Scy
713254562Scy	return ESRCH;
714254562Scy}
715254562Scy
716254562Scy
717254562Scy/* ------------------------------------------------------------------------ */
718254562Scy/* Function:    ipf_dstlist_node_free                                       */
719254562Scy/* Returns:     Nil                                                         */
720254562Scy/* Parameters:  softd(I) - pointer to the destination list context          */
721254562Scy/*              d(I)     - pointer to destination list                      */
722254562Scy/*              node(I)  - pointer to node to free                          */
723254562Scy/* Locks:       MUTEX(ipld_lock) or WRITE(ipf_poolrw)                       */
724254562Scy/*                                                                          */
725254562Scy/* Free the destination node by first removing it from any lists and then   */
726254562Scy/* checking if this was the last reference held to the object. While the    */
727254562Scy/* array of pointers to nodes is compacted, its size isn't reduced (by way  */
728254562Scy/* of allocating a new smaller one and copying) because the belief is that  */
729254562Scy/* it is likely the array will again reach that size.                       */
730254562Scy/* ------------------------------------------------------------------------ */
731254562Scystatic void
732254562Scyipf_dstlist_node_free(softd, d, node)
733254562Scy	ipf_dstl_softc_t *softd;
734254562Scy	ippool_dst_t *d;
735254562Scy	ipf_dstnode_t *node;
736254562Scy{
737254562Scy	int i;
738254562Scy
739254562Scy	/*
740254562Scy	 * Compact the array of pointers to nodes.
741254562Scy	 */
742254562Scy	for (i = 0; i < d->ipld_nodes; i++)
743254562Scy		if (d->ipld_dests[i] == node)
744254562Scy			break;
745254562Scy	if (d->ipld_nodes - i > 1) {
746254562Scy		bcopy(&d->ipld_dests[i + 1], &d->ipld_dests[i],
747254562Scy		      sizeof(*d->ipld_dests) * (d->ipld_nodes - i - 1));
748254562Scy	}
749254562Scy	d->ipld_nodes--;
750254562Scy
751254562Scy	if (node->ipfd_pnext != NULL)
752254562Scy		*node->ipfd_pnext = node->ipfd_next;
753254562Scy	if (node->ipfd_next != NULL)
754254562Scy		node->ipfd_next->ipfd_pnext = node->ipfd_pnext;
755254562Scy	node->ipfd_pnext = NULL;
756254562Scy	node->ipfd_next = NULL;
757254562Scy
758254562Scy	if ((node->ipfd_flags & IPDST_DELETE) == 0) {
759254562Scy		softd->stats.ipls_numderefnodes++;
760254562Scy		node->ipfd_flags |= IPDST_DELETE;
761254562Scy	}
762254562Scy
763254562Scy	ipf_dstlist_node_deref(softd, node);
764254562Scy}
765254562Scy
766254562Scy
767254562Scy/* ------------------------------------------------------------------------ */
768254562Scy/* Function:    ipf_dstlist_stats_get                                       */
769254562Scy/* Returns:     int - 0 = success, else error                               */
770254562Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
771254562Scy/*              arg(I)   - pointer to local context to use                  */
772254562Scy/*              op(I)    - pointer to lookup operation data                 */
773254562Scy/*                                                                          */
774254562Scy/* Return the current statistics for destination lists. This may be for all */
775254562Scy/* of them or just information pertaining to a particular table.            */
776254562Scy/* ------------------------------------------------------------------------ */
777254562Scy/*ARGSUSED*/
778254562Scystatic int
779254562Scyipf_dstlist_stats_get(softc, arg, op)
780254562Scy	ipf_main_softc_t *softc;
781254562Scy	void *arg;
782254562Scy	iplookupop_t *op;
783254562Scy{
784254562Scy	ipf_dstl_softc_t *softd = arg;
785254562Scy	ipf_dstl_stat_t stats;
786254562Scy	int unit, i, err = 0;
787254562Scy
788254562Scy	if (op->iplo_size != sizeof(ipf_dstl_stat_t)) {
789254562Scy		IPFERROR(120023);
790254562Scy		return EINVAL;
791254562Scy	}
792254562Scy
793254562Scy	stats = softd->stats;
794254562Scy	unit = op->iplo_unit;
795254562Scy	if (unit == IPL_LOGALL) {
796254562Scy		for (i = 0; i <= IPL_LOGMAX; i++)
797254562Scy			stats.ipls_list[i] = softd->dstlist[i];
798254562Scy	} else if (unit >= 0 && unit <= IPL_LOGMAX) {
799254562Scy		void *ptr;
800254562Scy
801254562Scy		if (op->iplo_name[0] != '\0')
802254562Scy			ptr = ipf_dstlist_table_find(softd, unit,
803254562Scy						     op->iplo_name);
804254562Scy		else
805254562Scy			ptr = softd->dstlist[unit + 1];
806254562Scy		stats.ipls_list[unit] = ptr;
807254562Scy	} else {
808254562Scy		IPFERROR(120024);
809254562Scy		err = EINVAL;
810254562Scy	}
811254562Scy
812254562Scy	if (err == 0) {
813254562Scy		err = COPYOUT(&stats, op->iplo_struct, sizeof(stats));
814254562Scy		if (err != 0) {
815254562Scy			IPFERROR(120025);
816254562Scy			return EFAULT;
817254562Scy		}
818254562Scy	}
819254562Scy	return 0;
820254562Scy}
821254562Scy
822254562Scy
823254562Scy/* ------------------------------------------------------------------------ */
824254562Scy/* Function:    ipf_dstlist_table_add                                       */
825254562Scy/* Returns:     int      - 0 = success, else error                          */
826254562Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
827254562Scy/*              arg(I)   - pointer to local context to use                  */
828254562Scy/*              op(I)    - pointer to lookup operation data                 */
829254562Scy/*                                                                          */
830254562Scy/* Add a new destination table to the list of those available for the given */
831254562Scy/* device. Because we seldom operate on these objects (find/add/delete),    */
832254562Scy/* they are just kept in a simple linked list.                              */
833254562Scy/* ------------------------------------------------------------------------ */
834254562Scystatic int
835254562Scyipf_dstlist_table_add(softc, arg, op)
836254562Scy	ipf_main_softc_t *softc;
837254562Scy	void *arg;
838254562Scy	iplookupop_t *op;
839254562Scy{
840254562Scy	ipf_dstl_softc_t *softd = arg;
841254562Scy	ippool_dst_t user, *d, *new;
842254562Scy	int unit, err;
843254562Scy
844254562Scy	d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name);
845254562Scy	if (d != NULL) {
846254562Scy		IPFERROR(120013);
847254562Scy		return EEXIST;
848254562Scy	}
849254562Scy
850254562Scy	err = COPYIN(op->iplo_struct, &user, sizeof(user));
851254562Scy	if (err != 0) {
852254562Scy		IPFERROR(120021);
853254562Scy		return EFAULT;
854254562Scy	}
855254562Scy
856254562Scy	KMALLOC(new, ippool_dst_t *);
857254562Scy	if (new == NULL) {
858254562Scy		softd->stats.ipls_nomem++;
859254562Scy		IPFERROR(120014);
860254562Scy		return ENOMEM;
861254562Scy	}
862254562Scy	bzero((char *)new, sizeof(*new));
863254562Scy
864254562Scy	MUTEX_INIT(&new->ipld_lock, "ipf dst table lock");
865254562Scy
866254562Scy	strncpy(new->ipld_name, op->iplo_name, FR_GROUPLEN);
867254562Scy	unit = op->iplo_unit;
868254562Scy	new->ipld_unit = unit;
869254562Scy	new->ipld_policy = user.ipld_policy;
870254562Scy	new->ipld_seed = ipf_random();
871254562Scy	new->ipld_ref = 1;
872254562Scy
873254562Scy	new->ipld_pnext = softd->tails[unit + 1];
874254562Scy	*softd->tails[unit + 1] = new;
875254562Scy	softd->tails[unit + 1] = &new->ipld_next;
876254562Scy	softd->stats.ipls_numlists++;
877254562Scy
878254562Scy	return 0;
879254562Scy}
880254562Scy
881254562Scy
882254562Scy/* ------------------------------------------------------------------------ */
883254562Scy/* Function:    ipf_dstlist_table_del                                       */
884254562Scy/* Returns:     int - 0 = success, else error                               */
885254562Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
886254562Scy/*              arg(I)   - pointer to local context to use                  */
887254562Scy/*              op(I)    - pointer to lookup operation data                 */
888254562Scy/*                                                                          */
889254562Scy/* Find a named destinstion list table and delete it. If there are other    */
890254562Scy/* references to it, the caller isn't told.                                 */
891254562Scy/* ------------------------------------------------------------------------ */
892254562Scystatic int
893254562Scyipf_dstlist_table_del(softc, arg, op)
894254562Scy	ipf_main_softc_t *softc;
895254562Scy	void *arg;
896254562Scy	iplookupop_t *op;
897254562Scy{
898254562Scy	ippool_dst_t *d;
899254562Scy
900254562Scy	d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name);
901254562Scy	if (d == NULL) {
902254562Scy		IPFERROR(120015);
903254562Scy		return ESRCH;
904254562Scy	}
905254562Scy
906254562Scy	if (d->ipld_dests != NULL) {
907254562Scy		IPFERROR(120016);
908254562Scy		return EBUSY;
909254562Scy	}
910254562Scy
911254562Scy	ipf_dstlist_table_remove(softc, arg, d);
912254562Scy
913254562Scy	return 0;
914254562Scy}
915254562Scy
916254562Scy
917254562Scy/* ------------------------------------------------------------------------ */
918254562Scy/* Function:    ipf_dstlist_table_remove                                    */
919254562Scy/* Returns:     Nil                                                         */
920254562Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
921254562Scy/*              softd(I) - pointer to the destination list context          */
922254562Scy/*              d(I)     - pointer to destination list                      */
923254562Scy/*                                                                          */
924254562Scy/* Remove a given destination list from existance. While the IPDST_DELETE   */
925254562Scy/* flag is set every time we call this function and the reference count is  */
926254562Scy/* non-zero, the "numdereflists" counter is always incremented because the  */
927254562Scy/* decision about whether it will be freed or not is not made here. This    */
928254562Scy/* means that the only action the code can take here is to treat it as if   */
929254562Scy/* it will become a detached.                                               */
930254562Scy/* ------------------------------------------------------------------------ */
931254562Scystatic void
932254562Scyipf_dstlist_table_remove(softc, softd, d)
933254562Scy	ipf_main_softc_t *softc;
934254562Scy	ipf_dstl_softc_t *softd;
935254562Scy	ippool_dst_t *d;
936254562Scy{
937254562Scy
938254562Scy	if (softd->tails[d->ipld_unit + 1] == &d->ipld_next)
939254562Scy		softd->tails[d->ipld_unit + 1] = d->ipld_pnext;
940254562Scy
941254562Scy	if (d->ipld_pnext != NULL)
942254562Scy		*d->ipld_pnext = d->ipld_next;
943254562Scy	if (d->ipld_next != NULL)
944254562Scy		d->ipld_next->ipld_pnext = d->ipld_pnext;
945254562Scy	d->ipld_pnext = NULL;
946254562Scy	d->ipld_next = NULL;
947254562Scy
948254562Scy	ipf_dstlist_table_clearnodes(softd, d);
949254562Scy
950254562Scy	softd->stats.ipls_numdereflists++;
951254562Scy	d->ipld_flags |= IPDST_DELETE;
952254562Scy
953254562Scy	ipf_dstlist_table_deref(softc, softd, d);
954254562Scy}
955254562Scy
956254562Scy
957254562Scy/* ------------------------------------------------------------------------ */
958254562Scy/* Function:    ipf_dstlist_table_free                                      */
959254562Scy/* Returns:     Nil                                                         */
960254562Scy/* Parameters:  softd(I) - pointer to the destination list context          */
961254562Scy/*              d(I)   - pointer to destination list                        */
962254562Scy/*                                                                          */
963254562Scy/* Free up a destination list data structure and any other memory that was  */
964254562Scy/* directly allocated as part of creating it. Individual destination list   */
965254562Scy/* nodes are not freed. It is assumed the caller will have already emptied  */
966254562Scy/* the destination list.                                                    */
967254562Scy/* ------------------------------------------------------------------------ */
968254562Scystatic void
969254562Scyipf_dstlist_table_free(softd, d)
970254562Scy	ipf_dstl_softc_t *softd;
971254562Scy	ippool_dst_t *d;
972254562Scy{
973254562Scy	MUTEX_DESTROY(&d->ipld_lock);
974254562Scy
975254562Scy	if ((d->ipld_flags & IPDST_DELETE) != 0)
976254562Scy		softd->stats.ipls_numdereflists--;
977254562Scy	softd->stats.ipls_numlists--;
978254562Scy
979254562Scy	if (d->ipld_dests != NULL) {
980254562Scy		KFREES(d->ipld_dests,
981254562Scy		       d->ipld_maxnodes * sizeof(*d->ipld_dests));
982254562Scy	}
983254562Scy
984254562Scy	KFREE(d);
985254562Scy}
986254562Scy
987254562Scy
988254562Scy/* ------------------------------------------------------------------------ */
989254562Scy/* Function:    ipf_dstlist_table_deref                                     */
990254562Scy/* Returns:     int - 0 = success, else error                               */
991254562Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
992254562Scy/*              arg(I)   - pointer to local context to use                  */
993254562Scy/*              op(I)    - pointer to lookup operation data                 */
994254562Scy/*                                                                          */
995254562Scy/* Drops the reference count on a destination list table object and free's  */
996254562Scy/* it if 0 has been reached.                                                */
997254562Scy/* ------------------------------------------------------------------------ */
998254562Scystatic int
999254562Scyipf_dstlist_table_deref(softc, arg, table)
1000254562Scy	ipf_main_softc_t *softc;
1001254562Scy	void *arg;
1002254562Scy	void *table;
1003254562Scy{
1004254562Scy	ippool_dst_t *d = table;
1005254562Scy
1006254562Scy	d->ipld_ref--;
1007254562Scy	if (d->ipld_ref > 0)
1008254562Scy		return d->ipld_ref;
1009254562Scy
1010254562Scy	ipf_dstlist_table_free(arg, d);
1011254562Scy
1012254562Scy	return 0;
1013254562Scy}
1014254562Scy
1015254562Scy
1016254562Scy/* ------------------------------------------------------------------------ */
1017254562Scy/* Function:    ipf_dstlist_table_clearnodes                                */
1018254562Scy/* Returns:     Nil                                                         */
1019254562Scy/* Parameters:  softd(I) - pointer to the destination list context          */
1020254562Scy/*              dst(I)   - pointer to destination list                      */
1021254562Scy/*                                                                          */
1022254562Scy/* Free all of the destination nodes attached to the given table.           */
1023254562Scy/* ------------------------------------------------------------------------ */
1024254562Scystatic void
1025254562Scyipf_dstlist_table_clearnodes(softd, dst)
1026254562Scy	ipf_dstl_softc_t *softd;
1027254562Scy	ippool_dst_t *dst;
1028254562Scy{
1029254562Scy	ipf_dstnode_t *node;
1030254562Scy
1031254562Scy	if (dst->ipld_dests == NULL)
1032254562Scy		return;
1033254562Scy
1034254562Scy	while ((node = *dst->ipld_dests) != NULL) {
1035254562Scy		ipf_dstlist_node_free(softd, dst, node);
1036254562Scy	}
1037254562Scy}
1038254562Scy
1039254562Scy
1040254562Scy/* ------------------------------------------------------------------------ */
1041254562Scy/* Function:    ipf_dstlist_table_find                                      */
1042254562Scy/* Returns:     int      - 0 = success, else error                          */
1043254562Scy/* Parameters:  arg(I)   - pointer to local context to use                  */
1044254562Scy/*              unit(I)  - device we are working with                       */
1045254562Scy/*              name(I)  - destination table name to find                   */
1046254562Scy/*                                                                          */
1047254562Scy/* Return a pointer to a destination table that matches the unit+name that  */
1048254562Scy/* is passed in.                                                            */
1049254562Scy/* ------------------------------------------------------------------------ */
1050254562Scystatic void *
1051254562Scyipf_dstlist_table_find(arg, unit, name)
1052254562Scy	void *arg;
1053254562Scy	int unit;
1054254562Scy	char *name;
1055254562Scy{
1056254562Scy	ipf_dstl_softc_t *softd = arg;
1057254562Scy	ippool_dst_t *d;
1058254562Scy
1059254562Scy	for (d = softd->dstlist[unit + 1]; d != NULL; d = d->ipld_next) {
1060254562Scy		if ((d->ipld_unit == unit) &&
1061254562Scy		    !strncmp(d->ipld_name, name, FR_GROUPLEN)) {
1062254562Scy			return d;
1063254562Scy		}
1064254562Scy	}
1065254562Scy
1066254562Scy	return NULL;
1067254562Scy}
1068254562Scy
1069254562Scy
1070254562Scy/* ------------------------------------------------------------------------ */
1071254562Scy/* Function:    ipf_dstlist_select_ref                                      */
1072254562Scy/* Returns:     void *   - NULL = failure, else pointer to table            */
1073254562Scy/* Parameters:  arg(I)   - pointer to local context to use                  */
1074254562Scy/*              unit(I)  - device we are working with                       */
1075254562Scy/*              name(I)  - destination table name to find                   */
1076254562Scy/*                                                                          */
1077254562Scy/* Attempt to find a destination table that matches the name passed in and  */
1078254562Scy/* if successful, bump up the reference count on it because we intend to    */
1079254562Scy/* store the pointer to it somewhere else.                                  */
1080254562Scy/* ------------------------------------------------------------------------ */
1081254562Scystatic void *
1082254562Scyipf_dstlist_select_ref(arg, unit, name)
1083254562Scy	void *arg;
1084254562Scy	int unit;
1085254562Scy	char *name;
1086254562Scy{
1087254562Scy	ippool_dst_t *d;
1088254562Scy
1089254562Scy	d = ipf_dstlist_table_find(arg, unit, name);
1090254562Scy	if (d != NULL) {
1091254562Scy		MUTEX_ENTER(&d->ipld_lock);
1092254562Scy		d->ipld_ref++;
1093254562Scy		MUTEX_EXIT(&d->ipld_lock);
1094254562Scy	}
1095254562Scy	return d;
1096254562Scy}
1097254562Scy
1098254562Scy
1099254562Scy/* ------------------------------------------------------------------------ */
1100254562Scy/* Function:    ipf_dstlist_select                                          */
1101254562Scy/* Returns:     void * - NULL = failure, else pointer to table              */
1102254562Scy/* Parameters:  fin(I) - pointer to packet information                      */
1103254562Scy/*              d(I)   - pointer to destination list                        */
1104254562Scy/*                                                                          */
1105254562Scy/* Find the next node in the destination list to be used according to the   */
1106254562Scy/* defined policy. Of these, "connection" is the most expensive policy to   */
1107254562Scy/* implement as it always looks for the node with the least number of       */
1108254562Scy/* connections associated with it.                                          */
1109254562Scy/*                                                                          */
1110254562Scy/* The hashes exclude the port numbers so that all protocols map to the     */
1111254562Scy/* same destination. Otherwise, someone doing a ping would target a         */
1112254562Scy/* different server than their TCP connection, etc. MD-5 is used to         */
1113254562Scy/* transform the addressese into something random that the other end could  */
1114254562Scy/* not easily guess and use in an attack. ipld_seed introduces an unknown   */
1115254562Scy/* into the hash calculation to increase the difficult of an attacker       */
1116254562Scy/* guessing the bucket.                                                     */
1117254562Scy/*                                                                          */
1118254562Scy/* One final comment: mixing different address families in a single pool    */
1119254562Scy/* will currently result in failures as the address family of the node is   */
1120254562Scy/* only matched up with that in the packet as the last step. While this can */
1121254562Scy/* be coded around for the weighted connection and round-robin models, it   */
1122254562Scy/* cannot be supported for the hash/random models as they do not search and */
1123254562Scy/* nor is the algorithm conducive to searching.                             */
1124254562Scy/* ------------------------------------------------------------------------ */
1125254562Scystatic ipf_dstnode_t *
1126254562Scyipf_dstlist_select(fin, d)
1127254562Scy	fr_info_t *fin;
1128254562Scy	ippool_dst_t *d;
1129254562Scy{
1130254562Scy	ipf_dstnode_t *node, *sel;
1131254562Scy	int connects;
1132254562Scy	u_32_t hash[4];
1133254562Scy	MD5_CTX ctx;
1134254562Scy	int family;
1135254562Scy	int x;
1136254562Scy
1137254562Scy	if (d->ipld_dests == NULL || *d->ipld_dests == NULL)
1138254562Scy		return NULL;
1139254562Scy
1140254562Scy	family = fin->fin_family;
1141254562Scy
1142254562Scy	MUTEX_ENTER(&d->ipld_lock);
1143254562Scy
1144254562Scy	switch (d->ipld_policy)
1145254562Scy	{
1146254562Scy	case IPLDP_ROUNDROBIN:
1147254562Scy		sel = d->ipld_selected;
1148254562Scy		if (sel == NULL) {
1149254562Scy			sel = *d->ipld_dests;
1150254562Scy		} else {
1151254562Scy			sel = sel->ipfd_next;
1152254562Scy			if (sel == NULL)
1153254562Scy				sel = *d->ipld_dests;
1154254562Scy		}
1155254562Scy		break;
1156254562Scy
1157254562Scy	case IPLDP_CONNECTION:
1158254562Scy		if (d->ipld_selected == NULL) {
1159254562Scy			sel = *d->ipld_dests;
1160254562Scy			break;
1161254562Scy		}
1162254562Scy
1163254562Scy		sel = d->ipld_selected;
1164254562Scy		connects = 0x7fffffff;
1165254562Scy		node = sel->ipfd_next;
1166254562Scy		if (node == NULL)
1167254562Scy			node = *d->ipld_dests;
1168254562Scy		while (node != d->ipld_selected) {
1169254562Scy			if (node->ipfd_states == 0) {
1170254562Scy				sel = node;
1171254562Scy				break;
1172254562Scy			}
1173254562Scy			if (node->ipfd_states < connects) {
1174254562Scy				sel = node;
1175254562Scy				connects = node->ipfd_states;
1176254562Scy			}
1177254562Scy			node = node->ipfd_next;
1178254562Scy			if (node == NULL)
1179254562Scy				node = *d->ipld_dests;
1180254562Scy		}
1181254562Scy		break;
1182254562Scy
1183254562Scy	case IPLDP_RANDOM :
1184254562Scy		x = ipf_random() % d->ipld_nodes;
1185254562Scy		sel = d->ipld_dests[x];
1186254562Scy		break;
1187254562Scy
1188254562Scy	case IPLDP_HASHED :
1189254562Scy		MD5Init(&ctx);
1190254562Scy		MD5Update(&ctx, (u_char *)&d->ipld_seed, sizeof(d->ipld_seed));
1191254562Scy		MD5Update(&ctx, (u_char *)&fin->fin_src6,
1192254562Scy			  sizeof(fin->fin_src6));
1193254562Scy		MD5Update(&ctx, (u_char *)&fin->fin_dst6,
1194254562Scy			  sizeof(fin->fin_dst6));
1195254562Scy		MD5Final((u_char *)hash, &ctx);
1196254562Scy		x = hash[0] % d->ipld_nodes;
1197254562Scy		sel = d->ipld_dests[x];
1198254562Scy		break;
1199254562Scy
1200254562Scy	case IPLDP_SRCHASH :
1201254562Scy		MD5Init(&ctx);
1202254562Scy		MD5Update(&ctx, (u_char *)&d->ipld_seed, sizeof(d->ipld_seed));
1203254562Scy		MD5Update(&ctx, (u_char *)&fin->fin_src6,
1204254562Scy			  sizeof(fin->fin_src6));
1205254562Scy		MD5Final((u_char *)hash, &ctx);
1206254562Scy		x = hash[0] % d->ipld_nodes;
1207254562Scy		sel = d->ipld_dests[x];
1208254562Scy		break;
1209254562Scy
1210254562Scy	case IPLDP_DSTHASH :
1211254562Scy		MD5Init(&ctx);
1212254562Scy		MD5Update(&ctx, (u_char *)&d->ipld_seed, sizeof(d->ipld_seed));
1213254562Scy		MD5Update(&ctx, (u_char *)&fin->fin_dst6,
1214254562Scy			  sizeof(fin->fin_dst6));
1215254562Scy		MD5Final((u_char *)hash, &ctx);
1216254562Scy		x = hash[0] % d->ipld_nodes;
1217254562Scy		sel = d->ipld_dests[x];
1218254562Scy		break;
1219254562Scy
1220254562Scy	default :
1221254562Scy		sel = NULL;
1222254562Scy		break;
1223254562Scy	}
1224254562Scy
1225254562Scy	if (sel->ipfd_dest.fd_addr.adf_family != family)
1226254562Scy		sel = NULL;
1227254562Scy	d->ipld_selected = sel;
1228254562Scy
1229254562Scy	MUTEX_EXIT(&d->ipld_lock);
1230254562Scy
1231254562Scy	return sel;
1232254562Scy}
1233254562Scy
1234254562Scy
1235254562Scy/* ------------------------------------------------------------------------ */
1236254562Scy/* Function:    ipf_dstlist_select_node                                     */
1237254562Scy/* Returns:     int      - -1 == failure, 0 == success                      */
1238254562Scy/* Parameters:  fin(I)   - pointer to packet information                    */
1239254562Scy/*              group(I) - destination pool to search                       */
1240254562Scy/*              addr(I)  - pointer to store selected address                */
1241254562Scy/*              pfdp(O)  - pointer to storage for selected destination node */
1242254562Scy/*                                                                          */
1243254562Scy/* This function is only responsible for obtaining the next IP address for  */
1244254562Scy/* use and storing it in the caller's address space (addr). "addr" is only  */
1245254562Scy/* used for storage if pfdp is NULL. No permanent reference is currently    */
1246254562Scy/* kept on the node.                                                        */
1247254562Scy/* ------------------------------------------------------------------------ */
1248254562Scyint
1249254562Scyipf_dstlist_select_node(fin, group, addr, pfdp)
1250254562Scy	fr_info_t *fin;
1251254562Scy	void *group;
1252254562Scy	u_32_t *addr;
1253254562Scy	frdest_t *pfdp;
1254254562Scy{
1255254562Scy#ifdef USE_MUTEXES
1256254562Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
1257254562Scy#endif
1258254562Scy	ippool_dst_t *d = group;
1259254562Scy	ipf_dstnode_t *node;
1260254562Scy	frdest_t *fdp;
1261254562Scy
1262254562Scy	READ_ENTER(&softc->ipf_poolrw);
1263254562Scy
1264254562Scy	node = ipf_dstlist_select(fin, d);
1265254562Scy	if (node == NULL) {
1266254562Scy		RWLOCK_EXIT(&softc->ipf_poolrw);
1267254562Scy		return -1;
1268254562Scy	}
1269254562Scy
1270254562Scy	if (pfdp != NULL) {
1271254562Scy		bcopy(&node->ipfd_dest, pfdp, sizeof(*pfdp));
1272254562Scy	} else {
1273254562Scy		if (fin->fin_family == AF_INET) {
1274254562Scy			addr[0] = node->ipfd_dest.fd_addr.adf_addr.i6[0];
1275254562Scy		} else if (fin->fin_family == AF_INET6) {
1276254562Scy			addr[0] = node->ipfd_dest.fd_addr.adf_addr.i6[0];
1277254562Scy			addr[1] = node->ipfd_dest.fd_addr.adf_addr.i6[1];
1278254562Scy			addr[2] = node->ipfd_dest.fd_addr.adf_addr.i6[2];
1279254562Scy			addr[3] = node->ipfd_dest.fd_addr.adf_addr.i6[3];
1280254562Scy		}
1281254562Scy	}
1282254562Scy
1283254562Scy	fdp = &node->ipfd_dest;
1284254562Scy	if (fdp->fd_ptr == NULL)
1285254562Scy		fdp->fd_ptr = fin->fin_ifp;
1286254562Scy
1287254562Scy	MUTEX_ENTER(&node->ipfd_lock);
1288254562Scy	node->ipfd_states++;
1289254562Scy	MUTEX_EXIT(&node->ipfd_lock);
1290254562Scy
1291254562Scy	RWLOCK_EXIT(&softc->ipf_poolrw);
1292254562Scy
1293254562Scy	return 0;
1294254562Scy}
1295254562Scy
1296254562Scy
1297254562Scy/* ------------------------------------------------------------------------ */
1298254562Scy/* Function:    ipf_dstlist_expire                                          */
1299254562Scy/* Returns:     Nil                                                         */
1300254562Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
1301254562Scy/*              arg(I)   - pointer to local context to use                  */
1302254562Scy/*                                                                          */
1303254562Scy/* There are currently no objects to expire in destination lists.           */
1304254562Scy/* ------------------------------------------------------------------------ */
1305254562Scystatic void
1306254562Scyipf_dstlist_expire(softc, arg)
1307254562Scy	ipf_main_softc_t *softc;
1308254562Scy	void *arg;
1309254562Scy{
1310254562Scy	return;
1311254562Scy}
1312254562Scy
1313254562Scy
1314254562Scy/* ------------------------------------------------------------------------ */
1315254562Scy/* Function:    ipf_dstlist_sync                                            */
1316254562Scy/* Returns:     Nil                                                         */
1317254562Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
1318254562Scy/*              arg(I)   - pointer to local context to use                  */
1319254562Scy/*                                                                          */
1320254562Scy/* When a network interface appears or disappears, we need to revalidate    */
1321254562Scy/* all of the network interface names that have been configured as a target */
1322254562Scy/* in a destination list.                                                   */
1323254562Scy/* ------------------------------------------------------------------------ */
1324254562Scyvoid
1325254562Scyipf_dstlist_sync(softc, arg)
1326254562Scy	ipf_main_softc_t *softc;
1327254562Scy	void *arg;
1328254562Scy{
1329254562Scy	ipf_dstl_softc_t *softd = arg;
1330254562Scy	ipf_dstnode_t *node;
1331254562Scy	ippool_dst_t *list;
1332254562Scy	int i;
1333254562Scy	int j;
1334254562Scy
1335254562Scy	for (i = 0; i < IPL_LOGMAX; i++) {
1336254562Scy		for (list = softd->dstlist[i]; list != NULL;
1337254562Scy		     list = list->ipld_next) {
1338254562Scy			for (j = 0; j < list->ipld_maxnodes; j++) {
1339254562Scy				node = list->ipld_dests[j];
1340254562Scy				if (node == NULL)
1341254562Scy					continue;
1342254562Scy				if (node->ipfd_dest.fd_name == -1)
1343254562Scy					continue;
1344254562Scy				(void) ipf_resolvedest(softc,
1345254562Scy						       node->ipfd_names,
1346254562Scy						       &node->ipfd_dest,
1347254562Scy						       AF_INET);
1348254562Scy			}
1349254562Scy		}
1350254562Scy	}
1351254562Scy}
1352