1#define __NO_VERSION__
2#include <linux/types.h>
3#include <linux/sched.h>
4#include <linux/timer.h>
5#include <linux/netfilter.h>
6#include <linux/module.h>
7#include <linux/in.h>
8#include <linux/ip.h>
9#include <linux/tcp.h>
10#include <linux/string.h>
11
12#include <net/tcp.h>
13
14#include <linux/netfilter_ipv4/ip_conntrack.h>
15#include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
16#include <linux/netfilter_ipv4/lockhelp.h>
17
18extern int clean_conntrack; // 2009.04 James. wanduck.
19
20#define DEBUGP(format, args...)
21
22/* Protects conntrack->proto.tcp */
23static DECLARE_RWLOCK(tcp_lock);
24
25
26/* Actually, I believe that neither ipmasq (where this code is stolen
27   from) nor ipfilter do it exactly right.  A new conntrack machine taking
28   into account packet loss (which creates uncertainty as to exactly
29   the conntrack of the connection) is required.  RSN.  --RR */
30
31static const char *tcp_conntrack_names[] = {
32	"NONE",
33	"ESTABLISHED",
34	"SYN_SENT",
35	"SYN_RECV",
36	"FIN_WAIT",
37	"TIME_WAIT",
38	"CLOSE",
39	"CLOSE_WAIT",
40	"LAST_ACK",
41	"LISTEN"
42};
43
44#define SECS *HZ
45#define MINS * 60 SECS
46#define HOURS * 60 MINS
47#define DAYS * 24 HOURS
48
49
50static unsigned long tcp_timeouts[]
51= { 30 MINS, 	/*	TCP_CONNTRACK_NONE,	*/
52    1 HOURS,	/*	TCP_CONNTRACK_ESTABLISHED,	*/
53    2 MINS,	/*	TCP_CONNTRACK_SYN_SENT,	*/
54    60 SECS,	/*	TCP_CONNTRACK_SYN_RECV,	*/
55    2 MINS,	/*	TCP_CONNTRACK_FIN_WAIT,	*/
56    2 MINS,	/*	TCP_CONNTRACK_TIME_WAIT,	*/
57    10 SECS,	/*	TCP_CONNTRACK_CLOSE,	*/
58    60 SECS,	/*	TCP_CONNTRACK_CLOSE_WAIT,	*/
59    30 SECS,	/*	TCP_CONNTRACK_LAST_ACK,	*/
60    2 MINS,	/*	TCP_CONNTRACK_LISTEN,	*/
61};
62
63#define sNO TCP_CONNTRACK_NONE
64#define sES TCP_CONNTRACK_ESTABLISHED
65#define sSS TCP_CONNTRACK_SYN_SENT
66#define sSR TCP_CONNTRACK_SYN_RECV
67#define sFW TCP_CONNTRACK_FIN_WAIT
68#define sTW TCP_CONNTRACK_TIME_WAIT
69#define sCL TCP_CONNTRACK_CLOSE
70#define sCW TCP_CONNTRACK_CLOSE_WAIT
71#define sLA TCP_CONNTRACK_LAST_ACK
72#define sLI TCP_CONNTRACK_LISTEN
73#define sIV TCP_CONNTRACK_MAX
74
75static enum tcp_conntrack tcp_conntracks[2][5][TCP_CONNTRACK_MAX] = {
76	{
77/*	ORIGINAL */
78/* 	  sNO, sES, sSS, sSR, sFW, sTW, sCL, sCW, sLA, sLI 	*/
79/*syn*/	{sSS, sES, sSS, sSR, sSS, sSS, sSS, sSS, sSS, sLI },
80/*fin*/	{sTW, sFW, sSS, sTW, sFW, sTW, sCL, sTW, sLA, sLI },
81/*ack*/	{sES, sES, sSS, sES, sFW, sTW, sCL, sCW, sLA, sES },
82/*rst*/ {sCL, sCL, sSS, sCL, sCL, sTW, sCL, sCL, sCL, sCL },
83/*none*/{sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV }
84	},
85	{
86/*	REPLY */
87/* 	  sNO, sES, sSS, sSR, sFW, sTW, sCL, sCW, sLA, sLI 	*/
88/*syn*/	{sSR, sES, sSR, sSR, sSR, sSR, sSR, sSR, sSR, sSR },
89/*fin*/	{sCL, sCW, sSS, sTW, sTW, sTW, sCL, sCW, sLA, sLI },
90/*ack*/	{sCL, sES, sSS, sSR, sFW, sTW, sCL, sCW, sCL, sLI },
91/*rst*/ {sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sLA, sLI },
92/*none*/{sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV }
93	}
94};
95
96static int tcp_pkt_to_tuple(const void *datah, size_t datalen,
97			    struct ip_conntrack_tuple *tuple)
98{
99	const struct tcphdr *hdr = datah;
100
101	tuple->src.u.tcp.port = hdr->source;
102	tuple->dst.u.tcp.port = hdr->dest;
103
104	return 1;
105}
106
107static int tcp_invert_tuple(struct ip_conntrack_tuple *tuple,
108			    const struct ip_conntrack_tuple *orig)
109{
110	tuple->src.u.tcp.port = orig->dst.u.tcp.port;
111	tuple->dst.u.tcp.port = orig->src.u.tcp.port;
112	return 1;
113}
114
115/* Print out the per-protocol part of the tuple. */
116static unsigned int tcp_print_tuple(char *buffer,
117				    const struct ip_conntrack_tuple *tuple)
118{
119	return sprintf(buffer, "sport=%hu dport=%hu ",
120		       ntohs(tuple->src.u.tcp.port),
121		       ntohs(tuple->dst.u.tcp.port));
122}
123
124/* Print out the private part of the conntrack. */
125static unsigned int tcp_print_conntrack(char *buffer,
126					const struct ip_conntrack *conntrack)
127{
128	enum tcp_conntrack state;
129
130// 2009.04 James. wanduck. {
131	if(clean_conntrack == 101)
132		ip_ct_refresh(conntrack, 0);
133// 2009.04 James. wanduck. }
134
135	READ_LOCK(&tcp_lock);
136	state = conntrack->proto.tcp.state;
137	READ_UNLOCK(&tcp_lock);
138
139	return sprintf(buffer, "%s ", tcp_conntrack_names[state]);
140}
141
142static unsigned int get_conntrack_index(const struct tcphdr *tcph)
143{
144	if (tcph->rst) return 3;
145	else if (tcph->syn) return 0;
146	else if (tcph->fin) return 1;
147	else if (tcph->ack) return 2;
148	else return 4;
149}
150
151/* Returns verdict for packet, or -1 for invalid. */
152static int tcp_packet(struct ip_conntrack *conntrack,
153		      struct iphdr *iph, size_t len,
154		      enum ip_conntrack_info ctinfo)
155{
156	enum tcp_conntrack newconntrack, oldtcpstate;
157	struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph + iph->ihl);
158
159	/* We're guaranteed to have the base header, but maybe not the
160           options. */
161	if (len < (iph->ihl + tcph->doff) * 4) {
162		DEBUGP("ip_conntrack_tcp: Truncated packet.\n");
163		return -1;
164	}
165
166	WRITE_LOCK(&tcp_lock);
167	oldtcpstate = conntrack->proto.tcp.state;
168	newconntrack
169		= tcp_conntracks
170		[CTINFO2DIR(ctinfo)]
171		[get_conntrack_index(tcph)][oldtcpstate];
172
173	/* Invalid */
174	if (newconntrack == TCP_CONNTRACK_MAX) {
175		DEBUGP("ip_conntrack_tcp: Invalid dir=%i index=%u conntrack=%u\n",
176		       CTINFO2DIR(ctinfo), get_conntrack_index(tcph),
177		       conntrack->proto.tcp.state);
178		WRITE_UNLOCK(&tcp_lock);
179		return -1;
180	}
181
182	conntrack->proto.tcp.state = newconntrack;
183
184	/* Poor man's window tracking: record SYN/ACK for handshake check */
185	if (oldtcpstate == TCP_CONNTRACK_SYN_SENT
186	    && CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY
187	    && tcph->syn && tcph->ack)
188		conntrack->proto.tcp.handshake_ack
189			= htonl(ntohl(tcph->seq) + 1);
190
191	/* If only reply is a RST, we can consider ourselves not to
192	   have an established connection: this is a fairly common
193	   problem case, so we can delete the conntrack
194	   immediately.  --RR */
195	if (!(conntrack->status & IPS_SEEN_REPLY) && tcph->rst) {
196		WRITE_UNLOCK(&tcp_lock);
197		if (del_timer(&conntrack->timeout))
198			conntrack->timeout.function((unsigned long)conntrack);
199	} else {
200		/* Set ASSURED if we see see valid ack in ESTABLISHED after SYN_RECV */
201		if (oldtcpstate == TCP_CONNTRACK_SYN_RECV
202		    && CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL
203		    && tcph->ack && !tcph->syn
204		    && tcph->ack_seq == conntrack->proto.tcp.handshake_ack)
205			set_bit(IPS_ASSURED_BIT, &conntrack->status);
206
207		WRITE_UNLOCK(&tcp_lock);
208		ip_ct_refresh(conntrack, tcp_timeouts[newconntrack]);
209	}
210
211	return NF_ACCEPT;
212}
213
214/* Called when a new connection for this protocol found. */
215static int tcp_new(struct ip_conntrack *conntrack,
216		   struct iphdr *iph, size_t len)
217{
218	enum tcp_conntrack newconntrack;
219	struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph + iph->ihl);
220
221	/* Don't need lock here: this conntrack not in circulation yet */
222	newconntrack
223		= tcp_conntracks[0][get_conntrack_index(tcph)]
224		[TCP_CONNTRACK_NONE];
225
226	/* Invalid: delete conntrack */
227	if (newconntrack == TCP_CONNTRACK_MAX) {
228		DEBUGP("ip_conntrack_tcp: invalid new deleting.\n");
229		return 0;
230	}
231
232	conntrack->proto.tcp.state = newconntrack;
233	return 1;
234}
235
236static int tcp_exp_matches_pkt(struct ip_conntrack_expect *exp,
237			       struct sk_buff **pskb)
238{
239	struct iphdr *iph = (*pskb)->nh.iph;
240	struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph + iph->ihl);
241	unsigned int datalen;
242
243	datalen = (*pskb)->len - iph->ihl*4 - tcph->doff*4;
244
245	return between(exp->seq, ntohl(tcph->seq), ntohl(tcph->seq) + datalen);
246}
247
248struct ip_conntrack_protocol ip_conntrack_protocol_tcp
249= { { NULL, NULL }, IPPROTO_TCP, "tcp",
250    tcp_pkt_to_tuple, tcp_invert_tuple, tcp_print_tuple, tcp_print_conntrack,
251    tcp_packet, tcp_new, NULL, tcp_exp_matches_pkt, NULL };
252