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