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