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