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