ip_state.c revision 53642
153642Sguido/* 253642Sguido * Copyright (C) 1995-1998 by Darren Reed. 353642Sguido * 453642Sguido * Redistribution and use in source and binary forms are permitted 553642Sguido * provided that this notice is preserved and due credit is given 653642Sguido * to the original author and the contributors. 753642Sguido */ 853642Sguido#if !defined(lint) 953642Sguidostatic const char sccsid[] = "@(#)ip_state.c 1.8 6/5/96 (C) 1993-1995 Darren Reed"; 1053642Sguido/*static const char rcsid[] = "@(#)$Id: ip_state.c,v 2.3.2.9 1999/10/21 14:31:09 darrenr Exp $";*/ 1153642Sguidostatic const char rcsid[] = "@(#)$FreeBSD: head/sys/contrib/ipfilter/netinet/ip_state.c 53642 1999-11-23 21:44:59Z guido $"; 1253642Sguido#endif 1353642Sguido 1453642Sguido#include <sys/errno.h> 1553642Sguido#include <sys/types.h> 1653642Sguido#include <sys/param.h> 1753642Sguido#include <sys/file.h> 1853642Sguido#if defined(__NetBSD__) && (NetBSD >= 199905) && !defined(IPFILTER_LKM) && \ 1953642Sguido defined(_KERNEL) 2053642Sguido# include "opt_ipfilter_log.h" 2153642Sguido#endif 2253642Sguido#if !defined(_KERNEL) && !defined(KERNEL) && !defined(__KERNEL__) 2353642Sguido# include <stdio.h> 2453642Sguido# include <stdlib.h> 2553642Sguido# include <string.h> 2653642Sguido#else 2753642Sguido# ifdef linux 2853642Sguido# include <linux/kernel.h> 2953642Sguido# include <linux/module.h> 3053642Sguido# endif 3153642Sguido#endif 3253642Sguido#if defined(KERNEL) && (__FreeBSD_version >= 220000) 3353642Sguido# include <sys/filio.h> 3453642Sguido# include <sys/fcntl.h> 3553642Sguido# if (__FreeBSD_version >= 300000) && !defined(IPFILTER_LKM) 3653642Sguido# include "opt_ipfilter.h" 3753642Sguido# endif 3853642Sguido#else 3953642Sguido# include <sys/ioctl.h> 4053642Sguido#endif 4153642Sguido#include <sys/time.h> 4253642Sguido#include <sys/uio.h> 4353642Sguido#ifndef linux 4453642Sguido# include <sys/protosw.h> 4553642Sguido#endif 4653642Sguido#include <sys/socket.h> 4753642Sguido#if defined(_KERNEL) && !defined(linux) 4853642Sguido# include <sys/systm.h> 4953642Sguido#endif 5053642Sguido#if !defined(__SVR4) && !defined(__svr4__) 5153642Sguido# ifndef linux 5253642Sguido# include <sys/mbuf.h> 5353642Sguido# endif 5453642Sguido#else 5553642Sguido# include <sys/filio.h> 5653642Sguido# include <sys/byteorder.h> 5753642Sguido# ifdef _KERNEL 5853642Sguido# include <sys/dditypes.h> 5953642Sguido# endif 6053642Sguido# include <sys/stream.h> 6153642Sguido# include <sys/kmem.h> 6253642Sguido#endif 6353642Sguido 6453642Sguido#include <net/if.h> 6553642Sguido#ifdef sun 6653642Sguido# include <net/af.h> 6753642Sguido#endif 6853642Sguido#include <net/route.h> 6953642Sguido#include <netinet/in.h> 7053642Sguido#include <netinet/in_systm.h> 7153642Sguido#include <netinet/ip.h> 7253642Sguido#include <netinet/tcp.h> 7353642Sguido#ifndef linux 7453642Sguido# include <netinet/ip_var.h> 7553642Sguido# include <netinet/tcp_fsm.h> 7653642Sguido#endif 7753642Sguido#include <netinet/udp.h> 7853642Sguido#include <netinet/ip_icmp.h> 7953642Sguido#include "netinet/ip_compat.h" 8053642Sguido#include <netinet/tcpip.h> 8153642Sguido#include "netinet/ip_fil.h" 8253642Sguido#include "netinet/ip_nat.h" 8353642Sguido#include "netinet/ip_frag.h" 8453642Sguido#include "netinet/ip_proxy.h" 8553642Sguido#include "netinet/ip_state.h" 8653642Sguido#if (__FreeBSD_version >= 300000) 8753642Sguido# include <sys/malloc.h> 8853642Sguido# if (defined(_KERNEL) || defined(KERNEL)) && !defined(IPFILTER_LKM) 8953642Sguido# include <sys/libkern.h> 9053642Sguido# include <sys/systm.h> 9153642Sguido# endif 9253642Sguido#endif 9353642Sguido 9453642Sguido#ifndef MIN 9553642Sguido# define MIN(a,b) (((a)<(b))?(a):(b)) 9653642Sguido#endif 9753642Sguido 9853642Sguido#define TCP_CLOSE (TH_FIN|TH_RST) 9953642Sguido 10053642Sguidostatic ipstate_t **ips_table = NULL; 10153642Sguidostatic int ips_num = 0; 10253642Sguidostatic ips_stat_t ips_stats; 10353642Sguido#if (SOLARIS || defined(__sgi)) && defined(_KERNEL) 10453642Sguidoextern KRWLOCK_T ipf_state, ipf_mutex; 10553642Sguidoextern kmutex_t ipf_rw; 10653642Sguido#endif 10753642Sguido 10853642Sguidostatic int fr_matchsrcdst __P((ipstate_t *, struct in_addr, struct in_addr, 10953642Sguido fr_info_t *, tcphdr_t *)); 11053642Sguidostatic frentry_t *fr_checkicmpmatchingstate __P((ip_t *, fr_info_t *)); 11153642Sguidostatic int fr_state_flush __P((int)); 11253642Sguidostatic ips_stat_t *fr_statetstats __P((void)); 11353642Sguidostatic void fr_delstate __P((ipstate_t *)); 11453642Sguido 11553642Sguido 11653642Sguido#define FIVE_DAYS (2 * 5 * 86400) /* 5 days: half closed session */ 11753642Sguido 11853642Sguido#define TCP_MSL 240 /* 2 minutes */ 11953642Sguidou_long fr_tcpidletimeout = FIVE_DAYS, 12053642Sguido fr_tcpclosewait = 2 * TCP_MSL, 12153642Sguido fr_tcplastack = 2 * TCP_MSL, 12253642Sguido fr_tcptimeout = 2 * TCP_MSL, 12353642Sguido fr_tcpclosed = 1, 12453642Sguido fr_udptimeout = 240, 12553642Sguido fr_icmptimeout = 120; 12653642Sguidoint fr_statemax = IPSTATE_MAX, 12753642Sguido fr_statesize = IPSTATE_SIZE; 12853642Sguidoint fr_state_doflush = 0; 12953642Sguido 13053642Sguido 13153642Sguidoint fr_stateinit() 13253642Sguido{ 13353642Sguido KMALLOCS(ips_table, ipstate_t **, fr_statesize * sizeof(ipstate_t *)); 13453642Sguido if (ips_table != NULL) 13553642Sguido bzero((char *)ips_table, fr_statesize * sizeof(ipstate_t *)); 13653642Sguido else 13753642Sguido return -1; 13853642Sguido return 0; 13953642Sguido} 14053642Sguido 14153642Sguido 14253642Sguidostatic ips_stat_t *fr_statetstats() 14353642Sguido{ 14453642Sguido ips_stats.iss_active = ips_num; 14553642Sguido ips_stats.iss_table = ips_table; 14653642Sguido return &ips_stats; 14753642Sguido} 14853642Sguido 14953642Sguido 15053642Sguido/* 15153642Sguido * flush state tables. two actions currently defined: 15253642Sguido * which == 0 : flush all state table entries 15353642Sguido * which == 1 : flush TCP connections which have started to close but are 15453642Sguido * stuck for some reason. 15553642Sguido */ 15653642Sguidostatic int fr_state_flush(which) 15753642Sguidoint which; 15853642Sguido{ 15953642Sguido register int i; 16053642Sguido register ipstate_t *is, **isp; 16153642Sguido#if defined(_KERNEL) && !SOLARIS 16253642Sguido int s; 16353642Sguido#endif 16453642Sguido int delete, removed = 0; 16553642Sguido 16653642Sguido SPL_NET(s); 16753642Sguido WRITE_ENTER(&ipf_state); 16853642Sguido for (i = fr_statesize - 1; i >= 0; i--) 16953642Sguido for (isp = &ips_table[i]; (is = *isp); ) { 17053642Sguido delete = 0; 17153642Sguido 17253642Sguido switch (which) 17353642Sguido { 17453642Sguido case 0 : 17553642Sguido delete = 1; 17653642Sguido break; 17753642Sguido case 1 : 17853642Sguido if ((is->is_p == IPPROTO_TCP) && 17953642Sguido (((is->is_state[0] <= TCPS_ESTABLISHED) && 18053642Sguido (is->is_state[1] > TCPS_ESTABLISHED)) || 18153642Sguido ((is->is_state[1] <= TCPS_ESTABLISHED) && 18253642Sguido (is->is_state[0] > TCPS_ESTABLISHED)))) 18353642Sguido delete = 1; 18453642Sguido break; 18553642Sguido } 18653642Sguido 18753642Sguido if (delete) { 18853642Sguido *isp = is->is_next; 18953642Sguido if (is->is_p == IPPROTO_TCP) 19053642Sguido ips_stats.iss_fin++; 19153642Sguido else 19253642Sguido ips_stats.iss_expire++; 19353642Sguido if (ips_table[i] == NULL) 19453642Sguido ips_stats.iss_inuse--; 19553642Sguido#ifdef IPFILTER_LOG 19653642Sguido ipstate_log(is, ISL_FLUSH); 19753642Sguido#endif 19853642Sguido fr_delstate(is); 19953642Sguido ips_num--; 20053642Sguido removed++; 20153642Sguido } else 20253642Sguido isp = &is->is_next; 20353642Sguido } 20453642Sguido if (fr_state_doflush) { 20553642Sguido (void) fr_state_flush(1); 20653642Sguido fr_state_doflush = 0; 20753642Sguido } 20853642Sguido RWLOCK_EXIT(&ipf_state); 20953642Sguido SPL_X(s); 21053642Sguido return removed; 21153642Sguido} 21253642Sguido 21353642Sguido 21453642Sguidoint fr_state_ioctl(data, cmd, mode) 21553642Sguidocaddr_t data; 21653642Sguido#if defined(__NetBSD__) || defined(__OpenBSD__) 21753642Sguidou_long cmd; 21853642Sguido#else 21953642Sguidoint cmd; 22053642Sguido#endif 22153642Sguidoint mode; 22253642Sguido{ 22353642Sguido int arg, ret, error = 0; 22453642Sguido 22553642Sguido switch (cmd) 22653642Sguido { 22753642Sguido case SIOCIPFFL : 22853642Sguido IRCOPY(data, (caddr_t)&arg, sizeof(arg)); 22953642Sguido if (arg == 0 || arg == 1) { 23053642Sguido ret = fr_state_flush(arg); 23153642Sguido IWCOPY((caddr_t)&ret, data, sizeof(ret)); 23253642Sguido } else 23353642Sguido error = EINVAL; 23453642Sguido break; 23553642Sguido case SIOCGIPST : 23653642Sguido IWCOPY((caddr_t)fr_statetstats(), data, sizeof(ips_stat_t)); 23753642Sguido break; 23853642Sguido case FIONREAD : 23953642Sguido#ifdef IPFILTER_LOG 24053642Sguido IWCOPY((caddr_t)&iplused[IPL_LOGSTATE], (caddr_t)data, 24153642Sguido sizeof(iplused[IPL_LOGSTATE])); 24253642Sguido#endif 24353642Sguido break; 24453642Sguido default : 24553642Sguido error = EINVAL; 24653642Sguido break; 24753642Sguido } 24853642Sguido return error; 24953642Sguido} 25053642Sguido 25153642Sguido 25253642Sguido/* 25353642Sguido * Create a new ipstate structure and hang it off the hash table. 25453642Sguido */ 25553642Sguidoipstate_t *fr_addstate(ip, fin, flags) 25653642Sguidoip_t *ip; 25753642Sguidofr_info_t *fin; 25853642Sguidou_int flags; 25953642Sguido{ 26053642Sguido register ipstate_t *is; 26153642Sguido register u_int hv; 26253642Sguido ipstate_t ips; 26353642Sguido u_int pass; 26453642Sguido 26553642Sguido if ((ip->ip_off & IP_OFFMASK) || (fin->fin_fi.fi_fl & FI_SHORT)) 26653642Sguido return NULL; 26753642Sguido if (ips_num == fr_statemax) { 26853642Sguido ips_stats.iss_max++; 26953642Sguido fr_state_doflush = 1; 27053642Sguido return NULL; 27153642Sguido } 27253642Sguido is = &ips; 27353642Sguido bzero((char *)is, sizeof(*is)); 27453642Sguido ips.is_age = 1; 27553642Sguido ips.is_state[0] = 0; 27653642Sguido ips.is_state[1] = 0; 27753642Sguido /* 27853642Sguido * Copy and calculate... 27953642Sguido */ 28053642Sguido hv = (is->is_p = ip->ip_p); 28153642Sguido hv += (is->is_src.s_addr = ip->ip_src.s_addr); 28253642Sguido hv += (is->is_dst.s_addr = ip->ip_dst.s_addr); 28353642Sguido 28453642Sguido switch (ip->ip_p) 28553642Sguido { 28653642Sguido case IPPROTO_ICMP : 28753642Sguido { 28853642Sguido struct icmp *ic = (struct icmp *)fin->fin_dp; 28953642Sguido 29053642Sguido switch (ic->icmp_type) 29153642Sguido { 29253642Sguido case ICMP_ECHO : 29353642Sguido is->is_icmp.ics_type = ICMP_ECHOREPLY; /* XXX */ 29453642Sguido hv += (is->is_icmp.ics_id = ic->icmp_id); 29553642Sguido hv += (is->is_icmp.ics_seq = ic->icmp_seq); 29653642Sguido break; 29753642Sguido case ICMP_TSTAMP : 29853642Sguido case ICMP_IREQ : 29953642Sguido case ICMP_MASKREQ : 30053642Sguido is->is_icmp.ics_type = ic->icmp_type + 1; 30153642Sguido break; 30253642Sguido default : 30353642Sguido return NULL; 30453642Sguido } 30553642Sguido ATOMIC_INC(ips_stats.iss_icmp); 30653642Sguido is->is_age = fr_icmptimeout; 30753642Sguido break; 30853642Sguido } 30953642Sguido case IPPROTO_TCP : 31053642Sguido { 31153642Sguido register tcphdr_t *tcp = (tcphdr_t *)fin->fin_dp; 31253642Sguido 31353642Sguido /* 31453642Sguido * The endian of the ports doesn't matter, but the ack and 31553642Sguido * sequence numbers do as we do mathematics on them later. 31653642Sguido */ 31753642Sguido is->is_dport = tcp->th_dport; 31853642Sguido is->is_sport = tcp->th_sport; 31953642Sguido if ((flags & (FI_W_DPORT|FI_W_SPORT)) == 0) { 32053642Sguido hv += tcp->th_dport; 32153642Sguido hv += tcp->th_sport; 32253642Sguido } 32353642Sguido if (tcp->th_seq != 0) { 32453642Sguido is->is_send = ntohl(tcp->th_seq) + ip->ip_len - 32553642Sguido fin->fin_hlen - (tcp->th_off << 2) + 32653642Sguido ((tcp->th_flags & TH_SYN) ? 1 : 0) + 32753642Sguido ((tcp->th_flags & TH_FIN) ? 1 : 0); 32853642Sguido is->is_maxsend = is->is_send + 1; 32953642Sguido } 33053642Sguido is->is_dend = 0; 33153642Sguido is->is_maxswin = ntohs(tcp->th_win); 33253642Sguido if (is->is_maxswin == 0) 33353642Sguido is->is_maxswin = 1; 33453642Sguido /* 33553642Sguido * If we're creating state for a starting connection, start the 33653642Sguido * timer on it as we'll never see an error if it fails to 33753642Sguido * connect. 33853642Sguido */ 33953642Sguido MUTEX_ENTER(&ipf_rw); 34053642Sguido ips_stats.iss_tcp++; 34153642Sguido fr_tcp_age(&is->is_age, is->is_state, ip, fin, 34253642Sguido tcp->th_sport == is->is_sport); 34353642Sguido MUTEX_EXIT(&ipf_rw); 34453642Sguido break; 34553642Sguido } 34653642Sguido case IPPROTO_UDP : 34753642Sguido { 34853642Sguido register tcphdr_t *tcp = (tcphdr_t *)fin->fin_dp; 34953642Sguido 35053642Sguido if ((flags & (FI_W_DPORT|FI_W_SPORT)) == 0) { 35153642Sguido hv += (is->is_dport = tcp->th_dport); 35253642Sguido hv += (is->is_sport = tcp->th_sport); 35353642Sguido } 35453642Sguido ATOMIC_INC(ips_stats.iss_udp); 35553642Sguido is->is_age = fr_udptimeout; 35653642Sguido break; 35753642Sguido } 35853642Sguido default : 35953642Sguido return NULL; 36053642Sguido } 36153642Sguido 36253642Sguido KMALLOC(is, ipstate_t *); 36353642Sguido if (is == NULL) { 36453642Sguido ATOMIC_INC(ips_stats.iss_nomem); 36553642Sguido return NULL; 36653642Sguido } 36753642Sguido bcopy((char *)&ips, (char *)is, sizeof(*is)); 36853642Sguido hv %= fr_statesize; 36953642Sguido RW_UPGRADE(&ipf_mutex); 37053642Sguido is->is_rule = fin->fin_fr; 37153642Sguido if (is->is_rule != NULL) { 37253642Sguido is->is_rule->fr_ref++; 37353642Sguido pass = is->is_rule->fr_flags; 37453642Sguido } else 37553642Sguido pass = fr_flags; 37653642Sguido MUTEX_DOWNGRADE(&ipf_mutex); 37753642Sguido WRITE_ENTER(&ipf_state); 37853642Sguido 37953642Sguido is->is_rout = pass & FR_OUTQUE ? 1 : 0; 38053642Sguido is->is_pass = pass; 38153642Sguido is->is_pkts = 1; 38253642Sguido is->is_bytes = ip->ip_len; 38353642Sguido /* 38453642Sguido * We want to check everything that is a property of this packet, 38553642Sguido * but we don't (automatically) care about it's fragment status as 38653642Sguido * this may change. 38753642Sguido */ 38853642Sguido is->is_opt = fin->fin_fi.fi_optmsk; 38953642Sguido is->is_optmsk = 0xffffffff; 39053642Sguido is->is_sec = fin->fin_fi.fi_secmsk; 39153642Sguido is->is_secmsk = 0xffff; 39253642Sguido is->is_auth = fin->fin_fi.fi_auth; 39353642Sguido is->is_authmsk = 0xffff; 39453642Sguido is->is_flags = fin->fin_fi.fi_fl & FI_CMP; 39553642Sguido is->is_flags |= FI_CMP << 4; 39653642Sguido is->is_flags |= flags & (FI_W_DPORT|FI_W_SPORT); 39753642Sguido /* 39853642Sguido * add into table. 39953642Sguido */ 40053642Sguido is->is_next = ips_table[hv]; 40153642Sguido ips_table[hv] = is; 40253642Sguido if (is->is_next == NULL) 40353642Sguido ips_stats.iss_inuse++; 40453642Sguido if (fin->fin_out) { 40553642Sguido is->is_ifpin = NULL; 40653642Sguido is->is_ifpout = fin->fin_ifp; 40753642Sguido } else { 40853642Sguido is->is_ifpin = fin->fin_ifp; 40953642Sguido is->is_ifpout = NULL; 41053642Sguido } 41153642Sguido if (pass & FR_LOGFIRST) 41253642Sguido is->is_pass &= ~(FR_LOGFIRST|FR_LOG); 41353642Sguido ATOMIC_INC(ips_num); 41453642Sguido#ifdef IPFILTER_LOG 41553642Sguido ipstate_log(is, ISL_NEW); 41653642Sguido#endif 41753642Sguido RWLOCK_EXIT(&ipf_state); 41853642Sguido fin->fin_rev = (is->is_dst.s_addr != ip->ip_dst.s_addr); 41953642Sguido if (fin->fin_fi.fi_fl & FI_FRAG) 42053642Sguido ipfr_newfrag(ip, fin, pass ^ FR_KEEPSTATE); 42153642Sguido return is; 42253642Sguido} 42353642Sguido 42453642Sguido 42553642Sguido 42653642Sguido/* 42753642Sguido * check to see if a packet with TCP headers fits within the TCP window. 42853642Sguido * change timeout depending on whether new packet is a SYN-ACK returning for a 42953642Sguido * SYN or a RST or FIN which indicate time to close up shop. 43053642Sguido */ 43153642Sguidoint fr_tcpstate(is, fin, ip, tcp) 43253642Sguidoregister ipstate_t *is; 43353642Sguidofr_info_t *fin; 43453642Sguidoip_t *ip; 43553642Sguidotcphdr_t *tcp; 43653642Sguido{ 43753642Sguido register tcp_seq seq, ack, end; 43853642Sguido register int ackskew; 43953642Sguido tcpdata_t *fdata, *tdata; 44053642Sguido u_short win, maxwin; 44153642Sguido int ret = 0; 44253642Sguido int source; 44353642Sguido 44453642Sguido /* 44553642Sguido * Find difference between last checked packet and this packet. 44653642Sguido */ 44753642Sguido source = (ip->ip_src.s_addr == is->is_src.s_addr); 44853642Sguido fdata = &is->is_tcp.ts_data[!source]; 44953642Sguido tdata = &is->is_tcp.ts_data[source]; 45053642Sguido seq = ntohl(tcp->th_seq); 45153642Sguido ack = ntohl(tcp->th_ack); 45253642Sguido win = ntohs(tcp->th_win); 45353642Sguido end = seq + ip->ip_len - fin->fin_hlen - (tcp->th_off << 2) + 45453642Sguido ((tcp->th_flags & TH_SYN) ? 1 : 0) + 45553642Sguido ((tcp->th_flags & TH_FIN) ? 1 : 0); 45653642Sguido 45753642Sguido if (fdata->td_end == 0) { 45853642Sguido /* 45953642Sguido * Must be a (outgoing) SYN-ACK in reply to a SYN. 46053642Sguido */ 46153642Sguido fdata->td_end = end; 46253642Sguido fdata->td_maxwin = 1; 46353642Sguido fdata->td_maxend = end + 1; 46453642Sguido } 46553642Sguido 46653642Sguido if (!(tcp->th_flags & TH_ACK)) { /* Pretend an ack was sent */ 46753642Sguido ack = tdata->td_end; 46853642Sguido win = 1; 46953642Sguido } else if (((tcp->th_flags & (TH_ACK|TH_RST)) == (TH_ACK|TH_RST)) && 47053642Sguido (ack == 0)) { 47153642Sguido /* gross hack to get around certain broken tcp stacks */ 47253642Sguido ack = tdata->td_end; 47353642Sguido } 47453642Sguido 47553642Sguido if (seq == end) 47653642Sguido seq = end = fdata->td_end; 47753642Sguido 47853642Sguido maxwin = tdata->td_maxwin; 47953642Sguido ackskew = tdata->td_end - ack; 48053642Sguido 48153642Sguido#define SEQ_GE(a,b) ((int)((a) - (b)) >= 0) 48253642Sguido#define SEQ_GT(a,b) ((int)((a) - (b)) > 0) 48353642Sguido if ((SEQ_GE(fdata->td_maxend, end)) && 48453642Sguido (SEQ_GE(seq + maxwin, fdata->td_end - maxwin)) && 48553642Sguido/* XXX what about big packets */ 48653642Sguido#define MAXACKWINDOW 66000 48753642Sguido (ackskew >= -MAXACKWINDOW) && 48853642Sguido (ackskew <= MAXACKWINDOW)) { 48953642Sguido /* if ackskew < 0 then this should be due to fragented 49053642Sguido * packets. There is no way to know the length of the 49153642Sguido * total packet in advance. 49253642Sguido * We do know the total length from the fragment cache though. 49353642Sguido * Note however that there might be more sessions with 49453642Sguido * exactly the same source and destination paramters in the 49553642Sguido * state cache (and source and destination is the only stuff 49653642Sguido * that is saved in the fragment cache). Note further that 49753642Sguido * some TCP connections in the state cache are hashed with 49853642Sguido * sport and dport as well which makes it not worthwhile to 49953642Sguido * look for them. 50053642Sguido * Thus, when ackskew is negative but still seems to belong 50153642Sguido * to this session, we bump up the destinations end value. 50253642Sguido */ 50353642Sguido if (ackskew < 0) 50453642Sguido tdata->td_end = ack; 50553642Sguido 50653642Sguido /* update max window seen */ 50753642Sguido if (fdata->td_maxwin < win) 50853642Sguido fdata->td_maxwin = win; 50953642Sguido if (SEQ_GT(end, fdata->td_end)) 51053642Sguido fdata->td_end = end; 51153642Sguido if (SEQ_GE(ack + win, tdata->td_maxend)) { 51253642Sguido tdata->td_maxend = ack + win; 51353642Sguido if (win == 0) 51453642Sguido tdata->td_maxend++; 51553642Sguido } 51653642Sguido 51753642Sguido ATOMIC_INC(ips_stats.iss_hits); 51853642Sguido is->is_pkts++; 51953642Sguido is->is_bytes += ip->ip_len; 52053642Sguido /* 52153642Sguido * Nearing end of connection, start timeout. 52253642Sguido */ 52353642Sguido MUTEX_ENTER(&ipf_rw); 52453642Sguido fr_tcp_age(&is->is_age, is->is_state, ip, fin, source); 52553642Sguido MUTEX_EXIT(&ipf_rw); 52653642Sguido ret = 1; 52753642Sguido } 52853642Sguido return ret; 52953642Sguido} 53053642Sguido 53153642Sguido 53253642Sguidostatic int fr_matchsrcdst(is, src, dst, fin, tcp) 53353642Sguidoipstate_t *is; 53453642Sguidostruct in_addr src, dst; 53553642Sguidofr_info_t *fin; 53653642Sguidotcphdr_t *tcp; 53753642Sguido{ 53853642Sguido int ret = 0, rev, out, flags; 53953642Sguido u_short sp, dp; 54053642Sguido void *ifp; 54153642Sguido 54253642Sguido rev = fin->fin_rev = (is->is_dst.s_addr != dst.s_addr); 54353642Sguido ifp = fin->fin_ifp; 54453642Sguido out = fin->fin_out; 54553642Sguido 54653642Sguido if (tcp != NULL) { 54753642Sguido flags = is->is_flags; 54853642Sguido sp = tcp->th_sport; 54953642Sguido dp = tcp->th_dport; 55053642Sguido } else { 55153642Sguido flags = 0; 55253642Sguido sp = 0; 55353642Sguido dp = 0; 55453642Sguido } 55553642Sguido 55653642Sguido if (rev == 0) { 55753642Sguido if (!out) { 55853642Sguido if (is->is_ifpin == ifp) 55953642Sguido ret = 1; 56053642Sguido } else { 56153642Sguido if (is->is_ifpout == NULL || is->is_ifpout == ifp) 56253642Sguido ret = 1; 56353642Sguido } 56453642Sguido } else { 56553642Sguido if (out) { 56653642Sguido if (is->is_ifpin == ifp) 56753642Sguido ret = 1; 56853642Sguido } else { 56953642Sguido if (is->is_ifpout == NULL || is->is_ifpout == ifp) 57053642Sguido ret = 1; 57153642Sguido } 57253642Sguido } 57353642Sguido if (ret == 0) 57453642Sguido return 0; 57553642Sguido ret = 0; 57653642Sguido 57753642Sguido if (rev == 0) { 57853642Sguido if ((is->is_dst.s_addr == dst.s_addr) && 57953642Sguido (is->is_src.s_addr == src.s_addr) && 58053642Sguido (!tcp || ((sp == is->is_sport || flags & FI_W_SPORT) && 58153642Sguido (dp == is->is_dport || flags & FI_W_DPORT)))) { 58253642Sguido ret = 1; 58353642Sguido } 58453642Sguido } else { 58553642Sguido if ((is->is_dst.s_addr == src.s_addr) && 58653642Sguido (is->is_src.s_addr == dst.s_addr) && 58753642Sguido (!tcp || ((sp == is->is_dport || flags & FI_W_DPORT) && 58853642Sguido (dp == is->is_sport || flags & FI_W_SPORT)))) { 58953642Sguido ret = 1; 59053642Sguido } 59153642Sguido } 59253642Sguido if (ret == 0) 59353642Sguido return 0; 59453642Sguido 59553642Sguido /* 59653642Sguido * Whether or not this should be here, is questionable, but the aim 59753642Sguido * is to get this out of the main line. 59853642Sguido */ 59953642Sguido if (tcp == NULL) 60053642Sguido flags = is->is_flags & (FI_CMP|(FI_CMP<<4)); 60153642Sguido 60253642Sguido if (((fin->fin_fi.fi_fl & (flags >> 4)) != (flags & FI_CMP)) || 60353642Sguido ((fin->fin_fi.fi_optmsk & is->is_optmsk) != is->is_opt) || 60453642Sguido ((fin->fin_fi.fi_secmsk & is->is_secmsk) != is->is_sec) || 60553642Sguido ((fin->fin_fi.fi_auth & is->is_authmsk) != is->is_auth)) 60653642Sguido return 0; 60753642Sguido 60853642Sguido if ((flags & (FI_W_SPORT|FI_W_DPORT))) { 60953642Sguido if ((flags & FI_W_SPORT) != 0) { 61053642Sguido if (rev == 0) { 61153642Sguido is->is_sport = sp; 61253642Sguido is->is_send = htonl(tcp->th_seq); 61353642Sguido } else { 61453642Sguido is->is_sport = dp; 61553642Sguido is->is_send = htonl(tcp->th_ack); 61653642Sguido } 61753642Sguido is->is_maxsend = is->is_send + 1; 61853642Sguido } else if ((flags & FI_W_DPORT) != 0) { 61953642Sguido if (rev == 0) { 62053642Sguido is->is_dport = dp; 62153642Sguido is->is_dend = htonl(tcp->th_ack); 62253642Sguido } else { 62353642Sguido is->is_dport = sp; 62453642Sguido is->is_dend = htonl(tcp->th_seq); 62553642Sguido } 62653642Sguido is->is_maxdend = is->is_dend + 1; 62753642Sguido } 62853642Sguido is->is_flags &= ~(FI_W_SPORT|FI_W_DPORT); 62953642Sguido } 63053642Sguido 63153642Sguido if (!rev) { 63253642Sguido if (out && (out == is->is_rout)) { 63353642Sguido if (!is->is_ifpout) 63453642Sguido is->is_ifpout = ifp; 63553642Sguido } else { 63653642Sguido if (!is->is_ifpin) 63753642Sguido is->is_ifpin = ifp; 63853642Sguido } 63953642Sguido } else { 64053642Sguido if (!out && (out != is->is_rout)) { 64153642Sguido if (!is->is_ifpin) 64253642Sguido is->is_ifpin = ifp; 64353642Sguido } else { 64453642Sguido if (!is->is_ifpout) 64553642Sguido is->is_ifpout = ifp; 64653642Sguido } 64753642Sguido } 64853642Sguido return 1; 64953642Sguido} 65053642Sguido 65153642Sguidofrentry_t *fr_checkicmpmatchingstate(ip, fin) 65253642Sguidoip_t *ip; 65353642Sguidofr_info_t *fin; 65453642Sguido{ 65553642Sguido register struct in_addr dst, src; 65653642Sguido register ipstate_t *is, **isp; 65753642Sguido register u_short sport, dport; 65853642Sguido register u_char pr; 65953642Sguido struct icmp *ic; 66053642Sguido fr_info_t ofin; 66153642Sguido u_int hv, dest; 66253642Sguido tcphdr_t *tcp; 66353642Sguido frentry_t *fr; 66453642Sguido ip_t *oip; 66553642Sguido int type; 66653642Sguido 66753642Sguido /* 66853642Sguido * Does it at least have the return (basic) IP header ? 66953642Sguido * Only a basic IP header (no options) should be with 67053642Sguido * an ICMP error header. 67153642Sguido */ 67253642Sguido if ((ip->ip_hl != 5) || (ip->ip_len < ICMPERR_MINPKTLEN)) 67353642Sguido return NULL; 67453642Sguido ic = (struct icmp *)((char *)ip + fin->fin_hlen); 67553642Sguido type = ic->icmp_type; 67653642Sguido /* 67753642Sguido * If it's not an error type, then return 67853642Sguido */ 67953642Sguido if ((type != ICMP_UNREACH) && (type != ICMP_SOURCEQUENCH) && 68053642Sguido (type != ICMP_REDIRECT) && (type != ICMP_TIMXCEED) && 68153642Sguido (type != ICMP_PARAMPROB)) 68253642Sguido return NULL; 68353642Sguido 68453642Sguido oip = (ip_t *)((char *)fin->fin_dp + ICMPERR_ICMPHLEN); 68553642Sguido if (ip->ip_len < ICMPERR_MAXPKTLEN + ((oip->ip_hl - 5) << 2)) 68653642Sguido return NULL; 68753642Sguido if ((oip->ip_p != IPPROTO_TCP) && (oip->ip_p != IPPROTO_UDP)) 68853642Sguido return NULL; 68953642Sguido 69053642Sguido tcp = (tcphdr_t *)((char *)oip + (oip->ip_hl << 2)); 69153642Sguido dport = tcp->th_dport; 69253642Sguido sport = tcp->th_sport; 69353642Sguido 69453642Sguido hv = (pr = oip->ip_p); 69553642Sguido hv += (src.s_addr = oip->ip_src.s_addr); 69653642Sguido hv += (dst.s_addr = oip->ip_dst.s_addr); 69753642Sguido hv += dport; 69853642Sguido hv += sport; 69953642Sguido hv %= fr_statesize; 70053642Sguido /* 70153642Sguido * we make an fin entry to be able to feed it to 70253642Sguido * matchsrcdst note that not all fields are encessary 70353642Sguido * but this is the cleanest way. Note further we fill 70453642Sguido * in fin_mp such that if someone uses it we'll get 70553642Sguido * a kernel panic. fr_matchsrcdst does not use this. 70653642Sguido * 70753642Sguido * watch out here, as ip is in host order and oip in network 70853642Sguido * order. Any change we make must be undone afterwards. 70953642Sguido */ 71053642Sguido oip->ip_len = ntohs(oip->ip_len); 71153642Sguido fr_makefrip(oip->ip_hl << 2, oip, &ofin); 71253642Sguido oip->ip_len = htons(oip->ip_len); 71353642Sguido ofin.fin_ifp = fin->fin_ifp; 71453642Sguido ofin.fin_out = !fin->fin_out; 71553642Sguido ofin.fin_mp = NULL; /* if dereferenced, panic XXX */ 71653642Sguido READ_ENTER(&ipf_state); 71753642Sguido for (isp = &ips_table[hv]; (is = *isp); isp = &is->is_next) { 71853642Sguido /* 71953642Sguido * Only allow this icmp though if the 72053642Sguido * encapsulated packet was allowed through the 72153642Sguido * other way around. Note that the minimal amount 72253642Sguido * of info present does not allow for checking against 72353642Sguido * tcp internals such as seq and ack numbers. 72453642Sguido */ 72553642Sguido if ((is->is_p == pr) && 72653642Sguido fr_matchsrcdst(is, src, dst, &ofin, tcp)) { 72753642Sguido fr = is->is_rule; 72853642Sguido ips_stats.iss_hits++; 72953642Sguido /* 73053642Sguido * we must swap src and dst here because the icmp 73153642Sguido * comes the other way around 73253642Sguido */ 73353642Sguido dest = (is->is_dst.s_addr != src.s_addr); 73453642Sguido is->is_pkts++; 73553642Sguido is->is_bytes += ip->ip_len; 73653642Sguido /* 73753642Sguido * we deliberately do not touch the timeouts 73853642Sguido * for the accompanying state table entry. 73953642Sguido * It remains to be seen if that is correct. XXX 74053642Sguido */ 74153642Sguido RWLOCK_EXIT(&ipf_state); 74253642Sguido return fr; 74353642Sguido } 74453642Sguido } 74553642Sguido RWLOCK_EXIT(&ipf_state); 74653642Sguido return NULL; 74753642Sguido} 74853642Sguido 74953642Sguido/* 75053642Sguido * Check if a packet has a registered state. 75153642Sguido */ 75253642Sguidofrentry_t *fr_checkstate(ip, fin) 75353642Sguidoip_t *ip; 75453642Sguidofr_info_t *fin; 75553642Sguido{ 75653642Sguido register struct in_addr dst, src; 75753642Sguido register ipstate_t *is, **isp; 75853642Sguido register u_char pr; 75953642Sguido u_int hv, hvm, hlen, tryagain, pass; 76053642Sguido struct icmp *ic; 76153642Sguido frentry_t *fr; 76253642Sguido tcphdr_t *tcp; 76353642Sguido 76453642Sguido if ((ip->ip_off & IP_OFFMASK) || (fin->fin_fi.fi_fl & FI_SHORT)) 76553642Sguido return NULL; 76653642Sguido 76753642Sguido is = NULL; 76853642Sguido hlen = fin->fin_hlen; 76953642Sguido tcp = (tcphdr_t *)((char *)ip + hlen); 77053642Sguido ic = (struct icmp *)tcp; 77153642Sguido hv = (pr = ip->ip_p); 77253642Sguido hv += (src.s_addr = ip->ip_src.s_addr); 77353642Sguido hv += (dst.s_addr = ip->ip_dst.s_addr); 77453642Sguido 77553642Sguido /* 77653642Sguido * Search the hash table for matching packet header info. 77753642Sguido */ 77853642Sguido switch (ip->ip_p) 77953642Sguido { 78053642Sguido case IPPROTO_ICMP : 78153642Sguido hv += ic->icmp_id; 78253642Sguido hv += ic->icmp_seq; 78353642Sguido hv %= fr_statesize; 78453642Sguido READ_ENTER(&ipf_state); 78553642Sguido for (isp = &ips_table[hv]; (is = *isp); isp = &is->is_next) 78653642Sguido if ((is->is_p == pr) && 78753642Sguido (ic->icmp_id == is->is_icmp.ics_id) && 78853642Sguido (ic->icmp_seq == is->is_icmp.ics_seq) && 78953642Sguido fr_matchsrcdst(is, src, dst, fin, NULL)) { 79053642Sguido if ((is->is_type == ICMP_ECHOREPLY) && 79153642Sguido (ic->icmp_type == ICMP_ECHO)) 79253642Sguido ; 79353642Sguido else if (is->is_type != ic->icmp_type) 79453642Sguido continue; 79553642Sguido is->is_age = fr_icmptimeout; 79653642Sguido break; 79753642Sguido } 79853642Sguido if (is != NULL) 79953642Sguido break; 80053642Sguido RWLOCK_EXIT(&ipf_state); 80153642Sguido /* 80253642Sguido * No matching icmp state entry. Perhaps this is a 80353642Sguido * response to another state entry. 80453642Sguido */ 80553642Sguido fr = fr_checkicmpmatchingstate(ip, fin); 80653642Sguido if (fr) 80753642Sguido return fr; 80853642Sguido break; 80953642Sguido case IPPROTO_TCP : 81053642Sguido { 81153642Sguido register u_short dport = tcp->th_dport, sport = tcp->th_sport; 81253642Sguido 81353642Sguido tryagain = 0; 81453642Sguidoretry_tcp: 81553642Sguido hvm = hv % fr_statesize; 81653642Sguido WRITE_ENTER(&ipf_state); 81753642Sguido for (isp = &ips_table[hvm]; (is = *isp); 81853642Sguido isp = &is->is_next) 81953642Sguido if ((is->is_p == pr) && 82053642Sguido fr_matchsrcdst(is, src, dst, fin, tcp)) { 82153642Sguido if (fr_tcpstate(is, fin, ip, tcp)) { 82253642Sguido break; 82353642Sguido#ifndef _KERNEL 82453642Sguido if (tcp->th_flags & TCP_CLOSE) { 82553642Sguido *isp = is->is_next; 82653642Sguido isp = &ips_table[hvm]; 82753642Sguido if (ips_table[hvm] == NULL) 82853642Sguido ips_stats.iss_inuse--; 82953642Sguido fr_delstate(is); 83053642Sguido ips_num--; 83153642Sguido } 83253642Sguido#endif 83353642Sguido break; 83453642Sguido } 83553642Sguido is = NULL; 83653642Sguido break; 83753642Sguido } 83853642Sguido if (is != NULL) 83953642Sguido break; 84053642Sguido RWLOCK_EXIT(&ipf_state); 84153642Sguido hv += dport; 84253642Sguido hv += sport; 84353642Sguido if (tryagain == 0) { 84453642Sguido tryagain = 1; 84553642Sguido goto retry_tcp; 84653642Sguido } 84753642Sguido break; 84853642Sguido } 84953642Sguido case IPPROTO_UDP : 85053642Sguido { 85153642Sguido register u_short dport = tcp->th_dport, sport = tcp->th_sport; 85253642Sguido 85353642Sguido tryagain = 0; 85453642Sguidoretry_udp: 85553642Sguido hvm = hv % fr_statesize; 85653642Sguido /* 85753642Sguido * Nothing else to match on but ports. and IP#'s 85853642Sguido */ 85953642Sguido READ_ENTER(&ipf_state); 86053642Sguido for (is = ips_table[hvm]; is; is = is->is_next) 86153642Sguido if ((is->is_p == pr) && 86253642Sguido fr_matchsrcdst(is, src, dst, fin, tcp)) { 86353642Sguido is->is_age = fr_udptimeout; 86453642Sguido break; 86553642Sguido } 86653642Sguido if (is != NULL) 86753642Sguido break; 86853642Sguido RWLOCK_EXIT(&ipf_state); 86953642Sguido hv += dport; 87053642Sguido hv += sport; 87153642Sguido if (tryagain == 0) { 87253642Sguido tryagain = 1; 87353642Sguido goto retry_udp; 87453642Sguido } 87553642Sguido break; 87653642Sguido } 87753642Sguido default : 87853642Sguido break; 87953642Sguido } 88053642Sguido if (is == NULL) { 88153642Sguido ATOMIC_INC(ips_stats.iss_miss); 88253642Sguido return NULL; 88353642Sguido } 88453642Sguido MUTEX_ENTER(&ipf_rw); 88553642Sguido is->is_bytes += ip->ip_len; 88653642Sguido ips_stats.iss_hits++; 88753642Sguido is->is_pkts++; 88853642Sguido MUTEX_EXIT(&ipf_rw); 88953642Sguido fr = is->is_rule; 89053642Sguido fin->fin_fr = fr; 89153642Sguido pass = is->is_pass; 89253642Sguido RWLOCK_EXIT(&ipf_state); 89353642Sguido if (fin->fin_fi.fi_fl & FI_FRAG) 89453642Sguido ipfr_newfrag(ip, fin, pass ^ FR_KEEPSTATE); 89553642Sguido return fr; 89653642Sguido} 89753642Sguido 89853642Sguido 89953642Sguidostatic void fr_delstate(is) 90053642Sguidoipstate_t *is; 90153642Sguido{ 90253642Sguido frentry_t *fr; 90353642Sguido 90453642Sguido fr = is->is_rule; 90553642Sguido if (fr != NULL) { 90653642Sguido ATOMIC_DEC(fr->fr_ref); 90753642Sguido if (fr->fr_ref == 0) 90853642Sguido KFREE(fr); 90953642Sguido } 91053642Sguido KFREE(is); 91153642Sguido} 91253642Sguido 91353642Sguido 91453642Sguido/* 91553642Sguido * Free memory in use by all state info. kept. 91653642Sguido */ 91753642Sguidovoid fr_stateunload() 91853642Sguido{ 91953642Sguido register int i; 92053642Sguido register ipstate_t *is, **isp; 92153642Sguido 92253642Sguido WRITE_ENTER(&ipf_state); 92353642Sguido for (i = fr_statesize - 1; i >= 0; i--) 92453642Sguido for (isp = &ips_table[i]; (is = *isp); ) { 92553642Sguido *isp = is->is_next; 92653642Sguido fr_delstate(is); 92753642Sguido ips_num--; 92853642Sguido } 92953642Sguido ips_stats.iss_inuse = 0; 93053642Sguido ips_num = 0; 93153642Sguido RWLOCK_EXIT(&ipf_state); 93253642Sguido KFREES(ips_table, fr_statesize * sizeof(ipstate_t *)); 93353642Sguido ips_table = NULL; 93453642Sguido} 93553642Sguido 93653642Sguido 93753642Sguido/* 93853642Sguido * Slowly expire held state for thingslike UDP and ICMP. Timeouts are set 93953642Sguido * in expectation of this being called twice per second. 94053642Sguido */ 94153642Sguidovoid fr_timeoutstate() 94253642Sguido{ 94353642Sguido register int i; 94453642Sguido register ipstate_t *is, **isp; 94553642Sguido#if defined(_KERNEL) && !SOLARIS 94653642Sguido int s; 94753642Sguido#endif 94853642Sguido 94953642Sguido SPL_NET(s); 95053642Sguido WRITE_ENTER(&ipf_state); 95153642Sguido for (i = fr_statesize - 1; i >= 0; i--) 95253642Sguido for (isp = &ips_table[i]; (is = *isp); ) 95353642Sguido if (is->is_age && !--is->is_age) { 95453642Sguido *isp = is->is_next; 95553642Sguido if (is->is_p == IPPROTO_TCP) 95653642Sguido ips_stats.iss_fin++; 95753642Sguido else 95853642Sguido ips_stats.iss_expire++; 95953642Sguido if (ips_table[i] == NULL) 96053642Sguido ips_stats.iss_inuse--; 96153642Sguido#ifdef IPFILTER_LOG 96253642Sguido ipstate_log(is, ISL_EXPIRE); 96353642Sguido#endif 96453642Sguido fr_delstate(is); 96553642Sguido ips_num--; 96653642Sguido } else 96753642Sguido isp = &is->is_next; 96853642Sguido RWLOCK_EXIT(&ipf_state); 96953642Sguido SPL_X(s); 97053642Sguido} 97153642Sguido 97253642Sguido 97353642Sguido/* 97453642Sguido * Original idea freom Pradeep Krishnan for use primarily with NAT code. 97553642Sguido * (pkrishna@netcom.com) 97653642Sguido */ 97753642Sguidovoid fr_tcp_age(age, state, ip, fin, dir) 97853642Sguidou_long *age; 97953642Sguidou_char *state; 98053642Sguidoip_t *ip; 98153642Sguidofr_info_t *fin; 98253642Sguidoint dir; 98353642Sguido{ 98453642Sguido tcphdr_t *tcp = (tcphdr_t *)fin->fin_dp; 98553642Sguido u_char flags = tcp->th_flags; 98653642Sguido int dlen, ostate; 98753642Sguido 98853642Sguido ostate = state[1 - dir]; 98953642Sguido 99053642Sguido dlen = ip->ip_len - fin->fin_hlen - (tcp->th_off << 2); 99153642Sguido 99253642Sguido if (flags & TH_RST) { 99353642Sguido if (!(tcp->th_flags & TH_PUSH) && !dlen) { 99453642Sguido *age = fr_tcpclosed; 99553642Sguido state[dir] = TCPS_CLOSED; 99653642Sguido } else { 99753642Sguido *age = fr_tcpclosewait; 99853642Sguido state[dir] = TCPS_CLOSE_WAIT; 99953642Sguido } 100053642Sguido return; 100153642Sguido } 100253642Sguido 100353642Sguido *age = fr_tcptimeout; /* 1 min */ 100453642Sguido 100553642Sguido switch(state[dir]) 100653642Sguido { 100753642Sguido case TCPS_CLOSED: 100853642Sguido if ((flags & (TH_FIN|TH_SYN|TH_RST|TH_ACK)) == TH_ACK) { 100953642Sguido state[dir] = TCPS_ESTABLISHED; 101053642Sguido *age = fr_tcpidletimeout; 101153642Sguido } 101253642Sguido case TCPS_FIN_WAIT_2: 101353642Sguido if ((flags & TH_OPENING) == TH_OPENING) 101453642Sguido state[dir] = TCPS_SYN_RECEIVED; 101553642Sguido else if (flags & TH_SYN) 101653642Sguido state[dir] = TCPS_SYN_SENT; 101753642Sguido break; 101853642Sguido case TCPS_SYN_RECEIVED: 101953642Sguido case TCPS_SYN_SENT: 102053642Sguido if ((flags & (TH_FIN|TH_ACK)) == TH_ACK) { 102153642Sguido state[dir] = TCPS_ESTABLISHED; 102253642Sguido *age = fr_tcpidletimeout; 102353642Sguido } else if ((flags & (TH_FIN|TH_ACK)) == (TH_FIN|TH_ACK)) { 102453642Sguido state[dir] = TCPS_CLOSE_WAIT; 102553642Sguido if (!(flags & TH_PUSH) && !dlen && 102653642Sguido ostate > TCPS_ESTABLISHED) 102753642Sguido *age = fr_tcplastack; 102853642Sguido else 102953642Sguido *age = fr_tcpclosewait; 103053642Sguido } 103153642Sguido break; 103253642Sguido case TCPS_ESTABLISHED: 103353642Sguido if (flags & TH_FIN) { 103453642Sguido state[dir] = TCPS_CLOSE_WAIT; 103553642Sguido if (!(flags & TH_PUSH) && !dlen && 103653642Sguido ostate > TCPS_ESTABLISHED) 103753642Sguido *age = fr_tcplastack; 103853642Sguido else 103953642Sguido *age = fr_tcpclosewait; 104053642Sguido } else { 104153642Sguido if (ostate < TCPS_CLOSE_WAIT) 104253642Sguido *age = fr_tcpidletimeout; 104353642Sguido } 104453642Sguido break; 104553642Sguido case TCPS_CLOSE_WAIT: 104653642Sguido if ((flags & TH_FIN) && !(flags & TH_PUSH) && !dlen && 104753642Sguido ostate > TCPS_ESTABLISHED) { 104853642Sguido *age = fr_tcplastack; 104953642Sguido state[dir] = TCPS_LAST_ACK; 105053642Sguido } else 105153642Sguido *age = fr_tcpclosewait; 105253642Sguido break; 105353642Sguido case TCPS_LAST_ACK: 105453642Sguido if (flags & TH_ACK) { 105553642Sguido state[dir] = TCPS_FIN_WAIT_2; 105653642Sguido if (!(flags & TH_PUSH) && !dlen && 105753642Sguido ostate > TCPS_ESTABLISHED) 105853642Sguido *age = fr_tcplastack; 105953642Sguido else { 106053642Sguido *age = fr_tcpclosewait; 106153642Sguido state[dir] = TCPS_CLOSE_WAIT; 106253642Sguido } 106353642Sguido } 106453642Sguido break; 106553642Sguido } 106653642Sguido} 106753642Sguido 106853642Sguido 106953642Sguido#ifdef IPFILTER_LOG 107053642Sguidovoid ipstate_log(is, type) 107153642Sguidostruct ipstate *is; 107253642Sguidou_int type; 107353642Sguido{ 107453642Sguido struct ipslog ipsl; 107553642Sguido void *items[1]; 107653642Sguido size_t sizes[1]; 107753642Sguido int types[1]; 107853642Sguido 107953642Sguido ipsl.isl_type = type; 108053642Sguido ipsl.isl_pkts = is->is_pkts; 108153642Sguido ipsl.isl_bytes = is->is_bytes; 108253642Sguido ipsl.isl_src = is->is_src; 108353642Sguido ipsl.isl_dst = is->is_dst; 108453642Sguido ipsl.isl_p = is->is_p; 108553642Sguido ipsl.isl_flags = is->is_flags; 108653642Sguido if (ipsl.isl_p == IPPROTO_TCP || ipsl.isl_p == IPPROTO_UDP) { 108753642Sguido ipsl.isl_sport = is->is_sport; 108853642Sguido ipsl.isl_dport = is->is_dport; 108953642Sguido if (ipsl.isl_p == IPPROTO_TCP) { 109053642Sguido ipsl.isl_state[0] = is->is_state[0]; 109153642Sguido ipsl.isl_state[1] = is->is_state[1]; 109253642Sguido } 109353642Sguido } else if (ipsl.isl_p == IPPROTO_ICMP) 109453642Sguido ipsl.isl_itype = is->is_icmp.ics_type; 109553642Sguido else { 109653642Sguido ipsl.isl_ps.isl_filler[0] = 0; 109753642Sguido ipsl.isl_ps.isl_filler[1] = 0; 109853642Sguido } 109953642Sguido items[0] = &ipsl; 110053642Sguido sizes[0] = sizeof(ipsl); 110153642Sguido types[0] = 0; 110253642Sguido 110353642Sguido (void) ipllog(IPL_LOGSTATE, NULL, items, sizes, types, 1); 110453642Sguido} 110553642Sguido#endif 1106