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