1254219Scy/*
2254219Scy * Copyright (C) 2012 by Darren Reed.
3254219Scy *
4254219Scy * See the IPFILTER.LICENCE file for details on licencing.
5254219Scy */
6254219Scy#if defined(KERNEL) || defined(_KERNEL)
7254219Scy# undef KERNEL
8254219Scy# undef _KERNEL
9254219Scy# define        KERNEL	1
10254219Scy# define        _KERNEL	1
11254219Scy#endif
12254219Scy#if defined(__osf__)
13254219Scy# define _PROTO_NET_H_
14254219Scy#endif
15254219Scy#include <sys/errno.h>
16254219Scy#include <sys/types.h>
17254219Scy#include <sys/param.h>
18254219Scy#include <sys/file.h>
19254219Scy#if !defined(_KERNEL) && !defined(__KERNEL__)
20254219Scy# include <stdio.h>
21254219Scy# include <stdlib.h>
22254219Scy# include <string.h>
23254219Scy# define _KERNEL
24254219Scy# ifdef __OpenBSD__
25254219Scystruct file;
26254219Scy# endif
27254219Scy# include <sys/uio.h>
28254219Scy# undef _KERNEL
29254219Scy#else
30254219Scy# include <sys/systm.h>
31254219Scy# if defined(NetBSD) && (__NetBSD_Version__ >= 104000000)
32254219Scy#  include <sys/proc.h>
33254219Scy# endif
34254219Scy#endif
35254219Scy#include <sys/time.h>
36254219Scy#if !defined(linux)
37254219Scy# include <sys/protosw.h>
38254219Scy#endif
39254219Scy#include <sys/socket.h>
40254219Scy#if defined(_KERNEL) && (!defined(__SVR4) && !defined(__svr4__))
41254219Scy# include <sys/mbuf.h>
42254219Scy#endif
43254219Scy#if defined(__SVR4) || defined(__svr4__)
44254219Scy# include <sys/filio.h>
45254219Scy# include <sys/byteorder.h>
46254219Scy# ifdef _KERNEL
47254219Scy#  include <sys/dditypes.h>
48254219Scy# endif
49254219Scy# include <sys/stream.h>
50254219Scy# include <sys/kmem.h>
51254219Scy#endif
52254219Scy#if defined(__FreeBSD_version) && (__FreeBSD_version >= 300000)
53254219Scy# include <sys/malloc.h>
54254219Scy#endif
55254219Scy
56254219Scy#include <net/if.h>
57254219Scy#include <netinet/in.h>
58254219Scy
59254219Scy#include "netinet/ip_compat.h"
60254219Scy#include "netinet/ip_fil.h"
61254219Scy#include "netinet/ip_nat.h"
62254219Scy#include "netinet/ip_lookup.h"
63254219Scy#include "netinet/ip_dstlist.h"
64254219Scy
65254219Scy/* END OF INCLUDES */
66254219Scy
67254219Scy#ifdef HAS_SYS_MD5_H
68254219Scy# include <sys/md5.h>
69254219Scy#else
70254219Scy# include "md5.h"
71254219Scy#endif
72254219Scy
73254219Scy#if !defined(lint)
74254219Scystatic const char rcsid[] = "@(#)$Id: ip_dstlist.c,v 1.13.2.12 2012/07/20 08:40:19 darren_r Exp $";
75254219Scy#endif
76254219Scy
77254219Scytypedef struct ipf_dstl_softc_s {
78254219Scy	ippool_dst_t	*dstlist[LOOKUP_POOL_SZ];
79254219Scy	ippool_dst_t	**tails[LOOKUP_POOL_SZ];
80254219Scy	ipf_dstl_stat_t	stats;
81254219Scy} ipf_dstl_softc_t;
82254219Scy
83254219Scy
84254219Scystatic void *ipf_dstlist_soft_create __P((ipf_main_softc_t *));
85254219Scystatic void ipf_dstlist_soft_destroy __P((ipf_main_softc_t *, void *));
86254219Scystatic int ipf_dstlist_soft_init __P((ipf_main_softc_t *, void *));
87254219Scystatic void ipf_dstlist_soft_fini __P((ipf_main_softc_t *, void *));
88254219Scystatic int ipf_dstlist_addr_find __P((ipf_main_softc_t *, void *, int,
89254219Scy				      void *, u_int));
90254219Scystatic size_t ipf_dstlist_flush __P((ipf_main_softc_t *, void *,
91254219Scy				     iplookupflush_t *));
92254219Scystatic int ipf_dstlist_iter_deref __P((ipf_main_softc_t *, void *, int, int,
93254219Scy				       void *));
94254219Scystatic int ipf_dstlist_iter_next __P((ipf_main_softc_t *, void *, ipftoken_t *,
95254219Scy				      ipflookupiter_t *));
96254219Scystatic int ipf_dstlist_node_add __P((ipf_main_softc_t *, void *,
97254219Scy				     iplookupop_t *, int));
98254219Scystatic int ipf_dstlist_node_del __P((ipf_main_softc_t *, void *,
99254219Scy				     iplookupop_t *, int));
100254219Scystatic int ipf_dstlist_stats_get __P((ipf_main_softc_t *, void *,
101254219Scy				      iplookupop_t *));
102254219Scystatic int ipf_dstlist_table_add __P((ipf_main_softc_t *, void *,
103254219Scy				      iplookupop_t *));
104254219Scystatic int ipf_dstlist_table_del __P((ipf_main_softc_t *, void *,
105254219Scy				      iplookupop_t *));
106254219Scystatic int ipf_dstlist_table_deref __P((ipf_main_softc_t *, void *, void *));
107254219Scystatic void *ipf_dstlist_table_find __P((void *, int, char *));
108254219Scystatic void ipf_dstlist_table_free __P((ipf_dstl_softc_t *, ippool_dst_t *));
109254219Scystatic void ipf_dstlist_table_remove __P((ipf_main_softc_t *,
110254219Scy					  ipf_dstl_softc_t *, ippool_dst_t *));
111254219Scystatic void ipf_dstlist_table_clearnodes __P((ipf_dstl_softc_t *,
112254219Scy					      ippool_dst_t *));
113254219Scystatic ipf_dstnode_t *ipf_dstlist_select __P((fr_info_t *, ippool_dst_t *));
114254219Scystatic void *ipf_dstlist_select_ref __P((void *, int, char *));
115254219Scystatic void ipf_dstlist_node_free __P((ipf_dstl_softc_t *, ippool_dst_t *, ipf_dstnode_t *));
116254219Scystatic int ipf_dstlist_node_deref __P((void *, ipf_dstnode_t *));
117254219Scystatic void ipf_dstlist_expire __P((ipf_main_softc_t *, void *));
118254219Scystatic void ipf_dstlist_sync __P((ipf_main_softc_t *, void *));
119254219Scy
120254219Scyipf_lookup_t ipf_dstlist_backend = {
121254219Scy	IPLT_DSTLIST,
122254219Scy	ipf_dstlist_soft_create,
123254219Scy	ipf_dstlist_soft_destroy,
124254219Scy	ipf_dstlist_soft_init,
125254219Scy	ipf_dstlist_soft_fini,
126254219Scy	ipf_dstlist_addr_find,
127254219Scy	ipf_dstlist_flush,
128254219Scy	ipf_dstlist_iter_deref,
129254219Scy	ipf_dstlist_iter_next,
130254219Scy	ipf_dstlist_node_add,
131254219Scy	ipf_dstlist_node_del,
132254219Scy	ipf_dstlist_stats_get,
133254219Scy	ipf_dstlist_table_add,
134254219Scy	ipf_dstlist_table_del,
135254219Scy	ipf_dstlist_table_deref,
136254219Scy	ipf_dstlist_table_find,
137254219Scy	ipf_dstlist_select_ref,
138254219Scy	ipf_dstlist_select_node,
139254219Scy	ipf_dstlist_expire,
140254219Scy	ipf_dstlist_sync
141254219Scy};
142254219Scy
143254219Scy
144254219Scy/* ------------------------------------------------------------------------ */
145254219Scy/* Function:    ipf_dstlist_soft_create                                     */
146254219Scy/* Returns:     int - 0 = success, else error                               */
147254219Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
148254219Scy/*                                                                          */
149254219Scy/* Allocating a chunk of memory filled with 0's is enough for the current   */
150254219Scy/* soft context used with destination lists.                                */
151254219Scy/* ------------------------------------------------------------------------ */
152254219Scystatic void *
153254219Scyipf_dstlist_soft_create(softc)
154254219Scy	ipf_main_softc_t *softc;
155254219Scy{
156254219Scy	ipf_dstl_softc_t *softd;
157254219Scy	int i;
158254219Scy
159254219Scy	KMALLOC(softd, ipf_dstl_softc_t *);
160254219Scy	if (softd == NULL) {
161254219Scy		IPFERROR(120028);
162254219Scy		return NULL;
163254219Scy	}
164254219Scy
165254219Scy	bzero((char *)softd, sizeof(*softd));
166254219Scy	for (i = 0; i <= IPL_LOGMAX; i++)
167254219Scy		softd->tails[i] = &softd->dstlist[i];
168254219Scy
169254219Scy	return softd;
170254219Scy}
171254219Scy
172254219Scy
173254219Scy/* ------------------------------------------------------------------------ */
174254219Scy/* Function:    ipf_dstlist_soft_destroy                                    */
175254219Scy/* Returns:     Nil                                                         */
176254219Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
177254219Scy/*              arg(I)   - pointer to local context to use                  */
178254219Scy/*                                                                          */
179254219Scy/* For destination lists, the only thing we have to do when destroying the  */
180254219Scy/* soft context is free it!                                                 */
181254219Scy/* ------------------------------------------------------------------------ */
182254219Scystatic void
183254219Scyipf_dstlist_soft_destroy(softc, arg)
184254219Scy	ipf_main_softc_t *softc;
185254219Scy	void *arg;
186254219Scy{
187254219Scy	ipf_dstl_softc_t *softd = arg;
188254219Scy
189254219Scy	KFREE(softd);
190254219Scy}
191254219Scy
192254219Scy
193254219Scy/* ------------------------------------------------------------------------ */
194254219Scy/* Function:    ipf_dstlist_soft_init                                       */
195254219Scy/* Returns:     int - 0 = success, else error                               */
196254219Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
197254219Scy/*              arg(I)   - pointer to local context to use                  */
198254219Scy/*                                                                          */
199254219Scy/* There is currently no soft context for destination list management.      */
200254219Scy/* ------------------------------------------------------------------------ */
201254219Scystatic int
202254219Scyipf_dstlist_soft_init(softc, arg)
203254219Scy	ipf_main_softc_t *softc;
204254219Scy	void *arg;
205254219Scy{
206254219Scy	return 0;
207254219Scy}
208254219Scy
209254219Scy
210254219Scy/* ------------------------------------------------------------------------ */
211254219Scy/* Function:    ipf_dstlist_soft_fini                                       */
212254219Scy/* Returns:     Nil                                                         */
213254219Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
214254219Scy/*              arg(I)   - pointer to local context to use                  */
215254219Scy/*                                                                          */
216254219Scy/* There is currently no soft context for destination list management.      */
217254219Scy/* ------------------------------------------------------------------------ */
218254219Scystatic void
219254219Scyipf_dstlist_soft_fini(softc, arg)
220254219Scy	ipf_main_softc_t *softc;
221254219Scy	void *arg;
222254219Scy{
223254219Scy	ipf_dstl_softc_t *softd = arg;
224254219Scy	int i;
225254219Scy
226254219Scy	for (i = -1; i <= IPL_LOGMAX; i++) {
227254219Scy		while (softd->dstlist[i + 1] != NULL) {
228254219Scy			ipf_dstlist_table_remove(softc, softd,
229254219Scy						 softd->dstlist[i + 1]);
230254219Scy		}
231254219Scy	}
232254219Scy
233254219Scy	ASSERT(softd->stats.ipls_numderefnodes == 0);
234254219Scy}
235254219Scy
236254219Scy
237254219Scy/* ------------------------------------------------------------------------ */
238254219Scy/* Function:    ipf_dstlist_addr_find                                       */
239254219Scy/* Returns:     int - 0 = success, else error                               */
240254219Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
241254219Scy/*              arg1(I)  - pointer to local context to use                  */
242254219Scy/*              arg2(I)  - pointer to local context to use                  */
243254219Scy/*              arg3(I)  - pointer to local context to use                  */
244254219Scy/*              arg4(I)  - pointer to local context to use                  */
245254219Scy/*                                                                          */
246254219Scy/* There is currently no such thing as searching a destination list for an  */
247254219Scy/* address so this function becomes a no-op. Its presence is required as    */
248254219Scy/* ipf_lookup_res_name() stores the "addr_find" function pointer in the     */
249254219Scy/* pointer passed in to it as funcptr, although it could be a generic null- */
250254219Scy/* op function rather than a specific one.                                  */
251254219Scy/* ------------------------------------------------------------------------ */
252254219Scy/*ARGSUSED*/
253254219Scystatic int
254254219Scyipf_dstlist_addr_find(softc, arg1, arg2, arg3, arg4)
255254219Scy	ipf_main_softc_t *softc;
256254219Scy	void *arg1, *arg3;
257254219Scy	int arg2;
258254219Scy	u_int arg4;
259254219Scy{
260254219Scy	return -1;
261254219Scy}
262254219Scy
263254219Scy
264254219Scy/* ------------------------------------------------------------------------ */
265254219Scy/* Function:    ipf_dstlist_flush                                           */
266254219Scy/* Returns:     int      - number of objects deleted                        */
267254219Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
268254219Scy/*              arg(I)   - pointer to local context to use                  */
269254219Scy/*              fop(I)   - pointer to lookup flush operation data           */
270254219Scy/*                                                                          */
271254219Scy/* Flush all of the destination tables that match the data passed in with   */
272254219Scy/* the iplookupflush_t. There are two ways to match objects: the device for */
273254219Scy/* which they are to be used with and their name.                           */
274254219Scy/* ------------------------------------------------------------------------ */
275254219Scystatic size_t
276254219Scyipf_dstlist_flush(softc, arg, fop)
277254219Scy	ipf_main_softc_t *softc;
278254219Scy	void *arg;
279254219Scy	iplookupflush_t *fop;
280254219Scy{
281254219Scy	ipf_dstl_softc_t *softd = arg;
282254219Scy	ippool_dst_t *node, *next;
283254219Scy	int n, i;
284254219Scy
285254219Scy	for (n = 0, i = -1; i <= IPL_LOGMAX; i++) {
286254219Scy		if (fop->iplf_unit != IPLT_ALL && fop->iplf_unit != i)
287254219Scy			continue;
288254219Scy		for (node = softd->dstlist[i + 1]; node != NULL; node = next) {
289254219Scy			next = node->ipld_next;
290254219Scy
291254219Scy			if ((*fop->iplf_name != '\0') &&
292254219Scy			    strncmp(fop->iplf_name, node->ipld_name,
293254219Scy				    FR_GROUPLEN))
294254219Scy				continue;
295254219Scy
296254219Scy			ipf_dstlist_table_remove(softc, softd, node);
297254219Scy			n++;
298254219Scy		}
299254219Scy	}
300254219Scy	return n;
301254219Scy}
302254219Scy
303254219Scy
304254219Scy/* ------------------------------------------------------------------------ */
305254219Scy/* Function:    ipf_dstlist_iter_deref                                      */
306254219Scy/* Returns:     int      - 0 = success, else error                          */
307254219Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
308254219Scy/*              arg(I)   - pointer to local context to use                  */
309254219Scy/*              otype(I) - type of data structure to iterate through        */
310254219Scy/*              unit(I)  - device we are working with                       */
311254219Scy/*              data(I)  - address of object in kernel space                */
312254219Scy/*                                                                          */
313254219Scy/* This function is called when the iteration token is being free'd and is  */
314254219Scy/* responsible for dropping the reference count of the structure it points  */
315254219Scy/* to.                                                                      */
316254219Scy/* ------------------------------------------------------------------------ */
317254219Scystatic int
318254219Scyipf_dstlist_iter_deref(softc, arg, otype, unit, data)
319254219Scy	ipf_main_softc_t *softc;
320254219Scy	void *arg;
321254219Scy	int otype, unit;
322254219Scy	void *data;
323254219Scy{
324254219Scy	if (data == NULL) {
325254219Scy		IPFERROR(120001);
326254219Scy		return EINVAL;
327254219Scy	}
328254219Scy
329254219Scy	if (unit < -1 || unit > IPL_LOGMAX) {
330254219Scy		IPFERROR(120002);
331254219Scy		return EINVAL;
332254219Scy	}
333254219Scy
334254219Scy	switch (otype)
335254219Scy	{
336254219Scy	case IPFLOOKUPITER_LIST :
337254219Scy		ipf_dstlist_table_deref(softc, arg, (ippool_dst_t *)data);
338254219Scy		break;
339254219Scy
340254219Scy	case IPFLOOKUPITER_NODE :
341254219Scy		ipf_dstlist_node_deref(arg, (ipf_dstnode_t *)data);
342254219Scy		break;
343254219Scy	}
344254219Scy
345254219Scy	return 0;
346254219Scy}
347254219Scy
348254219Scy
349254219Scy/* ------------------------------------------------------------------------ */
350254219Scy/* Function:    ipf_dstlist_iter_next                                       */
351254219Scy/* Returns:     int - 0 = success, else error                               */
352254219Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
353254219Scy/*              arg(I)   - pointer to local context to use                  */
354254219Scy/*              op(I)    - pointer to lookup operation data                 */
355254219Scy/*              uid(I)   - uid of process doing the ioctl                   */
356254219Scy/*                                                                          */
357254219Scy/* This function is responsible for either selecting the next destination   */
358254219Scy/* list or node on a destination list to be returned as a user process      */
359254219Scy/* iterates through the list of destination lists or nodes.                 */
360254219Scy/* ------------------------------------------------------------------------ */
361254219Scystatic int
362254219Scyipf_dstlist_iter_next(softc, arg, token, iter)
363254219Scy	ipf_main_softc_t *softc;
364254219Scy	void *arg;
365254219Scy	ipftoken_t *token;
366254219Scy	ipflookupiter_t *iter;
367254219Scy{
368254219Scy	ipf_dstnode_t zn, *nextnode = NULL, *node = NULL;
369254219Scy	ippool_dst_t zero, *next = NULL, *dsttab = NULL;
370254219Scy	ipf_dstl_softc_t *softd = arg;
371254219Scy	int err = 0;
372254219Scy	void *hint;
373254219Scy
374254219Scy	switch (iter->ili_otype)
375254219Scy	{
376254219Scy	case IPFLOOKUPITER_LIST :
377254219Scy		dsttab = token->ipt_data;
378254219Scy		if (dsttab == NULL) {
379254219Scy			next = softd->dstlist[(int)iter->ili_unit + 1];
380254219Scy		} else {
381254219Scy			next = dsttab->ipld_next;
382254219Scy		}
383254219Scy
384254219Scy		if (next != NULL) {
385254219Scy			ATOMIC_INC32(next->ipld_ref);
386254219Scy			token->ipt_data = next;
387254219Scy			hint = next->ipld_next;
388254219Scy		} else {
389254219Scy			bzero((char *)&zero, sizeof(zero));
390254219Scy			next = &zero;
391254219Scy			token->ipt_data = NULL;
392254219Scy			hint = NULL;
393254219Scy		}
394254219Scy		break;
395254219Scy
396254219Scy	case IPFLOOKUPITER_NODE :
397254219Scy		node = token->ipt_data;
398254219Scy		if (node == NULL) {
399254219Scy			dsttab = ipf_dstlist_table_find(arg, iter->ili_unit,
400254219Scy							iter->ili_name);
401254219Scy			if (dsttab == NULL) {
402254219Scy				IPFERROR(120004);
403254219Scy				err = ESRCH;
404254219Scy				nextnode = NULL;
405254219Scy			} else {
406254219Scy				if (dsttab->ipld_dests == NULL)
407254219Scy					nextnode = NULL;
408254219Scy				else
409254219Scy					nextnode = *dsttab->ipld_dests;
410254219Scy				dsttab = NULL;
411254219Scy			}
412254219Scy		} else {
413254219Scy			nextnode = node->ipfd_next;
414254219Scy		}
415254219Scy
416254219Scy		if (nextnode != NULL) {
417254219Scy			MUTEX_ENTER(&nextnode->ipfd_lock);
418254219Scy			nextnode->ipfd_ref++;
419254219Scy			MUTEX_EXIT(&nextnode->ipfd_lock);
420254219Scy			token->ipt_data = nextnode;
421254219Scy			hint = nextnode->ipfd_next;
422254219Scy		} else {
423254219Scy			bzero((char *)&zn, sizeof(zn));
424254219Scy			nextnode = &zn;
425254219Scy			token->ipt_data = NULL;
426254219Scy			hint = NULL;
427254219Scy		}
428254219Scy		break;
429254219Scy	default :
430254219Scy		IPFERROR(120003);
431254219Scy		err = EINVAL;
432254219Scy		break;
433254219Scy	}
434254219Scy
435254219Scy	if (err != 0)
436254219Scy		return err;
437254219Scy
438254219Scy	switch (iter->ili_otype)
439254219Scy	{
440254219Scy	case IPFLOOKUPITER_LIST :
441254219Scy		if (dsttab != NULL)
442254219Scy			ipf_dstlist_table_deref(softc, arg, dsttab);
443254219Scy		err = COPYOUT(next, iter->ili_data, sizeof(*next));
444254219Scy		if (err != 0) {
445254219Scy			IPFERROR(120005);
446254219Scy			err = EFAULT;
447254219Scy		}
448254219Scy		break;
449254219Scy
450254219Scy	case IPFLOOKUPITER_NODE :
451254219Scy		if (node != NULL)
452254219Scy			ipf_dstlist_node_deref(arg, node);
453254219Scy		err = COPYOUT(nextnode, iter->ili_data, sizeof(*nextnode));
454254219Scy		if (err != 0) {
455254219Scy			IPFERROR(120006);
456254219Scy			err = EFAULT;
457254219Scy		}
458254219Scy		break;
459254219Scy	}
460254219Scy
461254219Scy	if (hint == NULL)
462254219Scy		ipf_token_mark_complete(token);
463254219Scy
464254219Scy	return err;
465254219Scy}
466254219Scy
467254219Scy
468254219Scy/* ------------------------------------------------------------------------ */
469254219Scy/* Function:    ipf_dstlist_node_add                                        */
470254219Scy/* Returns:     int - 0 = success, else error                               */
471254219Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
472254219Scy/*              arg(I)   - pointer to local context to use                  */
473254219Scy/*              op(I)    - pointer to lookup operation data                 */
474254219Scy/*              uid(I)   - uid of process doing the ioctl                   */
475254219Scy/* Locks:       WRITE(ipf_poolrw)                                           */
476254219Scy/*                                                                          */
477254219Scy/* Add a new node to a destination list. To do this, we only copy in the    */
478254219Scy/* frdest_t structure because that contains the only data required from the */
479254219Scy/* application to create a new node. The frdest_t doesn't contain the name  */
480254219Scy/* itself. When loading filter rules, fd_name is a 'pointer' to the name.   */
481254219Scy/* In this case, the 'pointer' does not work, instead it is the length of   */
482254219Scy/* the name and the name is immediately following the frdest_t structure.   */
483254219Scy/* fd_name must include the trailing \0, so it should be strlen(str) + 1.   */
484254219Scy/* For simple sanity checking, an upper bound on the size of fd_name is     */
485254219Scy/* imposed - 128.                                                          */
486254219Scy/* ------------------------------------------------------------------------ */
487254219Scystatic int
488254219Scyipf_dstlist_node_add(softc, arg, op, uid)
489254219Scy	ipf_main_softc_t *softc;
490254219Scy	void *arg;
491254219Scy	iplookupop_t *op;
492254219Scy	int uid;
493254219Scy{
494254219Scy	ipf_dstl_softc_t *softd = arg;
495254219Scy	ipf_dstnode_t *node, **nodes;
496254219Scy	ippool_dst_t *d;
497254219Scy	frdest_t dest;
498254219Scy	int err;
499254219Scy
500254219Scy	if (op->iplo_size < sizeof(frdest_t)) {
501254219Scy		IPFERROR(120007);
502254219Scy		return EINVAL;
503254219Scy	}
504254219Scy
505254219Scy	err = COPYIN(op->iplo_struct, &dest, sizeof(dest));
506254219Scy	if (err != 0) {
507254219Scy		IPFERROR(120009);
508254219Scy		return EFAULT;
509254219Scy	}
510254219Scy
511254219Scy	d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name);
512254219Scy	if (d == NULL) {
513254219Scy		IPFERROR(120010);
514254219Scy		return ESRCH;
515254219Scy	}
516254219Scy
517254219Scy	switch (dest.fd_addr.adf_family)
518254219Scy	{
519254219Scy	case AF_INET :
520254219Scy	case AF_INET6 :
521254219Scy		break;
522254219Scy	default :
523254219Scy		IPFERROR(120019);
524254219Scy		return EINVAL;
525254219Scy	}
526254219Scy
527254219Scy	if (dest.fd_name < -1 || dest.fd_name > 128) {
528254219Scy		IPFERROR(120018);
529254219Scy		return EINVAL;
530254219Scy	}
531254219Scy
532254219Scy	KMALLOCS(node, ipf_dstnode_t *, sizeof(*node) + dest.fd_name);
533254219Scy	if (node == NULL) {
534254219Scy		softd->stats.ipls_nomem++;
535254219Scy		IPFERROR(120008);
536254219Scy		return ENOMEM;
537254219Scy	}
538254219Scy	bzero((char *)node, sizeof(*node) + dest.fd_name);
539254219Scy
540254219Scy	bcopy(&dest, &node->ipfd_dest, sizeof(dest));
541254219Scy	node->ipfd_size = sizeof(*node) + dest.fd_name;
542254219Scy
543254219Scy	if (dest.fd_name > 0) {
544254219Scy		/*
545254219Scy		 * fd_name starts out as the length of the string to copy
546254219Scy		 * in (including \0) and ends up being the offset from
547254219Scy		 * fd_names (0).
548254219Scy		 */
549254219Scy		err = COPYIN((char *)op->iplo_struct + sizeof(dest),
550254219Scy			     node->ipfd_names, dest.fd_name);
551254219Scy		if (err != 0) {
552254219Scy			IPFERROR(120017);
553254219Scy			KFREES(node, node->ipfd_size);
554254219Scy			return EFAULT;
555254219Scy		}
556254219Scy		node->ipfd_dest.fd_name = 0;
557254219Scy	} else {
558254219Scy		node->ipfd_dest.fd_name = -1;
559254219Scy	}
560254219Scy
561254219Scy	if (d->ipld_nodes == d->ipld_maxnodes) {
562254219Scy		KMALLOCS(nodes, ipf_dstnode_t **,
563254219Scy			 sizeof(*nodes) * (d->ipld_maxnodes + 1));
564254219Scy		if (nodes == NULL) {
565254219Scy			softd->stats.ipls_nomem++;
566254219Scy			IPFERROR(120022);
567254219Scy			KFREES(node, node->ipfd_size);
568254219Scy			return ENOMEM;
569254219Scy		}
570254219Scy		if (d->ipld_dests != NULL) {
571254219Scy			bcopy(d->ipld_dests, nodes,
572254219Scy			      sizeof(*nodes) * d->ipld_maxnodes);
573254219Scy			KFREES(d->ipld_dests, sizeof(*nodes) * d->ipld_nodes);
574254219Scy			nodes[0]->ipfd_pnext = nodes;
575254219Scy		}
576254219Scy		d->ipld_dests = nodes;
577254219Scy		d->ipld_maxnodes++;
578254219Scy	}
579254219Scy	d->ipld_dests[d->ipld_nodes] = node;
580254219Scy	d->ipld_nodes++;
581254219Scy
582254219Scy	if (d->ipld_nodes == 1) {
583254219Scy		node->ipfd_pnext = d->ipld_dests;
584254219Scy	} else if (d->ipld_nodes > 1) {
585254219Scy		node->ipfd_pnext = &d->ipld_dests[d->ipld_nodes - 2]->ipfd_next;
586254219Scy	}
587254219Scy	*node->ipfd_pnext = node;
588254219Scy
589254219Scy	MUTEX_INIT(&node->ipfd_lock, "ipf dst node lock");
590254219Scy	node->ipfd_uid = uid;
591254219Scy	node->ipfd_ref = 1;
592254219Scy	if (node->ipfd_dest.fd_name == 0)
593254219Scy		(void) ipf_resolvedest(softc, node->ipfd_names,
594254219Scy				       &node->ipfd_dest, AF_INET);
595254219Scy#ifdef USE_INET6
596254219Scy	if (node->ipfd_dest.fd_name == 0 &&
597254219Scy	    node->ipfd_dest.fd_ptr == (void *)-1)
598254219Scy		(void) ipf_resolvedest(softc, node->ipfd_names,
599254219Scy				       &node->ipfd_dest, AF_INET6);
600254219Scy#endif
601254219Scy
602254219Scy	softd->stats.ipls_numnodes++;
603254219Scy
604254219Scy	return 0;
605254219Scy}
606254219Scy
607254219Scy
608254219Scy/* ------------------------------------------------------------------------ */
609254219Scy/* Function:    ipf_dstlist_node_deref                                      */
610254219Scy/* Returns:     int - 0 = success, else error                               */
611254219Scy/* Parameters:  arg(I)  - pointer to local context to use                   */
612254219Scy/*              node(I) - pointer to destionation node to free              */
613254219Scy/*                                                                          */
614254219Scy/* Dereference the use count by one. If it drops to zero then we can assume */
615254219Scy/* that it has been removed from any lists/tables and is ripe for freeing.  */
616254219Scy/* The pointer to context is required for the purpose of maintaining        */
617254219Scy/* statistics.                                                              */
618254219Scy/* ------------------------------------------------------------------------ */
619254219Scystatic int
620254219Scyipf_dstlist_node_deref(arg, node)
621254219Scy	void *arg;
622254219Scy	ipf_dstnode_t *node;
623254219Scy{
624254219Scy	ipf_dstl_softc_t *softd = arg;
625254219Scy	int ref;
626254219Scy
627254219Scy	MUTEX_ENTER(&node->ipfd_lock);
628254219Scy	ref = --node->ipfd_ref;
629254219Scy	MUTEX_EXIT(&node->ipfd_lock);
630254219Scy
631254219Scy	if (ref > 0)
632254219Scy		return 0;
633254219Scy
634254219Scy	if ((node->ipfd_flags & IPDST_DELETE) != 0)
635254219Scy		softd->stats.ipls_numderefnodes--;
636254219Scy	MUTEX_DESTROY(&node->ipfd_lock);
637254219Scy	KFREES(node, node->ipfd_size);
638254219Scy	softd->stats.ipls_numnodes--;
639254219Scy
640254219Scy	return 0;
641254219Scy}
642254219Scy
643254219Scy
644254219Scy/* ------------------------------------------------------------------------ */
645254219Scy/* Function:    ipf_dstlist_node_del                                        */
646254219Scy/* Returns:     int      - 0 = success, else error                          */
647254219Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
648254219Scy/*              arg(I)   - pointer to local context to use                  */
649254219Scy/*              op(I)    - pointer to lookup operation data                 */
650254219Scy/*              uid(I)   - uid of process doing the ioctl                   */
651254219Scy/*                                                                          */
652254219Scy/* Look for a matching destination node on the named table and free it if   */
653254219Scy/* found. Because the name embedded in the frdest_t is variable in length,  */
654254219Scy/* it is necessary to allocate some memory locally, to complete this op.    */
655254219Scy/* ------------------------------------------------------------------------ */
656254219Scystatic int
657254219Scyipf_dstlist_node_del(softc, arg, op, uid)
658254219Scy	ipf_main_softc_t *softc;
659254219Scy	void *arg;
660254219Scy	iplookupop_t *op;
661254219Scy	int uid;
662254219Scy{
663254219Scy	ipf_dstl_softc_t *softd = arg;
664254219Scy	ipf_dstnode_t *node;
665254219Scy	frdest_t frd, *temp;
666254219Scy	ippool_dst_t *d;
667254219Scy	size_t size;
668254219Scy	int err;
669254219Scy
670254219Scy	d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name);
671254219Scy	if (d == NULL) {
672254219Scy		IPFERROR(120012);
673254219Scy		return ESRCH;
674254219Scy	}
675254219Scy
676254219Scy	err = COPYIN(op->iplo_struct, &frd, sizeof(frd));
677254219Scy	if (err != 0) {
678254219Scy		IPFERROR(120011);
679254219Scy		return EFAULT;
680254219Scy	}
681254219Scy
682254219Scy	size = sizeof(*temp) + frd.fd_name;
683254219Scy	KMALLOCS(temp, frdest_t *, size);
684254219Scy	if (temp == NULL) {
685254219Scy		softd->stats.ipls_nomem++;
686254219Scy		IPFERROR(120026);
687254219Scy		return ENOMEM;
688254219Scy	}
689254219Scy
690254219Scy	err = COPYIN(op->iplo_struct, temp, size);
691254219Scy	if (err != 0) {
692254219Scy		IPFERROR(120027);
693254219Scy		return EFAULT;
694254219Scy	}
695254219Scy
696254219Scy	MUTEX_ENTER(&d->ipld_lock);
697254219Scy	for (node = *d->ipld_dests; node != NULL; node = node->ipfd_next) {
698254219Scy		if ((uid != 0) && (node->ipfd_uid != uid))
699254219Scy			continue;
700254219Scy		if (node->ipfd_size != size)
701254219Scy			continue;
702254219Scy		if (!bcmp(&node->ipfd_dest.fd_ip6, &frd.fd_ip6,
703254219Scy			  size - offsetof(frdest_t, fd_ip6))) {
704254219Scy			ipf_dstlist_node_free(softd, d, node);
705254219Scy			MUTEX_EXIT(&d->ipld_lock);
706254219Scy			KFREES(temp, size);
707254219Scy			return 0;
708254219Scy		}
709254219Scy	}
710254219Scy	MUTEX_EXIT(&d->ipld_lock);
711254219Scy	KFREES(temp, size);
712254219Scy
713254219Scy	return ESRCH;
714254219Scy}
715254219Scy
716254219Scy
717254219Scy/* ------------------------------------------------------------------------ */
718254219Scy/* Function:    ipf_dstlist_node_free                                       */
719254219Scy/* Returns:     Nil                                                         */
720254219Scy/* Parameters:  softd(I) - pointer to the destination list context          */
721254219Scy/*              d(I)     - pointer to destination list                      */
722254219Scy/*              node(I)  - pointer to node to free                          */
723254219Scy/* Locks:       MUTEX(ipld_lock) or WRITE(ipf_poolrw)                       */
724254219Scy/*                                                                          */
725254219Scy/* Free the destination node by first removing it from any lists and then   */
726254219Scy/* checking if this was the last reference held to the object. While the    */
727254219Scy/* array of pointers to nodes is compacted, its size isn't reduced (by way  */
728254219Scy/* of allocating a new smaller one and copying) because the belief is that  */
729254219Scy/* it is likely the array will again reach that size.                       */
730254219Scy/* ------------------------------------------------------------------------ */
731254219Scystatic void
732254219Scyipf_dstlist_node_free(softd, d, node)
733254219Scy	ipf_dstl_softc_t *softd;
734254219Scy	ippool_dst_t *d;
735254219Scy	ipf_dstnode_t *node;
736254219Scy{
737254219Scy	int i;
738254219Scy
739254219Scy	/*
740254219Scy	 * Compact the array of pointers to nodes.
741254219Scy	 */
742254219Scy	for (i = 0; i < d->ipld_nodes; i++)
743254219Scy		if (d->ipld_dests[i] == node)
744254219Scy			break;
745254219Scy	if (d->ipld_nodes - i > 1) {
746254219Scy		bcopy(&d->ipld_dests[i + 1], &d->ipld_dests[i],
747254219Scy		      sizeof(*d->ipld_dests) * (d->ipld_nodes - i - 1));
748254219Scy	}
749254219Scy	d->ipld_nodes--;
750254219Scy
751254219Scy	if (node->ipfd_pnext != NULL)
752254219Scy		*node->ipfd_pnext = node->ipfd_next;
753254219Scy	if (node->ipfd_next != NULL)
754254219Scy		node->ipfd_next->ipfd_pnext = node->ipfd_pnext;
755254219Scy	node->ipfd_pnext = NULL;
756254219Scy	node->ipfd_next = NULL;
757254219Scy
758254219Scy	if ((node->ipfd_flags & IPDST_DELETE) == 0) {
759254219Scy		softd->stats.ipls_numderefnodes++;
760254219Scy		node->ipfd_flags |= IPDST_DELETE;
761254219Scy	}
762254219Scy
763254219Scy	ipf_dstlist_node_deref(softd, node);
764254219Scy}
765254219Scy
766254219Scy
767254219Scy/* ------------------------------------------------------------------------ */
768254219Scy/* Function:    ipf_dstlist_stats_get                                       */
769254219Scy/* Returns:     int - 0 = success, else error                               */
770254219Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
771254219Scy/*              arg(I)   - pointer to local context to use                  */
772254219Scy/*              op(I)    - pointer to lookup operation data                 */
773254219Scy/*                                                                          */
774254219Scy/* Return the current statistics for destination lists. This may be for all */
775254219Scy/* of them or just information pertaining to a particular table.            */
776254219Scy/* ------------------------------------------------------------------------ */
777254219Scy/*ARGSUSED*/
778254219Scystatic int
779254219Scyipf_dstlist_stats_get(softc, arg, op)
780254219Scy	ipf_main_softc_t *softc;
781254219Scy	void *arg;
782254219Scy	iplookupop_t *op;
783254219Scy{
784254219Scy	ipf_dstl_softc_t *softd = arg;
785254219Scy	ipf_dstl_stat_t stats;
786254219Scy	int unit, i, err = 0;
787254219Scy
788254219Scy	if (op->iplo_size != sizeof(ipf_dstl_stat_t)) {
789254219Scy		IPFERROR(120023);
790254219Scy		return EINVAL;
791254219Scy	}
792254219Scy
793254219Scy	stats = softd->stats;
794254219Scy	unit = op->iplo_unit;
795254219Scy	if (unit == IPL_LOGALL) {
796254219Scy		for (i = 0; i <= IPL_LOGMAX; i++)
797254219Scy			stats.ipls_list[i] = softd->dstlist[i];
798254219Scy	} else if (unit >= 0 && unit <= IPL_LOGMAX) {
799254219Scy		void *ptr;
800254219Scy
801254219Scy		if (op->iplo_name[0] != '\0')
802254219Scy			ptr = ipf_dstlist_table_find(softd, unit,
803254219Scy						     op->iplo_name);
804254219Scy		else
805254219Scy			ptr = softd->dstlist[unit + 1];
806254219Scy		stats.ipls_list[unit] = ptr;
807254219Scy	} else {
808254219Scy		IPFERROR(120024);
809254219Scy		err = EINVAL;
810254219Scy	}
811254219Scy
812254219Scy	if (err == 0) {
813254219Scy		err = COPYOUT(&stats, op->iplo_struct, sizeof(stats));
814254219Scy		if (err != 0) {
815254219Scy			IPFERROR(120025);
816254219Scy			return EFAULT;
817254219Scy		}
818254219Scy	}
819254219Scy	return 0;
820254219Scy}
821254219Scy
822254219Scy
823254219Scy/* ------------------------------------------------------------------------ */
824254219Scy/* Function:    ipf_dstlist_table_add                                       */
825254219Scy/* Returns:     int      - 0 = success, else error                          */
826254219Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
827254219Scy/*              arg(I)   - pointer to local context to use                  */
828254219Scy/*              op(I)    - pointer to lookup operation data                 */
829254219Scy/*                                                                          */
830254219Scy/* Add a new destination table to the list of those available for the given */
831254219Scy/* device. Because we seldom operate on these objects (find/add/delete),    */
832254219Scy/* they are just kept in a simple linked list.                              */
833254219Scy/* ------------------------------------------------------------------------ */
834254219Scystatic int
835254219Scyipf_dstlist_table_add(softc, arg, op)
836254219Scy	ipf_main_softc_t *softc;
837254219Scy	void *arg;
838254219Scy	iplookupop_t *op;
839254219Scy{
840254219Scy	ipf_dstl_softc_t *softd = arg;
841254219Scy	ippool_dst_t user, *d, *new;
842254219Scy	int unit, err;
843254219Scy
844254219Scy	d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name);
845254219Scy	if (d != NULL) {
846254219Scy		IPFERROR(120013);
847254219Scy		return EEXIST;
848254219Scy	}
849254219Scy
850254219Scy	err = COPYIN(op->iplo_struct, &user, sizeof(user));
851254219Scy	if (err != 0) {
852254219Scy		IPFERROR(120021);
853254219Scy		return EFAULT;
854254219Scy	}
855254219Scy
856254219Scy	KMALLOC(new, ippool_dst_t *);
857254219Scy	if (new == NULL) {
858254219Scy		softd->stats.ipls_nomem++;
859254219Scy		IPFERROR(120014);
860254219Scy		return ENOMEM;
861254219Scy	}
862254219Scy	bzero((char *)new, sizeof(*new));
863254219Scy
864254219Scy	MUTEX_INIT(&new->ipld_lock, "ipf dst table lock");
865254219Scy
866254219Scy	strncpy(new->ipld_name, op->iplo_name, FR_GROUPLEN);
867254219Scy	unit = op->iplo_unit;
868254219Scy	new->ipld_unit = unit;
869254219Scy	new->ipld_policy = user.ipld_policy;
870254219Scy	new->ipld_seed = ipf_random();
871254219Scy	new->ipld_ref = 1;
872254219Scy
873254219Scy	new->ipld_pnext = softd->tails[unit + 1];
874254219Scy	*softd->tails[unit + 1] = new;
875254219Scy	softd->tails[unit + 1] = &new->ipld_next;
876254219Scy	softd->stats.ipls_numlists++;
877254219Scy
878254219Scy	return 0;
879254219Scy}
880254219Scy
881254219Scy
882254219Scy/* ------------------------------------------------------------------------ */
883254219Scy/* Function:    ipf_dstlist_table_del                                       */
884254219Scy/* Returns:     int - 0 = success, else error                               */
885254219Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
886254219Scy/*              arg(I)   - pointer to local context to use                  */
887254219Scy/*              op(I)    - pointer to lookup operation data                 */
888254219Scy/*                                                                          */
889254219Scy/* Find a named destinstion list table and delete it. If there are other    */
890254219Scy/* references to it, the caller isn't told.                                 */
891254219Scy/* ------------------------------------------------------------------------ */
892254219Scystatic int
893254219Scyipf_dstlist_table_del(softc, arg, op)
894254219Scy	ipf_main_softc_t *softc;
895254219Scy	void *arg;
896254219Scy	iplookupop_t *op;
897254219Scy{
898254219Scy	ippool_dst_t *d;
899254219Scy
900254219Scy	d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name);
901254219Scy	if (d == NULL) {
902254219Scy		IPFERROR(120015);
903254219Scy		return ESRCH;
904254219Scy	}
905254219Scy
906254219Scy	if (d->ipld_dests != NULL) {
907254219Scy		IPFERROR(120016);
908254219Scy		return EBUSY;
909254219Scy	}
910254219Scy
911254219Scy	ipf_dstlist_table_remove(softc, arg, d);
912254219Scy
913254219Scy	return 0;
914254219Scy}
915254219Scy
916254219Scy
917254219Scy/* ------------------------------------------------------------------------ */
918254219Scy/* Function:    ipf_dstlist_table_remove                                    */
919254219Scy/* Returns:     Nil                                                         */
920254219Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
921254219Scy/*              softd(I) - pointer to the destination list context          */
922254219Scy/*              d(I)     - pointer to destination list                      */
923254219Scy/*                                                                          */
924254219Scy/* Remove a given destination list from existance. While the IPDST_DELETE   */
925254219Scy/* flag is set every time we call this function and the reference count is  */
926254219Scy/* non-zero, the "numdereflists" counter is always incremented because the  */
927254219Scy/* decision about whether it will be freed or not is not made here. This    */
928254219Scy/* means that the only action the code can take here is to treat it as if   */
929254219Scy/* it will become a detached.                                               */
930254219Scy/* ------------------------------------------------------------------------ */
931254219Scystatic void
932254219Scyipf_dstlist_table_remove(softc, softd, d)
933254219Scy	ipf_main_softc_t *softc;
934254219Scy	ipf_dstl_softc_t *softd;
935254219Scy	ippool_dst_t *d;
936254219Scy{
937254219Scy
938254219Scy	if (softd->tails[d->ipld_unit + 1] == &d->ipld_next)
939254219Scy		softd->tails[d->ipld_unit + 1] = d->ipld_pnext;
940254219Scy
941254219Scy	if (d->ipld_pnext != NULL)
942254219Scy		*d->ipld_pnext = d->ipld_next;
943254219Scy	if (d->ipld_next != NULL)
944254219Scy		d->ipld_next->ipld_pnext = d->ipld_pnext;
945254219Scy	d->ipld_pnext = NULL;
946254219Scy	d->ipld_next = NULL;
947254219Scy
948254219Scy	ipf_dstlist_table_clearnodes(softd, d);
949254219Scy
950254219Scy	softd->stats.ipls_numdereflists++;
951254219Scy	d->ipld_flags |= IPDST_DELETE;
952254219Scy
953254219Scy	ipf_dstlist_table_deref(softc, softd, d);
954254219Scy}
955254219Scy
956254219Scy
957254219Scy/* ------------------------------------------------------------------------ */
958254219Scy/* Function:    ipf_dstlist_table_free                                      */
959254219Scy/* Returns:     Nil                                                         */
960254219Scy/* Parameters:  softd(I) - pointer to the destination list context          */
961254219Scy/*              d(I)   - pointer to destination list                        */
962254219Scy/*                                                                          */
963254219Scy/* Free up a destination list data structure and any other memory that was  */
964254219Scy/* directly allocated as part of creating it. Individual destination list   */
965254219Scy/* nodes are not freed. It is assumed the caller will have already emptied  */
966254219Scy/* the destination list.                                                    */
967254219Scy/* ------------------------------------------------------------------------ */
968254219Scystatic void
969254219Scyipf_dstlist_table_free(softd, d)
970254219Scy	ipf_dstl_softc_t *softd;
971254219Scy	ippool_dst_t *d;
972254219Scy{
973254219Scy	MUTEX_DESTROY(&d->ipld_lock);
974254219Scy
975254219Scy	if ((d->ipld_flags & IPDST_DELETE) != 0)
976254219Scy		softd->stats.ipls_numdereflists--;
977254219Scy	softd->stats.ipls_numlists--;
978254219Scy
979254219Scy	if (d->ipld_dests != NULL) {
980254219Scy		KFREES(d->ipld_dests,
981254219Scy		       d->ipld_maxnodes * sizeof(*d->ipld_dests));
982254219Scy	}
983254219Scy
984254219Scy	KFREE(d);
985254219Scy}
986254219Scy
987254219Scy
988254219Scy/* ------------------------------------------------------------------------ */
989254219Scy/* Function:    ipf_dstlist_table_deref                                     */
990254219Scy/* Returns:     int - 0 = success, else error                               */
991254219Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
992254219Scy/*              arg(I)   - pointer to local context to use                  */
993254219Scy/*              op(I)    - pointer to lookup operation data                 */
994254219Scy/*                                                                          */
995254219Scy/* Drops the reference count on a destination list table object and free's  */
996254219Scy/* it if 0 has been reached.                                                */
997254219Scy/* ------------------------------------------------------------------------ */
998254219Scystatic int
999254219Scyipf_dstlist_table_deref(softc, arg, table)
1000254219Scy	ipf_main_softc_t *softc;
1001254219Scy	void *arg;
1002254219Scy	void *table;
1003254219Scy{
1004254219Scy	ippool_dst_t *d = table;
1005254219Scy
1006254219Scy	d->ipld_ref--;
1007254219Scy	if (d->ipld_ref > 0)
1008254219Scy		return d->ipld_ref;
1009254219Scy
1010254219Scy	ipf_dstlist_table_free(arg, d);
1011254219Scy
1012254219Scy	return 0;
1013254219Scy}
1014254219Scy
1015254219Scy
1016254219Scy/* ------------------------------------------------------------------------ */
1017254219Scy/* Function:    ipf_dstlist_table_clearnodes                                */
1018254219Scy/* Returns:     Nil                                                         */
1019254219Scy/* Parameters:  softd(I) - pointer to the destination list context          */
1020254219Scy/*              dst(I)   - pointer to destination list                      */
1021254219Scy/*                                                                          */
1022254219Scy/* Free all of the destination nodes attached to the given table.           */
1023254219Scy/* ------------------------------------------------------------------------ */
1024254219Scystatic void
1025254219Scyipf_dstlist_table_clearnodes(softd, dst)
1026254219Scy	ipf_dstl_softc_t *softd;
1027254219Scy	ippool_dst_t *dst;
1028254219Scy{
1029254219Scy	ipf_dstnode_t *node;
1030254219Scy
1031254219Scy	if (dst->ipld_dests == NULL)
1032254219Scy		return;
1033254219Scy
1034254219Scy	while ((node = *dst->ipld_dests) != NULL) {
1035254219Scy		ipf_dstlist_node_free(softd, dst, node);
1036254219Scy	}
1037254219Scy}
1038254219Scy
1039254219Scy
1040254219Scy/* ------------------------------------------------------------------------ */
1041254219Scy/* Function:    ipf_dstlist_table_find                                      */
1042254219Scy/* Returns:     int      - 0 = success, else error                          */
1043254219Scy/* Parameters:  arg(I)   - pointer to local context to use                  */
1044254219Scy/*              unit(I)  - device we are working with                       */
1045254219Scy/*              name(I)  - destination table name to find                   */
1046254219Scy/*                                                                          */
1047254219Scy/* Return a pointer to a destination table that matches the unit+name that  */
1048254219Scy/* is passed in.                                                            */
1049254219Scy/* ------------------------------------------------------------------------ */
1050254219Scystatic void *
1051254219Scyipf_dstlist_table_find(arg, unit, name)
1052254219Scy	void *arg;
1053254219Scy	int unit;
1054254219Scy	char *name;
1055254219Scy{
1056254219Scy	ipf_dstl_softc_t *softd = arg;
1057254219Scy	ippool_dst_t *d;
1058254219Scy
1059254219Scy	for (d = softd->dstlist[unit + 1]; d != NULL; d = d->ipld_next) {
1060254219Scy		if ((d->ipld_unit == unit) &&
1061254219Scy		    !strncmp(d->ipld_name, name, FR_GROUPLEN)) {
1062254219Scy			return d;
1063254219Scy		}
1064254219Scy	}
1065254219Scy
1066254219Scy	return NULL;
1067254219Scy}
1068254219Scy
1069254219Scy
1070254219Scy/* ------------------------------------------------------------------------ */
1071254219Scy/* Function:    ipf_dstlist_select_ref                                      */
1072254219Scy/* Returns:     void *   - NULL = failure, else pointer to table            */
1073254219Scy/* Parameters:  arg(I)   - pointer to local context to use                  */
1074254219Scy/*              unit(I)  - device we are working with                       */
1075254219Scy/*              name(I)  - destination table name to find                   */
1076254219Scy/*                                                                          */
1077254219Scy/* Attempt to find a destination table that matches the name passed in and  */
1078254219Scy/* if successful, bump up the reference count on it because we intend to    */
1079254219Scy/* store the pointer to it somewhere else.                                  */
1080254219Scy/* ------------------------------------------------------------------------ */
1081254219Scystatic void *
1082254219Scyipf_dstlist_select_ref(arg, unit, name)
1083254219Scy	void *arg;
1084254219Scy	int unit;
1085254219Scy	char *name;
1086254219Scy{
1087254219Scy	ippool_dst_t *d;
1088254219Scy
1089254219Scy	d = ipf_dstlist_table_find(arg, unit, name);
1090254219Scy	if (d != NULL) {
1091254219Scy		MUTEX_ENTER(&d->ipld_lock);
1092254219Scy		d->ipld_ref++;
1093254219Scy		MUTEX_EXIT(&d->ipld_lock);
1094254219Scy	}
1095254219Scy	return d;
1096254219Scy}
1097254219Scy
1098254219Scy
1099254219Scy/* ------------------------------------------------------------------------ */
1100254219Scy/* Function:    ipf_dstlist_select                                          */
1101254219Scy/* Returns:     void * - NULL = failure, else pointer to table              */
1102254219Scy/* Parameters:  fin(I) - pointer to packet information                      */
1103254219Scy/*              d(I)   - pointer to destination list                        */
1104254219Scy/*                                                                          */
1105254219Scy/* Find the next node in the destination list to be used according to the   */
1106254219Scy/* defined policy. Of these, "connection" is the most expensive policy to   */
1107254219Scy/* implement as it always looks for the node with the least number of       */
1108254219Scy/* connections associated with it.                                          */
1109254219Scy/*                                                                          */
1110254219Scy/* The hashes exclude the port numbers so that all protocols map to the     */
1111254219Scy/* same destination. Otherwise, someone doing a ping would target a         */
1112254219Scy/* different server than their TCP connection, etc. MD-5 is used to         */
1113254219Scy/* transform the addressese into something random that the other end could  */
1114254219Scy/* not easily guess and use in an attack. ipld_seed introduces an unknown   */
1115254219Scy/* into the hash calculation to increase the difficult of an attacker       */
1116254219Scy/* guessing the bucket.                                                     */
1117254219Scy/*                                                                          */
1118254219Scy/* One final comment: mixing different address families in a single pool    */
1119254219Scy/* will currently result in failures as the address family of the node is   */
1120254219Scy/* only matched up with that in the packet as the last step. While this can */
1121254219Scy/* be coded around for the weighted connection and round-robin models, it   */
1122254219Scy/* cannot be supported for the hash/random models as they do not search and */
1123254219Scy/* nor is the algorithm conducive to searching.                             */
1124254219Scy/* ------------------------------------------------------------------------ */
1125254219Scystatic ipf_dstnode_t *
1126254219Scyipf_dstlist_select(fin, d)
1127254219Scy	fr_info_t *fin;
1128254219Scy	ippool_dst_t *d;
1129254219Scy{
1130254219Scy	ipf_dstnode_t *node, *sel;
1131254219Scy	int connects;
1132254219Scy	u_32_t hash[4];
1133254219Scy	MD5_CTX ctx;
1134254219Scy	int family;
1135254219Scy	int x;
1136254219Scy
1137254219Scy	if (d->ipld_dests == NULL || *d->ipld_dests == NULL)
1138254219Scy		return NULL;
1139254219Scy
1140254219Scy	family = fin->fin_family;
1141254219Scy
1142254219Scy	MUTEX_ENTER(&d->ipld_lock);
1143254219Scy
1144254219Scy	switch (d->ipld_policy)
1145254219Scy	{
1146254219Scy	case IPLDP_ROUNDROBIN:
1147254219Scy		sel = d->ipld_selected;
1148254219Scy		if (sel == NULL) {
1149254219Scy			sel = *d->ipld_dests;
1150254219Scy		} else {
1151254219Scy			sel = sel->ipfd_next;
1152254219Scy			if (sel == NULL)
1153254219Scy				sel = *d->ipld_dests;
1154254219Scy		}
1155254219Scy		break;
1156254219Scy
1157254219Scy	case IPLDP_CONNECTION:
1158254219Scy		if (d->ipld_selected == NULL) {
1159254219Scy			sel = *d->ipld_dests;
1160254219Scy			break;
1161254219Scy		}
1162254219Scy
1163254219Scy		sel = d->ipld_selected;
1164254219Scy		connects = 0x7fffffff;
1165254219Scy		node = sel->ipfd_next;
1166254219Scy		if (node == NULL)
1167254219Scy			node = *d->ipld_dests;
1168254219Scy		while (node != d->ipld_selected) {
1169254219Scy			if (node->ipfd_states == 0) {
1170254219Scy				sel = node;
1171254219Scy				break;
1172254219Scy			}
1173254219Scy			if (node->ipfd_states < connects) {
1174254219Scy				sel = node;
1175254219Scy				connects = node->ipfd_states;
1176254219Scy			}
1177254219Scy			node = node->ipfd_next;
1178254219Scy			if (node == NULL)
1179254219Scy				node = *d->ipld_dests;
1180254219Scy		}
1181254219Scy		break;
1182254219Scy
1183254219Scy	case IPLDP_RANDOM :
1184254219Scy		x = ipf_random() % d->ipld_nodes;
1185254219Scy		sel = d->ipld_dests[x];
1186254219Scy		break;
1187254219Scy
1188254219Scy	case IPLDP_HASHED :
1189254219Scy		MD5Init(&ctx);
1190254219Scy		MD5Update(&ctx, (u_char *)&d->ipld_seed, sizeof(d->ipld_seed));
1191254219Scy		MD5Update(&ctx, (u_char *)&fin->fin_src6,
1192254219Scy			  sizeof(fin->fin_src6));
1193254219Scy		MD5Update(&ctx, (u_char *)&fin->fin_dst6,
1194254219Scy			  sizeof(fin->fin_dst6));
1195254219Scy		MD5Final((u_char *)hash, &ctx);
1196254219Scy		x = hash[0] % d->ipld_nodes;
1197254219Scy		sel = d->ipld_dests[x];
1198254219Scy		break;
1199254219Scy
1200254219Scy	case IPLDP_SRCHASH :
1201254219Scy		MD5Init(&ctx);
1202254219Scy		MD5Update(&ctx, (u_char *)&d->ipld_seed, sizeof(d->ipld_seed));
1203254219Scy		MD5Update(&ctx, (u_char *)&fin->fin_src6,
1204254219Scy			  sizeof(fin->fin_src6));
1205254219Scy		MD5Final((u_char *)hash, &ctx);
1206254219Scy		x = hash[0] % d->ipld_nodes;
1207254219Scy		sel = d->ipld_dests[x];
1208254219Scy		break;
1209254219Scy
1210254219Scy	case IPLDP_DSTHASH :
1211254219Scy		MD5Init(&ctx);
1212254219Scy		MD5Update(&ctx, (u_char *)&d->ipld_seed, sizeof(d->ipld_seed));
1213254219Scy		MD5Update(&ctx, (u_char *)&fin->fin_dst6,
1214254219Scy			  sizeof(fin->fin_dst6));
1215254219Scy		MD5Final((u_char *)hash, &ctx);
1216254219Scy		x = hash[0] % d->ipld_nodes;
1217254219Scy		sel = d->ipld_dests[x];
1218254219Scy		break;
1219254219Scy
1220254219Scy	default :
1221254219Scy		sel = NULL;
1222254219Scy		break;
1223254219Scy	}
1224254219Scy
1225254219Scy	if (sel->ipfd_dest.fd_addr.adf_family != family)
1226254219Scy		sel = NULL;
1227254219Scy	d->ipld_selected = sel;
1228254219Scy
1229254219Scy	MUTEX_EXIT(&d->ipld_lock);
1230254219Scy
1231254219Scy	return sel;
1232254219Scy}
1233254219Scy
1234254219Scy
1235254219Scy/* ------------------------------------------------------------------------ */
1236254219Scy/* Function:    ipf_dstlist_select_node                                     */
1237254219Scy/* Returns:     int      - -1 == failure, 0 == success                      */
1238254219Scy/* Parameters:  fin(I)   - pointer to packet information                    */
1239254219Scy/*              group(I) - destination pool to search                       */
1240254219Scy/*              addr(I)  - pointer to store selected address                */
1241254219Scy/*              pfdp(O)  - pointer to storage for selected destination node */
1242254219Scy/*                                                                          */
1243254219Scy/* This function is only responsible for obtaining the next IP address for  */
1244254219Scy/* use and storing it in the caller's address space (addr). "addr" is only  */
1245254219Scy/* used for storage if pfdp is NULL. No permanent reference is currently    */
1246254219Scy/* kept on the node.                                                        */
1247254219Scy/* ------------------------------------------------------------------------ */
1248254219Scyint
1249254219Scyipf_dstlist_select_node(fin, group, addr, pfdp)
1250254219Scy	fr_info_t *fin;
1251254219Scy	void *group;
1252254219Scy	u_32_t *addr;
1253254219Scy	frdest_t *pfdp;
1254254219Scy{
1255254219Scy#ifdef USE_MUTEXES
1256254219Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
1257254219Scy#endif
1258254219Scy	ippool_dst_t *d = group;
1259254219Scy	ipf_dstnode_t *node;
1260254219Scy	frdest_t *fdp;
1261254219Scy
1262254219Scy	READ_ENTER(&softc->ipf_poolrw);
1263254219Scy
1264254219Scy	node = ipf_dstlist_select(fin, d);
1265254219Scy	if (node == NULL) {
1266254219Scy		RWLOCK_EXIT(&softc->ipf_poolrw);
1267254219Scy		return -1;
1268254219Scy	}
1269254219Scy
1270254219Scy	if (pfdp != NULL) {
1271254219Scy		bcopy(&node->ipfd_dest, pfdp, sizeof(*pfdp));
1272254219Scy	} else {
1273254219Scy		if (fin->fin_family == AF_INET) {
1274254219Scy			addr[0] = node->ipfd_dest.fd_addr.adf_addr.i6[0];
1275254219Scy		} else if (fin->fin_family == AF_INET6) {
1276254219Scy			addr[0] = node->ipfd_dest.fd_addr.adf_addr.i6[0];
1277254219Scy			addr[1] = node->ipfd_dest.fd_addr.adf_addr.i6[1];
1278254219Scy			addr[2] = node->ipfd_dest.fd_addr.adf_addr.i6[2];
1279254219Scy			addr[3] = node->ipfd_dest.fd_addr.adf_addr.i6[3];
1280254219Scy		}
1281254219Scy	}
1282254219Scy
1283254219Scy	fdp = &node->ipfd_dest;
1284254219Scy	if (fdp->fd_ptr == NULL)
1285254219Scy		fdp->fd_ptr = fin->fin_ifp;
1286254219Scy
1287254219Scy	MUTEX_ENTER(&node->ipfd_lock);
1288254219Scy	node->ipfd_states++;
1289254219Scy	MUTEX_EXIT(&node->ipfd_lock);
1290254219Scy
1291254219Scy	RWLOCK_EXIT(&softc->ipf_poolrw);
1292254219Scy
1293254219Scy	return 0;
1294254219Scy}
1295254219Scy
1296254219Scy
1297254219Scy/* ------------------------------------------------------------------------ */
1298254219Scy/* Function:    ipf_dstlist_expire                                          */
1299254219Scy/* Returns:     Nil                                                         */
1300254219Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
1301254219Scy/*              arg(I)   - pointer to local context to use                  */
1302254219Scy/*                                                                          */
1303254219Scy/* There are currently no objects to expire in destination lists.           */
1304254219Scy/* ------------------------------------------------------------------------ */
1305254219Scystatic void
1306254219Scyipf_dstlist_expire(softc, arg)
1307254219Scy	ipf_main_softc_t *softc;
1308254219Scy	void *arg;
1309254219Scy{
1310254219Scy	return;
1311254219Scy}
1312254219Scy
1313254219Scy
1314254219Scy/* ------------------------------------------------------------------------ */
1315254219Scy/* Function:    ipf_dstlist_sync                                            */
1316254219Scy/* Returns:     Nil                                                         */
1317254219Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
1318254219Scy/*              arg(I)   - pointer to local context to use                  */
1319254219Scy/*                                                                          */
1320254219Scy/* When a network interface appears or disappears, we need to revalidate    */
1321254219Scy/* all of the network interface names that have been configured as a target */
1322254219Scy/* in a destination list.                                                   */
1323254219Scy/* ------------------------------------------------------------------------ */
1324254219Scyvoid
1325254219Scyipf_dstlist_sync(softc, arg)
1326254219Scy	ipf_main_softc_t *softc;
1327254219Scy	void *arg;
1328254219Scy{
1329254219Scy	ipf_dstl_softc_t *softd = arg;
1330254219Scy	ipf_dstnode_t *node;
1331254219Scy	ippool_dst_t *list;
1332254219Scy	int i;
1333254219Scy	int j;
1334254219Scy
1335254219Scy	for (i = 0; i < IPL_LOGMAX; i++) {
1336254219Scy		for (list = softd->dstlist[i]; list != NULL;
1337254219Scy		     list = list->ipld_next) {
1338254219Scy			for (j = 0; j < list->ipld_maxnodes; j++) {
1339254219Scy				node = list->ipld_dests[j];
1340254219Scy				if (node == NULL)
1341254219Scy					continue;
1342254219Scy				if (node->ipfd_dest.fd_name == -1)
1343254219Scy					continue;
1344254219Scy				(void) ipf_resolvedest(softc,
1345254219Scy						       node->ipfd_names,
1346254219Scy						       &node->ipfd_dest,
1347254219Scy						       AF_INET);
1348254219Scy			}
1349254219Scy		}
1350254219Scy	}
1351254219Scy}
1352