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