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