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