summaryrefslogtreecommitdiff
path: root/sys/netinet/ip_state.c
diff options
context:
space:
mode:
authorkstailey <kstailey@cvs.openbsd.org>1997-02-11 22:24:21 +0000
committerkstailey <kstailey@cvs.openbsd.org>1997-02-11 22:24:21 +0000
commit80aea2f1d26c615419d2d028087115c47f9fd46f (patch)
treef63dc8d784c2927447f3929c843a58673ed5ffc0 /sys/netinet/ip_state.c
parent5e04debc4b0038a40277cd3f8198955ee51896d3 (diff)
IPF 1.3.7
Diffstat (limited to 'sys/netinet/ip_state.c')
-rw-r--r--sys/netinet/ip_state.c316
1 files changed, 226 insertions, 90 deletions
diff --git a/sys/netinet/ip_state.c b/sys/netinet/ip_state.c
index c6c04d57c77..71af2c23176 100644
--- a/sys/netinet/ip_state.c
+++ b/sys/netinet/ip_state.c
@@ -1,3 +1,4 @@
+/* $OpenBSD: ip_state.c,v 1.7 1997/02/11 22:23:28 kstailey Exp $ */
/*
* (C)opyright 1995 by Darren Reed.
*
@@ -6,9 +7,9 @@
* to the original author and the contributors.
*/
#if 0
-#ifndef lint
+#if !defined(lint) && defined(LIBC_SCCS)
static char sccsid[] = "@(#)ip_state.c 1.8 6/5/96 (C) 1993-1995 Darren Reed";
-static char rcsid[] = "$OpenBSD: ip_state.c,v 1.6 1997/01/18 08:29:21 downsj Exp $";
+static char rcsid[] = "Id: ip_state.c,v 2.0.1.2 1997/01/09 15:22:45 darrenr Exp ";
#endif
#endif
@@ -16,24 +17,18 @@ static char rcsid[] = "$OpenBSD: ip_state.c,v 1.6 1997/01/18 08:29:21 downsj Exp
# include <stdlib.h>
# include <string.h>
#endif
-#ifndef linux
#include <sys/errno.h>
#include <sys/types.h>
-#if defined(_KERNEL) || defined(KERNEL)
-#include <sys/systm.h>
-#endif
#include <sys/param.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/uio.h>
#include <sys/protosw.h>
#include <sys/socket.h>
+#ifdef _KERNEL
+# include <sys/systm.h>
+#endif
#if !defined(__SVR4) && !defined(__svr4__)
-# if defined(__OpenBSD__)
-# include <sys/dirent.h>
-# else
-# include <sys/dir.h>
-# endif
# include <sys/mbuf.h>
#else
# include <sys/byteorder.h>
@@ -52,11 +47,10 @@ static char rcsid[] = "$OpenBSD: ip_state.c,v 1.6 1997/01/18 08:29:21 downsj Exp
#include <netinet/ip.h>
#include <netinet/ip_var.h>
#include <netinet/tcp.h>
+#include <netinet/tcp_fsm.h>
#include <netinet/udp.h>
#include <netinet/tcpip.h>
#include <netinet/ip_icmp.h>
-#include <sys/syslog.h>
-#endif
#include "ip_fil_compat.h"
#include "ip_fil.h"
#include "ip_state.h"
@@ -64,6 +58,15 @@ static char rcsid[] = "$OpenBSD: ip_state.c,v 1.6 1997/01/18 08:29:21 downsj Exp
#define MIN(a,b) (((a)<(b))?(a):(b))
#endif
+void set_tcp_age __P((int *, u_char *, ip_t *, fr_info_t *, int));
+#ifndef _KERNEL
+int fr_tcpstate __P((register ipstate_t *, fr_info_t *, ip_t *, tcphdr_t *,
+ u_short, ipstate_t **));
+#else
+int fr_tcpstate __P((register ipstate_t *, fr_info_t *, ip_t *, tcphdr_t *,
+ u_short));
+#endif
+
#define TCP_CLOSE (TH_FIN|TH_RST)
ipstate_t *ips_table[IPSTATE_SIZE];
@@ -77,6 +80,17 @@ extern kmutex_t ipf_state;
#endif
+#define FIVE_DAYS (2 * 5 * 86400) /* 5 days: half closed session */
+
+u_long fr_tcpidletimeout = FIVE_DAYS,
+ fr_tcpclosewait = 60,
+ fr_tcplastack = 20,
+ fr_tcptimeout = 120,
+ fr_tcpclosed = 1,
+ fr_udptimeout = 120,
+ fr_icmptimeout = 120;
+
+
ips_stat_t *
fr_statetstats()
{
@@ -104,10 +118,15 @@ fr_addstate(ip, fin, pass)
register ipstate_t *is = &ips;
register u_int hv;
+ if ((ip->ip_off & 0x1fff) || (fin->fin_fi.fi_fl & FI_SHORT))
+ return -1;
if (ips_num == IPSTATE_MAX) {
ips_stats.iss_max++;
return -1;
}
+ ips.is_age = 1;
+ ips.is_state[0] = 0;
+ ips.is_state[1] = 0;
/*
* Copy and calculate...
*/
@@ -137,7 +156,7 @@ fr_addstate(ip, fin, pass)
return -1;
}
ips_stats.iss_icmp++;
- is->is_age = 120;
+ is->is_age = fr_icmptimeout;
break;
}
case IPPROTO_TCP :
@@ -152,7 +171,8 @@ fr_addstate(ip, fin, pass)
hv += (is->is_sport = tcp->th_sport);
is->is_seq = ntohl(tcp->th_seq);
is->is_ack = ntohl(tcp->th_ack);
- is->is_win = ntohs(tcp->th_win);
+ is->is_swin = ntohs(tcp->th_win);
+ is->is_dwin = is->is_swin; /* start them the same */
ips_stats.iss_tcp++;
/*
* If we're creating state for a starting connectoin, start the
@@ -160,9 +180,9 @@ fr_addstate(ip, fin, pass)
* connect.
*/
if ((tcp->th_flags & (TH_SYN|TH_ACK)) == TH_SYN)
- is->is_age = 120;
- else
- is->is_age = 0;
+ is->is_ack = 0; /* Trumpet WinSock 'ism */
+ set_tcp_age(&is->is_age, is->is_state, ip, fin,
+ tcp->th_sport == is->is_sport);
break;
}
case IPPROTO_UDP :
@@ -172,7 +192,7 @@ fr_addstate(ip, fin, pass)
hv += (is->is_dport = tcp->th_dport);
hv += (is->is_sport = tcp->th_sport);
ips_stats.iss_udp++;
- is->is_age = 120;
+ is->is_age = fr_udptimeout;
break;
}
default :
@@ -198,6 +218,91 @@ fr_addstate(ip, fin, pass)
/*
+ * check to see if a packet with TCP headers fits within the TCP window.
+ * change timeout depending on whether new packet is a SYN-ACK returning for a
+ * SYN or a RST or FIN which indicate time to close up shop.
+ */
+int
+fr_tcpstate(is, fin, ip, tcp, sport
+#ifndef _KERNEL
+ ,isp)
+ ipstate_t **isp;
+#else
+ )
+#endif
+ register ipstate_t *is;
+ fr_info_t *fin;
+ ip_t *ip;
+ tcphdr_t *tcp;
+ u_short sport;
+{
+ register int seqskew, ackskew;
+ register u_short swin, dwin;
+ register tcp_seq seq, ack;
+ int source;
+
+ /*
+ * Find difference between last checked packet and this packet.
+ */
+ seq = ntohl(tcp->th_seq);
+ ack = ntohl(tcp->th_ack);
+ if (sport == is->is_sport) {
+ seqskew = seq - is->is_seq;
+ ackskew = ack - is->is_ack;
+ } else {
+ seqskew = ack - is->is_seq;
+ if (!is->is_ack)
+ /*
+ * Must be a SYN-ACK in reply to a SYN.
+ */
+ is->is_ack = seq;
+ ackskew = seq - is->is_ack;
+ }
+
+ /*
+ * Make skew values absolute
+ */
+ if (seqskew < 0)
+ seqskew = -seqskew;
+ if (ackskew < 0)
+ ackskew = -ackskew;
+
+ /*
+ * If the difference in sequence and ack numbers is within the
+ * window size of the connection, store these values and match
+ * the packet.
+ */
+ if ((source = (sport == is->is_sport))) {
+ swin = is->is_swin;
+ dwin = is->is_dwin;
+ } else {
+ dwin = is->is_swin;
+ swin = is->is_dwin;
+ }
+
+ if ((seqskew <= swin) && (ackskew <= dwin)) {
+ if (source) {
+ is->is_seq = seq;
+ is->is_ack = ack;
+ is->is_swin = ntohs(tcp->th_win);
+ } else {
+ is->is_seq = ack;
+ is->is_ack = seq;
+ is->is_dwin = ntohs(tcp->th_win);
+ }
+ ips_stats.iss_hits++;
+ /*
+ * Nearing end of connection, start timeout.
+ */
+ set_tcp_age(&is->is_age, is->is_state, ip, fin,
+ tcp->th_sport == is->is_sport);
+ return 1;
+ }
+ return 0;
+}
+
+
+/*
* Check if a packet has a registered state.
*/
int
@@ -212,7 +317,7 @@ fr_checkstate(ip, fin)
tcphdr_t *tcp;
u_int hv, hlen;
- if ((ip->ip_off & 0x1fff) && !(fin->fin_fi.fi_fl & FI_SHORT))
+ if ((ip->ip_off & 0x1fff) || (fin->fin_fi.fi_fl & FI_SHORT))
return 0;
hlen = fin->fin_hlen;
@@ -244,7 +349,7 @@ fr_checkstate(ip, fin)
if (is->is_icmp.ics_type &&
is->is_icmp.ics_type != ic->icmp_type)
continue;
- is->is_age = 120;
+ is->is_age = fr_icmptimeout;
ips_stats.iss_hits++;
MUTEX_EXIT(&ipf_state);
return is->is_pass;
@@ -254,92 +359,34 @@ fr_checkstate(ip, fin)
case IPPROTO_TCP :
{
register u_short dport = tcp->th_dport, sport = tcp->th_sport;
- register u_short win = ntohs(tcp->th_win);
- tcp_seq seq, ack;
hv += dport;
hv += sport;
hv %= IPSTATE_SIZE;
MUTEX_ENTER(&ipf_state);
for (isp = &ips_table[hv]; (is = *isp); isp = &is->is_next) {
- register int dl, seqskew, ackskew;
-
if ((is->is_p == pr) &&
PAIRS(sport, dport, is->is_sport, is->is_dport) &&
- IPPAIR(src, dst, is->is_src, is->is_dst)) {
- dl = ip->ip_len - hlen - sizeof(tcphdr_t);
- /*
- * Find difference between last checked packet
- * and this packet.
- */
- seq = ntohl(tcp->th_seq);
- ack = ntohl(tcp->th_ack);
- if (sport == is->is_sport) {
- seqskew = seq - is->is_seq;
- ackskew = ack - is->is_ack;
- } else {
- seqskew = ack - is->is_seq;
- if (!is->is_ack) {
- /*
- * Must be a SYN-ACK in reply
- * to a SYN. Set age timeout
- * to 0 to stop deletion.
- */
- is->is_ack = seq;
- is->is_age = 0;
- }
- ackskew = seq - is->is_ack;
- }
-
- /*
- * Make skew values absolute
- */
- if (seqskew < 0)
- seqskew = -seqskew;
- if (ackskew < 0)
- ackskew = -ackskew;
- /*
- * If the difference in sequence and ack
- * numbers is within the window size of the
- * connection, store these values and match
- * the packet.
- */
- if ((seqskew <= win) && (ackskew <= win)) {
- is->is_win = win;
- if (sport == is->is_sport) {
- is->is_seq = seq;
- is->is_ack = ack;
- } else {
- is->is_seq = ack;
- is->is_ack = seq;
- }
- ips_stats.iss_hits++;
- /*
- * Nearing end of connection, start
- * timeout.
- */
+ IPPAIR(src, dst, is->is_src, is->is_dst))
+ if (fr_tcpstate(is, fin, ip, tcp, sport
+#ifndef _KERNEL
+ , NULL
+#endif
+ )) {
#ifdef _KERNEL
- if (!is->is_age) {
- if (tcp->th_flags & TH_FIN)
- is->is_age = 120;
- if (tcp->th_flags & TH_RST)
- is->is_age = 1;
- }
MUTEX_EXIT(&ipf_state);
return is->is_pass;
#else
- if (tcp->th_flags & TCP_CLOSE) {
- int pass = is->is_pass;
+ int pass = is->is_pass;
+ if (tcp->th_flags & TCP_CLOSE) {
*isp = is->is_next;
isp = &ips_table[hv];
KFREE(is);
- return pass;
}
- return is->is_pass;
+ return pass;
#endif
}
- }
}
MUTEX_EXIT(&ipf_state);
break;
@@ -360,7 +407,7 @@ fr_checkstate(ip, fin)
PAIRS(sport, dport, is->is_sport, is->is_dport) &&
IPPAIR(src, dst, is->is_src, is->is_dst)) {
ips_stats.iss_hits++;
- is->is_age = 120;
+ is->is_age = fr_udptimeout;
MUTEX_EXIT(&ipf_state);
return is->is_pass;
}
@@ -395,7 +442,7 @@ fr_stateunload()
/*
- * Slowly expire held state for thingslike UDP and ICMP. Timeouts are set
+ * Slowly expire held state for things like UDP and ICMP. Timeouts are set
* in expectation of this being called twice per second.
*/
void
@@ -419,3 +466,92 @@ fr_timeoutstate()
isp = &is->is_next;
MUTEX_EXIT(&ipf_state);
}
+
+
+/*
+ * Original idea freom Pradeep Krishnan for use primarily with NAT code.
+ * (pkrishna@netcom.com)
+ */
+void
+set_tcp_age(age, state, ip, fin, dir)
+ int *age;
+ u_char *state;
+ ip_t *ip;
+ fr_info_t *fin;
+ int dir;
+{
+ tcphdr_t *tcp = (tcphdr_t *)fin->fin_dp;
+ u_char flags = tcp->th_flags;
+ int dlen, ostate;
+
+ ostate = state[1 - dir];
+
+ dlen = ip->ip_len - fin->fin_hlen - (tcp->th_off << 2);
+
+ if (flags & TH_RST) {
+ if (!(tcp->th_flags & TH_PUSH) && !dlen) {
+ *age = fr_tcpclosed;
+ state[dir] = TCPS_CLOSED;
+ } else {
+ *age = fr_tcpclosewait;
+ state[dir] = TCPS_CLOSE_WAIT;
+ }
+ return;
+ }
+
+ *age = fr_tcptimeout; /* 1 min */
+
+ switch(state[dir])
+ {
+ case TCPS_FIN_WAIT_2:
+ case TCPS_CLOSED:
+ if ((flags & TH_OPENING) == TH_OPENING)
+ state[dir] = TCPS_SYN_RECEIVED;
+ else if (flags & TH_SYN)
+ state[dir] = TCPS_SYN_SENT;
+ break;
+ case TCPS_SYN_RECEIVED:
+ if ((flags & (TH_FIN|TH_ACK)) == TH_ACK) {
+ state[dir] = TCPS_ESTABLISHED;
+ *age = fr_tcpidletimeout;
+ }
+ break;
+ case TCPS_SYN_SENT:
+ if ((flags & (TH_FIN|TH_ACK)) == TH_ACK) {
+ state[dir] = TCPS_ESTABLISHED;
+ *age = fr_tcpidletimeout;
+ }
+ break;
+ case TCPS_ESTABLISHED:
+ if (flags & TH_FIN) {
+ state[dir] = TCPS_CLOSE_WAIT;
+ if (!(flags & TH_PUSH) && !dlen &&
+ ostate > TCPS_ESTABLISHED)
+ *age = fr_tcplastack;
+ else
+ *age = fr_tcpclosewait;
+ } else
+ *age = fr_tcpidletimeout;
+ break;
+ case TCPS_CLOSE_WAIT:
+ if ((flags & TH_FIN) && !(flags & TH_PUSH) && !dlen &&
+ ostate > TCPS_ESTABLISHED) {
+ *age = fr_tcplastack;
+ state[dir] = TCPS_LAST_ACK;
+ } else
+ *age = fr_tcpclosewait;
+ break;
+ case TCPS_LAST_ACK:
+ if (flags & TH_ACK) {
+ state[dir] = TCPS_FIN_WAIT_2;
+ if (!(flags & TH_PUSH) && !dlen &&
+ ostate > TCPS_ESTABLISHED)
+ *age = fr_tcplastack;
+ else {
+ *age = fr_tcpclosewait;
+ state[dir] = TCPS_CLOSE_WAIT;
+ }
+ }
+ break;
+ }
+}