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