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