⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 nf_conntrack_proto_tcp.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
					  + TCPOLEN_SACK_PERBLOCK)			    && !((opsize - TCPOLEN_SACK_BASE)				 % TCPOLEN_SACK_PERBLOCK)) {				for (i = 0;				     i < (opsize - TCPOLEN_SACK_BASE);				     i += TCPOLEN_SACK_PERBLOCK) {					tmp = ntohl(*((__be32 *)(ptr+i)+1));					if (after(tmp, *sack))						*sack = tmp;				}				return;			}			ptr += opsize - 2;			length -= opsize;		}	}}static int tcp_in_window(struct nf_conn *ct,			 struct ip_ct_tcp *state,			 enum ip_conntrack_dir dir,			 unsigned int index,			 const struct sk_buff *skb,			 unsigned int dataoff,			 struct tcphdr *tcph,			 int pf){	struct ip_ct_tcp_state *sender = &state->seen[dir];	struct ip_ct_tcp_state *receiver = &state->seen[!dir];	struct nf_conntrack_tuple *tuple = &ct->tuplehash[dir].tuple;	__u32 seq, ack, sack, end, win, swin;	int res;	/*	 * Get the required data from the packet.	 */	seq = ntohl(tcph->seq);	ack = sack = ntohl(tcph->ack_seq);	win = ntohs(tcph->window);	end = segment_seq_plus_len(seq, skb->len, dataoff, tcph);	if (receiver->flags & IP_CT_TCP_FLAG_SACK_PERM)		tcp_sack(skb, dataoff, tcph, &sack);	pr_debug("tcp_in_window: START\n");	pr_debug("tcp_in_window: ");	NF_CT_DUMP_TUPLE(tuple);	pr_debug("seq=%u ack=%u sack=%u win=%u end=%u\n",		 seq, ack, sack, win, end);	pr_debug("tcp_in_window: sender end=%u maxend=%u maxwin=%u scale=%i "		 "receiver end=%u maxend=%u maxwin=%u scale=%i\n",		 sender->td_end, sender->td_maxend, sender->td_maxwin,		 sender->td_scale,		 receiver->td_end, receiver->td_maxend, receiver->td_maxwin,		 receiver->td_scale);	if (sender->td_end == 0) {		/*		 * Initialize sender data.		 */		if (tcph->syn && tcph->ack) {			/*			 * Outgoing SYN-ACK in reply to a SYN.			 */			sender->td_end =			sender->td_maxend = end;			sender->td_maxwin = (win == 0 ? 1 : win);			tcp_options(skb, dataoff, tcph, sender);			/*			 * RFC 1323:			 * Both sides must send the Window Scale option			 * to enable window scaling in either direction.			 */			if (!(sender->flags & IP_CT_TCP_FLAG_WINDOW_SCALE			      && receiver->flags & IP_CT_TCP_FLAG_WINDOW_SCALE))				sender->td_scale =				receiver->td_scale = 0;		} else {			/*			 * We are in the middle of a connection,			 * its history is lost for us.			 * Let's try to use the data from the packet.			 */			sender->td_end = end;			sender->td_maxwin = (win == 0 ? 1 : win);			sender->td_maxend = end + sender->td_maxwin;		}	} else if (((state->state == TCP_CONNTRACK_SYN_SENT		     && dir == IP_CT_DIR_ORIGINAL)		   || (state->state == TCP_CONNTRACK_SYN_RECV		     && dir == IP_CT_DIR_REPLY))		   && after(end, sender->td_end)) {		/*		 * RFC 793: "if a TCP is reinitialized ... then it need		 * not wait at all; it must only be sure to use sequence		 * numbers larger than those recently used."		 */		sender->td_end =		sender->td_maxend = end;		sender->td_maxwin = (win == 0 ? 1 : win);		tcp_options(skb, dataoff, tcph, sender);	}	if (!(tcph->ack)) {		/*		 * If there is no ACK, just pretend it was set and OK.		 */		ack = sack = receiver->td_end;	} else if (((tcp_flag_word(tcph) & (TCP_FLAG_ACK|TCP_FLAG_RST)) ==		    (TCP_FLAG_ACK|TCP_FLAG_RST))		   && (ack == 0)) {		/*		 * Broken TCP stacks, that set ACK in RST packets as well		 * with zero ack value.		 */		ack = sack = receiver->td_end;	}	if (seq == end	    && (!tcph->rst		|| (seq == 0 && state->state == TCP_CONNTRACK_SYN_SENT)))		/*		 * Packets contains no data: we assume it is valid		 * and check the ack value only.		 * However RST segments are always validated by their		 * SEQ number, except when seq == 0 (reset sent answering		 * SYN.		 */		seq = end = sender->td_end;	pr_debug("tcp_in_window: ");	NF_CT_DUMP_TUPLE(tuple);	pr_debug("seq=%u ack=%u sack =%u win=%u end=%u\n",		 seq, ack, sack, win, end);	pr_debug("tcp_in_window: sender end=%u maxend=%u maxwin=%u scale=%i "		 "receiver end=%u maxend=%u maxwin=%u scale=%i\n",		 sender->td_end, sender->td_maxend, sender->td_maxwin,		 sender->td_scale,		 receiver->td_end, receiver->td_maxend, receiver->td_maxwin,		 receiver->td_scale);	pr_debug("tcp_in_window: I=%i II=%i III=%i IV=%i\n",		 before(seq, sender->td_maxend + 1),		 after(end, sender->td_end - receiver->td_maxwin - 1),		 before(sack, receiver->td_end + 1),		 after(ack, receiver->td_end - MAXACKWINDOW(sender)));	if (before(seq, sender->td_maxend + 1) &&	    after(end, sender->td_end - receiver->td_maxwin - 1) &&	    before(sack, receiver->td_end + 1) &&	    after(ack, receiver->td_end - MAXACKWINDOW(sender))) {		/*		 * Take into account window scaling (RFC 1323).		 */		if (!tcph->syn)			win <<= sender->td_scale;		/*		 * Update sender data.		 */		swin = win + (sack - ack);		if (sender->td_maxwin < swin)			sender->td_maxwin = swin;		if (after(end, sender->td_end))			sender->td_end = end;		/*		 * Update receiver data.		 */		if (after(end, sender->td_maxend))			receiver->td_maxwin += end - sender->td_maxend;		if (after(sack + win, receiver->td_maxend - 1)) {			receiver->td_maxend = sack + win;			if (win == 0)				receiver->td_maxend++;		}		/*		 * Check retransmissions.		 */		if (index == TCP_ACK_SET) {			if (state->last_dir == dir			    && state->last_seq == seq			    && state->last_ack == ack			    && state->last_end == end			    && state->last_win == win)				state->retrans++;			else {				state->last_dir = dir;				state->last_seq = seq;				state->last_ack = ack;				state->last_end = end;				state->last_win = win;				state->retrans = 0;			}		}		res = 1;	} else {		res = 0;		if (sender->flags & IP_CT_TCP_FLAG_BE_LIBERAL ||		    nf_ct_tcp_be_liberal)			res = 1;		if (!res && LOG_INVALID(IPPROTO_TCP))			nf_log_packet(pf, 0, skb, NULL, NULL, NULL,			"nf_ct_tcp: %s ",			before(seq, sender->td_maxend + 1) ?			after(end, sender->td_end - receiver->td_maxwin - 1) ?			before(sack, receiver->td_end + 1) ?			after(ack, receiver->td_end - MAXACKWINDOW(sender)) ? "BUG"			: "ACK is under the lower bound (possible overly delayed ACK)"			: "ACK is over the upper bound (ACKed data not seen yet)"			: "SEQ is under the lower bound (already ACKed data retransmitted)"			: "SEQ is over the upper bound (over the window of the receiver)");	}	pr_debug("tcp_in_window: res=%i sender end=%u maxend=%u maxwin=%u "		 "receiver end=%u maxend=%u maxwin=%u\n",		 res, sender->td_end, sender->td_maxend, sender->td_maxwin,		 receiver->td_end, receiver->td_maxend, receiver->td_maxwin);	return res;}#ifdef CONFIG_NF_NAT_NEEDED/* Update sender->td_end after NAT successfully mangled the packet *//* Caller must linearize skb at tcp header. */void nf_conntrack_tcp_update(struct sk_buff *skb,			     unsigned int dataoff,			     struct nf_conn *conntrack,			     int dir){	struct tcphdr *tcph = (void *)skb->data + dataoff;	struct ip_ct_tcp_state *sender = &conntrack->proto.tcp.seen[dir];	struct ip_ct_tcp_state *receiver = &conntrack->proto.tcp.seen[!dir];	__u32 end;	end = segment_seq_plus_len(ntohl(tcph->seq), skb->len, dataoff, tcph);	write_lock_bh(&tcp_lock);	/*	 * We have to worry for the ack in the reply packet only...	 */	if (after(end, conntrack->proto.tcp.seen[dir].td_end))		conntrack->proto.tcp.seen[dir].td_end = end;	conntrack->proto.tcp.last_end = end;	write_unlock_bh(&tcp_lock);	pr_debug("tcp_update: sender end=%u maxend=%u maxwin=%u scale=%i "		 "receiver end=%u maxend=%u maxwin=%u scale=%i\n",		 sender->td_end, sender->td_maxend, sender->td_maxwin,		 sender->td_scale,		 receiver->td_end, receiver->td_maxend, receiver->td_maxwin,		 receiver->td_scale);}EXPORT_SYMBOL_GPL(nf_conntrack_tcp_update);#endif#define	TH_FIN	0x01#define	TH_SYN	0x02#define	TH_RST	0x04#define	TH_PUSH	0x08#define	TH_ACK	0x10#define	TH_URG	0x20#define	TH_ECE	0x40#define	TH_CWR	0x80/* table of valid flag combinations - PUSH, ECE and CWR are always valid */static u8 tcp_valid_flags[(TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG) + 1] ={	[TH_SYN]			= 1,	[TH_SYN|TH_URG]			= 1,	[TH_SYN|TH_ACK]			= 1,	[TH_RST]			= 1,	[TH_RST|TH_ACK]			= 1,	[TH_FIN|TH_ACK]			= 1,	[TH_FIN|TH_ACK|TH_URG]		= 1,	[TH_ACK]			= 1,	[TH_ACK|TH_URG]			= 1,};/* Protect conntrack agaist broken packets. Code taken from ipt_unclean.c.  */static int tcp_error(struct sk_buff *skb,		     unsigned int dataoff,		     enum ip_conntrack_info *ctinfo,		     int pf,		     unsigned int hooknum){	struct tcphdr _tcph, *th;	unsigned int tcplen = skb->len - dataoff;	u_int8_t tcpflags;	/* Smaller that minimal TCP header? */	th = skb_header_pointer(skb, dataoff, sizeof(_tcph), &_tcph);	if (th == NULL) {		if (LOG_INVALID(IPPROTO_TCP))			nf_log_packet(pf, 0, skb, NULL, NULL, NULL,				"nf_ct_tcp: short packet ");		return -NF_ACCEPT;	}	/* Not whole TCP header or malformed packet */	if (th->doff*4 < sizeof(struct tcphdr) || tcplen < th->doff*4) {		if (LOG_INVALID(IPPROTO_TCP))			nf_log_packet(pf, 0, skb, NULL, NULL, NULL,				"nf_ct_tcp: truncated/malformed packet ");		return -NF_ACCEPT;	}	/* Checksum invalid? Ignore.	 * We skip checking packets on the outgoing path	 * because the checksum is assumed to be correct.	 */	/* FIXME: Source route IP option packets --RR */	if (nf_conntrack_checksum &&	    ((pf == PF_INET && hooknum == NF_IP_PRE_ROUTING) ||	     (pf == PF_INET6 && hooknum == NF_IP6_PRE_ROUTING)) &&	    nf_checksum(skb, hooknum, dataoff, IPPROTO_TCP, pf)) {		if (LOG_INVALID(IPPROTO_TCP))			nf_log_packet(pf, 0, skb, NULL, NULL, NULL,				  "nf_ct_tcp: bad TCP checksum ");		return -NF_ACCEPT;	}	/* Check TCP flags. */	tcpflags = (((u_int8_t *)th)[13] & ~(TH_ECE|TH_CWR|TH_PUSH));	if (!tcp_valid_flags[tcpflags]) {		if (LOG_INVALID(IPPROTO_TCP))			nf_log_packet(pf, 0, skb, NULL, NULL, NULL,				  "nf_ct_tcp: invalid TCP flag combination ");		return -NF_ACCEPT;	}	return NF_ACCEPT;}/* Returns verdict for packet, or -1 for invalid. */static int tcp_packet(struct nf_conn *conntrack,		      const struct sk_buff *skb,		      unsigned int dataoff,		      enum ip_conntrack_info ctinfo,		      int pf,		      unsigned int hooknum){	struct nf_conntrack_tuple *tuple;	enum tcp_conntrack new_state, old_state;	enum ip_conntrack_dir dir;	struct tcphdr *th, _tcph;	unsigned long timeout;	unsigned int index;	th = skb_header_pointer(skb, dataoff, 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];	tuple = &conntrack->tuplehash[dir].tuple;	switch (new_state) {	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)		    || (conntrack->proto.tcp.last_dir == dir		        && conntrack->proto.tcp.last_index == TCP_RST_SET)) {			/* Attempt to reopen a closed/aborted 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;		}		/* Fall through */	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.		 */		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, 0, skb, NULL, NULL, NULL,					  "nf_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, dataoff, th);		write_unlock_bh(&tcp_lock);		if (LOG_INVALID(IPPROTO_TCP))			nf_log_packet(pf, 0, skb, NULL, NULL, NULL,				  "nf_ct_tcp: invalid packed ignored ");		return NF_ACCEPT;	case TCP_CONNTRACK_MAX:		/* Invalid packet */		pr_debug("nf_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, 0, skb, NULL, NULL, NULL,				  "nf_ct_tcp: invalid state ");		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 through			 * 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 through */	default:		/* Keep compilers happy. */		break;	}	if (!tcp_in_window(conntrack, &conntrack->proto.tcp, dir, index,			   skb, dataoff, th, pf)) {		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;	conntrack->proto.tcp.last_dir = dir;	pr_debug("tcp_conntracks: ");	NF_CT_DUMP_TUPLE(tuple);	pr_debug("syn=%i ack=%i fin=%i rst=%i old=%i new=%i\n",		 (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))

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -