1254562Scy/* 2254562Scy * Copyright (C) 2012 by Darren Reed. 3254562Scy * 4254562Scy * See the IPFILTER.LICENCE file for details on licencing. 5254562Scy */ 6254562Scy#if defined(KERNEL) || defined(_KERNEL) 7254562Scy# undef KERNEL 8254562Scy# undef _KERNEL 9254562Scy# define KERNEL 1 10254562Scy# define _KERNEL 1 11254562Scy#endif 12254562Scy#if defined(__osf__) 13254562Scy# define _PROTO_NET_H_ 14254562Scy#endif 15254562Scy#include <sys/errno.h> 16254562Scy#include <sys/types.h> 17254562Scy#include <sys/param.h> 18254562Scy#include <sys/file.h> 19254562Scy#if !defined(_KERNEL) && !defined(__KERNEL__) 20254562Scy# include <stdio.h> 21254562Scy# include <stdlib.h> 22254562Scy# include <string.h> 23254562Scy# define _KERNEL 24254562Scy# ifdef __OpenBSD__ 25254562Scystruct file; 26254562Scy# endif 27254562Scy# include <sys/uio.h> 28254562Scy# undef _KERNEL 29254562Scy#else 30254562Scy# include <sys/systm.h> 31254562Scy# if defined(NetBSD) && (__NetBSD_Version__ >= 104000000) 32254562Scy# include <sys/proc.h> 33254562Scy# endif 34254562Scy#endif 35254562Scy#include <sys/time.h> 36254562Scy#if !defined(linux) 37254562Scy# include <sys/protosw.h> 38254562Scy#endif 39254562Scy#include <sys/socket.h> 40254562Scy#if defined(_KERNEL) && (!defined(__SVR4) && !defined(__svr4__)) 41254562Scy# include <sys/mbuf.h> 42254562Scy#endif 43254562Scy#if defined(__SVR4) || defined(__svr4__) 44254562Scy# include <sys/filio.h> 45254562Scy# include <sys/byteorder.h> 46254562Scy# ifdef _KERNEL 47254562Scy# include <sys/dditypes.h> 48254562Scy# endif 49254562Scy# include <sys/stream.h> 50254562Scy# include <sys/kmem.h> 51254562Scy#endif 52254562Scy#if defined(__FreeBSD_version) && (__FreeBSD_version >= 300000) 53254562Scy# include <sys/malloc.h> 54254562Scy#endif 55254562Scy 56254562Scy#include <net/if.h> 57254562Scy#include <netinet/in.h> 58254562Scy 59254562Scy#include "netinet/ip_compat.h" 60254562Scy#include "netinet/ip_fil.h" 61254562Scy#include "netinet/ip_nat.h" 62254562Scy#include "netinet/ip_lookup.h" 63254562Scy#include "netinet/ip_dstlist.h" 64254562Scy 65254562Scy/* END OF INCLUDES */ 66254562Scy 67254562Scy#ifdef HAS_SYS_MD5_H 68254562Scy# include <sys/md5.h> 69254562Scy#else 70254562Scy# include "md5.h" 71254562Scy#endif 72254562Scy 73254562Scy#if !defined(lint) 74254562Scystatic const char rcsid[] = "@(#)$Id: ip_dstlist.c,v 1.13.2.12 2012/07/20 08:40:19 darren_r Exp $"; 75254562Scy#endif 76254562Scy 77254562Scytypedef struct ipf_dstl_softc_s { 78254562Scy ippool_dst_t *dstlist[LOOKUP_POOL_SZ]; 79254562Scy ippool_dst_t **tails[LOOKUP_POOL_SZ]; 80254562Scy ipf_dstl_stat_t stats; 81254562Scy} ipf_dstl_softc_t; 82254562Scy 83254562Scy 84254562Scystatic void *ipf_dstlist_soft_create __P((ipf_main_softc_t *)); 85254562Scystatic void ipf_dstlist_soft_destroy __P((ipf_main_softc_t *, void *)); 86254562Scystatic int ipf_dstlist_soft_init __P((ipf_main_softc_t *, void *)); 87254562Scystatic void ipf_dstlist_soft_fini __P((ipf_main_softc_t *, void *)); 88254562Scystatic int ipf_dstlist_addr_find __P((ipf_main_softc_t *, void *, int, 89254562Scy void *, u_int)); 90254562Scystatic size_t ipf_dstlist_flush __P((ipf_main_softc_t *, void *, 91254562Scy iplookupflush_t *)); 92254562Scystatic int ipf_dstlist_iter_deref __P((ipf_main_softc_t *, void *, int, int, 93254562Scy void *)); 94254562Scystatic int ipf_dstlist_iter_next __P((ipf_main_softc_t *, void *, ipftoken_t *, 95254562Scy ipflookupiter_t *)); 96254562Scystatic int ipf_dstlist_node_add __P((ipf_main_softc_t *, void *, 97254562Scy iplookupop_t *, int)); 98254562Scystatic int ipf_dstlist_node_del __P((ipf_main_softc_t *, void *, 99254562Scy iplookupop_t *, int)); 100254562Scystatic int ipf_dstlist_stats_get __P((ipf_main_softc_t *, void *, 101254562Scy iplookupop_t *)); 102254562Scystatic int ipf_dstlist_table_add __P((ipf_main_softc_t *, void *, 103254562Scy iplookupop_t *)); 104254562Scystatic int ipf_dstlist_table_del __P((ipf_main_softc_t *, void *, 105254562Scy iplookupop_t *)); 106254562Scystatic int ipf_dstlist_table_deref __P((ipf_main_softc_t *, void *, void *)); 107254562Scystatic void *ipf_dstlist_table_find __P((void *, int, char *)); 108254562Scystatic void ipf_dstlist_table_free __P((ipf_dstl_softc_t *, ippool_dst_t *)); 109254562Scystatic void ipf_dstlist_table_remove __P((ipf_main_softc_t *, 110254562Scy ipf_dstl_softc_t *, ippool_dst_t *)); 111254562Scystatic void ipf_dstlist_table_clearnodes __P((ipf_dstl_softc_t *, 112254562Scy ippool_dst_t *)); 113254562Scystatic ipf_dstnode_t *ipf_dstlist_select __P((fr_info_t *, ippool_dst_t *)); 114254562Scystatic void *ipf_dstlist_select_ref __P((void *, int, char *)); 115254562Scystatic void ipf_dstlist_node_free __P((ipf_dstl_softc_t *, ippool_dst_t *, ipf_dstnode_t *)); 116254562Scystatic int ipf_dstlist_node_deref __P((void *, ipf_dstnode_t *)); 117254562Scystatic void ipf_dstlist_expire __P((ipf_main_softc_t *, void *)); 118254562Scystatic void ipf_dstlist_sync __P((ipf_main_softc_t *, void *)); 119254562Scy 120254562Scyipf_lookup_t ipf_dstlist_backend = { 121254562Scy IPLT_DSTLIST, 122254562Scy ipf_dstlist_soft_create, 123254562Scy ipf_dstlist_soft_destroy, 124254562Scy ipf_dstlist_soft_init, 125254562Scy ipf_dstlist_soft_fini, 126254562Scy ipf_dstlist_addr_find, 127254562Scy ipf_dstlist_flush, 128254562Scy ipf_dstlist_iter_deref, 129254562Scy ipf_dstlist_iter_next, 130254562Scy ipf_dstlist_node_add, 131254562Scy ipf_dstlist_node_del, 132254562Scy ipf_dstlist_stats_get, 133254562Scy ipf_dstlist_table_add, 134254562Scy ipf_dstlist_table_del, 135254562Scy ipf_dstlist_table_deref, 136254562Scy ipf_dstlist_table_find, 137254562Scy ipf_dstlist_select_ref, 138254562Scy ipf_dstlist_select_node, 139254562Scy ipf_dstlist_expire, 140254562Scy ipf_dstlist_sync 141254562Scy}; 142254562Scy 143254562Scy 144254562Scy/* ------------------------------------------------------------------------ */ 145254562Scy/* Function: ipf_dstlist_soft_create */ 146254562Scy/* Returns: int - 0 = success, else error */ 147254562Scy/* Parameters: softc(I) - pointer to soft context main structure */ 148254562Scy/* */ 149254562Scy/* Allocating a chunk of memory filled with 0's is enough for the current */ 150254562Scy/* soft context used with destination lists. */ 151254562Scy/* ------------------------------------------------------------------------ */ 152254562Scystatic void * 153254562Scyipf_dstlist_soft_create(softc) 154254562Scy ipf_main_softc_t *softc; 155254562Scy{ 156254562Scy ipf_dstl_softc_t *softd; 157254562Scy int i; 158254562Scy 159254562Scy KMALLOC(softd, ipf_dstl_softc_t *); 160254562Scy if (softd == NULL) { 161254562Scy IPFERROR(120028); 162254562Scy return NULL; 163254562Scy } 164254562Scy 165254562Scy bzero((char *)softd, sizeof(*softd)); 166254562Scy for (i = 0; i <= IPL_LOGMAX; i++) 167254562Scy softd->tails[i] = &softd->dstlist[i]; 168254562Scy 169254562Scy return softd; 170254562Scy} 171254562Scy 172254562Scy 173254562Scy/* ------------------------------------------------------------------------ */ 174254562Scy/* Function: ipf_dstlist_soft_destroy */ 175254562Scy/* Returns: Nil */ 176254562Scy/* Parameters: softc(I) - pointer to soft context main structure */ 177254562Scy/* arg(I) - pointer to local context to use */ 178254562Scy/* */ 179254562Scy/* For destination lists, the only thing we have to do when destroying the */ 180254562Scy/* soft context is free it! */ 181254562Scy/* ------------------------------------------------------------------------ */ 182254562Scystatic void 183254562Scyipf_dstlist_soft_destroy(softc, arg) 184254562Scy ipf_main_softc_t *softc; 185254562Scy void *arg; 186254562Scy{ 187254562Scy ipf_dstl_softc_t *softd = arg; 188254562Scy 189254562Scy KFREE(softd); 190254562Scy} 191254562Scy 192254562Scy 193254562Scy/* ------------------------------------------------------------------------ */ 194254562Scy/* Function: ipf_dstlist_soft_init */ 195254562Scy/* Returns: int - 0 = success, else error */ 196254562Scy/* Parameters: softc(I) - pointer to soft context main structure */ 197254562Scy/* arg(I) - pointer to local context to use */ 198254562Scy/* */ 199254562Scy/* There is currently no soft context for destination list management. */ 200254562Scy/* ------------------------------------------------------------------------ */ 201254562Scystatic int 202254562Scyipf_dstlist_soft_init(softc, arg) 203254562Scy ipf_main_softc_t *softc; 204254562Scy void *arg; 205254562Scy{ 206254562Scy return 0; 207254562Scy} 208254562Scy 209254562Scy 210254562Scy/* ------------------------------------------------------------------------ */ 211254562Scy/* Function: ipf_dstlist_soft_fini */ 212254562Scy/* Returns: Nil */ 213254562Scy/* Parameters: softc(I) - pointer to soft context main structure */ 214254562Scy/* arg(I) - pointer to local context to use */ 215254562Scy/* */ 216254562Scy/* There is currently no soft context for destination list management. */ 217254562Scy/* ------------------------------------------------------------------------ */ 218254562Scystatic void 219254562Scyipf_dstlist_soft_fini(softc, arg) 220254562Scy ipf_main_softc_t *softc; 221254562Scy void *arg; 222254562Scy{ 223254562Scy ipf_dstl_softc_t *softd = arg; 224254562Scy int i; 225254562Scy 226254562Scy for (i = -1; i <= IPL_LOGMAX; i++) { 227254562Scy while (softd->dstlist[i + 1] != NULL) { 228254562Scy ipf_dstlist_table_remove(softc, softd, 229254562Scy softd->dstlist[i + 1]); 230254562Scy } 231254562Scy } 232254562Scy 233254562Scy ASSERT(softd->stats.ipls_numderefnodes == 0); 234254562Scy} 235254562Scy 236254562Scy 237254562Scy/* ------------------------------------------------------------------------ */ 238254562Scy/* Function: ipf_dstlist_addr_find */ 239254562Scy/* Returns: int - 0 = success, else error */ 240254562Scy/* Parameters: softc(I) - pointer to soft context main structure */ 241254562Scy/* arg1(I) - pointer to local context to use */ 242254562Scy/* arg2(I) - pointer to local context to use */ 243254562Scy/* arg3(I) - pointer to local context to use */ 244254562Scy/* arg4(I) - pointer to local context to use */ 245254562Scy/* */ 246254562Scy/* There is currently no such thing as searching a destination list for an */ 247254562Scy/* address so this function becomes a no-op. Its presence is required as */ 248254562Scy/* ipf_lookup_res_name() stores the "addr_find" function pointer in the */ 249254562Scy/* pointer passed in to it as funcptr, although it could be a generic null- */ 250254562Scy/* op function rather than a specific one. */ 251254562Scy/* ------------------------------------------------------------------------ */ 252254562Scy/*ARGSUSED*/ 253254562Scystatic int 254254562Scyipf_dstlist_addr_find(softc, arg1, arg2, arg3, arg4) 255254562Scy ipf_main_softc_t *softc; 256254562Scy void *arg1, *arg3; 257254562Scy int arg2; 258254562Scy u_int arg4; 259254562Scy{ 260254562Scy return -1; 261254562Scy} 262254562Scy 263254562Scy 264254562Scy/* ------------------------------------------------------------------------ */ 265254562Scy/* Function: ipf_dstlist_flush */ 266254562Scy/* Returns: int - number of objects deleted */ 267254562Scy/* Parameters: softc(I) - pointer to soft context main structure */ 268254562Scy/* arg(I) - pointer to local context to use */ 269254562Scy/* fop(I) - pointer to lookup flush operation data */ 270254562Scy/* */ 271254562Scy/* Flush all of the destination tables that match the data passed in with */ 272254562Scy/* the iplookupflush_t. There are two ways to match objects: the device for */ 273254562Scy/* which they are to be used with and their name. */ 274254562Scy/* ------------------------------------------------------------------------ */ 275254562Scystatic size_t 276254562Scyipf_dstlist_flush(softc, arg, fop) 277254562Scy ipf_main_softc_t *softc; 278254562Scy void *arg; 279254562Scy iplookupflush_t *fop; 280254562Scy{ 281254562Scy ipf_dstl_softc_t *softd = arg; 282254562Scy ippool_dst_t *node, *next; 283254562Scy int n, i; 284254562Scy 285254562Scy for (n = 0, i = -1; i <= IPL_LOGMAX; i++) { 286254562Scy if (fop->iplf_unit != IPLT_ALL && fop->iplf_unit != i) 287254562Scy continue; 288254562Scy for (node = softd->dstlist[i + 1]; node != NULL; node = next) { 289254562Scy next = node->ipld_next; 290254562Scy 291254562Scy if ((*fop->iplf_name != '\0') && 292254562Scy strncmp(fop->iplf_name, node->ipld_name, 293254562Scy FR_GROUPLEN)) 294254562Scy continue; 295254562Scy 296254562Scy ipf_dstlist_table_remove(softc, softd, node); 297254562Scy n++; 298254562Scy } 299254562Scy } 300254562Scy return n; 301254562Scy} 302254562Scy 303254562Scy 304254562Scy/* ------------------------------------------------------------------------ */ 305254562Scy/* Function: ipf_dstlist_iter_deref */ 306254562Scy/* Returns: int - 0 = success, else error */ 307254562Scy/* Parameters: softc(I) - pointer to soft context main structure */ 308254562Scy/* arg(I) - pointer to local context to use */ 309254562Scy/* otype(I) - type of data structure to iterate through */ 310254562Scy/* unit(I) - device we are working with */ 311254562Scy/* data(I) - address of object in kernel space */ 312254562Scy/* */ 313254562Scy/* This function is called when the iteration token is being free'd and is */ 314254562Scy/* responsible for dropping the reference count of the structure it points */ 315254562Scy/* to. */ 316254562Scy/* ------------------------------------------------------------------------ */ 317254562Scystatic int 318254562Scyipf_dstlist_iter_deref(softc, arg, otype, unit, data) 319254562Scy ipf_main_softc_t *softc; 320254562Scy void *arg; 321254562Scy int otype, unit; 322254562Scy void *data; 323254562Scy{ 324254562Scy if (data == NULL) { 325254562Scy IPFERROR(120001); 326254562Scy return EINVAL; 327254562Scy } 328254562Scy 329254562Scy if (unit < -1 || unit > IPL_LOGMAX) { 330254562Scy IPFERROR(120002); 331254562Scy return EINVAL; 332254562Scy } 333254562Scy 334254562Scy switch (otype) 335254562Scy { 336254562Scy case IPFLOOKUPITER_LIST : 337254562Scy ipf_dstlist_table_deref(softc, arg, (ippool_dst_t *)data); 338254562Scy break; 339254562Scy 340254562Scy case IPFLOOKUPITER_NODE : 341254562Scy ipf_dstlist_node_deref(arg, (ipf_dstnode_t *)data); 342254562Scy break; 343254562Scy } 344254562Scy 345254562Scy return 0; 346254562Scy} 347254562Scy 348254562Scy 349254562Scy/* ------------------------------------------------------------------------ */ 350254562Scy/* Function: ipf_dstlist_iter_next */ 351254562Scy/* Returns: int - 0 = success, else error */ 352254562Scy/* Parameters: softc(I) - pointer to soft context main structure */ 353254562Scy/* arg(I) - pointer to local context to use */ 354254562Scy/* op(I) - pointer to lookup operation data */ 355254562Scy/* uid(I) - uid of process doing the ioctl */ 356254562Scy/* */ 357254562Scy/* This function is responsible for either selecting the next destination */ 358254562Scy/* list or node on a destination list to be returned as a user process */ 359254562Scy/* iterates through the list of destination lists or nodes. */ 360254562Scy/* ------------------------------------------------------------------------ */ 361254562Scystatic int 362254562Scyipf_dstlist_iter_next(softc, arg, token, iter) 363254562Scy ipf_main_softc_t *softc; 364254562Scy void *arg; 365254562Scy ipftoken_t *token; 366254562Scy ipflookupiter_t *iter; 367254562Scy{ 368254562Scy ipf_dstnode_t zn, *nextnode = NULL, *node = NULL; 369254562Scy ippool_dst_t zero, *next = NULL, *dsttab = NULL; 370254562Scy ipf_dstl_softc_t *softd = arg; 371254562Scy int err = 0; 372254562Scy void *hint; 373254562Scy 374254562Scy switch (iter->ili_otype) 375254562Scy { 376254562Scy case IPFLOOKUPITER_LIST : 377254562Scy dsttab = token->ipt_data; 378254562Scy if (dsttab == NULL) { 379254562Scy next = softd->dstlist[(int)iter->ili_unit + 1]; 380254562Scy } else { 381254562Scy next = dsttab->ipld_next; 382254562Scy } 383254562Scy 384254562Scy if (next != NULL) { 385254562Scy ATOMIC_INC32(next->ipld_ref); 386254562Scy token->ipt_data = next; 387254562Scy hint = next->ipld_next; 388254562Scy } else { 389254562Scy bzero((char *)&zero, sizeof(zero)); 390254562Scy next = &zero; 391254562Scy token->ipt_data = NULL; 392254562Scy hint = NULL; 393254562Scy } 394254562Scy break; 395254562Scy 396254562Scy case IPFLOOKUPITER_NODE : 397254562Scy node = token->ipt_data; 398254562Scy if (node == NULL) { 399254562Scy dsttab = ipf_dstlist_table_find(arg, iter->ili_unit, 400254562Scy iter->ili_name); 401254562Scy if (dsttab == NULL) { 402254562Scy IPFERROR(120004); 403254562Scy err = ESRCH; 404254562Scy nextnode = NULL; 405254562Scy } else { 406254562Scy if (dsttab->ipld_dests == NULL) 407254562Scy nextnode = NULL; 408254562Scy else 409254562Scy nextnode = *dsttab->ipld_dests; 410254562Scy dsttab = NULL; 411254562Scy } 412254562Scy } else { 413254562Scy nextnode = node->ipfd_next; 414254562Scy } 415254562Scy 416254562Scy if (nextnode != NULL) { 417254562Scy MUTEX_ENTER(&nextnode->ipfd_lock); 418254562Scy nextnode->ipfd_ref++; 419254562Scy MUTEX_EXIT(&nextnode->ipfd_lock); 420254562Scy token->ipt_data = nextnode; 421254562Scy hint = nextnode->ipfd_next; 422254562Scy } else { 423254562Scy bzero((char *)&zn, sizeof(zn)); 424254562Scy nextnode = &zn; 425254562Scy token->ipt_data = NULL; 426254562Scy hint = NULL; 427254562Scy } 428254562Scy break; 429254562Scy default : 430254562Scy IPFERROR(120003); 431254562Scy err = EINVAL; 432254562Scy break; 433254562Scy } 434254562Scy 435254562Scy if (err != 0) 436254562Scy return err; 437254562Scy 438254562Scy switch (iter->ili_otype) 439254562Scy { 440254562Scy case IPFLOOKUPITER_LIST : 441254562Scy if (dsttab != NULL) 442254562Scy ipf_dstlist_table_deref(softc, arg, dsttab); 443254562Scy err = COPYOUT(next, iter->ili_data, sizeof(*next)); 444254562Scy if (err != 0) { 445254562Scy IPFERROR(120005); 446254562Scy err = EFAULT; 447254562Scy } 448254562Scy break; 449254562Scy 450254562Scy case IPFLOOKUPITER_NODE : 451254562Scy if (node != NULL) 452254562Scy ipf_dstlist_node_deref(arg, node); 453254562Scy err = COPYOUT(nextnode, iter->ili_data, sizeof(*nextnode)); 454254562Scy if (err != 0) { 455254562Scy IPFERROR(120006); 456254562Scy err = EFAULT; 457254562Scy } 458254562Scy break; 459254562Scy } 460254562Scy 461254562Scy if (hint == NULL) 462254562Scy ipf_token_mark_complete(token); 463254562Scy 464254562Scy return err; 465254562Scy} 466254562Scy 467254562Scy 468254562Scy/* ------------------------------------------------------------------------ */ 469254562Scy/* Function: ipf_dstlist_node_add */ 470254562Scy/* Returns: int - 0 = success, else error */ 471254562Scy/* Parameters: softc(I) - pointer to soft context main structure */ 472254562Scy/* arg(I) - pointer to local context to use */ 473254562Scy/* op(I) - pointer to lookup operation data */ 474254562Scy/* uid(I) - uid of process doing the ioctl */ 475254562Scy/* Locks: WRITE(ipf_poolrw) */ 476254562Scy/* */ 477254562Scy/* Add a new node to a destination list. To do this, we only copy in the */ 478254562Scy/* frdest_t structure because that contains the only data required from the */ 479254562Scy/* application to create a new node. The frdest_t doesn't contain the name */ 480254562Scy/* itself. When loading filter rules, fd_name is a 'pointer' to the name. */ 481254562Scy/* In this case, the 'pointer' does not work, instead it is the length of */ 482254562Scy/* the name and the name is immediately following the frdest_t structure. */ 483254562Scy/* fd_name must include the trailing \0, so it should be strlen(str) + 1. */ 484254562Scy/* For simple sanity checking, an upper bound on the size of fd_name is */ 485254562Scy/* imposed - 128. */ 486254562Scy/* ------------------------------------------------------------------------ */ 487254562Scystatic int 488254562Scyipf_dstlist_node_add(softc, arg, op, uid) 489254562Scy ipf_main_softc_t *softc; 490254562Scy void *arg; 491254562Scy iplookupop_t *op; 492254562Scy int uid; 493254562Scy{ 494254562Scy ipf_dstl_softc_t *softd = arg; 495254562Scy ipf_dstnode_t *node, **nodes; 496254562Scy ippool_dst_t *d; 497254562Scy frdest_t dest; 498254562Scy int err; 499254562Scy 500254562Scy if (op->iplo_size < sizeof(frdest_t)) { 501254562Scy IPFERROR(120007); 502254562Scy return EINVAL; 503254562Scy } 504254562Scy 505254562Scy err = COPYIN(op->iplo_struct, &dest, sizeof(dest)); 506254562Scy if (err != 0) { 507254562Scy IPFERROR(120009); 508254562Scy return EFAULT; 509254562Scy } 510254562Scy 511254562Scy d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name); 512254562Scy if (d == NULL) { 513254562Scy IPFERROR(120010); 514254562Scy return ESRCH; 515254562Scy } 516254562Scy 517254562Scy switch (dest.fd_addr.adf_family) 518254562Scy { 519254562Scy case AF_INET : 520254562Scy case AF_INET6 : 521254562Scy break; 522254562Scy default : 523254562Scy IPFERROR(120019); 524254562Scy return EINVAL; 525254562Scy } 526254562Scy 527254562Scy if (dest.fd_name < -1 || dest.fd_name > 128) { 528254562Scy IPFERROR(120018); 529254562Scy return EINVAL; 530254562Scy } 531254562Scy 532254562Scy KMALLOCS(node, ipf_dstnode_t *, sizeof(*node) + dest.fd_name); 533254562Scy if (node == NULL) { 534254562Scy softd->stats.ipls_nomem++; 535254562Scy IPFERROR(120008); 536254562Scy return ENOMEM; 537254562Scy } 538254562Scy bzero((char *)node, sizeof(*node) + dest.fd_name); 539254562Scy 540254562Scy bcopy(&dest, &node->ipfd_dest, sizeof(dest)); 541254562Scy node->ipfd_size = sizeof(*node) + dest.fd_name; 542254562Scy 543254562Scy if (dest.fd_name > 0) { 544254562Scy /* 545254562Scy * fd_name starts out as the length of the string to copy 546254562Scy * in (including \0) and ends up being the offset from 547254562Scy * fd_names (0). 548254562Scy */ 549254562Scy err = COPYIN((char *)op->iplo_struct + sizeof(dest), 550254562Scy node->ipfd_names, dest.fd_name); 551254562Scy if (err != 0) { 552254562Scy IPFERROR(120017); 553254562Scy KFREES(node, node->ipfd_size); 554254562Scy return EFAULT; 555254562Scy } 556254562Scy node->ipfd_dest.fd_name = 0; 557254562Scy } else { 558254562Scy node->ipfd_dest.fd_name = -1; 559254562Scy } 560254562Scy 561254562Scy if (d->ipld_nodes == d->ipld_maxnodes) { 562254562Scy KMALLOCS(nodes, ipf_dstnode_t **, 563254562Scy sizeof(*nodes) * (d->ipld_maxnodes + 1)); 564254562Scy if (nodes == NULL) { 565254562Scy softd->stats.ipls_nomem++; 566254562Scy IPFERROR(120022); 567254562Scy KFREES(node, node->ipfd_size); 568254562Scy return ENOMEM; 569254562Scy } 570254562Scy if (d->ipld_dests != NULL) { 571254562Scy bcopy(d->ipld_dests, nodes, 572254562Scy sizeof(*nodes) * d->ipld_maxnodes); 573254562Scy KFREES(d->ipld_dests, sizeof(*nodes) * d->ipld_nodes); 574254562Scy nodes[0]->ipfd_pnext = nodes; 575254562Scy } 576254562Scy d->ipld_dests = nodes; 577254562Scy d->ipld_maxnodes++; 578254562Scy } 579254562Scy d->ipld_dests[d->ipld_nodes] = node; 580254562Scy d->ipld_nodes++; 581254562Scy 582254562Scy if (d->ipld_nodes == 1) { 583254562Scy node->ipfd_pnext = d->ipld_dests; 584254562Scy } else if (d->ipld_nodes > 1) { 585254562Scy node->ipfd_pnext = &d->ipld_dests[d->ipld_nodes - 2]->ipfd_next; 586254562Scy } 587254562Scy *node->ipfd_pnext = node; 588254562Scy 589254562Scy MUTEX_INIT(&node->ipfd_lock, "ipf dst node lock"); 590254562Scy node->ipfd_uid = uid; 591254562Scy node->ipfd_ref = 1; 592254562Scy if (node->ipfd_dest.fd_name == 0) 593254562Scy (void) ipf_resolvedest(softc, node->ipfd_names, 594254562Scy &node->ipfd_dest, AF_INET); 595254562Scy#ifdef USE_INET6 596254562Scy if (node->ipfd_dest.fd_name == 0 && 597254562Scy node->ipfd_dest.fd_ptr == (void *)-1) 598254562Scy (void) ipf_resolvedest(softc, node->ipfd_names, 599254562Scy &node->ipfd_dest, AF_INET6); 600254562Scy#endif 601254562Scy 602254562Scy softd->stats.ipls_numnodes++; 603254562Scy 604254562Scy return 0; 605254562Scy} 606254562Scy 607254562Scy 608254562Scy/* ------------------------------------------------------------------------ */ 609254562Scy/* Function: ipf_dstlist_node_deref */ 610254562Scy/* Returns: int - 0 = success, else error */ 611254562Scy/* Parameters: arg(I) - pointer to local context to use */ 612254562Scy/* node(I) - pointer to destionation node to free */ 613254562Scy/* */ 614254562Scy/* Dereference the use count by one. If it drops to zero then we can assume */ 615254562Scy/* that it has been removed from any lists/tables and is ripe for freeing. */ 616254562Scy/* The pointer to context is required for the purpose of maintaining */ 617254562Scy/* statistics. */ 618254562Scy/* ------------------------------------------------------------------------ */ 619254562Scystatic int 620254562Scyipf_dstlist_node_deref(arg, node) 621254562Scy void *arg; 622254562Scy ipf_dstnode_t *node; 623254562Scy{ 624254562Scy ipf_dstl_softc_t *softd = arg; 625254562Scy int ref; 626254562Scy 627254562Scy MUTEX_ENTER(&node->ipfd_lock); 628254562Scy ref = --node->ipfd_ref; 629254562Scy MUTEX_EXIT(&node->ipfd_lock); 630254562Scy 631254562Scy if (ref > 0) 632254562Scy return 0; 633254562Scy 634254562Scy if ((node->ipfd_flags & IPDST_DELETE) != 0) 635254562Scy softd->stats.ipls_numderefnodes--; 636254562Scy MUTEX_DESTROY(&node->ipfd_lock); 637254562Scy KFREES(node, node->ipfd_size); 638254562Scy softd->stats.ipls_numnodes--; 639254562Scy 640254562Scy return 0; 641254562Scy} 642254562Scy 643254562Scy 644254562Scy/* ------------------------------------------------------------------------ */ 645254562Scy/* Function: ipf_dstlist_node_del */ 646254562Scy/* Returns: int - 0 = success, else error */ 647254562Scy/* Parameters: softc(I) - pointer to soft context main structure */ 648254562Scy/* arg(I) - pointer to local context to use */ 649254562Scy/* op(I) - pointer to lookup operation data */ 650254562Scy/* uid(I) - uid of process doing the ioctl */ 651254562Scy/* */ 652254562Scy/* Look for a matching destination node on the named table and free it if */ 653254562Scy/* found. Because the name embedded in the frdest_t is variable in length, */ 654254562Scy/* it is necessary to allocate some memory locally, to complete this op. */ 655254562Scy/* ------------------------------------------------------------------------ */ 656254562Scystatic int 657254562Scyipf_dstlist_node_del(softc, arg, op, uid) 658254562Scy ipf_main_softc_t *softc; 659254562Scy void *arg; 660254562Scy iplookupop_t *op; 661254562Scy int uid; 662254562Scy{ 663254562Scy ipf_dstl_softc_t *softd = arg; 664254562Scy ipf_dstnode_t *node; 665254562Scy frdest_t frd, *temp; 666254562Scy ippool_dst_t *d; 667254562Scy size_t size; 668254562Scy int err; 669254562Scy 670254562Scy d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name); 671254562Scy if (d == NULL) { 672254562Scy IPFERROR(120012); 673254562Scy return ESRCH; 674254562Scy } 675254562Scy 676254562Scy err = COPYIN(op->iplo_struct, &frd, sizeof(frd)); 677254562Scy if (err != 0) { 678254562Scy IPFERROR(120011); 679254562Scy return EFAULT; 680254562Scy } 681254562Scy 682254562Scy size = sizeof(*temp) + frd.fd_name; 683254562Scy KMALLOCS(temp, frdest_t *, size); 684254562Scy if (temp == NULL) { 685254562Scy softd->stats.ipls_nomem++; 686254562Scy IPFERROR(120026); 687254562Scy return ENOMEM; 688254562Scy } 689254562Scy 690254562Scy err = COPYIN(op->iplo_struct, temp, size); 691254562Scy if (err != 0) { 692254562Scy IPFERROR(120027); 693254562Scy return EFAULT; 694254562Scy } 695254562Scy 696254562Scy MUTEX_ENTER(&d->ipld_lock); 697254562Scy for (node = *d->ipld_dests; node != NULL; node = node->ipfd_next) { 698254562Scy if ((uid != 0) && (node->ipfd_uid != uid)) 699254562Scy continue; 700254562Scy if (node->ipfd_size != size) 701254562Scy continue; 702254562Scy if (!bcmp(&node->ipfd_dest.fd_ip6, &frd.fd_ip6, 703254562Scy size - offsetof(frdest_t, fd_ip6))) { 704254562Scy ipf_dstlist_node_free(softd, d, node); 705254562Scy MUTEX_EXIT(&d->ipld_lock); 706254562Scy KFREES(temp, size); 707254562Scy return 0; 708254562Scy } 709254562Scy } 710254562Scy MUTEX_EXIT(&d->ipld_lock); 711254562Scy KFREES(temp, size); 712254562Scy 713254562Scy return ESRCH; 714254562Scy} 715254562Scy 716254562Scy 717254562Scy/* ------------------------------------------------------------------------ */ 718254562Scy/* Function: ipf_dstlist_node_free */ 719254562Scy/* Returns: Nil */ 720254562Scy/* Parameters: softd(I) - pointer to the destination list context */ 721254562Scy/* d(I) - pointer to destination list */ 722254562Scy/* node(I) - pointer to node to free */ 723254562Scy/* Locks: MUTEX(ipld_lock) or WRITE(ipf_poolrw) */ 724254562Scy/* */ 725254562Scy/* Free the destination node by first removing it from any lists and then */ 726254562Scy/* checking if this was the last reference held to the object. While the */ 727254562Scy/* array of pointers to nodes is compacted, its size isn't reduced (by way */ 728254562Scy/* of allocating a new smaller one and copying) because the belief is that */ 729254562Scy/* it is likely the array will again reach that size. */ 730254562Scy/* ------------------------------------------------------------------------ */ 731254562Scystatic void 732254562Scyipf_dstlist_node_free(softd, d, node) 733254562Scy ipf_dstl_softc_t *softd; 734254562Scy ippool_dst_t *d; 735254562Scy ipf_dstnode_t *node; 736254562Scy{ 737254562Scy int i; 738254562Scy 739254562Scy /* 740254562Scy * Compact the array of pointers to nodes. 741254562Scy */ 742254562Scy for (i = 0; i < d->ipld_nodes; i++) 743254562Scy if (d->ipld_dests[i] == node) 744254562Scy break; 745254562Scy if (d->ipld_nodes - i > 1) { 746254562Scy bcopy(&d->ipld_dests[i + 1], &d->ipld_dests[i], 747254562Scy sizeof(*d->ipld_dests) * (d->ipld_nodes - i - 1)); 748254562Scy } 749254562Scy d->ipld_nodes--; 750254562Scy 751254562Scy if (node->ipfd_pnext != NULL) 752254562Scy *node->ipfd_pnext = node->ipfd_next; 753254562Scy if (node->ipfd_next != NULL) 754254562Scy node->ipfd_next->ipfd_pnext = node->ipfd_pnext; 755254562Scy node->ipfd_pnext = NULL; 756254562Scy node->ipfd_next = NULL; 757254562Scy 758254562Scy if ((node->ipfd_flags & IPDST_DELETE) == 0) { 759254562Scy softd->stats.ipls_numderefnodes++; 760254562Scy node->ipfd_flags |= IPDST_DELETE; 761254562Scy } 762254562Scy 763254562Scy ipf_dstlist_node_deref(softd, node); 764254562Scy} 765254562Scy 766254562Scy 767254562Scy/* ------------------------------------------------------------------------ */ 768254562Scy/* Function: ipf_dstlist_stats_get */ 769254562Scy/* Returns: int - 0 = success, else error */ 770254562Scy/* Parameters: softc(I) - pointer to soft context main structure */ 771254562Scy/* arg(I) - pointer to local context to use */ 772254562Scy/* op(I) - pointer to lookup operation data */ 773254562Scy/* */ 774254562Scy/* Return the current statistics for destination lists. This may be for all */ 775254562Scy/* of them or just information pertaining to a particular table. */ 776254562Scy/* ------------------------------------------------------------------------ */ 777254562Scy/*ARGSUSED*/ 778254562Scystatic int 779254562Scyipf_dstlist_stats_get(softc, arg, op) 780254562Scy ipf_main_softc_t *softc; 781254562Scy void *arg; 782254562Scy iplookupop_t *op; 783254562Scy{ 784254562Scy ipf_dstl_softc_t *softd = arg; 785254562Scy ipf_dstl_stat_t stats; 786254562Scy int unit, i, err = 0; 787254562Scy 788254562Scy if (op->iplo_size != sizeof(ipf_dstl_stat_t)) { 789254562Scy IPFERROR(120023); 790254562Scy return EINVAL; 791254562Scy } 792254562Scy 793254562Scy stats = softd->stats; 794254562Scy unit = op->iplo_unit; 795254562Scy if (unit == IPL_LOGALL) { 796254562Scy for (i = 0; i <= IPL_LOGMAX; i++) 797254562Scy stats.ipls_list[i] = softd->dstlist[i]; 798254562Scy } else if (unit >= 0 && unit <= IPL_LOGMAX) { 799254562Scy void *ptr; 800254562Scy 801254562Scy if (op->iplo_name[0] != '\0') 802254562Scy ptr = ipf_dstlist_table_find(softd, unit, 803254562Scy op->iplo_name); 804254562Scy else 805254562Scy ptr = softd->dstlist[unit + 1]; 806254562Scy stats.ipls_list[unit] = ptr; 807254562Scy } else { 808254562Scy IPFERROR(120024); 809254562Scy err = EINVAL; 810254562Scy } 811254562Scy 812254562Scy if (err == 0) { 813254562Scy err = COPYOUT(&stats, op->iplo_struct, sizeof(stats)); 814254562Scy if (err != 0) { 815254562Scy IPFERROR(120025); 816254562Scy return EFAULT; 817254562Scy } 818254562Scy } 819254562Scy return 0; 820254562Scy} 821254562Scy 822254562Scy 823254562Scy/* ------------------------------------------------------------------------ */ 824254562Scy/* Function: ipf_dstlist_table_add */ 825254562Scy/* Returns: int - 0 = success, else error */ 826254562Scy/* Parameters: softc(I) - pointer to soft context main structure */ 827254562Scy/* arg(I) - pointer to local context to use */ 828254562Scy/* op(I) - pointer to lookup operation data */ 829254562Scy/* */ 830254562Scy/* Add a new destination table to the list of those available for the given */ 831254562Scy/* device. Because we seldom operate on these objects (find/add/delete), */ 832254562Scy/* they are just kept in a simple linked list. */ 833254562Scy/* ------------------------------------------------------------------------ */ 834254562Scystatic int 835254562Scyipf_dstlist_table_add(softc, arg, op) 836254562Scy ipf_main_softc_t *softc; 837254562Scy void *arg; 838254562Scy iplookupop_t *op; 839254562Scy{ 840254562Scy ipf_dstl_softc_t *softd = arg; 841254562Scy ippool_dst_t user, *d, *new; 842254562Scy int unit, err; 843254562Scy 844254562Scy d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name); 845254562Scy if (d != NULL) { 846254562Scy IPFERROR(120013); 847254562Scy return EEXIST; 848254562Scy } 849254562Scy 850254562Scy err = COPYIN(op->iplo_struct, &user, sizeof(user)); 851254562Scy if (err != 0) { 852254562Scy IPFERROR(120021); 853254562Scy return EFAULT; 854254562Scy } 855254562Scy 856254562Scy KMALLOC(new, ippool_dst_t *); 857254562Scy if (new == NULL) { 858254562Scy softd->stats.ipls_nomem++; 859254562Scy IPFERROR(120014); 860254562Scy return ENOMEM; 861254562Scy } 862254562Scy bzero((char *)new, sizeof(*new)); 863254562Scy 864254562Scy MUTEX_INIT(&new->ipld_lock, "ipf dst table lock"); 865254562Scy 866254562Scy strncpy(new->ipld_name, op->iplo_name, FR_GROUPLEN); 867254562Scy unit = op->iplo_unit; 868254562Scy new->ipld_unit = unit; 869254562Scy new->ipld_policy = user.ipld_policy; 870254562Scy new->ipld_seed = ipf_random(); 871254562Scy new->ipld_ref = 1; 872254562Scy 873254562Scy new->ipld_pnext = softd->tails[unit + 1]; 874254562Scy *softd->tails[unit + 1] = new; 875254562Scy softd->tails[unit + 1] = &new->ipld_next; 876254562Scy softd->stats.ipls_numlists++; 877254562Scy 878254562Scy return 0; 879254562Scy} 880254562Scy 881254562Scy 882254562Scy/* ------------------------------------------------------------------------ */ 883254562Scy/* Function: ipf_dstlist_table_del */ 884254562Scy/* Returns: int - 0 = success, else error */ 885254562Scy/* Parameters: softc(I) - pointer to soft context main structure */ 886254562Scy/* arg(I) - pointer to local context to use */ 887254562Scy/* op(I) - pointer to lookup operation data */ 888254562Scy/* */ 889254562Scy/* Find a named destinstion list table and delete it. If there are other */ 890254562Scy/* references to it, the caller isn't told. */ 891254562Scy/* ------------------------------------------------------------------------ */ 892254562Scystatic int 893254562Scyipf_dstlist_table_del(softc, arg, op) 894254562Scy ipf_main_softc_t *softc; 895254562Scy void *arg; 896254562Scy iplookupop_t *op; 897254562Scy{ 898254562Scy ippool_dst_t *d; 899254562Scy 900254562Scy d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name); 901254562Scy if (d == NULL) { 902254562Scy IPFERROR(120015); 903254562Scy return ESRCH; 904254562Scy } 905254562Scy 906254562Scy if (d->ipld_dests != NULL) { 907254562Scy IPFERROR(120016); 908254562Scy return EBUSY; 909254562Scy } 910254562Scy 911254562Scy ipf_dstlist_table_remove(softc, arg, d); 912254562Scy 913254562Scy return 0; 914254562Scy} 915254562Scy 916254562Scy 917254562Scy/* ------------------------------------------------------------------------ */ 918254562Scy/* Function: ipf_dstlist_table_remove */ 919254562Scy/* Returns: Nil */ 920254562Scy/* Parameters: softc(I) - pointer to soft context main structure */ 921254562Scy/* softd(I) - pointer to the destination list context */ 922254562Scy/* d(I) - pointer to destination list */ 923254562Scy/* */ 924254562Scy/* Remove a given destination list from existance. While the IPDST_DELETE */ 925254562Scy/* flag is set every time we call this function and the reference count is */ 926254562Scy/* non-zero, the "numdereflists" counter is always incremented because the */ 927254562Scy/* decision about whether it will be freed or not is not made here. This */ 928254562Scy/* means that the only action the code can take here is to treat it as if */ 929254562Scy/* it will become a detached. */ 930254562Scy/* ------------------------------------------------------------------------ */ 931254562Scystatic void 932254562Scyipf_dstlist_table_remove(softc, softd, d) 933254562Scy ipf_main_softc_t *softc; 934254562Scy ipf_dstl_softc_t *softd; 935254562Scy ippool_dst_t *d; 936254562Scy{ 937254562Scy 938254562Scy if (softd->tails[d->ipld_unit + 1] == &d->ipld_next) 939254562Scy softd->tails[d->ipld_unit + 1] = d->ipld_pnext; 940254562Scy 941254562Scy if (d->ipld_pnext != NULL) 942254562Scy *d->ipld_pnext = d->ipld_next; 943254562Scy if (d->ipld_next != NULL) 944254562Scy d->ipld_next->ipld_pnext = d->ipld_pnext; 945254562Scy d->ipld_pnext = NULL; 946254562Scy d->ipld_next = NULL; 947254562Scy 948254562Scy ipf_dstlist_table_clearnodes(softd, d); 949254562Scy 950254562Scy softd->stats.ipls_numdereflists++; 951254562Scy d->ipld_flags |= IPDST_DELETE; 952254562Scy 953254562Scy ipf_dstlist_table_deref(softc, softd, d); 954254562Scy} 955254562Scy 956254562Scy 957254562Scy/* ------------------------------------------------------------------------ */ 958254562Scy/* Function: ipf_dstlist_table_free */ 959254562Scy/* Returns: Nil */ 960254562Scy/* Parameters: softd(I) - pointer to the destination list context */ 961254562Scy/* d(I) - pointer to destination list */ 962254562Scy/* */ 963254562Scy/* Free up a destination list data structure and any other memory that was */ 964254562Scy/* directly allocated as part of creating it. Individual destination list */ 965254562Scy/* nodes are not freed. It is assumed the caller will have already emptied */ 966254562Scy/* the destination list. */ 967254562Scy/* ------------------------------------------------------------------------ */ 968254562Scystatic void 969254562Scyipf_dstlist_table_free(softd, d) 970254562Scy ipf_dstl_softc_t *softd; 971254562Scy ippool_dst_t *d; 972254562Scy{ 973254562Scy MUTEX_DESTROY(&d->ipld_lock); 974254562Scy 975254562Scy if ((d->ipld_flags & IPDST_DELETE) != 0) 976254562Scy softd->stats.ipls_numdereflists--; 977254562Scy softd->stats.ipls_numlists--; 978254562Scy 979254562Scy if (d->ipld_dests != NULL) { 980254562Scy KFREES(d->ipld_dests, 981254562Scy d->ipld_maxnodes * sizeof(*d->ipld_dests)); 982254562Scy } 983254562Scy 984254562Scy KFREE(d); 985254562Scy} 986254562Scy 987254562Scy 988254562Scy/* ------------------------------------------------------------------------ */ 989254562Scy/* Function: ipf_dstlist_table_deref */ 990254562Scy/* Returns: int - 0 = success, else error */ 991254562Scy/* Parameters: softc(I) - pointer to soft context main structure */ 992254562Scy/* arg(I) - pointer to local context to use */ 993254562Scy/* op(I) - pointer to lookup operation data */ 994254562Scy/* */ 995254562Scy/* Drops the reference count on a destination list table object and free's */ 996254562Scy/* it if 0 has been reached. */ 997254562Scy/* ------------------------------------------------------------------------ */ 998254562Scystatic int 999254562Scyipf_dstlist_table_deref(softc, arg, table) 1000254562Scy ipf_main_softc_t *softc; 1001254562Scy void *arg; 1002254562Scy void *table; 1003254562Scy{ 1004254562Scy ippool_dst_t *d = table; 1005254562Scy 1006254562Scy d->ipld_ref--; 1007254562Scy if (d->ipld_ref > 0) 1008254562Scy return d->ipld_ref; 1009254562Scy 1010254562Scy ipf_dstlist_table_free(arg, d); 1011254562Scy 1012254562Scy return 0; 1013254562Scy} 1014254562Scy 1015254562Scy 1016254562Scy/* ------------------------------------------------------------------------ */ 1017254562Scy/* Function: ipf_dstlist_table_clearnodes */ 1018254562Scy/* Returns: Nil */ 1019254562Scy/* Parameters: softd(I) - pointer to the destination list context */ 1020254562Scy/* dst(I) - pointer to destination list */ 1021254562Scy/* */ 1022254562Scy/* Free all of the destination nodes attached to the given table. */ 1023254562Scy/* ------------------------------------------------------------------------ */ 1024254562Scystatic void 1025254562Scyipf_dstlist_table_clearnodes(softd, dst) 1026254562Scy ipf_dstl_softc_t *softd; 1027254562Scy ippool_dst_t *dst; 1028254562Scy{ 1029254562Scy ipf_dstnode_t *node; 1030254562Scy 1031254562Scy if (dst->ipld_dests == NULL) 1032254562Scy return; 1033254562Scy 1034254562Scy while ((node = *dst->ipld_dests) != NULL) { 1035254562Scy ipf_dstlist_node_free(softd, dst, node); 1036254562Scy } 1037254562Scy} 1038254562Scy 1039254562Scy 1040254562Scy/* ------------------------------------------------------------------------ */ 1041254562Scy/* Function: ipf_dstlist_table_find */ 1042254562Scy/* Returns: int - 0 = success, else error */ 1043254562Scy/* Parameters: arg(I) - pointer to local context to use */ 1044254562Scy/* unit(I) - device we are working with */ 1045254562Scy/* name(I) - destination table name to find */ 1046254562Scy/* */ 1047254562Scy/* Return a pointer to a destination table that matches the unit+name that */ 1048254562Scy/* is passed in. */ 1049254562Scy/* ------------------------------------------------------------------------ */ 1050254562Scystatic void * 1051254562Scyipf_dstlist_table_find(arg, unit, name) 1052254562Scy void *arg; 1053254562Scy int unit; 1054254562Scy char *name; 1055254562Scy{ 1056254562Scy ipf_dstl_softc_t *softd = arg; 1057254562Scy ippool_dst_t *d; 1058254562Scy 1059254562Scy for (d = softd->dstlist[unit + 1]; d != NULL; d = d->ipld_next) { 1060254562Scy if ((d->ipld_unit == unit) && 1061254562Scy !strncmp(d->ipld_name, name, FR_GROUPLEN)) { 1062254562Scy return d; 1063254562Scy } 1064254562Scy } 1065254562Scy 1066254562Scy return NULL; 1067254562Scy} 1068254562Scy 1069254562Scy 1070254562Scy/* ------------------------------------------------------------------------ */ 1071254562Scy/* Function: ipf_dstlist_select_ref */ 1072254562Scy/* Returns: void * - NULL = failure, else pointer to table */ 1073254562Scy/* Parameters: arg(I) - pointer to local context to use */ 1074254562Scy/* unit(I) - device we are working with */ 1075254562Scy/* name(I) - destination table name to find */ 1076254562Scy/* */ 1077254562Scy/* Attempt to find a destination table that matches the name passed in and */ 1078254562Scy/* if successful, bump up the reference count on it because we intend to */ 1079254562Scy/* store the pointer to it somewhere else. */ 1080254562Scy/* ------------------------------------------------------------------------ */ 1081254562Scystatic void * 1082254562Scyipf_dstlist_select_ref(arg, unit, name) 1083254562Scy void *arg; 1084254562Scy int unit; 1085254562Scy char *name; 1086254562Scy{ 1087254562Scy ippool_dst_t *d; 1088254562Scy 1089254562Scy d = ipf_dstlist_table_find(arg, unit, name); 1090254562Scy if (d != NULL) { 1091254562Scy MUTEX_ENTER(&d->ipld_lock); 1092254562Scy d->ipld_ref++; 1093254562Scy MUTEX_EXIT(&d->ipld_lock); 1094254562Scy } 1095254562Scy return d; 1096254562Scy} 1097254562Scy 1098254562Scy 1099254562Scy/* ------------------------------------------------------------------------ */ 1100254562Scy/* Function: ipf_dstlist_select */ 1101254562Scy/* Returns: void * - NULL = failure, else pointer to table */ 1102254562Scy/* Parameters: fin(I) - pointer to packet information */ 1103254562Scy/* d(I) - pointer to destination list */ 1104254562Scy/* */ 1105254562Scy/* Find the next node in the destination list to be used according to the */ 1106254562Scy/* defined policy. Of these, "connection" is the most expensive policy to */ 1107254562Scy/* implement as it always looks for the node with the least number of */ 1108254562Scy/* connections associated with it. */ 1109254562Scy/* */ 1110254562Scy/* The hashes exclude the port numbers so that all protocols map to the */ 1111254562Scy/* same destination. Otherwise, someone doing a ping would target a */ 1112254562Scy/* different server than their TCP connection, etc. MD-5 is used to */ 1113254562Scy/* transform the addressese into something random that the other end could */ 1114254562Scy/* not easily guess and use in an attack. ipld_seed introduces an unknown */ 1115254562Scy/* into the hash calculation to increase the difficult of an attacker */ 1116254562Scy/* guessing the bucket. */ 1117254562Scy/* */ 1118254562Scy/* One final comment: mixing different address families in a single pool */ 1119254562Scy/* will currently result in failures as the address family of the node is */ 1120254562Scy/* only matched up with that in the packet as the last step. While this can */ 1121254562Scy/* be coded around for the weighted connection and round-robin models, it */ 1122254562Scy/* cannot be supported for the hash/random models as they do not search and */ 1123254562Scy/* nor is the algorithm conducive to searching. */ 1124254562Scy/* ------------------------------------------------------------------------ */ 1125254562Scystatic ipf_dstnode_t * 1126254562Scyipf_dstlist_select(fin, d) 1127254562Scy fr_info_t *fin; 1128254562Scy ippool_dst_t *d; 1129254562Scy{ 1130254562Scy ipf_dstnode_t *node, *sel; 1131254562Scy int connects; 1132254562Scy u_32_t hash[4]; 1133254562Scy MD5_CTX ctx; 1134254562Scy int family; 1135254562Scy int x; 1136254562Scy 1137254562Scy if (d->ipld_dests == NULL || *d->ipld_dests == NULL) 1138254562Scy return NULL; 1139254562Scy 1140254562Scy family = fin->fin_family; 1141254562Scy 1142254562Scy MUTEX_ENTER(&d->ipld_lock); 1143254562Scy 1144254562Scy switch (d->ipld_policy) 1145254562Scy { 1146254562Scy case IPLDP_ROUNDROBIN: 1147254562Scy sel = d->ipld_selected; 1148254562Scy if (sel == NULL) { 1149254562Scy sel = *d->ipld_dests; 1150254562Scy } else { 1151254562Scy sel = sel->ipfd_next; 1152254562Scy if (sel == NULL) 1153254562Scy sel = *d->ipld_dests; 1154254562Scy } 1155254562Scy break; 1156254562Scy 1157254562Scy case IPLDP_CONNECTION: 1158254562Scy if (d->ipld_selected == NULL) { 1159254562Scy sel = *d->ipld_dests; 1160254562Scy break; 1161254562Scy } 1162254562Scy 1163254562Scy sel = d->ipld_selected; 1164254562Scy connects = 0x7fffffff; 1165254562Scy node = sel->ipfd_next; 1166254562Scy if (node == NULL) 1167254562Scy node = *d->ipld_dests; 1168254562Scy while (node != d->ipld_selected) { 1169254562Scy if (node->ipfd_states == 0) { 1170254562Scy sel = node; 1171254562Scy break; 1172254562Scy } 1173254562Scy if (node->ipfd_states < connects) { 1174254562Scy sel = node; 1175254562Scy connects = node->ipfd_states; 1176254562Scy } 1177254562Scy node = node->ipfd_next; 1178254562Scy if (node == NULL) 1179254562Scy node = *d->ipld_dests; 1180254562Scy } 1181254562Scy break; 1182254562Scy 1183254562Scy case IPLDP_RANDOM : 1184254562Scy x = ipf_random() % d->ipld_nodes; 1185254562Scy sel = d->ipld_dests[x]; 1186254562Scy break; 1187254562Scy 1188254562Scy case IPLDP_HASHED : 1189254562Scy MD5Init(&ctx); 1190254562Scy MD5Update(&ctx, (u_char *)&d->ipld_seed, sizeof(d->ipld_seed)); 1191254562Scy MD5Update(&ctx, (u_char *)&fin->fin_src6, 1192254562Scy sizeof(fin->fin_src6)); 1193254562Scy MD5Update(&ctx, (u_char *)&fin->fin_dst6, 1194254562Scy sizeof(fin->fin_dst6)); 1195254562Scy MD5Final((u_char *)hash, &ctx); 1196254562Scy x = hash[0] % d->ipld_nodes; 1197254562Scy sel = d->ipld_dests[x]; 1198254562Scy break; 1199254562Scy 1200254562Scy case IPLDP_SRCHASH : 1201254562Scy MD5Init(&ctx); 1202254562Scy MD5Update(&ctx, (u_char *)&d->ipld_seed, sizeof(d->ipld_seed)); 1203254562Scy MD5Update(&ctx, (u_char *)&fin->fin_src6, 1204254562Scy sizeof(fin->fin_src6)); 1205254562Scy MD5Final((u_char *)hash, &ctx); 1206254562Scy x = hash[0] % d->ipld_nodes; 1207254562Scy sel = d->ipld_dests[x]; 1208254562Scy break; 1209254562Scy 1210254562Scy case IPLDP_DSTHASH : 1211254562Scy MD5Init(&ctx); 1212254562Scy MD5Update(&ctx, (u_char *)&d->ipld_seed, sizeof(d->ipld_seed)); 1213254562Scy MD5Update(&ctx, (u_char *)&fin->fin_dst6, 1214254562Scy sizeof(fin->fin_dst6)); 1215254562Scy MD5Final((u_char *)hash, &ctx); 1216254562Scy x = hash[0] % d->ipld_nodes; 1217254562Scy sel = d->ipld_dests[x]; 1218254562Scy break; 1219254562Scy 1220254562Scy default : 1221254562Scy sel = NULL; 1222254562Scy break; 1223254562Scy } 1224254562Scy 1225254562Scy if (sel->ipfd_dest.fd_addr.adf_family != family) 1226254562Scy sel = NULL; 1227254562Scy d->ipld_selected = sel; 1228254562Scy 1229254562Scy MUTEX_EXIT(&d->ipld_lock); 1230254562Scy 1231254562Scy return sel; 1232254562Scy} 1233254562Scy 1234254562Scy 1235254562Scy/* ------------------------------------------------------------------------ */ 1236254562Scy/* Function: ipf_dstlist_select_node */ 1237254562Scy/* Returns: int - -1 == failure, 0 == success */ 1238254562Scy/* Parameters: fin(I) - pointer to packet information */ 1239254562Scy/* group(I) - destination pool to search */ 1240254562Scy/* addr(I) - pointer to store selected address */ 1241254562Scy/* pfdp(O) - pointer to storage for selected destination node */ 1242254562Scy/* */ 1243254562Scy/* This function is only responsible for obtaining the next IP address for */ 1244254562Scy/* use and storing it in the caller's address space (addr). "addr" is only */ 1245254562Scy/* used for storage if pfdp is NULL. No permanent reference is currently */ 1246254562Scy/* kept on the node. */ 1247254562Scy/* ------------------------------------------------------------------------ */ 1248254562Scyint 1249254562Scyipf_dstlist_select_node(fin, group, addr, pfdp) 1250254562Scy fr_info_t *fin; 1251254562Scy void *group; 1252254562Scy u_32_t *addr; 1253254562Scy frdest_t *pfdp; 1254254562Scy{ 1255254562Scy#ifdef USE_MUTEXES 1256254562Scy ipf_main_softc_t *softc = fin->fin_main_soft; 1257254562Scy#endif 1258254562Scy ippool_dst_t *d = group; 1259254562Scy ipf_dstnode_t *node; 1260254562Scy frdest_t *fdp; 1261254562Scy 1262254562Scy READ_ENTER(&softc->ipf_poolrw); 1263254562Scy 1264254562Scy node = ipf_dstlist_select(fin, d); 1265254562Scy if (node == NULL) { 1266254562Scy RWLOCK_EXIT(&softc->ipf_poolrw); 1267254562Scy return -1; 1268254562Scy } 1269254562Scy 1270254562Scy if (pfdp != NULL) { 1271254562Scy bcopy(&node->ipfd_dest, pfdp, sizeof(*pfdp)); 1272254562Scy } else { 1273254562Scy if (fin->fin_family == AF_INET) { 1274254562Scy addr[0] = node->ipfd_dest.fd_addr.adf_addr.i6[0]; 1275254562Scy } else if (fin->fin_family == AF_INET6) { 1276254562Scy addr[0] = node->ipfd_dest.fd_addr.adf_addr.i6[0]; 1277254562Scy addr[1] = node->ipfd_dest.fd_addr.adf_addr.i6[1]; 1278254562Scy addr[2] = node->ipfd_dest.fd_addr.adf_addr.i6[2]; 1279254562Scy addr[3] = node->ipfd_dest.fd_addr.adf_addr.i6[3]; 1280254562Scy } 1281254562Scy } 1282254562Scy 1283254562Scy fdp = &node->ipfd_dest; 1284254562Scy if (fdp->fd_ptr == NULL) 1285254562Scy fdp->fd_ptr = fin->fin_ifp; 1286254562Scy 1287254562Scy MUTEX_ENTER(&node->ipfd_lock); 1288254562Scy node->ipfd_states++; 1289254562Scy MUTEX_EXIT(&node->ipfd_lock); 1290254562Scy 1291254562Scy RWLOCK_EXIT(&softc->ipf_poolrw); 1292254562Scy 1293254562Scy return 0; 1294254562Scy} 1295254562Scy 1296254562Scy 1297254562Scy/* ------------------------------------------------------------------------ */ 1298254562Scy/* Function: ipf_dstlist_expire */ 1299254562Scy/* Returns: Nil */ 1300254562Scy/* Parameters: softc(I) - pointer to soft context main structure */ 1301254562Scy/* arg(I) - pointer to local context to use */ 1302254562Scy/* */ 1303254562Scy/* There are currently no objects to expire in destination lists. */ 1304254562Scy/* ------------------------------------------------------------------------ */ 1305254562Scystatic void 1306254562Scyipf_dstlist_expire(softc, arg) 1307254562Scy ipf_main_softc_t *softc; 1308254562Scy void *arg; 1309254562Scy{ 1310254562Scy return; 1311254562Scy} 1312254562Scy 1313254562Scy 1314254562Scy/* ------------------------------------------------------------------------ */ 1315254562Scy/* Function: ipf_dstlist_sync */ 1316254562Scy/* Returns: Nil */ 1317254562Scy/* Parameters: softc(I) - pointer to soft context main structure */ 1318254562Scy/* arg(I) - pointer to local context to use */ 1319254562Scy/* */ 1320254562Scy/* When a network interface appears or disappears, we need to revalidate */ 1321254562Scy/* all of the network interface names that have been configured as a target */ 1322254562Scy/* in a destination list. */ 1323254562Scy/* ------------------------------------------------------------------------ */ 1324254562Scyvoid 1325254562Scyipf_dstlist_sync(softc, arg) 1326254562Scy ipf_main_softc_t *softc; 1327254562Scy void *arg; 1328254562Scy{ 1329254562Scy ipf_dstl_softc_t *softd = arg; 1330254562Scy ipf_dstnode_t *node; 1331254562Scy ippool_dst_t *list; 1332254562Scy int i; 1333254562Scy int j; 1334254562Scy 1335254562Scy for (i = 0; i < IPL_LOGMAX; i++) { 1336254562Scy for (list = softd->dstlist[i]; list != NULL; 1337254562Scy list = list->ipld_next) { 1338254562Scy for (j = 0; j < list->ipld_maxnodes; j++) { 1339254562Scy node = list->ipld_dests[j]; 1340254562Scy if (node == NULL) 1341254562Scy continue; 1342254562Scy if (node->ipfd_dest.fd_name == -1) 1343254562Scy continue; 1344254562Scy (void) ipf_resolvedest(softc, 1345254562Scy node->ipfd_names, 1346254562Scy &node->ipfd_dest, 1347254562Scy AF_INET); 1348254562Scy } 1349254562Scy } 1350254562Scy } 1351254562Scy} 1352