/* Returns verdict for packet, and may modify conntracktype */
static int udp_packet(struct ip_conntrack *conntrack,
		      struct iphdr *iph, size_t len,
		      enum ip_conntrack_info ctinfo)
{
	/* If we've seen traffic both ways, this is some kind of UDP
	   stream.  Extend timeout. */
	if (test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status)) {
		ip_ct_refresh_acct(conntrack,ctinfo,iph,ip_ct_udp_timeout_stream);
		/* Also, more likely to be important, and not a probe */
		set_bit(IPS_ASSURED_BIT, &conntrack->status);
	} else
		ip_ct_refresh_acct(conntrack,ctinfo,iph, ip_ct_udp_timeout);

	return NF_ACCEPT;
}
static int timeout_ct_or_exp(const struct ip_conntrack_tuple *t)
{
	struct ip_conntrack_tuple_hash *h;
	struct ip_conntrack_expect *exp;

	DEBUGP("trying to timeout ct or exp for tuple ");
	DUMP_TUPLE(t);

	h = __ip_conntrack_find(t, NULL);
	if (h)  {
		struct ip_conntrack *sibling = tuplehash_to_ctrack(h);
		DEBUGP("setting timeout of conntrack %p to 0\n", sibling);
		sibling->proto.gre.timeout = 0;
		sibling->proto.gre.stream_timeout = 0;
		/* refresh_acct will not modify counters if skb == NULL */
		ip_ct_refresh_acct(sibling, 0, NULL, 0);
		return 1;
	} else {
		exp = __ip_conntrack_exp_find(t);
		if (exp) {
			DEBUGP("unexpect_related of expect %p\n", exp);
			ip_conntrack_unexpect_related(exp);
			return 1;
		}
	}

	return 0;
}
/* Returns verdict for packet, or -1 for invalid. */
static int packet(struct ip_conntrack *conntrack,
		  const struct sk_buff *skb,
		  enum ip_conntrack_info ctinfo)
{
	ip_ct_refresh_acct(conntrack, ctinfo, skb, ip_ct_generic_timeout);
	return NF_ACCEPT;
}
/* timeout GRE data connections */
static int pptp_timeout_related(struct ip_conntrack *ct, enum ip_conntrack_info ctinfo)
{
	struct list_head *cur_item, *next;
	struct ip_conntrack_expect *exp;

	/* FIXME: do we have to lock something ? */
	for (cur_item = ct->sibling_list.next;
	    cur_item != &ct->sibling_list; cur_item = next) {
		next = cur_item->next;
		exp = list_entry(cur_item, struct ip_conntrack_expect,
				 expected_list);

		ip_ct_gre_keymap_destroy(exp);
		if (!exp->sibling) {
			ip_conntrack_unexpect_related(exp);
			continue;
		}

		DEBUGP("setting timeout of conntrack %p to 0\n",
			exp->sibling);
		exp->sibling->proto.gre.timeout = 0;
		exp->sibling->proto.gre.stream_timeout = 0;
		ip_ct_refresh_acct(exp->sibling, ctinfo, NULL, 0);
	}

	return 0;
}
/* Returns verdict for packet, or -1 for invalid. */
static int established(struct ip_conntrack *conntrack,
		       struct iphdr *iph, size_t len,
		       enum ip_conntrack_info ctinfo)
{
	ip_ct_refresh_acct(conntrack, ctinfo, iph, ip_ct_generic_timeout);
	return NF_ACCEPT;
}
/* Returns verdict for packet, or -1 for invalid. */
static int icmp_packet(struct ip_conntrack *ct,
		       const struct sk_buff *skb,
		       enum ip_conntrack_info ctinfo)
{
	/* Try to delete connection immediately after all replies:
           won't actually vanish as we still have skb, and del_timer
           means this will only run once even if count hits zero twice
           (theoretically possible with SMP) */
	if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) {
		if (atomic_dec_and_test(&ct->proto.icmp.count)
		    && del_timer(&ct->timeout))
			ct->timeout.function((unsigned long)ct);
	} else {
		atomic_inc(&ct->proto.icmp.count);
		ip_conntrack_event_cache(IPCT_PROTOINFO_VOLATILE, skb);
		//if (ct->status & IPS_SEEN_REPLY)
		//TT 20060103 : let ct aging out if no reply in ICMP_TIMEOUT period
		if ((ct->status & IPS_SEEN_REPLY) || !(ct->timeout.expires))
			ip_ct_refresh_acct(ct, ctinfo, skb, ip_ct_icmp_timeout);
	}
	return NF_ACCEPT;
}
/* Returns verdict for packet, or -1 for invalid. */
static int tcp_packet(struct ip_conntrack *conntrack,
		      const struct sk_buff *skb,
		      enum ip_conntrack_info ctinfo)
{
	enum tcp_conntrack new_state, old_state;
	enum ip_conntrack_dir dir;
	struct iphdr *iph = skb->nh.iph;
	struct tcphdr *th, _tcph;
	unsigned long timeout;
	unsigned int index;
	
