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) 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 return EFAULT; 686 } 687 688 MUTEX_ENTER(&d->ipld_lock); 689 for (node = *d->ipld_dests; node != NULL; node = node->ipfd_next) { 690 if ((uid != 0) && (node->ipfd_uid != uid)) 691 continue; 692 if (node->ipfd_size != size) 693 continue; 694 if (!bcmp(&node->ipfd_dest.fd_ip6, &frd.fd_ip6, 695 size - offsetof(frdest_t, fd_ip6))) { 696 ipf_dstlist_node_free(softd, d, node); 697 MUTEX_EXIT(&d->ipld_lock); 698 KFREES(temp, size); 699 return 0; 700 } 701 } 702 MUTEX_EXIT(&d->ipld_lock); 703 KFREES(temp, size); 704 705 return ESRCH; 706} 707 708 709/* ------------------------------------------------------------------------ */ 710/* Function: ipf_dstlist_node_free */ 711/* Returns: Nil */ 712/* Parameters: softd(I) - pointer to the destination list context */ 713/* d(I) - pointer to destination list */ 714/* node(I) - pointer to node to free */ 715/* Locks: MUTEX(ipld_lock) or WRITE(ipf_poolrw) */ 716/* */ 717/* Free the destination node by first removing it from any lists and then */ 718/* checking if this was the last reference held to the object. While the */ 719/* array of pointers to nodes is compacted, its size isn't reduced (by way */ 720/* of allocating a new smaller one and copying) because the belief is that */ 721/* it is likely the array will again reach that size. */ 722/* ------------------------------------------------------------------------ */ 723static void 724ipf_dstlist_node_free(softd, d, node) 725 ipf_dstl_softc_t *softd; 726 ippool_dst_t *d; 727 ipf_dstnode_t *node; 728{ 729 int i; 730 731 /* 732 * Compact the array of pointers to nodes. 733 */ 734 for (i = 0; i < d->ipld_nodes; i++) 735 if (d->ipld_dests[i] == node) 736 break; 737 if (d->ipld_nodes - i > 1) { 738 bcopy(&d->ipld_dests[i + 1], &d->ipld_dests[i], 739 sizeof(*d->ipld_dests) * (d->ipld_nodes - i - 1)); 740 } 741 d->ipld_nodes--; 742 743 if (node->ipfd_pnext != NULL) 744 *node->ipfd_pnext = node->ipfd_next; 745 if (node->ipfd_next != NULL) 746 node->ipfd_next->ipfd_pnext = node->ipfd_pnext; 747 node->ipfd_pnext = NULL; 748 node->ipfd_next = NULL; 749 750 if ((node->ipfd_flags & IPDST_DELETE) == 0) { 751 softd->stats.ipls_numderefnodes++; 752 node->ipfd_flags |= IPDST_DELETE; 753 } 754 755 ipf_dstlist_node_deref(softd, node); 756} 757 758 759/* ------------------------------------------------------------------------ */ 760/* Function: ipf_dstlist_stats_get */ 761/* Returns: int - 0 = success, else error */ 762/* Parameters: softc(I) - pointer to soft context main structure */ 763/* arg(I) - pointer to local context to use */ 764/* op(I) - pointer to lookup operation data */ 765/* */ 766/* Return the current statistics for destination lists. This may be for all */ 767/* of them or just information pertaining to a particular table. */ 768/* ------------------------------------------------------------------------ */ 769/*ARGSUSED*/ 770static int 771ipf_dstlist_stats_get(softc, arg, op) 772 ipf_main_softc_t *softc; 773 void *arg; 774 iplookupop_t *op; 775{ 776 ipf_dstl_softc_t *softd = arg; 777 ipf_dstl_stat_t stats; 778 int unit, i, err = 0; 779 780 if (op->iplo_size != sizeof(ipf_dstl_stat_t)) { 781 IPFERROR(120023); 782 return EINVAL; 783 } 784 785 stats = softd->stats; 786 unit = op->iplo_unit; 787 if (unit == IPL_LOGALL) { 788 for (i = 0; i <= IPL_LOGMAX; i++) 789 stats.ipls_list[i] = softd->dstlist[i]; 790 } else if (unit >= 0 && unit <= IPL_LOGMAX) { 791 void *ptr; 792 793 if (op->iplo_name[0] != '\0') 794 ptr = ipf_dstlist_table_find(softd, unit, 795 op->iplo_name); 796 else 797 ptr = softd->dstlist[unit + 1]; 798 stats.ipls_list[unit] = ptr; 799 } else { 800 IPFERROR(120024); 801 err = EINVAL; 802 } 803 804 if (err == 0) { 805 err = COPYOUT(&stats, op->iplo_struct, sizeof(stats)); 806 if (err != 0) { 807 IPFERROR(120025); 808 return EFAULT; 809 } 810 } 811 return 0; 812} 813 814 815/* ------------------------------------------------------------------------ */ 816/* Function: ipf_dstlist_table_add */ 817/* Returns: int - 0 = success, else error */ 818/* Parameters: softc(I) - pointer to soft context main structure */ 819/* arg(I) - pointer to local context to use */ 820/* op(I) - pointer to lookup operation data */ 821/* */ 822/* Add a new destination table to the list of those available for the given */ 823/* device. Because we seldom operate on these objects (find/add/delete), */ 824/* they are just kept in a simple linked list. */ 825/* ------------------------------------------------------------------------ */ 826static int 827ipf_dstlist_table_add(softc, arg, op) 828 ipf_main_softc_t *softc; 829 void *arg; 830 iplookupop_t *op; 831{ 832 ipf_dstl_softc_t *softd = arg; 833 ippool_dst_t user, *d, *new; 834 int unit, err; 835 836 d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name); 837 if (d != NULL) { 838 IPFERROR(120013); 839 return EEXIST; 840 } 841 842 err = COPYIN(op->iplo_struct, &user, sizeof(user)); 843 if (err != 0) { 844 IPFERROR(120021); 845 return EFAULT; 846 } 847 848 KMALLOC(new, ippool_dst_t *); 849 if (new == NULL) { 850 softd->stats.ipls_nomem++; 851 IPFERROR(120014); 852 return ENOMEM; 853 } 854 bzero((char *)new, sizeof(*new)); 855 856 MUTEX_INIT(&new->ipld_lock, "ipf dst table lock"); 857 858 strncpy(new->ipld_name, op->iplo_name, FR_GROUPLEN); 859 unit = op->iplo_unit; 860 new->ipld_unit = unit; 861 new->ipld_policy = user.ipld_policy; 862 new->ipld_seed = ipf_random(); 863 new->ipld_ref = 1; 864 865 new->ipld_pnext = softd->tails[unit + 1]; 866 *softd->tails[unit + 1] = new; 867 softd->tails[unit + 1] = &new->ipld_next; 868 softd->stats.ipls_numlists++; 869 870 return 0; 871} 872 873 874/* ------------------------------------------------------------------------ */ 875/* Function: ipf_dstlist_table_del */ 876/* Returns: int - 0 = success, else error */ 877/* Parameters: softc(I) - pointer to soft context main structure */ 878/* arg(I) - pointer to local context to use */ 879/* op(I) - pointer to lookup operation data */ 880/* */ 881/* Find a named destinstion list table and delete it. If there are other */ 882/* references to it, the caller isn't told. */ 883/* ------------------------------------------------------------------------ */ 884static int 885ipf_dstlist_table_del(softc, arg, op) 886 ipf_main_softc_t *softc; 887 void *arg; 888 iplookupop_t *op; 889{ 890 ippool_dst_t *d; 891 892 d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name); 893 if (d == NULL) { 894 IPFERROR(120015); 895 return ESRCH; 896 } 897 898 if (d->ipld_dests != NULL) { 899 IPFERROR(120016); 900 return EBUSY; 901 } 902 903 ipf_dstlist_table_remove(softc, arg, d); 904 905 return 0; 906} 907 908 909/* ------------------------------------------------------------------------ */ 910/* Function: ipf_dstlist_table_remove */ 911/* Returns: Nil */ 912/* Parameters: softc(I) - pointer to soft context main structure */ 913/* softd(I) - pointer to the destination list context */ 914/* d(I) - pointer to destination list */ 915/* */ 916/* Remove a given destination list from existance. While the IPDST_DELETE */ 917/* flag is set every time we call this function and the reference count is */ 918/* non-zero, the "numdereflists" counter is always incremented because the */ 919/* decision about whether it will be freed or not is not made here. This */ 920/* means that the only action the code can take here is to treat it as if */ 921/* it will become a detached. */ 922/* ------------------------------------------------------------------------ */ 923static void 924ipf_dstlist_table_remove(softc, softd, d) 925 ipf_main_softc_t *softc; 926 ipf_dstl_softc_t *softd; 927 ippool_dst_t *d; 928{ 929 930 if (softd->tails[d->ipld_unit + 1] == &d->ipld_next) 931 softd->tails[d->ipld_unit + 1] = d->ipld_pnext; 932 933 if (d->ipld_pnext != NULL) 934 *d->ipld_pnext = d->ipld_next; 935 if (d->ipld_next != NULL) 936 d->ipld_next->ipld_pnext = d->ipld_pnext; 937 d->ipld_pnext = NULL; 938 d->ipld_next = NULL; 939 940 ipf_dstlist_table_clearnodes(softd, d); 941 942 softd->stats.ipls_numdereflists++; 943 d->ipld_flags |= IPDST_DELETE; 944 945 ipf_dstlist_table_deref(softc, softd, d); 946} 947 948 949/* ------------------------------------------------------------------------ */ 950/* Function: ipf_dstlist_table_free */ 951/* Returns: Nil */ 952/* Parameters: softd(I) - pointer to the destination list context */ 953/* d(I) - pointer to destination list */ 954/* */ 955/* Free up a destination list data structure and any other memory that was */ 956/* directly allocated as part of creating it. Individual destination list */ 957/* nodes are not freed. It is assumed the caller will have already emptied */ 958/* the destination list. */ 959/* ------------------------------------------------------------------------ */ 960static void 961ipf_dstlist_table_free(softd, d) 962 ipf_dstl_softc_t *softd; 963 ippool_dst_t *d; 964{ 965 MUTEX_DESTROY(&d->ipld_lock); 966 967 if ((d->ipld_flags & IPDST_DELETE) != 0) 968 softd->stats.ipls_numdereflists--; 969 softd->stats.ipls_numlists--; 970 971 if (d->ipld_dests != NULL) { 972 KFREES(d->ipld_dests, 973 d->ipld_maxnodes * sizeof(*d->ipld_dests)); 974 } 975 976 KFREE(d); 977} 978 979 980/* ------------------------------------------------------------------------ */ 981/* Function: ipf_dstlist_table_deref */ 982/* Returns: int - 0 = success, else error */ 983/* Parameters: softc(I) - pointer to soft context main structure */ 984/* arg(I) - pointer to local context to use */ 985/* op(I) - pointer to lookup operation data */ 986/* */ 987/* Drops the reference count on a destination list table object and free's */ 988/* it if 0 has been reached. */ 989/* ------------------------------------------------------------------------ */ 990static int 991ipf_dstlist_table_deref(softc, arg, table) 992 ipf_main_softc_t *softc; 993 void *arg; 994 void *table; 995{ 996 ippool_dst_t *d = table; 997 998 d->ipld_ref--; 999 if (d->ipld_ref > 0) 1000 return d->ipld_ref; 1001 1002 ipf_dstlist_table_free(arg, d); 1003 1004 return 0; 1005} 1006 1007 1008/* ------------------------------------------------------------------------ */ 1009/* Function: ipf_dstlist_table_clearnodes */ 1010/* Returns: Nil */ 1011/* Parameters: softd(I) - pointer to the destination list context */ 1012/* dst(I) - pointer to destination list */ 1013/* */ 1014/* Free all of the destination nodes attached to the given table. */ 1015/* ------------------------------------------------------------------------ */ 1016static void 1017ipf_dstlist_table_clearnodes(softd, dst) 1018 ipf_dstl_softc_t *softd; 1019 ippool_dst_t *dst; 1020{ 1021 ipf_dstnode_t *node; 1022 1023 if (dst->ipld_dests == NULL) 1024 return; 1025 1026 while ((node = *dst->ipld_dests) != NULL) { 1027 ipf_dstlist_node_free(softd, dst, node); 1028 } 1029} 1030 1031 1032/* ------------------------------------------------------------------------ */ 1033/* Function: ipf_dstlist_table_find */ 1034/* Returns: int - 0 = success, else error */ 1035/* Parameters: arg(I) - pointer to local context to use */ 1036/* unit(I) - device we are working with */ 1037/* name(I) - destination table name to find */ 1038/* */ 1039/* Return a pointer to a destination table that matches the unit+name that */ 1040/* is passed in. */ 1041/* ------------------------------------------------------------------------ */ 1042static void * 1043ipf_dstlist_table_find(arg, unit, name) 1044 void *arg; 1045 int unit; 1046 char *name; 1047{ 1048 ipf_dstl_softc_t *softd = arg; 1049 ippool_dst_t *d; 1050 1051 for (d = softd->dstlist[unit + 1]; d != NULL; d = d->ipld_next) { 1052 if ((d->ipld_unit == unit) && 1053 !strncmp(d->ipld_name, name, FR_GROUPLEN)) { 1054 return d; 1055 } 1056 } 1057 1058 return NULL; 1059} 1060 1061 1062/* ------------------------------------------------------------------------ */ 1063/* Function: ipf_dstlist_select_ref */ 1064/* Returns: void * - NULL = failure, else pointer to table */ 1065/* Parameters: arg(I) - pointer to local context to use */ 1066/* unit(I) - device we are working with */ 1067/* name(I) - destination table name to find */ 1068/* */ 1069/* Attempt to find a destination table that matches the name passed in and */ 1070/* if successful, bump up the reference count on it because we intend to */ 1071/* store the pointer to it somewhere else. */ 1072/* ------------------------------------------------------------------------ */ 1073static void * 1074ipf_dstlist_select_ref(arg, unit, name) 1075 void *arg; 1076 int unit; 1077 char *name; 1078{ 1079 ippool_dst_t *d; 1080 1081 d = ipf_dstlist_table_find(arg, unit, name); 1082 if (d != NULL) { 1083 MUTEX_ENTER(&d->ipld_lock); 1084 d->ipld_ref++; 1085 MUTEX_EXIT(&d->ipld_lock); 1086 } 1087 return d; 1088} 1089 1090 1091/* ------------------------------------------------------------------------ */ 1092/* Function: ipf_dstlist_select */ 1093/* Returns: void * - NULL = failure, else pointer to table */ 1094/* Parameters: fin(I) - pointer to packet information */ 1095/* d(I) - pointer to destination list */ 1096/* */ 1097/* Find the next node in the destination list to be used according to the */ 1098/* defined policy. Of these, "connection" is the most expensive policy to */ 1099/* implement as it always looks for the node with the least number of */ 1100/* connections associated with it. */ 1101/* */ 1102/* The hashes exclude the port numbers so that all protocols map to the */ 1103/* same destination. Otherwise, someone doing a ping would target a */ 1104/* different server than their TCP connection, etc. MD-5 is used to */ 1105/* transform the addressese into something random that the other end could */ 1106/* not easily guess and use in an attack. ipld_seed introduces an unknown */ 1107/* into the hash calculation to increase the difficult of an attacker */ 1108/* guessing the bucket. */ 1109/* */ 1110/* One final comment: mixing different address families in a single pool */ 1111/* will currently result in failures as the address family of the node is */ 1112/* only matched up with that in the packet as the last step. While this can */ 1113/* be coded around for the weighted connection and round-robin models, it */ 1114/* cannot be supported for the hash/random models as they do not search and */ 1115/* nor is the algorithm conducive to searching. */ 1116/* ------------------------------------------------------------------------ */ 1117static ipf_dstnode_t * 1118ipf_dstlist_select(fin, d) 1119 fr_info_t *fin; 1120 ippool_dst_t *d; 1121{ 1122 ipf_dstnode_t *node, *sel; 1123 int connects; 1124 u_32_t hash[4]; 1125 MD5_CTX ctx; 1126 int family; 1127 int x; 1128 1129 if (d->ipld_dests == NULL || *d->ipld_dests == NULL) 1130 return NULL; 1131 1132 family = fin->fin_family; 1133 1134 MUTEX_ENTER(&d->ipld_lock); 1135 1136 switch (d->ipld_policy) 1137 { 1138 case IPLDP_ROUNDROBIN: 1139 sel = d->ipld_selected; 1140 if (sel == NULL) { 1141 sel = *d->ipld_dests; 1142 } else { 1143 sel = sel->ipfd_next; 1144 if (sel == NULL) 1145 sel = *d->ipld_dests; 1146 } 1147 break; 1148 1149 case IPLDP_CONNECTION: 1150 if (d->ipld_selected == NULL) { 1151 sel = *d->ipld_dests; 1152 break; 1153 } 1154 1155 sel = d->ipld_selected; 1156 connects = 0x7fffffff; 1157 node = sel->ipfd_next; 1158 if (node == NULL) 1159 node = *d->ipld_dests; 1160 while (node != d->ipld_selected) { 1161 if (node->ipfd_states == 0) { 1162 sel = node; 1163 break; 1164 } 1165 if (node->ipfd_states < connects) { 1166 sel = node; 1167 connects = node->ipfd_states; 1168 } 1169 node = node->ipfd_next; 1170 if (node == NULL) 1171 node = *d->ipld_dests; 1172 } 1173 break; 1174 1175 case IPLDP_RANDOM : 1176 x = ipf_random() % d->ipld_nodes; 1177 sel = d->ipld_dests[x]; 1178 break; 1179 1180 case IPLDP_HASHED : 1181 MD5Init(&ctx); 1182 MD5Update(&ctx, (u_char *)&d->ipld_seed, sizeof(d->ipld_seed)); 1183 MD5Update(&ctx, (u_char *)&fin->fin_src6, 1184 sizeof(fin->fin_src6)); 1185 MD5Update(&ctx, (u_char *)&fin->fin_dst6, 1186 sizeof(fin->fin_dst6)); 1187 MD5Final((u_char *)hash, &ctx); 1188 x = hash[0] % d->ipld_nodes; 1189 sel = d->ipld_dests[x]; 1190 break; 1191 1192 case IPLDP_SRCHASH : 1193 MD5Init(&ctx); 1194 MD5Update(&ctx, (u_char *)&d->ipld_seed, sizeof(d->ipld_seed)); 1195 MD5Update(&ctx, (u_char *)&fin->fin_src6, 1196 sizeof(fin->fin_src6)); 1197 MD5Final((u_char *)hash, &ctx); 1198 x = hash[0] % d->ipld_nodes; 1199 sel = d->ipld_dests[x]; 1200 break; 1201 1202 case IPLDP_DSTHASH : 1203 MD5Init(&ctx); 1204 MD5Update(&ctx, (u_char *)&d->ipld_seed, sizeof(d->ipld_seed)); 1205 MD5Update(&ctx, (u_char *)&fin->fin_dst6, 1206 sizeof(fin->fin_dst6)); 1207 MD5Final((u_char *)hash, &ctx); 1208 x = hash[0] % d->ipld_nodes; 1209 sel = d->ipld_dests[x]; 1210 break; 1211 1212 default : 1213 sel = NULL; 1214 break; 1215 } 1216 1217 if (sel->ipfd_dest.fd_addr.adf_family != family) 1218 sel = NULL; 1219 d->ipld_selected = sel; 1220 1221 MUTEX_EXIT(&d->ipld_lock); 1222 1223 return sel; 1224} 1225 1226 1227/* ------------------------------------------------------------------------ */ 1228/* Function: ipf_dstlist_select_node */ 1229/* Returns: int - -1 == failure, 0 == success */ 1230/* Parameters: fin(I) - pointer to packet information */ 1231/* group(I) - destination pool to search */ 1232/* addr(I) - pointer to store selected address */ 1233/* pfdp(O) - pointer to storage for selected destination node */ 1234/* */ 1235/* This function is only responsible for obtaining the next IP address for */ 1236/* use and storing it in the caller's address space (addr). "addr" is only */ 1237/* used for storage if pfdp is NULL. No permanent reference is currently */ 1238/* kept on the node. */ 1239/* ------------------------------------------------------------------------ */ 1240int 1241ipf_dstlist_select_node(fin, group, addr, pfdp) 1242 fr_info_t *fin; 1243 void *group; 1244 u_32_t *addr; 1245 frdest_t *pfdp; 1246{ 1247#ifdef USE_MUTEXES 1248 ipf_main_softc_t *softc = fin->fin_main_soft; 1249#endif 1250 ippool_dst_t *d = group; 1251 ipf_dstnode_t *node; 1252 frdest_t *fdp; 1253 1254 READ_ENTER(&softc->ipf_poolrw); 1255 1256 node = ipf_dstlist_select(fin, d); 1257 if (node == NULL) { 1258 RWLOCK_EXIT(&softc->ipf_poolrw); 1259 return -1; 1260 } 1261 1262 if (pfdp != NULL) { 1263 bcopy(&node->ipfd_dest, pfdp, sizeof(*pfdp)); 1264 } else { 1265 if (fin->fin_family == AF_INET) { 1266 addr[0] = node->ipfd_dest.fd_addr.adf_addr.i6[0]; 1267 } else if (fin->fin_family == AF_INET6) { 1268 addr[0] = node->ipfd_dest.fd_addr.adf_addr.i6[0]; 1269 addr[1] = node->ipfd_dest.fd_addr.adf_addr.i6[1]; 1270 addr[2] = node->ipfd_dest.fd_addr.adf_addr.i6[2]; 1271 addr[3] = node->ipfd_dest.fd_addr.adf_addr.i6[3]; 1272 } 1273 } 1274 1275 fdp = &node->ipfd_dest; 1276 if (fdp->fd_ptr == NULL) 1277 fdp->fd_ptr = fin->fin_ifp; 1278 1279 MUTEX_ENTER(&node->ipfd_lock); 1280 node->ipfd_states++; 1281 MUTEX_EXIT(&node->ipfd_lock); 1282 1283 RWLOCK_EXIT(&softc->ipf_poolrw); 1284 1285 return 0; 1286} 1287 1288 1289/* ------------------------------------------------------------------------ */ 1290/* Function: ipf_dstlist_expire */ 1291/* Returns: Nil */ 1292/* Parameters: softc(I) - pointer to soft context main structure */ 1293/* arg(I) - pointer to local context to use */ 1294/* */ 1295/* There are currently no objects to expire in destination lists. */ 1296/* ------------------------------------------------------------------------ */ 1297static void 1298ipf_dstlist_expire(softc, arg) 1299 ipf_main_softc_t *softc; 1300 void *arg; 1301{ 1302 return; 1303} 1304 1305 1306/* ------------------------------------------------------------------------ */ 1307/* Function: ipf_dstlist_sync */ 1308/* Returns: Nil */ 1309/* Parameters: softc(I) - pointer to soft context main structure */ 1310/* arg(I) - pointer to local context to use */ 1311/* */ 1312/* When a network interface appears or disappears, we need to revalidate */ 1313/* all of the network interface names that have been configured as a target */ 1314/* in a destination list. */ 1315/* ------------------------------------------------------------------------ */ 1316void 1317ipf_dstlist_sync(softc, arg) 1318 ipf_main_softc_t *softc; 1319 void *arg; 1320{ 1321 ipf_dstl_softc_t *softd = arg; 1322 ipf_dstnode_t *node; 1323 ippool_dst_t *list; 1324 int i; 1325 int j; 1326 1327 for (i = 0; i < IPL_LOGMAX; i++) { 1328 for (list = softd->dstlist[i]; list != NULL; 1329 list = list->ipld_next) { 1330 for (j = 0; j < list->ipld_maxnodes; j++) { 1331 node = list->ipld_dests[j]; 1332 if (node == NULL) 1333 continue; 1334 if (node->ipfd_dest.fd_name == -1) 1335 continue; 1336 (void) ipf_resolvedest(softc, 1337 node->ipfd_names, 1338 &node->ipfd_dest, 1339 AF_INET); 1340 } 1341 } 1342 } 1343} 1344