Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 24 Nov 1999 18:09:18 +0900 (JST)
From:      Jun-ichiro itojun Hagino <itojun@itojun.org>
To:        FreeBSD-gnats-submit@freebsd.org
Cc:        green@freebsd.org, core@kame.net
Subject:   kern/15071: tcp fails to handle TIME_WAIT special case
Message-ID:  <199911240909.SAA03218@coconut.itojun.org>

next in thread | raw e-mail | index | archive | help

>Number:         15071
>Category:       kern
>Synopsis:       tcp fails to handle TIME_WAIT special case
>Confidential:   no
>Severity:       critical
>Priority:       high
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Wed Nov 24 01:10:00 PST 1999
>Closed-Date:
>Last-Modified:
>Originator:     Jun-ichiro itojun Hagino
>Release:        FreeBSD-current
>Organization:
itojun.org
>Environment:

	As far as I've checked, HEAD, RELENG_3 and RELENG_2 are affected.

>Description:

	in tcp_input() (sys/netinet/tcp_input.c) there are two places
	"goto findpcb" is used.  They are for aceepting new connction
	while tcb is in TIME_WAIT state.

	However, because FreeBSD tcp_input strips off mbuf until
	tcp data segment at the following line, "goto findpcb" attempt
	will never success.
  
	/*
	 * Drop TCP, IP headers and TCP options.
	 */
	m->m_data += sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
	m->m_len  -= sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);

>How-To-Repeat:

	Due to this we KAME team encountered panic in IPsec policy management
	engine.  I dunno if there's any problem with normal FreeBSD.  Serious
	cracker may try to hand-cruft tcp data segment that contains wrong
	tcp header (to be uesd when "goto findpcb" is kicked).

>Fix:

	There are two possible fixes to this.  I prefer the latter one
	as we will need to introduce the latter one anyways for IPv6 support.
	
	Short term fix will be to compute m_data and m_len back to the
	original:

Index: tcp_input.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet/tcp_input.c,v
retrieving revision 1.96
diff -c -r1.96 tcp_input.c
*** tcp_input.c	1999/10/09 20:42:15	1.96
--- tcp_input.c	1999/11/24 08:55:01
***************
*** 286,291 ****
--- 286,292 ----
  	register int tiflags;
  	struct socket *so = 0;
  	int todrop, acked, ourfinisacked, needoutput = 0;
+ 	int hdroptlen;
  	struct in_addr laddr;
  	int dropsocket = 0;
  	int iss = 0;
***************
*** 376,383 ****
  	/*
  	 * Drop TCP, IP headers and TCP options.
  	 */
! 	m->m_data += sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
! 	m->m_len  -= sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
  
  	/*
  	 * Locate pcb for segment.
--- 377,385 ----
  	/*
  	 * Drop TCP, IP headers and TCP options.
  	 */
! 	hdroptlen = sizeof(struct tcpiphdr) + off - sizeof(struct tcphdr);
! 	m->m_data += hdroptlen;
! 	m->m_len -= hdroptlen;
  
  	/*
  	 * Locate pcb for segment.
***************
*** 1059,1064 ****
--- 1061,1068 ----
  				goto dropwithreset;
  			if (CC_GT(to.to_cc, tp->cc_recv)) {
  				tp = tcp_close(tp);
+ 				m->m_data -= hdroptlen;
+ 				m->m_len += hdroptlen;
  				goto findpcb;
  			}
  			else
***************
*** 1282,1287 ****
--- 1286,1293 ----
  			    SEQ_GT(ti->ti_seq, tp->rcv_nxt)) {
  				iss = tp->snd_nxt + TCP_ISSINCR;
  				tp = tcp_close(tp);
+ 				m->m_data -= hdroptlen;
+ 				m->m_len += hdroptlen;
  				goto findpcb;
  			}
  			/*



	Long-term (and more clean) fix would be to avoid modifying m_len and
	m_data.

Index: tcp_input.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet/tcp_input.c,v
retrieving revision 1.96
diff -c -r1.96 tcp_input.c
*** tcp_input.c	1999/10/09 20:42:15	1.96
--- tcp_input.c	1999/11/24 09:00:42
***************
*** 112,118 ****
  static void	 tcp_dooptions __P((struct tcpcb *,
  	    u_char *, int, struct tcpiphdr *, struct tcpopt *));
  static void	 tcp_pulloutofband __P((struct socket *,
! 	    struct tcpiphdr *, struct mbuf *));
  static int	 tcp_reass __P((struct tcpcb *, struct tcpiphdr *, struct mbuf *));
  static void	 tcp_xmit_timer __P((struct tcpcb *, int));
  
--- 112,118 ----
  static void	 tcp_dooptions __P((struct tcpcb *,
  	    u_char *, int, struct tcpiphdr *, struct tcpopt *));
  static void	 tcp_pulloutofband __P((struct socket *,
! 	    struct tcpiphdr *, struct mbuf *, int));
  static int	 tcp_reass __P((struct tcpcb *, struct tcpiphdr *, struct mbuf *));
  static void	 tcp_xmit_timer __P((struct tcpcb *, int));
  
***************
*** 286,291 ****
--- 286,292 ----
  	register int tiflags;
  	struct socket *so = 0;
  	int todrop, acked, ourfinisacked, needoutput = 0;
+ 	int hdroptlen = 0;
  	struct in_addr laddr;
  	int dropsocket = 0;
  	int iss = 0;
***************
*** 374,383 ****
  	NTOHS(ti->ti_urp);
  
  	/*
! 	 * Drop TCP, IP headers and TCP options.
  	 */
