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