1 /* $NetBSD: ip_frag.c,v 1.7 2018/06/03 10:37:23 maxv Exp $ */ 2 3 /* 4 * Copyright (C) 2012 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/errno.h> 15 #include <sys/types.h> 16 #include <sys/param.h> 17 #include <sys/time.h> 18 #include <sys/file.h> 19 #ifdef __hpux 20 # include <sys/timeout.h> 21 #endif 22 #if !defined(_KERNEL) 23 # include <stdio.h> 24 # include <string.h> 25 # include <stdlib.h> 26 # define _KERNEL 27 # ifdef __OpenBSD__ 28 struct file; 29 # endif 30 # include <sys/uio.h> 31 # undef _KERNEL 32 #endif 33 #if defined(_KERNEL) && \ 34 defined(__FreeBSD_version) && (__FreeBSD_version >= 220000) 35 # include <sys/filio.h> 36 # include <sys/fcntl.h> 37 #else 38 # include <sys/ioctl.h> 39 #endif 40 #if !defined(linux) 41 # include <sys/protosw.h> 42 #endif 43 #include <sys/socket.h> 44 #if defined(_KERNEL) 45 # include <sys/systm.h> 46 # if !defined(__SVR4) && !defined(__svr4__) 47 # include <sys/mbuf.h> 48 # endif 49 #endif 50 #if !defined(__SVR4) && !defined(__svr4__) 51 # if defined(_KERNEL) && !defined(__sgi) && !defined(AIX) 52 # include <sys/kernel.h> 53 # endif 54 #else 55 # include <sys/byteorder.h> 56 # ifdef _KERNEL 57 # include <sys/dditypes.h> 58 # endif 59 # include <sys/stream.h> 60 # include <sys/kmem.h> 61 #endif 62 #include <net/if.h> 63 #ifdef sun 64 # include <net/af.h> 65 #endif 66 #include <netinet/in.h> 67 #include <netinet/in_systm.h> 68 #include <netinet/ip.h> 69 #if !defined(linux) 70 # include <netinet/ip_var.h> 71 #endif 72 #include <netinet/tcp.h> 73 #include <netinet/udp.h> 74 #include <netinet/ip_icmp.h> 75 #include "netinet/ip_compat.h" 76 #include "netinet/ip_fil.h" 77 #include "netinet/ip_nat.h" 78 #include "netinet/ip_frag.h" 79 #include "netinet/ip_state.h" 80 #include "netinet/ip_auth.h" 81 #include "netinet/ip_lookup.h" 82 #include "netinet/ip_proxy.h" 83 #include "netinet/ip_sync.h" 84 /* END OF INCLUDES */ 85 86 #if !defined(lint) 87 #if defined(__NetBSD__) 88 #include <sys/cdefs.h> 89 __KERNEL_RCSID(0, "$NetBSD: ip_frag.c,v 1.7 2018/06/03 10:37:23 maxv Exp $"); 90 #else 91 static const char sccsid[] = "@(#)ip_frag.c 1.11 3/24/96 (C) 1993-2000 Darren Reed"; 92 static const char rcsid[] = "@(#)Id: ip_frag.c,v 1.1.1.2 2012/07/22 13:45:17 darrenr Exp"; 93 #endif 94 #endif 95 96 97 typedef struct ipf_frag_softc_s { 98 ipfrwlock_t ipfr_ipidfrag; 99 ipfrwlock_t ipfr_frag; 100 ipfrwlock_t ipfr_natfrag; 101 int ipfr_size; 102 int ipfr_ttl; 103 int ipfr_lock; 104 int ipfr_inited; 105 ipfr_t *ipfr_list; 106 ipfr_t **ipfr_tail; 107 ipfr_t *ipfr_natlist; 108 ipfr_t **ipfr_nattail; 109 ipfr_t *ipfr_ipidlist; 110 ipfr_t **ipfr_ipidtail; 111 ipfr_t **ipfr_heads; 112 ipfr_t **ipfr_nattab; 113 ipfr_t **ipfr_ipidtab; 114 ipfrstat_t ipfr_stats; 115 } ipf_frag_softc_t; 116 117 118 #ifdef USE_MUTEXES 119 static ipfr_t *ipfr_frag_new(ipf_main_softc_t *, ipf_frag_softc_t *, 120 fr_info_t *, u_32_t, ipfr_t **, 121 ipfrwlock_t *); 122 static ipfr_t *ipf_frag_lookup(ipf_main_softc_t *, ipf_frag_softc_t *, fr_info_t *, ipfr_t **, ipfrwlock_t *); 123 static void ipf_frag_deref(void *, ipfr_t **, ipfrwlock_t *); 124 static int ipf_frag_next(ipf_main_softc_t *, ipftoken_t *, ipfgeniter_t *, 125 ipfr_t **, ipfrwlock_t *); 126 #else 127 static ipfr_t *ipfr_frag_new(ipf_main_softc_t *, ipf_frag_softc_t *, 128 fr_info_t *, u_32_t, ipfr_t **); 129 static ipfr_t *ipf_frag_lookup(ipf_main_softc_t *, ipf_frag_softc_t *, fr_info_t *, ipfr_t **); 130 static void ipf_frag_deref(void *, ipfr_t **); 131 static int ipf_frag_next(ipf_main_softc_t *, ipftoken_t *, ipfgeniter_t *, 132 ipfr_t **); 133 #endif 134 static void ipf_frag_delete(ipf_main_softc_t *, ipfr_t *, ipfr_t ***); 135 static void ipf_frag_free(ipf_frag_softc_t *, ipfr_t *); 136 137 static frentry_t ipfr_block; 138 139 #define FBUMP(x) softf->ipfr_stats.x++ 140 #define FBUMPD(x) do { softf->ipfr_stats.x++; DT(x); } while (0) 141 142 143 /* ------------------------------------------------------------------------ */ 144 /* Function: ipf_frag_main_load */ 145 /* Returns: int - 0 == success, -1 == error */ 146 /* Parameters: Nil */ 147 /* */ 148 /* Initialise the filter rule associted with blocked packets - everyone can */ 149 /* use it. */ 150 /* ------------------------------------------------------------------------ */ 151 int 152 ipf_frag_main_load(void) 153 { 154 bzero((char *)&ipfr_block, sizeof(ipfr_block)); 155 ipfr_block.fr_flags = FR_BLOCK|FR_QUICK; 156 ipfr_block.fr_ref = 1; 157 158 return 0; 159 } 160 161 162 /* ------------------------------------------------------------------------ */ 163 /* Function: ipf_frag_main_unload */ 164 /* Returns: int - 0 == success, -1 == error */ 165 /* Parameters: Nil */ 166 /* */ 167 /* A null-op function that exists as a placeholder so that the flow in */ 168 /* other functions is obvious. */ 169 /* ------------------------------------------------------------------------ */ 170 int 171 ipf_frag_main_unload(void) 172 { 173 return 0; 174 } 175 176 177 /* ------------------------------------------------------------------------ */ 178 /* Function: ipf_frag_soft_create */ 179 /* Returns: void * - NULL = failure, else pointer to local context */ 180 /* Parameters: softc(I) - pointer to soft context main structure */ 181 /* */ 182 /* Allocate a new soft context structure to track fragment related info. */ 183 /* ------------------------------------------------------------------------ */ 184 /*ARGSUSED*/ 185 void * 186 ipf_frag_soft_create(ipf_main_softc_t *softc) 187 { 188 ipf_frag_softc_t *softf; 189 190 KMALLOC(softf, ipf_frag_softc_t *); 191 if (softf == NULL) 192 return NULL; 193 194 bzero((char *)softf, sizeof(*softf)); 195 196 RWLOCK_INIT(&softf->ipfr_ipidfrag, "frag ipid lock"); 197 RWLOCK_INIT(&softf->ipfr_frag, "ipf fragment rwlock"); 198 RWLOCK_INIT(&softf->ipfr_natfrag, "ipf NAT fragment rwlock"); 199 200 softf->ipfr_size = IPFT_SIZE; 201 softf->ipfr_ttl = IPF_TTLVAL(60); 202 softf->ipfr_lock = 1; 203 softf->ipfr_tail = &softf->ipfr_list; 204 softf->ipfr_nattail = &softf->ipfr_natlist; 205 softf->ipfr_ipidtail = &softf->ipfr_ipidlist; 206 207 return softf; 208 } 209 210 211 /* ------------------------------------------------------------------------ */ 212 /* Function: ipf_frag_soft_destroy */ 213 /* Returns: Nil */ 214 /* Parameters: softc(I) - pointer to soft context main structure */ 215 /* arg(I) - pointer to local context to use */ 216 /* */ 217 /* Initialise the hash tables for the fragment cache lookups. */ 218 /* ------------------------------------------------------------------------ */ 219 void 220 ipf_frag_soft_destroy(ipf_main_softc_t *softc, void *arg) 221 { 222 ipf_frag_softc_t *softf = arg; 223 224 RW_DESTROY(&softf->ipfr_ipidfrag); 225 RW_DESTROY(&softf->ipfr_frag); 226 RW_DESTROY(&softf->ipfr_natfrag); 227 228 KFREE(softf); 229 } 230 231 232 /* ------------------------------------------------------------------------ */ 233 /* Function: ipf_frag_soft_init */ 234 /* Returns: int - 0 == success, -1 == error */ 235 /* Parameters: softc(I) - pointer to soft context main structure */ 236 /* arg(I) - pointer to local context to use */ 237 /* */ 238 /* Initialise the hash tables for the fragment cache lookups. */ 239 /* ------------------------------------------------------------------------ */ 240 /*ARGSUSED*/ 241 int 242 ipf_frag_soft_init(ipf_main_softc_t *softc, void *arg) 243 { 244 ipf_frag_softc_t *softf = arg; 245 246 KMALLOCS(softf->ipfr_heads, ipfr_t **, 247 softf->ipfr_size * sizeof(ipfr_t *)); 248 if (softf->ipfr_heads == NULL) 249 return -1; 250 251 bzero((char *)softf->ipfr_heads, softf->ipfr_size * sizeof(ipfr_t *)); 252 253 KMALLOCS(softf->ipfr_nattab, ipfr_t **, 254 softf->ipfr_size * sizeof(ipfr_t *)); 255 if (softf->ipfr_nattab == NULL) 256 return -2; 257 258 bzero((char *)softf->ipfr_nattab, softf->ipfr_size * sizeof(ipfr_t *)); 259 260 KMALLOCS(softf->ipfr_ipidtab, ipfr_t **, 261 softf->ipfr_size * sizeof(ipfr_t *)); 262 if (softf->ipfr_ipidtab == NULL) 263 return -3; 264 265 bzero((char *)softf->ipfr_ipidtab, 266 softf->ipfr_size * sizeof(ipfr_t *)); 267 268 softf->ipfr_lock = 0; 269 softf->ipfr_inited = 1; 270 271 return 0; 272 } 273 274 275 /* ------------------------------------------------------------------------ */ 276 /* Function: ipf_frag_soft_fini */ 277 /* Returns: int - 0 == success, -1 == error */ 278 /* Parameters: softc(I) - pointer to soft context main structure */ 279 /* arg(I) - pointer to local context to use */ 280 /* */ 281 /* Free all memory allocated whilst running and from initialisation. */ 282 /* ------------------------------------------------------------------------ */ 283 int 284 ipf_frag_soft_fini(ipf_main_softc_t *softc, void *arg) 285 { 286 ipf_frag_softc_t *softf = arg; 287 288 softf->ipfr_lock = 1; 289 290 if (softf->ipfr_inited == 1) { 291 ipf_frag_clear(softc); 292 293 softf->ipfr_inited = 0; 294 } 295 296 if (softf->ipfr_heads != NULL) 297 KFREES(softf->ipfr_heads, 298 softf->ipfr_size * sizeof(ipfr_t *)); 299 softf->ipfr_heads = NULL; 300 301 if (softf->ipfr_nattab != NULL) 302 KFREES(softf->ipfr_nattab, 303 softf->ipfr_size * sizeof(ipfr_t *)); 304 softf->ipfr_nattab = NULL; 305 306 if (softf->ipfr_ipidtab != NULL) 307 KFREES(softf->ipfr_ipidtab, 308 softf->ipfr_size * sizeof(ipfr_t *)); 309 softf->ipfr_ipidtab = NULL; 310 311 return 0; 312 } 313 314 315 /* ------------------------------------------------------------------------ */ 316 /* Function: ipf_frag_set_lock */ 317 /* Returns: Nil */ 318 /* Parameters: arg(I) - pointer to local context to use */ 319 /* tmp(I) - new value for lock */ 320 /* */ 321 /* Stub function that allows for external manipulation of ipfr_lock */ 322 /* ------------------------------------------------------------------------ */ 323 void 324 ipf_frag_setlock(void *arg, int tmp) 325 { 326 ipf_frag_softc_t *softf = arg; 327 328 softf->ipfr_lock = tmp; 329 } 330 331 332 /* ------------------------------------------------------------------------ */ 333 /* Function: ipf_frag_stats */ 334 /* Returns: ipfrstat_t* - pointer to struct with current frag stats */ 335 /* Parameters: arg(I) - pointer to local context to use */ 336 /* */ 337 /* Updates ipfr_stats with current information and returns a pointer to it */ 338 /* ------------------------------------------------------------------------ */ 339 ipfrstat_t * 340 ipf_frag_stats(void *arg) 341 { 342 ipf_frag_softc_t *softf = arg; 343 344 softf->ipfr_stats.ifs_table = softf->ipfr_heads; 345 softf->ipfr_stats.ifs_nattab = softf->ipfr_nattab; 346 return &softf->ipfr_stats; 347 } 348 349 350 /* ------------------------------------------------------------------------ */ 351 /* Function: ipfr_frag_new */ 352 /* Returns: ipfr_t * - pointer to fragment cache state info or NULL */ 353 /* Parameters: fin(I) - pointer to packet information */ 354 /* table(I) - pointer to frag table to add to */ 355 /* lock(I) - pointer to lock to get a write hold of */ 356 /* */ 357 /* Add a new entry to the fragment cache, registering it as having come */ 358 /* through this box, with the result of the filter operation. */ 359 /* */ 360 /* If this function succeeds, it returns with a write lock held on "lock". */ 361 /* If it fails, no lock is held on return. */ 362 /* ------------------------------------------------------------------------ */ 363 static ipfr_t * 364 ipfr_frag_new( 365 ipf_main_softc_t *softc, 366 ipf_frag_softc_t *softf, 367 fr_info_t *fin, 368 u_32_t pass, 369 ipfr_t *table[] 370 #ifdef USE_MUTEXES 371 , ipfrwlock_t *lock 372 #endif 373 ) 374 { 375 ipfr_t *fra, frag, *fran; 376 u_int idx, off; 377 frentry_t *fr; 378 379 if (softf->ipfr_stats.ifs_inuse >= softf->ipfr_size) { 380 FBUMPD(ifs_maximum); 381 return NULL; 382 } 383 384 if ((fin->fin_flx & (FI_FRAG|FI_BAD)) != FI_FRAG) { 385 FBUMPD(ifs_newbad); 386 return NULL; 387 } 388 389 if (pass & FR_FRSTRICT) { 390 if (fin->fin_off != 0) { 391 FBUMPD(ifs_newrestrictnot0); 392 return NULL; 393 } 394 } 395 396 frag.ipfr_v = fin->fin_v; 397 idx = fin->fin_v; 398 frag.ipfr_p = fin->fin_p; 399 idx += fin->fin_p; 400 frag.ipfr_id = fin->fin_id; 401 idx += fin->fin_id; 402 frag.ipfr_source = fin->fin_fi.fi_src; 403 idx += frag.ipfr_src.s_addr; 404 frag.ipfr_dest = fin->fin_fi.fi_dst; 405 idx += frag.ipfr_dst.s_addr; 406 frag.ipfr_ifp = fin->fin_ifp; 407 idx *= 127; 408 idx %= softf->ipfr_size; 409 410 frag.ipfr_optmsk = fin->fin_fi.fi_optmsk & IPF_OPTCOPY; 411 frag.ipfr_secmsk = fin->fin_fi.fi_secmsk; 412 frag.ipfr_auth = fin->fin_fi.fi_auth; 413 414 off = fin->fin_off >> 3; 415 if (off == 0) { 416 char *ptr; 417 int end; 418 419 #ifdef USE_INET6 420 if (fin->fin_v == 6) { 421 422 ptr = (char *)fin->fin_fraghdr + 423 sizeof(struct ip6_frag); 424 } else 425 #endif 426 { 427 ptr = fin->fin_dp; 428 } 429 end = fin->fin_plen - (ptr - (char *)fin->fin_ip); 430 frag.ipfr_firstend = end >> 3; 431 } else { 432 frag.ipfr_firstend = 0; 433 } 434 435 /* 436 * allocate some memory, if possible, if not, just record that we 437 * failed to do so. 438 */ 439 KMALLOC(fran, ipfr_t *); 440 if (fran == NULL) { 441 FBUMPD(ifs_nomem); 442 return NULL; 443 } 444 445 WRITE_ENTER(lock); 446 447 /* 448 * first, make sure it isn't already there... 449 */ 450 for (fra = table[idx]; (fra != NULL); fra = fra->ipfr_hnext) 451 if (!bcmp((char *)&frag.ipfr_ifp, (char *)&fra->ipfr_ifp, 452 IPFR_CMPSZ)) { 453 RWLOCK_EXIT(lock); 454 FBUMPD(ifs_exists); 455 KFREE(fran); 456 return NULL; 457 } 458 459 fra = fran; 460 fran = NULL; 461 fr = fin->fin_fr; 462 fra->ipfr_rule = fr; 463 if (fr != NULL) { 464 MUTEX_ENTER(&fr->fr_lock); 465 fr->fr_ref++; 466 MUTEX_EXIT(&fr->fr_lock); 467 } 468 469 /* 470 * Insert the fragment into the fragment table, copy the struct used 471 * in the search using bcopy rather than reassign each field. 472 * Set the ttl to the default. 473 */ 474 if ((fra->ipfr_hnext = table[idx]) != NULL) 475 table[idx]->ipfr_hprev = &fra->ipfr_hnext; 476 fra->ipfr_hprev = table + idx; 477 fra->ipfr_data = NULL; 478 table[idx] = fra; 479 bcopy((char *)&frag.ipfr_ifp, (char *)&fra->ipfr_ifp, IPFR_CMPSZ); 480 fra->ipfr_v = fin->fin_v; 481 fra->ipfr_ttl = softc->ipf_ticks + softf->ipfr_ttl; 482 fra->ipfr_firstend = frag.ipfr_firstend; 483 484 /* 485 * Compute the offset of the expected start of the next packet. 486 */ 487 if (off == 0) 488 fra->ipfr_seen0 = 1; 489 fra->ipfr_off = off + (fin->fin_dlen >> 3); 490 fra->ipfr_pass = pass; 491 fra->ipfr_ref = 1; 492 fra->ipfr_pkts = 1; 493 fra->ipfr_bytes = fin->fin_plen; 494 FBUMP(ifs_inuse); 495 FBUMP(ifs_new); 496 return fra; 497 } 498 499 500 /* ------------------------------------------------------------------------ */ 501 /* Function: ipf_frag_new */ 502 /* Returns: int - 0 == success, -1 == error */ 503 /* Parameters: fin(I) - pointer to packet information */ 504 /* */ 505 /* Add a new entry to the fragment cache table based on the current packet */ 506 /* ------------------------------------------------------------------------ */ 507 int 508 ipf_frag_new(ipf_main_softc_t *softc, fr_info_t *fin, u_32_t pass) 509 { 510 ipf_frag_softc_t *softf = softc->ipf_frag_soft; 511 ipfr_t *fra; 512 513 if (softf->ipfr_lock != 0) 514 return -1; 515 516 #ifdef USE_MUTEXES 517 fra = ipfr_frag_new(softc, softf, fin, pass, softf->ipfr_heads, &softc->ipf_frag); 518 #else 519 fra = ipfr_frag_new(softc, softf, fin, pass, softf->ipfr_heads); 520 #endif 521 if (fra != NULL) { 522 *softf->ipfr_tail = fra; 523 fra->ipfr_prev = softf->ipfr_tail; 524 softf->ipfr_tail = &fra->ipfr_next; 525 fra->ipfr_next = NULL; 526 RWLOCK_EXIT(&softc->ipf_frag); 527 } 528 return fra ? 0 : -1; 529 } 530 531 532 /* ------------------------------------------------------------------------ */ 533 /* Function: ipf_frag_natnew */ 534 /* Returns: int - 0 == success, -1 == error */ 535 /* Parameters: fin(I) - pointer to packet information */ 536 /* nat(I) - pointer to NAT structure */ 537 /* */ 538 /* Create a new NAT fragment cache entry based on the current packet and */ 539 /* the NAT structure for this "session". */ 540 /* ------------------------------------------------------------------------ */ 541 int 542 ipf_frag_natnew(ipf_main_softc_t *softc, fr_info_t *fin, u_32_t pass, 543 nat_t *nat) 544 { 545 ipf_frag_softc_t *softf = softc->ipf_frag_soft; 546 ipfr_t *fra; 547 548 if (softf->ipfr_lock != 0) 549 return 0; 550 551 #ifdef USE_MUTEXES 552 fra = ipfr_frag_new(softc, softf, fin, pass, softf->ipfr_nattab, 553 &softf->ipfr_natfrag); 554 #else 555 fra = ipfr_frag_new(softc, softf, fin, pass, softf->ipfr_nattab); 556 #endif 557 if (fra != NULL) { 558 fra->ipfr_data = nat; 559 nat->nat_data = fra; 560 *softf->ipfr_nattail = fra; 561 fra->ipfr_prev = softf->ipfr_nattail; 562 softf->ipfr_nattail = &fra->ipfr_next; 563 fra->ipfr_next = NULL; 564 RWLOCK_EXIT(&softf->ipfr_natfrag); 565 return 0; 566 } 567 return -1; 568 } 569 570 571 /* ------------------------------------------------------------------------ */ 572 /* Function: ipf_frag_ipidnew */ 573 /* Returns: int - 0 == success, -1 == error */ 574 /* Parameters: fin(I) - pointer to packet information */ 575 /* ipid(I) - new IP ID for this fragmented packet */ 576 /* */ 577 /* Create a new fragment cache entry for this packet and store, as a data */ 578 /* pointer, the new IP ID value. */ 579 /* ------------------------------------------------------------------------ */ 580 int 581 ipf_frag_ipidnew(fr_info_t *fin, u_32_t ipid) 582 { 583 ipf_main_softc_t *softc = fin->fin_main_soft; 584 ipf_frag_softc_t *softf = softc->ipf_frag_soft; 585 ipfr_t *fra; 586 587 if (softf->ipfr_lock) 588 return 0; 589 590 #ifdef USE_MUTEXES 591 fra = ipfr_frag_new(softc, softf, fin, 0, softf->ipfr_ipidtab, &softf->ipfr_ipidfrag); 592 #else 593 fra = ipfr_frag_new(softc, softf, fin, 0, softf->ipfr_ipidtab); 594 #endif 595 if (fra != NULL) { 596 fra->ipfr_data = (void *)(intptr_t)ipid; 597 *softf->ipfr_ipidtail = fra; 598 fra->ipfr_prev = softf->ipfr_ipidtail; 599 softf->ipfr_ipidtail = &fra->ipfr_next; 600 fra->ipfr_next = NULL; 601 RWLOCK_EXIT(&softf->ipfr_ipidfrag); 602 } 603 return fra ? 0 : -1; 604 } 605 606 607 /* ------------------------------------------------------------------------ */ 608 /* Function: ipf_frag_lookup */ 609 /* Returns: ipfr_t * - pointer to ipfr_t structure if there's a */ 610 /* matching entry in the frag table, else NULL */ 611 /* Parameters: fin(I) - pointer to packet information */ 612 /* table(I) - pointer to fragment cache table to search */ 613 /* */ 614 /* Check the fragment cache to see if there is already a record of this */ 615 /* packet with its filter result known. */ 616 /* */ 617 /* If this function succeeds, it returns with a write lock held on "lock". */ 618 /* If it fails, no lock is held on return. */ 619 /* ------------------------------------------------------------------------ */ 620 static ipfr_t * 621 ipf_frag_lookup( 622 ipf_main_softc_t *softc, 623 ipf_frag_softc_t *softf, 624 fr_info_t *fin, 625 ipfr_t *table[] 626 #ifdef USE_MUTEXES 627 , ipfrwlock_t *lock 628 #endif 629 ) 630 { 631 ipfr_t *f, frag; 632 u_int idx; 633 634 /* 635 * We don't want to let short packets match because they could be 636 * compromising the security of other rules that want to match on 637 * layer 4 fields (and can't because they have been fragmented off.) 638 * Why do this check here? The counter acts as an indicator of this 639 * kind of attack, whereas if it was elsewhere, it wouldn't know if 640 * other matching packets had been seen. 641 */ 642 if (fin->fin_flx & FI_SHORT) { 643 FBUMPD(ifs_short); 644 return NULL; 645 } 646 647 if ((fin->fin_flx & FI_BAD) != 0) { 648 FBUMPD(ifs_bad); 649 return NULL; 650 } 651 652 /* 653 * For fragments, we record protocol, packet id, TOS and both IP#'s 654 * (these should all be the same for all fragments of a packet). 655 * 656 * build up a hash value to index the table with. 657 */ 658 frag.ipfr_v = fin->fin_v; 659 idx = fin->fin_v; 660 frag.ipfr_p = fin->fin_p; 661 idx += fin->fin_p; 662 frag.ipfr_id = fin->fin_id; 663 idx += fin->fin_id; 664 frag.ipfr_source = fin->fin_fi.fi_src; 665 idx += frag.ipfr_src.s_addr; 666 frag.ipfr_dest = fin->fin_fi.fi_dst; 667 idx += frag.ipfr_dst.s_addr; 668 frag.ipfr_ifp = fin->fin_ifp; 669 idx *= 127; 670 idx %= softf->ipfr_size; 671 672 frag.ipfr_optmsk = fin->fin_fi.fi_optmsk & IPF_OPTCOPY; 673 frag.ipfr_secmsk = fin->fin_fi.fi_secmsk; 674 frag.ipfr_auth = fin->fin_fi.fi_auth; 675 676 READ_ENTER(lock); 677 678 /* 679 * check the table, careful to only compare the right amount of data 680 */ 681 for (f = table[idx]; f; f = f->ipfr_hnext) { 682 if (!bcmp((char *)&frag.ipfr_ifp, (char *)&f->ipfr_ifp, 683 IPFR_CMPSZ)) { 684 u_short off; 685 686 /* 687 * XXX - We really need to be guarding against the 688 * retransmission of (src,dst,id,offset-range) here 689 * because a fragmented packet is never resent with 690 * the same IP ID# (or shouldn't). 691 */ 692 off = fin->fin_off >> 3; 693 if (f->ipfr_seen0) { 694 if (off == 0) { 695 FBUMPD(ifs_retrans0); 696 continue; 697 } 698 699 /* 700 * Case 3. See comment for frpr_fragment6. 701 */ 702 if ((f->ipfr_firstend != 0) && 703 (off < f->ipfr_firstend)) { 704 FBUMP(ifs_overlap); 705 DT2(ifs_overlap, u_short, off, 706 ipfr_t *, f); 707 fin->fin_flx |= FI_BAD; 708 break; 709 } 710 } else if (off == 0) 711 f->ipfr_seen0 = 1; 712 713 #if 0 714 /* We can't do this, since we only have a read lock! */ 715 if (f != table[idx]) { 716 ipfr_t **fp; 717 718 /* 719 * Move fragment info. to the top of the list 720 * to speed up searches. First, delink... 721 */ 722 fp = f->ipfr_hprev; 723 (*fp) = f->ipfr_hnext; 724 if (f->ipfr_hnext != NULL) 725 f->ipfr_hnext->ipfr_hprev = fp; 726 /* 727 * Then put back at the top of the chain. 728 */ 729 f->ipfr_hnext = table[idx]; 730 table[idx]->ipfr_hprev = &f->ipfr_hnext; 731 f->ipfr_hprev = table + idx; 732 table[idx] = f; 733 } 734 #endif 735 736 /* 737 * If we've followed the fragments, and this is the 738 * last (in order), shrink expiration time. 739 */ 740 if (off == f->ipfr_off) { 741 f->ipfr_off = (fin->fin_dlen >> 3) + off; 742 743 /* 744 * Well, we could shrink the expiration time 745 * but only if every fragment has been seen 746 * in order upto this, the last. ipfr_badorder 747 * is used here to count those out of order 748 * and if it equals 0 when we get to the last 749 * fragment then we can assume all of the 750 * fragments have been seen and in order. 751 */ 752 #if 0 753 /* 754 * Doing this properly requires moving it to 755 * the head of the list which is infesible. 756 */ 757 if ((more == 0) && (f->ipfr_badorder == 0)) 758 f->ipfr_ttl = softc->ipf_ticks + 1; 759 #endif 760 } else { 761 f->ipfr_badorder++; 762 FBUMPD(ifs_unordered); 763 if (f->ipfr_pass & FR_FRSTRICT) { 764 FBUMPD(ifs_strict); 765 continue; 766 } 767 } 768 f->ipfr_pkts++; 769 f->ipfr_bytes += fin->fin_plen; 770 FBUMP(ifs_hits); 771 return f; 772 } 773 } 774 775 RWLOCK_EXIT(lock); 776 FBUMP(ifs_miss); 777 return NULL; 778 } 779 780 781 /* ------------------------------------------------------------------------ */ 782 /* Function: ipf_frag_natknown */ 783 /* Returns: nat_t* - pointer to 'parent' NAT structure if frag table */ 784 /* match found, else NULL */ 785 /* Parameters: fin(I) - pointer to packet information */ 786 /* */ 787 /* Functional interface for NAT lookups of the NAT fragment cache */ 788 /* ------------------------------------------------------------------------ */ 789 nat_t * 790 ipf_frag_natknown(fr_info_t *fin) 791 { 792 ipf_main_softc_t *softc = fin->fin_main_soft; 793 ipf_frag_softc_t *softf = softc->ipf_frag_soft; 794 nat_t *nat; 795 ipfr_t *ipf; 796 797 if ((softf->ipfr_lock) || !softf->ipfr_natlist) 798 return NULL; 799 #ifdef USE_MUTEXES 800 ipf = ipf_frag_lookup(softc, softf, fin, softf->ipfr_nattab, 801 &softf->ipfr_natfrag); 802 #else 803 ipf = ipf_frag_lookup(softc, softf, fin, softf->ipfr_nattab); 804 #endif 805 if (ipf != NULL) { 806 nat = ipf->ipfr_data; 807 /* 808 * This is the last fragment for this packet. 809 */ 810 if ((ipf->ipfr_ttl == softc->ipf_ticks + 1) && (nat != NULL)) { 811 nat->nat_data = NULL; 812 ipf->ipfr_data = NULL; 813 } 814 RWLOCK_EXIT(&softf->ipfr_natfrag); 815 } else 816 nat = NULL; 817 return nat; 818 } 819 820 821 /* ------------------------------------------------------------------------ */ 822 /* Function: ipf_frag_ipidknown */ 823 /* Returns: u_32_t - IPv4 ID for this packet if match found, else */ 824 /* return 0xfffffff to indicate no match. */ 825 /* Parameters: fin(I) - pointer to packet information */ 826 /* */ 827 /* Functional interface for IP ID lookups of the IP ID fragment cache */ 828 /* ------------------------------------------------------------------------ */ 829 u_32_t 830 ipf_frag_ipidknown(fr_info_t *fin) 831 { 832 ipf_main_softc_t *softc = fin->fin_main_soft; 833 ipf_frag_softc_t *softf = softc->ipf_frag_soft; 834 ipfr_t *ipf; 835 u_32_t id; 836 837 if (softf->ipfr_lock || !softf->ipfr_ipidlist) 838 return 0xffffffff; 839 840 #ifdef USE_MUTEXES 841 ipf = ipf_frag_lookup(softc, softf, fin, softf->ipfr_ipidtab, 842 &softf->ipfr_ipidfrag); 843 #else 844 ipf = ipf_frag_lookup(softc, softf, fin, softf->ipfr_ipidtab); 845 #endif 846 if (ipf != NULL) { 847 id = (u_32_t)(intptr_t)ipf->ipfr_data; 848 RWLOCK_EXIT(&softf->ipfr_ipidfrag); 849 } else 850 id = 0xffffffff; 851 return id; 852 } 853 854 855 /* ------------------------------------------------------------------------ */ 856 /* Function: ipf_frag_known */ 857 /* Returns: frentry_t* - pointer to filter rule if a match is found in */ 858 /* the frag cache table, else NULL. */ 859 /* Parameters: fin(I) - pointer to packet information */ 860 /* passp(O) - pointer to where to store rule flags resturned */ 861 /* */ 862 /* Functional interface for normal lookups of the fragment cache. If a */ 863 /* match is found, return the rule pointer and flags from the rule, except */ 864 /* that if FR_LOGFIRST is set, reset FR_LOG. */ 865 /* ------------------------------------------------------------------------ */ 866 frentry_t * 867 ipf_frag_known(fr_info_t *fin, u_32_t *passp) 868 { 869 ipf_main_softc_t *softc = fin->fin_main_soft; 870 ipf_frag_softc_t *softf = softc->ipf_frag_soft; 871 frentry_t *fr = NULL; 872 ipfr_t *fra; 873 u_32_t pass; 874 875 if ((softf->ipfr_lock) || (softf->ipfr_list == NULL)) 876 return NULL; 877 878 #ifdef USE_MUTEXES 879 fra = ipf_frag_lookup(softc, softf, fin, softf->ipfr_heads, 880 &softc->ipf_frag); 881 #else 882 fra = ipf_frag_lookup(softc, softf, fin, softf->ipfr_heads); 883 #endif 884 if (fra != NULL) { 885 if (fin->fin_flx & FI_BAD) { 886 fr = &ipfr_block; 887 fin->fin_reason = FRB_BADFRAG; 888 } else { 889 fr = fra->ipfr_rule; 890 } 891 fin->fin_fr = fr; 892 if (fr != NULL) { 893 pass = fr->fr_flags; 894 if ((pass & FR_KEEPSTATE) != 0) { 895 fin->fin_flx |= FI_STATE; 896 /* 897 * Reset the keep state flag here so that we 898 * don't try and add a new state entry because 899 * of a match here. That leads to blocking of 900 * the packet later because the add fails. 901 */ 902 pass &= ~FR_KEEPSTATE; 903 } 904 if ((pass & FR_LOGFIRST) != 0) 905 pass &= ~(FR_LOGFIRST|FR_LOG); 906 *passp = pass; 907 } 908 RWLOCK_EXIT(&softc->ipf_frag); 909 } 910 return fr; 911 } 912 913 914 /* ------------------------------------------------------------------------ */ 915 /* Function: ipf_frag_natforget */ 916 /* Returns: Nil */ 917 /* Parameters: ptr(I) - pointer to data structure */ 918 /* */ 919 /* Search through all of the fragment cache entries for NAT and wherever a */ 920 /* pointer is found to match ptr, reset it to NULL. */ 921 /* ------------------------------------------------------------------------ */ 922 void 923 ipf_frag_natforget(ipf_main_softc_t *softc, void *ptr) 924 { 925 ipf_frag_softc_t *softf = softc->ipf_frag_soft; 926 ipfr_t *fr; 927 928 WRITE_ENTER(&softf->ipfr_natfrag); 929 for (fr = softf->ipfr_natlist; fr; fr = fr->ipfr_next) 930 if (fr->ipfr_data == ptr) 931 fr->ipfr_data = NULL; 932 RWLOCK_EXIT(&softf->ipfr_natfrag); 933 } 934 935 936 /* ------------------------------------------------------------------------ */ 937 /* Function: ipf_frag_delete */ 938 /* Returns: Nil */ 939 /* Parameters: fra(I) - pointer to fragment structure to delete */ 940 /* tail(IO) - pointer to the pointer to the tail of the frag */ 941 /* list */ 942 /* */ 943 /* Remove a fragment cache table entry from the table & list. Also free */ 944 /* the filter rule it is associated with it if it is no longer used as a */ 945 /* result of decreasing the reference count. */ 946 /* ------------------------------------------------------------------------ */ 947 static void 948 ipf_frag_delete(ipf_main_softc_t *softc, ipfr_t *fra, ipfr_t ***tail) 949 { 950 ipf_frag_softc_t *softf = softc->ipf_frag_soft; 951 952 if (fra->ipfr_next) 953 fra->ipfr_next->ipfr_prev = fra->ipfr_prev; 954 *fra->ipfr_prev = fra->ipfr_next; 955 if (*tail == &fra->ipfr_next) 956 *tail = fra->ipfr_prev; 957 958 if (fra->ipfr_hnext) 959 fra->ipfr_hnext->ipfr_hprev = fra->ipfr_hprev; 960 *fra->ipfr_hprev = fra->ipfr_hnext; 961 962 if (fra->ipfr_rule != NULL) { 963 (void) ipf_derefrule(softc, &fra->ipfr_rule); 964 } 965 966 if (fra->ipfr_ref <= 0) 967 ipf_frag_free(softf, fra); 968 } 969 970 971 /* ------------------------------------------------------------------------ */ 972 /* Function: ipf_frag_free */ 973 /* Returns: Nil */ 974 /* */ 975 /* ------------------------------------------------------------------------ */ 976 static void 977 ipf_frag_free(ipf_frag_softc_t *softf, ipfr_t *fra) 978 { 979 KFREE(fra); 980 FBUMP(ifs_expire); 981 softf->ipfr_stats.ifs_inuse--; 982 } 983 984 985 /* ------------------------------------------------------------------------ */ 986 /* Function: ipf_frag_clear */ 987 /* Returns: Nil */ 988 /* Parameters: Nil */ 989 /* */ 990 /* Free memory in use by fragment state information kept. Do the normal */ 991 /* fragment state stuff first and then the NAT-fragment table. */ 992 /* ------------------------------------------------------------------------ */ 993 void 994 ipf_frag_clear(ipf_main_softc_t *softc) 995 { 996 ipf_frag_softc_t *softf = softc->ipf_frag_soft; 997 ipfr_t *fra; 998 nat_t *nat; 999 1000 WRITE_ENTER(&softc->ipf_frag); 1001 while ((fra = softf->ipfr_list) != NULL) { 1002 fra->ipfr_ref--; 1003 ipf_frag_delete(softc, fra, &softf->ipfr_tail); 1004 } 1005 softf->ipfr_tail = &softf->ipfr_list; 1006 RWLOCK_EXIT(&softc->ipf_frag); 1007 1008 WRITE_ENTER(&softc->ipf_nat); 1009 WRITE_ENTER(&softf->ipfr_natfrag); 1010 while ((fra = softf->ipfr_natlist) != NULL) { 1011 nat = fra->ipfr_data; 1012 if (nat != NULL) { 1013 if (nat->nat_data == fra) 1014 nat->nat_data = NULL; 1015 } 1016 fra->ipfr_ref--; 1017 ipf_frag_delete(softc, fra, &softf->ipfr_nattail); 1018 } 1019 softf->ipfr_nattail = &softf->ipfr_natlist; 1020 RWLOCK_EXIT(&softf->ipfr_natfrag); 1021 RWLOCK_EXIT(&softc->ipf_nat); 1022 } 1023 1024 1025 /* ------------------------------------------------------------------------ */ 1026 /* Function: ipf_frag_expire */ 1027 /* Returns: Nil */ 1028 /* Parameters: Nil */ 1029 /* */ 1030 /* Expire entries in the fragment cache table that have been there too long */ 1031 /* ------------------------------------------------------------------------ */ 1032 void 1033 ipf_frag_expire(ipf_main_softc_t *softc) 1034 { 1035 ipf_frag_softc_t *softf = softc->ipf_frag_soft; 1036 ipfr_t **fp, *fra; 1037 nat_t *nat; 1038 SPL_INT(s); 1039 1040 if (softf->ipfr_lock) 1041 return; 1042 1043 SPL_NET(s); 1044 WRITE_ENTER(&softc->ipf_frag); 1045 /* 1046 * Go through the entire table, looking for entries to expire, 1047 * which is indicated by the ttl being less than or equal to ipf_ticks. 1048 */ 1049 for (fp = &softf->ipfr_list; ((fra = *fp) != NULL); ) { 1050 if (fra->ipfr_ttl > softc->ipf_ticks) 1051 break; 1052 fra->ipfr_ref--; 1053 ipf_frag_delete(softc, fra, &softf->ipfr_tail); 1054 } 1055 RWLOCK_EXIT(&softc->ipf_frag); 1056 1057 WRITE_ENTER(&softf->ipfr_ipidfrag); 1058 for (fp = &softf->ipfr_ipidlist; ((fra = *fp) != NULL); ) { 1059 if (fra->ipfr_ttl > softc->ipf_ticks) 1060 break; 1061 fra->ipfr_ref--; 1062 ipf_frag_delete(softc, fra, &softf->ipfr_ipidtail); 1063 } 1064 RWLOCK_EXIT(&softf->ipfr_ipidfrag); 1065 1066 /* 1067 * Same again for the NAT table, except that if the structure also 1068 * still points to a NAT structure, and the NAT structure points back 1069 * at the one to be free'd, NULL the reference from the NAT struct. 1070 * NOTE: We need to grab both mutex's early, and in this order so as 1071 * to prevent a deadlock if both try to expire at the same time. 1072 * The extra if() statement here is because it locks out all NAT 1073 * operations - no need to do that if there are no entries in this 1074 * list, right? 1075 */ 1076 if (softf->ipfr_natlist != NULL) { 1077 WRITE_ENTER(&softc->ipf_nat); 1078 WRITE_ENTER(&softf->ipfr_natfrag); 1079 for (fp = &softf->ipfr_natlist; ((fra = *fp) != NULL); ) { 1080 if (fra->ipfr_ttl > softc->ipf_ticks) 1081 break; 1082 nat = fra->ipfr_data; 1083 if (nat != NULL) { 1084 if (nat->nat_data == fra) 1085 nat->nat_data = NULL; 1086 } 1087 fra->ipfr_ref--; 1088 ipf_frag_delete(softc, fra, &softf->ipfr_nattail); 1089 } 1090 RWLOCK_EXIT(&softf->ipfr_natfrag); 1091 RWLOCK_EXIT(&softc->ipf_nat); 1092 } 1093 SPL_X(s); 1094 } 1095 1096 1097 /* ------------------------------------------------------------------------ */ 1098 /* Function: ipf_frag_pkt_next */ 1099 /* ------------------------------------------------------------------------ */ 1100 int 1101 ipf_frag_pkt_next(ipf_main_softc_t *softc, ipftoken_t *token, ipfgeniter_t *itp) 1102 { 1103 ipf_frag_softc_t *softf = softc->ipf_frag_soft; 1104 1105 #ifdef USE_MUTEXES 1106 return ipf_frag_next(softc, token, itp, &softf->ipfr_list, 1107 &softf->ipfr_frag); 1108 #else 1109 return ipf_frag_next(softc, token, itp, &softf->ipfr_list); 1110 #endif 1111 } 1112 1113 1114 /* ------------------------------------------------------------------------ */ 1115 /* Function: ipf_frag_nat_next */ 1116 /* ------------------------------------------------------------------------ */ 1117 int 1118 ipf_frag_nat_next(ipf_main_softc_t *softc, ipftoken_t *token, ipfgeniter_t *itp) 1119 { 1120 ipf_frag_softc_t *softf = softc->ipf_frag_soft;; 1121 1122 #ifdef USE_MUTEXES 1123 return ipf_frag_next(softc, token, itp, &softf->ipfr_natlist, 1124 &softf->ipfr_natfrag); 1125 #else 1126 return ipf_frag_next(softc, token, itp, &softf->ipfr_natlist); 1127 #endif 1128 } 1129 1130 /* ------------------------------------------------------------------------ */ 1131 /* Function: ipf_frag_next */ 1132 /* Returns: int - 0 == success, else error */ 1133 /* Parameters: token(I) - pointer to token information for this caller */ 1134 /* itp(I) - pointer to generic iterator from caller */ 1135 /* top(I) - top of the fragment list */ 1136 /* lock(I) - fragment cache lock */ 1137 /* */ 1138 /* This function is used to interate through the list of entries in the */ 1139 /* fragment cache. It increases the reference count on the one currently */ 1140 /* being returned so that the caller can come back and resume from it later.*/ 1141 /* */ 1142 /* This function is used for both the NAT fragment cache as well as the ipf */ 1143 /* fragment cache - hence the reason for passing in top and lock. */ 1144 /* ------------------------------------------------------------------------ */ 1145 static int 1146 ipf_frag_next( 1147 ipf_main_softc_t *softc, 1148 ipftoken_t *token, 1149 ipfgeniter_t *itp, 1150 ipfr_t **top 1151 #ifdef USE_MUTEXES 1152 , ipfrwlock_t *lock 1153 #endif 1154 ) 1155 { 1156 ipfr_t *frag, *next, zero; 1157 int error = 0; 1158 1159 if (itp->igi_data == NULL) { 1160 IPFERROR(20001); 1161 return EFAULT; 1162 } 1163 1164 if (itp->igi_nitems != 1) { 1165 IPFERROR(20003); 1166 return EFAULT; 1167 } 1168 1169 frag = token->ipt_data; 1170 1171 READ_ENTER(lock); 1172 1173 if (frag == NULL) 1174 next = *top; 1175 else 1176 next = frag->ipfr_next; 1177 1178 if (next != NULL) { 1179 ATOMIC_INC(next->ipfr_ref); 1180 token->ipt_data = next; 1181 } else { 1182 bzero(&zero, sizeof(zero)); 1183 next = &zero; 1184 token->ipt_data = NULL; 1185 } 1186 if (next->ipfr_next == NULL) 1187 ipf_token_mark_complete(token); 1188 1189 RWLOCK_EXIT(lock); 1190 1191 error = COPYOUT(next, itp->igi_data, sizeof(*next)); 1192 if (error != 0) 1193 IPFERROR(20002); 1194 1195 if (frag != NULL) { 1196 #ifdef USE_MUTEXES 1197 ipf_frag_deref(softc, &frag, lock); 1198 #else 1199 ipf_frag_deref(softc, &frag); 1200 #endif 1201 } 1202 return error; 1203 } 1204 1205 1206 /* ------------------------------------------------------------------------ */ 1207 /* Function: ipf_frag_pkt_deref */ 1208 /* Returns: Nil */ 1209 /* */ 1210 /* ------------------------------------------------------------------------ */ 1211 void 1212 ipf_frag_pkt_deref(ipf_main_softc_t *softc, void *data) 1213 { 1214 ipfr_t **frp = data; 1215 1216 #ifdef USE_MUTEXES 1217 ipf_frag_softc_t *softf = softc->ipf_frag_soft; 1218 1219 ipf_frag_deref(softc->ipf_frag_soft, frp, &softf->ipfr_frag); 1220 #else 1221 ipf_frag_deref(softc->ipf_frag_soft, frp); 1222 #endif 1223 } 1224 1225 1226 /* ------------------------------------------------------------------------ */ 1227 /* Function: ipf_frag_nat_deref */ 1228 /* Returns: Nil */ 1229 /* */ 1230 /* ------------------------------------------------------------------------ */ 1231 void 1232 ipf_frag_nat_deref(ipf_main_softc_t *softc, void *data) 1233 { 1234 ipfr_t **frp = data; 1235 1236 #ifdef USE_MUTEXES 1237 ipf_frag_softc_t *softf = softc->ipf_frag_soft; 1238 1239 ipf_frag_deref(softc->ipf_frag_soft, frp, &softf->ipfr_natfrag); 1240 #else 1241 ipf_frag_deref(softc->ipf_frag_soft, frp); 1242 #endif 1243 } 1244 1245 1246 /* ------------------------------------------------------------------------ */ 1247 /* Function: ipf_frag_deref */ 1248 /* Returns: Nil */ 1249 /* Parameters: frp(IO) - pointer to fragment structure to deference */ 1250 /* lock(I) - lock associated with the fragment */ 1251 /* */ 1252 /* This function dereferences a fragment structure (ipfr_t). The pointer */ 1253 /* passed in will always be reset back to NULL, even if the structure is */ 1254 /* not freed, to enforce the notion that the caller is no longer entitled */ 1255 /* to use the pointer it is dropping the reference to. */ 1256 /* ------------------------------------------------------------------------ */ 1257 static void 1258 ipf_frag_deref(void *arg, ipfr_t **frp 1259 #ifdef USE_MUTEXES 1260 , ipfrwlock_t *lock 1261 #endif 1262 ) 1263 { 1264 ipf_frag_softc_t *softf = arg; 1265 ipfr_t *fra; 1266 1267 fra = *frp; 1268 *frp = NULL; 1269 1270 WRITE_ENTER(lock); 1271 fra->ipfr_ref--; 1272 if (fra->ipfr_ref <= 0) 1273 ipf_frag_free(softf, fra); 1274 RWLOCK_EXIT(lock); 1275 } 1276