	th = skb_header_pointer(skb, iph->ihl * 4,
				sizeof(_tcph), &_tcph);
	BUG_ON(th == NULL);
	
	write_lock_bh(&tcp_lock);
	old_state = conntrack->proto.tcp.state;
	dir = CTINFO2DIR(ctinfo);
	index = get_conntrack_index(th);
	new_state = tcp_conntracks[dir][index][old_state];

	switch (new_state) {
	case TCP_CONNTRACK_IGNORE:
		/* Ignored packets:
		 * 
		 * a) SYN in ORIGINAL
		 * b) SYN/ACK in REPLY
		 * c) ACK in reply direction after initial SYN in original.
		 */

		/*	jimmy added 20080616, when SPI enable, block 
			invlaid syn+ack with unexpected sequence 
		*/
		if(sysctl_spi_enable){
			if((index == TCP_SYNACK_SET)  &&  
				(conntrack->proto.tcp.last_index != TCP_SYN_SET) &&
				//(conntrack->proto.tcp.last_dir != dir) && // Control: 21 <-> random ,Data: random <-> random
				(ntohl(th->ack_seq) != conntrack->proto.tcp.last_end)
				)
			{
				if (LOG_INVALID(IPPROTO_TCP)){
					nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL,
						"ip_ct_tcp: Invalid SynAck packet ");
				}
				
				printk(KERN_NOTICE "Blocked incoming TCP SynAck packet from %u.%u.%u.%u:%hu to %u.%u.%u.%u:%hu with unexpected sequence\n", NIPQUAD(iph->saddr), ntohs(th->source),
										NIPQUAD(iph->daddr), ntohs(th->dest));

				write_unlock_bh(&tcp_lock);
	    		return -NF_DROP;
			}
		}
	/* ------------------------------------------------------------- */
		if (index == TCP_SYNACK_SET
		    && conntrack->proto.tcp.last_index == TCP_SYN_SET
		    && conntrack->proto.tcp.last_dir != dir
		    && ntohl(th->ack_seq) ==
		    	     conntrack->proto.tcp.last_end) {
			/* This SYN/ACK acknowledges a SYN that we earlier 
			 * ignored as invalid. This means that the client and
			 * the server are both in sync, while the firewall is
			 * not. We kill this session and block the SYN/ACK so
			 * that the client cannot but retransmit its SYN and 
			 * thus initiate a clean new session.
			 */
		    	write_unlock_bh(&tcp_lock);
			if (LOG_INVALID(IPPROTO_TCP))
				nf_log_packet(PF_INET, 0, skb, NULL, NULL,
					      NULL, "ip_ct_tcp: "
					      "killing out of sync session ");
		    	if (del_timer(&conntrack->timeout))
		    		conntrack->timeout.function((unsigned long)
		    					    conntrack);
		    	return -NF_DROP;
		}
		conntrack->proto.tcp.last_index = index;
		conntrack->proto.tcp.last_dir = dir;
		conntrack->proto.tcp.last_seq = ntohl(th->seq);
		conntrack->proto.tcp.last_end = 
		    segment_seq_plus_len(ntohl(th->seq), skb->len, iph, th);
		
