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