! 	m->m_data += sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
! 	m->m_len  -= sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
  
  	/*
  	 * Locate pcb for segment.
--- 375,383 ----
  	NTOHS(ti->ti_urp);
  
  	/*
! 	 * Compute mbuf offset for TCP data segment.
  	 */
! 	hdroptlen = sizeof(struct tcpiphdr) + off - sizeof(struct tcphdr);
  
  	/*
  	 * Locate pcb for segment.
***************
*** 673,678 ****
--- 673,679 ----
  			/*
  			 * Add data to socket buffer.
  			 */
+ 			m_adj(m, hdroptlen);
  			sbappend(&so->so_rcv, m);
  			sorwakeup(so);
  			if (tcp_delack_enabled) {
***************
*** 1240,1246 ****
  			tcpstat.tcps_rcvpartduppack++;
  			tcpstat.tcps_rcvpartdupbyte += todrop;
  		}
! 		m_adj(m, todrop);
  		ti->ti_seq += todrop;
  		ti->ti_len -= todrop;
  		if (ti->ti_urp > todrop)
--- 1241,1247 ----
  			tcpstat.tcps_rcvpartduppack++;
  			tcpstat.tcps_rcvpartdupbyte += todrop;
  		}
! 		hdroptlen += todrop;	/* drop from head afterwards */
  		ti->ti_seq += todrop;
  		ti->ti_len -= todrop;
  		if (ti->ti_urp > todrop)
***************
*** 1725,1731 ****
  		     && (so->so_options & SO_OOBINLINE) == 0
  #endif
  		     )
! 			tcp_pulloutofband(so, ti, m);
  	} else
  		/*
  		 * If no out of band data is expected,
--- 1726,1732 ----
  		     && (so->so_options & SO_OOBINLINE) == 0
  #endif
  		     )
! 			tcp_pulloutofband(so, ti, m, hdroptlen);
  	} else
  		/*
  		 * If no out of band data is expected,
***************
*** 1746,1751 ****
--- 1747,1753 ----
  	 */
  	if ((ti->ti_len || (tiflags&TH_FIN)) &&
  	    TCPS_HAVERCVDFIN(tp->t_state) == 0) {
+ 		m_adj(m, hdroptlen);
  		TCP_REASS(tp, ti, m, so, tiflags);
  		/*
  		 * Note the amount of data that peer has sent into
***************
*** 2036,2047 ****
   * sequencing purposes.
   */
  static void
! tcp_pulloutofband(so, ti, m)
  	struct socket *so;
  	struct tcpiphdr *ti;
  	register struct mbuf *m;
  {
! 	int cnt = ti->ti_urp - 1;
  
  	while (cnt >= 0) {
  		if (m->m_len > cnt) {
--- 2038,2050 ----
   * sequencing purposes.
   */
  static void
! tcp_pulloutofband(so, ti, m, off)
  	struct socket *so;
  	struct tcpiphdr *ti;
  	register struct mbuf *m;
+ 	int off;
  {
! 	int cnt = off + ti->ti_urp - 1;
  
  	while (cnt >= 0) {
  		if (m->m_len > cnt) {

>Release-Note:
>Audit-Trail:
>Unformatted:


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-bugs" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199911240909.SAA03218>