		write_unlock_bh(&tcp_lock);
		if (LOG_INVALID(IPPROTO_TCP))
			nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL,
				  "ip_ct_tcp: invalid packet ignored ");
		return NF_ACCEPT;
	case TCP_CONNTRACK_MAX:
		/* Invalid packet */
		DEBUGP("ip_ct_tcp: Invalid dir=%i index=%u ostate=%u\n",
		       dir, get_conntrack_index(th),
		       old_state);
		write_unlock_bh(&tcp_lock);
		if (LOG_INVALID(IPPROTO_TCP))
			nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL,
				  "ip_ct_tcp: invalid state ");
		return -NF_ACCEPT;
	case TCP_CONNTRACK_SYN_SENT:
		if (old_state < TCP_CONNTRACK_TIME_WAIT)
			break;
		if ((conntrack->proto.tcp.seen[dir].flags &
		         IP_CT_TCP_FLAG_CLOSE_INIT)
		    || after(ntohl(th->seq),
		    	     conntrack->proto.tcp.seen[dir].td_end)) {	
		    	/* Attempt to reopen a closed connection.
		    	* Delete this connection and look up again. */
		    	write_unlock_bh(&tcp_lock);
		    	if (del_timer(&conntrack->timeout))
		    		conntrack->timeout.function((unsigned long)
		    					    conntrack);
		    	return -NF_REPEAT;
		} else {
			write_unlock_bh(&tcp_lock);
			if (LOG_INVALID(IPPROTO_TCP))
				nf_log_packet(PF_INET, 0, skb, NULL, NULL,
					      NULL, "ip_ct_tcp: invalid SYN");
			return -NF_ACCEPT;
		}
	case TCP_CONNTRACK_CLOSE:
		if (index == TCP_RST_SET
		    && ((test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status)
		         && conntrack->proto.tcp.last_index == TCP_SYN_SET)
		        || (!test_bit(IPS_ASSURED_BIT, &conntrack->status)
		            && conntrack->proto.tcp.last_index == TCP_ACK_SET))
		    && ntohl(th->ack_seq) == conntrack->proto.tcp.last_end) {
			/* RST sent to invalid SYN or ACK we had let trough
			 * at a) and c) above:
			 *
			 * a) SYN was in window then
			 * c) we hold a half-open connection.
			 *
			 * Delete our connection entry.
			 * We skip window checking, because packet might ACK
			 * segments we ignored. */
			goto in_window;
		}
		/* Just fall trough */
	default:
		/* Keep compilers happy. */
		break;
	}
	if (sysctl_spi_enable){
	if (!tcp_in_window(&conntrack->proto.tcp, dir, index, 
			   skb, iph, th)) {
		write_unlock_bh(&tcp_lock);
		return -NF_ACCEPT;
	}
	}	
	
    in_window:
	/* From now on we have got in-window packets */	
	conntrack->proto.tcp.last_index = index;

	DEBUGP("tcp_conntracks: src=%u.%u.%u.%u:%hu dst=%u.%u.%u.%u:%hu "
	       "syn=%i ack=%i fin=%i rst=%i old=%i new=%i\n",
		NIPQUAD(iph->saddr), ntohs(th->source),
		NIPQUAD(iph->daddr), ntohs(th->dest),
		(th->syn ? 1 : 0), (th->ack ? 1 : 0),
		(th->fin ? 1 : 0), (th->rst ? 1 : 0),
		old_state, new_state);

	conntrack->proto.tcp.state = new_state;
	if (old_state != new_state 
	    && (new_state == TCP_CONNTRACK_FIN_WAIT
	    	|| new_state == TCP_CONNTRACK_CLOSE))
		conntrack->proto.tcp.seen[dir].flags |= IP_CT_TCP_FLAG_CLOSE_INIT;
	timeout = conntrack->proto.tcp.retrans >= ip_ct_tcp_max_retrans
		  && *tcp_timeouts[new_state] > ip_ct_tcp_timeout_max_retrans
		  ? ip_ct_tcp_timeout_max_retrans : *tcp_timeouts[new_state];
	write_unlock_bh(&tcp_lock);

	ip_conntrack_event_cache(IPCT_PROTOINFO_VOLATILE, skb);
	if (new_state != old_state)
		ip_conntrack_event_cache(IPCT_PROTOINFO, skb);

	if (!test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status)) {
		/* If only reply is a RST, we can consider ourselves not to
		   have an established connection: this is a fairly common
		   problem case, so we can delete the conntrack
		   immediately.  --RR */
		if (th->rst) {
			if (del_timer(&conntrack->timeout))
				conntrack->timeout.function((unsigned long)
							    conntrack);
			return NF_ACCEPT;
		}
	} else if (!test_bit(IPS_ASSURED_BIT, &conntrack->status)
		   && (old_state == TCP_CONNTRACK_SYN_RECV
		       || old_state == TCP_CONNTRACK_ESTABLISHED)
		   && new_state == TCP_CONNTRACK_ESTABLISHED) {
		/* Set ASSURED if we see see valid ack in ESTABLISHED 
		   after SYN_RECV or a valid answer for a picked up 
		   connection. */
		set_bit(IPS_ASSURED_BIT, &conntrack->status);
		ip_conntrack_event_cache(IPCT_STATUS, skb);
	}
	ip_ct_refresh_acct(conntrack, ctinfo, skb, timeout);

	return NF_ACCEPT;
}
/* Returns verdict for packet, or -1 for invalid. */
static int tcp_packet(struct ip_conntrack *conntrack,
		      const struct sk_buff *skb,
		      enum ip_conntrack_info ctinfo)
{
	enum tcp_conntrack new_state, old_state;
	enum ip_conntrack_dir dir;
	struct iphdr *iph = skb->nh.iph;
	struct tcphdr *th, _tcph;
	unsigned long timeout;
	unsigned int index;
	
