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