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