	th = skb_header_pointer(skb, iph->ihl * 4,
				sizeof(_tcph), &_tcph);
	BUG_ON(th == NULL);
	
	WRITE_LOCK(&tcp_lock);
	old_state = conntrack->proto.tcp.state;
	dir = CTINFO2DIR(ctinfo);
	index = get_conntrack_index(th);
	new_state = tcp_conntracks[dir][index][old_state];

	switch (new_state) {
	case TCP_CONNTRACK_IGNORE:
		/* Either SYN in ORIGINAL
		 * or SYN/ACK in REPLY
		 * or ACK in REPLY direction (half-open connection). */
		if (index == TCP_SYNACK_SET
		    && conntrack->proto.tcp.last_index == TCP_SYN_SET
		    && conntrack->proto.tcp.last_dir != dir
		    && after(ntohl(th->ack_seq),
		    	     conntrack->proto.tcp.last_seq)) {
			/* This SYN/ACK acknowledges a SYN that we earlier 
			 * ignored as invalid. This means that the client and
			 * the server are both in sync, while the firewall is
			 * not. We kill this session and block the SYN/ACK so
			 * that the client cannot but retransmit its SYN and 
			 * thus initiate a clean new session.
			 */
		    	WRITE_UNLOCK(&tcp_lock);
			if (LOG_INVALID(IPPROTO_TCP))
				nf_log_packet(PF_INET, 0, skb, NULL, NULL, 
					  "ip_ct_tcp: killing out of sync session ");
		    	if (del_timer(&conntrack->timeout))
		    		conntrack->timeout.function((unsigned long)
		    					    conntrack);
		    	return -NF_DROP;
		}
		conntrack->proto.tcp.last_index = index;
		conntrack->proto.tcp.last_dir = dir;
		conntrack->proto.tcp.last_seq = ntohl(th->seq);
		
		WRITE_UNLOCK(&tcp_lock);
		if (LOG_INVALID(IPPROTO_TCP))
			nf_log_packet(PF_INET, 0, skb, NULL, NULL, 
				  "ip_ct_tcp: invalid packet ignored ");
		return NF_ACCEPT;
	case TCP_CONNTRACK_MAX:
		/* Invalid packet */
		DEBUGP("ip_ct_tcp: Invalid dir=%i index=%u ostate=%u\n",
		       dir, get_conntrack_index(th),
		       old_state);
		WRITE_UNLOCK(&tcp_lock);
		if (LOG_INVALID(IPPROTO_TCP))
			nf_log_packet(PF_INET, 0, skb, NULL, NULL, 
				  "ip_ct_tcp: invalid state ");
		return -NF_ACCEPT;
	case TCP_CONNTRACK_SYN_SENT:
		if (old_state >= TCP_CONNTRACK_TIME_WAIT) {	
		    	/* Attempt to reopen a closed connection.
		    	* Delete this connection and look up again. */
		    	WRITE_UNLOCK(&tcp_lock);
		    	if (del_timer(&conntrack->timeout))
		    		conntrack->timeout.function((unsigned long)
		    					    conntrack);
		    	return -NF_REPEAT;
		}
		break;
	case TCP_CONNTRACK_CLOSE:
		if (index == TCP_RST_SET
		    && ((test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status)
		         && conntrack->proto.tcp.last_index <= TCP_SYNACK_SET)
		        || (!test_bit(IPS_ASSURED_BIT, &conntrack->status)
			 && conntrack->proto.tcp.last_index == TCP_ACK_SET))
		    && after(ntohl(th->ack_seq),
		    	     conntrack->proto.tcp.last_seq)) {
			/* Ignore RST closing down invalid SYN or ACK
			   we had let trough. */ 
		    	WRITE_UNLOCK(&tcp_lock);
			if (LOG_INVALID(IPPROTO_TCP))
				nf_log_packet(PF_INET, 0, skb, NULL, NULL, 
					  "ip_ct_tcp: invalid RST (ignored) ");
			return NF_ACCEPT;
		}
		/* Just fall trough */
	default:
		/* Keep compilers happy. */
		break;
	}

	if (!tcp_in_window(&conntrack->proto.tcp, dir, &index, 
			   skb, iph, th)) {
		WRITE_UNLOCK(&tcp_lock);
		return -NF_ACCEPT;
	}
	/* From now on we have got in-window packets */
	
	/* If FIN was trimmed off, we don't change state. */
	conntrack->proto.tcp.last_index = index;
	new_state = tcp_conntracks[dir][index][old_state];

	DEBUGP("tcp_conntracks: src=%u.%u.%u.%u:%hu dst=%u.%u.%u.%u:%hu "
	       "syn=%i ack=%i fin=%i rst=%i old=%i new=%i\n",
		NIPQUAD(iph->saddr), ntohs(th->source),
		NIPQUAD(iph->daddr), ntohs(th->dest),
		(th->syn ? 1 : 0), (th->ack ? 1 : 0),
		(th->fin ? 1 : 0), (th->rst ? 1 : 0),
		old_state, new_state);

	conntrack->proto.tcp.state = new_state;
	timeout = conntrack->proto.tcp.retrans >= ip_ct_tcp_max_retrans
		  && *tcp_timeouts[new_state] > ip_ct_tcp_timeout_max_retrans
		  ? ip_ct_tcp_timeout_max_retrans : *tcp_timeouts[new_state];
	WRITE_UNLOCK(&tcp_lock);

	if (!test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status)) {
		/* If only reply is a RST, we can consider ourselves not to
		   have an established connection: this is a fairly common
		   problem case, so we can delete the conntrack
		   immediately.  --RR */
		if (th->rst) {
			if (del_timer(&conntrack->timeout))
				conntrack->timeout.function((unsigned long)
							    conntrack);
			return NF_ACCEPT;
		}
	} else if (!test_bit(IPS_ASSURED_BIT, &conntrack->status)
		   && (old_state == TCP_CONNTRACK_SYN_RECV
		       || old_state == TCP_CONNTRACK_ESTABLISHED)
		   && new_state == TCP_CONNTRACK_ESTABLISHED) {
		/* Set ASSURED if we see see valid ack in ESTABLISHED 
		   after SYN_RECV or a valid answer for a picked up 
		   connection. */
			set_bit(IPS_ASSURED_BIT, &conntrack->status);
	}
	ip_ct_refresh_acct(conntrack, ctinfo, skb, timeout);

	return NF_ACCEPT;
}
/* Returns verdict for packet, or -1 for invalid. */
static int tcp_packet(struct ip_conntrack *conntrack,
		      enum ip_conntrack_info ctinfo,
			  struct sk_buff *skb, int hooknum)
{
	struct iphdr *iph = skb->nh.iph;
	size_t len = skb->len;
	enum tcp_conntrack new_state, old_state;
	enum ip_conntrack_dir dir;
	struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph + iph->ihl);
	unsigned long timeout;
	unsigned int index;
	
	/* Do not handle unclean packets, which could cause false alarms. */
	if (tcp_error(skb, hooknum) != NF_ACCEPT)
		return -NF_ACCEPT;
	
	WRITE_LOCK(&tcp_lock);
	old_state = conntrack->proto.tcp.state;
	dir = CTINFO2DIR(ctinfo);
	index = get_conntrack_index(tcph);
	new_state = tcp_conntracks[dir][index][old_state];

	switch (new_state) {
	case TCP_CONNTRACK_IGNORE:
		/* Either SYN in ORIGINAL
		 * or SYN/ACK in REPLY. */
		if (index == TCP_SYNACK_SET
		    && conntrack->proto.tcp.last_index == TCP_SYN_SET
		    && conntrack->proto.tcp.last_dir != dir
		    && ntohl(tcph->ack_seq) ==
		    	     conntrack->proto.tcp.last_end) {
			/* This SYN/ACK acknowledges a SYN that we earlier 
			 * ignored as invalid. This means that the client and
			 * the server are both in sync, while the firewall is
			 * not. We kill this session and block the SYN/ACK so
			 * that the client cannot but retransmit its SYN and 
			 * thus initiate a clean new session.
			 */
		    	WRITE_UNLOCK(&tcp_lock);
			if (NET_RATELIMIT(ip_ct_tcp_log_invalid))
				nf_log(PF_INET, (char *)iph, len, 
					"ip_ct_tcp: killing out of sync session ");
		    	if (del_timer(&conntrack->timeout))
		    		conntrack->timeout.function((unsigned long)
		    					    conntrack);
		    	return -NF_DROP;
		}
		conntrack->proto.tcp.last_index = index;
		conntrack->proto.tcp.last_dir = dir;
		conntrack->proto.tcp.last_seq = ntohl(tcph->seq);
		conntrack->proto.tcp.last_end = 
		    segment_seq_plus_len(ntohl(tcph->seq), len, iph, tcph);
		
		WRITE_UNLOCK(&tcp_lock);
		if (NET_RATELIMIT(ip_ct_tcp_log_invalid))
			nf_log(PF_INET, (char *)iph, len, 
				"ip_ct_tcp: invalid packet ignored ");
		return NF_ACCEPT;
	case TCP_CONNTRACK_MAX:
		/* Invalid packet */
		DEBUGP("ip_ct_tcp: Invalid dir=%i index=%u ostate=%u\n",
		       dir, get_conntrack_index(tcph),
		       old_state);
		WRITE_UNLOCK(&tcp_lock);
		if (NET_RATELIMIT(ip_ct_tcp_log_invalid))
			nf_log(PF_INET, (char *)iph, len, 
				"ip_ct_tcp: invalid state ");
		return -NF_ACCEPT;
	case TCP_CONNTRACK_SYN_SENT:
		if (old_state < TCP_CONNTRACK_TIME_WAIT)
			break;
		if ((conntrack->proto.tcp.seen[dir].flags &
		         IP_CT_TCP_FLAG_CLOSE_INIT)
		    || after(ntohl(tcph->seq),
		    	     conntrack->proto.tcp.seen[dir].td_end)) {	
		    	/* Attempt to reopen a closed connection.
		    	* Delete this connection and look up again. */
		    	WRITE_UNLOCK(&tcp_lock);
		    	if (del_timer(&conntrack->timeout))
		    		conntrack->timeout.function((unsigned long)
		    					    conntrack);
		    	return -NF_REPEAT;
		} else {
			WRITE_UNLOCK(&tcp_lock);
			if (NET_RATELIMIT(ip_ct_tcp_log_invalid))
				nf_log(PF_INET, (char *)iph, len, 
					"ip_ct_tcp: invalid SYN ");
			return -NF_ACCEPT;
		}
	case TCP_CONNTRACK_CLOSE:
		if (index == TCP_RST_SET
		    && test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status)
		    && conntrack->proto.tcp.last_index == TCP_SYN_SET
		    && ntohl(tcph->ack_seq) == conntrack->proto.tcp.last_end) {
			/* RST sent to invalid SYN we had let trough 
			 * SYN was in window then, tear down connection.
			 * We skip window checking, because packet might ACK
			 * segments we ignored in the SYN. */
			goto in_window;
		}
		/* Just fall trough */
	default:
		/* Keep compilers happy. */
		break;
	}

	if (!tcp_in_window(&conntrack->proto.tcp, dir, index, 
			   iph, len, tcph)) {
		WRITE_UNLOCK(&tcp_lock);
		return -NF_ACCEPT;
	}
    in_window:
	/* From now on we have got in-window packets */	
	conntrack->proto.tcp.last_index = index;

	DEBUGP("tcp_conntracks: src=%u.%u.%u.%u:%hu dst=%u.%u.%u.%u:%hu "
	       "syn=%i ack=%i fin=%i rst=%i old=%i new=%i\n",
		NIPQUAD(iph->saddr), ntohs(tcph->source),
		NIPQUAD(iph->daddr), ntohs(tcph->dest),
		(tcph->syn ? 1 : 0), (tcph->ack ? 1 : 0),
		(tcph->fin ? 1 : 0), (tcph->rst ? 1 : 0),
		old_state, new_state);

	conntrack->proto.tcp.state = new_state;
	if (old_state != new_state 
	    && (new_state == TCP_CONNTRACK_FIN_WAIT
	    	|| new_state == TCP_CONNTRACK_CLOSE))
		conntrack->proto.tcp.seen[dir].flags |= IP_CT_TCP_FLAG_CLOSE_INIT;
	timeout = conntrack->proto.tcp.retrans >= ip_ct_tcp_max_retrans
		  && *tcp_timeouts[new_state] > ip_ct_tcp_timeout_max_retrans
		  ? ip_ct_tcp_timeout_max_retrans : *tcp_timeouts[new_state];
	WRITE_UNLOCK(&tcp_lock);

	if (!test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status)) {
		/* If only reply is a RST, we can consider ourselves not to
		   have an established connection: this is a fairly common
		   problem case, so we can delete the conntrack
		   immediately.  --RR */
		if (tcph->rst) {
			if (del_timer(&conntrack->timeout))
				conntrack->timeout.function((unsigned long)
							    conntrack);
			return NF_ACCEPT;
		}
	} else if (!test_bit(IPS_ASSURED_BIT, &conntrack->status)
		   && (old_state == TCP_CONNTRACK_SYN_RECV
		       || old_state == TCP_CONNTRACK_ESTABLISHED)
		   && new_state == TCP_CONNTRACK_ESTABLISHED) {
		/* Set ASSURED if we see see valid ack in ESTABLISHED 
		   after SYN_RECV or a valid answer for a picked up 
		   connection. */
			set_bit(IPS_ASSURED_BIT, &conntrack->status);
	}
	ip_ct_refresh_acct(conntrack, ctinfo, iph, timeout);

	return NF_ACCEPT;
}
/* Returns verdict for packet, or -1 for invalid. */
static int tcp_packet(struct ip_conntrack *conntrack,
		      struct iphdr *iph, size_t len,
		      enum ip_conntrack_info ctinfo)
{
	enum tcp_conntrack newconntrack, oldtcpstate;
	struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph + iph->ihl);

	/* We're guaranteed to have the base header, but maybe not the
           options. */
	if (len < (iph->ihl + tcph->doff) * 4) {
		DEBUGP("ip_conntrack_tcp: Truncated packet.\n");
		return -1;
	}

	WRITE_LOCK(&tcp_lock);
	oldtcpstate = conntrack->proto.tcp.state;
	newconntrack
		= tcp_conntracks
		[CTINFO2DIR(ctinfo)]
		[get_conntrack_index(tcph)][oldtcpstate];

	/* Invalid */
	if (newconntrack == TCP_CONNTRACK_MAX) {
		DEBUGP("ip_conntrack_tcp: Invalid dir=%i index=%u conntrack=%u\n",
		       CTINFO2DIR(ctinfo), get_conntrack_index(tcph),
		       conntrack->proto.tcp.state);
		WRITE_UNLOCK(&tcp_lock);
		return -1;
	}

	conntrack->proto.tcp.state = newconntrack;

	/* Poor man's window tracking: record SYN/ACK for handshake check */
	if (oldtcpstate == TCP_CONNTRACK_SYN_SENT
	    && CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY
	    && tcph->syn && tcph->ack)
		conntrack->proto.tcp.handshake_ack
			= htonl(ntohl(tcph->seq) + 1);

	/* If only reply is a RST, we can consider ourselves not to
	   have an established connection: this is a fairly common
	   problem case, so we can delete the conntrack
	   immediately.  --RR */
	if (!test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status) && tcph->rst) {
		WRITE_UNLOCK(&tcp_lock);
		if (del_timer(&conntrack->timeout))
			conntrack->timeout.function((unsigned long)conntrack);
	} else {
		/* Set ASSURED if we see see valid ack in ESTABLISHED after SYN_RECV */
		if (oldtcpstate == TCP_CONNTRACK_SYN_RECV
		    && CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL
		    && tcph->ack && !tcph->syn
		    && tcph->ack_seq == conntrack->proto.tcp.handshake_ack)
			set_bit(IPS_ASSURED_BIT, &conntrack->status);

		WRITE_UNLOCK(&tcp_lock);
		ip_ct_refresh_acct(conntrack,ctinfo,iph, *tcp_timeouts[newconntrack]);
	}

	return NF_ACCEPT;
}