12393Syz155240 /* 22393Syz155240 * Copyright (C) 1995-2003 by Darren Reed. 32393Syz155240 * 42393Syz155240 * See the IPFILTER.LICENCE file for details on licencing. 52393Syz155240 * 6*11761SZdenek.Kotala@Sun.COM * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 72393Syz155240 * Use is subject to license terms. 82393Syz155240 */ 92393Syz155240 102393Syz155240 #if defined(KERNEL) || defined(_KERNEL) 112393Syz155240 # undef KERNEL 122393Syz155240 # undef _KERNEL 132393Syz155240 # define KERNEL 1 142393Syz155240 # define _KERNEL 1 152393Syz155240 #endif 162393Syz155240 #include <sys/errno.h> 172393Syz155240 #include <sys/types.h> 182393Syz155240 #include <sys/param.h> 192393Syz155240 #include <sys/file.h> 202393Syz155240 #if defined(__NetBSD__) && (NetBSD >= 199905) && !defined(IPFILTER_LKM) && \ 212393Syz155240 defined(_KERNEL) 222393Syz155240 # include "opt_ipfilter_log.h" 232393Syz155240 #endif 242393Syz155240 #if defined(_KERNEL) && defined(__FreeBSD_version) && \ 252393Syz155240 (__FreeBSD_version >= 400000) && !defined(KLD_MODULE) 262393Syz155240 #include "opt_inet6.h" 272393Syz155240 #endif 282393Syz155240 #if !defined(_KERNEL) && !defined(__KERNEL__) 292393Syz155240 # include <stdio.h> 302393Syz155240 # include <stdlib.h> 312393Syz155240 # include <string.h> 322393Syz155240 # define _KERNEL 332393Syz155240 # ifdef __OpenBSD__ 342393Syz155240 struct file; 352393Syz155240 # endif 362393Syz155240 # include <sys/uio.h> 372393Syz155240 # undef _KERNEL 382393Syz155240 #endif 392393Syz155240 #if defined(_KERNEL) && (__FreeBSD_version >= 220000) 402393Syz155240 # include <sys/filio.h> 412393Syz155240 # include <sys/fcntl.h> 422393Syz155240 # if (__FreeBSD_version >= 300000) && !defined(IPFILTER_LKM) 432393Syz155240 # include "opt_ipfilter.h" 442393Syz155240 # endif 452393Syz155240 #else 462393Syz155240 # include <sys/ioctl.h> 472393Syz155240 #endif 482393Syz155240 #include <sys/time.h> 492393Syz155240 #if !defined(linux) 502393Syz155240 # include <sys/protosw.h> 512393Syz155240 #endif 522393Syz155240 #include <sys/socket.h> 532393Syz155240 #if defined(_KERNEL) 542393Syz155240 # include <sys/systm.h> 552393Syz155240 # if !defined(__SVR4) && !defined(__svr4__) 562393Syz155240 # include <sys/mbuf.h> 572393Syz155240 # endif 582393Syz155240 #endif 592393Syz155240 #if defined(__SVR4) || defined(__svr4__) 602393Syz155240 # include <sys/filio.h> 612393Syz155240 # include <sys/byteorder.h> 622393Syz155240 # ifdef _KERNEL 632393Syz155240 # include <sys/dditypes.h> 642393Syz155240 # endif 652393Syz155240 # include <sys/stream.h> 662393Syz155240 # include <sys/kmem.h> 672393Syz155240 #endif 682393Syz155240 692393Syz155240 #include <net/if.h> 702393Syz155240 #ifdef sun 712393Syz155240 # include <net/af.h> 722393Syz155240 #endif 732393Syz155240 #include <net/route.h> 742393Syz155240 #include <netinet/in.h> 752393Syz155240 #include <netinet/in_systm.h> 762393Syz155240 #include <netinet/ip.h> 772393Syz155240 #include <netinet/tcp.h> 782393Syz155240 #if !defined(linux) 792393Syz155240 # include <netinet/ip_var.h> 802393Syz155240 #endif 812393Syz155240 #if !defined(__hpux) && !defined(linux) 822393Syz155240 # include <netinet/tcp_fsm.h> 832393Syz155240 #endif 842393Syz155240 #include <netinet/udp.h> 852393Syz155240 #include <netinet/ip_icmp.h> 862393Syz155240 #include "netinet/ip_compat.h" 872393Syz155240 #include <netinet/tcpip.h> 882393Syz155240 #include "netinet/ip_fil.h" 892393Syz155240 #include "netinet/ip_nat.h" 902393Syz155240 #include "netinet/ip_frag.h" 912393Syz155240 #include "netinet/ip_state.h" 922393Syz155240 #include "netinet/ip_proxy.h" 933448Sdh155122 #include "netinet/ipf_stack.h" 942393Syz155240 #ifdef IPFILTER_SYNC 952393Syz155240 #include "netinet/ip_sync.h" 962393Syz155240 #endif 972393Syz155240 #ifdef IPFILTER_SCAN 982393Syz155240 #include "netinet/ip_scan.h" 992393Syz155240 #endif 1002393Syz155240 #ifdef USE_INET6 1012393Syz155240 #include <netinet/icmp6.h> 1022393Syz155240 #endif 1032393Syz155240 #if (__FreeBSD_version >= 300000) 1042393Syz155240 # include <sys/malloc.h> 1052393Syz155240 # if defined(_KERNEL) && !defined(IPFILTER_LKM) 1062393Syz155240 # include <sys/libkern.h> 1072393Syz155240 # include <sys/systm.h> 1082393Syz155240 # endif 1092393Syz155240 #endif 1102393Syz155240 /* END OF INCLUDES */ 1112393Syz155240 1122393Syz155240 1132393Syz155240 #if !defined(lint) 1142393Syz155240 static const char sccsid[] = "@(#)ip_state.c 1.8 6/5/96 (C) 1993-2000 Darren Reed"; 1152393Syz155240 static const char rcsid[] = "@(#)$Id: ip_state.c,v 2.186.2.36 2005/08/11 19:58:03 darrenr Exp $"; 1162393Syz155240 #endif 1172393Syz155240 1182393Syz155240 #ifdef USE_INET6 1192393Syz155240 static ipstate_t *fr_checkicmp6matchingstate __P((fr_info_t *)); 1202393Syz155240 #endif 1212393Syz155240 static ipstate_t *fr_matchsrcdst __P((fr_info_t *, ipstate_t *, i6addr_t *, 1222393Syz155240 i6addr_t *, tcphdr_t *, u_32_t)); 1232393Syz155240 static ipstate_t *fr_checkicmpmatchingstate __P((fr_info_t *)); 1243448Sdh155122 static int fr_state_flush __P((int, int, ipf_stack_t *)); 1253448Sdh155122 static ips_stat_t *fr_statetstats __P((ipf_stack_t *)); 1263448Sdh155122 static int fr_state_remove __P((caddr_t, ipf_stack_t *)); 1273448Sdh155122 static void fr_ipsmove __P((ipstate_t *, u_int, ipf_stack_t *)); 1282393Syz155240 static int fr_tcpstate __P((fr_info_t *, tcphdr_t *, ipstate_t *)); 1292393Syz155240 static int fr_tcpoptions __P((fr_info_t *, tcphdr_t *, tcpdata_t *)); 1302393Syz155240 static ipstate_t *fr_stclone __P((fr_info_t *, tcphdr_t *, ipstate_t *)); 1312393Syz155240 static void fr_fixinisn __P((fr_info_t *, ipstate_t *)); 1322393Syz155240 static void fr_fixoutisn __P((fr_info_t *, ipstate_t *)); 1332393Syz155240 static void fr_checknewisn __P((fr_info_t *, ipstate_t *)); 1343448Sdh155122 static int fr_stateiter __P((ipftoken_t *, ipfgeniter_t *, ipf_stack_t *)); 1353448Sdh155122 1363448Sdh155122 int fr_stputent __P((caddr_t, ipf_stack_t *)); 1373448Sdh155122 int fr_stgetent __P((caddr_t, ipf_stack_t *)); 1382393Syz155240 1392393Syz155240 #define ONE_DAY IPF_TTLVAL(1 * 86400) /* 1 day */ 1402393Syz155240 #define FIVE_DAYS (5 * ONE_DAY) 1413448Sdh155122 #define DOUBLE_HASH(x, ifs) \ 1423448Sdh155122 (((x) + ifs->ifs_ips_seed[(x) % ifs->ifs_fr_statesize]) % ifs->ifs_fr_statesize) 1433448Sdh155122 1442393Syz155240 1452393Syz155240 /* ------------------------------------------------------------------------ */ 1462393Syz155240 /* Function: fr_stateinit */ 1472393Syz155240 /* Returns: int - 0 == success, -1 == failure */ 1488170SJohn.Ojemann@Sun.COM /* Parameters: ifs - ipf stack instance */ 1492393Syz155240 /* */ 1502393Syz155240 /* Initialise all the global variables used within the state code. */ 1512393Syz155240 /* This action also includes initiailising locks. */ 1522393Syz155240 /* ------------------------------------------------------------------------ */ 1533448Sdh155122 int fr_stateinit(ifs) 1543448Sdh155122 ipf_stack_t *ifs; 1552393Syz155240 { 1567259Sdr146992 #if defined(NEED_LOCAL_RAND) || !defined(_KERNEL) 1577259Sdr146992 struct timeval tv; 1587259Sdr146992 #endif 1592393Syz155240 int i; 1602393Syz155240 1613448Sdh155122 KMALLOCS(ifs->ifs_ips_table, ipstate_t **, 1623448Sdh155122 ifs->ifs_fr_statesize * sizeof(ipstate_t *)); 1633448Sdh155122 if (ifs->ifs_ips_table == NULL) 1642393Syz155240 return -1; 1653448Sdh155122 bzero((char *)ifs->ifs_ips_table, 1663448Sdh155122 ifs->ifs_fr_statesize * sizeof(ipstate_t *)); 1673448Sdh155122 1683448Sdh155122 KMALLOCS(ifs->ifs_ips_seed, u_long *, 1693448Sdh155122 ifs->ifs_fr_statesize * sizeof(*ifs->ifs_ips_seed)); 1703448Sdh155122 if (ifs->ifs_ips_seed == NULL) 1712393Syz155240 return -2; 1727259Sdr146992 #if defined(NEED_LOCAL_RAND) || !defined(_KERNEL) 1737259Sdr146992 tv.tv_sec = 0; 1747259Sdr146992 GETKTIME(&tv); 1757259Sdr146992 #endif 1763448Sdh155122 for (i = 0; i < ifs->ifs_fr_statesize; i++) { 1772393Syz155240 /* 1782393Syz155240 * XXX - ips_seed[X] should be a random number of sorts. 1792393Syz155240 */ 1807259Sdr146992 #if !defined(NEED_LOCAL_RAND) && defined(_KERNEL) 1817259Sdr146992 ifs->ifs_ips_seed[i] = ipf_random(); 1822393Syz155240 #else 1833448Sdh155122 ifs->ifs_ips_seed[i] = ((u_long)ifs->ifs_ips_seed + i) * 1843448Sdh155122 ifs->ifs_fr_statesize; 1857259Sdr146992 ifs->ifs_ips_seed[i] += tv.tv_sec; 1863448Sdh155122 ifs->ifs_ips_seed[i] *= (u_long)ifs->ifs_ips_seed; 1873448Sdh155122 ifs->ifs_ips_seed[i] ^= 0x5a5aa5a5; 1883448Sdh155122 ifs->ifs_ips_seed[i] *= ifs->ifs_fr_statemax; 1892393Syz155240 #endif 1902393Syz155240 } 1912393Syz155240 1922393Syz155240 /* fill icmp reply type table */ 1932393Syz155240 for (i = 0; i <= ICMP_MAXTYPE; i++) 1942393Syz155240 icmpreplytype4[i] = -1; 1952393Syz155240 icmpreplytype4[ICMP_ECHO] = ICMP_ECHOREPLY; 1962393Syz155240 icmpreplytype4[ICMP_TSTAMP] = ICMP_TSTAMPREPLY; 1972393Syz155240 icmpreplytype4[ICMP_IREQ] = ICMP_IREQREPLY; 1982393Syz155240 icmpreplytype4[ICMP_MASKREQ] = ICMP_MASKREPLY; 1992393Syz155240 #ifdef USE_INET6 2002393Syz155240 /* fill icmp reply type table */ 2012393Syz155240 for (i = 0; i <= ICMP6_MAXTYPE; i++) 2022393Syz155240 icmpreplytype6[i] = -1; 2032393Syz155240 icmpreplytype6[ICMP6_ECHO_REQUEST] = ICMP6_ECHO_REPLY; 2042393Syz155240 icmpreplytype6[ICMP6_MEMBERSHIP_QUERY] = ICMP6_MEMBERSHIP_REPORT; 2052393Syz155240 icmpreplytype6[ICMP6_NI_QUERY] = ICMP6_NI_REPLY; 2062393Syz155240 icmpreplytype6[ND_ROUTER_SOLICIT] = ND_ROUTER_ADVERT; 2072393Syz155240 icmpreplytype6[ND_NEIGHBOR_SOLICIT] = ND_NEIGHBOR_ADVERT; 2082393Syz155240 #endif 2092393Syz155240 2103448Sdh155122 KMALLOCS(ifs->ifs_ips_stats.iss_bucketlen, u_long *, 2113448Sdh155122 ifs->ifs_fr_statesize * sizeof(u_long)); 2123448Sdh155122 if (ifs->ifs_ips_stats.iss_bucketlen == NULL) 2132393Syz155240 return -1; 2143448Sdh155122 bzero((char *)ifs->ifs_ips_stats.iss_bucketlen, 2153448Sdh155122 ifs->ifs_fr_statesize * sizeof(u_long)); 2163448Sdh155122 2173448Sdh155122 if (ifs->ifs_fr_state_maxbucket == 0) { 2183448Sdh155122 for (i = ifs->ifs_fr_statesize; i > 0; i >>= 1) 2193448Sdh155122 ifs->ifs_fr_state_maxbucket++; 2203448Sdh155122 ifs->ifs_fr_state_maxbucket *= 2; 2212393Syz155240 } 2222393Syz155240 2233448Sdh155122 fr_sttab_init(ifs->ifs_ips_tqtqb, ifs); 2243448Sdh155122 ifs->ifs_ips_tqtqb[IPF_TCP_NSTATES - 1].ifq_next = &ifs->ifs_ips_udptq; 2253448Sdh155122 ifs->ifs_ips_udptq.ifq_ttl = (u_long)ifs->ifs_fr_udptimeout; 2263448Sdh155122 ifs->ifs_ips_udptq.ifq_ref = 1; 2273448Sdh155122 ifs->ifs_ips_udptq.ifq_head = NULL; 2283448Sdh155122 ifs->ifs_ips_udptq.ifq_tail = &ifs->ifs_ips_udptq.ifq_head; 2293448Sdh155122 MUTEX_INIT(&ifs->ifs_ips_udptq.ifq_lock, "ipftq udp tab"); 2303448Sdh155122 ifs->ifs_ips_udptq.ifq_next = &ifs->ifs_ips_udpacktq; 2313448Sdh155122 ifs->ifs_ips_udpacktq.ifq_ttl = (u_long)ifs->ifs_fr_udpacktimeout; 2323448Sdh155122 ifs->ifs_ips_udpacktq.ifq_ref = 1; 2333448Sdh155122 ifs->ifs_ips_udpacktq.ifq_head = NULL; 2343448Sdh155122 ifs->ifs_ips_udpacktq.ifq_tail = &ifs->ifs_ips_udpacktq.ifq_head; 2353448Sdh155122 MUTEX_INIT(&ifs->ifs_ips_udpacktq.ifq_lock, "ipftq udpack tab"); 2363448Sdh155122 ifs->ifs_ips_udpacktq.ifq_next = &ifs->ifs_ips_icmptq; 2373448Sdh155122 ifs->ifs_ips_icmptq.ifq_ttl = (u_long)ifs->ifs_fr_icmptimeout; 2383448Sdh155122 ifs->ifs_ips_icmptq.ifq_ref = 1; 2393448Sdh155122 ifs->ifs_ips_icmptq.ifq_head = NULL; 2403448Sdh155122 ifs->ifs_ips_icmptq.ifq_tail = &ifs->ifs_ips_icmptq.ifq_head; 2413448Sdh155122 MUTEX_INIT(&ifs->ifs_ips_icmptq.ifq_lock, "ipftq icmp tab"); 2423448Sdh155122 ifs->ifs_ips_icmptq.ifq_next = &ifs->ifs_ips_icmpacktq; 2433448Sdh155122 ifs->ifs_ips_icmpacktq.ifq_ttl = (u_long)ifs->ifs_fr_icmpacktimeout; 2443448Sdh155122 ifs->ifs_ips_icmpacktq.ifq_ref = 1; 2453448Sdh155122 ifs->ifs_ips_icmpacktq.ifq_head = NULL; 2463448Sdh155122 ifs->ifs_ips_icmpacktq.ifq_tail = &ifs->ifs_ips_icmpacktq.ifq_head; 2473448Sdh155122 MUTEX_INIT(&ifs->ifs_ips_icmpacktq.ifq_lock, "ipftq icmpack tab"); 2483448Sdh155122 ifs->ifs_ips_icmpacktq.ifq_next = &ifs->ifs_ips_iptq; 2493448Sdh155122 ifs->ifs_ips_iptq.ifq_ttl = (u_long)ifs->ifs_fr_iptimeout; 2503448Sdh155122 ifs->ifs_ips_iptq.ifq_ref = 1; 2513448Sdh155122 ifs->ifs_ips_iptq.ifq_head = NULL; 2523448Sdh155122 ifs->ifs_ips_iptq.ifq_tail = &ifs->ifs_ips_iptq.ifq_head; 2533448Sdh155122 MUTEX_INIT(&ifs->ifs_ips_iptq.ifq_lock, "ipftq ip tab"); 2544431San207044 ifs->ifs_ips_iptq.ifq_next = &ifs->ifs_ips_deletetq; 2554431San207044 /* entry's ttl in deletetq is just 1 tick */ 2564431San207044 ifs->ifs_ips_deletetq.ifq_ttl = (u_long) 1; 2574431San207044 ifs->ifs_ips_deletetq.ifq_ref = 1; 2584431San207044 ifs->ifs_ips_deletetq.ifq_head = NULL; 2594431San207044 ifs->ifs_ips_deletetq.ifq_tail = &ifs->ifs_ips_deletetq.ifq_head; 2604431San207044 MUTEX_INIT(&ifs->ifs_ips_deletetq.ifq_lock, "state delete queue"); 2614431San207044 ifs->ifs_ips_deletetq.ifq_next = NULL; 2623448Sdh155122 2633448Sdh155122 RWLOCK_INIT(&ifs->ifs_ipf_state, "ipf IP state rwlock"); 2643448Sdh155122 MUTEX_INIT(&ifs->ifs_ipf_stinsert, "ipf state insert mutex"); 2653448Sdh155122 ifs->ifs_fr_state_init = 1; 2663448Sdh155122 2673448Sdh155122 ifs->ifs_ips_last_force_flush = ifs->ifs_fr_ticks; 2682393Syz155240 return 0; 2692393Syz155240 } 2702393Syz155240 2712393Syz155240 2722393Syz155240 /* ------------------------------------------------------------------------ */ 2732393Syz155240 /* Function: fr_stateunload */ 2742393Syz155240 /* Returns: Nil */ 2758170SJohn.Ojemann@Sun.COM /* Parameters: ifs - ipf stack instance */ 2762393Syz155240 /* */ 2772393Syz155240 /* Release and destroy any resources acquired or initialised so that */ 2782393Syz155240 /* IPFilter can be unloaded or re-initialised. */ 2792393Syz155240 /* ------------------------------------------------------------------------ */ 2803448Sdh155122 void fr_stateunload(ifs) 2813448Sdh155122 ipf_stack_t *ifs; 2822393Syz155240 { 2832393Syz155240 ipftq_t *ifq, *ifqnext; 2842393Syz155240 ipstate_t *is; 2852393Syz155240 2863448Sdh155122 while ((is = ifs->ifs_ips_list) != NULL) 2878170SJohn.Ojemann@Sun.COM (void) fr_delstate(is, 0, ifs); 2882393Syz155240 2892393Syz155240 /* 2902393Syz155240 * Proxy timeout queues are not cleaned here because although they 2912393Syz155240 * exist on the state list, appr_unload is called after fr_stateunload 2922393Syz155240 * and the proxies actually are responsible for them being created. 2932393Syz155240 * Should the proxy timeouts have their own list? There's no real 2942393Syz155240 * justification as this is the only complicationA 2952393Syz155240 */ 2963448Sdh155122 for (ifq = ifs->ifs_ips_utqe; ifq != NULL; ifq = ifqnext) { 2972393Syz155240 ifqnext = ifq->ifq_next; 2982393Syz155240 if (((ifq->ifq_flags & IFQF_PROXY) == 0) && 2992393Syz155240 (fr_deletetimeoutqueue(ifq) == 0)) 3003448Sdh155122 fr_freetimeoutqueue(ifq, ifs); 3012393Syz155240 } 3022393Syz155240 3033448Sdh155122 ifs->ifs_ips_stats.iss_inuse = 0; 3043448Sdh155122 ifs->ifs_ips_num = 0; 3053448Sdh155122 3063448Sdh155122 if (ifs->ifs_fr_state_init == 1) { 3073448Sdh155122 fr_sttab_destroy(ifs->ifs_ips_tqtqb); 3083448Sdh155122 MUTEX_DESTROY(&ifs->ifs_ips_udptq.ifq_lock); 3093448Sdh155122 MUTEX_DESTROY(&ifs->ifs_ips_icmptq.ifq_lock); 3103448Sdh155122 MUTEX_DESTROY(&ifs->ifs_ips_udpacktq.ifq_lock); 3113448Sdh155122 MUTEX_DESTROY(&ifs->ifs_ips_icmpacktq.ifq_lock); 3123448Sdh155122 MUTEX_DESTROY(&ifs->ifs_ips_iptq.ifq_lock); 3134431San207044 MUTEX_DESTROY(&ifs->ifs_ips_deletetq.ifq_lock); 3142393Syz155240 } 3152393Syz155240 3163448Sdh155122 if (ifs->ifs_ips_table != NULL) { 3173448Sdh155122 KFREES(ifs->ifs_ips_table, 3183448Sdh155122 ifs->ifs_fr_statesize * sizeof(*ifs->ifs_ips_table)); 3193448Sdh155122 ifs->ifs_ips_table = NULL; 3202393Syz155240 } 3212393Syz155240 3223448Sdh155122 if (ifs->ifs_ips_seed != NULL) { 3233448Sdh155122 KFREES(ifs->ifs_ips_seed, 3243448Sdh155122 ifs->ifs_fr_statesize * sizeof(*ifs->ifs_ips_seed)); 3253448Sdh155122 ifs->ifs_ips_seed = NULL; 3262393Syz155240 } 3272393Syz155240 3283448Sdh155122 if (ifs->ifs_ips_stats.iss_bucketlen != NULL) { 3293448Sdh155122 KFREES(ifs->ifs_ips_stats.iss_bucketlen, 3303448Sdh155122 ifs->ifs_fr_statesize * sizeof(u_long)); 3313448Sdh155122 ifs->ifs_ips_stats.iss_bucketlen = NULL; 3323448Sdh155122 } 3333448Sdh155122 3343448Sdh155122 if (ifs->ifs_fr_state_maxbucket_reset == 1) 3353448Sdh155122 ifs->ifs_fr_state_maxbucket = 0; 3363448Sdh155122 3373448Sdh155122 if (ifs->ifs_fr_state_init == 1) { 3383448Sdh155122 ifs->ifs_fr_state_init = 0; 3393448Sdh155122 RW_DESTROY(&ifs->ifs_ipf_state); 3403448Sdh155122 MUTEX_DESTROY(&ifs->ifs_ipf_stinsert); 3412393Syz155240 } 3422393Syz155240 } 3432393Syz155240 3442393Syz155240 3452393Syz155240 /* ------------------------------------------------------------------------ */ 3462393Syz155240 /* Function: fr_statetstats */ 3472393Syz155240 /* Returns: ips_state_t* - pointer to state stats structure */ 3482393Syz155240 /* Parameters: Nil */ 3492393Syz155240 /* */ 3502393Syz155240 /* Put all the current numbers and pointers into a single struct and return */ 3512393Syz155240 /* a pointer to it. */ 3522393Syz155240 /* ------------------------------------------------------------------------ */ 3533448Sdh155122 static ips_stat_t *fr_statetstats(ifs) 3543448Sdh155122 ipf_stack_t *ifs; 3552393Syz155240 { 3563448Sdh155122 ifs->ifs_ips_stats.iss_active = ifs->ifs_ips_num; 3573448Sdh155122 ifs->ifs_ips_stats.iss_statesize = ifs->ifs_fr_statesize; 3583448Sdh155122 ifs->ifs_ips_stats.iss_statemax = ifs->ifs_fr_statemax; 3593448Sdh155122 ifs->ifs_ips_stats.iss_table = ifs->ifs_ips_table; 3603448Sdh155122 ifs->ifs_ips_stats.iss_list = ifs->ifs_ips_list; 3613448Sdh155122 ifs->ifs_ips_stats.iss_ticks = ifs->ifs_fr_ticks; 3623448Sdh155122 return &ifs->ifs_ips_stats; 3632393Syz155240 } 3642393Syz155240 3652393Syz155240 /* ------------------------------------------------------------------------ */ 3662393Syz155240 /* Function: fr_state_remove */ 3672393Syz155240 /* Returns: int - 0 == success, != 0 == failure */ 3682393Syz155240 /* Parameters: data(I) - pointer to state structure to delete from table */ 3698170SJohn.Ojemann@Sun.COM /* ifs - ipf stack instance */ 3702393Syz155240 /* */ 3712393Syz155240 /* Search for a state structure that matches the one passed, according to */ 3722393Syz155240 /* the IP addresses and other protocol specific information. */ 3732393Syz155240 /* ------------------------------------------------------------------------ */ 3743448Sdh155122 static int fr_state_remove(data, ifs) 3752393Syz155240 caddr_t data; 3763448Sdh155122 ipf_stack_t *ifs; 3772393Syz155240 { 3782393Syz155240 ipstate_t *sp, st; 3792393Syz155240 int error; 3802393Syz155240 3812393Syz155240 sp = &st; 3822393Syz155240 error = fr_inobj(data, &st, IPFOBJ_IPSTATE); 3832393Syz155240 if (error) 3842393Syz155240 return EFAULT; 3852393Syz155240 3863448Sdh155122 WRITE_ENTER(&ifs->ifs_ipf_state); 3873448Sdh155122 for (sp = ifs->ifs_ips_list; sp; sp = sp->is_next) 3882393Syz155240 if ((sp->is_p == st.is_p) && (sp->is_v == st.is_v) && 3892393Syz155240 !bcmp((caddr_t)&sp->is_src, (caddr_t)&st.is_src, 3902393Syz155240 sizeof(st.is_src)) && 3917333SJohn.Ojemann@Sun.COM !bcmp((caddr_t)&sp->is_dst, (caddr_t)&st.is_dst, 3922393Syz155240 sizeof(st.is_dst)) && 3932393Syz155240 !bcmp((caddr_t)&sp->is_ps, (caddr_t)&st.is_ps, 3942393Syz155240 sizeof(st.is_ps))) { 3958170SJohn.Ojemann@Sun.COM (void) fr_delstate(sp, ISL_REMOVE, ifs); 3963448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_state); 3972393Syz155240 return 0; 3982393Syz155240 } 3993448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_state); 4002393Syz155240 return ESRCH; 4012393Syz155240 } 4022393Syz155240 4032393Syz155240 4042393Syz155240 /* ------------------------------------------------------------------------ */ 4052393Syz155240 /* Function: fr_state_ioctl */ 4062393Syz155240 /* Returns: int - 0 == success, != 0 == failure */ 4072393Syz155240 /* Parameters: data(I) - pointer to ioctl data */ 4082393Syz155240 /* cmd(I) - ioctl command integer */ 4092393Syz155240 /* mode(I) - file mode bits used with open */ 4108170SJohn.Ojemann@Sun.COM /* uid(I) - uid of caller */ 4118170SJohn.Ojemann@Sun.COM /* ctx(I) - pointer to give the uid context */ 4128170SJohn.Ojemann@Sun.COM /* ifs - ipf stack instance */ 4132393Syz155240 /* */ 4142393Syz155240 /* Processes an ioctl call made to operate on the IP Filter state device. */ 4152393Syz155240 /* ------------------------------------------------------------------------ */ 4163448Sdh155122 int fr_state_ioctl(data, cmd, mode, uid, ctx, ifs) 4172393Syz155240 caddr_t data; 4182393Syz155240 ioctlcmd_t cmd; 4193448Sdh155122 int mode, uid; 4203448Sdh155122 void *ctx; 4213448Sdh155122 ipf_stack_t *ifs; 4222393Syz155240 { 4232393Syz155240 int arg, ret, error = 0; 4242393Syz155240 4252393Syz155240 switch (cmd) 4262393Syz155240 { 4272393Syz155240 /* 4282393Syz155240 * Delete an entry from the state table. 4292393Syz155240 */ 4302393Syz155240 case SIOCDELST : 4317433SJohn.Ojemann@Sun.COM error = fr_state_remove(data, ifs); 4322393Syz155240 break; 4332393Syz155240 /* 4342393Syz155240 * Flush the state table 4352393Syz155240 */ 4362393Syz155240 case SIOCIPFFL : 4377433SJohn.Ojemann@Sun.COM error = BCOPYIN(data, (char *)&arg, sizeof(arg)); 4387433SJohn.Ojemann@Sun.COM if (error != 0) { 4397433SJohn.Ojemann@Sun.COM error = EFAULT; 4407433SJohn.Ojemann@Sun.COM } else { 4418170SJohn.Ojemann@Sun.COM if (VALID_TABLE_FLUSH_OPT(arg)) { 4427433SJohn.Ojemann@Sun.COM WRITE_ENTER(&ifs->ifs_ipf_state); 4437433SJohn.Ojemann@Sun.COM ret = fr_state_flush(arg, 4, ifs); 4447433SJohn.Ojemann@Sun.COM RWLOCK_EXIT(&ifs->ifs_ipf_state); 4457433SJohn.Ojemann@Sun.COM error = BCOPYOUT((char *)&ret, data, 4467433SJohn.Ojemann@Sun.COM sizeof(ret)); 4477433SJohn.Ojemann@Sun.COM if (error != 0) 4487433SJohn.Ojemann@Sun.COM return EFAULT; 4497433SJohn.Ojemann@Sun.COM } else { 4507433SJohn.Ojemann@Sun.COM error = EINVAL; 4517433SJohn.Ojemann@Sun.COM } 4527433SJohn.Ojemann@Sun.COM } 4532393Syz155240 break; 4547433SJohn.Ojemann@Sun.COM 4552393Syz155240 #ifdef USE_INET6 4562393Syz155240 case SIOCIPFL6 : 4577433SJohn.Ojemann@Sun.COM error = BCOPYIN(data, (char *)&arg, sizeof(arg)); 4587433SJohn.Ojemann@Sun.COM if (error != 0) { 4597433SJohn.Ojemann@Sun.COM error = EFAULT; 4607433SJohn.Ojemann@Sun.COM } else { 4618170SJohn.Ojemann@Sun.COM if (VALID_TABLE_FLUSH_OPT(arg)) { 4627433SJohn.Ojemann@Sun.COM WRITE_ENTER(&ifs->ifs_ipf_state); 4637433SJohn.Ojemann@Sun.COM ret = fr_state_flush(arg, 6, ifs); 4647433SJohn.Ojemann@Sun.COM RWLOCK_EXIT(&ifs->ifs_ipf_state); 4657433SJohn.Ojemann@Sun.COM error = BCOPYOUT((char *)&ret, data, 4667433SJohn.Ojemann@Sun.COM sizeof(ret)); 4677433SJohn.Ojemann@Sun.COM if (error != 0) 4687433SJohn.Ojemann@Sun.COM return EFAULT; 4697433SJohn.Ojemann@Sun.COM } else { 4707433SJohn.Ojemann@Sun.COM error = EINVAL; 4717433SJohn.Ojemann@Sun.COM } 4727433SJohn.Ojemann@Sun.COM } 4732393Syz155240 break; 4742393Syz155240 #endif 4752393Syz155240 #ifdef IPFILTER_LOG 4762393Syz155240 /* 4772393Syz155240 * Flush the state log. 4782393Syz155240 */ 4792393Syz155240 case SIOCIPFFB : 4802393Syz155240 if (!(mode & FWRITE)) 4812393Syz155240 error = EPERM; 4822393Syz155240 else { 4832393Syz155240 int tmp; 4842393Syz155240 4853448Sdh155122 tmp = ipflog_clear(IPL_LOGSTATE, ifs); 4867433SJohn.Ojemann@Sun.COM error = BCOPYOUT((char *)&tmp, data, sizeof(tmp)); 4877433SJohn.Ojemann@Sun.COM if (error != 0) 4887433SJohn.Ojemann@Sun.COM error = EFAULT; 4892393Syz155240 } 4902393Syz155240 break; 4912393Syz155240 /* 4922393Syz155240 * Turn logging of state information on/off. 4932393Syz155240 */ 4942393Syz155240 case SIOCSETLG : 4957433SJohn.Ojemann@Sun.COM if (!(mode & FWRITE)) { 4962393Syz155240 error = EPERM; 4977433SJohn.Ojemann@Sun.COM } else { 4987433SJohn.Ojemann@Sun.COM error = BCOPYIN((char *)data, 4997433SJohn.Ojemann@Sun.COM (char *)&ifs->ifs_ipstate_logging, 5007433SJohn.Ojemann@Sun.COM sizeof(ifs->ifs_ipstate_logging)); 5017433SJohn.Ojemann@Sun.COM if (error != 0) 5027433SJohn.Ojemann@Sun.COM error = EFAULT; 5032393Syz155240 } 5042393Syz155240 break; 5052393Syz155240 /* 5062393Syz155240 * Return the current state of logging. 5072393Syz155240 */ 5082393Syz155240 case SIOCGETLG : 5097433SJohn.Ojemann@Sun.COM error = BCOPYOUT((char *)&ifs->ifs_ipstate_logging, 5107433SJohn.Ojemann@Sun.COM (char *)data, 5117433SJohn.Ojemann@Sun.COM sizeof(ifs->ifs_ipstate_logging)); 5127433SJohn.Ojemann@Sun.COM if (error != 0) 5137433SJohn.Ojemann@Sun.COM error = EFAULT; 5142393Syz155240 break; 5152393Syz155240 /* 5162393Syz155240 * Return the number of bytes currently waiting to be read. 5172393Syz155240 */ 5182393Syz155240 case FIONREAD : 5193448Sdh155122 arg = ifs->ifs_iplused[IPL_LOGSTATE]; /* returned in an int */ 5207433SJohn.Ojemann@Sun.COM error = BCOPYOUT((char *)&arg, data, sizeof(arg)); 5217433SJohn.Ojemann@Sun.COM if (error != 0) 5227433SJohn.Ojemann@Sun.COM error = EFAULT; 5232393Syz155240 break; 5242393Syz155240 #endif 5252393Syz155240 /* 5262393Syz155240 * Get the current state statistics. 5272393Syz155240 */ 5282393Syz155240 case SIOCGETFS : 5293448Sdh155122 error = fr_outobj(data, fr_statetstats(ifs), IPFOBJ_STATESTAT); 5302393Syz155240 break; 5312393Syz155240 /* 5322393Syz155240 * Lock/Unlock the state table. (Locking prevents any changes, which 5332393Syz155240 * means no packets match). 5342393Syz155240 */ 5352393Syz155240 case SIOCSTLCK : 5362393Syz155240 if (!(mode & FWRITE)) { 5372393Syz155240 error = EPERM; 5382393Syz155240 } else { 5397433SJohn.Ojemann@Sun.COM error = fr_lock(data, &ifs->ifs_fr_state_lock); 5402393Syz155240 } 5412393Syz155240 break; 5422393Syz155240 /* 5432393Syz155240 * Add an entry to the current state table. 5442393Syz155240 */ 5452393Syz155240 case SIOCSTPUT : 5468170SJohn.Ojemann@Sun.COM if (!ifs->ifs_fr_state_lock || !(mode & FWRITE)) { 5472393Syz155240 error = EACCES; 5482393Syz155240 break; 5492393Syz155240 } 5503448Sdh155122 error = fr_stputent(data, ifs); 5512393Syz155240 break; 5522393Syz155240 /* 5532393Syz155240 * Get a state table entry. 5542393Syz155240 */ 5552393Syz155240 case SIOCSTGET : 5563448Sdh155122 if (!ifs->ifs_fr_state_lock) { 5572393Syz155240 error = EACCES; 5582393Syz155240 break; 5592393Syz155240 } 5603448Sdh155122 error = fr_stgetent(data, ifs); 5612393Syz155240 break; 5623448Sdh155122 5633448Sdh155122 case SIOCGENITER : 5643448Sdh155122 { 5653448Sdh155122 ipftoken_t *token; 5663448Sdh155122 ipfgeniter_t iter; 5673448Sdh155122 5683448Sdh155122 error = fr_inobj(data, &iter, IPFOBJ_GENITER); 5693448Sdh155122 if (error != 0) 5703448Sdh155122 break; 5713448Sdh155122 5723448Sdh155122 token = ipf_findtoken(IPFGENITER_STATE, uid, ctx, ifs); 5733448Sdh155122 if (token != NULL) 5743448Sdh155122 error = fr_stateiter(token, &iter, ifs); 5753448Sdh155122 else 5763448Sdh155122 error = ESRCH; 5773448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_tokens); 5783448Sdh155122 break; 5793448Sdh155122 } 5803448Sdh155122 5813448Sdh155122 case SIOCIPFDELTOK : 5827433SJohn.Ojemann@Sun.COM error = BCOPYIN(data, (char *)&arg, sizeof(arg)); 5837433SJohn.Ojemann@Sun.COM if (error != 0) { 5847433SJohn.Ojemann@Sun.COM error = EFAULT; 5857433SJohn.Ojemann@Sun.COM } else { 5867433SJohn.Ojemann@Sun.COM error = ipf_deltoken(arg, uid, ctx, ifs); 5877433SJohn.Ojemann@Sun.COM } 5883448Sdh155122 break; 5893448Sdh155122 5902393Syz155240 default : 5912393Syz155240 error = EINVAL; 5922393Syz155240 break; 5932393Syz155240 } 5942393Syz155240 return error; 5952393Syz155240 } 5962393Syz155240 5972393Syz155240 5982393Syz155240 /* ------------------------------------------------------------------------ */ 5992393Syz155240 /* Function: fr_stgetent */ 6002393Syz155240 /* Returns: int - 0 == success, != 0 == failure */ 6012393Syz155240 /* Parameters: data(I) - pointer to state structure to retrieve from table */ 6022393Syz155240 /* */ 6032393Syz155240 /* Copy out state information from the kernel to a user space process. If */ 6042393Syz155240 /* there is a filter rule associated with the state entry, copy that out */ 6052393Syz155240 /* as well. The entry to copy out is taken from the value of "ips_next" in */ 6062393Syz155240 /* the struct passed in and if not null and not found in the list of current*/ 6072393Syz155240 /* state entries, the retrieval fails. */ 6082393Syz155240 /* ------------------------------------------------------------------------ */ 6093448Sdh155122 int fr_stgetent(data, ifs) 6102393Syz155240 caddr_t data; 6113448Sdh155122 ipf_stack_t *ifs; 6122393Syz155240 { 6132393Syz155240 ipstate_t *is, *isn; 6142393Syz155240 ipstate_save_t ips; 6152393Syz155240 int error; 6162393Syz155240 6172393Syz155240 error = fr_inobj(data, &ips, IPFOBJ_STATESAVE); 6182393Syz155240 if (error) 6192393Syz155240 return EFAULT; 6202393Syz155240 6212393Syz155240 isn = ips.ips_next; 6222393Syz155240 if (isn == NULL) { 6233448Sdh155122 isn = ifs->ifs_ips_list; 6242393Syz155240 if (isn == NULL) { 6252393Syz155240 if (ips.ips_next == NULL) 6262393Syz155240 return ENOENT; 6272393Syz155240 return 0; 6282393Syz155240 } 6292393Syz155240 } else { 6302393Syz155240 /* 6312393Syz155240 * Make sure the pointer we're copying from exists in the 6322393Syz155240 * current list of entries. Security precaution to prevent 6332393Syz155240 * copying of random kernel data. 6342393Syz155240 */ 6353448Sdh155122 for (is = ifs->ifs_ips_list; is; is = is->is_next) 6362393Syz155240 if (is == isn) 6372393Syz155240 break; 6382393Syz155240 if (!is) 6392393Syz155240 return ESRCH; 6402393Syz155240 } 6412393Syz155240 ips.ips_next = isn->is_next; 6422393Syz155240 bcopy((char *)isn, (char *)&ips.ips_is, sizeof(ips.ips_is)); 6432393Syz155240 ips.ips_rule = isn->is_rule; 6442393Syz155240 if (isn->is_rule != NULL) 6452393Syz155240 bcopy((char *)isn->is_rule, (char *)&ips.ips_fr, 6462393Syz155240 sizeof(ips.ips_fr)); 6472393Syz155240 error = fr_outobj(data, &ips, IPFOBJ_STATESAVE); 6482393Syz155240 if (error) 6492393Syz155240 return EFAULT; 6502393Syz155240 return 0; 6512393Syz155240 } 6522393Syz155240 6532393Syz155240 6542393Syz155240 /* ------------------------------------------------------------------------ */ 6552393Syz155240 /* Function: fr_stputent */ 6562393Syz155240 /* Returns: int - 0 == success, != 0 == failure */ 6572393Syz155240 /* Parameters: data(I) - pointer to state information struct */ 6588170SJohn.Ojemann@Sun.COM /* ifs - ipf stack instance */ 6592393Syz155240 /* */ 6602393Syz155240 /* This function implements the SIOCSTPUT ioctl: insert a state entry into */ 6612393Syz155240 /* the state table. If the state info. includes a pointer to a filter rule */ 6622393Syz155240 /* then also add in an orphaned rule (will not show up in any "ipfstat -io" */ 6632393Syz155240 /* output. */ 6642393Syz155240 /* ------------------------------------------------------------------------ */ 6653448Sdh155122 int fr_stputent(data, ifs) 6662393Syz155240 caddr_t data; 6673448Sdh155122 ipf_stack_t *ifs; 6682393Syz155240 { 6692393Syz155240 ipstate_t *is, *isn; 6702393Syz155240 ipstate_save_t ips; 6712393Syz155240 int error, i; 6722393Syz155240 frentry_t *fr; 6732393Syz155240 char *name; 6742393Syz155240 6752393Syz155240 error = fr_inobj(data, &ips, IPFOBJ_STATESAVE); 6762393Syz155240 if (error) 6772393Syz155240 return EFAULT; 6782393Syz155240 6798170SJohn.Ojemann@Sun.COM /* 6808170SJohn.Ojemann@Sun.COM * Trigger automatic call to fr_state_flush() if the 6818170SJohn.Ojemann@Sun.COM * table has reached capacity specified by hi watermark. 6828170SJohn.Ojemann@Sun.COM */ 6838170SJohn.Ojemann@Sun.COM if (ST_TAB_WATER_LEVEL(ifs) > ifs->ifs_state_flush_level_hi) 6848170SJohn.Ojemann@Sun.COM ifs->ifs_fr_state_doflush = 1; 6858170SJohn.Ojemann@Sun.COM 6868170SJohn.Ojemann@Sun.COM /* 6878170SJohn.Ojemann@Sun.COM * If automatic flushing did not do its job, and the table 6888170SJohn.Ojemann@Sun.COM * has filled up, don't try to create a new entry. 6898170SJohn.Ojemann@Sun.COM */ 6908170SJohn.Ojemann@Sun.COM if (ifs->ifs_ips_num >= ifs->ifs_fr_statemax) { 6918170SJohn.Ojemann@Sun.COM ATOMIC_INCL(ifs->ifs_ips_stats.iss_max); 6928170SJohn.Ojemann@Sun.COM return ENOMEM; 6938170SJohn.Ojemann@Sun.COM } 6948170SJohn.Ojemann@Sun.COM 6952393Syz155240 KMALLOC(isn, ipstate_t *); 6962393Syz155240 if (isn == NULL) 6972393Syz155240 return ENOMEM; 6982393Syz155240 6992393Syz155240 bcopy((char *)&ips.ips_is, (char *)isn, sizeof(*isn)); 7002393Syz155240 bzero((char *)isn, offsetof(struct ipstate, is_pkts)); 7012393Syz155240 isn->is_sti.tqe_pnext = NULL; 7022393Syz155240 isn->is_sti.tqe_next = NULL; 7032393Syz155240 isn->is_sti.tqe_ifq = NULL; 7042393Syz155240 isn->is_sti.tqe_parent = isn; 7052393Syz155240 isn->is_ifp[0] = NULL; 7062393Syz155240 isn->is_ifp[1] = NULL; 7072393Syz155240 isn->is_ifp[2] = NULL; 7082393Syz155240 isn->is_ifp[3] = NULL; 7092393Syz155240 isn->is_sync = NULL; 7102393Syz155240 fr = ips.ips_rule; 7112393Syz155240 7122393Syz155240 if (fr == NULL) { 7133448Sdh155122 READ_ENTER(&ifs->ifs_ipf_state); 7143448Sdh155122 fr_stinsert(isn, 0, ifs); 7152393Syz155240 MUTEX_EXIT(&isn->is_lock); 7163448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_state); 7172393Syz155240 return 0; 7182393Syz155240 } 7192393Syz155240 7202393Syz155240 if (isn->is_flags & SI_NEWFR) { 7212393Syz155240 KMALLOC(fr, frentry_t *); 7222393Syz155240 if (fr == NULL) { 7232393Syz155240 KFREE(isn); 7242393Syz155240 return ENOMEM; 7252393Syz155240 } 7262393Syz155240 bcopy((char *)&ips.ips_fr, (char *)fr, sizeof(*fr)); 7272393Syz155240 isn->is_rule = fr; 7282393Syz155240 ips.ips_is.is_rule = fr; 7292393Syz155240 MUTEX_NUKE(&fr->fr_lock); 7302393Syz155240 MUTEX_INIT(&fr->fr_lock, "state filter rule lock"); 7312393Syz155240 7322393Syz155240 /* 7332393Syz155240 * Look up all the interface names in the rule. 7342393Syz155240 */ 7352393Syz155240 for (i = 0; i < 4; i++) { 7362393Syz155240 name = fr->fr_ifnames[i]; 7373448Sdh155122 fr->fr_ifas[i] = fr_resolvenic(name, fr->fr_v, ifs); 7382393Syz155240 name = isn->is_ifname[i]; 7393448Sdh155122 isn->is_ifp[i] = fr_resolvenic(name, isn->is_v, ifs); 7402393Syz155240 } 7412393Syz155240 7422393Syz155240 fr->fr_ref = 0; 7432393Syz155240 fr->fr_dsize = 0; 7442393Syz155240 fr->fr_data = NULL; 7457131Sdr146992 fr->fr_type = FR_T_NONE; 7462393Syz155240 7473448Sdh155122 fr_resolvedest(&fr->fr_tif, fr->fr_v, ifs); 7483448Sdh155122 fr_resolvedest(&fr->fr_dif, fr->fr_v, ifs); 7493894Sjojemann fr_resolvedest(&fr->fr_rif, fr->fr_v, ifs); 7502393Syz155240 7512393Syz155240 /* 7522393Syz155240 * send a copy back to userland of what we ended up 7532393Syz155240 * to allow for verification. 7542393Syz155240 */ 7552393Syz155240 error = fr_outobj(data, &ips, IPFOBJ_STATESAVE); 7562393Syz155240 if (error) { 7572393Syz155240 KFREE(isn); 7582393Syz155240 MUTEX_DESTROY(&fr->fr_lock); 7592393Syz155240 KFREE(fr); 7602393Syz155240 return EFAULT; 7612393Syz155240 } 7623448Sdh155122 READ_ENTER(&ifs->ifs_ipf_state); 7633448Sdh155122 fr_stinsert(isn, 0, ifs); 7642393Syz155240 MUTEX_EXIT(&isn->is_lock); 7653448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_state); 7662393Syz155240 7672393Syz155240 } else { 7683448Sdh155122 READ_ENTER(&ifs->ifs_ipf_state); 7693448Sdh155122 for (is = ifs->ifs_ips_list; is; is = is->is_next) 7702393Syz155240 if (is->is_rule == fr) { 7713448Sdh155122 fr_stinsert(isn, 0, ifs); 7722393Syz155240 MUTEX_EXIT(&isn->is_lock); 7732393Syz155240 break; 7742393Syz155240 } 7752393Syz155240 7762393Syz155240 if (is == NULL) { 7772393Syz155240 KFREE(isn); 7782393Syz155240 isn = NULL; 7792393Syz155240 } 7803448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_state); 7812393Syz155240 7822393Syz155240 return (isn == NULL) ? ESRCH : 0; 7832393Syz155240 } 7842393Syz155240 7852393Syz155240 return 0; 7862393Syz155240 } 7872393Syz155240 7882393Syz155240 7892393Syz155240 /* ------------------------------------------------------------------------ */ 7902393Syz155240 /* Function: fr_stinsert */ 7912393Syz155240 /* Returns: Nil */ 7922393Syz155240 /* Parameters: is(I) - pointer to state structure */ 7932393Syz155240 /* rev(I) - flag indicating forward/reverse direction of packet */ 7942393Syz155240 /* */ 7952393Syz155240 /* Inserts a state structure into the hash table (for lookups) and the list */ 7962393Syz155240 /* of state entries (for enumeration). Resolves all of the interface names */ 7972393Syz155240 /* to pointers and adjusts running stats for the hash table as appropriate. */ 7982393Syz155240 /* */ 7992393Syz155240 /* Locking: it is assumed that some kind of lock on ipf_state is held. */ 8002393Syz155240 /* Exits with is_lock initialised and held. */ 8012393Syz155240 /* ------------------------------------------------------------------------ */ 8023448Sdh155122 void fr_stinsert(is, rev, ifs) 8032393Syz155240 ipstate_t *is; 8042393Syz155240 int rev; 8053448Sdh155122 ipf_stack_t *ifs; 8062393Syz155240 { 8072393Syz155240 frentry_t *fr; 8082393Syz155240 u_int hv; 8092393Syz155240 int i; 8102393Syz155240 8112393Syz155240 MUTEX_INIT(&is->is_lock, "ipf state entry"); 8122393Syz155240 8132393Syz155240 fr = is->is_rule; 8142393Syz155240 if (fr != NULL) { 8152393Syz155240 MUTEX_ENTER(&fr->fr_lock); 8162393Syz155240 fr->fr_ref++; 8172393Syz155240 fr->fr_statecnt++; 8182393Syz155240 MUTEX_EXIT(&fr->fr_lock); 8192393Syz155240 } 8202393Syz155240 8212393Syz155240 /* 8222393Syz155240 * Look up all the interface names in the state entry. 8232393Syz155240 */ 8242393Syz155240 for (i = 0; i < 4; i++) { 8252393Syz155240 if (is->is_ifp[i] != NULL) 8262393Syz155240 continue; 8273448Sdh155122 is->is_ifp[i] = fr_resolvenic(is->is_ifname[i], is->is_v, ifs); 8282393Syz155240 } 8292393Syz155240 8302393Syz155240 /* 8312393Syz155240 * If we could trust is_hv, then the modulous would not be needed, but 8322393Syz155240 * when running with IPFILTER_SYNC, this stops bad values. 8332393Syz155240 */ 8343448Sdh155122 hv = is->is_hv % ifs->ifs_fr_statesize; 8352393Syz155240 is->is_hv = hv; 8362393Syz155240 8372393Syz155240 /* 8382393Syz155240 * We need to get both of these locks...the first because it is 8392393Syz155240 * possible that once the insert is complete another packet might 8402393Syz155240 * come along, match the entry and want to update it. 8412393Syz155240 */ 8422393Syz155240 MUTEX_ENTER(&is->is_lock); 8433448Sdh155122 MUTEX_ENTER(&ifs->ifs_ipf_stinsert); 8442393Syz155240 8452393Syz155240 /* 8462393Syz155240 * add into list table. 8472393Syz155240 */ 8483448Sdh155122 if (ifs->ifs_ips_list != NULL) 8493448Sdh155122 ifs->ifs_ips_list->is_pnext = &is->is_next; 8503448Sdh155122 is->is_pnext = &ifs->ifs_ips_list; 8513448Sdh155122 is->is_next = ifs->ifs_ips_list; 8523448Sdh155122 ifs->ifs_ips_list = is; 8533448Sdh155122 8543448Sdh155122 if (ifs->ifs_ips_table[hv] != NULL) 8553448Sdh155122 ifs->ifs_ips_table[hv]->is_phnext = &is->is_hnext; 8562393Syz155240 else 8573448Sdh155122 ifs->ifs_ips_stats.iss_inuse++; 8583448Sdh155122 is->is_phnext = ifs->ifs_ips_table + hv; 8593448Sdh155122 is->is_hnext = ifs->ifs_ips_table[hv]; 8603448Sdh155122 ifs->ifs_ips_table[hv] = is; 8613448Sdh155122 ifs->ifs_ips_stats.iss_bucketlen[hv]++; 8623448Sdh155122 ifs->ifs_ips_num++; 8633448Sdh155122 MUTEX_EXIT(&ifs->ifs_ipf_stinsert); 8643448Sdh155122 8653448Sdh155122 fr_setstatequeue(is, rev, ifs); 8662393Syz155240 } 8672393Syz155240 8686252San207044 /* ------------------------------------------------------------------------ */ 8696252San207044 /* Function: fr_match_ipv4addrs */ 8706252San207044 /* Returns: int - 2 strong match (same addresses, same direction) */ 8716252San207044 /* 1 weak match (same address, opposite direction) */ 8726252San207044 /* 0 no match */ 8736252San207044 /* */ 8746252San207044 /* Function matches IPv4 addresses. */ 8756252San207044 /* ------------------------------------------------------------------------ */ 8766252San207044 static int fr_match_ipv4addrs(is1, is2) 8776252San207044 ipstate_t *is1; 8786252San207044 ipstate_t *is2; 8796252San207044 { 8806252San207044 int rv; 8816252San207044 8826252San207044 if (is1->is_saddr == is2->is_saddr && is1->is_daddr == is2->is_daddr) 8836252San207044 rv = 2; 8847704SAlexandr.Nedvedicky@Sun.COM else if (is1->is_saddr == is2->is_daddr && 8856252San207044 is1->is_daddr == is2->is_saddr) 8866252San207044 rv = 1; 8876252San207044 else 8886252San207044 rv = 0; 8897704SAlexandr.Nedvedicky@Sun.COM 8906252San207044 return (rv); 8916252San207044 } 8926252San207044 8936252San207044 /* ------------------------------------------------------------------------ */ 8946252San207044 /* Function: fr_match_ipv6addrs */ 8956252San207044 /* Returns: int - 2 strong match (same addresses, same direction) */ 8966252San207044 /* 1 weak match (same addresses, opposite direction) */ 8976252San207044 /* 0 no match */ 8986252San207044 /* */ 8996252San207044 /* Function matches IPv6 addresses. */ 9006252San207044 /* ------------------------------------------------------------------------ */ 9016252San207044 static int fr_match_ipv6addrs(is1, is2) 9026252San207044 ipstate_t *is1; 9036252San207044 ipstate_t *is2; 9046252San207044 { 9056252San207044 int rv; 9066252San207044 9077704SAlexandr.Nedvedicky@Sun.COM if (IP6_EQ(&is1->is_src, &is2->is_src) && 9087704SAlexandr.Nedvedicky@Sun.COM IP6_EQ(&is1->is_dst, &is2->is_dst)) 9096252San207044 rv = 2; 9106252San207044 else if (IP6_EQ(&is1->is_src, &is2->is_dst) && 9116252San207044 IP6_EQ(&is1->is_dst, &is2->is_src)) { 9126252San207044 rv = 1; 9136252San207044 } 9146252San207044 else 9156252San207044 rv = 0; 9166252San207044 9176252San207044 return (rv); 9186252San207044 } 9196252San207044 /* ------------------------------------------------------------------------ */ 9206252San207044 /* Function: fr_match_addresses */ 9216252San207044 /* Returns: int - 2 strong match (same addresses, same direction) */ 9227704SAlexandr.Nedvedicky@Sun.COM /* 1 weak match (same address, opposite directions) */ 9236252San207044 /* 0 no match */ 9246252San207044 /* Parameters: is1, is2 pointers to states we are checking */ 9256252San207044 /* */ 9266252San207044 /* Matches addresses, function uses fr_match_ipvXaddrs() to deal with IPv4 */ 9276252San207044 /* and IPv6 address format. */ 9286252San207044 /* ------------------------------------------------------------------------ */ 9296252San207044 static int fr_match_addresses(is1, is2) 9306252San207044 ipstate_t *is1; 9316252San207044 ipstate_t *is2; 9326252San207044 { 9336252San207044 int rv; 9346252San207044 9356252San207044 if (is1->is_v == 4) { 9366252San207044 rv = fr_match_ipv4addrs(is1, is2); 9377704SAlexandr.Nedvedicky@Sun.COM } else { 9386252San207044 rv = fr_match_ipv6addrs(is1, is2); 9396252San207044 } 9407704SAlexandr.Nedvedicky@Sun.COM 9416252San207044 return (rv); 9426252San207044 } 9436252San207044 9446252San207044 /* ------------------------------------------------------------------------ */ 9456252San207044 /* Function: fr_match_ppairs */ 9466252San207044 /* Returns: int - 2 strong match (same ports, same direction) */ 9476252San207044 /* 1 weak match (same ports, different direction) */ 9486252San207044 /* 0 no match */ 9496252San207044 /* Parameters ppairs1, ppairs - src, dst ports we want to match. */ 9506252San207044 /* */ 9516252San207044 /* Matches two port_pair_t types (port pairs). Each port pair contains */ 9526252San207044 /* src, dst port, which belong to session (state entry). */ 9536252San207044 /* ------------------------------------------------------------------------ */ 9546252San207044 static int fr_match_ppairs(ppairs1, ppairs2) 9556252San207044 port_pair_t *ppairs1; 9566252San207044 port_pair_t *ppairs2; 9576252San207044 { 9586252San207044 int rv; 9596252San207044 9607704SAlexandr.Nedvedicky@Sun.COM if (ppairs1->pp_sport == ppairs2->pp_sport && 9616252San207044 ppairs1->pp_dport == ppairs2->pp_dport) 9626252San207044 rv = 2; 9637704SAlexandr.Nedvedicky@Sun.COM else if (ppairs1->pp_sport == ppairs2->pp_dport && 9646252San207044 ppairs1->pp_dport == ppairs2->pp_sport) 9656252San207044 rv = 1; 9666252San207044 else 9676252San207044 rv = 0; 9686252San207044 9696252San207044 return (rv); 9706252San207044 } 9716252San207044 9726252San207044 /* ------------------------------------------------------------------------ */ 9736252San207044 /* Function: fr_match_l4_hdr */ 9747704SAlexandr.Nedvedicky@Sun.COM /* Returns: int - 0 no match, */ 9756252San207044 /* 1 weak match (same ports, different directions) */ 9766252San207044 /* 2 strong match (same ports, same direction) */ 9776252San207044 /* Parameters is1, is2 - states we want to match */ 9786252San207044 /* */ 9796252San207044 /* Function matches L4 header data (source ports for TCP, UDP, CallIds for */ 9806252San207044 /* GRE protocol). */ 9816252San207044 /* ------------------------------------------------------------------------ */ 9826252San207044 static int fr_match_l4_hdr(is1, is2) 9836252San207044 ipstate_t *is1; 9846252San207044 ipstate_t *is2; 9856252San207044 { 9866252San207044 int rv = 0; 9876252San207044 port_pair_t pp1; 9886252San207044 port_pair_t pp2; 9896252San207044 9906252San207044 if (is1->is_p != is2->is_p) 9916252San207044 return (0); 9926252San207044 9936252San207044 switch (is1->is_p) { 9946252San207044 case IPPROTO_TCP: 9956252San207044 pp1.pp_sport = is1->is_ps.is_ts.ts_sport; 9966252San207044 pp1.pp_dport = is1->is_ps.is_ts.ts_dport; 9976252San207044 pp2.pp_sport = is2->is_ps.is_ts.ts_sport; 9986252San207044 pp2.pp_dport = is2->is_ps.is_ts.ts_dport; 9996252San207044 rv = fr_match_ppairs(&pp1, &pp2); 10006252San207044 break; 10016252San207044 case IPPROTO_UDP: 10026252San207044 pp1.pp_sport = is1->is_ps.is_us.us_sport; 10036252San207044 pp1.pp_dport = is1->is_ps.is_us.us_dport; 10046252San207044 pp2.pp_sport = is2->is_ps.is_us.us_sport; 10056252San207044 pp2.pp_dport = is2->is_ps.is_us.us_dport; 10066252San207044 rv = fr_match_ppairs(&pp1, &pp2); 10076252San207044 break; 10086252San207044 case IPPROTO_GRE: 10096252San207044 /* greinfo_t can be also interprted as port pair */ 10106252San207044 pp1.pp_sport = is1->is_ps.is_ug.gs_call[0]; 10116252San207044 pp1.pp_dport = is1->is_ps.is_ug.gs_call[1]; 10126252San207044 pp2.pp_sport = is2->is_ps.is_ug.gs_call[0]; 10136252San207044 pp2.pp_dport = is2->is_ps.is_ug.gs_call[1]; 10146252San207044 rv = fr_match_ppairs(&pp1, &pp2); 10156252San207044 break; 10166252San207044 case IPPROTO_ICMP: 10176252San207044 case IPPROTO_ICMPV6: 10187704SAlexandr.Nedvedicky@Sun.COM if (bcmp(&is1->is_ps, &is2->is_ps, sizeof (icmpinfo_t))) 10196252San207044 rv = 1; 10206252San207044 else 10216252San207044 rv = 0; 10226252San207044 break; 10236252San207044 default: 10246252San207044 rv = 0; 10256252San207044 } 10266252San207044 10276252San207044 return (rv); 10286252San207044 } 10296252San207044 10306252San207044 /* ------------------------------------------------------------------------ */ 10316252San207044 /* Function: fr_matchstates */ 10326252San207044 /* Returns: int - nonzero match, zero no match */ 10336252San207044 /* Parameters is1, is2 - states we want to match */ 10346252San207044 /* */ 10356252San207044 /* The state entries are equal (identical match) if they belong to the same */ 10367704SAlexandr.Nedvedicky@Sun.COM /* session. Any time new state entry is being added the fr_addstate() */ 10376252San207044 /* function creates temporal state entry from the data it gets from IP and */ 10386252San207044 /* L4 header. The fr_matchstats() must be also aware of packet direction, */ 10396252San207044 /* which is also stored within the state entry. We should keep in mind the */ 10406252San207044 /* information about packet direction is spread accross L3 (addresses) and */ 10416252San207044 /* L4 (ports). There are three possible relationships betwee is1, is2: */ 10426252San207044 /* - no match (match(is1, is2) == 0)) */ 10437704SAlexandr.Nedvedicky@Sun.COM /* - weak match same addresses (ports), but different */ 10446252San207044 /* directions (1) (fr_match_xxxx(is1, is2) == 1) */ 10456252San207044 /* - strong match same addresses (ports) and same directions */ 10466252San207044 /* (2) (fr_match_xxxx(is1, is2) == 2) */ 10476252San207044 /* */ 10486252San207044 /* There are functions, which match match addresses (L3 header) in is1, is2 */ 10496252San207044 /* and functions, which are used to compare ports (L4 header) data. We say */ 10506252San207044 /* the is1 and is2 are same (identical) if there is a match */ 10516252San207044 /* (fr_match_l4_hdr(is1, is2) != 0) and matchlevels are same for entries */ 10526252San207044 /* (fr_match_l3_hdr(is1, is2) == fr_match_l4_hdr(is1, is2)) for is1, is2. */ 10536252San207044 /* Such requirement deals with case as follows: */ 10546252San207044 /* suppose there are two connections between hosts A, B. Connection 1: */ 10556252San207044 /* a.a.a.a:12345 <=> b.b.b.b:54321 */ 10566252San207044 /* Connection 2: */ 10577704SAlexandr.Nedvedicky@Sun.COM /* a.a.a.a:54321 <=> b.b.b.b:12345 */ 10586252San207044 /* since we've introduced match levels into our fr_matchstates(), we are */ 10596252San207044 /* able to identify, which packets belong to connection A and which belong */ 10606252San207044 /* to connection B. Assume there are two entries is1, is2. is1 has been */ 10616252San207044 /* from con. 1 packet, which travelled from A to B: */ 10626252San207044 /* a.a.a.a:12345 -> b.b.b.b:54321 */ 10636252San207044 /* while s2, has been created from packet which belongs to con. 2 and is */ 10646252San207044 /* also coming from A to B: */ 10656252San207044 /* a.a.a.a:54321 -> b.b.b.b:12345 */ 10666252San207044 /* fr_match_l3_hdr(is1, is2) == 2 -> strong match, while */ 10676252San207044 /* fr_match_l4_hdr(is1, is2) == 1 -> weak match. Since match levels are */ 10686252San207044 /* different the state entries are not identical -> no match as a final */ 10696252San207044 /* result. */ 10706252San207044 /* ------------------------------------------------------------------------ */ 10716252San207044 static int fr_matchstates(is1, is2) 10727704SAlexandr.Nedvedicky@Sun.COM ipstate_t *is1; 10736252San207044 ipstate_t *is2; 10746252San207044 { 10756252San207044 int rv; 10766252San207044 int amatch; 10776252San207044 int pmatch; 10786252San207044 10796252San207044 if (bcmp(&is1->is_pass, &is2->is_pass, 10807704SAlexandr.Nedvedicky@Sun.COM offsetof(struct ipstate, is_ps) - 10817704SAlexandr.Nedvedicky@Sun.COM offsetof(struct ipstate, is_pass)) == 0) { 10827704SAlexandr.Nedvedicky@Sun.COM 10836252San207044 pmatch = fr_match_l4_hdr(is1, is2); 10846252San207044 amatch = fr_match_addresses(is1, is2); 10857704SAlexandr.Nedvedicky@Sun.COM /* 10866252San207044 * If addresses match (amatch != 0), then 'match levels' 10876252San207044 * must be same for matching entries. If amatch and pmatch 10886252San207044 * have different values (different match levels), then 10896252San207044 * is1 and is2 belong to different sessions. 10906252San207044 */ 10916252San207044 rv = (amatch != 0) && (amatch == pmatch); 10926252San207044 } 10936252San207044 else 10946252San207044 rv = 0; 10956252San207044 10966252San207044 return (rv); 10976252San207044 } 10982393Syz155240 10992393Syz155240 /* ------------------------------------------------------------------------ */ 11002393Syz155240 /* Function: fr_addstate */ 11012393Syz155240 /* Returns: ipstate_t* - NULL == failure, else pointer to new state */ 11022393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 11032393Syz155240 /* stsave(O) - pointer to place to save pointer to created */ 11042393Syz155240 /* state structure. */ 11052393Syz155240 /* flags(I) - flags to use when creating the structure */ 11062393Syz155240 /* */ 11072393Syz155240 /* Creates a new IP state structure from the packet information collected. */ 11082393Syz155240 /* Inserts it into the state table and appends to the bottom of the active */ 11092393Syz155240 /* list. If the capacity of the table has reached the maximum allowed then */ 11102393Syz155240 /* the call will fail and a flush is scheduled for the next timeout call. */ 11112393Syz155240 /* ------------------------------------------------------------------------ */ 11122393Syz155240 ipstate_t *fr_addstate(fin, stsave, flags) 11132393Syz155240 fr_info_t *fin; 11142393Syz155240 ipstate_t **stsave; 11152393Syz155240 u_int flags; 11162393Syz155240 { 11172393Syz155240 ipstate_t *is, ips; 11182393Syz155240 struct icmp *ic; 11192393Syz155240 u_int pass, hv; 11202393Syz155240 frentry_t *fr; 11212393Syz155240 tcphdr_t *tcp; 11222393Syz155240 grehdr_t *gre; 11232393Syz155240 void *ifp; 11242393Syz155240 int out; 11253448Sdh155122 ipf_stack_t *ifs = fin->fin_ifs; 11263448Sdh155122 11273448Sdh155122 if (ifs->ifs_fr_state_lock || 11282393Syz155240 (fin->fin_flx & (FI_SHORT|FI_STATE|FI_FRAGBODY|FI_BAD))) 11292393Syz155240 return NULL; 11302393Syz155240 11312393Syz155240 if ((fin->fin_flx & FI_OOW) && !(fin->fin_tcpf & TH_SYN)) 11322393Syz155240 return NULL; 11332393Syz155240 11342393Syz155240 /* 11358170SJohn.Ojemann@Sun.COM * Trigger automatic call to fr_state_flush() if the 11368170SJohn.Ojemann@Sun.COM * table has reached capacity specified by hi watermark. 11372393Syz155240 */ 11388170SJohn.Ojemann@Sun.COM if (ST_TAB_WATER_LEVEL(ifs) > ifs->ifs_state_flush_level_hi) 11398170SJohn.Ojemann@Sun.COM ifs->ifs_fr_state_doflush = 1; 11408170SJohn.Ojemann@Sun.COM 11418170SJohn.Ojemann@Sun.COM /* 11428170SJohn.Ojemann@Sun.COM * If the max number of state entries has been reached, and there is no 11438170SJohn.Ojemann@Sun.COM * limit on the state count for the rule, then do not continue. In the 11448170SJohn.Ojemann@Sun.COM * case where a limit exists, it's ok allow the entries to be created as 11458170SJohn.Ojemann@Sun.COM * long as specified limit itself has not been reached. 11468170SJohn.Ojemann@Sun.COM * 11478170SJohn.Ojemann@Sun.COM * Note that because the lock isn't held on fr, it is possible to exceed 11488170SJohn.Ojemann@Sun.COM * the specified size of the table. However, the cost of this is being 11498170SJohn.Ojemann@Sun.COM * ignored here; as the number by which it can go over is a product of 11508170SJohn.Ojemann@Sun.COM * the number of simultaneous threads that could be executing in here. 11518170SJohn.Ojemann@Sun.COM * So, a limit of 100 won't result in 200, but could result in 101 or 102. 11528170SJohn.Ojemann@Sun.COM * 11538170SJohn.Ojemann@Sun.COM * Also note that, since the automatic flush should have been triggered 11548170SJohn.Ojemann@Sun.COM * well before we reach the maximum number of state table entries, the 11558170SJohn.Ojemann@Sun.COM * likelihood of reaching the max (and thus exceedng it) is minimal. 11568170SJohn.Ojemann@Sun.COM */ 11572944Sjojemann fr = fin->fin_fr; 11582944Sjojemann if (fr != NULL) { 11598170SJohn.Ojemann@Sun.COM if ((ifs->ifs_ips_num >= ifs->ifs_fr_statemax) && 11608170SJohn.Ojemann@Sun.COM (fr->fr_statemax == 0)) { 11613448Sdh155122 ATOMIC_INCL(ifs->ifs_ips_stats.iss_max); 11622944Sjojemann return NULL; 11632944Sjojemann } 11642944Sjojemann if ((fr->fr_statemax != 0) && 11652944Sjojemann (fr->fr_statecnt >= fr->fr_statemax)) { 11663448Sdh155122 ATOMIC_INCL(ifs->ifs_ips_stats.iss_maxref); 11673448Sdh155122 ifs->ifs_fr_state_doflush = 1; 11682944Sjojemann return NULL; 11692944Sjojemann } 11702393Syz155240 } 11712393Syz155240 11722393Syz155240 ic = NULL; 11732393Syz155240 tcp = NULL; 11742393Syz155240 out = fin->fin_out; 11752393Syz155240 is = &ips; 11762393Syz155240 bzero((char *)is, sizeof(*is)); 11776252San207044 11786252San207044 if (fr == NULL) { 11796252San207044 pass = ifs->ifs_fr_flags; 11806252San207044 is->is_tag = FR_NOLOGTAG; 11817704SAlexandr.Nedvedicky@Sun.COM } else { 11826252San207044 pass = fr->fr_flags; 11836252San207044 } 11846252San207044 11853448Sdh155122 is->is_die = 1 + ifs->ifs_fr_ticks; 11866252San207044 /* 11876252San207044 * We want to check everything that is a property of this packet, 11886252San207044 * but we don't (automatically) care about it's fragment status as 11896252San207044 * this may change. 11906252San207044 */ 11916252San207044 is->is_pass = pass; 11926252San207044 is->is_v = fin->fin_v; 11936252San207044 is->is_opt[0] = fin->fin_optmsk; 11946252San207044 is->is_optmsk[0] = 0xffffffff; 11956252San207044 is->is_optmsk[1] = 0xffffffff; 11966252San207044 if (is->is_v == 6) { 11976252San207044 is->is_opt[0] &= ~0x8; 11986252San207044 is->is_optmsk[0] &= ~0x8; 11996252San207044 is->is_optmsk[1] &= ~0x8; 12006252San207044 } 12016252San207044 is->is_sec = fin->fin_secmsk; 12026252San207044 is->is_secmsk = 0xffff; 12036252San207044 is->is_auth = fin->fin_auth; 12046252San207044 is->is_authmsk = 0xffff; 12052393Syz155240 12062393Syz155240 /* 12072393Syz155240 * Copy and calculate... 12082393Syz155240 */ 12092393Syz155240 hv = (is->is_p = fin->fin_fi.fi_p); 12102393Syz155240 is->is_src = fin->fin_fi.fi_src; 12112393Syz155240 hv += is->is_saddr; 12122393Syz155240 is->is_dst = fin->fin_fi.fi_dst; 12132393Syz155240 hv += is->is_daddr; 12142393Syz155240 #ifdef USE_INET6 12152393Syz155240 if (fin->fin_v == 6) { 12162393Syz155240 /* 12172393Syz155240 * For ICMPv6, we check to see if the destination address is 12182393Syz155240 * a multicast address. If it is, do not include it in the 12192393Syz155240 * calculation of the hash because the correct reply will come 12202393Syz155240 * back from a real address, not a multicast address. 12212393Syz155240 */ 12222393Syz155240 if ((is->is_p == IPPROTO_ICMPV6) && 12232393Syz155240 IN6_IS_ADDR_MULTICAST(&is->is_dst.in6)) { 12242393Syz155240 /* 12252393Syz155240 * So you can do keep state with neighbour discovery. 12262393Syz155240 * 12272393Syz155240 * Here we could use the address from the neighbour 12282393Syz155240 * solicit message to put in the state structure and 12292393Syz155240 * we could use that without a wildcard flag too... 12302393Syz155240 */ 12312393Syz155240 is->is_flags |= SI_W_DADDR; 12322393Syz155240 hv -= is->is_daddr; 12332393Syz155240 } else { 12342393Syz155240 hv += is->is_dst.i6[1]; 12352393Syz155240 hv += is->is_dst.i6[2]; 12362393Syz155240 hv += is->is_dst.i6[3]; 12372393Syz155240 } 12382393Syz155240 hv += is->is_src.i6[1]; 12392393Syz155240 hv += is->is_src.i6[2]; 12402393Syz155240 hv += is->is_src.i6[3]; 12412393Syz155240 } 12422393Syz155240 #endif 12437433SJohn.Ojemann@Sun.COM if ((fin->fin_v == 4) && 12447433SJohn.Ojemann@Sun.COM (fin->fin_flx & (FI_MULTICAST|FI_BROADCAST|FI_MBCAST))) { 12457433SJohn.Ojemann@Sun.COM if (fin->fin_out == 0) { 12467433SJohn.Ojemann@Sun.COM flags |= SI_W_DADDR|SI_CLONE; 12477433SJohn.Ojemann@Sun.COM hv -= is->is_daddr; 12487433SJohn.Ojemann@Sun.COM } else { 12497433SJohn.Ojemann@Sun.COM flags |= SI_W_SADDR|SI_CLONE; 12507433SJohn.Ojemann@Sun.COM hv -= is->is_saddr; 12517433SJohn.Ojemann@Sun.COM } 12527433SJohn.Ojemann@Sun.COM } 12532393Syz155240 12542393Syz155240 switch (is->is_p) 12552393Syz155240 { 12562393Syz155240 #ifdef USE_INET6 12572393Syz155240 case IPPROTO_ICMPV6 : 12582393Syz155240 ic = fin->fin_dp; 12592393Syz155240 12602393Syz155240 switch (ic->icmp_type) 12612393Syz155240 { 12622393Syz155240 case ICMP6_ECHO_REQUEST : 12632393Syz155240 is->is_icmp.ici_type = ic->icmp_type; 12642393Syz155240 hv += (is->is_icmp.ici_id = ic->icmp_id); 12652393Syz155240 break; 12662393Syz155240 case ICMP6_MEMBERSHIP_QUERY : 12672393Syz155240 case ND_ROUTER_SOLICIT : 12682393Syz155240 case ND_NEIGHBOR_SOLICIT : 12692393Syz155240 case ICMP6_NI_QUERY : 12702393Syz155240 is->is_icmp.ici_type = ic->icmp_type; 12712393Syz155240 break; 12722393Syz155240 default : 12732393Syz155240 return NULL; 12742393Syz155240 } 12753448Sdh155122 ATOMIC_INCL(ifs->ifs_ips_stats.iss_icmp); 12762393Syz155240 break; 12772393Syz155240 #endif 12782393Syz155240 case IPPROTO_ICMP : 12792393Syz155240 ic = fin->fin_dp; 12802393Syz155240 12812393Syz155240 switch (ic->icmp_type) 12822393Syz155240 { 12832393Syz155240 case ICMP_ECHO : 12842393Syz155240 case ICMP_TSTAMP : 12852393Syz155240 case ICMP_IREQ : 12862393Syz155240 case ICMP_MASKREQ : 12872393Syz155240 is->is_icmp.ici_type = ic->icmp_type; 12882393Syz155240 hv += (is->is_icmp.ici_id = ic->icmp_id); 12892393Syz155240 break; 12902393Syz155240 default : 12912393Syz155240 return NULL; 12922393Syz155240 } 12933448Sdh155122 ATOMIC_INCL(ifs->ifs_ips_stats.iss_icmp); 12942393Syz155240 break; 12952393Syz155240 12962393Syz155240 case IPPROTO_GRE : 12972393Syz155240 gre = fin->fin_dp; 12982393Syz155240 12992393Syz155240 is->is_gre.gs_flags = gre->gr_flags; 13002393Syz155240 is->is_gre.gs_ptype = gre->gr_ptype; 13012393Syz155240 if (GRE_REV(is->is_gre.gs_flags) == 1) { 13022393Syz155240 is->is_call[0] = fin->fin_data[0]; 13032393Syz155240 is->is_call[1] = fin->fin_data[1]; 13042393Syz155240 } 13052393Syz155240 break; 13062393Syz155240 13072393Syz155240 case IPPROTO_TCP : 13082393Syz155240 tcp = fin->fin_dp; 13092393Syz155240 13102393Syz155240 if (tcp->th_flags & TH_RST) 13112393Syz155240 return NULL; 13122393Syz155240 /* 13132393Syz155240 * The endian of the ports doesn't matter, but the ack and 13142393Syz155240 * sequence numbers do as we do mathematics on them later. 13152393Syz155240 */ 13162393Syz155240 is->is_sport = htons(fin->fin_data[0]); 13172393Syz155240 is->is_dport = htons(fin->fin_data[1]); 13182393Syz155240 if ((flags & (SI_W_DPORT|SI_W_SPORT)) == 0) { 13192393Syz155240 hv += is->is_sport; 13202393Syz155240 hv += is->is_dport; 13212393Syz155240 } 13222393Syz155240 13232393Syz155240 /* 13242393Syz155240 * If this is a real packet then initialise fields in the 13252393Syz155240 * state information structure from the TCP header information. 13262393Syz155240 */ 13272393Syz155240 13282393Syz155240 is->is_maxdwin = 1; 13292393Syz155240 is->is_maxswin = ntohs(tcp->th_win); 13302393Syz155240 if (is->is_maxswin == 0) 13312393Syz155240 is->is_maxswin = 1; 13322393Syz155240 13332393Syz155240 if ((fin->fin_flx & FI_IGNORE) == 0) { 13342393Syz155240 is->is_send = ntohl(tcp->th_seq) + fin->fin_dlen - 13352393Syz155240 (TCP_OFF(tcp) << 2) + 13362393Syz155240 ((tcp->th_flags & TH_SYN) ? 1 : 0) + 13372393Syz155240 ((tcp->th_flags & TH_FIN) ? 1 : 0); 13382393Syz155240 is->is_maxsend = is->is_send; 13392393Syz155240 13402393Syz155240 /* 13412393Syz155240 * Window scale option is only present in 13422393Syz155240 * SYN/SYN-ACK packet. 13432393Syz155240 */ 13442393Syz155240 if ((tcp->th_flags & ~(TH_FIN|TH_ACK|TH_ECNALL)) == 13452393Syz155240 TH_SYN && 13462393Syz155240 (TCP_OFF(tcp) > (sizeof(tcphdr_t) >> 2))) { 13472393Syz155240 if (fr_tcpoptions(fin, tcp, 13487704SAlexandr.Nedvedicky@Sun.COM &is->is_tcp.ts_data[0]) == -1) { 13496647San207044 fin->fin_flx |= FI_BAD; 13506647San207044 } 13512393Syz155240 } 13522393Syz155240 13532393Syz155240 if ((fin->fin_out != 0) && (pass & FR_NEWISN) != 0) { 13542393Syz155240 fr_checknewisn(fin, is); 13552393Syz155240 fr_fixoutisn(fin, is); 13562393Syz155240 } 13572393Syz155240 13582393Syz155240 if ((tcp->th_flags & TH_OPENING) == TH_SYN) 13592393Syz155240 flags |= IS_TCPFSM; 13602393Syz155240 else { 13612393Syz155240 is->is_maxdwin = is->is_maxswin * 2; 13622393Syz155240 is->is_dend = ntohl(tcp->th_ack); 13632393Syz155240 is->is_maxdend = ntohl(tcp->th_ack); 13642393Syz155240 is->is_maxdwin *= 2; 13652393Syz155240 } 13662393Syz155240 } 13672393Syz155240 13682393Syz155240 /* 13692393Syz155240 * If we're creating state for a starting connection, start the 13702393Syz155240 * timer on it as we'll never see an error if it fails to 13712393Syz155240 * connect. 13722393Syz155240 */ 13733448Sdh155122 ATOMIC_INCL(ifs->ifs_ips_stats.iss_tcp); 13742393Syz155240 break; 13752393Syz155240 13762393Syz155240 case IPPROTO_UDP : 13772393Syz155240 tcp = fin->fin_dp; 13782393Syz155240 13792393Syz155240 is->is_sport = htons(fin->fin_data[0]); 13802393Syz155240 is->is_dport = htons(fin->fin_data[1]); 13812393Syz155240 if ((flags & (SI_W_DPORT|SI_W_SPORT)) == 0) { 13822393Syz155240 hv += tcp->th_dport; 13832393Syz155240 hv += tcp->th_sport; 13842393Syz155240 } 13853448Sdh155122 ATOMIC_INCL(ifs->ifs_ips_stats.iss_udp); 13862393Syz155240 break; 13872393Syz155240 13882393Syz155240 default : 13892393Syz155240 break; 13902393Syz155240 } 13913448Sdh155122 hv = DOUBLE_HASH(hv, ifs); 13922393Syz155240 is->is_hv = hv; 13932393Syz155240 is->is_rule = fr; 13942393Syz155240 is->is_flags = flags & IS_INHERITED; 13952393Syz155240 13962393Syz155240 /* 13972393Syz155240 * Look for identical state. 13982393Syz155240 */ 13993448Sdh155122 for (is = ifs->ifs_ips_table[is->is_hv % ifs->ifs_fr_statesize]; 14003448Sdh155122 is != NULL; 14012393Syz155240 is = is->is_hnext) { 14026252San207044 if (fr_matchstates(&ips, is) == 1) 14032393Syz155240 break; 14042393Syz155240 } 14057704SAlexandr.Nedvedicky@Sun.COM 14066252San207044 /* 14076252San207044 * we've found a matching state -> state already exists, 14086252San207044 * we are not going to add a duplicate record. 14096252San207044 */ 14102393Syz155240 if (is != NULL) 14112393Syz155240 return NULL; 14122393Syz155240 14133448Sdh155122 if (ifs->ifs_ips_stats.iss_bucketlen[hv] >= ifs->ifs_fr_state_maxbucket) { 14143448Sdh155122 ATOMIC_INCL(ifs->ifs_ips_stats.iss_bucketfull); 14152393Syz155240 return NULL; 14162393Syz155240 } 14172393Syz155240 KMALLOC(is, ipstate_t *); 14182393Syz155240 if (is == NULL) { 14193448Sdh155122 ATOMIC_INCL(ifs->ifs_ips_stats.iss_nomem); 14202393Syz155240 return NULL; 14212393Syz155240 } 14222393Syz155240 bcopy((char *)&ips, (char *)is, sizeof(*is)); 14232393Syz155240 /* 14242393Syz155240 * Do not do the modulous here, it is done in fr_stinsert(). 14252393Syz155240 */ 14262393Syz155240 if (fr != NULL) { 14272393Syz155240 (void) strncpy(is->is_group, fr->fr_group, FR_GROUPLEN); 14282393Syz155240 if (fr->fr_age[0] != 0) { 14293448Sdh155122 is->is_tqehead[0] = 14303448Sdh155122 fr_addtimeoutqueue(&ifs->ifs_ips_utqe, 14313448Sdh155122 fr->fr_age[0], ifs); 14322393Syz155240 is->is_sti.tqe_flags |= TQE_RULEBASED; 14332393Syz155240 } 14342393Syz155240 if (fr->fr_age[1] != 0) { 14353448Sdh155122 is->is_tqehead[1] = 14363448Sdh155122 fr_addtimeoutqueue(&ifs->ifs_ips_utqe, 14373448Sdh155122 fr->fr_age[1], ifs); 14382393Syz155240 is->is_sti.tqe_flags |= TQE_RULEBASED; 14392393Syz155240 } 14402393Syz155240 is->is_tag = fr->fr_logtag; 14412393Syz155240 14422393Syz155240 is->is_ifp[(out << 1) + 1] = fr->fr_ifas[1]; 14432393Syz155240 is->is_ifp[(1 - out) << 1] = fr->fr_ifas[2]; 14442393Syz155240 is->is_ifp[((1 - out) << 1) + 1] = fr->fr_ifas[3]; 14452393Syz155240 14462393Syz155240 if (((ifp = fr->fr_ifas[1]) != NULL) && 14472393Syz155240 (ifp != (void *)-1)) { 14482958Sdr146992 COPYIFNAME(ifp, is->is_ifname[(out << 1) + 1], fr->fr_v); 14492393Syz155240 } 14502393Syz155240 if (((ifp = fr->fr_ifas[2]) != NULL) && 14512393Syz155240 (ifp != (void *)-1)) { 14522958Sdr146992 COPYIFNAME(ifp, is->is_ifname[(1 - out) << 1], fr->fr_v); 14532393Syz155240 } 14542393Syz155240 if (((ifp = fr->fr_ifas[3]) != NULL) && 14552393Syz155240 (ifp != (void *)-1)) { 14562958Sdr146992 COPYIFNAME(ifp, is->is_ifname[((1 - out) << 1) + 1], fr->fr_v); 14572393Syz155240 } 14582393Syz155240 } 14592393Syz155240 14602393Syz155240 is->is_ifp[out << 1] = fin->fin_ifp; 14612393Syz155240 if (fin->fin_ifp != NULL) { 14628463SDarren.Reed@Sun.COM COPYIFNAME(fin->fin_ifp, is->is_ifname[out << 1], fin->fin_v); 14632393Syz155240 } 14642393Syz155240 14658624SDarren.Reed@Sun.COM is->is_ref = 1; 14662393Syz155240 is->is_pkts[0] = 0, is->is_bytes[0] = 0; 14672393Syz155240 is->is_pkts[1] = 0, is->is_bytes[1] = 0; 14682393Syz155240 is->is_pkts[2] = 0, is->is_bytes[2] = 0; 14692393Syz155240 is->is_pkts[3] = 0, is->is_bytes[3] = 0; 14702393Syz155240 if ((fin->fin_flx & FI_IGNORE) == 0) { 14712393Syz155240 is->is_pkts[out] = 1; 14722393Syz155240 is->is_bytes[out] = fin->fin_plen; 14732393Syz155240 is->is_flx[out][0] = fin->fin_flx & FI_CMP; 14742393Syz155240 is->is_flx[out][0] &= ~FI_OOW; 14752393Syz155240 } 14762393Syz155240 14772393Syz155240 if (pass & FR_STSTRICT) 14782393Syz155240 is->is_flags |= IS_STRICT; 14792393Syz155240 14802393Syz155240 if (pass & FR_STATESYNC) 14812393Syz155240 is->is_flags |= IS_STATESYNC; 14822393Syz155240 14832393Syz155240 if (flags & (SI_WILDP|SI_WILDA)) { 14843448Sdh155122 ATOMIC_INCL(ifs->ifs_ips_stats.iss_wild); 14852393Syz155240 } 14862393Syz155240 is->is_rulen = fin->fin_rule; 14872393Syz155240 14882393Syz155240 14892393Syz155240 if (pass & FR_LOGFIRST) 14902393Syz155240 is->is_pass &= ~(FR_LOGFIRST|FR_LOG); 14912393Syz155240 14923448Sdh155122 READ_ENTER(&ifs->ifs_ipf_state); 14932393Syz155240 is->is_me = stsave; 14942393Syz155240 14953448Sdh155122 fr_stinsert(is, fin->fin_rev, ifs); 14962393Syz155240 14972393Syz155240 if (fin->fin_p == IPPROTO_TCP) { 14982393Syz155240 /* 14992393Syz155240 * If we're creating state for a starting connection, start the 15002393Syz155240 * timer on it as we'll never see an error if it fails to 15012393Syz155240 * connect. 15022393Syz155240 */ 15033448Sdh155122 (void) fr_tcp_age(&is->is_sti, fin, ifs->ifs_ips_tqtqb, 15043448Sdh155122 is->is_flags); 15052393Syz155240 MUTEX_EXIT(&is->is_lock); 15062393Syz155240 #ifdef IPFILTER_SCAN 15072393Syz155240 if ((is->is_flags & SI_CLONE) == 0) 15082393Syz155240 (void) ipsc_attachis(is); 15092393Syz155240 #endif 15102393Syz155240 } else { 15112393Syz155240 MUTEX_EXIT(&is->is_lock); 15122393Syz155240 } 15132393Syz155240 #ifdef IPFILTER_SYNC 15142393Syz155240 if ((is->is_flags & IS_STATESYNC) && ((is->is_flags & SI_CLONE) == 0)) 15152393Syz155240 is->is_sync = ipfsync_new(SMC_STATE, fin, is); 15162393Syz155240 #endif 15173448Sdh155122 if (ifs->ifs_ipstate_logging) 15183448Sdh155122 ipstate_log(is, ISL_NEW, ifs); 15193448Sdh155122 15203448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_state); 15212393Syz155240 fin->fin_rev = IP6_NEQ(&is->is_dst, &fin->fin_daddr); 15222393Syz155240 fin->fin_flx |= FI_STATE; 15232393Syz155240 if (fin->fin_flx & FI_FRAG) 15242393Syz155240 (void) fr_newfrag(fin, pass ^ FR_KEEPSTATE); 15252393Syz155240 15262393Syz155240 return is; 15272393Syz155240 } 15282393Syz155240 15292393Syz155240 15302393Syz155240 /* ------------------------------------------------------------------------ */ 15312393Syz155240 /* Function: fr_tcpoptions */ 15322393Syz155240 /* Returns: int - 1 == packet matches state entry, 0 == it does not */ 15332393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 15342393Syz155240 /* tcp(I) - pointer to TCP packet header */ 15352393Syz155240 /* td(I) - pointer to TCP data held as part of the state */ 15362393Syz155240 /* */ 15372393Syz155240 /* Look after the TCP header for any options and deal with those that are */ 15382393Syz155240 /* present. Record details about those that we recogise. */ 15392393Syz155240 /* ------------------------------------------------------------------------ */ 15402393Syz155240 static int fr_tcpoptions(fin, tcp, td) 15412393Syz155240 fr_info_t *fin; 15422393Syz155240 tcphdr_t *tcp; 15432393Syz155240 tcpdata_t *td; 15442393Syz155240 { 15452393Syz155240 int off, mlen, ol, i, len, retval; 15462393Syz155240 char buf[64], *s, opt; 15472393Syz155240 mb_t *m = NULL; 15482393Syz155240 15492393Syz155240 len = (TCP_OFF(tcp) << 2); 15502393Syz155240 if (fin->fin_dlen < len) 15512393Syz155240 return 0; 15522393Syz155240 len -= sizeof(*tcp); 15532393Syz155240 15542393Syz155240 off = fin->fin_plen - fin->fin_dlen + sizeof(*tcp) + fin->fin_ipoff; 15552393Syz155240 15562393Syz155240 m = fin->fin_m; 15572393Syz155240 mlen = MSGDSIZE(m) - off; 15582393Syz155240 if (len > mlen) { 15592393Syz155240 len = mlen; 15602393Syz155240 retval = 0; 15612393Syz155240 } else { 15622393Syz155240 retval = 1; 15632393Syz155240 } 15642393Syz155240 15652393Syz155240 COPYDATA(m, off, len, buf); 15662393Syz155240 15672393Syz155240 for (s = buf; len > 0; ) { 15682393Syz155240 opt = *s; 15692393Syz155240 if (opt == TCPOPT_EOL) 15702393Syz155240 break; 15712393Syz155240 else if (opt == TCPOPT_NOP) 15722393Syz155240 ol = 1; 15732393Syz155240 else { 15742393Syz155240 if (len < 2) 15752393Syz155240 break; 15762393Syz155240 ol = (int)*(s + 1); 15772393Syz155240 if (ol < 2 || ol > len) 15782393Syz155240 break; 15792393Syz155240 15802393Syz155240 /* 15812393Syz155240 * Extract the TCP options we are interested in out of 15822393Syz155240 * the header and store them in the the tcpdata struct. 15832393Syz155240 */ 15842393Syz155240 switch (opt) 15852393Syz155240 { 15862393Syz155240 case TCPOPT_WINDOW : 15872393Syz155240 if (ol == TCPOLEN_WINDOW) { 15882393Syz155240 i = (int)*(s + 2); 15892393Syz155240 if (i > TCP_WSCALE_MAX) 15902393Syz155240 i = TCP_WSCALE_MAX; 15912393Syz155240 else if (i < 0) 15922393Syz155240 i = 0; 15932393Syz155240 td->td_winscale = i; 15947704SAlexandr.Nedvedicky@Sun.COM td->td_winflags |= TCP_WSCALE_SEEN | 15957704SAlexandr.Nedvedicky@Sun.COM TCP_WSCALE_FIRST; 15966647San207044 } else 15976647San207044 retval = -1; 15982393Syz155240 break; 15992393Syz155240 case TCPOPT_MAXSEG : 16002393Syz155240 /* 16012393Syz155240 * So, if we wanted to set the TCP MAXSEG, 16022393Syz155240 * it should be done here... 16032393Syz155240 */ 16042393Syz155240 if (ol == TCPOLEN_MAXSEG) { 16052393Syz155240 i = (int)*(s + 2); 16062393Syz155240 i <<= 8; 16072393Syz155240 i += (int)*(s + 3); 16082393Syz155240 td->td_maxseg = i; 16096647San207044 } else 16106647San207044 retval = -1; 16116647San207044 break; 16126647San207044 case TCPOPT_SACK_PERMITTED : 16136647San207044 if (ol == TCPOLEN_SACK_PERMITTED) 16146647San207044 td->td_winflags |= TCP_SACK_PERMIT; 16156647San207044 else 16166647San207044 retval = -1; 16172393Syz155240 break; 16182393Syz155240 } 16192393Syz155240 } 16202393Syz155240 len -= ol; 16212393Syz155240 s += ol; 16222393Syz155240 } 16232393Syz155240 return retval; 16242393Syz155240 } 16252393Syz155240 16262393Syz155240 16272393Syz155240 /* ------------------------------------------------------------------------ */ 16282393Syz155240 /* Function: fr_tcpstate */ 16292393Syz155240 /* Returns: int - 1 == packet matches state entry, 0 == it does not */ 16302393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 16312393Syz155240 /* tcp(I) - pointer to TCP packet header */ 16322393Syz155240 /* is(I) - pointer to master state structure */ 16332393Syz155240 /* */ 16342393Syz155240 /* Check to see if a packet with TCP headers fits within the TCP window. */ 16352393Syz155240 /* Change timeout depending on whether new packet is a SYN-ACK returning */ 16362393Syz155240 /* for a SYN or a RST or FIN which indicate time to close up shop. */ 16372393Syz155240 /* ------------------------------------------------------------------------ */ 16382393Syz155240 static int fr_tcpstate(fin, tcp, is) 16392393Syz155240 fr_info_t *fin; 16402393Syz155240 tcphdr_t *tcp; 16412393Syz155240 ipstate_t *is; 16422393Syz155240 { 16432393Syz155240 int source, ret = 0, flags; 16442393Syz155240 tcpdata_t *fdata, *tdata; 16453448Sdh155122 ipf_stack_t *ifs = fin->fin_ifs; 16462393Syz155240 16472393Syz155240 source = !fin->fin_rev; 16482393Syz155240 if (((is->is_flags & IS_TCPFSM) != 0) && (source == 1) && 16492393Syz155240 (ntohs(is->is_sport) != fin->fin_data[0])) 16502393Syz155240 source = 0; 16512393Syz155240 fdata = &is->is_tcp.ts_data[!source]; 16522393Syz155240 tdata = &is->is_tcp.ts_data[source]; 16532393Syz155240 16542393Syz155240 MUTEX_ENTER(&is->is_lock); 16554431San207044 16564431San207044 /* 16574431San207044 * If a SYN packet is received for a connection that is in a half 16584431San207044 * closed state, then move its state entry to deletetq. In such case 16594431San207044 * the SYN packet will be consequently dropped. This allows new state 16604431San207044 * entry to be created with a retransmited SYN packet. 16614431San207044 */ 16624431San207044 if ((tcp->th_flags & TH_OPENING) == TH_SYN) { 16639888SAlexandr.Nedvedicky@Sun.COM if ((is->is_state[source] > IPF_TCPS_ESTABLISHED) && 16649888SAlexandr.Nedvedicky@Sun.COM (is->is_state[!source] > IPF_TCPS_ESTABLISHED)) { 16659888SAlexandr.Nedvedicky@Sun.COM is->is_state[source] = IPF_TCPS_CLOSED; 16669888SAlexandr.Nedvedicky@Sun.COM is->is_state[!source] = IPF_TCPS_CLOSED; 16674431San207044 /* 16684431San207044 * Do not update is->is_sti.tqe_die in case state entry 16694431San207044 * is already present in deletetq. It prevents state 16704431San207044 * entry ttl update by retransmitted SYN packets, which 16714431San207044 * may arrive before timer tick kicks off. The SYN 16724431San207044 * packet will be dropped again. 16734431San207044 */ 16744431San207044 if (is->is_sti.tqe_ifq != &ifs->ifs_ips_deletetq) 16754431San207044 fr_movequeue(&is->is_sti, is->is_sti.tqe_ifq, 16764431San207044 &fin->fin_ifs->ifs_ips_deletetq, 16774431San207044 fin->fin_ifs); 16784431San207044 16794431San207044 MUTEX_EXIT(&is->is_lock); 16804431San207044 return 0; 16814431San207044 } 16824431San207044 } 16834431San207044 16842393Syz155240 if (fr_tcpinwindow(fin, fdata, tdata, tcp, is->is_flags)) { 16852393Syz155240 #ifdef IPFILTER_SCAN 16862393Syz155240 if (is->is_flags & (IS_SC_CLIENT|IS_SC_SERVER)) { 16872393Syz155240 ipsc_packet(fin, is); 16882393Syz155240 if (FR_ISBLOCK(is->is_pass)) { 16892393Syz155240 MUTEX_EXIT(&is->is_lock); 16902393Syz155240 return 1; 16912393Syz155240 } 16922393Syz155240 } 16932393Syz155240 #endif 16942393Syz155240 16952393Syz155240 /* 16962393Syz155240 * Nearing end of connection, start timeout. 16972393Syz155240 */ 16983448Sdh155122 ret = fr_tcp_age(&is->is_sti, fin, ifs->ifs_ips_tqtqb, 16993448Sdh155122 is->is_flags); 17002393Syz155240 if (ret == 0) { 17012393Syz155240 MUTEX_EXIT(&is->is_lock); 17022393Syz155240 return 0; 17032393Syz155240 } 17042393Syz155240 17052393Syz155240 /* 17062393Syz155240 * set s0's as appropriate. Use syn-ack packet as it 17072393Syz155240 * contains both pieces of required information. 17082393Syz155240 */ 17092393Syz155240 /* 17102393Syz155240 * Window scale option is only present in SYN/SYN-ACK packet. 17112393Syz155240 * Compare with ~TH_FIN to mask out T/TCP setups. 17122393Syz155240 */ 17132393Syz155240 flags = tcp->th_flags & ~(TH_FIN|TH_ECNALL); 17142393Syz155240 if (flags == (TH_SYN|TH_ACK)) { 17152393Syz155240 is->is_s0[source] = ntohl(tcp->th_ack); 17162393Syz155240 is->is_s0[!source] = ntohl(tcp->th_seq) + 1; 17177704SAlexandr.Nedvedicky@Sun.COM if (TCP_OFF(tcp) > (sizeof (tcphdr_t) >> 2)) { 17186647San207044 (void) fr_tcpoptions(fin, tcp, fdata); 17192393Syz155240 } 17202393Syz155240 if ((fin->fin_out != 0) && (is->is_pass & FR_NEWISN)) 17212393Syz155240 fr_checknewisn(fin, is); 17222393Syz155240 } else if (flags == TH_SYN) { 17232393Syz155240 is->is_s0[source] = ntohl(tcp->th_seq) + 1; 17242393Syz155240 if ((TCP_OFF(tcp) > (sizeof(tcphdr_t) >> 2))) 17256647San207044 (void) fr_tcpoptions(fin, tcp, tdata); 17262393Syz155240 17272393Syz155240 if ((fin->fin_out != 0) && (is->is_pass & FR_NEWISN)) 17282393Syz155240 fr_checknewisn(fin, is); 17292393Syz155240 17302393Syz155240 } 17312393Syz155240 ret = 1; 17322393Syz155240 } else 17332393Syz155240 fin->fin_flx |= FI_OOW; 17342393Syz155240 MUTEX_EXIT(&is->is_lock); 17352393Syz155240 return ret; 17362393Syz155240 } 17372393Syz155240 17382393Syz155240 17392393Syz155240 /* ------------------------------------------------------------------------ */ 17402393Syz155240 /* Function: fr_checknewisn */ 17412393Syz155240 /* Returns: Nil */ 17422393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 17432393Syz155240 /* is(I) - pointer to master state structure */ 17442393Syz155240 /* */ 17452393Syz155240 /* Check to see if this TCP connection is expecting and needs a new */ 17462393Syz155240 /* sequence number for a particular direction of the connection. */ 17472393Syz155240 /* */ 17482393Syz155240 /* NOTE: This does not actually change the sequence numbers, only gets new */ 17492393Syz155240 /* one ready. */ 17502393Syz155240 /* ------------------------------------------------------------------------ */ 17512393Syz155240 static void fr_checknewisn(fin, is) 17522393Syz155240 fr_info_t *fin; 17532393Syz155240 ipstate_t *is; 17542393Syz155240 { 17552393Syz155240 u_32_t sumd, old, new; 17562393Syz155240 tcphdr_t *tcp; 17572393Syz155240 int i; 17582393Syz155240 17592393Syz155240 i = fin->fin_rev; 17602393Syz155240 tcp = fin->fin_dp; 17612393Syz155240 17622393Syz155240 if (((i == 0) && !(is->is_flags & IS_ISNSYN)) || 17632393Syz155240 ((i == 1) && !(is->is_flags & IS_ISNACK))) { 17642393Syz155240 old = ntohl(tcp->th_seq); 17652393Syz155240 new = fr_newisn(fin); 17662393Syz155240 is->is_isninc[i] = new - old; 17672393Syz155240 CALC_SUMD(old, new, sumd); 17682393Syz155240 is->is_sumd[i] = (sumd & 0xffff) + (sumd >> 16); 17692393Syz155240 17702393Syz155240 is->is_flags |= ((i == 0) ? IS_ISNSYN : IS_ISNACK); 17712393Syz155240 } 17722393Syz155240 } 17732393Syz155240 17742393Syz155240 17752393Syz155240 /* ------------------------------------------------------------------------ */ 17762393Syz155240 /* Function: fr_tcpinwindow */ 17772393Syz155240 /* Returns: int - 1 == packet inside TCP "window", 0 == not inside. */ 17782393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 17792393Syz155240 /* fdata(I) - pointer to tcp state informatio (forward) */ 17802393Syz155240 /* tdata(I) - pointer to tcp state informatio (reverse) */ 17812393Syz155240 /* tcp(I) - pointer to TCP packet header */ 17822393Syz155240 /* */ 17832393Syz155240 /* Given a packet has matched addresses and ports, check to see if it is */ 17842393Syz155240 /* within the TCP data window. In a show of generosity, allow packets that */ 17852393Syz155240 /* are within the window space behind the current sequence # as well. */ 17862393Syz155240 /* ------------------------------------------------------------------------ */ 17872393Syz155240 int fr_tcpinwindow(fin, fdata, tdata, tcp, flags) 17882393Syz155240 fr_info_t *fin; 17892393Syz155240 tcpdata_t *fdata, *tdata; 17902393Syz155240 tcphdr_t *tcp; 17912393Syz155240 int flags; 17922393Syz155240 { 17932393Syz155240 tcp_seq seq, ack, end; 17942393Syz155240 int ackskew, tcpflags; 17952393Syz155240 u_32_t win, maxwin; 17966647San207044 int dsize, inseq; 17972393Syz155240 17982393Syz155240 /* 17992393Syz155240 * Find difference between last checked packet and this packet. 18002393Syz155240 */ 18012393Syz155240 tcpflags = tcp->th_flags; 18022393Syz155240 seq = ntohl(tcp->th_seq); 18032393Syz155240 ack = ntohl(tcp->th_ack); 18046647San207044 18052393Syz155240 if (tcpflags & TH_SYN) 18062393Syz155240 win = ntohs(tcp->th_win); 18072393Syz155240 else 18082393Syz155240 win = ntohs(tcp->th_win) << fdata->td_winscale; 18097704SAlexandr.Nedvedicky@Sun.COM 18106647San207044 /* 18116647San207044 * win 0 means the receiving endpoint has closed the window, because it 18126647San207044 * has not enough memory to receive data from sender. In such case we 18136647San207044 * are pretending window size to be 1 to let TCP probe data through. 18146647San207044 * TCP probe data can be either 0 or 1 octet of data, the RFC does not 18156647San207044 * state this accurately, so we have to allow 1 octet (win = 1) even if 18166647San207044 * the window is closed (win == 0). 18176647San207044 */ 18182393Syz155240 if (win == 0) 18192393Syz155240 win = 1; 18202393Syz155240 18216647San207044 dsize = fin->fin_dlen - (TCP_OFF(tcp) << 2) + 18227704SAlexandr.Nedvedicky@Sun.COM ((tcpflags & TH_SYN) ? 1 : 0) + ((tcpflags & TH_FIN) ? 1 : 0); 18236647San207044 18242393Syz155240 /* 18252393Syz155240 * if window scaling is present, the scaling is only allowed 18262393Syz155240 * for windows not in the first SYN packet. In that packet the 18272393Syz155240 * window is 65535 to specify the largest window possible 18282393Syz155240 * for receivers not implementing the window scale option. 18292393Syz155240 * Currently, we do not assume TTCP here. That means that 18302393Syz155240 * if we see a second packet from a host (after the initial 18312393Syz155240 * SYN), we can assume that the receiver of the SYN did 18322393Syz155240 * already send back the SYN/ACK (and thus that we know if 18332393Syz155240 * the receiver also does window scaling) 18342393Syz155240 */ 18352393Syz155240 if (!(tcpflags & TH_SYN) && (fdata->td_winflags & TCP_WSCALE_FIRST)) { 18366647San207044 fdata->td_maxwin = win; 18372393Syz155240 } 18382393Syz155240 18396647San207044 end = seq + dsize; 18402393Syz155240 18412393Syz155240 if ((fdata->td_end == 0) && 18422393Syz155240 (!(flags & IS_TCPFSM) || 18432393Syz155240 ((tcpflags & TH_OPENING) == TH_OPENING))) { 18442393Syz155240 /* 18452393Syz155240 * Must be a (outgoing) SYN-ACK in reply to a SYN. 18462393Syz155240 */ 18476647San207044 fdata->td_end = end - 1; 18482393Syz155240 fdata->td_maxwin = 1; 18492393Syz155240 fdata->td_maxend = end + win; 18502393Syz155240 } 18512393Syz155240 18522393Syz155240 if (!(tcpflags & TH_ACK)) { /* Pretend an ack was sent */ 18532393Syz155240 ack = tdata->td_end; 18542393Syz155240 } else if (((tcpflags & (TH_ACK|TH_RST)) == (TH_ACK|TH_RST)) && 18552393Syz155240 (ack == 0)) { 18562393Syz155240 /* gross hack to get around certain broken tcp stacks */ 18572393Syz155240 ack = tdata->td_end; 18582393Syz155240 } 18592393Syz155240 18602393Syz155240 maxwin = tdata->td_maxwin; 18612393Syz155240 ackskew = tdata->td_end - ack; 18622393Syz155240 18632393Syz155240 /* 18642393Syz155240 * Strict sequencing only allows in-order delivery. 18652393Syz155240 */ 18662393Syz155240 if ((flags & IS_STRICT) != 0) { 18672393Syz155240 if (seq != fdata->td_end) { 18687704SAlexandr.Nedvedicky@Sun.COM DTRACE_PROBE(strict_check); 18692393Syz155240 return 0; 18702393Syz155240 } 18712393Syz155240 } 18722393Syz155240 18732393Syz155240 #define SEQ_GE(a,b) ((int)((a) - (b)) >= 0) 18742393Syz155240 #define SEQ_GT(a,b) ((int)((a) - (b)) > 0) 18756647San207044 inseq = 0; 18767704SAlexandr.Nedvedicky@Sun.COM DTRACE_PROBE4( 18777704SAlexandr.Nedvedicky@Sun.COM dyn_params, 18787704SAlexandr.Nedvedicky@Sun.COM int, dsize, 18797704SAlexandr.Nedvedicky@Sun.COM int, ackskew, 18807704SAlexandr.Nedvedicky@Sun.COM int, maxwin, 18817704SAlexandr.Nedvedicky@Sun.COM int, win 18827704SAlexandr.Nedvedicky@Sun.COM ); 18832393Syz155240 if ( 18842393Syz155240 #if defined(_KERNEL) 18857704SAlexandr.Nedvedicky@Sun.COM /* 18867704SAlexandr.Nedvedicky@Sun.COM * end <-> s + n 18877704SAlexandr.Nedvedicky@Sun.COM * maxend <-> ack + win 18887704SAlexandr.Nedvedicky@Sun.COM * this is upperbound check 18897704SAlexandr.Nedvedicky@Sun.COM */ 18902393Syz155240 (SEQ_GE(fdata->td_maxend, end)) && 18917704SAlexandr.Nedvedicky@Sun.COM /* 18927704SAlexandr.Nedvedicky@Sun.COM * this is lowerbound check 18937704SAlexandr.Nedvedicky@Sun.COM */ 18942393Syz155240 (SEQ_GE(seq, fdata->td_end - maxwin)) && 18952393Syz155240 #endif 18962393Syz155240 /* XXX what about big packets */ 18972393Syz155240 #define MAXACKWINDOW 66000 18982393Syz155240 (-ackskew <= (MAXACKWINDOW << fdata->td_winscale)) && 18992393Syz155240 ( ackskew <= (MAXACKWINDOW << fdata->td_winscale))) { 19006647San207044 inseq = 1; 19016647San207044 /* 19026647San207044 * Microsoft Windows will send the next packet to the right of the 19036647San207044 * window if SACK is in use. 19046647San207044 */ 19056647San207044 } else if ((seq == fdata->td_maxend) && (ackskew == 0) && 19066647San207044 (fdata->td_winflags & TCP_SACK_PERMIT) && 19076647San207044 (tdata->td_winflags & TCP_SACK_PERMIT)) { 19086647San207044 inseq = 1; 19096647San207044 /* 19106647San207044 * RST ACK with SEQ equal to 0 is sent by some OSes (i.e. Solaris) as a 19116647San207044 * response to initial SYN packet, when there is no application 19126647San207044 * listeing to on a port, where the SYN packet has came to. 19136647San207044 */ 19146647San207044 } else if ((seq == 0) && (tcpflags == (TH_RST|TH_ACK)) && 19157704SAlexandr.Nedvedicky@Sun.COM (ackskew >= -1) && (ackskew <= 1)) { 19166647San207044 inseq = 1; 19176647San207044 } else if (!(flags & IS_TCPFSM)) { 19186647San207044 19196647San207044 if (!(fdata->td_winflags & 19206647San207044 (TCP_WSCALE_SEEN|TCP_WSCALE_FIRST))) { 19216647San207044 /* 19226647San207044 * No TCPFSM and no window scaling, so make some 19236647San207044 * extra guesses. 19246647San207044 */ 19256647San207044 if ((seq == fdata->td_maxend) && (ackskew == 0)) 19266647San207044 inseq = 1; 19276647San207044 else if (SEQ_GE(seq + maxwin, fdata->td_end - maxwin)) 19286647San207044 inseq = 1; 19296647San207044 } 19306647San207044 } 19316647San207044 19326647San207044 if (inseq) { 19332393Syz155240 /* if ackskew < 0 then this should be due to fragmented 19342393Syz155240 * packets. There is no way to know the length of the 19352393Syz155240 * total packet in advance. 19362393Syz155240 * We do know the total length from the fragment cache though. 19372393Syz155240 * Note however that there might be more sessions with 19382393Syz155240 * exactly the same source and destination parameters in the 19392393Syz155240 * state cache (and source and destination is the only stuff 19402393Syz155240 * that is saved in the fragment cache). Note further that 19412393Syz155240 * some TCP connections in the state cache are hashed with 19422393Syz155240 * sport and dport as well which makes it not worthwhile to 19432393Syz155240 * look for them. 19442393Syz155240 * Thus, when ackskew is negative but still seems to belong 19452393Syz155240 * to this session, we bump up the destinations end value. 19462393Syz155240 */ 19477704SAlexandr.Nedvedicky@Sun.COM if (ackskew < 0) { 19487704SAlexandr.Nedvedicky@Sun.COM DTRACE_PROBE2(end_update_td, 19497704SAlexandr.Nedvedicky@Sun.COM int, tdata->td_end, 19507704SAlexandr.Nedvedicky@Sun.COM int, ack 19517704SAlexandr.Nedvedicky@Sun.COM ); 19522393Syz155240 tdata->td_end = ack; 19537704SAlexandr.Nedvedicky@Sun.COM } 19542393Syz155240 19552393Syz155240 /* update max window seen */ 19567704SAlexandr.Nedvedicky@Sun.COM if (fdata->td_maxwin < win) { 19577704SAlexandr.Nedvedicky@Sun.COM DTRACE_PROBE2(win_update_fd, 19587704SAlexandr.Nedvedicky@Sun.COM int, fdata->td_maxwin, 19597704SAlexandr.Nedvedicky@Sun.COM int, win 19607704SAlexandr.Nedvedicky@Sun.COM ); 19612393Syz155240 fdata->td_maxwin = win; 19627704SAlexandr.Nedvedicky@Sun.COM } 19637704SAlexandr.Nedvedicky@Sun.COM 19647704SAlexandr.Nedvedicky@Sun.COM if (SEQ_GT(end, fdata->td_end)) { 19657704SAlexandr.Nedvedicky@Sun.COM DTRACE_PROBE2(end_update_fd, 19667704SAlexandr.Nedvedicky@Sun.COM int, fdata->td_end, 19677704SAlexandr.Nedvedicky@Sun.COM int, end 19687704SAlexandr.Nedvedicky@Sun.COM ); 19692393Syz155240 fdata->td_end = end; 19707704SAlexandr.Nedvedicky@Sun.COM } 19717704SAlexandr.Nedvedicky@Sun.COM 19727704SAlexandr.Nedvedicky@Sun.COM if (SEQ_GE(ack + win, tdata->td_maxend)) { 19737704SAlexandr.Nedvedicky@Sun.COM DTRACE_PROBE2(max_end_update_td, 19747704SAlexandr.Nedvedicky@Sun.COM int, tdata->td_maxend, 19757704SAlexandr.Nedvedicky@Sun.COM int, ack + win 19767704SAlexandr.Nedvedicky@Sun.COM ); 19772393Syz155240 tdata->td_maxend = ack + win; 19787704SAlexandr.Nedvedicky@Sun.COM } 19797704SAlexandr.Nedvedicky@Sun.COM 19802393Syz155240 return 1; 19812393Syz155240 } 19826647San207044 fin->fin_flx |= FI_OOW; 19837704SAlexandr.Nedvedicky@Sun.COM 19847704SAlexandr.Nedvedicky@Sun.COM #if defined(_KERNEL) 19857704SAlexandr.Nedvedicky@Sun.COM if (!(SEQ_GE(seq, fdata->td_end - maxwin))) 19867704SAlexandr.Nedvedicky@Sun.COM fin->fin_flx |= FI_NEG_OOW; 19877704SAlexandr.Nedvedicky@Sun.COM #endif 19887704SAlexandr.Nedvedicky@Sun.COM 19892393Syz155240 return 0; 19902393Syz155240 } 19912393Syz155240 19922393Syz155240 19932393Syz155240 /* ------------------------------------------------------------------------ */ 19942393Syz155240 /* Function: fr_stclone */ 19952393Syz155240 /* Returns: ipstate_t* - NULL == cloning failed, */ 19962393Syz155240 /* else pointer to new state structure */ 19972393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 19982393Syz155240 /* tcp(I) - pointer to TCP/UDP header */ 19992393Syz155240 /* is(I) - pointer to master state structure */ 20002393Syz155240 /* */ 20012393Syz155240 /* Create a "duplcate" state table entry from the master. */ 20022393Syz155240 /* ------------------------------------------------------------------------ */ 20032393Syz155240 static ipstate_t *fr_stclone(fin, tcp, is) 20042393Syz155240 fr_info_t *fin; 20052393Syz155240 tcphdr_t *tcp; 20062393Syz155240 ipstate_t *is; 20072393Syz155240 { 20082393Syz155240 ipstate_t *clone; 20092393Syz155240 u_32_t send; 20103448Sdh155122 ipf_stack_t *ifs = fin->fin_ifs; 20113448Sdh155122 20128170SJohn.Ojemann@Sun.COM /* 20138170SJohn.Ojemann@Sun.COM * Trigger automatic call to fr_state_flush() if the 20148170SJohn.Ojemann@Sun.COM * table has reached capacity specified by hi watermark. 20158170SJohn.Ojemann@Sun.COM */ 20168170SJohn.Ojemann@Sun.COM if (ST_TAB_WATER_LEVEL(ifs) > ifs->ifs_state_flush_level_hi) 20178170SJohn.Ojemann@Sun.COM ifs->ifs_fr_state_doflush = 1; 20188170SJohn.Ojemann@Sun.COM 20198170SJohn.Ojemann@Sun.COM /* 20208170SJohn.Ojemann@Sun.COM * If automatic flushing did not do its job, and the table 20218170SJohn.Ojemann@Sun.COM * has filled up, don't try to create a new entry. A NULL 20228170SJohn.Ojemann@Sun.COM * return will indicate that the cloning has failed. 20238170SJohn.Ojemann@Sun.COM */ 20248170SJohn.Ojemann@Sun.COM if (ifs->ifs_ips_num >= ifs->ifs_fr_statemax) { 20253448Sdh155122 ATOMIC_INCL(ifs->ifs_ips_stats.iss_max); 20262393Syz155240 return NULL; 20272393Syz155240 } 20288170SJohn.Ojemann@Sun.COM 20292393Syz155240 KMALLOC(clone, ipstate_t *); 20302393Syz155240 if (clone == NULL) 20312393Syz155240 return NULL; 20322393Syz155240 bcopy((char *)is, (char *)clone, sizeof(*clone)); 20332393Syz155240 20342393Syz155240 MUTEX_NUKE(&clone->is_lock); 20352393Syz155240 20363448Sdh155122 clone->is_die = ONE_DAY + ifs->ifs_fr_ticks; 20372393Syz155240 clone->is_state[0] = 0; 20382393Syz155240 clone->is_state[1] = 0; 20392393Syz155240 send = ntohl(tcp->th_seq) + fin->fin_dlen - (TCP_OFF(tcp) << 2) + 20402393Syz155240 ((tcp->th_flags & TH_SYN) ? 1 : 0) + 20412393Syz155240 ((tcp->th_flags & TH_FIN) ? 1 : 0); 20422393Syz155240 20432393Syz155240 if (fin->fin_rev == 1) { 20442393Syz155240 clone->is_dend = send; 20452393Syz155240 clone->is_maxdend = send; 20462393Syz155240 clone->is_send = 0; 20472393Syz155240 clone->is_maxswin = 1; 20482393Syz155240 clone->is_maxdwin = ntohs(tcp->th_win); 20492393Syz155240 if (clone->is_maxdwin == 0) 20502393Syz155240 clone->is_maxdwin = 1; 20512393Syz155240 } else { 20522393Syz155240 clone->is_send = send; 20532393Syz155240 clone->is_maxsend = send; 20542393Syz155240 clone->is_dend = 0; 20552393Syz155240 clone->is_maxdwin = 1; 20562393Syz155240 clone->is_maxswin = ntohs(tcp->th_win); 20572393Syz155240 if (clone->is_maxswin == 0) 20582393Syz155240 clone->is_maxswin = 1; 20592393Syz155240 } 20602393Syz155240 20612393Syz155240 clone->is_flags &= ~SI_CLONE; 20622393Syz155240 clone->is_flags |= SI_CLONED; 20633448Sdh155122 fr_stinsert(clone, fin->fin_rev, ifs); 20648624SDarren.Reed@Sun.COM clone->is_ref = 1; 20652393Syz155240 if (clone->is_p == IPPROTO_TCP) { 20663448Sdh155122 (void) fr_tcp_age(&clone->is_sti, fin, ifs->ifs_ips_tqtqb, 20672393Syz155240 clone->is_flags); 20682393Syz155240 } 20692393Syz155240 MUTEX_EXIT(&clone->is_lock); 20702393Syz155240 #ifdef IPFILTER_SCAN 20712393Syz155240 (void) ipsc_attachis(is); 20722393Syz155240 #endif 20732393Syz155240 #ifdef IPFILTER_SYNC 20742393Syz155240 if (is->is_flags & IS_STATESYNC) 20752393Syz155240 clone->is_sync = ipfsync_new(SMC_STATE, fin, clone); 20762393Syz155240 #endif 20772393Syz155240 return clone; 20782393Syz155240 } 20792393Syz155240 20802393Syz155240 20812393Syz155240 /* ------------------------------------------------------------------------ */ 20822393Syz155240 /* Function: fr_matchsrcdst */ 20832393Syz155240 /* Returns: Nil */ 20842393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 20852393Syz155240 /* is(I) - pointer to state structure */ 20862393Syz155240 /* src(I) - pointer to source address */ 20872393Syz155240 /* dst(I) - pointer to destination address */ 20882393Syz155240 /* tcp(I) - pointer to TCP/UDP header */ 20892393Syz155240 /* */ 20902393Syz155240 /* Match a state table entry against an IP packet. The logic below is that */ 20912393Syz155240 /* ret gets set to one if the match succeeds, else remains 0. If it is */ 20922393Syz155240 /* still 0 after the test. no match. */ 20932393Syz155240 /* ------------------------------------------------------------------------ */ 20942393Syz155240 static ipstate_t *fr_matchsrcdst(fin, is, src, dst, tcp, cmask) 20952393Syz155240 fr_info_t *fin; 20962393Syz155240 ipstate_t *is; 20972393Syz155240 i6addr_t *src, *dst; 20982393Syz155240 tcphdr_t *tcp; 20992393Syz155240 u_32_t cmask; 21002393Syz155240 { 21012393Syz155240 int ret = 0, rev, out, flags, flx = 0, idx; 21022393Syz155240 u_short sp, dp; 21032393Syz155240 u_32_t cflx; 21042393Syz155240 void *ifp; 21053448Sdh155122 ipf_stack_t *ifs = fin->fin_ifs; 21062393Syz155240 21072393Syz155240 rev = IP6_NEQ(&is->is_dst, dst); 21082393Syz155240 ifp = fin->fin_ifp; 21092393Syz155240 out = fin->fin_out; 21102393Syz155240 flags = is->is_flags; 21112393Syz155240 sp = 0; 21122393Syz155240 dp = 0; 21132393Syz155240 21142393Syz155240 if (tcp != NULL) { 21152393Syz155240 sp = htons(fin->fin_sport); 21162393Syz155240 dp = ntohs(fin->fin_dport); 21172393Syz155240 } 21182393Syz155240 if (!rev) { 21192393Syz155240 if (tcp != NULL) { 21202393Syz155240 if (!(flags & SI_W_SPORT) && (sp != is->is_sport)) 21212393Syz155240 rev = 1; 21222393Syz155240 else if (!(flags & SI_W_DPORT) && (dp != is->is_dport)) 21232393Syz155240 rev = 1; 21242393Syz155240 } 21252393Syz155240 } 21262393Syz155240 21272393Syz155240 idx = (out << 1) + rev; 21282393Syz155240 21292393Syz155240 /* 21302393Syz155240 * If the interface for this 'direction' is set, make sure it matches. 21312393Syz155240 * An interface name that is not set matches any, as does a name of *. 21322393Syz155240 */ 21332393Syz155240 if ((is->is_ifp[idx] == NULL && 21342393Syz155240 (*is->is_ifname[idx] == '\0' || *is->is_ifname[idx] == '*')) || 21352393Syz155240 is->is_ifp[idx] == ifp) 21362393Syz155240 ret = 1; 21372393Syz155240 21382393Syz155240 if (ret == 0) 21392393Syz155240 return NULL; 21402393Syz155240 ret = 0; 21412393Syz155240 21422393Syz155240 /* 21432393Syz155240 * Match addresses and ports. 21442393Syz155240 */ 21452393Syz155240 if (rev == 0) { 21462393Syz155240 if ((IP6_EQ(&is->is_dst, dst) || (flags & SI_W_DADDR)) && 21472393Syz155240 (IP6_EQ(&is->is_src, src) || (flags & SI_W_SADDR))) { 21482393Syz155240 if (tcp) { 21492393Syz155240 if ((sp == is->is_sport || flags & SI_W_SPORT)&& 21502393Syz155240 (dp == is->is_dport || flags & SI_W_DPORT)) 21512393Syz155240 ret = 1; 21522393Syz155240 } else { 21532393Syz155240 ret = 1; 21542393Syz155240 } 21552393Syz155240 } 21562393Syz155240 } else { 21572393Syz155240 if ((IP6_EQ(&is->is_dst, src) || (flags & SI_W_DADDR)) && 21582393Syz155240 (IP6_EQ(&is->is_src, dst) || (flags & SI_W_SADDR))) { 21592393Syz155240 if (tcp) { 21602393Syz155240 if ((dp == is->is_sport || flags & SI_W_SPORT)&& 21612393Syz155240 (sp == is->is_dport || flags & SI_W_DPORT)) 21622393Syz155240 ret = 1; 21632393Syz155240 } else { 21642393Syz155240 ret = 1; 21652393Syz155240 } 21662393Syz155240 } 21672393Syz155240 } 21682393Syz155240 21692393Syz155240 if (ret == 0) 21702393Syz155240 return NULL; 21712393Syz155240 21722393Syz155240 /* 21732393Syz155240 * Whether or not this should be here, is questionable, but the aim 21742393Syz155240 * is to get this out of the main line. 21752393Syz155240 */ 21762393Syz155240 if (tcp == NULL) 21772393Syz155240 flags = is->is_flags & ~(SI_WILDP|SI_NEWFR|SI_CLONE|SI_CLONED); 21782393Syz155240 21792393Syz155240 /* 21802393Syz155240 * Only one of the source or destination address can be flaged as a 21812393Syz155240 * wildcard. Fill in the missing address, if set. 21822393Syz155240 * For IPv6, if the address being copied in is multicast, then 21832393Syz155240 * don't reset the wild flag - multicast causes it to be set in the 21842393Syz155240 * first place! 21852393Syz155240 */ 21862393Syz155240 if ((flags & (SI_W_SADDR|SI_W_DADDR))) { 21872393Syz155240 fr_ip_t *fi = &fin->fin_fi; 21882393Syz155240 21892393Syz155240 if ((flags & SI_W_SADDR) != 0) { 21902393Syz155240 if (rev == 0) { 21912393Syz155240 #ifdef USE_INET6 21922393Syz155240 if (is->is_v == 6 && 21932393Syz155240 IN6_IS_ADDR_MULTICAST(&fi->fi_src.in6)) 21942393Syz155240 /*EMPTY*/; 21952393Syz155240 else 21962393Syz155240 #endif 21972393Syz155240 { 21982393Syz155240 is->is_src = fi->fi_src; 21992393Syz155240 is->is_flags &= ~SI_W_SADDR; 22002393Syz155240 } 22012393Syz155240 } else { 22022393Syz155240 #ifdef USE_INET6 22032393Syz155240 if (is->is_v == 6 && 22042393Syz155240 IN6_IS_ADDR_MULTICAST(&fi->fi_dst.in6)) 22052393Syz155240 /*EMPTY*/; 22062393Syz155240 else 22072393Syz155240 #endif 22082393Syz155240 { 22092393Syz155240 is->is_src = fi->fi_dst; 22102393Syz155240 is->is_flags &= ~SI_W_SADDR; 22112393Syz155240 } 22122393Syz155240 } 22132393Syz155240 } else if ((flags & SI_W_DADDR) != 0) { 22142393Syz155240 if (rev == 0) { 22152393Syz155240 #ifdef USE_INET6 22162393Syz155240 if (is->is_v == 6 && 22172393Syz155240 IN6_IS_ADDR_MULTICAST(&fi->fi_dst.in6)) 22182393Syz155240 /*EMPTY*/; 22192393Syz155240 else 22202393Syz155240 #endif 22212393Syz155240 { 22222393Syz155240 is->is_dst = fi->fi_dst; 22232393Syz155240 is->is_flags &= ~SI_W_DADDR; 22242393Syz155240 } 22252393Syz155240 } else { 22262393Syz155240 #ifdef USE_INET6 22272393Syz155240 if (is->is_v == 6 && 22282393Syz155240 IN6_IS_ADDR_MULTICAST(&fi->fi_src.in6)) 22292393Syz155240 /*EMPTY*/; 22302393Syz155240 else 22312393Syz155240 #endif 22322393Syz155240 { 22332393Syz155240 is->is_dst = fi->fi_src; 22342393Syz155240 is->is_flags &= ~SI_W_DADDR; 22352393Syz155240 } 22362393Syz155240 } 22372393Syz155240 } 22382393Syz155240 if ((is->is_flags & (SI_WILDA|SI_WILDP)) == 0) { 22393448Sdh155122 ATOMIC_DECL(ifs->ifs_ips_stats.iss_wild); 22402393Syz155240 } 22412393Syz155240 } 22422393Syz155240 22432393Syz155240 flx = fin->fin_flx & cmask; 22442393Syz155240 cflx = is->is_flx[out][rev]; 22452393Syz155240 22462393Syz155240 /* 22472393Syz155240 * Match up any flags set from IP options. 22482393Syz155240 */ 22492393Syz155240 if ((cflx && (flx != (cflx & cmask))) || 22502393Syz155240 ((fin->fin_optmsk & is->is_optmsk[rev]) != is->is_opt[rev]) || 22512393Syz155240 ((fin->fin_secmsk & is->is_secmsk) != is->is_sec) || 22522393Syz155240 ((fin->fin_auth & is->is_authmsk) != is->is_auth)) 22532393Syz155240 return NULL; 22542393Syz155240 22552393Syz155240 /* 22562393Syz155240 * Only one of the source or destination port can be flagged as a 22572393Syz155240 * wildcard. When filling it in, fill in a copy of the matched entry 22582393Syz155240 * if it has the cloning flag set. 22592393Syz155240 */ 22602393Syz155240 if ((fin->fin_flx & FI_IGNORE) != 0) { 22612393Syz155240 fin->fin_rev = rev; 22622393Syz155240 return is; 22632393Syz155240 } 22642393Syz155240 22652393Syz155240 if ((flags & (SI_W_SPORT|SI_W_DPORT))) { 22662393Syz155240 if ((flags & SI_CLONE) != 0) { 22672393Syz155240 ipstate_t *clone; 22682393Syz155240 22692393Syz155240 clone = fr_stclone(fin, tcp, is); 22702393Syz155240 if (clone == NULL) 22712393Syz155240 return NULL; 22722393Syz155240 is = clone; 22732393Syz155240 } else { 22743448Sdh155122 ATOMIC_DECL(ifs->ifs_ips_stats.iss_wild); 22752393Syz155240 } 22762393Syz155240 22772393Syz155240 if ((flags & SI_W_SPORT) != 0) { 22782393Syz155240 if (rev == 0) { 22792393Syz155240 is->is_sport = sp; 22802393Syz155240 is->is_send = ntohl(tcp->th_seq); 22812393Syz155240 } else { 22822393Syz155240 is->is_sport = dp; 22832393Syz155240 is->is_send = ntohl(tcp->th_ack); 22842393Syz155240 } 22852393Syz155240 is->is_maxsend = is->is_send + 1; 22862393Syz155240 } else if ((flags & SI_W_DPORT) != 0) { 22872393Syz155240 if (rev == 0) { 22882393Syz155240 is->is_dport = dp; 22892393Syz155240 is->is_dend = ntohl(tcp->th_ack); 22902393Syz155240 } else { 22912393Syz155240 is->is_dport = sp; 22922393Syz155240 is->is_dend = ntohl(tcp->th_seq); 22932393Syz155240 } 22942393Syz155240 is->is_maxdend = is->is_dend + 1; 22952393Syz155240 } 22962393Syz155240 is->is_flags &= ~(SI_W_SPORT|SI_W_DPORT); 22973448Sdh155122 if ((flags & SI_CLONED) && ifs->ifs_ipstate_logging) 22983448Sdh155122 ipstate_log(is, ISL_CLONE, ifs); 22992393Syz155240 } 23002393Syz155240 23012393Syz155240 ret = -1; 23022393Syz155240 23032393Syz155240 if (is->is_flx[out][rev] == 0) { 23042393Syz155240 is->is_flx[out][rev] = flx; 23052393Syz155240 is->is_opt[rev] = fin->fin_optmsk; 23062393Syz155240 if (is->is_v == 6) { 23072393Syz155240 is->is_opt[rev] &= ~0x8; 23082393Syz155240 is->is_optmsk[rev] &= ~0x8; 23092393Syz155240 } 23102393Syz155240 } 23112393Syz155240 23122393Syz155240 /* 23132393Syz155240 * Check if the interface name for this "direction" is set and if not, 23142393Syz155240 * fill it in. 23152393Syz155240 */ 23162393Syz155240 if (is->is_ifp[idx] == NULL && 23172393Syz155240 (*is->is_ifname[idx] == '\0' || *is->is_ifname[idx] == '*')) { 23182393Syz155240 is->is_ifp[idx] = ifp; 23192958Sdr146992 COPYIFNAME(ifp, is->is_ifname[idx], fin->fin_v); 23202393Syz155240 } 23212393Syz155240 fin->fin_rev = rev; 23222393Syz155240 return is; 23232393Syz155240 } 23242393Syz155240 23252393Syz155240 23262393Syz155240 /* ------------------------------------------------------------------------ */ 23272393Syz155240 /* Function: fr_checkicmpmatchingstate */ 23282393Syz155240 /* Returns: Nil */ 23292393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 23302393Syz155240 /* */ 23312393Syz155240 /* If we've got an ICMP error message, using the information stored in the */ 23322393Syz155240 /* ICMP packet, look for a matching state table entry. */ 23332393Syz155240 /* */ 23342393Syz155240 /* If we return NULL then no lock on ipf_state is held. */ 23352393Syz155240 /* If we return non-null then a read-lock on ipf_state is held. */ 23362393Syz155240 /* ------------------------------------------------------------------------ */ 23372393Syz155240 static ipstate_t *fr_checkicmpmatchingstate(fin) 23382393Syz155240 fr_info_t *fin; 23392393Syz155240 { 23402393Syz155240 ipstate_t *is, **isp; 23412393Syz155240 u_short sport, dport; 23422393Syz155240 u_char pr; 23432393Syz155240 int backward, i, oi; 23442393Syz155240 i6addr_t dst, src; 23452393Syz155240 struct icmp *ic; 23462393Syz155240 u_short savelen; 23472393Syz155240 icmphdr_t *icmp; 23482393Syz155240 fr_info_t ofin; 23492393Syz155240 tcphdr_t *tcp; 23502393Syz155240 int len; 23512393Syz155240 ip_t *oip; 23522393Syz155240 u_int hv; 23533448Sdh155122 ipf_stack_t *ifs = fin->fin_ifs; 23542393Syz155240 23552393Syz155240 /* 23562393Syz155240 * Does it at least have the return (basic) IP header ? 23572393Syz155240 * Is it an actual recognised ICMP error type? 23582393Syz155240 * Only a basic IP header (no options) should be with 23592393Syz155240 * an ICMP error header. 23602393Syz155240 */ 23612393Syz155240 if ((fin->fin_v != 4) || (fin->fin_hlen != sizeof(ip_t)) || 23622393Syz155240 (fin->fin_plen < ICMPERR_MINPKTLEN) || 23632393Syz155240 !(fin->fin_flx & FI_ICMPERR)) 23642393Syz155240 return NULL; 23652393Syz155240 ic = fin->fin_dp; 23662393Syz155240 23672393Syz155240 oip = (ip_t *)((char *)ic + ICMPERR_ICMPHLEN); 23682393Syz155240 /* 23692393Syz155240 * Check if the at least the old IP header (with options) and 23702393Syz155240 * 8 bytes of payload is present. 23712393Syz155240 */ 23722393Syz155240 if (fin->fin_plen < ICMPERR_MAXPKTLEN + ((IP_HL(oip) - 5) << 2)) 23732393Syz155240 return NULL; 23742393Syz155240 23752393Syz155240 /* 23762393Syz155240 * Sanity Checks. 23772393Syz155240 */ 23782393Syz155240 len = fin->fin_dlen - ICMPERR_ICMPHLEN; 23792393Syz155240 if ((len <= 0) || ((IP_HL(oip) << 2) > len)) 23802393Syz155240 return NULL; 23812393Syz155240 23822393Syz155240 /* 23832393Syz155240 * Is the buffer big enough for all of it ? It's the size of the IP 23842393Syz155240 * header claimed in the encapsulated part which is of concern. It 23852393Syz155240 * may be too big to be in this buffer but not so big that it's 23862393Syz155240 * outside the ICMP packet, leading to TCP deref's causing problems. 23872393Syz155240 * This is possible because we don't know how big oip_hl is when we 23882393Syz155240 * do the pullup early in fr_check() and thus can't guarantee it is 23892393Syz155240 * all here now. 23902393Syz155240 */ 23912393Syz155240 #ifdef _KERNEL 23922393Syz155240 { 23932393Syz155240 mb_t *m; 23942393Syz155240 23952393Syz155240 m = fin->fin_m; 23962393Syz155240 # if defined(MENTAT) 23972393Syz155240 if ((char *)oip + len > (char *)m->b_wptr) 23982393Syz155240 return NULL; 23992393Syz155240 # else 24002393Syz155240 if ((char *)oip + len > (char *)fin->fin_ip + m->m_len) 24012393Syz155240 return NULL; 24022393Syz155240 # endif 24032393Syz155240 } 24042393Syz155240 #endif 24053607Szf203873 bcopy((char *)fin, (char *)&ofin, sizeof(*fin)); 24062393Syz155240 24072393Syz155240 /* 24082393Syz155240 * in the IPv4 case we must zero the i6addr union otherwise 24092393Syz155240 * the IP6_EQ and IP6_NEQ macros produce the wrong results because 24102393Syz155240 * of the 'junk' in the unused part of the union 24112393Syz155240 */ 24122393Syz155240 bzero((char *)&src, sizeof(src)); 24132393Syz155240 bzero((char *)&dst, sizeof(dst)); 24142393Syz155240 24152393Syz155240 /* 24162393Syz155240 * we make an fin entry to be able to feed it to 24172393Syz155240 * matchsrcdst note that not all fields are encessary 24182393Syz155240 * but this is the cleanest way. Note further we fill 24192393Syz155240 * in fin_mp such that if someone uses it we'll get 24202393Syz155240 * a kernel panic. fr_matchsrcdst does not use this. 24212393Syz155240 * 24222393Syz155240 * watch out here, as ip is in host order and oip in network 24232393Syz155240 * order. Any change we make must be undone afterwards, like 24242393Syz155240 * oip->ip_off - it is still in network byte order so fix it. 24252393Syz155240 */ 24262393Syz155240 savelen = oip->ip_len; 24272393Syz155240 oip->ip_len = len; 24282393Syz155240 oip->ip_off = ntohs(oip->ip_off); 24292393Syz155240 24302393Syz155240 ofin.fin_flx = FI_NOCKSUM; 24312393Syz155240 ofin.fin_v = 4; 24322393Syz155240 ofin.fin_ip = oip; 24332393Syz155240 ofin.fin_m = NULL; /* if dereferenced, panic XXX */ 24342393Syz155240 ofin.fin_mp = NULL; /* if dereferenced, panic XXX */ 24352393Syz155240 ofin.fin_plen = fin->fin_dlen - ICMPERR_ICMPHLEN; 24362393Syz155240 (void) fr_makefrip(IP_HL(oip) << 2, oip, &ofin); 24372393Syz155240 ofin.fin_ifp = fin->fin_ifp; 24382393Syz155240 ofin.fin_out = !fin->fin_out; 24392393Syz155240 /* 24402393Syz155240 * Reset the short and bad flag here because in fr_matchsrcdst() 24412393Syz155240 * the flags for the current packet (fin_flx) are compared against 24422393Syz155240 * those for the existing session. 24432393Syz155240 */ 24442393Syz155240 ofin.fin_flx &= ~(FI_BAD|FI_SHORT); 24452393Syz155240 24462393Syz155240 /* 24472393Syz155240 * Put old values of ip_len and ip_off back as we don't know 24482393Syz155240 * if we have to forward the packet (or process it again. 24492393Syz155240 */ 24502393Syz155240 oip->ip_len = savelen; 24512393Syz155240 oip->ip_off = htons(oip->ip_off); 24522393Syz155240 24532393Syz155240 switch (oip->ip_p) 24542393Syz155240 { 24552393Syz155240 case IPPROTO_ICMP : 24562393Syz155240 /* 24572393Syz155240 * an ICMP error can only be generated as a result of an 24582393Syz155240 * ICMP query, not as the response on an ICMP error 24592393Syz155240 * 24602393Syz155240 * XXX theoretically ICMP_ECHOREP and the other reply's are 24612393Syz155240 * ICMP query's as well, but adding them here seems strange XXX 24622393Syz155240 */ 24632393Syz155240 if ((ofin.fin_flx & FI_ICMPERR) != 0) 24642393Syz155240 return NULL; 24652393Syz155240 24662393Syz155240 /* 24672393Syz155240 * perform a lookup of the ICMP packet in the state table 24682393Syz155240 */ 24692393Syz155240 icmp = (icmphdr_t *)((char *)oip + (IP_HL(oip) << 2)); 24702393Syz155240 hv = (pr = oip->ip_p); 24712393Syz155240 src.in4 = oip->ip_src; 24722393Syz155240 hv += src.in4.s_addr; 24732393Syz155240 dst.in4 = oip->ip_dst; 24742393Syz155240 hv += dst.in4.s_addr; 24752393Syz155240 hv += icmp->icmp_id; 24763448Sdh155122 hv = DOUBLE_HASH(hv, ifs); 24773448Sdh155122 24783448Sdh155122 READ_ENTER(&ifs->ifs_ipf_state); 24793448Sdh155122 for (isp = &ifs->ifs_ips_table[hv]; ((is = *isp) != NULL); ) { 24802393Syz155240 isp = &is->is_hnext; 24812393Syz155240 if ((is->is_p != pr) || (is->is_v != 4)) 24822393Syz155240 continue; 24832393Syz155240 if (is->is_pass & FR_NOICMPERR) 24842393Syz155240 continue; 24852393Syz155240 is = fr_matchsrcdst(&ofin, is, &src, &dst, 24862393Syz155240 NULL, FI_ICMPCMP); 24872393Syz155240 if (is != NULL) { 24882393Syz155240 if ((is->is_pass & FR_NOICMPERR) != 0) { 24893448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_state); 24902393Syz155240 return NULL; 24912393Syz155240 } 24922393Syz155240 /* 24932393Syz155240 * i : the index of this packet (the icmp 24942393Syz155240 * unreachable) 24952393Syz155240 * oi : the index of the original packet found 24962393Syz155240 * in the icmp header (i.e. the packet 24972393Syz155240 * causing this icmp) 24982393Syz155240 * backward : original packet was backward 24992393Syz155240 * compared to the state 25002393Syz155240 */ 25012393Syz155240 backward = IP6_NEQ(&is->is_src, &src); 25022393Syz155240 fin->fin_rev = !backward; 25032393Syz155240 i = (!backward << 1) + fin->fin_out; 25042393Syz155240 oi = (backward << 1) + ofin.fin_out; 25052393Syz155240 if (is->is_icmppkts[i] > is->is_pkts[oi]) 25062393Syz155240 continue; 25073448Sdh155122 ifs->ifs_ips_stats.iss_hits++; 25082393Syz155240 is->is_icmppkts[i]++; 25092393Syz155240 return is; 25102393Syz155240 } 25112393Syz155240 } 25123448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_state); 25132393Syz155240 return NULL; 25142393Syz155240 case IPPROTO_TCP : 25152393Syz155240 case IPPROTO_UDP : 25162393Syz155240 break; 25172393Syz155240 default : 25182393Syz155240 return NULL; 25192393Syz155240 } 25202393Syz155240 25212393Syz155240 tcp = (tcphdr_t *)((char *)oip + (IP_HL(oip) << 2)); 25222393Syz155240 dport = tcp->th_dport; 25232393Syz155240 sport = tcp->th_sport; 25242393Syz155240 25252393Syz155240 hv = (pr = oip->ip_p); 25262393Syz155240 src.in4 = oip->ip_src; 25272393Syz155240 hv += src.in4.s_addr; 25282393Syz155240 dst.in4 = oip->ip_dst; 25292393Syz155240 hv += dst.in4.s_addr; 25302393Syz155240 hv += dport; 25312393Syz155240 hv += sport; 25323448Sdh155122 hv = DOUBLE_HASH(hv, ifs); 25333448Sdh155122 25343448Sdh155122 READ_ENTER(&ifs->ifs_ipf_state); 25353448Sdh155122 for (isp = &ifs->ifs_ips_table[hv]; ((is = *isp) != NULL); ) { 25362393Syz155240 isp = &is->is_hnext; 25372393Syz155240 /* 25382393Syz155240 * Only allow this icmp though if the 25392393Syz155240 * encapsulated packet was allowed through the 25402393Syz155240 * other way around. Note that the minimal amount 25412393Syz155240 * of info present does not allow for checking against 25422393Syz155240 * tcp internals such as seq and ack numbers. Only the 25432393Syz155240 * ports are known to be present and can be even if the 25442393Syz155240 * short flag is set. 25452393Syz155240 */ 25462393Syz155240 if ((is->is_p == pr) && (is->is_v == 4) && 25472393Syz155240 (is = fr_matchsrcdst(&ofin, is, &src, &dst, 25482393Syz155240 tcp, FI_ICMPCMP))) { 25492393Syz155240 /* 25502393Syz155240 * i : the index of this packet (the icmp unreachable) 25512393Syz155240 * oi : the index of the original packet found in the 25522393Syz155240 * icmp header (i.e. the packet causing this icmp) 25532393Syz155240 * backward : original packet was backward compared to 25542393Syz155240 * the state 25552393Syz155240 */ 25562393Syz155240 backward = IP6_NEQ(&is->is_src, &src); 25572393Syz155240 fin->fin_rev = !backward; 25582393Syz155240 i = (!backward << 1) + fin->fin_out; 25592393Syz155240 oi = (backward << 1) + ofin.fin_out; 25602393Syz155240 25612393Syz155240 if (((is->is_pass & FR_NOICMPERR) != 0) || 25622393Syz155240 (is->is_icmppkts[i] > is->is_pkts[oi])) 25632393Syz155240 break; 25643448Sdh155122 ifs->ifs_ips_stats.iss_hits++; 25652393Syz155240 is->is_icmppkts[i]++; 25662393Syz155240 /* 25672393Syz155240 * we deliberately do not touch the timeouts 25682393Syz155240 * for the accompanying state table entry. 25692393Syz155240 * It remains to be seen if that is correct. XXX 25702393Syz155240 */ 25712393Syz155240 return is; 25722393Syz155240 } 25732393Syz155240 } 25743448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_state); 25752393Syz155240 return NULL; 25762393Syz155240 } 25772393Syz155240 25782393Syz155240 25792393Syz155240 /* ------------------------------------------------------------------------ */ 25802393Syz155240 /* Function: fr_ipsmove */ 25812393Syz155240 /* Returns: Nil */ 25822393Syz155240 /* Parameters: is(I) - pointer to state table entry */ 25832393Syz155240 /* hv(I) - new hash value for state table entry */ 25842393Syz155240 /* Write Locks: ipf_state */ 25852393Syz155240 /* */ 25862393Syz155240 /* Move a state entry from one position in the hash table to another. */ 25872393Syz155240 /* ------------------------------------------------------------------------ */ 25883448Sdh155122 static void fr_ipsmove(is, hv, ifs) 25892393Syz155240 ipstate_t *is; 25902393Syz155240 u_int hv; 25913448Sdh155122 ipf_stack_t *ifs; 25922393Syz155240 { 25932393Syz155240 ipstate_t **isp; 25942393Syz155240 u_int hvm; 25952393Syz155240 25963448Sdh155122 ASSERT(rw_read_locked(&ifs->ifs_ipf_state.ipf_lk) == 0); 25972393Syz155240 25982393Syz155240 hvm = is->is_hv; 25992393Syz155240 /* 26002393Syz155240 * Remove the hash from the old location... 26012393Syz155240 */ 26022393Syz155240 isp = is->is_phnext; 26032393Syz155240 if (is->is_hnext) 26042393Syz155240 is->is_hnext->is_phnext = isp; 26052393Syz155240 *isp = is->is_hnext; 26063448Sdh155122 if (ifs->ifs_ips_table[hvm] == NULL) 26073448Sdh155122 ifs->ifs_ips_stats.iss_inuse--; 26083448Sdh155122 ifs->ifs_ips_stats.iss_bucketlen[hvm]--; 26092393Syz155240 26102393Syz155240 /* 26112393Syz155240 * ...and put the hash in the new one. 26122393Syz155240 */ 26133448Sdh155122 hvm = DOUBLE_HASH(hv, ifs); 26142393Syz155240 is->is_hv = hvm; 26153448Sdh155122 isp = &ifs->ifs_ips_table[hvm]; 26162393Syz155240 if (*isp) 26172393Syz155240 (*isp)->is_phnext = &is->is_hnext; 26182393Syz155240 else 26193448Sdh155122 ifs->ifs_ips_stats.iss_inuse++; 26203448Sdh155122 ifs->ifs_ips_stats.iss_bucketlen[hvm]++; 26212393Syz155240 is->is_phnext = isp; 26222393Syz155240 is->is_hnext = *isp; 26232393Syz155240 *isp = is; 26242393Syz155240 } 26252393Syz155240 26262393Syz155240 26272393Syz155240 /* ------------------------------------------------------------------------ */ 26282393Syz155240 /* Function: fr_stlookup */ 26292393Syz155240 /* Returns: ipstate_t* - NULL == no matching state found, */ 26302393Syz155240 /* else pointer to state information is returned */ 26312393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 26322393Syz155240 /* tcp(I) - pointer to TCP/UDP header. */ 26332393Syz155240 /* */ 26342393Syz155240 /* Search the state table for a matching entry to the packet described by */ 26352393Syz155240 /* the contents of *fin. */ 26362393Syz155240 /* */ 26372393Syz155240 /* If we return NULL then no lock on ipf_state is held. */ 26382393Syz155240 /* If we return non-null then a read-lock on ipf_state is held. */ 26392393Syz155240 /* ------------------------------------------------------------------------ */ 26402393Syz155240 ipstate_t *fr_stlookup(fin, tcp, ifqp) 26412393Syz155240 fr_info_t *fin; 26422393Syz155240 tcphdr_t *tcp; 26432393Syz155240 ipftq_t **ifqp; 26442393Syz155240 { 26452393Syz155240 u_int hv, hvm, pr, v, tryagain; 26462393Syz155240 ipstate_t *is, **isp; 26472393Syz155240 u_short dport, sport; 26482393Syz155240 i6addr_t src, dst; 26492393Syz155240 struct icmp *ic; 26502393Syz155240 ipftq_t *ifq; 26512393Syz155240 int oow; 26523448Sdh155122 ipf_stack_t *ifs = fin->fin_ifs; 26532393Syz155240 26542393Syz155240 is = NULL; 26552393Syz155240 ifq = NULL; 26562393Syz155240 tcp = fin->fin_dp; 26572393Syz155240 ic = (struct icmp *)tcp; 26582393Syz155240 hv = (pr = fin->fin_fi.fi_p); 26592393Syz155240 src = fin->fin_fi.fi_src; 26602393Syz155240 dst = fin->fin_fi.fi_dst; 26612393Syz155240 hv += src.in4.s_addr; 26622393Syz155240 hv += dst.in4.s_addr; 26632393Syz155240 26642393Syz155240 v = fin->fin_fi.fi_v; 26652393Syz155240 #ifdef USE_INET6 26662393Syz155240 if (v == 6) { 26672393Syz155240 hv += fin->fin_fi.fi_src.i6[1]; 26682393Syz155240 hv += fin->fin_fi.fi_src.i6[2]; 26692393Syz155240 hv += fin->fin_fi.fi_src.i6[3]; 26702393Syz155240 26712393Syz155240 if ((fin->fin_p == IPPROTO_ICMPV6) && 26722393Syz155240 IN6_IS_ADDR_MULTICAST(&fin->fin_fi.fi_dst.in6)) { 26732393Syz155240 hv -= dst.in4.s_addr; 26742393Syz155240 } else { 26752393Syz155240 hv += fin->fin_fi.fi_dst.i6[1]; 26762393Syz155240 hv += fin->fin_fi.fi_dst.i6[2]; 26772393Syz155240 hv += fin->fin_fi.fi_dst.i6[3]; 26782393Syz155240 } 26792393Syz155240 } 26802393Syz155240 #endif 26817433SJohn.Ojemann@Sun.COM if ((v == 4) && 26827433SJohn.Ojemann@Sun.COM (fin->fin_flx & (FI_MULTICAST|FI_BROADCAST|FI_MBCAST))) { 26837433SJohn.Ojemann@Sun.COM if (fin->fin_out == 0) { 26847433SJohn.Ojemann@Sun.COM hv -= src.in4.s_addr; 26857433SJohn.Ojemann@Sun.COM } else { 26867433SJohn.Ojemann@Sun.COM hv -= dst.in4.s_addr; 26877433SJohn.Ojemann@Sun.COM } 26887433SJohn.Ojemann@Sun.COM } 26892393Syz155240 26902393Syz155240 /* 26912393Syz155240 * Search the hash table for matching packet header info. 26922393Syz155240 */ 26932393Syz155240 switch (pr) 26942393Syz155240 { 26952393Syz155240 #ifdef USE_INET6 26962393Syz155240 case IPPROTO_ICMPV6 : 26972393Syz155240 tryagain = 0; 26982393Syz155240 if (v == 6) { 26992393Syz155240 if ((ic->icmp_type == ICMP6_ECHO_REQUEST) || 27002393Syz155240 (ic->icmp_type == ICMP6_ECHO_REPLY)) { 27012393Syz155240 hv += ic->icmp_id; 27022393Syz155240 } 27032393Syz155240 } 27043448Sdh155122 READ_ENTER(&ifs->ifs_ipf_state); 27052393Syz155240 icmp6again: 27063448Sdh155122 hvm = DOUBLE_HASH(hv, ifs); 27073448Sdh155122 for (isp = &ifs->ifs_ips_table[hvm]; ((is = *isp) != NULL); ) { 27082393Syz155240 isp = &is->is_hnext; 27092393Syz155240 if ((is->is_p != pr) || (is->is_v != v)) 27102393Syz155240 continue; 27112393Syz155240 is = fr_matchsrcdst(fin, is, &src, &dst, NULL, FI_CMP); 27122393Syz155240 if (is != NULL && 27132393Syz155240 fr_matchicmpqueryreply(v, &is->is_icmp, 27142393Syz155240 ic, fin->fin_rev)) { 27152393Syz155240 if (fin->fin_rev) 27163448Sdh155122 ifq = &ifs->ifs_ips_icmpacktq; 27172393Syz155240 else 27183448Sdh155122 ifq = &ifs->ifs_ips_icmptq; 27192393Syz155240 break; 27202393Syz155240 } 27212393Syz155240 } 27222393Syz155240 27232393Syz155240 if (is != NULL) { 27242393Syz155240 if ((tryagain != 0) && !(is->is_flags & SI_W_DADDR)) { 27252393Syz155240 hv += fin->fin_fi.fi_src.i6[0]; 27262393Syz155240 hv += fin->fin_fi.fi_src.i6[1]; 27272393Syz155240 hv += fin->fin_fi.fi_src.i6[2]; 27282393Syz155240 hv += fin->fin_fi.fi_src.i6[3]; 27293448Sdh155122 fr_ipsmove(is, hv, ifs); 27303448Sdh155122 MUTEX_DOWNGRADE(&ifs->ifs_ipf_state); 27312393Syz155240 } 27322393Syz155240 break; 27332393Syz155240 } 27343448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_state); 27352393Syz155240 27362393Syz155240 /* 27372393Syz155240 * No matching icmp state entry. Perhaps this is a 27382393Syz155240 * response to another state entry. 27392393Syz155240 * 27402393Syz155240 * XXX With some ICMP6 packets, the "other" address is already 27412393Syz155240 * in the packet, after the ICMP6 header, and this could be 27422393Syz155240 * used in place of the multicast address. However, taking 27432393Syz155240 * advantage of this requires some significant code changes 27442393Syz155240 * to handle the specific types where that is the case. 27452393Syz155240 */ 27463448Sdh155122 if ((ifs->ifs_ips_stats.iss_wild != 0) && (v == 6) && (tryagain == 0) && 27472393Syz155240 !IN6_IS_ADDR_MULTICAST(&fin->fin_fi.fi_src.in6)) { 27482393Syz155240 hv -= fin->fin_fi.fi_src.i6[0]; 27492393Syz155240 hv -= fin->fin_fi.fi_src.i6[1]; 27502393Syz155240 hv -= fin->fin_fi.fi_src.i6[2]; 27512393Syz155240 hv -= fin->fin_fi.fi_src.i6[3]; 27522393Syz155240 tryagain = 1; 27533448Sdh155122 WRITE_ENTER(&ifs->ifs_ipf_state); 27542393Syz155240 goto icmp6again; 27552393Syz155240 } 27562393Syz155240 27572393Syz155240 is = fr_checkicmp6matchingstate(fin); 27582393Syz155240 if (is != NULL) 27592393Syz155240 return is; 27602393Syz155240 break; 27612393Syz155240 #endif 27622393Syz155240 27632393Syz155240 case IPPROTO_ICMP : 27642393Syz155240 if (v == 4) { 27652393Syz155240 hv += ic->icmp_id; 27662393Syz155240 } 27673448Sdh155122 hv = DOUBLE_HASH(hv, ifs); 27683448Sdh155122 READ_ENTER(&ifs->ifs_ipf_state); 27693448Sdh155122 for (isp = &ifs->ifs_ips_table[hv]; ((is = *isp) != NULL); ) { 27702393Syz155240 isp = &is->is_hnext; 27712393Syz155240 if ((is->is_p != pr) || (is->is_v != v)) 27722393Syz155240 continue; 27732393Syz155240 is = fr_matchsrcdst(fin, is, &src, &dst, NULL, FI_CMP); 27742393Syz155240 if (is != NULL && 27752393Syz155240 fr_matchicmpqueryreply(v, &is->is_icmp, 27762393Syz155240 ic, fin->fin_rev)) { 27772393Syz155240 if (fin->fin_rev) 27783448Sdh155122 ifq = &ifs->ifs_ips_icmpacktq; 27792393Syz155240 else 27803448Sdh155122 ifq = &ifs->ifs_ips_icmptq; 27812393Syz155240 break; 27822393Syz155240 } 27832393Syz155240 } 27842393Syz155240 if (is == NULL) { 27853448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_state); 27862393Syz155240 } 27872393Syz155240 break; 27882393Syz155240 27892393Syz155240 case IPPROTO_TCP : 27902393Syz155240 case IPPROTO_UDP : 27912393Syz155240 ifqp = NULL; 27922393Syz155240 sport = htons(fin->fin_data[0]); 27932393Syz155240 hv += sport; 27942393Syz155240 dport = htons(fin->fin_data[1]); 27952393Syz155240 hv += dport; 27962393Syz155240 oow = 0; 27972393Syz155240 tryagain = 0; 27983448Sdh155122 READ_ENTER(&ifs->ifs_ipf_state); 27992393Syz155240 retry_tcpudp: 28003448Sdh155122 hvm = DOUBLE_HASH(hv, ifs); 28013448Sdh155122 for (isp = &ifs->ifs_ips_table[hvm]; ((is = *isp) != NULL); ) { 28022393Syz155240 isp = &is->is_hnext; 28032393Syz155240 if ((is->is_p != pr) || (is->is_v != v)) 28042393Syz155240 continue; 28052393Syz155240 fin->fin_flx &= ~FI_OOW; 28062393Syz155240 is = fr_matchsrcdst(fin, is, &src, &dst, tcp, FI_CMP); 28072393Syz155240 if (is != NULL) { 28082393Syz155240 if (pr == IPPROTO_TCP) { 28092393Syz155240 if (!fr_tcpstate(fin, tcp, is)) { 28102393Syz155240 oow |= fin->fin_flx & FI_OOW; 28112393Syz155240 continue; 28122393Syz155240 } 28132393Syz155240 } 28142393Syz155240 break; 28152393Syz155240 } 28162393Syz155240 } 28172393Syz155240 if (is != NULL) { 28182393Syz155240 if (tryagain && 28192393Syz155240 !(is->is_flags & (SI_CLONE|SI_WILDP|SI_WILDA))) { 28202393Syz155240 hv += dport; 28212393Syz155240 hv += sport; 28223448Sdh155122 fr_ipsmove(is, hv, ifs); 28233448Sdh155122 MUTEX_DOWNGRADE(&ifs->ifs_ipf_state); 28242393Syz155240 } 28252393Syz155240 break; 28262393Syz155240 } 28273448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_state); 28283448Sdh155122 28297433SJohn.Ojemann@Sun.COM if (ifs->ifs_ips_stats.iss_wild) { 28307433SJohn.Ojemann@Sun.COM if (tryagain == 0) { 28317433SJohn.Ojemann@Sun.COM hv -= dport; 28327433SJohn.Ojemann@Sun.COM hv -= sport; 28337433SJohn.Ojemann@Sun.COM } else if (tryagain == 1) { 28347433SJohn.Ojemann@Sun.COM hv = fin->fin_fi.fi_p; 28357433SJohn.Ojemann@Sun.COM /* 28367433SJohn.Ojemann@Sun.COM * If we try to pretend this is a reply to a 28377433SJohn.Ojemann@Sun.COM * multicast/broadcast packet then we need to 28387433SJohn.Ojemann@Sun.COM * exclude part of the address from the hash 28397433SJohn.Ojemann@Sun.COM * calculation. 28407433SJohn.Ojemann@Sun.COM */ 28417433SJohn.Ojemann@Sun.COM if (fin->fin_out == 0) { 28427433SJohn.Ojemann@Sun.COM hv += src.in4.s_addr; 28437433SJohn.Ojemann@Sun.COM } else { 28447433SJohn.Ojemann@Sun.COM hv += dst.in4.s_addr; 28457433SJohn.Ojemann@Sun.COM } 28467433SJohn.Ojemann@Sun.COM hv += dport; 28477433SJohn.Ojemann@Sun.COM hv += sport; 28487433SJohn.Ojemann@Sun.COM } 28497433SJohn.Ojemann@Sun.COM tryagain++; 28507433SJohn.Ojemann@Sun.COM if (tryagain <= 2) { 28517433SJohn.Ojemann@Sun.COM WRITE_ENTER(&ifs->ifs_ipf_state); 28527433SJohn.Ojemann@Sun.COM goto retry_tcpudp; 28537433SJohn.Ojemann@Sun.COM } 28542393Syz155240 } 28552393Syz155240 fin->fin_flx |= oow; 28562393Syz155240 break; 28572393Syz155240 28582393Syz155240 #if 0 28592393Syz155240 case IPPROTO_GRE : 28602393Syz155240 gre = fin->fin_dp; 28612393Syz155240 if (GRE_REV(gre->gr_flags) == 1) { 28622393Syz155240 hv += gre->gr_call; 28632393Syz155240 } 28642393Syz155240 /* FALLTHROUGH */ 28652393Syz155240 #endif 28662393Syz155240 default : 28672393Syz155240 ifqp = NULL; 28683448Sdh155122 hvm = DOUBLE_HASH(hv, ifs); 28693448Sdh155122 READ_ENTER(&ifs->ifs_ipf_state); 28703448Sdh155122 for (isp = &ifs->ifs_ips_table[hvm]; ((is = *isp) != NULL); ) { 28712393Syz155240 isp = &is->is_hnext; 28722393Syz155240 if ((is->is_p != pr) || (is->is_v != v)) 28732393Syz155240 continue; 28742393Syz155240 is = fr_matchsrcdst(fin, is, &src, &dst, NULL, FI_CMP); 28752393Syz155240 if (is != NULL) { 28763448Sdh155122 ifq = &ifs->ifs_ips_iptq; 28772393Syz155240 break; 28782393Syz155240 } 28792393Syz155240 } 28802393Syz155240 if (is == NULL) { 28813448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_state); 28822393Syz155240 } 28832393Syz155240 break; 28842393Syz155240 } 28852393Syz155240 28862393Syz155240 if ((is != NULL) && ((is->is_sti.tqe_flags & TQE_RULEBASED) != 0) && 28872393Syz155240 (is->is_tqehead[fin->fin_rev] != NULL)) 28882393Syz155240 ifq = is->is_tqehead[fin->fin_rev]; 28892393Syz155240 if (ifq != NULL && ifqp != NULL) 28902393Syz155240 *ifqp = ifq; 28912393Syz155240 return is; 28922393Syz155240 } 28932393Syz155240 28942393Syz155240 28952393Syz155240 /* ------------------------------------------------------------------------ */ 28962393Syz155240 /* Function: fr_updatestate */ 28972393Syz155240 /* Returns: Nil */ 28982393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 28992393Syz155240 /* is(I) - pointer to state table entry */ 29002393Syz155240 /* Read Locks: ipf_state */ 29012393Syz155240 /* */ 29022393Syz155240 /* Updates packet and byte counters for a newly received packet. Seeds the */ 29032393Syz155240 /* fragment cache with a new entry as required. */ 29042393Syz155240 /* ------------------------------------------------------------------------ */ 29052393Syz155240 void fr_updatestate(fin, is, ifq) 29062393Syz155240 fr_info_t *fin; 29072393Syz155240 ipstate_t *is; 29082393Syz155240 ipftq_t *ifq; 29092393Syz155240 { 29102393Syz155240 ipftqent_t *tqe; 29112393Syz155240 int i, pass; 29123448Sdh155122 ipf_stack_t *ifs = fin->fin_ifs; 29132393Syz155240 29142393Syz155240 i = (fin->fin_rev << 1) + fin->fin_out; 29152393Syz155240 29162393Syz155240 /* 29172393Syz155240 * For TCP packets, ifq == NULL. For all others, check if this new 29182393Syz155240 * queue is different to the last one it was on and move it if so. 29192393Syz155240 */ 29202393Syz155240 tqe = &is->is_sti; 29212393Syz155240 MUTEX_ENTER(&is->is_lock); 29222393Syz155240 if ((tqe->tqe_flags & TQE_RULEBASED) != 0) 29232393Syz155240 ifq = is->is_tqehead[fin->fin_rev]; 29242393Syz155240 29252393Syz155240 if (ifq != NULL) 29263448Sdh155122 fr_movequeue(tqe, tqe->tqe_ifq, ifq, ifs); 29272393Syz155240 29282393Syz155240 is->is_pkts[i]++; 29298624SDarren.Reed@Sun.COM fin->fin_pktnum = is->is_pkts[i] + is->is_icmppkts[i]; 29302393Syz155240 is->is_bytes[i] += fin->fin_plen; 29312393Syz155240 MUTEX_EXIT(&is->is_lock); 29322393Syz155240 29332393Syz155240 #ifdef IPFILTER_SYNC 29342393Syz155240 if (is->is_flags & IS_STATESYNC) 29352393Syz155240 ipfsync_update(SMC_STATE, fin, is->is_sync); 29362393Syz155240 #endif 29372393Syz155240 29383448Sdh155122 ATOMIC_INCL(ifs->ifs_ips_stats.iss_hits); 29392393Syz155240 29402393Syz155240 fin->fin_fr = is->is_rule; 29412393Syz155240 29422393Syz155240 /* 29432393Syz155240 * If this packet is a fragment and the rule says to track fragments, 29442393Syz155240 * then create a new fragment cache entry. 29452393Syz155240 */ 29462393Syz155240 pass = is->is_pass; 29472393Syz155240 if ((fin->fin_flx & FI_FRAG) && FR_ISPASS(pass)) 29482393Syz155240 (void) fr_newfrag(fin, pass ^ FR_KEEPSTATE); 29492393Syz155240 } 29502393Syz155240 29512393Syz155240 29522393Syz155240 /* ------------------------------------------------------------------------ */ 29532393Syz155240 /* Function: fr_checkstate */ 29542393Syz155240 /* Returns: frentry_t* - NULL == search failed, */ 29552393Syz155240 /* else pointer to rule for matching state */ 29562393Syz155240 /* Parameters: ifp(I) - pointer to interface */ 29572393Syz155240 /* passp(I) - pointer to filtering result flags */ 29582393Syz155240 /* */ 29592393Syz155240 /* Check if a packet is associated with an entry in the state table. */ 29602393Syz155240 /* ------------------------------------------------------------------------ */ 29612393Syz155240 frentry_t *fr_checkstate(fin, passp) 29622393Syz155240 fr_info_t *fin; 29632393Syz155240 u_32_t *passp; 29642393Syz155240 { 29652393Syz155240 ipstate_t *is; 29662393Syz155240 frentry_t *fr; 29672393Syz155240 tcphdr_t *tcp; 29682393Syz155240 ipftq_t *ifq; 29692393Syz155240 u_int pass; 29703448Sdh155122 ipf_stack_t *ifs = fin->fin_ifs; 29713448Sdh155122 29723448Sdh155122 if (ifs->ifs_fr_state_lock || (ifs->ifs_ips_list == NULL) || 29732393Syz155240 (fin->fin_flx & (FI_SHORT|FI_STATE|FI_FRAGBODY|FI_BAD))) 29742393Syz155240 return NULL; 29752393Syz155240 29762393Syz155240 is = NULL; 29772393Syz155240 if ((fin->fin_flx & FI_TCPUDP) || 29782393Syz155240 (fin->fin_fi.fi_p == IPPROTO_ICMP) 29792393Syz155240 #ifdef USE_INET6 29802393Syz155240 || (fin->fin_fi.fi_p == IPPROTO_ICMPV6) 29812393Syz155240 #endif 29822393Syz155240 ) 29832393Syz155240 tcp = fin->fin_dp; 29842393Syz155240 else 29852393Syz155240 tcp = NULL; 29862393Syz155240 29872393Syz155240 /* 29882393Syz155240 * Search the hash table for matching packet header info. 29892393Syz155240 */ 29902393Syz155240 ifq = NULL; 29918624SDarren.Reed@Sun.COM is = fr_stlookup(fin, tcp, &ifq); 29922393Syz155240 switch (fin->fin_p) 29932393Syz155240 { 29942393Syz155240 #ifdef USE_INET6 29952393Syz155240 case IPPROTO_ICMPV6 : 29962393Syz155240 if (is != NULL) 29972393Syz155240 break; 29982393Syz155240 if (fin->fin_v == 6) { 29992393Syz155240 is = fr_checkicmp6matchingstate(fin); 30002393Syz155240 if (is != NULL) 30012393Syz155240 goto matched; 30022393Syz155240 } 30032393Syz155240 break; 30042393Syz155240 #endif 30052393Syz155240 case IPPROTO_ICMP : 30062393Syz155240 if (is != NULL) 30072393Syz155240 break; 30082393Syz155240 /* 30092393Syz155240 * No matching icmp state entry. Perhaps this is a 30102393Syz155240 * response to another state entry. 30112393Syz155240 */ 30122393Syz155240 is = fr_checkicmpmatchingstate(fin); 30132393Syz155240 if (is != NULL) 30142393Syz155240 goto matched; 30152393Syz155240 break; 30162393Syz155240 case IPPROTO_TCP : 30172393Syz155240 if (is == NULL) 30182393Syz155240 break; 30192393Syz155240 30202393Syz155240 if (is->is_pass & FR_NEWISN) { 30212393Syz155240 if (fin->fin_out == 0) 30222393Syz155240 fr_fixinisn(fin, is); 30232393Syz155240 else if (fin->fin_out == 1) 30242393Syz155240 fr_fixoutisn(fin, is); 30252393Syz155240 } 30262393Syz155240 break; 30272393Syz155240 default : 30282393Syz155240 if (fin->fin_rev) 30293448Sdh155122 ifq = &ifs->ifs_ips_udpacktq; 30302393Syz155240 else 30313448Sdh155122 ifq = &ifs->ifs_ips_udptq; 30322393Syz155240 break; 30332393Syz155240 } 30342393Syz155240 if (is == NULL) { 30353448Sdh155122 ATOMIC_INCL(ifs->ifs_ips_stats.iss_miss); 30362393Syz155240 return NULL; 30372393Syz155240 } 30382393Syz155240 30392393Syz155240 matched: 30402393Syz155240 fr = is->is_rule; 30412393Syz155240 if (fr != NULL) { 30422393Syz155240 if ((fin->fin_out == 0) && (fr->fr_nattag.ipt_num[0] != 0)) { 3043*11761SZdenek.Kotala@Sun.COM if (fin->fin_nattag == NULL) { 3044*11761SZdenek.Kotala@Sun.COM RWLOCK_EXIT(&ifs->ifs_ipf_state); 30452393Syz155240 return NULL; 3046*11761SZdenek.Kotala@Sun.COM } 3047*11761SZdenek.Kotala@Sun.COM if (fr_matchtag(&fr->fr_nattag, fin->fin_nattag) != 0) { 3048*11761SZdenek.Kotala@Sun.COM RWLOCK_EXIT(&ifs->ifs_ipf_state); 30492393Syz155240 return NULL; 3050*11761SZdenek.Kotala@Sun.COM } 30512393Syz155240 } 30522393Syz155240 (void) strncpy(fin->fin_group, fr->fr_group, FR_GROUPLEN); 30532393Syz155240 fin->fin_icode = fr->fr_icode; 30542393Syz155240 } 30552393Syz155240 30562393Syz155240 fin->fin_rule = is->is_rulen; 30572393Syz155240 pass = is->is_pass; 30582393Syz155240 fr_updatestate(fin, is, ifq); 30598624SDarren.Reed@Sun.COM 30603448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_state); 30612393Syz155240 fin->fin_flx |= FI_STATE; 30622393Syz155240 if ((pass & FR_LOGFIRST) != 0) 30632393Syz155240 pass &= ~(FR_LOGFIRST|FR_LOG); 30642393Syz155240 *passp = pass; 30652393Syz155240 return fr; 30662393Syz155240 } 30672393Syz155240 30682393Syz155240 30692393Syz155240 /* ------------------------------------------------------------------------ */ 30702393Syz155240 /* Function: fr_fixoutisn */ 30712393Syz155240 /* Returns: Nil */ 30722393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 30732393Syz155240 /* is(I) - pointer to master state structure */ 30742393Syz155240 /* */ 30752393Syz155240 /* Called only for outbound packets, adjusts the sequence number and the */ 30762393Syz155240 /* TCP checksum to match that change. */ 30772393Syz155240 /* ------------------------------------------------------------------------ */ 30782393Syz155240 static void fr_fixoutisn(fin, is) 30792393Syz155240 fr_info_t *fin; 30802393Syz155240 ipstate_t *is; 30812393Syz155240 { 30822393Syz155240 tcphdr_t *tcp; 30832393Syz155240 int rev; 30842393Syz155240 u_32_t seq; 30852393Syz155240 30862393Syz155240 tcp = fin->fin_dp; 30872393Syz155240 rev = fin->fin_rev; 30882393Syz155240 if ((is->is_flags & IS_ISNSYN) != 0) { 30892393Syz155240 if (rev == 0) { 30902393Syz155240 seq = ntohl(tcp->th_seq); 30912393Syz155240 seq += is->is_isninc[0]; 30922393Syz155240 tcp->th_seq = htonl(seq); 30932958Sdr146992 fix_outcksum(&tcp->th_sum, is->is_sumd[0]); 30942393Syz155240 } 30952393Syz155240 } 30962393Syz155240 if ((is->is_flags & IS_ISNACK) != 0) { 30972393Syz155240 if (rev == 1) { 30982393Syz155240 seq = ntohl(tcp->th_seq); 30992393Syz155240 seq += is->is_isninc[1]; 31002393Syz155240 tcp->th_seq = htonl(seq); 31012958Sdr146992 fix_outcksum(&tcp->th_sum, is->is_sumd[1]); 31022393Syz155240 } 31032393Syz155240 } 31042393Syz155240 } 31052393Syz155240 31062393Syz155240 31072393Syz155240 /* ------------------------------------------------------------------------ */ 31082393Syz155240 /* Function: fr_fixinisn */ 31092393Syz155240 /* Returns: Nil */ 31102393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 31112393Syz155240 /* is(I) - pointer to master state structure */ 31122393Syz155240 /* */ 31132393Syz155240 /* Called only for inbound packets, adjusts the acknowledge number and the */ 31142393Syz155240 /* TCP checksum to match that change. */ 31152393Syz155240 /* ------------------------------------------------------------------------ */ 31162393Syz155240 static void fr_fixinisn(fin, is) 31172393Syz155240 fr_info_t *fin; 31182393Syz155240 ipstate_t *is; 31192393Syz155240 { 31202393Syz155240 tcphdr_t *tcp; 31212393Syz155240 int rev; 31222393Syz155240 u_32_t ack; 31232393Syz155240 31242393Syz155240 tcp = fin->fin_dp; 31252393Syz155240 rev = fin->fin_rev; 31262393Syz155240 if ((is->is_flags & IS_ISNSYN) != 0) { 31272393Syz155240 if (rev == 1) { 31282393Syz155240 ack = ntohl(tcp->th_ack); 31292393Syz155240 ack -= is->is_isninc[0]; 31302393Syz155240 tcp->th_ack = htonl(ack); 31312958Sdr146992 fix_incksum(&tcp->th_sum, is->is_sumd[0]); 31322393Syz155240 } 31332393Syz155240 } 31342393Syz155240 if ((is->is_flags & IS_ISNACK) != 0) { 31352393Syz155240 if (rev == 0) { 31362393Syz155240 ack = ntohl(tcp->th_ack); 31372393Syz155240 ack -= is->is_isninc[1]; 31382393Syz155240 tcp->th_ack = htonl(ack); 31392958Sdr146992 fix_incksum(&tcp->th_sum, is->is_sumd[1]); 31402393Syz155240 } 31412393Syz155240 } 31422393Syz155240 } 31432393Syz155240 31442393Syz155240 31452393Syz155240 /* ------------------------------------------------------------------------ */ 31462393Syz155240 /* Function: fr_statesync */ 31472393Syz155240 /* Returns: Nil */ 31482958Sdr146992 /* Parameters: action(I) - type of synchronisation to do */ 31492958Sdr146992 /* v(I) - IP version being sync'd (v4 or v6) */ 31502958Sdr146992 /* ifp(I) - interface identifier associated with action */ 31512958Sdr146992 /* name(I) - name associated with ifp parameter */ 31522393Syz155240 /* */ 31532393Syz155240 /* Walk through all state entries and if an interface pointer match is */ 31542393Syz155240 /* found then look it up again, based on its name in case the pointer has */ 31552393Syz155240 /* changed since last time. */ 31562393Syz155240 /* */ 31572393Syz155240 /* If ifp is passed in as being non-null then we are only doing updates for */ 31582393Syz155240 /* existing, matching, uses of it. */ 31592393Syz155240 /* ------------------------------------------------------------------------ */ 31603448Sdh155122 void fr_statesync(action, v, ifp, name, ifs) 31612958Sdr146992 int action, v; 31622393Syz155240 void *ifp; 31632958Sdr146992 char *name; 31643448Sdh155122 ipf_stack_t *ifs; 31652393Syz155240 { 31662393Syz155240 ipstate_t *is; 31672393Syz155240 int i; 31682393Syz155240 31693448Sdh155122 if (ifs->ifs_fr_running <= 0) 31702393Syz155240 return; 31712393Syz155240 31723448Sdh155122 WRITE_ENTER(&ifs->ifs_ipf_state); 31733448Sdh155122 31743448Sdh155122 if (ifs->ifs_fr_running <= 0) { 31753448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_state); 31762393Syz155240 return; 31772393Syz155240 } 31782393Syz155240 31792958Sdr146992 switch (action) 31802958Sdr146992 { 31812958Sdr146992 case IPFSYNC_RESYNC : 31823448Sdh155122 for (is = ifs->ifs_ips_list; is; is = is->is_next) { 31832958Sdr146992 if (v != 0 && is->is_v != v) 31842958Sdr146992 continue; 31852958Sdr146992 /* 31862958Sdr146992 * Look up all the interface names in the state entry. 31872958Sdr146992 */ 31882958Sdr146992 for (i = 0; i < 4; i++) { 31892393Syz155240 is->is_ifp[i] = fr_resolvenic(is->is_ifname[i], 31903448Sdh155122 is->is_v, ifs); 31912958Sdr146992 } 31922393Syz155240 } 31932958Sdr146992 break; 31942958Sdr146992 case IPFSYNC_NEWIFP : 31953448Sdh155122 for (is = ifs->ifs_ips_list; is; is = is->is_next) { 31962958Sdr146992 if (v != 0 && is->is_v != v) 31972958Sdr146992 continue; 31982958Sdr146992 /* 31992958Sdr146992 * Look up all the interface names in the state entry. 32002958Sdr146992 */ 32012958Sdr146992 for (i = 0; i < 4; i++) { 32022958Sdr146992 if (!strncmp(is->is_ifname[i], name, 32032958Sdr146992 sizeof(is->is_ifname[i]))) 32042958Sdr146992 is->is_ifp[i] = ifp; 32052958Sdr146992 } 32062958Sdr146992 } 32072958Sdr146992 break; 32082958Sdr146992 case IPFSYNC_OLDIFP : 32093448Sdh155122 for (is = ifs->ifs_ips_list; is; is = is->is_next) { 32102958Sdr146992 if (v != 0 && is->is_v != v) 32112958Sdr146992 continue; 32122958Sdr146992 /* 32132958Sdr146992 * Look up all the interface names in the state entry. 32142958Sdr146992 */ 32152958Sdr146992 for (i = 0; i < 4; i++) { 32162958Sdr146992 if (is->is_ifp[i] == ifp) 32172958Sdr146992 is->is_ifp[i] = (void *)-1; 32182958Sdr146992 } 32192958Sdr146992 } 32202958Sdr146992 break; 32212393Syz155240 } 32223448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_state); 32232393Syz155240 } 32242393Syz155240 32252393Syz155240 322611105SAlexandr.Nedvedicky@Sun.COM #if SOLARIS2 >= 10 322711105SAlexandr.Nedvedicky@Sun.COM /* ------------------------------------------------------------------------ */ 322811105SAlexandr.Nedvedicky@Sun.COM /* Function: fr_stateifindexsync */ 322911105SAlexandr.Nedvedicky@Sun.COM /* Returns: void */ 323011105SAlexandr.Nedvedicky@Sun.COM /* Parameters: ifp - current network interface descriptor (ifindex) */ 323111105SAlexandr.Nedvedicky@Sun.COM /* newifp - new interface descriptor (new ifindex) */ 323211105SAlexandr.Nedvedicky@Sun.COM /* ifs - pointer to IPF stack */ 323311105SAlexandr.Nedvedicky@Sun.COM /* */ 323411105SAlexandr.Nedvedicky@Sun.COM /* Write Locks: assumes ipf_mutex is locked */ 323511105SAlexandr.Nedvedicky@Sun.COM /* */ 323611105SAlexandr.Nedvedicky@Sun.COM /* Updates all interface indeces matching to ifp with new interface index */ 323711105SAlexandr.Nedvedicky@Sun.COM /* value. */ 323811105SAlexandr.Nedvedicky@Sun.COM /* ------------------------------------------------------------------------ */ 323911105SAlexandr.Nedvedicky@Sun.COM void fr_stateifindexsync(ifp, newifp, ifs) 324011105SAlexandr.Nedvedicky@Sun.COM void *ifp; 324111105SAlexandr.Nedvedicky@Sun.COM void *newifp; 324211105SAlexandr.Nedvedicky@Sun.COM ipf_stack_t *ifs; 324311105SAlexandr.Nedvedicky@Sun.COM { 324411105SAlexandr.Nedvedicky@Sun.COM ipstate_t *is; 324511105SAlexandr.Nedvedicky@Sun.COM int i; 324611105SAlexandr.Nedvedicky@Sun.COM 324711105SAlexandr.Nedvedicky@Sun.COM WRITE_ENTER(&ifs->ifs_ipf_state); 324811105SAlexandr.Nedvedicky@Sun.COM 324911105SAlexandr.Nedvedicky@Sun.COM for (is = ifs->ifs_ips_list; is != NULL; is = is->is_next) { 325011105SAlexandr.Nedvedicky@Sun.COM 325111105SAlexandr.Nedvedicky@Sun.COM for (i = 0; i < 4; i++) { 325211105SAlexandr.Nedvedicky@Sun.COM if (is->is_ifp[i] == ifp) 325311105SAlexandr.Nedvedicky@Sun.COM is->is_ifp[i] = newifp; 325411105SAlexandr.Nedvedicky@Sun.COM } 325511105SAlexandr.Nedvedicky@Sun.COM } 325611105SAlexandr.Nedvedicky@Sun.COM 325711105SAlexandr.Nedvedicky@Sun.COM RWLOCK_EXIT(&ifs->ifs_ipf_state); 325811105SAlexandr.Nedvedicky@Sun.COM } 325911105SAlexandr.Nedvedicky@Sun.COM #endif 326011105SAlexandr.Nedvedicky@Sun.COM 32612393Syz155240 /* ------------------------------------------------------------------------ */ 32622393Syz155240 /* Function: fr_delstate */ 32638170SJohn.Ojemann@Sun.COM /* Returns: int - 0 = entry deleted, else ref count on entry */ 32642393Syz155240 /* Parameters: is(I) - pointer to state structure to delete */ 32652393Syz155240 /* why(I) - if not 0, log reason why it was deleted */ 32668170SJohn.Ojemann@Sun.COM /* ifs - ipf stack instance */ 32673187Sdr146992 /* Write Locks: ipf_state/ipf_global */ 32682393Syz155240 /* */ 32692393Syz155240 /* Deletes a state entry from the enumerated list as well as the hash table */ 32702393Syz155240 /* and timeout queue lists. Make adjustments to hash table statistics and */ 32712393Syz155240 /* global counters as required. */ 32722393Syz155240 /* ------------------------------------------------------------------------ */ 32738170SJohn.Ojemann@Sun.COM int fr_delstate(is, why, ifs) 32742393Syz155240 ipstate_t *is; 32752393Syz155240 int why; 32763448Sdh155122 ipf_stack_t *ifs; 32772393Syz155240 { 32787432SJohn.Ojemann@Sun.COM int removed = 0; 32792393Syz155240 32803448Sdh155122 ASSERT(rw_write_held(&ifs->ifs_ipf_global.ipf_lk) == 0 || 32813448Sdh155122 rw_write_held(&ifs->ifs_ipf_state.ipf_lk) == 0); 32822393Syz155240 32832393Syz155240 /* 32847432SJohn.Ojemann@Sun.COM * Start by removing the entry from the hash table of state entries 32857432SJohn.Ojemann@Sun.COM * so it will not be "used" again. 32867432SJohn.Ojemann@Sun.COM * 32877432SJohn.Ojemann@Sun.COM * It will remain in the "list" of state entries until all references 32887432SJohn.Ojemann@Sun.COM * have been accounted for. 32892393Syz155240 */ 32902393Syz155240 if (is->is_phnext != NULL) { 32917432SJohn.Ojemann@Sun.COM removed = 1; 32922393Syz155240 *is->is_phnext = is->is_hnext; 32932393Syz155240 if (is->is_hnext != NULL) 32942393Syz155240 is->is_hnext->is_phnext = is->is_phnext; 32953448Sdh155122 if (ifs->ifs_ips_table[is->is_hv] == NULL) 32963448Sdh155122 ifs->ifs_ips_stats.iss_inuse--; 32973448Sdh155122 ifs->ifs_ips_stats.iss_bucketlen[is->is_hv]--; 32982393Syz155240 32992393Syz155240 is->is_phnext = NULL; 33002393Syz155240 is->is_hnext = NULL; 33012393Syz155240 } 33022393Syz155240 33032393Syz155240 /* 33043448Sdh155122 * Because ifs->ifs_ips_stats.iss_wild is a count of entries in the state 33052393Syz155240 * table that have wildcard flags set, only decerement it once 33062393Syz155240 * and do it here. 33072393Syz155240 */ 33082393Syz155240 if (is->is_flags & (SI_WILDP|SI_WILDA)) { 33092393Syz155240 if (!(is->is_flags & SI_CLONED)) { 33103448Sdh155122 ATOMIC_DECL(ifs->ifs_ips_stats.iss_wild); 33112393Syz155240 } 33122393Syz155240 is->is_flags &= ~(SI_WILDP|SI_WILDA); 33132393Syz155240 } 33142393Syz155240 33152393Syz155240 /* 33162393Syz155240 * Next, remove it from the timeout queue it is in. 33172393Syz155240 */ 33182393Syz155240 fr_deletequeueentry(&is->is_sti); 33192393Syz155240 33202393Syz155240 is->is_me = NULL; 33212393Syz155240 33222393Syz155240 /* 33232393Syz155240 * If it is still in use by something else, do not go any further, 33242393Syz155240 * but note that at this point it is now an orphan. 33252393Syz155240 */ 33265055Sdr146992 MUTEX_ENTER(&is->is_lock); 33275055Sdr146992 if (is->is_ref > 1) { 33285055Sdr146992 is->is_ref--; 33295055Sdr146992 MUTEX_EXIT(&is->is_lock); 33307432SJohn.Ojemann@Sun.COM if (removed) 33317432SJohn.Ojemann@Sun.COM ifs->ifs_ips_stats.iss_orphans++; 33328170SJohn.Ojemann@Sun.COM return (is->is_ref); 33335055Sdr146992 } 33345055Sdr146992 MUTEX_EXIT(&is->is_lock); 33355055Sdr146992 33365055Sdr146992 is->is_ref = 0; 33372393Syz155240 33387432SJohn.Ojemann@Sun.COM /* 33397432SJohn.Ojemann@Sun.COM * If entry has already been removed from table, 33407432SJohn.Ojemann@Sun.COM * it means we're simply cleaning up an orphan. 33417432SJohn.Ojemann@Sun.COM */ 33427432SJohn.Ojemann@Sun.COM if (!removed) 33437432SJohn.Ojemann@Sun.COM ifs->ifs_ips_stats.iss_orphans--; 33447432SJohn.Ojemann@Sun.COM 33456274Sjojemann if (is->is_tqehead[0] != NULL) 33466274Sjojemann (void) fr_deletetimeoutqueue(is->is_tqehead[0]); 33476274Sjojemann 33486274Sjojemann if (is->is_tqehead[1] != NULL) 33496274Sjojemann (void) fr_deletetimeoutqueue(is->is_tqehead[1]); 33502393Syz155240 33512393Syz155240 #ifdef IPFILTER_SYNC 33522393Syz155240 if (is->is_sync) 33532393Syz155240 ipfsync_del(is->is_sync); 33542393Syz155240 #endif 33552393Syz155240 #ifdef IPFILTER_SCAN 33562393Syz155240 (void) ipsc_detachis(is); 33572393Syz155240 #endif 33582393Syz155240 33597432SJohn.Ojemann@Sun.COM /* 33607432SJohn.Ojemann@Sun.COM * Now remove it from master list of state table entries. 33617432SJohn.Ojemann@Sun.COM */ 33627432SJohn.Ojemann@Sun.COM if (is->is_pnext != NULL) { 33637432SJohn.Ojemann@Sun.COM *is->is_pnext = is->is_next; 33647432SJohn.Ojemann@Sun.COM if (is->is_next != NULL) { 33657432SJohn.Ojemann@Sun.COM is->is_next->is_pnext = is->is_pnext; 33667432SJohn.Ojemann@Sun.COM is->is_next = NULL; 33677432SJohn.Ojemann@Sun.COM } 33687432SJohn.Ojemann@Sun.COM is->is_pnext = NULL; 33697432SJohn.Ojemann@Sun.COM } 33707432SJohn.Ojemann@Sun.COM 33713448Sdh155122 if (ifs->ifs_ipstate_logging != 0 && why != 0) 33723448Sdh155122 ipstate_log(is, why, ifs); 33732393Syz155240 33742393Syz155240 if (is->is_rule != NULL) { 33752393Syz155240 is->is_rule->fr_statecnt--; 33763448Sdh155122 (void)fr_derefrule(&is->is_rule, ifs); 33772393Syz155240 } 33782393Syz155240 33792393Syz155240 MUTEX_DESTROY(&is->is_lock); 33802393Syz155240 KFREE(is); 33813448Sdh155122 ifs->ifs_ips_num--; 33828170SJohn.Ojemann@Sun.COM 33838170SJohn.Ojemann@Sun.COM return (0); 33842393Syz155240 } 33852393Syz155240 33862393Syz155240 33872393Syz155240 /* ------------------------------------------------------------------------ */ 33882393Syz155240 /* Function: fr_timeoutstate */ 33892393Syz155240 /* Returns: Nil */ 33908170SJohn.Ojemann@Sun.COM /* Parameters: ifs - ipf stack instance */ 33912393Syz155240 /* */ 33922393Syz155240 /* Slowly expire held state for thingslike UDP and ICMP. The algorithm */ 33932393Syz155240 /* used here is to keep the queue sorted with the oldest things at the top */ 33942393Syz155240 /* and the youngest at the bottom. So if the top one doesn't need to be */ 33952393Syz155240 /* expired then neither will any under it. */ 33962393Syz155240 /* ------------------------------------------------------------------------ */ 33973448Sdh155122 void fr_timeoutstate(ifs) 33983448Sdh155122 ipf_stack_t *ifs; 33992393Syz155240 { 34002393Syz155240 ipftq_t *ifq, *ifqnext; 34012393Syz155240 ipftqent_t *tqe, *tqn; 34022393Syz155240 ipstate_t *is; 34032393Syz155240 SPL_INT(s); 34042393Syz155240 34052393Syz155240 SPL_NET(s); 34063448Sdh155122 WRITE_ENTER(&ifs->ifs_ipf_state); 34073448Sdh155122 for (ifq = ifs->ifs_ips_tqtqb; ifq != NULL; ifq = ifq->ifq_next) 34082393Syz155240 for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); ) { 34093448Sdh155122 if (tqe->tqe_die > ifs->ifs_fr_ticks) 34102393Syz155240 break; 34112393Syz155240 tqn = tqe->tqe_next; 34122393Syz155240 is = tqe->tqe_parent; 34138170SJohn.Ojemann@Sun.COM (void) fr_delstate(is, ISL_EXPIRE, ifs); 34142393Syz155240 } 34152393Syz155240 34166274Sjojemann for (ifq = ifs->ifs_ips_utqe; ifq != NULL; ifq = ifq->ifq_next) { 34172393Syz155240 for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); ) { 34183448Sdh155122 if (tqe->tqe_die > ifs->ifs_fr_ticks) 34192393Syz155240 break; 34202393Syz155240 tqn = tqe->tqe_next; 34212393Syz155240 is = tqe->tqe_parent; 34228170SJohn.Ojemann@Sun.COM (void) fr_delstate(is, ISL_EXPIRE, ifs); 34232393Syz155240 } 34242393Syz155240 } 34252393Syz155240 34263448Sdh155122 for (ifq = ifs->ifs_ips_utqe; ifq != NULL; ifq = ifqnext) { 34272393Syz155240 ifqnext = ifq->ifq_next; 34282393Syz155240 34292393Syz155240 if (((ifq->ifq_flags & IFQF_DELETE) != 0) && 34302393Syz155240 (ifq->ifq_ref == 0)) { 34313448Sdh155122 fr_freetimeoutqueue(ifq, ifs); 34322393Syz155240 } 34332393Syz155240 } 34342393Syz155240 34353448Sdh155122 if (ifs->ifs_fr_state_doflush) { 34368170SJohn.Ojemann@Sun.COM (void) fr_state_flush(FLUSH_TABLE_EXTRA, 0, ifs); 34373448Sdh155122 ifs->ifs_fr_state_doflush = 0; 34382393Syz155240 } 34393448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_state); 34402393Syz155240 SPL_X(s); 34412393Syz155240 } 34422393Syz155240 34432393Syz155240 34448170SJohn.Ojemann@Sun.COM /* ---------------------------------------------------------------------- */ 34458170SJohn.Ojemann@Sun.COM /* Function: fr_state_flush */ 34468170SJohn.Ojemann@Sun.COM /* Returns: int - 0 == success, -1 == failure */ 34478170SJohn.Ojemann@Sun.COM /* Parameters: flush_option - how to flush the active State table */ 34488170SJohn.Ojemann@Sun.COM /* proto - IP version to flush (4, 6, or both) */ 34498170SJohn.Ojemann@Sun.COM /* ifs - ipf stack instance */ 34508170SJohn.Ojemann@Sun.COM /* Write Locks: ipf_state */ 34518170SJohn.Ojemann@Sun.COM /* */ 34528170SJohn.Ojemann@Sun.COM /* Flush state tables. Three possible flush options currently defined: */ 34538170SJohn.Ojemann@Sun.COM /* */ 34548170SJohn.Ojemann@Sun.COM /* FLUSH_TABLE_ALL : Flush all state table entries */ 34558170SJohn.Ojemann@Sun.COM /* */ 34568170SJohn.Ojemann@Sun.COM /* FLUSH_TABLE_CLOSING : Flush entries with TCP connections which */ 34578170SJohn.Ojemann@Sun.COM /* have started to close on both ends using */ 34588170SJohn.Ojemann@Sun.COM /* ipf_flushclosing(). */ 34598170SJohn.Ojemann@Sun.COM /* */ 34608170SJohn.Ojemann@Sun.COM /* FLUSH_TABLE_EXTRA : First, flush entries which are "almost" closed. */ 34618170SJohn.Ojemann@Sun.COM /* Then, if needed, flush entries with TCP */ 34628170SJohn.Ojemann@Sun.COM /* connections which have been idle for a long */ 34638170SJohn.Ojemann@Sun.COM /* time with ipf_extraflush(). */ 34648170SJohn.Ojemann@Sun.COM /* ---------------------------------------------------------------------- */ 34658170SJohn.Ojemann@Sun.COM static int fr_state_flush(flush_option, proto, ifs) 34668170SJohn.Ojemann@Sun.COM int flush_option, proto; 34673448Sdh155122 ipf_stack_t *ifs; 34682393Syz155240 { 34698170SJohn.Ojemann@Sun.COM ipstate_t *is, *isn; 34708170SJohn.Ojemann@Sun.COM int removed; 34712393Syz155240 SPL_INT(s); 34722393Syz155240 34732393Syz155240 removed = 0; 34742393Syz155240 34752393Syz155240 SPL_NET(s); 34768170SJohn.Ojemann@Sun.COM switch (flush_option) 34778170SJohn.Ojemann@Sun.COM { 34788170SJohn.Ojemann@Sun.COM case FLUSH_TABLE_ALL: 34798170SJohn.Ojemann@Sun.COM isn = ifs->ifs_ips_list; 34808170SJohn.Ojemann@Sun.COM while ((is = isn) != NULL) { 34818170SJohn.Ojemann@Sun.COM isn = is->is_next; 34828170SJohn.Ojemann@Sun.COM if ((proto != 0) && (is->is_v != proto)) 34838170SJohn.Ojemann@Sun.COM continue; 34848170SJohn.Ojemann@Sun.COM if (fr_delstate(is, ISL_FLUSH, ifs) == 0) 34858170SJohn.Ojemann@Sun.COM removed++; 34862393Syz155240 } 34878170SJohn.Ojemann@Sun.COM break; 34888170SJohn.Ojemann@Sun.COM 34898170SJohn.Ojemann@Sun.COM case FLUSH_TABLE_CLOSING: 34908170SJohn.Ojemann@Sun.COM removed = ipf_flushclosing(STATE_FLUSH, 34918170SJohn.Ojemann@Sun.COM IPF_TCPS_CLOSE_WAIT, 34928170SJohn.Ojemann@Sun.COM ifs->ifs_ips_tqtqb, 34938170SJohn.Ojemann@Sun.COM ifs->ifs_ips_utqe, 34948170SJohn.Ojemann@Sun.COM ifs); 34958170SJohn.Ojemann@Sun.COM break; 34968170SJohn.Ojemann@Sun.COM 34978170SJohn.Ojemann@Sun.COM case FLUSH_TABLE_EXTRA: 34988170SJohn.Ojemann@Sun.COM removed = ipf_flushclosing(STATE_FLUSH, 34998170SJohn.Ojemann@Sun.COM IPF_TCPS_FIN_WAIT_2, 35008170SJohn.Ojemann@Sun.COM ifs->ifs_ips_tqtqb, 35018170SJohn.Ojemann@Sun.COM ifs->ifs_ips_utqe, 35028170SJohn.Ojemann@Sun.COM ifs); 35038170SJohn.Ojemann@Sun.COM 35048170SJohn.Ojemann@Sun.COM /* 35058170SJohn.Ojemann@Sun.COM * Be sure we haven't done this in the last 10 seconds. 35068170SJohn.Ojemann@Sun.COM */ 35078170SJohn.Ojemann@Sun.COM if (ifs->ifs_fr_ticks - ifs->ifs_ips_last_force_flush < 35088170SJohn.Ojemann@Sun.COM IPF_TTLVAL(10)) 35092393Syz155240 break; 35108170SJohn.Ojemann@Sun.COM ifs->ifs_ips_last_force_flush = ifs->ifs_fr_ticks; 35118170SJohn.Ojemann@Sun.COM removed += ipf_extraflush(STATE_FLUSH, 35128170SJohn.Ojemann@Sun.COM &ifs->ifs_ips_tqtqb[IPF_TCPS_ESTABLISHED], 35138170SJohn.Ojemann@Sun.COM ifs->ifs_ips_utqe, 35148170SJohn.Ojemann@Sun.COM ifs); 35158170SJohn.Ojemann@Sun.COM break; 35168170SJohn.Ojemann@Sun.COM 35178170SJohn.Ojemann@Sun.COM default: /* Flush Nothing */ 35188170SJohn.Ojemann@Sun.COM break; 35192393Syz155240 } 35202393Syz155240 35212393Syz155240 SPL_X(s); 35228170SJohn.Ojemann@Sun.COM return (removed); 35232393Syz155240 } 35242393Syz155240 35252393Syz155240 35262393Syz155240 /* ------------------------------------------------------------------------ */ 35272393Syz155240 /* Function: fr_tcp_age */ 35282393Syz155240 /* Returns: int - 1 == state transition made, 0 == no change (rejected) */ 35292393Syz155240 /* Parameters: tq(I) - pointer to timeout queue information */ 35302393Syz155240 /* fin(I) - pointer to packet information */ 35312393Syz155240 /* tqtab(I) - TCP timeout queue table this is in */ 35322393Syz155240 /* flags(I) - flags from state/NAT entry */ 35332393Syz155240 /* */ 35342393Syz155240 /* Rewritten by Arjan de Vet <Arjan.deVet@adv.iae.nl>, 2000-07-29: */ 35352393Syz155240 /* */ 35362393Syz155240 /* - (try to) base state transitions on real evidence only, */ 35372393Syz155240 /* i.e. packets that are sent and have been received by ipfilter; */ 35382393Syz155240 /* diagram 18.12 of TCP/IP volume 1 by W. Richard Stevens was used. */ 35392393Syz155240 /* */ 35402393Syz155240 /* - deal with half-closed connections correctly; */ 35412393Syz155240 /* */ 35422393Syz155240 /* - store the state of the source in state[0] such that ipfstat */ 35432393Syz155240 /* displays the state as source/dest instead of dest/source; the calls */ 35442393Syz155240 /* to fr_tcp_age have been changed accordingly. */ 35452393Syz155240 /* */ 35462393Syz155240 /* Internal Parameters: */ 35472393Syz155240 /* */ 35482393Syz155240 /* state[0] = state of source (host that initiated connection) */ 35492393Syz155240 /* state[1] = state of dest (host that accepted the connection) */ 35502393Syz155240 /* */ 35512393Syz155240 /* dir == 0 : a packet from source to dest */ 35522393Syz155240 /* dir == 1 : a packet from dest to source */ 35532393Syz155240 /* */ 35542393Syz155240 /* Locking: it is assumed that the parent of the tqe structure is locked. */ 35552393Syz155240 /* ------------------------------------------------------------------------ */ 35562393Syz155240 int fr_tcp_age(tqe, fin, tqtab, flags) 35572393Syz155240 ipftqent_t *tqe; 35582393Syz155240 fr_info_t *fin; 35592393Syz155240 ipftq_t *tqtab; 35602393Syz155240 int flags; 35612393Syz155240 { 35622393Syz155240 int dlen, ostate, nstate, rval, dir; 35632393Syz155240 u_char tcpflags; 35642393Syz155240 tcphdr_t *tcp; 35653448Sdh155122 ipf_stack_t *ifs = fin->fin_ifs; 35662393Syz155240 35672393Syz155240 tcp = fin->fin_dp; 35682393Syz155240 35692393Syz155240 rval = 0; 35702393Syz155240 dir = fin->fin_rev; 35712393Syz155240 tcpflags = tcp->th_flags; 35722393Syz155240 dlen = fin->fin_dlen - (TCP_OFF(tcp) << 2); 35732393Syz155240 35749888SAlexandr.Nedvedicky@Sun.COM ostate = tqe->tqe_state[1 - dir]; 35759888SAlexandr.Nedvedicky@Sun.COM nstate = tqe->tqe_state[dir]; 35769888SAlexandr.Nedvedicky@Sun.COM 35779888SAlexandr.Nedvedicky@Sun.COM DTRACE_PROBE4( 35789888SAlexandr.Nedvedicky@Sun.COM indata, 35799888SAlexandr.Nedvedicky@Sun.COM fr_info_t *, fin, 35809888SAlexandr.Nedvedicky@Sun.COM int, ostate, 35819888SAlexandr.Nedvedicky@Sun.COM int, nstate, 35829888SAlexandr.Nedvedicky@Sun.COM u_char, tcpflags 35839888SAlexandr.Nedvedicky@Sun.COM ); 35849888SAlexandr.Nedvedicky@Sun.COM 35852393Syz155240 if (tcpflags & TH_RST) { 35862393Syz155240 if (!(tcpflags & TH_PUSH) && !dlen) 35872393Syz155240 nstate = IPF_TCPS_CLOSED; 35882393Syz155240 else 35892393Syz155240 nstate = IPF_TCPS_CLOSE_WAIT; 35909888SAlexandr.Nedvedicky@Sun.COM 35919888SAlexandr.Nedvedicky@Sun.COM /* 35929888SAlexandr.Nedvedicky@Sun.COM * Once RST is received, we must advance peer's state to 35939888SAlexandr.Nedvedicky@Sun.COM * CLOSE_WAIT. 35949888SAlexandr.Nedvedicky@Sun.COM */ 35959888SAlexandr.Nedvedicky@Sun.COM if (ostate <= IPF_TCPS_ESTABLISHED) { 35969888SAlexandr.Nedvedicky@Sun.COM tqe->tqe_state[1 - dir] = IPF_TCPS_CLOSE_WAIT; 35979888SAlexandr.Nedvedicky@Sun.COM } 35982393Syz155240 rval = 1; 35992393Syz155240 } else { 36002393Syz155240 36012393Syz155240 switch (nstate) 36022393Syz155240 { 36039888SAlexandr.Nedvedicky@Sun.COM case IPF_TCPS_LISTEN: /* 0 */ 36042393Syz155240 if ((tcpflags & TH_OPENING) == TH_OPENING) { 36052393Syz155240 /* 36062393Syz155240 * 'dir' received an S and sends SA in 36072393Syz155240 * response, CLOSED -> SYN_RECEIVED 36082393Syz155240 */ 36092393Syz155240 nstate = IPF_TCPS_SYN_RECEIVED; 36102393Syz155240 rval = 1; 36112393Syz155240 } else if ((tcpflags & TH_OPENING) == TH_SYN) { 36122393Syz155240 /* 'dir' sent S, CLOSED -> SYN_SENT */ 36132393Syz155240 nstate = IPF_TCPS_SYN_SENT; 36142393Syz155240 rval = 1; 36152393Syz155240 } 36162393Syz155240 /* 36172393Syz155240 * the next piece of code makes it possible to get 36182393Syz155240 * already established connections into the state table 36192393Syz155240 * after a restart or reload of the filter rules; this 36202393Syz155240 * does not work when a strict 'flags S keep state' is 36212393Syz155240 * used for tcp connections of course 36222393Syz155240 */ 36232393Syz155240 if (((flags & IS_TCPFSM) == 0) && 36242393Syz155240 ((tcpflags & TH_ACKMASK) == TH_ACK)) { 36252393Syz155240 /* 36262393Syz155240 * we saw an A, guess 'dir' is in ESTABLISHED 36272393Syz155240 * mode 36282393Syz155240 */ 36292393Syz155240 switch (ostate) 36302393Syz155240 { 36319888SAlexandr.Nedvedicky@Sun.COM case IPF_TCPS_LISTEN : 36322393Syz155240 case IPF_TCPS_SYN_RECEIVED : 36332393Syz155240 nstate = IPF_TCPS_HALF_ESTAB; 36342393Syz155240 rval = 1; 36352393Syz155240 break; 36362393Syz155240 case IPF_TCPS_HALF_ESTAB : 36372393Syz155240 case IPF_TCPS_ESTABLISHED : 36382393Syz155240 nstate = IPF_TCPS_ESTABLISHED; 36392393Syz155240 rval = 1; 36402393Syz155240 break; 36412393Syz155240 default : 36422393Syz155240 break; 36432393Syz155240 } 36442393Syz155240 } 36452393Syz155240 /* 36462393Syz155240 * TODO: besides regular ACK packets we can have other 36472393Syz155240 * packets as well; it is yet to be determined how we 36482393Syz155240 * should initialize the states in those cases 36492393Syz155240 */ 36502393Syz155240 break; 36512393Syz155240 36529888SAlexandr.Nedvedicky@Sun.COM case IPF_TCPS_SYN_SENT: /* 1 */ 36532393Syz155240 if ((tcpflags & ~(TH_ECN|TH_CWR)) == TH_SYN) { 36542393Syz155240 /* 36552393Syz155240 * A retransmitted SYN packet. We do not reset 36562393Syz155240 * the timeout here to fr_tcptimeout because a 36572393Syz155240 * connection connect timeout does not renew 36582393Syz155240 * after every packet that is sent. We need to 36592393Syz155240 * set rval so as to indicate the packet has 36602393Syz155240 * passed the check for its flags being valid 36612393Syz155240 * in the TCP FSM. Setting rval to 2 has the 36622393Syz155240 * result of not resetting the timeout. 36632393Syz155240 */ 36642393Syz155240 rval = 2; 36652393Syz155240 } else if ((tcpflags & (TH_SYN|TH_FIN|TH_ACK)) == 36662393Syz155240 TH_ACK) { 36672393Syz155240 /* 36682393Syz155240 * we see an A from 'dir' which is in SYN_SENT 36692393Syz155240 * state: 'dir' sent an A in response to an SA 36702393Syz155240 * which it received, SYN_SENT -> ESTABLISHED 36712393Syz155240 */ 36722393Syz155240 nstate = IPF_TCPS_ESTABLISHED; 36732393Syz155240 rval = 1; 36742393Syz155240 } else if (tcpflags & TH_FIN) { 36752393Syz155240 /* 36762393Syz155240 * we see an F from 'dir' which is in SYN_SENT 36772393Syz155240 * state and wants to close its side of the 36782393Syz155240 * connection; SYN_SENT -> FIN_WAIT_1 36792393Syz155240 */ 36802393Syz155240 nstate = IPF_TCPS_FIN_WAIT_1; 36812393Syz155240 rval = 1; 36822393Syz155240 } else if ((tcpflags & TH_OPENING) == TH_OPENING) { 36832393Syz155240 /* 36842393Syz155240 * we see an SA from 'dir' which is already in 36852393Syz155240 * SYN_SENT state, this means we have a 36862393Syz155240 * simultaneous open; SYN_SENT -> SYN_RECEIVED 36872393Syz155240 */ 36882393Syz155240 nstate = IPF_TCPS_SYN_RECEIVED; 36892393Syz155240 rval = 1; 36902393Syz155240 } 36912393Syz155240 break; 36922393Syz155240 36939888SAlexandr.Nedvedicky@Sun.COM case IPF_TCPS_SYN_RECEIVED: /* 2 */ 36942393Syz155240 if ((tcpflags & (TH_SYN|TH_FIN|TH_ACK)) == TH_ACK) { 36952393Syz155240 /* 36962393Syz155240 * we see an A from 'dir' which was in 36972393Syz155240 * SYN_RECEIVED state so it must now be in 36982393Syz155240 * established state, SYN_RECEIVED -> 36992393Syz155240 * ESTABLISHED 37002393Syz155240 */ 37012393Syz155240 nstate = IPF_TCPS_ESTABLISHED; 37022393Syz155240 rval = 1; 37032393Syz155240 } else if ((tcpflags & ~(TH_ECN|TH_CWR)) == 37042393Syz155240 TH_OPENING) { 37052393Syz155240 /* 37062393Syz155240 * We see an SA from 'dir' which is already in 37072393Syz155240 * SYN_RECEIVED state. 37082393Syz155240 */ 37092393Syz155240 rval = 2; 37102393Syz155240 } else if (tcpflags & TH_FIN) { 37112393Syz155240 /* 37122393Syz155240 * we see an F from 'dir' which is in 37132393Syz155240 * SYN_RECEIVED state and wants to close its 37142393Syz155240 * side of the connection; SYN_RECEIVED -> 37152393Syz155240 * FIN_WAIT_1 37162393Syz155240 */ 37172393Syz155240 nstate = IPF_TCPS_FIN_WAIT_1; 37182393Syz155240 rval = 1; 37192393Syz155240 } 37202393Syz155240 break; 37212393Syz155240 37229888SAlexandr.Nedvedicky@Sun.COM case IPF_TCPS_HALF_ESTAB: /* 3 */ 37239888SAlexandr.Nedvedicky@Sun.COM if (tcpflags & TH_FIN) { 37249888SAlexandr.Nedvedicky@Sun.COM nstate = IPF_TCPS_FIN_WAIT_1; 37259888SAlexandr.Nedvedicky@Sun.COM rval = 1; 37269888SAlexandr.Nedvedicky@Sun.COM } else if ((tcpflags & TH_ACKMASK) == TH_ACK) { 37279888SAlexandr.Nedvedicky@Sun.COM /* 37289888SAlexandr.Nedvedicky@Sun.COM * If we've picked up a connection in mid 37299888SAlexandr.Nedvedicky@Sun.COM * flight, we could be looking at a follow on 37309888SAlexandr.Nedvedicky@Sun.COM * packet from the same direction as the one 37319888SAlexandr.Nedvedicky@Sun.COM * that created this state. Recognise it but 37329888SAlexandr.Nedvedicky@Sun.COM * do not advance the entire connection's 37339888SAlexandr.Nedvedicky@Sun.COM * state. 37349888SAlexandr.Nedvedicky@Sun.COM */ 37359888SAlexandr.Nedvedicky@Sun.COM switch (ostate) 37369888SAlexandr.Nedvedicky@Sun.COM { 37379888SAlexandr.Nedvedicky@Sun.COM case IPF_TCPS_LISTEN : 37389888SAlexandr.Nedvedicky@Sun.COM case IPF_TCPS_SYN_SENT : 37399888SAlexandr.Nedvedicky@Sun.COM case IPF_TCPS_SYN_RECEIVED : 37409888SAlexandr.Nedvedicky@Sun.COM rval = 1; 37419888SAlexandr.Nedvedicky@Sun.COM break; 37429888SAlexandr.Nedvedicky@Sun.COM case IPF_TCPS_HALF_ESTAB : 37439888SAlexandr.Nedvedicky@Sun.COM case IPF_TCPS_ESTABLISHED : 37442393Syz155240 nstate = IPF_TCPS_ESTABLISHED; 37459888SAlexandr.Nedvedicky@Sun.COM rval = 1; 37469888SAlexandr.Nedvedicky@Sun.COM break; 37479888SAlexandr.Nedvedicky@Sun.COM default : 37489888SAlexandr.Nedvedicky@Sun.COM break; 37492393Syz155240 } 37502393Syz155240 } 37512393Syz155240 break; 37522393Syz155240 37539888SAlexandr.Nedvedicky@Sun.COM case IPF_TCPS_ESTABLISHED: /* 4 */ 37542393Syz155240 rval = 1; 37552393Syz155240 if (tcpflags & TH_FIN) { 37562393Syz155240 /* 37572393Syz155240 * 'dir' closed its side of the connection; 37582393Syz155240 * this gives us a half-closed connection; 37592393Syz155240 * ESTABLISHED -> FIN_WAIT_1 37602393Syz155240 */ 37619888SAlexandr.Nedvedicky@Sun.COM if (ostate == IPF_TCPS_FIN_WAIT_1) { 37629888SAlexandr.Nedvedicky@Sun.COM nstate = IPF_TCPS_CLOSING; 37639888SAlexandr.Nedvedicky@Sun.COM } else { 37649888SAlexandr.Nedvedicky@Sun.COM nstate = IPF_TCPS_FIN_WAIT_1; 37659888SAlexandr.Nedvedicky@Sun.COM } 37662393Syz155240 } else if (tcpflags & TH_ACK) { 37672393Syz155240 /* 37682393Syz155240 * an ACK, should we exclude other flags here? 37692393Syz155240 */ 37702393Syz155240 if (ostate == IPF_TCPS_FIN_WAIT_1) { 37712393Syz155240 /* 37722393Syz155240 * We know the other side did an active 37732393Syz155240 * close, so we are ACKing the recvd 37742393Syz155240 * FIN packet (does the window matching 37752393Syz155240 * code guarantee this?) and go into 37762393Syz155240 * CLOSE_WAIT state; this gives us a 37772393Syz155240 * half-closed connection 37782393Syz155240 */ 37792393Syz155240 nstate = IPF_TCPS_CLOSE_WAIT; 37802393Syz155240 } else if (ostate < IPF_TCPS_CLOSE_WAIT) { 37812393Syz155240 /* 37822393Syz155240 * still a fully established 37832393Syz155240 * connection reset timeout 37842393Syz155240 */ 37852393Syz155240 nstate = IPF_TCPS_ESTABLISHED; 37862393Syz155240 } 37872393Syz155240 } 37882393Syz155240 break; 37892393Syz155240 37909888SAlexandr.Nedvedicky@Sun.COM case IPF_TCPS_CLOSE_WAIT: /* 5 */ 37912393Syz155240 rval = 1; 37922393Syz155240 if (tcpflags & TH_FIN) { 37932393Syz155240 /* 37942393Syz155240 * application closed and 'dir' sent a FIN, 37952393Syz155240 * we're now going into LAST_ACK state 37962393Syz155240 */ 37972393Syz155240 nstate = IPF_TCPS_LAST_ACK; 37982393Syz155240 } else { 37992393Syz155240 /* 38002393Syz155240 * we remain in CLOSE_WAIT because the other 38012393Syz155240 * side has closed already and we did not 38022393Syz155240 * close our side yet; reset timeout 38032393Syz155240 */ 38042393Syz155240 nstate = IPF_TCPS_CLOSE_WAIT; 38052393Syz155240 } 38062393Syz155240 break; 38072393Syz155240 38089888SAlexandr.Nedvedicky@Sun.COM case IPF_TCPS_FIN_WAIT_1: /* 6 */ 38092393Syz155240 rval = 1; 38102393Syz155240 if ((tcpflags & TH_ACK) && 38112393Syz155240 ostate > IPF_TCPS_CLOSE_WAIT) { 38122393Syz155240 /* 38132393Syz155240 * if the other side is not active anymore 38142393Syz155240 * it has sent us a FIN packet that we are 38152393Syz155240 * ack'ing now with an ACK; this means both 38162393Syz155240 * sides have now closed the connection and 38179888SAlexandr.Nedvedicky@Sun.COM * we go into LAST_ACK 38182393Syz155240 */ 38192393Syz155240 /* 38202393Syz155240 * XXX: how do we know we really are ACKing 38212393Syz155240 * the FIN packet here? does the window code 38222393Syz155240 * guarantee that? 38232393Syz155240 */ 38249888SAlexandr.Nedvedicky@Sun.COM nstate = IPF_TCPS_LAST_ACK; 38252393Syz155240 } else { 38262393Syz155240 /* 38272393Syz155240 * we closed our side of the connection 38282393Syz155240 * already but the other side is still active 38292393Syz155240 * (ESTABLISHED/CLOSE_WAIT); continue with 38302393Syz155240 * this half-closed connection 38312393Syz155240 */ 38322393Syz155240 nstate = IPF_TCPS_FIN_WAIT_1; 38332393Syz155240 } 38342393Syz155240 break; 38352393Syz155240 38369888SAlexandr.Nedvedicky@Sun.COM case IPF_TCPS_CLOSING: /* 7 */ 38379888SAlexandr.Nedvedicky@Sun.COM if ((tcpflags & (TH_FIN|TH_ACK)) == TH_ACK) { 38389888SAlexandr.Nedvedicky@Sun.COM nstate = IPF_TCPS_TIME_WAIT; 38399888SAlexandr.Nedvedicky@Sun.COM } 38409888SAlexandr.Nedvedicky@Sun.COM rval = 1; 38412393Syz155240 break; 38422393Syz155240 38439888SAlexandr.Nedvedicky@Sun.COM case IPF_TCPS_LAST_ACK: /* 8 */ 38442393Syz155240 /* 38456647San207044 * We want to reset timer here to keep state in table. 38466647San207044 * If we would allow the state to time out here, while 38476647San207044 * there would still be packets being retransmitted, we 38486647San207044 * would cut off line between the two peers preventing 38496647San207044 * them to close connection properly. 38502393Syz155240 */ 38516647San207044 rval = 1; 38522393Syz155240 break; 38532393Syz155240 38549888SAlexandr.Nedvedicky@Sun.COM case IPF_TCPS_FIN_WAIT_2: /* 9 */ 38559888SAlexandr.Nedvedicky@Sun.COM /* NOT USED */ 38562393Syz155240 break; 38572393Syz155240 38589888SAlexandr.Nedvedicky@Sun.COM case IPF_TCPS_TIME_WAIT: /* 10 */ 38592393Syz155240 /* we're in 2MSL timeout now */ 38609888SAlexandr.Nedvedicky@Sun.COM if (ostate == IPF_TCPS_LAST_ACK) { 38619888SAlexandr.Nedvedicky@Sun.COM nstate = IPF_TCPS_CLOSED; 38629888SAlexandr.Nedvedicky@Sun.COM rval = 1; 38639888SAlexandr.Nedvedicky@Sun.COM } else { 38649888SAlexandr.Nedvedicky@Sun.COM rval = 2; 38659888SAlexandr.Nedvedicky@Sun.COM } 38669888SAlexandr.Nedvedicky@Sun.COM break; 38679888SAlexandr.Nedvedicky@Sun.COM 38689888SAlexandr.Nedvedicky@Sun.COM case IPF_TCPS_CLOSED: /* 11 */ 38699888SAlexandr.Nedvedicky@Sun.COM rval = 2; 38702393Syz155240 break; 38712393Syz155240 38722393Syz155240 default : 38732393Syz155240 #if defined(_KERNEL) 38749888SAlexandr.Nedvedicky@Sun.COM ASSERT(nstate >= IPF_TCPS_LISTEN && 38759888SAlexandr.Nedvedicky@Sun.COM nstate <= IPF_TCPS_CLOSED); 38762393Syz155240 #else 38772393Syz155240 abort(); 38782393Syz155240 #endif 38792393Syz155240 break; 38802393Syz155240 } 38812393Syz155240 } 38822393Syz155240 38832393Syz155240 /* 38842393Syz155240 * If rval == 2 then do not update the queue position, but treat the 38852393Syz155240 * packet as being ok. 38862393Syz155240 */ 38879888SAlexandr.Nedvedicky@Sun.COM if (rval == 2) { 38889888SAlexandr.Nedvedicky@Sun.COM DTRACE_PROBE1(state_keeping_timer, int, nstate); 38892393Syz155240 rval = 1; 38909888SAlexandr.Nedvedicky@Sun.COM } 38912393Syz155240 else if (rval == 1) { 38922393Syz155240 tqe->tqe_state[dir] = nstate; 38939888SAlexandr.Nedvedicky@Sun.COM /* 38949888SAlexandr.Nedvedicky@Sun.COM * The nstate can either advance to a new state, or remain 38959888SAlexandr.Nedvedicky@Sun.COM * unchanged, resetting the timer by moving to the bottom of 38969888SAlexandr.Nedvedicky@Sun.COM * the queue. 38979888SAlexandr.Nedvedicky@Sun.COM */ 38989888SAlexandr.Nedvedicky@Sun.COM DTRACE_PROBE1(state_done, int, nstate); 38999888SAlexandr.Nedvedicky@Sun.COM 39002393Syz155240 if ((tqe->tqe_flags & TQE_RULEBASED) == 0) 39013448Sdh155122 fr_movequeue(tqe, tqe->tqe_ifq, tqtab + nstate, ifs); 39022393Syz155240 } 39032393Syz155240 39042393Syz155240 return rval; 39052393Syz155240 } 39062393Syz155240 39072393Syz155240 39082393Syz155240 /* ------------------------------------------------------------------------ */ 39092393Syz155240 /* Function: ipstate_log */ 39102393Syz155240 /* Returns: Nil */ 39112393Syz155240 /* Parameters: is(I) - pointer to state structure */ 39122393Syz155240 /* type(I) - type of log entry to create */ 39132393Syz155240 /* */ 39142393Syz155240 /* Creates a state table log entry using the state structure and type info. */ 39152393Syz155240 /* passed in. Log packet/byte counts, source/destination address and other */ 39162393Syz155240 /* protocol specific information. */ 39172393Syz155240 /* ------------------------------------------------------------------------ */ 39183448Sdh155122 void ipstate_log(is, type, ifs) 39192393Syz155240 struct ipstate *is; 39202393Syz155240 u_int type; 39213448Sdh155122 ipf_stack_t *ifs; 39222393Syz155240 { 39232393Syz155240 #ifdef IPFILTER_LOG 39242393Syz155240 struct ipslog ipsl; 39252393Syz155240 size_t sizes[1]; 39262393Syz155240 void *items[1]; 39272393Syz155240 int types[1]; 39282393Syz155240 39292393Syz155240 /* 39302393Syz155240 * Copy information out of the ipstate_t structure and into the 39312393Syz155240 * structure used for logging. 39322393Syz155240 */ 39332393Syz155240 ipsl.isl_type = type; 39342393Syz155240 ipsl.isl_pkts[0] = is->is_pkts[0] + is->is_icmppkts[0]; 39352393Syz155240 ipsl.isl_bytes[0] = is->is_bytes[0]; 39362393Syz155240 ipsl.isl_pkts[1] = is->is_pkts[1] + is->is_icmppkts[1]; 39372393Syz155240 ipsl.isl_bytes[1] = is->is_bytes[1]; 39382393Syz155240 ipsl.isl_pkts[2] = is->is_pkts[2] + is->is_icmppkts[2]; 39392393Syz155240 ipsl.isl_bytes[2] = is->is_bytes[2]; 39402393Syz155240 ipsl.isl_pkts[3] = is->is_pkts[3] + is->is_icmppkts[3]; 39412393Syz155240 ipsl.isl_bytes[3] = is->is_bytes[3]; 39422393Syz155240 ipsl.isl_src = is->is_src; 39432393Syz155240 ipsl.isl_dst = is->is_dst; 39442393Syz155240 ipsl.isl_p = is->is_p; 39452393Syz155240 ipsl.isl_v = is->is_v; 39462393Syz155240 ipsl.isl_flags = is->is_flags; 39472393Syz155240 ipsl.isl_tag = is->is_tag; 39482393Syz155240 ipsl.isl_rulen = is->is_rulen; 39492393Syz155240 (void) strncpy(ipsl.isl_group, is->is_group, FR_GROUPLEN); 39502393Syz155240 39512393Syz155240 if (ipsl.isl_p == IPPROTO_TCP || ipsl.isl_p == IPPROTO_UDP) { 39522393Syz155240 ipsl.isl_sport = is->is_sport; 39532393Syz155240 ipsl.isl_dport = is->is_dport; 39542393Syz155240 if (ipsl.isl_p == IPPROTO_TCP) { 39552393Syz155240 ipsl.isl_state[0] = is->is_state[0]; 39562393Syz155240 ipsl.isl_state[1] = is->is_state[1]; 39572393Syz155240 } 39582393Syz155240 } else if (ipsl.isl_p == IPPROTO_ICMP) { 39592393Syz155240 ipsl.isl_itype = is->is_icmp.ici_type; 39602393Syz155240 } else if (ipsl.isl_p == IPPROTO_ICMPV6) { 39612393Syz155240 ipsl.isl_itype = is->is_icmp.ici_type; 39622393Syz155240 } else { 39632393Syz155240 ipsl.isl_ps.isl_filler[0] = 0; 39642393Syz155240 ipsl.isl_ps.isl_filler[1] = 0; 39652393Syz155240 } 39662393Syz155240 39672393Syz155240 items[0] = &ipsl; 39682393Syz155240 sizes[0] = sizeof(ipsl); 39692393Syz155240 types[0] = 0; 39702393Syz155240 39713448Sdh155122 if (ipllog(IPL_LOGSTATE, NULL, items, sizes, types, 1, ifs)) { 39723448Sdh155122 ATOMIC_INCL(ifs->ifs_ips_stats.iss_logged); 39732393Syz155240 } else { 39743448Sdh155122 ATOMIC_INCL(ifs->ifs_ips_stats.iss_logfail); 39752393Syz155240 } 39762393Syz155240 #endif 39772393Syz155240 } 39782393Syz155240 39792393Syz155240 39802393Syz155240 #ifdef USE_INET6 39812393Syz155240 /* ------------------------------------------------------------------------ */ 39822393Syz155240 /* Function: fr_checkicmp6matchingstate */ 39832393Syz155240 /* Returns: ipstate_t* - NULL == no match found, */ 39842393Syz155240 /* else pointer to matching state entry */ 39852393Syz155240 /* Parameters: fin(I) - pointer to packet information */ 39862393Syz155240 /* Locks: NULL == no locks, else Read Lock on ipf_state */ 39872393Syz155240 /* */ 39882393Syz155240 /* If we've got an ICMPv6 error message, using the information stored in */ 39892393Syz155240 /* the ICMPv6 packet, look for a matching state table entry. */ 39902393Syz155240 /* ------------------------------------------------------------------------ */ 39912393Syz155240 static ipstate_t *fr_checkicmp6matchingstate(fin) 39922393Syz155240 fr_info_t *fin; 39932393Syz155240 { 39942393Syz155240 struct icmp6_hdr *ic6, *oic; 39952393Syz155240 int backward, i; 39962393Syz155240 ipstate_t *is, **isp; 39972393Syz155240 u_short sport, dport; 39982393Syz155240 i6addr_t dst, src; 39992393Syz155240 u_short savelen; 40002393Syz155240 icmpinfo_t *ic; 40012393Syz155240 fr_info_t ofin; 40022393Syz155240 tcphdr_t *tcp; 40032393Syz155240 ip6_t *oip6; 40042393Syz155240 u_char pr; 40052393Syz155240 u_int hv; 40063448Sdh155122 ipf_stack_t *ifs = fin->fin_ifs; 40072393Syz155240 40082393Syz155240 /* 40092393Syz155240 * Does it at least have the return (basic) IP header ? 40102393Syz155240 * Is it an actual recognised ICMP error type? 40112393Syz155240 * Only a basic IP header (no options) should be with 40122393Syz155240 * an ICMP error header. 40132393Syz155240 */ 40142393Syz155240 if ((fin->fin_v != 6) || (fin->fin_plen < ICMP6ERR_MINPKTLEN) || 40152393Syz155240 !(fin->fin_flx & FI_ICMPERR)) 40162393Syz155240 return NULL; 40172393Syz155240 40182393Syz155240 ic6 = fin->fin_dp; 40192393Syz155240 40202393Syz155240 oip6 = (ip6_t *)((char *)ic6 + ICMPERR_ICMPHLEN); 40212393Syz155240 if (fin->fin_plen < sizeof(*oip6)) 40222393Syz155240 return NULL; 40232393Syz155240 40243607Szf203873 bcopy((char *)fin, (char *)&ofin, sizeof(*fin)); 40252393Syz155240 ofin.fin_v = 6; 40262393Syz155240 ofin.fin_ifp = fin->fin_ifp; 40272393Syz155240 ofin.fin_out = !fin->fin_out; 40282393Syz155240 ofin.fin_m = NULL; /* if dereferenced, panic XXX */ 40292393Syz155240 ofin.fin_mp = NULL; /* if dereferenced, panic XXX */ 40302393Syz155240 40312393Syz155240 /* 40322393Syz155240 * We make a fin entry to be able to feed it to 40332393Syz155240 * matchsrcdst. Note that not all fields are necessary 40342393Syz155240 * but this is the cleanest way. Note further we fill 40352393Syz155240 * in fin_mp such that if someone uses it we'll get 40362393Syz155240 * a kernel panic. fr_matchsrcdst does not use this. 40372393Syz155240 * 40382393Syz155240 * watch out here, as ip is in host order and oip6 in network 40392393Syz155240 * order. Any change we make must be undone afterwards. 40402393Syz155240 */ 40412393Syz155240 savelen = oip6->ip6_plen; 40422393Syz155240 oip6->ip6_plen = fin->fin_dlen - ICMPERR_ICMPHLEN; 40432393Syz155240 ofin.fin_flx = FI_NOCKSUM; 40442393Syz155240 ofin.fin_ip = (ip_t *)oip6; 40452393Syz155240 ofin.fin_plen = oip6->ip6_plen; 40462393Syz155240 (void) fr_makefrip(sizeof(*oip6), (ip_t *)oip6, &ofin); 40472393Syz155240 ofin.fin_flx &= ~(FI_BAD|FI_SHORT); 40482393Syz155240 oip6->ip6_plen = savelen; 40492393Syz155240 40502393Syz155240 if (oip6->ip6_nxt == IPPROTO_ICMPV6) { 40512393Syz155240 oic = (struct icmp6_hdr *)(oip6 + 1); 40522393Syz155240 /* 40532393Syz155240 * an ICMP error can only be generated as a result of an 40542393Syz155240 * ICMP query, not as the response on an ICMP error 40552393Syz155240 * 40562393Syz155240 * XXX theoretically ICMP_ECHOREP and the other reply's are 40572393Syz155240 * ICMP query's as well, but adding them here seems strange XXX 40582393Syz155240 */ 40592393Syz155240 if (!(oic->icmp6_type & ICMP6_INFOMSG_MASK)) 40602393Syz155240 return NULL; 40612393Syz155240 40622393Syz155240 /* 40632393Syz155240 * perform a lookup of the ICMP packet in the state table 40642393Syz155240 */ 40652393Syz155240 hv = (pr = oip6->ip6_nxt); 40662393Syz155240 src.in6 = oip6->ip6_src; 40672393Syz155240 hv += src.in4.s_addr; 40682393Syz155240 dst.in6 = oip6->ip6_dst; 40692393Syz155240 hv += dst.in4.s_addr; 40702393Syz155240 hv += oic->icmp6_id; 40712393Syz155240 hv += oic->icmp6_seq; 40723448Sdh155122 hv = DOUBLE_HASH(hv, ifs); 40733448Sdh155122 40743448Sdh155122 READ_ENTER(&ifs->ifs_ipf_state); 40753448Sdh155122 for (isp = &ifs->ifs_ips_table[hv]; ((is = *isp) != NULL); ) { 40762393Syz155240 ic = &is->is_icmp; 40772393Syz155240 isp = &is->is_hnext; 40782393Syz155240 if ((is->is_p == pr) && 40792393Syz155240 !(is->is_pass & FR_NOICMPERR) && 40802393Syz155240 (oic->icmp6_id == ic->ici_id) && 40812393Syz155240 (oic->icmp6_seq == ic->ici_seq) && 40822393Syz155240 (is = fr_matchsrcdst(&ofin, is, &src, 40832393Syz155240 &dst, NULL, FI_ICMPCMP))) { 40842393Syz155240 /* 40852393Syz155240 * in the state table ICMP query's are stored 40862393Syz155240 * with the type of the corresponding ICMP 40872393Syz155240 * response. Correct here 40882393Syz155240 */ 40892393Syz155240 if (((ic->ici_type == ICMP6_ECHO_REPLY) && 40902393Syz155240 (oic->icmp6_type == ICMP6_ECHO_REQUEST)) || 40912393Syz155240 (ic->ici_type - 1 == oic->icmp6_type )) { 40923448Sdh155122 ifs->ifs_ips_stats.iss_hits++; 40932393Syz155240 backward = IP6_NEQ(&is->is_dst, &src); 40942393Syz155240 fin->fin_rev = !backward; 40952393Syz155240 i = (backward << 1) + fin->fin_out; 40962393Syz155240 is->is_icmppkts[i]++; 40972393Syz155240 return is; 40982393Syz155240 } 40992393Syz155240 } 41002393Syz155240 } 41013448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_state); 41022393Syz155240 return NULL; 41032393Syz155240 } 41042393Syz155240 41052393Syz155240 hv = (pr = oip6->ip6_nxt); 41062393Syz155240 src.in6 = oip6->ip6_src; 41072393Syz155240 hv += src.i6[0]; 41082393Syz155240 hv += src.i6[1]; 41092393Syz155240 hv += src.i6[2]; 41102393Syz155240 hv += src.i6[3]; 41112393Syz155240 dst.in6 = oip6->ip6_dst; 41122393Syz155240 hv += dst.i6[0]; 41132393Syz155240 hv += dst.i6[1]; 41142393Syz155240 hv += dst.i6[2]; 41152393Syz155240 hv += dst.i6[3]; 41162393Syz155240 41172393Syz155240 if ((oip6->ip6_nxt == IPPROTO_TCP) || (oip6->ip6_nxt == IPPROTO_UDP)) { 41182393Syz155240 tcp = (tcphdr_t *)(oip6 + 1); 41192393Syz155240 dport = tcp->th_dport; 41202393Syz155240 sport = tcp->th_sport; 41212393Syz155240 hv += dport; 41222393Syz155240 hv += sport; 41232393Syz155240 } else 41242393Syz155240 tcp = NULL; 41253448Sdh155122 hv = DOUBLE_HASH(hv, ifs); 41263448Sdh155122 41273448Sdh155122 READ_ENTER(&ifs->ifs_ipf_state); 41283448Sdh155122 for (isp = &ifs->ifs_ips_table[hv]; ((is = *isp) != NULL); ) { 41292393Syz155240 isp = &is->is_hnext; 41302393Syz155240 /* 41312393Syz155240 * Only allow this icmp though if the 41322393Syz155240 * encapsulated packet was allowed through the 41332393Syz155240 * other way around. Note that the minimal amount 41342393Syz155240 * of info present does not allow for checking against 41352393Syz155240 * tcp internals such as seq and ack numbers. 41362393Syz155240 */ 41372393Syz155240 if ((is->is_p != pr) || (is->is_v != 6) || 41382393Syz155240 (is->is_pass & FR_NOICMPERR)) 41392393Syz155240 continue; 41402393Syz155240 is = fr_matchsrcdst(&ofin, is, &src, &dst, tcp, FI_ICMPCMP); 41412393Syz155240 if (is != NULL) { 41423448Sdh155122 ifs->ifs_ips_stats.iss_hits++; 41432393Syz155240 backward = IP6_NEQ(&is->is_dst, &src); 41442393Syz155240 fin->fin_rev = !backward; 41452393Syz155240 i = (backward << 1) + fin->fin_out; 41462393Syz155240 is->is_icmppkts[i]++; 41472393Syz155240 /* 41482393Syz155240 * we deliberately do not touch the timeouts 41492393Syz155240 * for the accompanying state table entry. 41502393Syz155240 * It remains to be seen if that is correct. XXX 41512393Syz155240 */ 41522393Syz155240 return is; 41532393Syz155240 } 41542393Syz155240 } 41553448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_state); 41562393Syz155240 return NULL; 41572393Syz155240 } 41582393Syz155240 #endif 41592393Syz155240 41602393Syz155240 41612393Syz155240 /* ------------------------------------------------------------------------ */ 41622393Syz155240 /* Function: fr_sttab_init */ 41632393Syz155240 /* Returns: Nil */ 41642393Syz155240 /* Parameters: tqp(I) - pointer to an array of timeout queues for TCP */ 41652393Syz155240 /* */ 41662393Syz155240 /* Initialise the array of timeout queues for TCP. */ 41672393Syz155240 /* ------------------------------------------------------------------------ */ 41683448Sdh155122 void fr_sttab_init(tqp, ifs) 41692393Syz155240 ipftq_t *tqp; 41703448Sdh155122 ipf_stack_t *ifs; 41712393Syz155240 { 41722393Syz155240 int i; 41732393Syz155240 41742393Syz155240 for (i = IPF_TCP_NSTATES - 1; i >= 0; i--) { 41752393Syz155240 tqp[i].ifq_ttl = 0; 41762393Syz155240 tqp[i].ifq_ref = 1; 41772393Syz155240 tqp[i].ifq_head = NULL; 41782393Syz155240 tqp[i].ifq_tail = &tqp[i].ifq_head; 41792393Syz155240 tqp[i].ifq_next = tqp + i + 1; 41802393Syz155240 MUTEX_INIT(&tqp[i].ifq_lock, "ipftq tcp tab"); 41812393Syz155240 } 41822393Syz155240 tqp[IPF_TCP_NSTATES - 1].ifq_next = NULL; 41833448Sdh155122 tqp[IPF_TCPS_CLOSED].ifq_ttl = ifs->ifs_fr_tcpclosed; 41843448Sdh155122 tqp[IPF_TCPS_LISTEN].ifq_ttl = ifs->ifs_fr_tcptimeout; 41853448Sdh155122 tqp[IPF_TCPS_SYN_SENT].ifq_ttl = ifs->ifs_fr_tcptimeout; 41863448Sdh155122 tqp[IPF_TCPS_SYN_RECEIVED].ifq_ttl = ifs->ifs_fr_tcptimeout; 41873448Sdh155122 tqp[IPF_TCPS_ESTABLISHED].ifq_ttl = ifs->ifs_fr_tcpidletimeout; 41883448Sdh155122 tqp[IPF_TCPS_CLOSE_WAIT].ifq_ttl = ifs->ifs_fr_tcphalfclosed; 41893448Sdh155122 tqp[IPF_TCPS_FIN_WAIT_1].ifq_ttl = ifs->ifs_fr_tcphalfclosed; 41903448Sdh155122 tqp[IPF_TCPS_CLOSING].ifq_ttl = ifs->ifs_fr_tcptimeout; 41913448Sdh155122 tqp[IPF_TCPS_LAST_ACK].ifq_ttl = ifs->ifs_fr_tcplastack; 41923448Sdh155122 tqp[IPF_TCPS_FIN_WAIT_2].ifq_ttl = ifs->ifs_fr_tcpclosewait; 41933448Sdh155122 tqp[IPF_TCPS_TIME_WAIT].ifq_ttl = ifs->ifs_fr_tcptimeout; 41943448Sdh155122 tqp[IPF_TCPS_HALF_ESTAB].ifq_ttl = ifs->ifs_fr_tcptimeout; 41952393Syz155240 } 41962393Syz155240 41972393Syz155240 41982393Syz155240 /* ------------------------------------------------------------------------ */ 41992393Syz155240 /* Function: fr_sttab_destroy */ 42002393Syz155240 /* Returns: Nil */ 42012393Syz155240 /* Parameters: tqp(I) - pointer to an array of timeout queues for TCP */ 42022393Syz155240 /* */ 42032393Syz155240 /* Do whatever is necessary to "destroy" each of the entries in the array */ 42042393Syz155240 /* of timeout queues for TCP. */ 42052393Syz155240 /* ------------------------------------------------------------------------ */ 42062393Syz155240 void fr_sttab_destroy(tqp) 42072393Syz155240 ipftq_t *tqp; 42082393Syz155240 { 42092393Syz155240 int i; 42102393Syz155240 42112393Syz155240 for (i = IPF_TCP_NSTATES - 1; i >= 0; i--) 42122393Syz155240 MUTEX_DESTROY(&tqp[i].ifq_lock); 42132393Syz155240 } 42142393Syz155240 42152393Syz155240 42162393Syz155240 /* ------------------------------------------------------------------------ */ 42172393Syz155240 /* Function: fr_statederef */ 42182393Syz155240 /* Returns: Nil */ 42192393Syz155240 /* Parameters: isp(I) - pointer to pointer to state table entry */ 42205417Sjojemann /* ifs - ipf stack instance */ 42212393Syz155240 /* */ 42222393Syz155240 /* Decrement the reference counter for this state table entry and free it */ 42232393Syz155240 /* if there are no more things using it. */ 42242393Syz155240 /* */ 42252393Syz155240 /* Internal parameters: */ 42262393Syz155240 /* state[0] = state of source (host that initiated connection) */ 42272393Syz155240 /* state[1] = state of dest (host that accepted the connection) */ 42282393Syz155240 /* ------------------------------------------------------------------------ */ 42295417Sjojemann void fr_statederef(isp, ifs) 42302393Syz155240 ipstate_t **isp; 42313448Sdh155122 ipf_stack_t *ifs; 42322393Syz155240 { 42335417Sjojemann ipstate_t *is; 42345417Sjojemann 42352393Syz155240 is = *isp; 42362393Syz155240 *isp = NULL; 42375055Sdr146992 42385055Sdr146992 MUTEX_ENTER(&is->is_lock); 42395055Sdr146992 if (is->is_ref > 1) { 42405055Sdr146992 is->is_ref--; 42415055Sdr146992 MUTEX_EXIT(&is->is_lock); 42422393Syz155240 #ifndef _KERNEL 42435055Sdr146992 if ((is->is_sti.tqe_state[0] > IPF_TCPS_ESTABLISHED) || 42442393Syz155240 (is->is_sti.tqe_state[1] > IPF_TCPS_ESTABLISHED)) { 42458170SJohn.Ojemann@Sun.COM (void) fr_delstate(is, ISL_ORPHAN, ifs); 42465055Sdr146992 } 42472393Syz155240 #endif 42485055Sdr146992 return; 42492393Syz155240 } 42505055Sdr146992 MUTEX_EXIT(&is->is_lock); 42515055Sdr146992 42525055Sdr146992 WRITE_ENTER(&ifs->ifs_ipf_state); 42538170SJohn.Ojemann@Sun.COM (void) fr_delstate(is, ISL_EXPIRE, ifs); 42543448Sdh155122 RWLOCK_EXIT(&ifs->ifs_ipf_state); 42552393Syz155240 } 42562393Syz155240 42572393Syz155240 42582393Syz155240 /* ------------------------------------------------------------------------ */ 42592393Syz155240 /* Function: fr_setstatequeue */ 42602393Syz155240 /* Returns: Nil */ 42612393Syz155240 /* Parameters: is(I) - pointer to state structure */ 42622393Syz155240 /* rev(I) - forward(0) or reverse(1) direction */ 42632393Syz155240 /* Locks: ipf_state (read or write) */ 42642393Syz155240 /* */ 42652393Syz155240 /* Put the state entry on its default queue entry, using rev as a helped in */ 42662393Syz155240 /* determining which queue it should be placed on. */ 42672393Syz155240 /* ------------------------------------------------------------------------ */ 42683448Sdh155122 void fr_setstatequeue(is, rev, ifs) 42692393Syz155240 ipstate_t *is; 42702393Syz155240 int rev; 42713448Sdh155122 ipf_stack_t *ifs; 42722393Syz155240 { 42732393Syz155240 ipftq_t *oifq, *nifq; 42742393Syz155240 42752393Syz155240 42762393Syz155240 if ((is->is_sti.tqe_flags & TQE_RULEBASED) != 0) 42772393Syz155240 nifq = is->is_tqehead[rev]; 42782393Syz155240 else 42792393Syz155240 nifq = NULL; 42802393Syz155240 42812393Syz155240 if (nifq == NULL) { 42822393Syz155240 switch (is->is_p) 42832393Syz155240 { 42842393Syz155240 #ifdef USE_INET6 42852393Syz155240 case IPPROTO_ICMPV6 : 42862393Syz155240 if (rev == 1) 42873448Sdh155122 nifq = &ifs->ifs_ips_icmpacktq; 42882393Syz155240 else 42893448Sdh155122 nifq = &ifs->ifs_ips_icmptq; 42902393Syz155240 break; 42912393Syz155240 #endif 42922393Syz155240 case IPPROTO_ICMP : 42932393Syz155240 if (rev == 1) 42943448Sdh155122 nifq = &ifs->ifs_ips_icmpacktq; 42952393Syz155240 else 42963448Sdh155122 nifq = &ifs->ifs_ips_icmptq; 42972393Syz155240 break; 42982393Syz155240 case IPPROTO_TCP : 42993448Sdh155122 nifq = ifs->ifs_ips_tqtqb + is->is_state[rev]; 43002393Syz155240 break; 43012393Syz155240 43022393Syz155240 case IPPROTO_UDP : 43032393Syz155240 if (rev == 1) 43043448Sdh155122 nifq = &ifs->ifs_ips_udpacktq; 43052393Syz155240 else 43063448Sdh155122 nifq = &ifs->ifs_ips_udptq; 43072393Syz155240 break; 43082393Syz155240 43092393Syz155240 default : 43103448Sdh155122 nifq = &ifs->ifs_ips_iptq; 43112393Syz155240 break; 43122393Syz155240 } 43132393Syz155240 } 43142393Syz155240 43152393Syz155240 oifq = is->is_sti.tqe_ifq; 43162393Syz155240 /* 43172393Syz155240 * If it's currently on a timeout queue, move it from one queue to 43182393Syz155240 * another, else put it on the end of the newly determined queue. 43192393Syz155240 */ 43202393Syz155240 if (oifq != NULL) 43213448Sdh155122 fr_movequeue(&is->is_sti, oifq, nifq, ifs); 43222393Syz155240 else 43233448Sdh155122 fr_queueappend(&is->is_sti, nifq, is, ifs); 43242393Syz155240 return; 43252393Syz155240 } 43263448Sdh155122 43273448Sdh155122 43283448Sdh155122 /* ------------------------------------------------------------------------ */ 43293448Sdh155122 /* Function: fr_stateiter */ 43303448Sdh155122 /* Returns: int - 0 == success, else error */ 43313448Sdh155122 /* Parameters: token(I) - pointer to ipftoken structure */ 43323448Sdh155122 /* itp(I) - pointer to ipfgeniter structure */ 43333448Sdh155122 /* */ 43343448Sdh155122 /* This function handles the SIOCGENITER ioctl for the state tables and */ 43353448Sdh155122 /* walks through the list of entries in the state table list (ips_list.) */ 43363448Sdh155122 /* ------------------------------------------------------------------------ */ 43373448Sdh155122 static int fr_stateiter(token, itp, ifs) 43383448Sdh155122 ipftoken_t *token; 43393448Sdh155122 ipfgeniter_t *itp; 43403448Sdh155122 ipf_stack_t *ifs; 43413448Sdh155122 { 43423448Sdh155122 ipstate_t *is, *next, zero; 43435417Sjojemann int error, count; 43445417Sjojemann char *dst; 43453448Sdh155122 43463448Sdh155122 if (itp->igi_data == NULL) 43473448Sdh155122 return EFAULT; 43483448Sdh155122 43495417Sjojemann if (itp->igi_nitems == 0) 43505417Sjojemann return EINVAL; 43515417Sjojemann 43523448Sdh155122 if (itp->igi_type != IPFGENITER_STATE) 43533448Sdh155122 return EINVAL; 43543448Sdh155122 43555417Sjojemann error = 0; 43565417Sjojemann 43573448Sdh155122 READ_ENTER(&ifs->ifs_ipf_state); 43586518Sjojemann 43596518Sjojemann /* 43606518Sjojemann * Get "previous" entry from the token and find the next entry. 43616518Sjojemann */ 43626518Sjojemann is = token->ipt_data; 43633448Sdh155122 if (is == NULL) { 43643448Sdh155122 next = ifs->ifs_ips_list; 43653448Sdh155122 } else { 43663448Sdh155122 next = is->is_next; 43673448Sdh155122 } 43683448Sdh155122 43696518Sjojemann dst = itp->igi_data; 43705417Sjojemann for (count = itp->igi_nitems; count > 0; count--) { 43716518Sjojemann /* 43726518Sjojemann * If we found an entry, add a reference to it and update the token. 43736518Sjojemann * Otherwise, zero out data to be returned and NULL out token. 43746518Sjojemann */ 43755417Sjojemann if (next != NULL) { 43765417Sjojemann MUTEX_ENTER(&next->is_lock); 43775417Sjojemann next->is_ref++; 43785417Sjojemann MUTEX_EXIT(&next->is_lock); 43795417Sjojemann token->ipt_data = next; 43805417Sjojemann } else { 43815417Sjojemann bzero(&zero, sizeof(zero)); 43825417Sjojemann next = &zero; 43836518Sjojemann token->ipt_data = NULL; 43845417Sjojemann } 43856518Sjojemann 43866518Sjojemann /* 43876518Sjojemann * Safe to release lock now the we have a reference. 43886518Sjojemann */ 43895417Sjojemann RWLOCK_EXIT(&ifs->ifs_ipf_state); 43905417Sjojemann 43913448Sdh155122 /* 43926518Sjojemann * Copy out data and clean up references and tokens. 43935417Sjojemann */ 43945417Sjojemann error = COPYOUT(next, dst, sizeof(*next)); 43955417Sjojemann if (error != 0) 43965417Sjojemann error = EFAULT; 43976518Sjojemann if (token->ipt_data == NULL) { 43986518Sjojemann ipf_freetoken(token, ifs); 43996518Sjojemann break; 44006518Sjojemann } else { 44016518Sjojemann if (is != NULL) 44026518Sjojemann fr_statederef(&is, ifs); 44036518Sjojemann if (next->is_next == NULL) { 44046518Sjojemann ipf_freetoken(token, ifs); 44056518Sjojemann break; 44066518Sjojemann } 44076518Sjojemann } 44086518Sjojemann 44095417Sjojemann if ((count == 1) || (error != 0)) 44105417Sjojemann break; 44115417Sjojemann 44126518Sjojemann READ_ENTER(&ifs->ifs_ipf_state); 44135417Sjojemann dst += sizeof(*next); 44145417Sjojemann is = next; 44155417Sjojemann next = is->is_next; 44163448Sdh155122 } 44173448Sdh155122 44183448Sdh155122 return error; 44193448Sdh155122 } 4420