ip_htable.c revision 170268
1/* $FreeBSD: head/sys/contrib/ipfilter/netinet/ip_htable.c 170268 2007-06-04 02:54:36Z darrenr $ */ 2 3/* 4 * Copyright (C) 1993-2001, 2003 by Darren Reed. 5 * 6 * See the IPFILTER.LICENCE file for details on licencing. 7 */ 8#if defined(KERNEL) || defined(_KERNEL) 9# undef KERNEL 10# undef _KERNEL 11# define KERNEL 1 12# define _KERNEL 1 13#endif 14#include <sys/param.h> 15#include <sys/types.h> 16#include <sys/errno.h> 17#include <sys/time.h> 18#include <sys/file.h> 19#if !defined(_KERNEL) 20# include <stdlib.h> 21# include <string.h> 22# define _KERNEL 23# ifdef __OpenBSD__ 24struct file; 25# endif 26# include <sys/uio.h> 27# undef _KERNEL 28#endif 29#include <sys/socket.h> 30#if defined(__FreeBSD_version) && (__FreeBSD_version >= 300000) 31# include <sys/malloc.h> 32#endif 33#if defined(__FreeBSD__) 34# include <sys/cdefs.h> 35# include <sys/proc.h> 36#endif 37#if !defined(__svr4__) && !defined(__SVR4) && !defined(__hpux) && \ 38 !defined(linux) 39# include <sys/mbuf.h> 40#endif 41#if defined(_KERNEL) 42# include <sys/systm.h> 43#else 44# include <stdio.h> 45#endif 46#include <netinet/in.h> 47#include <net/if.h> 48 49#include "netinet/ip_compat.h" 50#include "netinet/ip_fil.h" 51#include "netinet/ip_lookup.h" 52#include "netinet/ip_htable.h" 53/* END OF INCLUDES */ 54 55#if !defined(lint) 56static const char rcsid[] = "@(#)$Id: ip_htable.c,v 2.34.2.9 2007/02/02 23:06:16 darrenr Exp $"; 57#endif 58 59#ifdef IPFILTER_LOOKUP 60static iphtent_t *fr_iphmfind __P((iphtable_t *, struct in_addr *)); 61static u_long ipht_nomem[IPL_LOGSIZE] = { 0, 0, 0, 0, 0, 0, 0, 0 }; 62static u_long ipf_nhtables[IPL_LOGSIZE] = { 0, 0, 0, 0, 0, 0, 0, 0 }; 63static u_long ipf_nhtnodes[IPL_LOGSIZE] = { 0, 0, 0, 0, 0, 0, 0, 0 }; 64 65iphtable_t *ipf_htables[IPL_LOGSIZE] = { NULL, NULL, NULL, NULL, 66 NULL, NULL, NULL, NULL }; 67 68 69void fr_htable_unload() 70{ 71 iplookupflush_t fop; 72 73 fop.iplf_unit = IPL_LOGALL; 74 (void)fr_flushhtable(&fop); 75} 76 77 78int fr_gethtablestat(op) 79iplookupop_t *op; 80{ 81 iphtstat_t stats; 82 83 if (op->iplo_size != sizeof(stats)) 84 return EINVAL; 85 86 stats.iphs_tables = ipf_htables[op->iplo_unit]; 87 stats.iphs_numtables = ipf_nhtables[op->iplo_unit]; 88 stats.iphs_numnodes = ipf_nhtnodes[op->iplo_unit]; 89 stats.iphs_nomem = ipht_nomem[op->iplo_unit]; 90 91 return COPYOUT(&stats, op->iplo_struct, sizeof(stats)); 92 93} 94 95 96/* 97 * Create a new hash table using the template passed. 98 */ 99int fr_newhtable(op) 100iplookupop_t *op; 101{ 102 iphtable_t *iph, *oiph; 103 char name[FR_GROUPLEN]; 104 int err, i, unit; 105 106 unit = op->iplo_unit; 107 if ((op->iplo_arg & IPHASH_ANON) == 0) 108 iph = fr_existshtable(unit, op->iplo_name); 109 else 110 iph = NULL; 111 112 if (iph == NULL) { 113 KMALLOC(iph, iphtable_t *); 114 if (iph == NULL) { 115 ipht_nomem[op->iplo_unit]++; 116 return ENOMEM; 117 } 118 err = COPYIN(op->iplo_struct, iph, sizeof(*iph)); 119 if (err != 0) { 120 KFREE(iph); 121 return EFAULT; 122 } 123 } else { 124 if ((iph->iph_flags & IPHASH_DELETE) == 0) 125 return EEXIST; 126 } 127 128 if (iph->iph_unit != unit) { 129 if ((iph->iph_flags & IPHASH_DELETE) == 0) { 130 KFREE(iph); 131 } 132 return EINVAL; 133 } 134 135 if ((op->iplo_arg & IPHASH_ANON) != 0) { 136 i = IPHASH_ANON; 137 do { 138 i++; 139#if defined(SNPRINTF) && defined(_KERNEL) 140 SNPRINTF(name, sizeof(name), "%u", i); 141#else 142 (void)sprintf(name, "%u", i); 143#endif 144 for (oiph = ipf_htables[unit]; oiph != NULL; 145 oiph = oiph->iph_next) 146 if (strncmp(oiph->iph_name, name, 147 sizeof(oiph->iph_name)) == 0) 148 break; 149 } while (oiph != NULL); 150 151 (void)strncpy(iph->iph_name, name, sizeof(iph->iph_name)); 152 (void)strncpy(op->iplo_name, name, sizeof(op->iplo_name)); 153 iph->iph_type |= IPHASH_ANON; 154 } 155 156 if ((iph->iph_flags & IPHASH_DELETE) == 0) { 157 KMALLOCS(iph->iph_table, iphtent_t **, 158 iph->iph_size * sizeof(*iph->iph_table)); 159 if (iph->iph_table == NULL) { 160 if ((iph->iph_flags & IPHASH_DELETE) == 0) { 161 KFREE(iph); 162 } 163 ipht_nomem[unit]++; 164 return ENOMEM; 165 } 166 167 bzero((char *)iph->iph_table, 168 iph->iph_size * sizeof(*iph->iph_table)); 169 iph->iph_masks = 0; 170 iph->iph_list = NULL; 171 172 iph->iph_ref = 1; 173 iph->iph_next = ipf_htables[unit]; 174 iph->iph_pnext = &ipf_htables[unit]; 175 if (ipf_htables[unit] != NULL) 176 ipf_htables[unit]->iph_pnext = &iph->iph_next; 177 ipf_htables[unit] = iph; 178 179 ipf_nhtables[unit]++; 180 } 181 182 iph->iph_flags &= ~IPHASH_DELETE; 183 184 return 0; 185} 186 187 188/* 189 */ 190int fr_removehtable(unit, name) 191int unit; 192char *name; 193{ 194 iphtable_t *iph; 195 196 iph = fr_findhtable(unit, name); 197 if (iph == NULL) 198 return ESRCH; 199 200 if (iph->iph_unit != unit) { 201 return EINVAL; 202 } 203 204 if (iph->iph_ref != 0) { 205 (void) fr_clearhtable(iph); 206 iph->iph_flags |= IPHASH_DELETE; 207 return 0; 208 } 209 210 fr_delhtable(iph); 211 212 return 0; 213} 214 215 216int fr_clearhtable(iph) 217iphtable_t *iph; 218{ 219 iphtent_t *ipe; 220 221 while ((ipe = iph->iph_list) != NULL) 222 if (fr_delhtent(iph, ipe) != 0) 223 return 1; 224 return 0; 225} 226 227 228int fr_delhtable(iph) 229iphtable_t *iph; 230{ 231 232 if (fr_clearhtable(iph) != 0) 233 return 1; 234 235 if (iph->iph_pnext != NULL) 236 *iph->iph_pnext = iph->iph_next; 237 if (iph->iph_next != NULL) 238 iph->iph_next->iph_pnext = iph->iph_pnext; 239 240 ipf_nhtables[iph->iph_unit]--; 241 242 return fr_derefhtable(iph); 243} 244 245 246/* 247 * Delete an entry from a hash table. 248 */ 249int fr_delhtent(iph, ipe) 250iphtable_t *iph; 251iphtent_t *ipe; 252{ 253 254 if (ipe->ipe_phnext != NULL) 255 *ipe->ipe_phnext = ipe->ipe_hnext; 256 if (ipe->ipe_hnext != NULL) 257 ipe->ipe_hnext->ipe_phnext = ipe->ipe_phnext; 258 259 if (ipe->ipe_pnext != NULL) 260 *ipe->ipe_pnext = ipe->ipe_next; 261 if (ipe->ipe_next != NULL) 262 ipe->ipe_next->ipe_pnext = ipe->ipe_pnext; 263 264 switch (iph->iph_type & ~IPHASH_ANON) 265 { 266 case IPHASH_GROUPMAP : 267 if (ipe->ipe_group != NULL) 268 fr_delgroup(ipe->ipe_group, IPL_LOGIPF, fr_active); 269 break; 270 271 default : 272 ipe->ipe_ptr = NULL; 273 ipe->ipe_value = 0; 274 break; 275 } 276 277 return fr_derefhtent(ipe); 278} 279 280 281int fr_derefhtable(iph) 282iphtable_t *iph; 283{ 284 int refs; 285 286 iph->iph_ref--; 287 refs = iph->iph_ref; 288 289 if (iph->iph_ref == 0) { 290 KFREES(iph->iph_table, iph->iph_size * sizeof(*iph->iph_table)); 291 KFREE(iph); 292 } 293 294 return refs; 295} 296 297 298int fr_derefhtent(ipe) 299iphtent_t *ipe; 300{ 301 302 ipe->ipe_ref--; 303 if (ipe->ipe_ref == 0) { 304 ipf_nhtnodes[ipe->ipe_unit]--; 305 306 KFREE(ipe); 307 308 return 0; 309 } 310 311 return ipe->ipe_ref; 312} 313 314 315iphtable_t *fr_existshtable(unit, name) 316int unit; 317char *name; 318{ 319 iphtable_t *iph; 320 321 for (iph = ipf_htables[unit]; iph != NULL; iph = iph->iph_next) 322 if (strncmp(iph->iph_name, name, sizeof(iph->iph_name)) == 0) 323 break; 324 return iph; 325} 326 327 328iphtable_t *fr_findhtable(unit, name) 329int unit; 330char *name; 331{ 332 iphtable_t *iph; 333 334 iph = fr_existshtable(unit, name); 335 if ((iph != NULL) && (iph->iph_flags & IPHASH_DELETE) == 0) 336 return iph; 337 338 return NULL; 339} 340 341 342size_t fr_flushhtable(op) 343iplookupflush_t *op; 344{ 345 iphtable_t *iph; 346 size_t freed; 347 int i; 348 349 freed = 0; 350 351 for (i = 0; i <= IPL_LOGMAX; i++) { 352 if (op->iplf_unit == i || op->iplf_unit == IPL_LOGALL) { 353 while ((iph = ipf_htables[i]) != NULL) { 354 if (fr_delhtable(iph) == 0) { 355 freed++; 356 } else { 357 iph->iph_flags |= IPHASH_DELETE; 358 } 359 } 360 } 361 } 362 363 return freed; 364} 365 366 367/* 368 * Add an entry to a hash table. 369 */ 370int fr_addhtent(iph, ipeo) 371iphtable_t *iph; 372iphtent_t *ipeo; 373{ 374 iphtent_t *ipe; 375 u_int hv; 376 int bits; 377 378 KMALLOC(ipe, iphtent_t *); 379 if (ipe == NULL) 380 return -1; 381 382 bcopy((char *)ipeo, (char *)ipe, sizeof(*ipe)); 383 ipe->ipe_addr.in4_addr &= ipe->ipe_mask.in4_addr; 384 ipe->ipe_addr.in4_addr = ntohl(ipe->ipe_addr.in4_addr); 385 bits = count4bits(ipe->ipe_mask.in4_addr); 386 ipe->ipe_mask.in4_addr = ntohl(ipe->ipe_mask.in4_addr); 387 388 hv = IPE_HASH_FN(ipe->ipe_addr.in4_addr, ipe->ipe_mask.in4_addr, 389 iph->iph_size); 390 ipe->ipe_ref = 1; 391 ipe->ipe_hnext = iph->iph_table[hv]; 392 ipe->ipe_phnext = iph->iph_table + hv; 393 394 if (iph->iph_table[hv] != NULL) 395 iph->iph_table[hv]->ipe_phnext = &ipe->ipe_hnext; 396 iph->iph_table[hv] = ipe; 397 398 ipe->ipe_next = iph->iph_list; 399 ipe->ipe_pnext = &iph->iph_list; 400 if (ipe->ipe_next != NULL) 401 ipe->ipe_next->ipe_pnext = &ipe->ipe_next; 402 iph->iph_list = ipe; 403 404 if ((bits >= 0) && (bits != 32)) 405 iph->iph_masks |= 1 << bits; 406 407 switch (iph->iph_type & ~IPHASH_ANON) 408 { 409 case IPHASH_GROUPMAP : 410 ipe->ipe_ptr = fr_addgroup(ipe->ipe_group, NULL, 411 iph->iph_flags, IPL_LOGIPF, 412 fr_active); 413 break; 414 415 default : 416 ipe->ipe_ptr = NULL; 417 ipe->ipe_value = 0; 418 break; 419 } 420 421 ipe->ipe_unit = iph->iph_unit; 422 ipf_nhtnodes[ipe->ipe_unit]++; 423 424 return 0; 425} 426 427 428void *fr_iphmfindgroup(tptr, aptr) 429void *tptr, *aptr; 430{ 431 struct in_addr *addr; 432 iphtable_t *iph; 433 iphtent_t *ipe; 434 void *rval; 435 436 READ_ENTER(&ip_poolrw); 437 iph = tptr; 438 addr = aptr; 439 440 ipe = fr_iphmfind(iph, addr); 441 if (ipe != NULL) 442 rval = ipe->ipe_ptr; 443 else 444 rval = NULL; 445 RWLOCK_EXIT(&ip_poolrw); 446 return rval; 447} 448 449 450/* ------------------------------------------------------------------------ */ 451/* Function: fr_iphmfindip */ 452/* Returns: int - 0 == +ve match, -1 == error, 1 == -ve/no match */ 453/* Parameters: tptr(I) - pointer to the pool to search */ 454/* ipversion(I) - IP protocol version (4 or 6) */ 455/* aptr(I) - pointer to address information */ 456/* */ 457/* Search the hash table for a given address and return a search result. */ 458/* ------------------------------------------------------------------------ */ 459int fr_iphmfindip(tptr, ipversion, aptr) 460void *tptr, *aptr; 461int ipversion; 462{ 463 struct in_addr *addr; 464 iphtable_t *iph; 465 iphtent_t *ipe; 466 int rval; 467 468 if (ipversion != 4) 469 return -1; 470 471 if (tptr == NULL || aptr == NULL) 472 return -1; 473 474 iph = tptr; 475 addr = aptr; 476 477 READ_ENTER(&ip_poolrw); 478 ipe = fr_iphmfind(iph, addr); 479 if (ipe != NULL) 480 rval = 0; 481 else 482 rval = 1; 483 RWLOCK_EXIT(&ip_poolrw); 484 return rval; 485} 486 487 488/* Locks: ip_poolrw */ 489static iphtent_t *fr_iphmfind(iph, addr) 490iphtable_t *iph; 491struct in_addr *addr; 492{ 493 u_32_t hmsk, msk, ips; 494 iphtent_t *ipe; 495 u_int hv; 496 497 hmsk = iph->iph_masks; 498 msk = 0xffffffff; 499maskloop: 500 ips = ntohl(addr->s_addr) & msk; 501 hv = IPE_HASH_FN(ips, msk, iph->iph_size); 502 for (ipe = iph->iph_table[hv]; (ipe != NULL); ipe = ipe->ipe_hnext) { 503 if (ipe->ipe_mask.in4_addr != msk || 504 ipe->ipe_addr.in4_addr != ips) { 505 continue; 506 } 507 break; 508 } 509 510 if ((ipe == NULL) && (hmsk != 0)) { 511 while (hmsk != 0) { 512 msk <<= 1; 513 if (hmsk & 0x80000000) 514 break; 515 hmsk <<= 1; 516 } 517 if (hmsk != 0) { 518 hmsk <<= 1; 519 goto maskloop; 520 } 521 } 522 return ipe; 523} 524 525 526int fr_htable_getnext(token, ilp) 527ipftoken_t *token; 528ipflookupiter_t *ilp; 529{ 530 iphtent_t *node, zn, *nextnode; 531 iphtable_t *iph, zp, *nextiph; 532 int err; 533 534 err = 0; 535 iph = NULL; 536 node = NULL; 537 nextiph = NULL; 538 nextnode = NULL; 539 540 READ_ENTER(&ip_poolrw); 541 542 switch (ilp->ili_otype) 543 { 544 case IPFLOOKUPITER_LIST : 545 iph = token->ipt_data; 546 if (iph == NULL) { 547 nextiph = ipf_htables[(int)ilp->ili_unit]; 548 } else { 549 nextiph = iph->iph_next; 550 } 551 552 if (nextiph != NULL) { 553 ATOMIC_INC(nextiph->iph_ref); 554 if (nextiph->iph_next == NULL) 555 token->ipt_alive = 0; 556 } else { 557 bzero((char *)&zp, sizeof(zp)); 558 nextiph = &zp; 559 } 560 break; 561 562 case IPFLOOKUPITER_NODE : 563 node = token->ipt_data; 564 if (node == NULL) { 565 iph = fr_findhtable(ilp->ili_unit, ilp->ili_name); 566 if (iph == NULL) 567 err = ESRCH; 568 else { 569 nextnode = iph->iph_list; 570 } 571 } else { 572 nextnode = node->ipe_next; 573 } 574 575 if (nextnode != NULL) { 576 ATOMIC_INC(nextnode->ipe_ref); 577 if (nextnode->ipe_next == NULL) 578 token->ipt_alive = 0; 579 } else { 580 bzero((char *)&zn, sizeof(zn)); 581 nextnode = &zn; 582 } 583 break; 584 default : 585 err = EINVAL; 586 break; 587 } 588 589 RWLOCK_EXIT(&ip_poolrw); 590 if (err != 0) 591 return err; 592 593 switch (ilp->ili_otype) 594 { 595 case IPFLOOKUPITER_LIST : 596 if (iph != NULL) { 597 WRITE_ENTER(&ip_poolrw); 598 fr_derefhtable(iph); 599 RWLOCK_EXIT(&ip_poolrw); 600 } 601 token->ipt_data = nextiph; 602 err = COPYOUT(nextiph, ilp->ili_data, sizeof(*nextiph)); 603 if (err != 0) 604 err = EFAULT; 605 break; 606 607 case IPFLOOKUPITER_NODE : 608 if (node != NULL) { 609 WRITE_ENTER(&ip_poolrw); 610 fr_derefhtent(node); 611 RWLOCK_EXIT(&ip_poolrw); 612 } 613 token->ipt_data = nextnode; 614 err = COPYOUT(nextnode, ilp->ili_data, sizeof(*nextnode)); 615 if (err != 0) 616 err = EFAULT; 617 break; 618 } 619 620 return err; 621} 622 623 624void fr_htable_iterderef(otype, unit, data) 625u_int otype; 626int unit; 627void *data; 628{ 629 630 if (data == NULL) 631 return; 632 633 if (unit < 0 || unit > IPL_LOGMAX) 634 return; 635 636 switch (otype) 637 { 638 case IPFLOOKUPITER_LIST : 639 WRITE_ENTER(&ip_poolrw); 640 fr_derefhtable((iphtable_t *)data); 641 RWLOCK_EXIT(&ip_poolrw); 642 break; 643 644 case IPFLOOKUPITER_NODE : 645 WRITE_ENTER(&ip_poolrw); 646 fr_derefhtent((iphtent_t *)data); 647 RWLOCK_EXIT(&ip_poolrw); 648 break; 649 default : 650 break; 651 } 652} 653 654#endif /* IPFILTER_LOOKUP */ 655