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