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