예제 #1
0
static unsigned int
target(struct sk_buff **pskb,
       const struct net_device *in,
       const struct net_device *out,
       unsigned int hooknum,
       const struct xt_target *target,
       const void *targinfo)
{
	const struct xt_connmark_target_info *markinfo = targinfo;
	u_int32_t diff;
	u_int32_t nfmark;
	u_int32_t newmark;
	u_int32_t ctinfo;
	u_int32_t *ctmark = nf_ct_get_mark(*pskb, &ctinfo);

	if (ctmark) {
		switch(markinfo->mode) {
		case XT_CONNMARK_SET:
			newmark = (*ctmark & ~markinfo->mask) | markinfo->mark;
			if (newmark != *ctmark) {
				*ctmark = newmark;
#if defined(CONFIG_IP_NF_CONNTRACK) || defined(CONFIG_IP_NF_CONNTRACK_MODULE)
				ip_conntrack_event_cache(IPCT_MARK, *pskb);
#else
				nf_conntrack_event_cache(IPCT_MARK, *pskb);
#endif
		}
			break;
		case XT_CONNMARK_SAVE:
			newmark = (*ctmark & ~markinfo->mask) |
				  ((*pskb)->nfmark & markinfo->mask);
			if (*ctmark != newmark) {
				*ctmark = newmark;
#if defined(CONFIG_IP_NF_CONNTRACK) || defined(CONFIG_IP_NF_CONNTRACK_MODULE)
				ip_conntrack_event_cache(IPCT_MARK, *pskb);
#else
				nf_conntrack_event_cache(IPCT_MARK, *pskb);
#endif
			}
			break;
		case XT_CONNMARK_RESTORE:
			nfmark = (*pskb)->nfmark;
			diff = (*ctmark ^ nfmark) & markinfo->mask;
			if (diff != 0)
				(*pskb)->nfmark = nfmark ^ diff;
			break;
		}
	}

	return XT_CONTINUE;
}
/* 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;
}