ip_dstlist.c revision 254219
1254219Scy/* 2254219Scy * Copyright (C) 2012 by Darren Reed. 3254219Scy * 4254219Scy * See the IPFILTER.LICENCE file for details on licencing. 5254219Scy */ 6254219Scy#if defined(KERNEL) || defined(_KERNEL) 7254219Scy# undef KERNEL 8254219Scy# undef _KERNEL 9254219Scy# define KERNEL 1 10254219Scy# define _KERNEL 1 11254219Scy#endif 12254219Scy#if defined(__osf__) 13254219Scy# define _PROTO_NET_H_ 14254219Scy#endif 15254219Scy#include <sys/errno.h> 16254219Scy#include <sys/types.h> 17254219Scy#include <sys/param.h> 18254219Scy#include <sys/file.h> 19254219Scy#if !defined(_KERNEL) && !defined(__KERNEL__) 20254219Scy# include <stdio.h> 21254219Scy# include <stdlib.h> 22254219Scy# include <string.h> 23254219Scy# define _KERNEL 24254219Scy# ifdef __OpenBSD__ 25254219Scystruct file; 26254219Scy# endif 27254219Scy# include <sys/uio.h> 28254219Scy# undef _KERNEL 29254219Scy#else 30254219Scy# include <sys/systm.h> 31254219Scy# if defined(NetBSD) && (__NetBSD_Version__ >= 104000000) 32254219Scy# include <sys/proc.h> 33254219Scy# endif 34254219Scy#endif 35254219Scy#include <sys/time.h> 36254219Scy#if !defined(linux) 37254219Scy# include <sys/protosw.h> 38254219Scy#endif 39254219Scy#include <sys/socket.h> 40254219Scy#if defined(_KERNEL) && (!defined(__SVR4) && !defined(__svr4__)) 41254219Scy# include <sys/mbuf.h> 42254219Scy#endif 43254219Scy#if defined(__SVR4) || defined(__svr4__) 44254219Scy# include <sys/filio.h> 45254219Scy# include <sys/byteorder.h> 46254219Scy# ifdef _KERNEL 47254219Scy# include <sys/dditypes.h> 48254219Scy# endif 49254219Scy# include <sys/stream.h> 50254219Scy# include <sys/kmem.h> 51254219Scy#endif 52254219Scy#if defined(__FreeBSD_version) && (__FreeBSD_version >= 300000) 53254219Scy# include <sys/malloc.h> 54254219Scy#endif 55254219Scy 56254219Scy#include <net/if.h> 57254219Scy#include <netinet/in.h> 58254219Scy 59254219Scy#include "netinet/ip_compat.h" 60254219Scy#include "netinet/ip_fil.h" 61254219Scy#include "netinet/ip_nat.h" 62254219Scy#include "netinet/ip_lookup.h" 63254219Scy#include "netinet/ip_dstlist.h" 64254219Scy 65254219Scy/* END OF INCLUDES */ 66254219Scy 67254219Scy#ifdef HAS_SYS_MD5_H 68254219Scy# include <sys/md5.h> 69254219Scy#else 70254219Scy# include "md5.h" 71254219Scy#endif 72254219Scy 73254219Scy#if !defined(lint) 74254219Scystatic const char rcsid[] = "@(#)$Id: ip_dstlist.c,v 1.13.2.12 2012/07/20 08:40:19 darren_r Exp $"; 75254219Scy#endif 76254219Scy 77254219Scytypedef struct ipf_dstl_softc_s { 78254219Scy ippool_dst_t *dstlist[LOOKUP_POOL_SZ]; 79254219Scy ippool_dst_t **tails[LOOKUP_POOL_SZ]; 80254219Scy ipf_dstl_stat_t stats; 81254219Scy} ipf_dstl_softc_t; 82254219Scy 83254219Scy 84254219Scystatic void *ipf_dstlist_soft_create __P((ipf_main_softc_t *)); 85254219Scystatic void ipf_dstlist_soft_destroy __P((ipf_main_softc_t *, void *)); 86254219Scystatic int ipf_dstlist_soft_init __P((ipf_main_softc_t *, void *)); 87254219Scystatic void ipf_dstlist_soft_fini __P((ipf_main_softc_t *, void *)); 88254219Scystatic int ipf_dstlist_addr_find __P((ipf_main_softc_t *, void *, int, 89254219Scy void *, u_int)); 90254219Scystatic size_t ipf_dstlist_flush __P((ipf_main_softc_t *, void *, 91254219Scy iplookupflush_t *)); 92254219Scystatic int ipf_dstlist_iter_deref __P((ipf_main_softc_t *, void *, int, int, 93254219Scy void *)); 94254219Scystatic int ipf_dstlist_iter_next __P((ipf_main_softc_t *, void *, ipftoken_t *, 95254219Scy ipflookupiter_t *)); 96254219Scystatic int ipf_dstlist_node_add __P((ipf_main_softc_t *, void *, 97254219Scy iplookupop_t *, int)); 98254219Scystatic int ipf_dstlist_node_del __P((ipf_main_softc_t *, void *, 99254219Scy iplookupop_t *, int)); 100254219Scystatic int ipf_dstlist_stats_get __P((ipf_main_softc_t *, void *, 101254219Scy iplookupop_t *)); 102254219Scystatic int ipf_dstlist_table_add __P((ipf_main_softc_t *, void *, 103254219Scy iplookupop_t *)); 104254219Scystatic int ipf_dstlist_table_del __P((ipf_main_softc_t *, void *, 105254219Scy iplookupop_t *)); 106254219Scystatic int ipf_dstlist_table_deref __P((ipf_main_softc_t *, void *, void *)); 107254219Scystatic void *ipf_dstlist_table_find __P((void *, int, char *)); 108254219Scystatic void ipf_dstlist_table_free __P((ipf_dstl_softc_t *, ippool_dst_t *)); 109254219Scystatic void ipf_dstlist_table_remove __P((ipf_main_softc_t *, 110254219Scy ipf_dstl_softc_t *, ippool_dst_t *)); 111254219Scystatic void ipf_dstlist_table_clearnodes __P((ipf_dstl_softc_t *, 112254219Scy ippool_dst_t *)); 113254219Scystatic ipf_dstnode_t *ipf_dstlist_select __P((fr_info_t *, ippool_dst_t *)); 114254219Scystatic void *ipf_dstlist_select_ref __P((void *, int, char *)); 115254219Scystatic void ipf_dstlist_node_free __P((ipf_dstl_softc_t *, ippool_dst_t *, ipf_dstnode_t *)); 116254219Scystatic int ipf_dstlist_node_deref __P((void *, ipf_dstnode_t *)); 117254219Scystatic void ipf_dstlist_expire __P((ipf_main_softc_t *, void *)); 118254219Scystatic void ipf_dstlist_sync __P((ipf_main_softc_t *, void *)); 119254219Scy 120254219Scyipf_lookup_t ipf_dstlist_backend = { 121254219Scy IPLT_DSTLIST, 122254219Scy ipf_dstlist_soft_create, 123254219Scy ipf_dstlist_soft_destroy, 124254219Scy ipf_dstlist_soft_init, 125254219Scy ipf_dstlist_soft_fini, 126254219Scy ipf_dstlist_addr_find, 127254219Scy ipf_dstlist_flush, 128254219Scy ipf_dstlist_iter_deref, 129254219Scy ipf_dstlist_iter_next, 130254219Scy ipf_dstlist_node_add, 131254219Scy ipf_dstlist_node_del, 132254219Scy ipf_dstlist_stats_get, 133254219Scy ipf_dstlist_table_add, 134254219Scy ipf_dstlist_table_del, 135254219Scy ipf_dstlist_table_deref, 136254219Scy ipf_dstlist_table_find, 137254219Scy ipf_dstlist_select_ref, 138254219Scy ipf_dstlist_select_node, 139254219Scy ipf_dstlist_expire, 140254219Scy ipf_dstlist_sync 141254219Scy}; 142254219Scy 143254219Scy 144254219Scy/* ------------------------------------------------------------------------ */ 145254219Scy/* Function: ipf_dstlist_soft_create */ 146254219Scy/* Returns: int - 0 = success, else error */ 147254219Scy/* Parameters: softc(I) - pointer to soft context main structure */ 148254219Scy/* */ 149254219Scy/* Allocating a chunk of memory filled with 0's is enough for the current */ 150254219Scy/* soft context used with destination lists. */ 151254219Scy/* ------------------------------------------------------------------------ */ 152254219Scystatic void * 153254219Scyipf_dstlist_soft_create(softc) 154254219Scy ipf_main_softc_t *softc; 155254219Scy{ 156254219Scy ipf_dstl_softc_t *softd; 157254219Scy int i; 158254219Scy 159254219Scy KMALLOC(softd, ipf_dstl_softc_t *); 160254219Scy if (softd == NULL) { 161254219Scy IPFERROR(120028); 162254219Scy return NULL; 163254219Scy } 164254219Scy 165254219Scy bzero((char *)softd, sizeof(*softd)); 166254219Scy for (i = 0; i <= IPL_LOGMAX; i++) 167254219Scy softd->tails[i] = &softd->dstlist[i]; 168254219Scy 169254219Scy return softd; 170254219Scy} 171254219Scy 172254219Scy 173254219Scy/* ------------------------------------------------------------------------ */ 174254219Scy/* Function: ipf_dstlist_soft_destroy */ 175254219Scy/* Returns: Nil */ 176254219Scy/* Parameters: softc(I) - pointer to soft context main structure */ 177254219Scy/* arg(I) - pointer to local context to use */ 178254219Scy/* */ 179254219Scy/* For destination lists, the only thing we have to do when destroying the */ 180254219Scy/* soft context is free it! */ 181254219Scy/* ------------------------------------------------------------------------ */ 182254219Scystatic void 183254219Scyipf_dstlist_soft_destroy(softc, arg) 184254219Scy ipf_main_softc_t *softc; 185254219Scy void *arg; 186254219Scy{ 187254219Scy ipf_dstl_softc_t *softd = arg; 188254219Scy 189254219Scy KFREE(softd); 190254219Scy} 191254219Scy 192254219Scy 193254219Scy/* ------------------------------------------------------------------------ */ 194254219Scy/* Function: ipf_dstlist_soft_init */ 195254219Scy/* Returns: int - 0 = success, else error */ 196254219Scy/* Parameters: softc(I) - pointer to soft context main structure */ 197254219Scy/* arg(I) - pointer to local context to use */ 198254219Scy/* */ 199254219Scy/* There is currently no soft context for destination list management. */ 200254219Scy/* ------------------------------------------------------------------------ */ 201254219Scystatic int 202254219Scyipf_dstlist_soft_init(softc, arg) 203254219Scy ipf_main_softc_t *softc; 204254219Scy void *arg; 205254219Scy{ 206254219Scy return 0; 207254219Scy} 208254219Scy 209254219Scy 210254219Scy/* ------------------------------------------------------------------------ */ 211254219Scy/* Function: ipf_dstlist_soft_fini */ 212254219Scy/* Returns: Nil */ 213254219Scy/* Parameters: softc(I) - pointer to soft context main structure */ 214254219Scy/* arg(I) - pointer to local context to use */ 215254219Scy/* */ 216254219Scy/* There is currently no soft context for destination list management. */ 217254219Scy/* ------------------------------------------------------------------------ */ 218254219Scystatic void 219254219Scyipf_dstlist_soft_fini(softc, arg) 220254219Scy ipf_main_softc_t *softc; 221254219Scy void *arg; 222254219Scy{ 223254219Scy ipf_dstl_softc_t *softd = arg; 224254219Scy int i; 225254219Scy 226254219Scy for (i = -1; i <= IPL_LOGMAX; i++) { 227254219Scy while (softd->dstlist[i + 1] != NULL) { 228254219Scy ipf_dstlist_table_remove(softc, softd, 229254219Scy softd->dstlist[i + 1]); 230254219Scy } 231254219Scy } 232254219Scy 233254219Scy ASSERT(softd->stats.ipls_numderefnodes == 0); 234254219Scy} 235254219Scy 236254219Scy 237254219Scy/* ------------------------------------------------------------------------ */ 238254219Scy/* Function: ipf_dstlist_addr_find */ 239254219Scy/* Returns: int - 0 = success, else error */ 240254219Scy/* Parameters: softc(I) - pointer to soft context main structure */ 241254219Scy/* arg1(I) - pointer to local context to use */ 242254219Scy/* arg2(I) - pointer to local context to use */ 243254219Scy/* arg3(I) - pointer to local context to use */ 244254219Scy/* arg4(I) - pointer to local context to use */ 245254219Scy/* */ 246254219Scy/* There is currently no such thing as searching a destination list for an */ 247254219Scy/* address so this function becomes a no-op. Its presence is required as */ 248254219Scy/* ipf_lookup_res_name() stores the "addr_find" function pointer in the */ 249254219Scy/* pointer passed in to it as funcptr, although it could be a generic null- */ 250254219Scy/* op function rather than a specific one. */ 251254219Scy/* ------------------------------------------------------------------------ */ 252254219Scy/*ARGSUSED*/ 253254219Scystatic int 254254219Scyipf_dstlist_addr_find(softc, arg1, arg2, arg3, arg4) 255254219Scy ipf_main_softc_t *softc; 256254219Scy void *arg1, *arg3; 257254219Scy int arg2; 258254219Scy u_int arg4; 259254219Scy{ 260254219Scy return -1; 261254219Scy} 262254219Scy 263254219Scy 264254219Scy/* ------------------------------------------------------------------------ */ 265254219Scy/* Function: ipf_dstlist_flush */ 266254219Scy/* Returns: int - number of objects deleted */ 267254219Scy/* Parameters: softc(I) - pointer to soft context main structure */ 268254219Scy/* arg(I) - pointer to local context to use */ 269254219Scy/* fop(I) - pointer to lookup flush operation data */ 270254219Scy/* */ 271254219Scy/* Flush all of the destination tables that match the data passed in with */ 272254219Scy/* the iplookupflush_t. There are two ways to match objects: the device for */ 273254219Scy/* which they are to be used with and their name. */ 274254219Scy/* ------------------------------------------------------------------------ */ 275254219Scystatic size_t 276254219Scyipf_dstlist_flush(softc, arg, fop) 277254219Scy ipf_main_softc_t *softc; 278254219Scy void *arg; 279254219Scy iplookupflush_t *fop; 280254219Scy{ 281254219Scy ipf_dstl_softc_t *softd = arg; 282254219Scy ippool_dst_t *node, *next; 283254219Scy int n, i; 284254219Scy 285254219Scy for (n = 0, i = -1; i <= IPL_LOGMAX; i++) { 286254219Scy if (fop->iplf_unit != IPLT_ALL && fop->iplf_unit != i) 287254219Scy continue; 288254219Scy for (node = softd->dstlist[i + 1]; node != NULL; node = next) { 289254219Scy next = node->ipld_next; 290254219Scy 291254219Scy if ((*fop->iplf_name != '\0') && 292254219Scy strncmp(fop->iplf_name, node->ipld_name, 293254219Scy FR_GROUPLEN)) 294254219Scy continue; 295254219Scy 296254219Scy ipf_dstlist_table_remove(softc, softd, node); 297254219Scy n++; 298254219Scy } 299254219Scy } 300254219Scy return n; 301254219Scy} 302254219Scy 303254219Scy 304254219Scy/* ------------------------------------------------------------------------ */ 305254219Scy/* Function: ipf_dstlist_iter_deref */ 306254219Scy/* Returns: int - 0 = success, else error */ 307254219Scy/* Parameters: softc(I) - pointer to soft context main structure */ 308254219Scy/* arg(I) - pointer to local context to use */ 309254219Scy/* otype(I) - type of data structure to iterate through */ 310254219Scy/* unit(I) - device we are working with */ 311254219Scy/* data(I) - address of object in kernel space */ 312254219Scy/* */ 313254219Scy/* This function is called when the iteration token is being free'd and is */ 314254219Scy/* responsible for dropping the reference count of the structure it points */ 315254219Scy/* to. */ 316254219Scy/* ------------------------------------------------------------------------ */ 317254219Scystatic int 318254219Scyipf_dstlist_iter_deref(softc, arg, otype, unit, data) 319254219Scy ipf_main_softc_t *softc; 320254219Scy void *arg; 321254219Scy int otype, unit; 322254219Scy void *data; 323254219Scy{ 324254219Scy if (data == NULL) { 325254219Scy IPFERROR(120001); 326254219Scy return EINVAL; 327254219Scy } 328254219Scy 329254219Scy if (unit < -1 || unit > IPL_LOGMAX) { 330254219Scy IPFERROR(120002); 331254219Scy return EINVAL; 332254219Scy } 333254219Scy 334254219Scy switch (otype) 335254219Scy { 336254219Scy case IPFLOOKUPITER_LIST : 337254219Scy ipf_dstlist_table_deref(softc, arg, (ippool_dst_t *)data); 338254219Scy break; 339254219Scy 340254219Scy case IPFLOOKUPITER_NODE : 341254219Scy ipf_dstlist_node_deref(arg, (ipf_dstnode_t *)data); 342254219Scy break; 343254219Scy } 344254219Scy 345254219Scy return 0; 346254219Scy} 347254219Scy 348254219Scy 349254219Scy/* ------------------------------------------------------------------------ */ 350254219Scy/* Function: ipf_dstlist_iter_next */ 351254219Scy/* Returns: int - 0 = success, else error */ 352254219Scy/* Parameters: softc(I) - pointer to soft context main structure */ 353254219Scy/* arg(I) - pointer to local context to use */ 354254219Scy/* op(I) - pointer to lookup operation data */ 355254219Scy/* uid(I) - uid of process doing the ioctl */ 356254219Scy/* */ 357254219Scy/* This function is responsible for either selecting the next destination */ 358254219Scy/* list or node on a destination list to be returned as a user process */ 359254219Scy/* iterates through the list of destination lists or nodes. */ 360254219Scy/* ------------------------------------------------------------------------ */ 361254219Scystatic int 362254219Scyipf_dstlist_iter_next(softc, arg, token, iter) 363254219Scy ipf_main_softc_t *softc; 364254219Scy void *arg; 365254219Scy ipftoken_t *token; 366254219Scy ipflookupiter_t *iter; 367254219Scy{ 368254219Scy ipf_dstnode_t zn, *nextnode = NULL, *node = NULL; 369254219Scy ippool_dst_t zero, *next = NULL, *dsttab = NULL; 370254219Scy ipf_dstl_softc_t *softd = arg; 371254219Scy int err = 0; 372254219Scy void *hint; 373254219Scy 374254219Scy switch (iter->ili_otype) 375254219Scy { 376254219Scy case IPFLOOKUPITER_LIST : 377254219Scy dsttab = token->ipt_data; 378254219Scy if (dsttab == NULL) { 379254219Scy next = softd->dstlist[(int)iter->ili_unit + 1]; 380254219Scy } else { 381254219Scy next = dsttab->ipld_next; 382254219Scy } 383254219Scy 384254219Scy if (next != NULL) { 385254219Scy ATOMIC_INC32(next->ipld_ref); 386254219Scy token->ipt_data = next; 387254219Scy hint = next->ipld_next; 388254219Scy } else { 389254219Scy bzero((char *)&zero, sizeof(zero)); 390254219Scy next = &zero; 391254219Scy token->ipt_data = NULL; 392254219Scy hint = NULL; 393254219Scy } 394254219Scy break; 395254219Scy 396254219Scy case IPFLOOKUPITER_NODE : 397254219Scy node = token->ipt_data; 398254219Scy if (node == NULL) { 399254219Scy dsttab = ipf_dstlist_table_find(arg, iter->ili_unit, 400254219Scy iter->ili_name); 401254219Scy if (dsttab == NULL) { 402254219Scy IPFERROR(120004); 403254219Scy err = ESRCH; 404254219Scy nextnode = NULL; 405254219Scy } else { 406254219Scy if (dsttab->ipld_dests == NULL) 407254219Scy nextnode = NULL; 408254219Scy else 409254219Scy nextnode = *dsttab->ipld_dests; 410254219Scy dsttab = NULL; 411254219Scy } 412254219Scy } else { 413254219Scy nextnode = node->ipfd_next; 414254219Scy } 415254219Scy 416254219Scy if (nextnode != NULL) { 417254219Scy MUTEX_ENTER(&nextnode->ipfd_lock); 418254219Scy nextnode->ipfd_ref++; 419254219Scy MUTEX_EXIT(&nextnode->ipfd_lock); 420254219Scy token->ipt_data = nextnode; 421254219Scy hint = nextnode->ipfd_next; 422254219Scy } else { 423254219Scy bzero((char *)&zn, sizeof(zn)); 424254219Scy nextnode = &zn; 425254219Scy token->ipt_data = NULL; 426254219Scy hint = NULL; 427254219Scy } 428254219Scy break; 429254219Scy default : 430254219Scy IPFERROR(120003); 431254219Scy err = EINVAL; 432254219Scy break; 433254219Scy } 434254219Scy 435254219Scy if (err != 0) 436254219Scy return err; 437254219Scy 438254219Scy switch (iter->ili_otype) 439254219Scy { 440254219Scy case IPFLOOKUPITER_LIST : 441254219Scy if (dsttab != NULL) 442254219Scy ipf_dstlist_table_deref(softc, arg, dsttab); 443254219Scy err = COPYOUT(next, iter->ili_data, sizeof(*next)); 444254219Scy if (err != 0) { 445254219Scy IPFERROR(120005); 446254219Scy err = EFAULT; 447254219Scy } 448254219Scy break; 449254219Scy 450254219Scy case IPFLOOKUPITER_NODE : 451254219Scy if (node != NULL) 452254219Scy ipf_dstlist_node_deref(arg, node); 453254219Scy err = COPYOUT(nextnode, iter->ili_data, sizeof(*nextnode)); 454254219Scy if (err != 0) { 455254219Scy IPFERROR(120006); 456254219Scy err = EFAULT; 457254219Scy } 458254219Scy break; 459254219Scy } 460254219Scy 461254219Scy if (hint == NULL) 462254219Scy ipf_token_mark_complete(token); 463254219Scy 464254219Scy return err; 465254219Scy} 466254219Scy 467254219Scy 468254219Scy/* ------------------------------------------------------------------------ */ 469254219Scy/* Function: ipf_dstlist_node_add */ 470254219Scy/* Returns: int - 0 = success, else error */ 471254219Scy/* Parameters: softc(I) - pointer to soft context main structure */ 472254219Scy/* arg(I) - pointer to local context to use */ 473254219Scy/* op(I) - pointer to lookup operation data */ 474254219Scy/* uid(I) - uid of process doing the ioctl */ 475254219Scy/* Locks: WRITE(ipf_poolrw) */ 476254219Scy/* */ 477254219Scy/* Add a new node to a destination list. To do this, we only copy in the */ 478254219Scy/* frdest_t structure because that contains the only data required from the */ 479254219Scy/* application to create a new node. The frdest_t doesn't contain the name */ 480254219Scy/* itself. When loading filter rules, fd_name is a 'pointer' to the name. */ 481254219Scy/* In this case, the 'pointer' does not work, instead it is the length of */ 482254219Scy/* the name and the name is immediately following the frdest_t structure. */ 483254219Scy/* fd_name must include the trailing \0, so it should be strlen(str) + 1. */ 484254219Scy/* For simple sanity checking, an upper bound on the size of fd_name is */ 485254219Scy/* imposed - 128. */ 486254219Scy/* ------------------------------------------------------------------------ */ 487254219Scystatic int 488254219Scyipf_dstlist_node_add(softc, arg, op, uid) 489254219Scy ipf_main_softc_t *softc; 490254219Scy void *arg; 491254219Scy iplookupop_t *op; 492254219Scy int uid; 493254219Scy{ 494254219Scy ipf_dstl_softc_t *softd = arg; 495254219Scy ipf_dstnode_t *node, **nodes; 496254219Scy ippool_dst_t *d; 497254219Scy frdest_t dest; 498254219Scy int err; 499254219Scy 500254219Scy if (op->iplo_size < sizeof(frdest_t)) { 501254219Scy IPFERROR(120007); 502254219Scy return EINVAL; 503254219Scy } 504254219Scy 505254219Scy err = COPYIN(op->iplo_struct, &dest, sizeof(dest)); 506254219Scy if (err != 0) { 507254219Scy IPFERROR(120009); 508254219Scy return EFAULT; 509254219Scy } 510254219Scy 511254219Scy d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name); 512254219Scy if (d == NULL) { 513254219Scy IPFERROR(120010); 514254219Scy return ESRCH; 515254219Scy } 516254219Scy 517254219Scy switch (dest.fd_addr.adf_family) 518254219Scy { 519254219Scy case AF_INET : 520254219Scy case AF_INET6 : 521254219Scy break; 522254219Scy default : 523254219Scy IPFERROR(120019); 524254219Scy return EINVAL; 525254219Scy } 526254219Scy 527254219Scy if (dest.fd_name < -1 || dest.fd_name > 128) { 528254219Scy IPFERROR(120018); 529254219Scy return EINVAL; 530254219Scy } 531254219Scy 532254219Scy KMALLOCS(node, ipf_dstnode_t *, sizeof(*node) + dest.fd_name); 533254219Scy if (node == NULL) { 534254219Scy softd->stats.ipls_nomem++; 535254219Scy IPFERROR(120008); 536254219Scy return ENOMEM; 537254219Scy } 538254219Scy bzero((char *)node, sizeof(*node) + dest.fd_name); 539254219Scy 540254219Scy bcopy(&dest, &node->ipfd_dest, sizeof(dest)); 541254219Scy node->ipfd_size = sizeof(*node) + dest.fd_name; 542254219Scy 543254219Scy if (dest.fd_name > 0) { 544254219Scy /* 545254219Scy * fd_name starts out as the length of the string to copy 546254219Scy * in (including \0) and ends up being the offset from 547254219Scy * fd_names (0). 548254219Scy */ 549254219Scy err = COPYIN((char *)op->iplo_struct + sizeof(dest), 550254219Scy node->ipfd_names, dest.fd_name); 551254219Scy if (err != 0) { 552254219Scy IPFERROR(120017); 553254219Scy KFREES(node, node->ipfd_size); 554254219Scy return EFAULT; 555254219Scy } 556254219Scy node->ipfd_dest.fd_name = 0; 557254219Scy } else { 558254219Scy node->ipfd_dest.fd_name = -1; 559254219Scy } 560254219Scy 561254219Scy if (d->ipld_nodes == d->ipld_maxnodes) { 562254219Scy KMALLOCS(nodes, ipf_dstnode_t **, 563254219Scy sizeof(*nodes) * (d->ipld_maxnodes + 1)); 564254219Scy if (nodes == NULL) { 565254219Scy softd->stats.ipls_nomem++; 566254219Scy IPFERROR(120022); 567254219Scy KFREES(node, node->ipfd_size); 568254219Scy return ENOMEM; 569254219Scy } 570254219Scy if (d->ipld_dests != NULL) { 571254219Scy bcopy(d->ipld_dests, nodes, 572254219Scy sizeof(*nodes) * d->ipld_maxnodes); 573254219Scy KFREES(d->ipld_dests, sizeof(*nodes) * d->ipld_nodes); 574254219Scy nodes[0]->ipfd_pnext = nodes; 575254219Scy } 576254219Scy d->ipld_dests = nodes; 577254219Scy d->ipld_maxnodes++; 578254219Scy } 579254219Scy d->ipld_dests[d->ipld_nodes] = node; 580254219Scy d->ipld_nodes++; 581254219Scy 582254219Scy if (d->ipld_nodes == 1) { 583254219Scy node->ipfd_pnext = d->ipld_dests; 584254219Scy } else if (d->ipld_nodes > 1) { 585254219Scy node->ipfd_pnext = &d->ipld_dests[d->ipld_nodes - 2]->ipfd_next; 586254219Scy } 587254219Scy *node->ipfd_pnext = node; 588254219Scy 589254219Scy MUTEX_INIT(&node->ipfd_lock, "ipf dst node lock"); 590254219Scy node->ipfd_uid = uid; 591254219Scy node->ipfd_ref = 1; 592254219Scy if (node->ipfd_dest.fd_name == 0) 593254219Scy (void) ipf_resolvedest(softc, node->ipfd_names, 594254219Scy &node->ipfd_dest, AF_INET); 595254219Scy#ifdef USE_INET6 596254219Scy if (node->ipfd_dest.fd_name == 0 && 597254219Scy node->ipfd_dest.fd_ptr == (void *)-1) 598254219Scy (void) ipf_resolvedest(softc, node->ipfd_names, 599254219Scy &node->ipfd_dest, AF_INET6); 600254219Scy#endif 601254219Scy 602254219Scy softd->stats.ipls_numnodes++; 603254219Scy 604254219Scy return 0; 605254219Scy} 606254219Scy 607254219Scy 608254219Scy/* ------------------------------------------------------------------------ */ 609254219Scy/* Function: ipf_dstlist_node_deref */ 610254219Scy/* Returns: int - 0 = success, else error */ 611254219Scy/* Parameters: arg(I) - pointer to local context to use */ 612254219Scy/* node(I) - pointer to destionation node to free */ 613254219Scy/* */ 614254219Scy/* Dereference the use count by one. If it drops to zero then we can assume */ 615254219Scy/* that it has been removed from any lists/tables and is ripe for freeing. */ 616254219Scy/* The pointer to context is required for the purpose of maintaining */ 617254219Scy/* statistics. */ 618254219Scy/* ------------------------------------------------------------------------ */ 619254219Scystatic int 620254219Scyipf_dstlist_node_deref(arg, node) 621254219Scy void *arg; 622254219Scy ipf_dstnode_t *node; 623254219Scy{ 624254219Scy ipf_dstl_softc_t *softd = arg; 625254219Scy int ref; 626254219Scy 627254219Scy MUTEX_ENTER(&node->ipfd_lock); 628254219Scy ref = --node->ipfd_ref; 629254219Scy MUTEX_EXIT(&node->ipfd_lock); 630254219Scy 631254219Scy if (ref > 0) 632254219Scy return 0; 633254219Scy 634254219Scy if ((node->ipfd_flags & IPDST_DELETE) != 0) 635254219Scy softd->stats.ipls_numderefnodes--; 636254219Scy MUTEX_DESTROY(&node->ipfd_lock); 637254219Scy KFREES(node, node->ipfd_size); 638254219Scy softd->stats.ipls_numnodes--; 639254219Scy 640254219Scy return 0; 641254219Scy} 642254219Scy 643254219Scy 644254219Scy/* ------------------------------------------------------------------------ */ 645254219Scy/* Function: ipf_dstlist_node_del */ 646254219Scy/* Returns: int - 0 = success, else error */ 647254219Scy/* Parameters: softc(I) - pointer to soft context main structure */ 648254219Scy/* arg(I) - pointer to local context to use */ 649254219Scy/* op(I) - pointer to lookup operation data */ 650254219Scy/* uid(I) - uid of process doing the ioctl */ 651254219Scy/* */ 652254219Scy/* Look for a matching destination node on the named table and free it if */ 653254219Scy/* found. Because the name embedded in the frdest_t is variable in length, */ 654254219Scy/* it is necessary to allocate some memory locally, to complete this op. */ 655254219Scy/* ------------------------------------------------------------------------ */ 656254219Scystatic int 657254219Scyipf_dstlist_node_del(softc, arg, op, uid) 658254219Scy ipf_main_softc_t *softc; 659254219Scy void *arg; 660254219Scy iplookupop_t *op; 661254219Scy int uid; 662254219Scy{ 663254219Scy ipf_dstl_softc_t *softd = arg; 664254219Scy ipf_dstnode_t *node; 665254219Scy frdest_t frd, *temp; 666254219Scy ippool_dst_t *d; 667254219Scy size_t size; 668254219Scy int err; 669254219Scy 670254219Scy d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name); 671254219Scy if (d == NULL) { 672254219Scy IPFERROR(120012); 673254219Scy return ESRCH; 674254219Scy } 675254219Scy 676254219Scy err = COPYIN(op->iplo_struct, &frd, sizeof(frd)); 677254219Scy if (err != 0) { 678254219Scy IPFERROR(120011); 679254219Scy return EFAULT; 680254219Scy } 681254219Scy 682254219Scy size = sizeof(*temp) + frd.fd_name; 683254219Scy KMALLOCS(temp, frdest_t *, size); 684254219Scy if (temp == NULL) { 685254219Scy softd->stats.ipls_nomem++; 686254219Scy IPFERROR(120026); 687254219Scy return ENOMEM; 688254219Scy } 689254219Scy 690254219Scy err = COPYIN(op->iplo_struct, temp, size); 691254219Scy if (err != 0) { 692254219Scy IPFERROR(120027); 693254219Scy return EFAULT; 694254219Scy } 695254219Scy 696254219Scy MUTEX_ENTER(&d->ipld_lock); 697254219Scy for (node = *d->ipld_dests; node != NULL; node = node->ipfd_next) { 698254219Scy if ((uid != 0) && (node->ipfd_uid != uid)) 699254219Scy continue; 700254219Scy if (node->ipfd_size != size) 701254219Scy continue; 702254219Scy if (!bcmp(&node->ipfd_dest.fd_ip6, &frd.fd_ip6, 703254219Scy size - offsetof(frdest_t, fd_ip6))) { 704254219Scy ipf_dstlist_node_free(softd, d, node); 705254219Scy MUTEX_EXIT(&d->ipld_lock); 706254219Scy KFREES(temp, size); 707254219Scy return 0; 708254219Scy } 709254219Scy } 710254219Scy MUTEX_EXIT(&d->ipld_lock); 711254219Scy KFREES(temp, size); 712254219Scy 713254219Scy return ESRCH; 714254219Scy} 715254219Scy 716254219Scy 717254219Scy/* ------------------------------------------------------------------------ */ 718254219Scy/* Function: ipf_dstlist_node_free */ 719254219Scy/* Returns: Nil */ 720254219Scy/* Parameters: softd(I) - pointer to the destination list context */ 721254219Scy/* d(I) - pointer to destination list */ 722254219Scy/* node(I) - pointer to node to free */ 723254219Scy/* Locks: MUTEX(ipld_lock) or WRITE(ipf_poolrw) */ 724254219Scy/* */ 725254219Scy/* Free the destination node by first removing it from any lists and then */ 726254219Scy/* checking if this was the last reference held to the object. While the */ 727254219Scy/* array of pointers to nodes is compacted, its size isn't reduced (by way */ 728254219Scy/* of allocating a new smaller one and copying) because the belief is that */ 729254219Scy/* it is likely the array will again reach that size. */ 730254219Scy/* ------------------------------------------------------------------------ */ 731254219Scystatic void 732254219Scyipf_dstlist_node_free(softd, d, node) 733254219Scy ipf_dstl_softc_t *softd; 734254219Scy ippool_dst_t *d; 735254219Scy ipf_dstnode_t *node; 736254219Scy{ 737254219Scy int i; 738254219Scy 739254219Scy /* 740254219Scy * Compact the array of pointers to nodes. 741254219Scy */ 742254219Scy for (i = 0; i < d->ipld_nodes; i++) 743254219Scy if (d->ipld_dests[i] == node) 744254219Scy break; 745254219Scy if (d->ipld_nodes - i > 1) { 746254219Scy bcopy(&d->ipld_dests[i + 1], &d->ipld_dests[i], 747254219Scy sizeof(*d->ipld_dests) * (d->ipld_nodes - i - 1)); 748254219Scy } 749254219Scy d->ipld_nodes--; 750254219Scy 751254219Scy if (node->ipfd_pnext != NULL) 752254219Scy *node->ipfd_pnext = node->ipfd_next; 753254219Scy if (node->ipfd_next != NULL) 754254219Scy node->ipfd_next->ipfd_pnext = node->ipfd_pnext; 755254219Scy node->ipfd_pnext = NULL; 756254219Scy node->ipfd_next = NULL; 757254219Scy 758254219Scy if ((node->ipfd_flags & IPDST_DELETE) == 0) { 759254219Scy softd->stats.ipls_numderefnodes++; 760254219Scy node->ipfd_flags |= IPDST_DELETE; 761254219Scy } 762254219Scy 763254219Scy ipf_dstlist_node_deref(softd, node); 764254219Scy} 765254219Scy 766254219Scy 767254219Scy/* ------------------------------------------------------------------------ */ 768254219Scy/* Function: ipf_dstlist_stats_get */ 769254219Scy/* Returns: int - 0 = success, else error */ 770254219Scy/* Parameters: softc(I) - pointer to soft context main structure */ 771254219Scy/* arg(I) - pointer to local context to use */ 772254219Scy/* op(I) - pointer to lookup operation data */ 773254219Scy/* */ 774254219Scy/* Return the current statistics for destination lists. This may be for all */ 775254219Scy/* of them or just information pertaining to a particular table. */ 776254219Scy/* ------------------------------------------------------------------------ */ 777254219Scy/*ARGSUSED*/ 778254219Scystatic int 779254219Scyipf_dstlist_stats_get(softc, arg, op) 780254219Scy ipf_main_softc_t *softc; 781254219Scy void *arg; 782254219Scy iplookupop_t *op; 783254219Scy{ 784254219Scy ipf_dstl_softc_t *softd = arg; 785254219Scy ipf_dstl_stat_t stats; 786254219Scy int unit, i, err = 0; 787254219Scy 788254219Scy if (op->iplo_size != sizeof(ipf_dstl_stat_t)) { 789254219Scy IPFERROR(120023); 790254219Scy return EINVAL; 791254219Scy } 792254219Scy 793254219Scy stats = softd->stats; 794254219Scy unit = op->iplo_unit; 795254219Scy if (unit == IPL_LOGALL) { 796254219Scy for (i = 0; i <= IPL_LOGMAX; i++) 797254219Scy stats.ipls_list[i] = softd->dstlist[i]; 798254219Scy } else if (unit >= 0 && unit <= IPL_LOGMAX) { 799254219Scy void *ptr; 800254219Scy 801254219Scy if (op->iplo_name[0] != '\0') 802254219Scy ptr = ipf_dstlist_table_find(softd, unit, 803254219Scy op->iplo_name); 804254219Scy else 805254219Scy ptr = softd->dstlist[unit + 1]; 806254219Scy stats.ipls_list[unit] = ptr; 807254219Scy } else { 808254219Scy IPFERROR(120024); 809254219Scy err = EINVAL; 810254219Scy } 811254219Scy 812254219Scy if (err == 0) { 813254219Scy err = COPYOUT(&stats, op->iplo_struct, sizeof(stats)); 814254219Scy if (err != 0) { 815254219Scy IPFERROR(120025); 816254219Scy return EFAULT; 817254219Scy } 818254219Scy } 819254219Scy return 0; 820254219Scy} 821254219Scy 822254219Scy 823254219Scy/* ------------------------------------------------------------------------ */ 824254219Scy/* Function: ipf_dstlist_table_add */ 825254219Scy/* Returns: int - 0 = success, else error */ 826254219Scy/* Parameters: softc(I) - pointer to soft context main structure */ 827254219Scy/* arg(I) - pointer to local context to use */ 828254219Scy/* op(I) - pointer to lookup operation data */ 829254219Scy/* */ 830254219Scy/* Add a new destination table to the list of those available for the given */ 831254219Scy/* device. Because we seldom operate on these objects (find/add/delete), */ 832254219Scy/* they are just kept in a simple linked list. */ 833254219Scy/* ------------------------------------------------------------------------ */ 834254219Scystatic int 835254219Scyipf_dstlist_table_add(softc, arg, op) 836254219Scy ipf_main_softc_t *softc; 837254219Scy void *arg; 838254219Scy iplookupop_t *op; 839254219Scy{ 840254219Scy ipf_dstl_softc_t *softd = arg; 841254219Scy ippool_dst_t user, *d, *new; 842254219Scy int unit, err; 843254219Scy 844254219Scy d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name); 845254219Scy if (d != NULL) { 846254219Scy IPFERROR(120013); 847254219Scy return EEXIST; 848254219Scy } 849254219Scy 850254219Scy err = COPYIN(op->iplo_struct, &user, sizeof(user)); 851254219Scy if (err != 0) { 852254219Scy IPFERROR(120021); 853254219Scy return EFAULT; 854254219Scy } 855254219Scy 856254219Scy KMALLOC(new, ippool_dst_t *); 857254219Scy if (new == NULL) { 858254219Scy softd->stats.ipls_nomem++; 859254219Scy IPFERROR(120014); 860254219Scy return ENOMEM; 861254219Scy } 862254219Scy bzero((char *)new, sizeof(*new)); 863254219Scy 864254219Scy MUTEX_INIT(&new->ipld_lock, "ipf dst table lock"); 865254219Scy 866254219Scy strncpy(new->ipld_name, op->iplo_name, FR_GROUPLEN); 867254219Scy unit = op->iplo_unit; 868254219Scy new->ipld_unit = unit; 869254219Scy new->ipld_policy = user.ipld_policy; 870254219Scy new->ipld_seed = ipf_random(); 871254219Scy new->ipld_ref = 1; 872254219Scy 873254219Scy new->ipld_pnext = softd->tails[unit + 1]; 874254219Scy *softd->tails[unit + 1] = new; 875254219Scy softd->tails[unit + 1] = &new->ipld_next; 876254219Scy softd->stats.ipls_numlists++; 877254219Scy 878254219Scy return 0; 879254219Scy} 880254219Scy 881254219Scy 882254219Scy/* ------------------------------------------------------------------------ */ 883254219Scy/* Function: ipf_dstlist_table_del */ 884254219Scy/* Returns: int - 0 = success, else error */ 885254219Scy/* Parameters: softc(I) - pointer to soft context main structure */ 886254219Scy/* arg(I) - pointer to local context to use */ 887254219Scy/* op(I) - pointer to lookup operation data */ 888254219Scy/* */ 889254219Scy/* Find a named destinstion list table and delete it. If there are other */ 890254219Scy/* references to it, the caller isn't told. */ 891254219Scy/* ------------------------------------------------------------------------ */ 892254219Scystatic int 893254219Scyipf_dstlist_table_del(softc, arg, op) 894254219Scy ipf_main_softc_t *softc; 895254219Scy void *arg; 896254219Scy iplookupop_t *op; 897254219Scy{ 898254219Scy ippool_dst_t *d; 899254219Scy 900254219Scy d = ipf_dstlist_table_find(arg, op->iplo_unit, op->iplo_name); 901254219Scy if (d == NULL) { 902254219Scy IPFERROR(120015); 903254219Scy return ESRCH; 904254219Scy } 905254219Scy 906254219Scy if (d->ipld_dests != NULL) { 907254219Scy IPFERROR(120016); 908254219Scy return EBUSY; 909254219Scy } 910254219Scy 911254219Scy ipf_dstlist_table_remove(softc, arg, d); 912254219Scy 913254219Scy return 0; 914254219Scy} 915254219Scy 916254219Scy 917254219Scy/* ------------------------------------------------------------------------ */ 918254219Scy/* Function: ipf_dstlist_table_remove */ 919254219Scy/* Returns: Nil */ 920254219Scy/* Parameters: softc(I) - pointer to soft context main structure */ 921254219Scy/* softd(I) - pointer to the destination list context */ 922254219Scy/* d(I) - pointer to destination list */ 923254219Scy/* */ 924254219Scy/* Remove a given destination list from existance. While the IPDST_DELETE */ 925254219Scy/* flag is set every time we call this function and the reference count is */ 926254219Scy/* non-zero, the "numdereflists" counter is always incremented because the */ 927254219Scy/* decision about whether it will be freed or not is not made here. This */ 928254219Scy/* means that the only action the code can take here is to treat it as if */ 929254219Scy/* it will become a detached. */ 930254219Scy/* ------------------------------------------------------------------------ */ 931254219Scystatic void 932254219Scyipf_dstlist_table_remove(softc, softd, d) 933254219Scy ipf_main_softc_t *softc; 934254219Scy ipf_dstl_softc_t *softd; 935254219Scy ippool_dst_t *d; 936254219Scy{ 937254219Scy 938254219Scy if (softd->tails[d->ipld_unit + 1] == &d->ipld_next) 939254219Scy softd->tails[d->ipld_unit + 1] = d->ipld_pnext; 940254219Scy 941254219Scy if (d->ipld_pnext != NULL) 942254219Scy *d->ipld_pnext = d->ipld_next; 943254219Scy if (d->ipld_next != NULL) 944254219Scy d->ipld_next->ipld_pnext = d->ipld_pnext; 945254219Scy d->ipld_pnext = NULL; 946254219Scy d->ipld_next = NULL; 947254219Scy 948254219Scy ipf_dstlist_table_clearnodes(softd, d); 949254219Scy 950254219Scy softd->stats.ipls_numdereflists++; 951254219Scy d->ipld_flags |= IPDST_DELETE; 952254219Scy 953254219Scy ipf_dstlist_table_deref(softc, softd, d); 954254219Scy} 955254219Scy 956254219Scy 957254219Scy/* ------------------------------------------------------------------------ */ 958254219Scy/* Function: ipf_dstlist_table_free */ 959254219Scy/* Returns: Nil */ 960254219Scy/* Parameters: softd(I) - pointer to the destination list context */ 961254219Scy/* d(I) - pointer to destination list */ 962254219Scy/* */ 963254219Scy/* Free up a destination list data structure and any other memory that was */ 964254219Scy/* directly allocated as part of creating it. Individual destination list */ 965254219Scy/* nodes are not freed. It is assumed the caller will have already emptied */ 966254219Scy/* the destination list. */ 967254219Scy/* ------------------------------------------------------------------------ */ 968254219Scystatic void 969254219Scyipf_dstlist_table_free(softd, d) 970254219Scy ipf_dstl_softc_t *softd; 971254219Scy ippool_dst_t *d; 972254219Scy{ 973254219Scy MUTEX_DESTROY(&d->ipld_lock); 974254219Scy 975254219Scy if ((d->ipld_flags & IPDST_DELETE) != 0) 976254219Scy softd->stats.ipls_numdereflists--; 977254219Scy softd->stats.ipls_numlists--; 978254219Scy 979254219Scy if (d->ipld_dests != NULL) { 980254219Scy KFREES(d->ipld_dests, 981254219Scy d->ipld_maxnodes * sizeof(*d->ipld_dests)); 982254219Scy } 983254219Scy 984254219Scy KFREE(d); 985254219Scy} 986254219Scy 987254219Scy 988254219Scy/* ------------------------------------------------------------------------ */ 989254219Scy/* Function: ipf_dstlist_table_deref */ 990254219Scy/* Returns: int - 0 = success, else error */ 991254219Scy/* Parameters: softc(I) - pointer to soft context main structure */ 992254219Scy/* arg(I) - pointer to local context to use */ 993254219Scy/* op(I) - pointer to lookup operation data */ 994254219Scy/* */ 995254219Scy/* Drops the reference count on a destination list table object and free's */ 996254219Scy/* it if 0 has been reached. */ 997254219Scy/* ------------------------------------------------------------------------ */ 998254219Scystatic int 999254219Scyipf_dstlist_table_deref(softc, arg, table) 1000254219Scy ipf_main_softc_t *softc; 1001254219Scy void *arg; 1002254219Scy void *table; 1003254219Scy{ 1004254219Scy ippool_dst_t *d = table; 1005254219Scy 1006254219Scy d->ipld_ref--; 1007254219Scy if (d->ipld_ref > 0) 1008254219Scy return d->ipld_ref; 1009254219Scy 1010254219Scy ipf_dstlist_table_free(arg, d); 1011254219Scy 1012254219Scy return 0; 1013254219Scy} 1014254219Scy 1015254219Scy 1016254219Scy/* ------------------------------------------------------------------------ */ 1017254219Scy/* Function: ipf_dstlist_table_clearnodes */ 1018254219Scy/* Returns: Nil */ 1019254219Scy/* Parameters: softd(I) - pointer to the destination list context */ 1020254219Scy/* dst(I) - pointer to destination list */ 1021254219Scy/* */ 1022254219Scy/* Free all of the destination nodes attached to the given table. */ 1023254219Scy/* ------------------------------------------------------------------------ */ 1024254219Scystatic void 1025254219Scyipf_dstlist_table_clearnodes(softd, dst) 1026254219Scy ipf_dstl_softc_t *softd; 1027254219Scy ippool_dst_t *dst; 1028254219Scy{ 1029254219Scy ipf_dstnode_t *node; 1030254219Scy 1031254219Scy if (dst->ipld_dests == NULL) 1032254219Scy return; 1033254219Scy 1034254219Scy while ((node = *dst->ipld_dests) != NULL) { 1035254219Scy ipf_dstlist_node_free(softd, dst, node); 1036254219Scy } 1037254219Scy} 1038254219Scy 1039254219Scy 1040254219Scy/* ------------------------------------------------------------------------ */ 1041254219Scy/* Function: ipf_dstlist_table_find */ 1042254219Scy/* Returns: int - 0 = success, else error */ 1043254219Scy/* Parameters: arg(I) - pointer to local context to use */ 1044254219Scy/* unit(I) - device we are working with */ 1045254219Scy/* name(I) - destination table name to find */ 1046254219Scy/* */ 1047254219Scy/* Return a pointer to a destination table that matches the unit+name that */ 1048254219Scy/* is passed in. */ 1049254219Scy/* ------------------------------------------------------------------------ */ 1050254219Scystatic void * 1051254219Scyipf_dstlist_table_find(arg, unit, name) 1052254219Scy void *arg; 1053254219Scy int unit; 1054254219Scy char *name; 1055254219Scy{ 1056254219Scy ipf_dstl_softc_t *softd = arg; 1057254219Scy ippool_dst_t *d; 1058254219Scy 1059254219Scy for (d = softd->dstlist[unit + 1]; d != NULL; d = d->ipld_next) { 1060254219Scy if ((d->ipld_unit == unit) && 1061254219Scy !strncmp(d->ipld_name, name, FR_GROUPLEN)) { 1062254219Scy return d; 1063254219Scy } 1064254219Scy } 1065254219Scy 1066254219Scy return NULL; 1067254219Scy} 1068254219Scy 1069254219Scy 1070254219Scy/* ------------------------------------------------------------------------ */ 1071254219Scy/* Function: ipf_dstlist_select_ref */ 1072254219Scy/* Returns: void * - NULL = failure, else pointer to table */ 1073254219Scy/* Parameters: arg(I) - pointer to local context to use */ 1074254219Scy/* unit(I) - device we are working with */ 1075254219Scy/* name(I) - destination table name to find */ 1076254219Scy/* */ 1077254219Scy/* Attempt to find a destination table that matches the name passed in and */ 1078254219Scy/* if successful, bump up the reference count on it because we intend to */ 1079254219Scy/* store the pointer to it somewhere else. */ 1080254219Scy/* ------------------------------------------------------------------------ */ 1081254219Scystatic void * 1082254219Scyipf_dstlist_select_ref(arg, unit, name) 1083254219Scy void *arg; 1084254219Scy int unit; 1085254219Scy char *name; 1086254219Scy{ 1087254219Scy ippool_dst_t *d; 1088254219Scy 1089254219Scy d = ipf_dstlist_table_find(arg, unit, name); 1090254219Scy if (d != NULL) { 1091254219Scy MUTEX_ENTER(&d->ipld_lock); 1092254219Scy d->ipld_ref++; 1093254219Scy MUTEX_EXIT(&d->ipld_lock); 1094254219Scy } 1095254219Scy return d; 1096254219Scy} 1097254219Scy 1098254219Scy 1099254219Scy/* ------------------------------------------------------------------------ */ 1100254219Scy/* Function: ipf_dstlist_select */ 1101254219Scy/* Returns: void * - NULL = failure, else pointer to table */ 1102254219Scy/* Parameters: fin(I) - pointer to packet information */ 1103254219Scy/* d(I) - pointer to destination list */ 1104254219Scy/* */ 1105254219Scy/* Find the next node in the destination list to be used according to the */ 1106254219Scy/* defined policy. Of these, "connection" is the most expensive policy to */ 1107254219Scy/* implement as it always looks for the node with the least number of */ 1108254219Scy/* connections associated with it. */ 1109254219Scy/* */ 1110254219Scy/* The hashes exclude the port numbers so that all protocols map to the */ 1111254219Scy/* same destination. Otherwise, someone doing a ping would target a */ 1112254219Scy/* different server than their TCP connection, etc. MD-5 is used to */ 1113254219Scy/* transform the addressese into something random that the other end could */ 1114254219Scy/* not easily guess and use in an attack. ipld_seed introduces an unknown */ 1115254219Scy/* into the hash calculation to increase the difficult of an attacker */ 1116254219Scy/* guessing the bucket. */ 1117254219Scy/* */ 1118254219Scy/* One final comment: mixing different address families in a single pool */ 1119254219Scy/* will currently result in failures as the address family of the node is */ 1120254219Scy/* only matched up with that in the packet as the last step. While this can */ 1121254219Scy/* be coded around for the weighted connection and round-robin models, it */ 1122254219Scy/* cannot be supported for the hash/random models as they do not search and */ 1123254219Scy/* nor is the algorithm conducive to searching. */ 1124254219Scy/* ------------------------------------------------------------------------ */ 1125254219Scystatic ipf_dstnode_t * 1126254219Scyipf_dstlist_select(fin, d) 1127254219Scy fr_info_t *fin; 1128254219Scy ippool_dst_t *d; 1129254219Scy{ 1130254219Scy ipf_dstnode_t *node, *sel; 1131254219Scy int connects; 1132254219Scy u_32_t hash[4]; 1133254219Scy MD5_CTX ctx; 1134254219Scy int family; 1135254219Scy int x; 1136254219Scy 1137254219Scy if (d->ipld_dests == NULL || *d->ipld_dests == NULL) 1138254219Scy return NULL; 1139254219Scy 1140254219Scy family = fin->fin_family; 1141254219Scy 1142254219Scy MUTEX_ENTER(&d->ipld_lock); 1143254219Scy 1144254219Scy switch (d->ipld_policy) 1145254219Scy { 1146254219Scy case IPLDP_ROUNDROBIN: 1147254219Scy sel = d->ipld_selected; 1148254219Scy if (sel == NULL) { 1149254219Scy sel = *d->ipld_dests; 1150254219Scy } else { 1151254219Scy sel = sel->ipfd_next; 1152254219Scy if (sel == NULL) 1153254219Scy sel = *d->ipld_dests; 1154254219Scy } 1155254219Scy break; 1156254219Scy 1157254219Scy case IPLDP_CONNECTION: 1158254219Scy if (d->ipld_selected == NULL) { 1159254219Scy sel = *d->ipld_dests; 1160254219Scy break; 1161254219Scy } 1162254219Scy 1163254219Scy sel = d->ipld_selected; 1164254219Scy connects = 0x7fffffff; 1165254219Scy node = sel->ipfd_next; 1166254219Scy if (node == NULL) 1167254219Scy node = *d->ipld_dests; 1168254219Scy while (node != d->ipld_selected) { 1169254219Scy if (node->ipfd_states == 0) { 1170254219Scy sel = node; 1171254219Scy break; 1172254219Scy } 1173254219Scy if (node->ipfd_states < connects) { 1174254219Scy sel = node; 1175254219Scy connects = node->ipfd_states; 1176254219Scy } 1177254219Scy node = node->ipfd_next; 1178254219Scy if (node == NULL) 1179254219Scy node = *d->ipld_dests; 1180254219Scy } 1181254219Scy break; 1182254219Scy 1183254219Scy case IPLDP_RANDOM : 1184254219Scy x = ipf_random() % d->ipld_nodes; 1185254219Scy sel = d->ipld_dests[x]; 1186254219Scy break; 1187254219Scy 1188254219Scy case IPLDP_HASHED : 1189254219Scy MD5Init(&ctx); 1190254219Scy MD5Update(&ctx, (u_char *)&d->ipld_seed, sizeof(d->ipld_seed)); 1191254219Scy MD5Update(&ctx, (u_char *)&fin->fin_src6, 1192254219Scy sizeof(fin->fin_src6)); 1193254219Scy MD5Update(&ctx, (u_char *)&fin->fin_dst6, 1194254219Scy sizeof(fin->fin_dst6)); 1195254219Scy MD5Final((u_char *)hash, &ctx); 1196254219Scy x = hash[0] % d->ipld_nodes; 1197254219Scy sel = d->ipld_dests[x]; 1198254219Scy break; 1199254219Scy 1200254219Scy case IPLDP_SRCHASH : 1201254219Scy MD5Init(&ctx); 1202254219Scy MD5Update(&ctx, (u_char *)&d->ipld_seed, sizeof(d->ipld_seed)); 1203254219Scy MD5Update(&ctx, (u_char *)&fin->fin_src6, 1204254219Scy sizeof(fin->fin_src6)); 1205254219Scy MD5Final((u_char *)hash, &ctx); 1206254219Scy x = hash[0] % d->ipld_nodes; 1207254219Scy sel = d->ipld_dests[x]; 1208254219Scy break; 1209254219Scy 1210254219Scy case IPLDP_DSTHASH : 1211254219Scy MD5Init(&ctx); 1212254219Scy MD5Update(&ctx, (u_char *)&d->ipld_seed, sizeof(d->ipld_seed)); 1213254219Scy MD5Update(&ctx, (u_char *)&fin->fin_dst6, 1214254219Scy sizeof(fin->fin_dst6)); 1215254219Scy MD5Final((u_char *)hash, &ctx); 1216254219Scy x = hash[0] % d->ipld_nodes; 1217254219Scy sel = d->ipld_dests[x]; 1218254219Scy break; 1219254219Scy 1220254219Scy default : 1221254219Scy sel = NULL; 1222254219Scy break; 1223254219Scy } 1224254219Scy 1225254219Scy if (sel->ipfd_dest.fd_addr.adf_family != family) 1226254219Scy sel = NULL; 1227254219Scy d->ipld_selected = sel; 1228254219Scy 1229254219Scy MUTEX_EXIT(&d->ipld_lock); 1230254219Scy 1231254219Scy return sel; 1232254219Scy} 1233254219Scy 1234254219Scy 1235254219Scy/* ------------------------------------------------------------------------ */ 1236254219Scy/* Function: ipf_dstlist_select_node */ 1237254219Scy/* Returns: int - -1 == failure, 0 == success */ 1238254219Scy/* Parameters: fin(I) - pointer to packet information */ 1239254219Scy/* group(I) - destination pool to search */ 1240254219Scy/* addr(I) - pointer to store selected address */ 1241254219Scy/* pfdp(O) - pointer to storage for selected destination node */ 1242254219Scy/* */ 1243254219Scy/* This function is only responsible for obtaining the next IP address for */ 1244254219Scy/* use and storing it in the caller's address space (addr). "addr" is only */ 1245254219Scy/* used for storage if pfdp is NULL. No permanent reference is currently */ 1246254219Scy/* kept on the node. */ 1247254219Scy/* ------------------------------------------------------------------------ */ 1248254219Scyint 1249254219Scyipf_dstlist_select_node(fin, group, addr, pfdp) 1250254219Scy fr_info_t *fin; 1251254219Scy void *group; 1252254219Scy u_32_t *addr; 1253254219Scy frdest_t *pfdp; 1254254219Scy{ 1255254219Scy#ifdef USE_MUTEXES 1256254219Scy ipf_main_softc_t *softc = fin->fin_main_soft; 1257254219Scy#endif 1258254219Scy ippool_dst_t *d = group; 1259254219Scy ipf_dstnode_t *node; 1260254219Scy frdest_t *fdp; 1261254219Scy 1262254219Scy READ_ENTER(&softc->ipf_poolrw); 1263254219Scy 1264254219Scy node = ipf_dstlist_select(fin, d); 1265254219Scy if (node == NULL) { 1266254219Scy RWLOCK_EXIT(&softc->ipf_poolrw); 1267254219Scy return -1; 1268254219Scy } 1269254219Scy 1270254219Scy if (pfdp != NULL) { 1271254219Scy bcopy(&node->ipfd_dest, pfdp, sizeof(*pfdp)); 1272254219Scy } else { 1273254219Scy if (fin->fin_family == AF_INET) { 1274254219Scy addr[0] = node->ipfd_dest.fd_addr.adf_addr.i6[0]; 1275254219Scy } else if (fin->fin_family == AF_INET6) { 1276254219Scy addr[0] = node->ipfd_dest.fd_addr.adf_addr.i6[0]; 1277254219Scy addr[1] = node->ipfd_dest.fd_addr.adf_addr.i6[1]; 1278254219Scy addr[2] = node->ipfd_dest.fd_addr.adf_addr.i6[2]; 1279254219Scy addr[3] = node->ipfd_dest.fd_addr.adf_addr.i6[3]; 1280254219Scy } 1281254219Scy } 1282254219Scy 1283254219Scy fdp = &node->ipfd_dest; 1284254219Scy if (fdp->fd_ptr == NULL) 1285254219Scy fdp->fd_ptr = fin->fin_ifp; 1286254219Scy 1287254219Scy MUTEX_ENTER(&node->ipfd_lock); 1288254219Scy node->ipfd_states++; 1289254219Scy MUTEX_EXIT(&node->ipfd_lock); 1290254219Scy 1291254219Scy RWLOCK_EXIT(&softc->ipf_poolrw); 1292254219Scy 1293254219Scy return 0; 1294254219Scy} 1295254219Scy 1296254219Scy 1297254219Scy/* ------------------------------------------------------------------------ */ 1298254219Scy/* Function: ipf_dstlist_expire */ 1299254219Scy/* Returns: Nil */ 1300254219Scy/* Parameters: softc(I) - pointer to soft context main structure */ 1301254219Scy/* arg(I) - pointer to local context to use */ 1302254219Scy/* */ 1303254219Scy/* There are currently no objects to expire in destination lists. */ 1304254219Scy/* ------------------------------------------------------------------------ */ 1305254219Scystatic void 1306254219Scyipf_dstlist_expire(softc, arg) 1307254219Scy ipf_main_softc_t *softc; 1308254219Scy void *arg; 1309254219Scy{ 1310254219Scy return; 1311254219Scy} 1312254219Scy 1313254219Scy 1314254219Scy/* ------------------------------------------------------------------------ */ 1315254219Scy/* Function: ipf_dstlist_sync */ 1316254219Scy/* Returns: Nil */ 1317254219Scy/* Parameters: softc(I) - pointer to soft context main structure */ 1318254219Scy/* arg(I) - pointer to local context to use */ 1319254219Scy/* */ 1320254219Scy/* When a network interface appears or disappears, we need to revalidate */ 1321254219Scy/* all of the network interface names that have been configured as a target */ 1322254219Scy/* in a destination list. */ 1323254219Scy/* ------------------------------------------------------------------------ */ 1324254219Scyvoid 1325254219Scyipf_dstlist_sync(softc, arg) 1326254219Scy ipf_main_softc_t *softc; 1327254219Scy void *arg; 1328254219Scy{ 1329254219Scy ipf_dstl_softc_t *softd = arg; 1330254219Scy ipf_dstnode_t *node; 1331254219Scy ippool_dst_t *list; 1332254219Scy int i; 1333254219Scy int j; 1334254219Scy 1335254219Scy for (i = 0; i < IPL_LOGMAX; i++) { 1336254219Scy for (list = softd->dstlist[i]; list != NULL; 1337254219Scy list = list->ipld_next) { 1338254219Scy for (j = 0; j < list->ipld_maxnodes; j++) { 1339254219Scy node = list->ipld_dests[j]; 1340254219Scy if (node == NULL) 1341254219Scy continue; 1342254219Scy if (node->ipfd_dest.fd_name == -1) 1343254219Scy continue; 1344254219Scy (void) ipf_resolvedest(softc, 1345254219Scy node->ipfd_names, 1346254219Scy &node->ipfd_dest, 1347254219Scy AF_INET); 1348254219Scy } 1349254219Scy } 1350254219Scy } 1351254219Scy} 1352