kern/72502: TCP should honour incoming RSTs even if the receive window is closed

Michiel Boland michiel at boland.org
Mon Oct 11 00:40:29 PDT 2004


>Number:         72502
>Category:       kern
>Synopsis:       TCP should honour incoming RSTs even if the receive window is closed
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Mon Oct 11 07:40:28 GMT 2004
>Closed-Date:
>Last-Modified:
>Originator:     Michiel Boland
>Release:        FreeBSD 6.0-CURRENT i386
>Organization:
>Environment:
System: FreeBSD leefnet.office.internl.net 6.0-CURRENT FreeBSD 6.0-CURRENT #0: Sat Oct 9 15:53:21 CEST 2004 root at leefnet.office.internl.net:/usr/obj/usr/src/sys/LEEFNET i386


	
>Description:
TCP should honour incoming RST segments, even if the receive window is closed.
(See RFC793, page 26.)
Currently, FreeBSD drops RST packets on the floor, which may cause applications
to hang indefinitely.
>How-To-Repeat:
The following code sets up two connected TCP sockets that send data to each
other until the window is closed. Then one of the sockets is closed, which
will generate a RST once the TCP at the other socket does a window probe.
But the RST is ignored, therefore the application will never exit.
After applying the patch below, the app will exit after a couple of seconds.
(FWIW Linux 2.4 and Solaris 7-9 do this correctly.)

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <poll.h>
#include <unistd.h>
#include <string.h>

int main(void)
{
	int o, s, t, u, do_t, do_u;
	struct pollfd pfd[2];
	struct sockaddr_in sa;
	char buf[4096];

	s = socket(AF_INET, SOCK_STREAM, 0);
	if (s == -1)
		return 1;
	o = 1;
	setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &o, sizeof o);
	memset(&sa, 0, sizeof sa);
	sa.sin_family = AF_INET;
	sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
	sa.sin_port = htons(3737);
	if (bind(s, (struct sockaddr *) &sa, sizeof sa) == -1)
		return 1;
	if (listen(s, 1) == -1)
		return 1;
	t = socket(AF_INET, SOCK_STREAM, 0);
	if (t == -1)
		return 1;
	if (connect(t, (struct sockaddr *) &sa, sizeof sa) == -1)
		return 1;
	u = accept(s, 0, 0);
	if (u == -1)
		return 1;
	close(s);
	fcntl(t, F_SETFL, fcntl(t, F_GETFL) | O_NONBLOCK);
	fcntl(u, F_SETFL, fcntl(t, F_GETFL) | O_NONBLOCK);
	do_t = 1;
	do_u = 1;
	pfd[0].fd = t;
	pfd[0].events = POLLOUT;
	pfd[1].fd = u;
	pfd[1].events = POLLOUT;
	while (do_t || do_u) {
		if (poll(pfd, 2, 1000) == 0) {
			close(t);
			pfd[0].fd = -1;
			do_t = 0;
			continue;
		}
		if (pfd[0].revents & POLLOUT) {
			if (write(t, buf, sizeof buf) == -1) {
				close(t);
				pfd[0].fd = -1;
				do_t = 0;
			}
		}
		if (pfd[1].revents & POLLOUT) {
			if (write(u, buf, sizeof buf) == -1) {
				close(u);
				pfd[1].fd = -1;
				do_u = 0;
			}
		}
	}
	return 0;
}

>Fix:

Apply the following patch to src/sys/netinet/tcp_input.c

--- tcp_input.c.orig	Tue Oct  5 20:36:23 2004
+++ tcp_input.c	Mon Oct 11 01:32:17 2004
@@ -1622,8 +1622,9 @@
 	 *      RFC 1337.
 	 */
 	if (thflags & TH_RST) {
-		if (SEQ_GEQ(th->th_seq, tp->last_ack_sent) &&
-		    SEQ_LT(th->th_seq, tp->last_ack_sent + tp->rcv_wnd)) {
+		if ((SEQ_GEQ(th->th_seq, tp->last_ack_sent) &&
+		    SEQ_LT(th->th_seq, tp->last_ack_sent + tp->rcv_wnd)) ||
+		    (tp->rcv_wnd == 0 && tp->last_ack_sent == th->th_seq)) {
 			switch (tp->t_state) {
 
 			case TCPS_SYN_RECEIVED:

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


More information about the freebsd-bugs mailing list