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