1 2/* 3 * Copyright (C) 2012 by Darren Reed. 4 * 5 * See the IPFILTER.LICENCE file for details on licencing. 6 */ 7#if defined(KERNEL) || defined(_KERNEL) 8# undef KERNEL 9# undef _KERNEL 10# define KERNEL 1 11# define _KERNEL 1 12#endif 13#include <sys/param.h> 14#include <sys/types.h> 15#include <sys/errno.h> 16#include <sys/time.h> 17#include <sys/file.h> 18#if !defined(_KERNEL) 19# include <stdlib.h> 20# include <string.h> 21# define _KERNEL 22# include <sys/uio.h> 23# undef _KERNEL 24#endif 25#include <sys/socket.h> 26#if defined(__FreeBSD__) 27# include <sys/malloc.h> 28#endif 29#if defined(__FreeBSD__) 30# include <sys/cdefs.h> 31# include <sys/proc.h> 32#endif 33#if !defined(__SVR4) 34# include <sys/mbuf.h> 35#endif 36#if defined(_KERNEL) 37# include <sys/systm.h> 38#else 39# include "ipf.h" 40#endif 41#include <netinet/in.h> 42#include <net/if.h> 43 44#include "netinet/ip_compat.h" 45#include "netinet/ip_fil.h" 46#include "netinet/ip_lookup.h" 47#include "netinet/ip_htable.h" 48/* END OF INCLUDES */ 49 50 51# ifdef USE_INET6 52static iphtent_t *ipf_iphmfind6(iphtable_t *, i6addr_t *); 53# endif 54static iphtent_t *ipf_iphmfind(iphtable_t *, struct in_addr *); 55static int ipf_iphmfindip(ipf_main_softc_t *, void *, int, void *, u_int); 56static int ipf_htable_clear(ipf_main_softc_t *, void *, iphtable_t *); 57static int ipf_htable_create(ipf_main_softc_t *, void *, iplookupop_t *); 58static int ipf_htable_deref(ipf_main_softc_t *, void *, void *); 59static int ipf_htable_destroy(ipf_main_softc_t *, void *, int, char *); 60static void *ipf_htable_exists(void *, int, char *); 61static size_t ipf_htable_flush(ipf_main_softc_t *, void *, 62 iplookupflush_t *); 63static void ipf_htable_free(void *, iphtable_t *); 64static int ipf_htable_iter_deref(ipf_main_softc_t *, void *, int, 65 int, void *); 66static int ipf_htable_iter_next(ipf_main_softc_t *, void *, ipftoken_t *, 67 ipflookupiter_t *); 68static int ipf_htable_node_add(ipf_main_softc_t *, void *, 69 iplookupop_t *, int); 70static int ipf_htable_node_del(ipf_main_softc_t *, void *, 71 iplookupop_t *, int); 72static int ipf_htable_remove(ipf_main_softc_t *, void *, iphtable_t *); 73static void *ipf_htable_soft_create(ipf_main_softc_t *); 74static void ipf_htable_soft_destroy(ipf_main_softc_t *, void *); 75static int ipf_htable_soft_init(ipf_main_softc_t *, void *); 76static void ipf_htable_soft_fini(ipf_main_softc_t *, void *); 77static int ipf_htable_stats_get(ipf_main_softc_t *, void *, 78 iplookupop_t *); 79static int ipf_htable_table_add(ipf_main_softc_t *, void *, 80 iplookupop_t *); 81static int ipf_htable_table_del(ipf_main_softc_t *, void *, 82 iplookupop_t *); 83static int ipf_htent_deref(void *, iphtent_t *); 84static iphtent_t *ipf_htent_find(iphtable_t *, iphtent_t *); 85static int ipf_htent_insert(ipf_main_softc_t *, void *, iphtable_t *, 86 iphtent_t *); 87static int ipf_htent_remove(ipf_main_softc_t *, void *, iphtable_t *, 88 iphtent_t *); 89static void *ipf_htable_select_add_ref(void *, int, char *); 90static void ipf_htable_expire(ipf_main_softc_t *, void *); 91 92 93typedef struct ipf_htable_softc_s { 94 u_long ipht_nomem[LOOKUP_POOL_SZ]; 95 u_long ipf_nhtables[LOOKUP_POOL_SZ]; 96 u_long ipf_nhtnodes[LOOKUP_POOL_SZ]; 97 iphtable_t *ipf_htables[LOOKUP_POOL_SZ]; 98 iphtent_t *ipf_node_explist; 99} ipf_htable_softc_t; 100 101ipf_lookup_t ipf_htable_backend = { 102 IPLT_HASH, 103 ipf_htable_soft_create, 104 ipf_htable_soft_destroy, 105 ipf_htable_soft_init, 106 ipf_htable_soft_fini, 107 ipf_iphmfindip, 108 ipf_htable_flush, 109 ipf_htable_iter_deref, 110 ipf_htable_iter_next, 111 ipf_htable_node_add, 112 ipf_htable_node_del, 113 ipf_htable_stats_get, 114 ipf_htable_table_add, 115 ipf_htable_table_del, 116 ipf_htable_deref, 117 ipf_htable_exists, 118 ipf_htable_select_add_ref, 119 NULL, 120 ipf_htable_expire, 121 NULL 122}; 123 124 125/* ------------------------------------------------------------------------ */ 126/* Function: ipf_htable_soft_create */ 127/* Returns: void * - NULL = failure, else pointer to local context */ 128/* Parameters: softc(I) - pointer to soft context main structure */ 129/* */ 130/* Initialise the routing table data structures where required. */ 131/* ------------------------------------------------------------------------ */ 132static void * 133ipf_htable_soft_create(ipf_main_softc_t *softc) 134{ 135 ipf_htable_softc_t *softh; 136 137 KMALLOC(softh, ipf_htable_softc_t *); 138 if (softh == NULL) { 139 IPFERROR(30026); 140 return (NULL); 141 } 142 143 bzero((char *)softh, sizeof(*softh)); 144 145 return (softh); 146} 147 148 149/* ------------------------------------------------------------------------ */ 150/* Function: ipf_htable_soft_destroy */ 151/* Returns: Nil */ 152/* Parameters: softc(I) - pointer to soft context main structure */ 153/* arg(I) - pointer to local context to use */ 154/* */ 155/* Clean up the pool by free'ing the radix tree associated with it and free */ 156/* up the pool context too. */ 157/* ------------------------------------------------------------------------ */ 158static void 159ipf_htable_soft_destroy(ipf_main_softc_t *softc, void *arg) 160{ 161 ipf_htable_softc_t *softh = arg; 162 163 KFREE(softh); 164} 165 166 167/* ------------------------------------------------------------------------ */ 168/* Function: ipf_htable_soft_init */ 169/* Returns: int - 0 = success, else error */ 170/* Parameters: softc(I) - pointer to soft context main structure */ 171/* arg(I) - pointer to local context to use */ 172/* */ 173/* Initialise the hash table ready for use. */ 174/* ------------------------------------------------------------------------ */ 175static int 176ipf_htable_soft_init(ipf_main_softc_t *softc, void *arg) 177{ 178 ipf_htable_softc_t *softh = arg; 179 180 bzero((char *)softh, sizeof(*softh)); 181 182 return (0); 183} 184 185 186/* ------------------------------------------------------------------------ */ 187/* Function: ipf_htable_soft_fini */ 188/* Returns: Nil */ 189/* Parameters: softc(I) - pointer to soft context main structure */ 190/* arg(I) - pointer to local context to use */ 191/* Locks: WRITE(ipf_global) */ 192/* */ 193/* Clean up all the pool data structures allocated and call the cleanup */ 194/* function for the radix tree that supports the pools. ipf_pool_destroy is */ 195/* used to delete the pools one by one to ensure they're properly freed up. */ 196/* ------------------------------------------------------------------------ */ 197static void 198ipf_htable_soft_fini(ipf_main_softc_t *softc, void *arg) 199{ 200 iplookupflush_t fop; 201 202 fop.iplf_type = IPLT_HASH; 203 fop.iplf_unit = IPL_LOGALL; 204 fop.iplf_arg = 0; 205 fop.iplf_count = 0; 206 *fop.iplf_name = '\0'; 207 ipf_htable_flush(softc, arg, &fop); 208} 209 210 211/* ------------------------------------------------------------------------ */ 212/* Function: ipf_htable_stats_get */ 213/* Returns: int - 0 = success, else error */ 214/* Parameters: softc(I) - pointer to soft context main structure */ 215/* arg(I) - pointer to local context to use */ 216/* op(I) - pointer to lookup operation data */ 217/* */ 218/* Copy the relevant statistics out of internal structures and into the */ 219/* structure used to export statistics. */ 220/* ------------------------------------------------------------------------ */ 221static int 222ipf_htable_stats_get(ipf_main_softc_t *softc, void *arg, iplookupop_t *op) 223{ 224 ipf_htable_softc_t *softh = arg; 225 iphtstat_t stats; 226 int err; 227 228 if (op->iplo_size != sizeof(stats)) { 229 IPFERROR(30001); 230 return (EINVAL); 231 } 232 233 stats.iphs_tables = softh->ipf_htables[op->iplo_unit + 1]; 234 stats.iphs_numtables = softh->ipf_nhtables[op->iplo_unit + 1]; 235 stats.iphs_numnodes = softh->ipf_nhtnodes[op->iplo_unit + 1]; 236 stats.iphs_nomem = softh->ipht_nomem[op->iplo_unit + 1]; 237 238 err = COPYOUT(&stats, op->iplo_struct, sizeof(stats)); 239 if (err != 0) { 240 IPFERROR(30013); 241 return (EFAULT); 242 } 243 return (0); 244 245} 246 247 248/* ------------------------------------------------------------------------ */ 249/* Function: ipf_htable_create */ 250/* Returns: int - 0 = success, else error */ 251/* Parameters: softc(I) - pointer to soft context main structure */ 252/* arg(I) - pointer to local context to use */ 253/* op(I) - pointer to lookup operation data */ 254/* */ 255/* Create a new hash table using the template passed. */ 256/* ------------------------------------------------------------------------ */ 257static int 258ipf_htable_create(ipf_main_softc_t *softc, void *arg, iplookupop_t *op) 259{ 260 ipf_htable_softc_t *softh = arg; 261 iphtable_t htab, *iph, *oiph; 262 char name[FR_GROUPLEN]; 263 int err, i, unit; 264 265 if (op->iplo_size != sizeof(htab)) { 266 IPFERROR(30024); 267 return (EINVAL); 268 } 269 err = COPYIN(op->iplo_struct, &htab, sizeof(htab)); 270 if (err != 0) { 271 IPFERROR(30003); 272 return (EFAULT); 273 } 274 275 unit = op->iplo_unit; 276 if (htab.iph_unit != unit) { 277 IPFERROR(30005); 278 return (EINVAL); 279 } 280 if (htab.iph_size < 1) { 281 IPFERROR(30025); 282 return (EINVAL); 283 } 284 285 286 if ((op->iplo_arg & IPHASH_ANON) == 0) { 287 iph = ipf_htable_exists(softh, unit, op->iplo_name); 288 if (iph != NULL) { 289 if ((iph->iph_flags & IPHASH_DELETE) == 0) { 290 IPFERROR(30004); 291 return (EEXIST); 292 } 293 iph->iph_flags &= ~IPHASH_DELETE; 294 iph->iph_ref++; 295 return (0); 296 } 297 } 298 299 KMALLOC(iph, iphtable_t *); 300 if (iph == NULL) { 301 softh->ipht_nomem[op->iplo_unit + 1]++; 302 IPFERROR(30002); 303 return (ENOMEM); 304 } 305 *iph = htab; 306 307 if ((op->iplo_arg & IPHASH_ANON) != 0) { 308 i = IPHASH_ANON; 309 do { 310 i++; 311 (void)snprintf(name, sizeof(name), "%u", i); 312 for (oiph = softh->ipf_htables[unit + 1]; oiph != NULL; 313 oiph = oiph->iph_next) 314 if (strncmp(oiph->iph_name, name, 315 sizeof(oiph->iph_name)) == 0) 316 break; 317 } while (oiph != NULL); 318 319 (void)strncpy(iph->iph_name, name, sizeof(iph->iph_name)); 320 (void)strncpy(op->iplo_name, name, sizeof(op->iplo_name)); 321 iph->iph_type |= IPHASH_ANON; 322 } else { 323 (void)strncpy(iph->iph_name, op->iplo_name, 324 sizeof(iph->iph_name)); 325 iph->iph_name[sizeof(iph->iph_name) - 1] = '\0'; 326 } 327 328 KMALLOCS(iph->iph_table, iphtent_t **, 329 iph->iph_size * sizeof(*iph->iph_table)); 330 if (iph->iph_table == NULL) { 331 KFREE(iph); 332 softh->ipht_nomem[unit + 1]++; 333 IPFERROR(30006); 334 return (ENOMEM); 335 } 336 337 bzero((char *)iph->iph_table, iph->iph_size * sizeof(*iph->iph_table)); 338 iph->iph_maskset[0] = 0; 339 iph->iph_maskset[1] = 0; 340 iph->iph_maskset[2] = 0; 341 iph->iph_maskset[3] = 0; 342 343 iph->iph_ref = 1; 344 iph->iph_list = NULL; 345 iph->iph_tail = &iph->iph_list; 346 iph->iph_next = softh->ipf_htables[unit + 1]; 347 iph->iph_pnext = &softh->ipf_htables[unit + 1]; 348 if (softh->ipf_htables[unit + 1] != NULL) 349 softh->ipf_htables[unit + 1]->iph_pnext = &iph->iph_next; 350 softh->ipf_htables[unit + 1] = iph; 351 352 softh->ipf_nhtables[unit + 1]++; 353 354 return (0); 355} 356 357 358/* ------------------------------------------------------------------------ */ 359/* Function: ipf_htable_table_del */ 360/* Returns: int - 0 = success, else error */ 361/* Parameters: softc(I) - pointer to soft context main structure */ 362/* arg(I) - pointer to local context to use */ 363/* op(I) - pointer to lookup operation data */ 364/* */ 365/* ------------------------------------------------------------------------ */ 366static int 367ipf_htable_table_del(ipf_main_softc_t *softc, void *arg, iplookupop_t *op) 368{ 369 return (ipf_htable_destroy(softc, arg, op->iplo_unit, op->iplo_name)); 370} 371 372 373/* ------------------------------------------------------------------------ */ 374/* Function: ipf_htable_destroy */ 375/* Returns: int - 0 = success, else error */ 376/* Parameters: softc(I) - pointer to soft context main structure */ 377/* arg(I) - pointer to local context to use */ 378/* op(I) - pointer to lookup operation data */ 379/* */ 380/* Find the hash table that belongs to the relevant part of ipfilter with a */ 381/* matching name and attempt to destroy it. If it is in use, empty it out */ 382/* and mark it for deletion so that when all the references disappear, it */ 383/* can be removed. */ 384/* ------------------------------------------------------------------------ */ 385static int 386ipf_htable_destroy(ipf_main_softc_t *softc, void *arg, int unit, char *name) 387{ 388 iphtable_t *iph; 389 390 iph = ipf_htable_find(arg, unit, name); 391 if (iph == NULL) { 392 IPFERROR(30007); 393 return (ESRCH); 394 } 395 396 if (iph->iph_unit != unit) { 397 IPFERROR(30008); 398 return (EINVAL); 399 } 400 401 if (iph->iph_ref != 0) { 402 ipf_htable_clear(softc, arg, iph); 403 iph->iph_flags |= IPHASH_DELETE; 404 return (0); 405 } 406 407 ipf_htable_remove(softc, arg, iph); 408 409 return (0); 410} 411 412 413/* ------------------------------------------------------------------------ */ 414/* Function: ipf_htable_clear */ 415/* Returns: int - 0 = success, else error */ 416/* Parameters: softc(I) - pointer to soft context main structure */ 417/* arg(I) - pointer to local context to use */ 418/* iph(I) - pointer to hash table to destroy */ 419/* */ 420/* Clean out the hash table by walking the list of entries and removing */ 421/* each one, one by one. */ 422/* ------------------------------------------------------------------------ */ 423static int 424ipf_htable_clear(ipf_main_softc_t *softc, void *arg, iphtable_t *iph) 425{ 426 iphtent_t *ipe; 427 428 while ((ipe = iph->iph_list) != NULL) 429 if (ipf_htent_remove(softc, arg, iph, ipe) != 0) 430 return (1); 431 return (0); 432} 433 434 435/* ------------------------------------------------------------------------ */ 436/* Function: ipf_htable_free */ 437/* Returns: Nil */ 438/* Parameters: arg(I) - pointer to local context to use */ 439/* iph(I) - pointer to hash table to destroy */ 440/* */ 441/* ------------------------------------------------------------------------ */ 442static void 443ipf_htable_free(void *arg, iphtable_t *iph) 444{ 445 ipf_htable_softc_t *softh = arg; 446 447 if (iph->iph_next != NULL) 448 iph->iph_next->iph_pnext = iph->iph_pnext; 449 if (iph->iph_pnext != NULL) 450 *iph->iph_pnext = iph->iph_next; 451 iph->iph_pnext = NULL; 452 iph->iph_next = NULL; 453 454 softh->ipf_nhtables[iph->iph_unit + 1]--; 455 456 KFREES(iph->iph_table, iph->iph_size * sizeof(*iph->iph_table)); 457 KFREE(iph); 458} 459 460 461/* ------------------------------------------------------------------------ */ 462/* Function: ipf_htable_remove */ 463/* Returns: int - 0 = success, else error */ 464/* Parameters: softc(I) - pointer to soft context main structure */ 465/* arg(I) - pointer to local context to use */ 466/* iph(I) - pointer to hash table to destroy */ 467/* */ 468/* It is necessary to unlink here as well as free (called by deref) so that */ 469/* the while loop in ipf_htable_flush() functions properly. */ 470/* ------------------------------------------------------------------------ */ 471static int 472ipf_htable_remove(ipf_main_softc_t *softc, void *arg, iphtable_t *iph) 473{ 474 475 if (ipf_htable_clear(softc, arg, iph) != 0) 476 return (1); 477 478 if (iph->iph_pnext != NULL) 479 *iph->iph_pnext = iph->iph_next; 480 if (iph->iph_next != NULL) 481 iph->iph_next->iph_pnext = iph->iph_pnext; 482 iph->iph_pnext = NULL; 483 iph->iph_next = NULL; 484 485 return (ipf_htable_deref(softc, arg, iph)); 486} 487 488 489/* ------------------------------------------------------------------------ */ 490/* Function: ipf_htable_node_del */ 491/* Returns: int - 0 = success, else error */ 492/* Parameters: softc(I) - pointer to soft context main structure */ 493/* arg(I) - pointer to local context to use */ 494/* op(I) - pointer to lookup operation data */ 495/* uid(I) - real uid of process doing operation */ 496/* */ 497/* ------------------------------------------------------------------------ */ 498static int 499ipf_htable_node_del(ipf_main_softc_t *softc, void *arg, iplookupop_t *op, 500 int uid) 501{ 502 iphtable_t *iph; 503 iphtent_t hte, *ent; 504 int err; 505 506 if (op->iplo_size != sizeof(hte)) { 507 IPFERROR(30014); 508 return (EINVAL); 509 } 510 511 err = COPYIN(op->iplo_struct, &hte, sizeof(hte)); 512 if (err != 0) { 513 IPFERROR(30015); 514 return (EFAULT); 515 } 516 517 iph = ipf_htable_find(arg, op->iplo_unit, op->iplo_name); 518 if (iph == NULL) { 519 IPFERROR(30016); 520 return (ESRCH); 521 } 522 523 ent = ipf_htent_find(iph, &hte); 524 if (ent == NULL) { 525 IPFERROR(30022); 526 return (ESRCH); 527 } 528 529 if ((uid != 0) && (ent->ipe_uid != uid)) { 530 IPFERROR(30023); 531 return (EACCES); 532 } 533 534 err = ipf_htent_remove(softc, arg, iph, ent); 535 536 return (err); 537} 538 539 540/* ------------------------------------------------------------------------ */ 541/* Function: ipf_htable_node_del */ 542/* Returns: int - 0 = success, else error */ 543/* Parameters: softc(I) - pointer to soft context main structure */ 544/* arg(I) - pointer to local context to use */ 545/* op(I) - pointer to lookup operation data */ 546/* */ 547/* ------------------------------------------------------------------------ */ 548static int 549ipf_htable_table_add(ipf_main_softc_t *softc, void *arg, iplookupop_t *op) 550{ 551 int err; 552 553 if (ipf_htable_find(arg, op->iplo_unit, op->iplo_name) != NULL) { 554 IPFERROR(30017); 555 err = EEXIST; 556 } else { 557 err = ipf_htable_create(softc, arg, op); 558 } 559 560 return (err); 561} 562 563 564/* ------------------------------------------------------------------------ */ 565/* Function: ipf_htent_remove */ 566/* Returns: int - 0 = success, else error */ 567/* Parameters: softc(I) - pointer to soft context main structure */ 568/* arg(I) - pointer to local context to use */ 569/* iph(I) - pointer to hash table */ 570/* ipe(I) - pointer to hash table entry to remove */ 571/* */ 572/* Delete an entry from a hash table. */ 573/* ------------------------------------------------------------------------ */ 574static int 575ipf_htent_remove(ipf_main_softc_t *softc, void *arg, iphtable_t *iph, 576 iphtent_t *ipe) 577{ 578 579 if (iph->iph_tail == &ipe->ipe_next) 580 iph->iph_tail = ipe->ipe_pnext; 581 582 if (ipe->ipe_hnext != NULL) 583 ipe->ipe_hnext->ipe_phnext = ipe->ipe_phnext; 584 if (ipe->ipe_phnext != NULL) 585 *ipe->ipe_phnext = ipe->ipe_hnext; 586 ipe->ipe_phnext = NULL; 587 ipe->ipe_hnext = NULL; 588 589 if (ipe->ipe_dnext != NULL) 590 ipe->ipe_dnext->ipe_pdnext = ipe->ipe_pdnext; 591 if (ipe->ipe_pdnext != NULL) 592 *ipe->ipe_pdnext = ipe->ipe_dnext; 593 ipe->ipe_pdnext = NULL; 594 ipe->ipe_dnext = NULL; 595 596 if (ipe->ipe_next != NULL) 597 ipe->ipe_next->ipe_pnext = ipe->ipe_pnext; 598 if (ipe->ipe_pnext != NULL) 599 *ipe->ipe_pnext = ipe->ipe_next; 600 ipe->ipe_pnext = NULL; 601 ipe->ipe_next = NULL; 602 603 switch (iph->iph_type & ~IPHASH_ANON) 604 { 605 case IPHASH_GROUPMAP : 606 if (ipe->ipe_group != NULL) 607 ipf_group_del(softc, ipe->ipe_ptr, NULL); 608 break; 609 610 default : 611 ipe->ipe_ptr = NULL; 612 ipe->ipe_value = 0; 613 break; 614 } 615 616 return (ipf_htent_deref(arg, ipe)); 617} 618 619 620/* ------------------------------------------------------------------------ */ 621/* Function: ipf_htable_deref */ 622/* Returns: int - 0 = success, else error */ 623/* Parameters: softc(I) - pointer to soft context main structure */ 624/* arg(I) - pointer to local context to use */ 625/* object(I) - pointer to hash table */ 626/* */ 627/* ------------------------------------------------------------------------ */ 628static int 629ipf_htable_deref(ipf_main_softc_t *softc, void *arg, void *object) 630{ 631 ipf_htable_softc_t *softh = arg; 632 iphtable_t *iph = object; 633 int refs; 634 635 iph->iph_ref--; 636 refs = iph->iph_ref; 637 638 if (iph->iph_ref == 0) { 639 ipf_htable_free(softh, iph); 640 } 641 642 return (refs); 643} 644 645 646/* ------------------------------------------------------------------------ */ 647/* Function: ipf_htent_deref */ 648/* Parameters: arg(I) - pointer to local context to use */ 649/* ipe(I) - */ 650/* */ 651/* ------------------------------------------------------------------------ */ 652static int 653ipf_htent_deref(void *arg, iphtent_t *ipe) 654{ 655 ipf_htable_softc_t *softh = arg; 656 657 ipe->ipe_ref--; 658 if (ipe->ipe_ref == 0) { 659 softh->ipf_nhtnodes[ipe->ipe_unit + 1]--; 660 KFREE(ipe); 661 662 return (0); 663 } 664 665 return (ipe->ipe_ref); 666} 667 668 669/* ------------------------------------------------------------------------ */ 670/* Function: ipf_htable_exists */ 671/* Parameters: arg(I) - pointer to local context to use */ 672/* */ 673/* ------------------------------------------------------------------------ */ 674static void * 675ipf_htable_exists(void *arg, int unit, char *name) 676{ 677 ipf_htable_softc_t *softh = arg; 678 iphtable_t *iph; 679 680 if (unit == IPL_LOGALL) { 681 int i; 682 683 for (i = 0; i <= LOOKUP_POOL_MAX; i++) { 684 for (iph = softh->ipf_htables[i]; iph != NULL; 685 iph = iph->iph_next) { 686 if (strncmp(iph->iph_name, name, 687 sizeof(iph->iph_name)) == 0) 688 break; 689 } 690 if (iph != NULL) 691 break; 692 } 693 } else { 694 for (iph = softh->ipf_htables[unit + 1]; iph != NULL; 695 iph = iph->iph_next) { 696 if (strncmp(iph->iph_name, name, 697 sizeof(iph->iph_name)) == 0) 698 break; 699 } 700 } 701 return (iph); 702} 703 704 705/* ------------------------------------------------------------------------ */ 706/* Function: ipf_htable_select_add_ref */ 707/* Returns: void * - NULL = failure, else pointer to the hash table */ 708/* Parameters: arg(I) - pointer to local context to use */ 709/* unit(I) - ipfilter device to which we are working on */ 710/* name(I) - name of the hash table */ 711/* */ 712/* ------------------------------------------------------------------------ */ 713static void * 714ipf_htable_select_add_ref(void *arg, int unit, char *name) 715{ 716 iphtable_t *iph; 717 718 iph = ipf_htable_exists(arg, unit, name); 719 if (iph != NULL) { 720 ATOMIC_INC32(iph->iph_ref); 721 } 722 return (iph); 723} 724 725 726/* ------------------------------------------------------------------------ */ 727/* Function: ipf_htable_find */ 728/* Returns: void * - NULL = failure, else pointer to the hash table */ 729/* Parameters: arg(I) - pointer to local context to use */ 730/* unit(I) - ipfilter device to which we are working on */ 731/* name(I) - name of the hash table */ 732/* */ 733/* This function is exposed becaues it is used in the group-map feature. */ 734/* ------------------------------------------------------------------------ */ 735iphtable_t * 736ipf_htable_find(void *arg, int unit, char *name) 737{ 738 iphtable_t *iph; 739 740 iph = ipf_htable_exists(arg, unit, name); 741 if ((iph != NULL) && (iph->iph_flags & IPHASH_DELETE) == 0) 742 return (iph); 743 744 return (NULL); 745} 746 747 748/* ------------------------------------------------------------------------ */ 749/* Function: ipf_htable_flush */ 750/* Returns: size_t - number of entries flushed */ 751/* Parameters: softc(I) - pointer to soft context main structure */ 752/* arg(I) - pointer to local context to use */ 753/* op(I) - pointer to lookup operation data */ 754/* */ 755/* ------------------------------------------------------------------------ */ 756static size_t 757ipf_htable_flush(ipf_main_softc_t *softc, void *arg, iplookupflush_t *op) 758{ 759 ipf_htable_softc_t *softh = arg; 760 iphtable_t *iph; 761 size_t freed; 762 int i; 763 764 freed = 0; 765 766 for (i = -1; i <= IPL_LOGMAX; i++) { 767 if (op->iplf_unit == i || op->iplf_unit == IPL_LOGALL) { 768 while ((iph = softh->ipf_htables[i + 1]) != NULL) { 769 if (ipf_htable_remove(softc, arg, iph) == 0) { 770 freed++; 771 } else { 772 iph->iph_flags |= IPHASH_DELETE; 773 } 774 } 775 } 776 } 777 778 return (freed); 779} 780 781 782/* ------------------------------------------------------------------------ */ 783/* Function: ipf_htable_node_add */ 784/* Returns: int - 0 = success, else error */ 785/* Parameters: softc(I) - pointer to soft context main structure */ 786/* arg(I) - pointer to local context to use */ 787/* op(I) - pointer to lookup operation data */ 788/* uid(I) - real uid of process doing operation */ 789/* */ 790/* ------------------------------------------------------------------------ */ 791static int 792ipf_htable_node_add(ipf_main_softc_t *softc, void *arg, iplookupop_t *op, 793 int uid) 794{ 795 iphtable_t *iph; 796 iphtent_t hte; 797 int err; 798 799 if (op->iplo_size != sizeof(hte)) { 800 IPFERROR(30018); 801 return (EINVAL); 802 } 803 804 err = COPYIN(op->iplo_struct, &hte, sizeof(hte)); 805 if (err != 0) { 806 IPFERROR(30019); 807 return (EFAULT); 808 } 809 hte.ipe_uid = uid; 810 811 iph = ipf_htable_find(arg, op->iplo_unit, op->iplo_name); 812 if (iph == NULL) { 813 IPFERROR(30020); 814 return (ESRCH); 815 } 816 817 if (ipf_htent_find(iph, &hte) != NULL) { 818 IPFERROR(30021); 819 return (EEXIST); 820 } 821 822 err = ipf_htent_insert(softc, arg, iph, &hte); 823 824 return (err); 825} 826 827 828/* ------------------------------------------------------------------------ */ 829/* Function: ipf_htent_insert */ 830/* Returns: int - 0 = success, -1 = error */ 831/* Parameters: softc(I) - pointer to soft context main structure */ 832/* arg(I) - pointer to local context to use */ 833/* op(I) - pointer to lookup operation data */ 834/* ipeo(I) - */ 835/* */ 836/* Add an entry to a hash table. */ 837/* ------------------------------------------------------------------------ */ 838static int 839ipf_htent_insert(ipf_main_softc_t *softc, void *arg, iphtable_t *iph, 840 iphtent_t *ipeo) 841{ 842 ipf_htable_softc_t *softh = arg; 843 iphtent_t *ipe; 844 u_int hv; 845 int bits; 846 847 KMALLOC(ipe, iphtent_t *); 848 if (ipe == NULL) 849 return (-1); 850 851 bcopy((char *)ipeo, (char *)ipe, sizeof(*ipe)); 852 ipe->ipe_addr.i6[0] &= ipe->ipe_mask.i6[0]; 853 if (ipe->ipe_family == AF_INET) { 854 bits = count4bits(ipe->ipe_mask.in4_addr); 855 ipe->ipe_addr.i6[1] = 0; 856 ipe->ipe_addr.i6[2] = 0; 857 ipe->ipe_addr.i6[3] = 0; 858 ipe->ipe_mask.i6[1] = 0; 859 ipe->ipe_mask.i6[2] = 0; 860 ipe->ipe_mask.i6[3] = 0; 861 hv = IPE_V4_HASH_FN(ipe->ipe_addr.in4_addr, 862 ipe->ipe_mask.in4_addr, iph->iph_size); 863 } else 864#ifdef USE_INET6 865 if (ipe->ipe_family == AF_INET6) { 866 ipe->ipe_addr.i6[1] &= ipe->ipe_mask.i6[1]; 867 ipe->ipe_addr.i6[2] &= ipe->ipe_mask.i6[2]; 868 ipe->ipe_addr.i6[3] &= ipe->ipe_mask.i6[3]; 869 870 bits = count6bits(ipe->ipe_mask.i6); 871 hv = IPE_V6_HASH_FN(ipe->ipe_addr.i6, 872 ipe->ipe_mask.i6, iph->iph_size); 873 } else 874#endif 875 { 876 KFREE(ipe); 877 return (-1); 878 } 879 880 ipe->ipe_owner = iph; 881 ipe->ipe_ref = 1; 882 ipe->ipe_hnext = iph->iph_table[hv]; 883 ipe->ipe_phnext = iph->iph_table + hv; 884 885 if (iph->iph_table[hv] != NULL) 886 iph->iph_table[hv]->ipe_phnext = &ipe->ipe_hnext; 887 iph->iph_table[hv] = ipe; 888 889 ipe->ipe_pnext = iph->iph_tail; 890 *iph->iph_tail = ipe; 891 iph->iph_tail = &ipe->ipe_next; 892 ipe->ipe_next = NULL; 893 894 if (ipe->ipe_die != 0) { 895 /* 896 * If the new node has a given expiration time, insert it 897 * into the list of expiring nodes with the ones to be 898 * removed first added to the front of the list. The 899 * insertion is O(n) but it is kept sorted for quick scans 900 * at expiration interval checks. 901 */ 902 iphtent_t *n; 903 904 ipe->ipe_die = softc->ipf_ticks + IPF_TTLVAL(ipe->ipe_die); 905 for (n = softh->ipf_node_explist; n != NULL; n = n->ipe_dnext) { 906 if (ipe->ipe_die < n->ipe_die) 907 break; 908 if (n->ipe_dnext == NULL) { 909 /* 910 * We've got to the last node and everything 911 * wanted to be expired before this new node, 912 * so we have to tack it on the end... 913 */ 914 n->ipe_dnext = ipe; 915 ipe->ipe_pdnext = &n->ipe_dnext; 916 n = NULL; 917 break; 918 } 919 } 920 921 if (softh->ipf_node_explist == NULL) { 922 softh->ipf_node_explist = ipe; 923 ipe->ipe_pdnext = &softh->ipf_node_explist; 924 } else if (n != NULL) { 925 ipe->ipe_dnext = n; 926 ipe->ipe_pdnext = n->ipe_pdnext; 927 n->ipe_pdnext = &ipe->ipe_dnext; 928 } 929 } 930 931 if (ipe->ipe_family == AF_INET) { 932 ipf_inet_mask_add(bits, &iph->iph_v4_masks); 933 } 934#ifdef USE_INET6 935 else if (ipe->ipe_family == AF_INET6) { 936 ipf_inet6_mask_add(bits, &ipe->ipe_mask, &iph->iph_v6_masks); 937 } 938#endif 939 940 switch (iph->iph_type & ~IPHASH_ANON) 941 { 942 case IPHASH_GROUPMAP : 943 ipe->ipe_ptr = ipf_group_add(softc, ipe->ipe_group, NULL, 944 iph->iph_flags, IPL_LOGIPF, 945 softc->ipf_active); 946 break; 947 948 default : 949 ipe->ipe_ptr = NULL; 950 ipe->ipe_value = 0; 951 break; 952 } 953 954 ipe->ipe_unit = iph->iph_unit; 955 softh->ipf_nhtnodes[ipe->ipe_unit + 1]++; 956 957 return (0); 958} 959 960 961/* ------------------------------------------------------------------------ */ 962/* Function: ipf_htent_find */ 963/* Returns: int - 0 = success, else error */ 964/* Parameters: iph(I) - pointer to table to search */ 965/* ipeo(I) - pointer to entry to find */ 966/* */ 967/* While it isn't absolutely necessary to for the address and mask to be */ 968/* passed in through an iphtent_t structure, one is always present when it */ 969/* is time to call this function, so it is just more convenient. */ 970/* ------------------------------------------------------------------------ */ 971static iphtent_t * 972ipf_htent_find(iphtable_t *iph, iphtent_t *ipeo) 973{ 974 iphtent_t ipe, *ent; 975 u_int hv; 976 int bits; 977 978 bcopy((char *)ipeo, (char *)&ipe, sizeof(ipe)); 979 ipe.ipe_addr.i6[0] &= ipe.ipe_mask.i6[0]; 980 ipe.ipe_addr.i6[1] &= ipe.ipe_mask.i6[1]; 981 ipe.ipe_addr.i6[2] &= ipe.ipe_mask.i6[2]; 982 ipe.ipe_addr.i6[3] &= ipe.ipe_mask.i6[3]; 983 if (ipe.ipe_family == AF_INET) { 984 bits = count4bits(ipe.ipe_mask.in4_addr); 985 ipe.ipe_addr.i6[1] = 0; 986 ipe.ipe_addr.i6[2] = 0; 987 ipe.ipe_addr.i6[3] = 0; 988 ipe.ipe_mask.i6[1] = 0; 989 ipe.ipe_mask.i6[2] = 0; 990 ipe.ipe_mask.i6[3] = 0; 991 hv = IPE_V4_HASH_FN(ipe.ipe_addr.in4_addr, 992 ipe.ipe_mask.in4_addr, iph->iph_size); 993 } else 994#ifdef USE_INET6 995 if (ipe.ipe_family == AF_INET6) { 996 bits = count6bits(ipe.ipe_mask.i6); 997 hv = IPE_V6_HASH_FN(ipe.ipe_addr.i6, 998 ipe.ipe_mask.i6, iph->iph_size); 999 } else 1000#endif 1001 return (NULL); 1002 1003 for (ent = iph->iph_table[hv]; ent != NULL; ent = ent->ipe_hnext) { 1004 if (ent->ipe_family != ipe.ipe_family) 1005 continue; 1006 if (IP6_NEQ(&ipe.ipe_addr, &ent->ipe_addr)) 1007 continue; 1008 if (IP6_NEQ(&ipe.ipe_mask, &ent->ipe_mask)) 1009 continue; 1010 break; 1011 } 1012 1013 return (ent); 1014} 1015 1016 1017/* ------------------------------------------------------------------------ */ 1018/* Function: ipf_iphmfindgroup */ 1019/* Returns: int - 0 = success, else error */ 1020/* Parameters: softc(I) - pointer to soft context main structure */ 1021/* tptr(I) - */ 1022/* aptr(I) - */ 1023/* */ 1024/* Search a hash table for a matching entry and return the pointer stored */ 1025/* in it for use as the next group of rules to search. */ 1026/* */ 1027/* This function is exposed becaues it is used in the group-map feature. */ 1028/* ------------------------------------------------------------------------ */ 1029void * 1030ipf_iphmfindgroup(ipf_main_softc_t *softc, void *tptr, void *aptr) 1031{ 1032 struct in_addr *addr; 1033 iphtable_t *iph; 1034 iphtent_t *ipe; 1035 void *rval; 1036 1037 READ_ENTER(&softc->ipf_poolrw); 1038 iph = tptr; 1039 addr = aptr; 1040 1041 ipe = ipf_iphmfind(iph, addr); 1042 if (ipe != NULL) 1043 rval = ipe->ipe_ptr; 1044 else 1045 rval = NULL; 1046 RWLOCK_EXIT(&softc->ipf_poolrw); 1047 return (rval); 1048} 1049 1050 1051/* ------------------------------------------------------------------------ */ 1052/* Function: ipf_iphmfindip */ 1053/* Returns: int - 0 == +ve match, -1 == error, 1 == -ve/no match */ 1054/* Parameters: softc(I) - pointer to soft context main structure */ 1055/* tptr(I) - pointer to the pool to search */ 1056/* ipversion(I) - IP protocol version (4 or 6) */ 1057/* aptr(I) - pointer to address information */ 1058/* bytes(I) - packet length */ 1059/* */ 1060/* Search the hash table for a given address and return a search result. */ 1061/* ------------------------------------------------------------------------ */ 1062static int 1063ipf_iphmfindip(ipf_main_softc_t *softc, void *tptr, int ipversion, void *aptr, 1064 u_int bytes) 1065{ 1066 struct in_addr *addr; 1067 iphtable_t *iph; 1068 iphtent_t *ipe; 1069 int rval; 1070 1071 if (tptr == NULL || aptr == NULL) 1072 return (-1); 1073 1074 iph = tptr; 1075 addr = aptr; 1076 1077 READ_ENTER(&softc->ipf_poolrw); 1078 if (ipversion == 4) { 1079 ipe = ipf_iphmfind(iph, addr); 1080#ifdef USE_INET6 1081 } else if (ipversion == 6) { 1082 ipe = ipf_iphmfind6(iph, (i6addr_t *)addr); 1083#endif 1084 } else { 1085 ipe = NULL; 1086 } 1087 1088 if (ipe != NULL) { 1089 rval = 0; 1090 ipe->ipe_hits++; 1091 ipe->ipe_bytes += bytes; 1092 } else { 1093 rval = 1; 1094 } 1095 RWLOCK_EXIT(&softc->ipf_poolrw); 1096 return (rval); 1097} 1098 1099 1100/* ------------------------------------------------------------------------ */ 1101/* Function: ipf_iphmfindip */ 1102/* Parameters: iph(I) - pointer to hash table */ 1103/* addr(I) - pointer to IPv4 address */ 1104/* Locks: ipf_poolrw */ 1105/* */ 1106/* ------------------------------------------------------------------------ */ 1107static iphtent_t * 1108ipf_iphmfind(iphtable_t *iph, struct in_addr *addr) 1109{ 1110 u_32_t msk, ips; 1111 iphtent_t *ipe; 1112 u_int hv; 1113 int i; 1114 1115 i = 0; 1116maskloop: 1117 msk = iph->iph_v4_masks.imt4_active[i]; 1118 ips = addr->s_addr & msk; 1119 hv = IPE_V4_HASH_FN(ips, msk, iph->iph_size); 1120 for (ipe = iph->iph_table[hv]; (ipe != NULL); ipe = ipe->ipe_hnext) { 1121 if ((ipe->ipe_family != AF_INET) || 1122 (ipe->ipe_mask.in4_addr != msk) || 1123 (ipe->ipe_addr.in4_addr != ips)) { 1124 continue; 1125 } 1126 break; 1127 } 1128 1129 if (ipe == NULL) { 1130 i++; 1131 if (i < iph->iph_v4_masks.imt4_max) 1132 goto maskloop; 1133 } 1134 return (ipe); 1135} 1136 1137 1138/* ------------------------------------------------------------------------ */ 1139/* Function: ipf_htable_iter_next */ 1140/* Returns: int - 0 = success, else error */ 1141/* Parameters: softc(I) - pointer to soft context main structure */ 1142/* arg(I) - pointer to local context to use */ 1143/* token(I) - */ 1144/* ilp(I) - */ 1145/* */ 1146/* ------------------------------------------------------------------------ */ 1147static int 1148ipf_htable_iter_next(ipf_main_softc_t *softc, void *arg, ipftoken_t *token, 1149 ipflookupiter_t *ilp) 1150{ 1151 ipf_htable_softc_t *softh = arg; 1152 iphtent_t *node, zn, *nextnode; 1153 iphtable_t *iph, zp, *nextiph; 1154 void *hnext; 1155 int err; 1156 1157 err = 0; 1158 iph = NULL; 1159 node = NULL; 1160 nextiph = NULL; 1161 nextnode = NULL; 1162 1163 READ_ENTER(&softc->ipf_poolrw); 1164 1165 switch (ilp->ili_otype) 1166 { 1167 case IPFLOOKUPITER_LIST : 1168 iph = token->ipt_data; 1169 if (iph == NULL) { 1170 nextiph = softh->ipf_htables[(int)ilp->ili_unit + 1]; 1171 } else { 1172 nextiph = iph->iph_next; 1173 } 1174 1175 if (nextiph != NULL) { 1176 ATOMIC_INC(nextiph->iph_ref); 1177 token->ipt_data = nextiph; 1178 } else { 1179 bzero((char *)&zp, sizeof(zp)); 1180 nextiph = &zp; 1181 token->ipt_data = NULL; 1182 } 1183 hnext = nextiph->iph_next; 1184 break; 1185 1186 case IPFLOOKUPITER_NODE : 1187 node = token->ipt_data; 1188 if (node == NULL) { 1189 iph = ipf_htable_find(arg, ilp->ili_unit, 1190 ilp->ili_name); 1191 if (iph == NULL) { 1192 IPFERROR(30009); 1193 err = ESRCH; 1194 } else { 1195 nextnode = iph->iph_list; 1196 } 1197 } else { 1198 nextnode = node->ipe_next; 1199 } 1200 1201 if (nextnode != NULL) { 1202 ATOMIC_INC(nextnode->ipe_ref); 1203 token->ipt_data = nextnode; 1204 } else { 1205 bzero((char *)&zn, sizeof(zn)); 1206 nextnode = &zn; 1207 token->ipt_data = NULL; 1208 } 1209 hnext = nextnode->ipe_next; 1210 break; 1211 1212 default : 1213 IPFERROR(30010); 1214 err = EINVAL; 1215 hnext = NULL; 1216 break; 1217 } 1218 1219 RWLOCK_EXIT(&softc->ipf_poolrw); 1220 if (err != 0) 1221 return (err); 1222 1223 switch (ilp->ili_otype) 1224 { 1225 case IPFLOOKUPITER_LIST : 1226 err = COPYOUT(nextiph, ilp->ili_data, sizeof(*nextiph)); 1227 if (err != 0) { 1228 IPFERROR(30011); 1229 err = EFAULT; 1230 } 1231 if (iph != NULL) { 1232 WRITE_ENTER(&softc->ipf_poolrw); 1233 ipf_htable_deref(softc, softh, iph); 1234 RWLOCK_EXIT(&softc->ipf_poolrw); 1235 } 1236 break; 1237 1238 case IPFLOOKUPITER_NODE : 1239 err = COPYOUT(nextnode, ilp->ili_data, sizeof(*nextnode)); 1240 if (err != 0) { 1241 IPFERROR(30012); 1242 err = EFAULT; 1243 } 1244 if (node != NULL) { 1245 WRITE_ENTER(&softc->ipf_poolrw); 1246 ipf_htent_deref(softc, node); 1247 RWLOCK_EXIT(&softc->ipf_poolrw); 1248 } 1249 break; 1250 } 1251 1252 if (hnext == NULL) 1253 ipf_token_mark_complete(token); 1254 1255 return (err); 1256} 1257 1258 1259/* ------------------------------------------------------------------------ */ 1260/* Function: ipf_htable_iter_deref */ 1261/* Returns: int - 0 = success, else error */ 1262/* Parameters: softc(I) - pointer to soft context main structure */ 1263/* arg(I) - pointer to local context to use */ 1264/* otype(I) - which data structure type is being walked */ 1265/* unit(I) - ipfilter device to which we are working on */ 1266/* data(I) - pointer to old data structure */ 1267/* */ 1268/* ------------------------------------------------------------------------ */ 1269static int 1270ipf_htable_iter_deref(ipf_main_softc_t *softc, void *arg, int otype, int unit, 1271 void *data) 1272{ 1273 1274 if (data == NULL) 1275 return (EFAULT); 1276 1277 if (unit < -1 || unit > IPL_LOGMAX) 1278 return (EINVAL); 1279 1280 switch (otype) 1281 { 1282 case IPFLOOKUPITER_LIST : 1283 ipf_htable_deref(softc, arg, (iphtable_t *)data); 1284 break; 1285 1286 case IPFLOOKUPITER_NODE : 1287 ipf_htent_deref(arg, (iphtent_t *)data); 1288 break; 1289 default : 1290 break; 1291 } 1292 1293 return (0); 1294} 1295 1296 1297#ifdef USE_INET6 1298/* ------------------------------------------------------------------------ */ 1299/* Function: ipf_iphmfind6 */ 1300/* Parameters: iph(I) - pointer to hash table */ 1301/* addr(I) - pointer to IPv6 address */ 1302/* Locks: ipf_poolrw */ 1303/* */ 1304/* ------------------------------------------------------------------------ */ 1305static iphtent_t * 1306ipf_iphmfind6(iphtable_t *iph, i6addr_t *addr) 1307{ 1308 i6addr_t *msk, ips; 1309 iphtent_t *ipe; 1310 u_int hv; 1311 int i; 1312 1313 i = 0; 1314maskloop: 1315 msk = iph->iph_v6_masks.imt6_active + i; 1316 ips.i6[0] = addr->i6[0] & msk->i6[0]; 1317 ips.i6[1] = addr->i6[1] & msk->i6[1]; 1318 ips.i6[2] = addr->i6[2] & msk->i6[2]; 1319 ips.i6[3] = addr->i6[3] & msk->i6[3]; 1320 hv = IPE_V6_HASH_FN(ips.i6, msk->i6, iph->iph_size); 1321 for (ipe = iph->iph_table[hv]; (ipe != NULL); ipe = ipe->ipe_next) { 1322 if ((ipe->ipe_family != AF_INET6) || 1323 IP6_NEQ(&ipe->ipe_mask, msk) || 1324 IP6_NEQ(&ipe->ipe_addr, &ips)) { 1325 continue; 1326 } 1327 break; 1328 } 1329 1330 if (ipe == NULL) { 1331 i++; 1332 if (i < iph->iph_v6_masks.imt6_max) 1333 goto maskloop; 1334 } 1335 return (ipe); 1336} 1337#endif 1338 1339 1340static void 1341ipf_htable_expire(ipf_main_softc_t *softc, void *arg) 1342{ 1343 ipf_htable_softc_t *softh = arg; 1344 iphtent_t *n; 1345 1346 while ((n = softh->ipf_node_explist) != NULL) { 1347 if (n->ipe_die > softc->ipf_ticks) 1348 break; 1349 1350 ipf_htent_remove(softc, softh, n->ipe_owner, n); 1351 } 1352} 1353 1354 1355#ifndef _KERNEL 1356 1357/* ------------------------------------------------------------------------ */ 1358/* */ 1359/* ------------------------------------------------------------------------ */ 1360void 1361ipf_htable_dump(ipf_main_softc_t *softc, void *arg) 1362{ 1363 ipf_htable_softc_t *softh = arg; 1364 iphtable_t *iph; 1365 int i; 1366 1367 printf("List of configured hash tables\n"); 1368 for (i = 0; i < IPL_LOGSIZE; i++) 1369 for (iph = softh->ipf_htables[i]; iph != NULL; 1370 iph = iph->iph_next) 1371 printhash(iph, bcopywrap, NULL, opts, NULL); 1372 1373} 1374#endif 1375