Skip to contentSkip to conversation

explicit connection confirmation

RFC PATCH In review Lennert Buytenhek 5 participants 14 messages Nov 7, 2002 – Aug 25, 2003

Subsystems (4)
lore
Share & tools
lore
#0 lore raw

RFC PATCH 1/1 explicit connection confirmation

(please CC on replies, I am not on this list)

Hi,

This patch gives userland the ability to decide whether to react with an incoming TCP SYN with a SYN-ACK or a RST. It was hacked up after Linux Kongress 2001 and has been sitting on my patch pile since april this year or something.

The basic idea is this:
- Put the listening TCP socket in TCP_CONFIRM_CONNECT mode.
- Sockets returned from accept() on this socket after this will be sockets in the SYN_RECV state instead of the ESTABLISHED state (unless syncookies had to be used). By writing to the socket, you cause a SYN-ACK to be sent, and by immediately closing the socket you cause a RST to be sent.

There are two issues left, AFAICS:
- SYN_RECV sockets currently don't time out for some reason
- it deadlocks instantly on SMP

It's against 2.4.18. Could someone have a look at it please? I unfortunately haven't had any time at all lately, so I would be really happy if someone else could take this over. (Well, I can dream, can't I?)

cheers,
Lennert

include/linux/tcp.h renamed from include/linux/tcp.h.orig +1 -0
@@ -127,6 +127,7 @@
 #define TCP_WINDOW_CLAMP	10	/* Bound advertised window */
 #define TCP_INFO		11	/* Information about this connection. */
 #define TCP_QUICKACK		12	/* Block/reenable quick acks */
+#define TCP_CONFIRM_CONNECT	13	/* Let user control connection acceptance */
 
 #define TCPI_OPT_TIMESTAMPS	1
 #define TCPI_OPT_SACK		2
include/net/sock.h renamed from include/net/sock.h.orig +6 -0
@@ -302,6 +302,7 @@
 	__u8	reordering;	/* Packet reordering metric.		*/
 	__u8	queue_shrunk;	/* Write queue has been shrunk recently.*/
 	__u8	defer_accept;	/* User waits for some data after accept() */
+	__u8	confirm_connect;/* User wants control over conn. acceptance */
 
 /* RTT measurement */
 	__u8	backoff;	/* backoff				*/
@@ -411,6 +412,11 @@
 	struct open_request	*accept_queue;
 	struct open_request	*accept_queue_tail;
 
+	/* Our corresponding open_request if this socket is unconfirmed
+	 * (i.e. if we haven't sent SYN-ACK or RST yet)
+	 */
+	struct open_request	*unconfirmed_openreq;
+
 	int			write_pending;	/* A write to socket waits to start. */
 
 	unsigned int		keepalive_time;	  /* time before keep alive takes place */
include/net/tcp.h renamed from include/net/tcp.h.orig +14 -1
@@ -505,7 +505,8 @@
 		sack_ok : 1,
 		wscale_ok : 1,
 		ecn_ok : 1,
-		acked : 1;
+		acked : 1,
+		unconfirmed : 1;
 	/* The following two fields can be easily recomputed I think -AK */
 	__u32			window_clamp;	/* window clamp at creation time */
 	__u32			rcv_wnd;	/* rcv_wnd offered first time */
@@ -533,6 +534,17 @@
 	tcp_openreq_fastfree(req);
 }
 
+static inline int tcp_is_unconfirmed(struct tcp_opt *tp)
+{
+	struct open_request *req;
+
+	req = tp->unconfirmed_openreq;
+	if (req != NULL && req->unconfirmed)
+		return 1;
+
+	return 0;
+}
+
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 #define TCP_INET_FAMILY(fam) ((fam) == AF_INET)
 #else
@@ -1661,6 +1673,7 @@
 	req->acked = 0;
 	req->ecn_ok = 0;
 	req->rmt_port = skb->h.th->source;
+	req->unconfirmed = 0;
 }
 
 #define TCP_MEM_QUANTUM	((int)PAGE_SIZE)
net/ipv4/tcp.c renamed from net/ipv4/tcp.c.orig +39 -5

109 lines not on this page · raw message

net/ipv4/tcp_input.c renamed from net/ipv4/tcp_input.c.orig +5 -0
@@ -3749,6 +3749,11 @@
 		switch(sk->state) {
 		case TCP_SYN_RECV:
 			if (acceptable) {
+				if (tp->unconfirmed_openreq != NULL) {
+					tcp_openreq_free(tp->unconfirmed_openreq);
+					tp->unconfirmed_openreq = NULL;
+				}
+
 				tp->copied_seq = tp->rcv_nxt;
 				mb();
 				tcp_set_state(sk, TCP_ESTABLISHED);
net/ipv4/tcp_minisocks.c renamed from net/ipv4/tcp_minisocks.c.orig +10 -2
@@ -696,6 +696,7 @@
 		tcp_init_wl(newtp, req->snt_isn, req->rcv_isn);
 
 		newtp->retransmits = 0;
+		newtp->confirm_connect = 0;
 		newtp->backoff = 0;
 		newtp->srtt = 0;
 		newtp->mdev = TCP_TIMEOUT_INIT;
@@ -839,7 +840,8 @@
 		 * Enforce "SYN-ACK" according to figure 8, figure 6
 		 * of RFC793, fixed by RFC1122.
 		 */
-		req->class->rtx_syn_ack(sk, req, NULL);
+		if (!req->unconfirmed)
+			req->class->rtx_syn_ack(sk, req, NULL);
 		return NULL;
 	}
 
@@ -864,7 +866,7 @@
 	if (paws_reject || !tcp_in_window(TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq,
 					  req->rcv_isn+1, req->rcv_isn+1+req->rcv_wnd)) {
 		/* Out of window: send ACK and drop. */
-		if (!(flg & TCP_FLAG_RST))
+		if (!req->unconfirmed && !(flg & TCP_FLAG_RST))
 			req->class->send_ack(skb, req);
 		if (paws_reject)
 			NET_INC_STATS_BH(PAWSEstabRejected);
@@ -907,6 +909,12 @@
 		return NULL;
 	}
 
+	/* @@@ If we are in SYN_RECV and haven't confirmed/rejected
+	 * the connection yet, this ACK is acking a never-sent packet.
+	 */
+	if (tcp_is_unconfirmed(tp))
+		return NULL;
+
 	/* OK, ACK is valid, create big socket and
 	 * feed this segment to it. It will repeat all
 	 * the tests. THIS SEGMENT MUST MOVE SOCKET TO
net/ipv4/tcp_ipv4.c renamed from net/ipv4/tcp_ipv4.c.orig +26 -1
@@ -1270,12 +1270,14 @@
 
 int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
 {
+	struct tcp_opt *master_tp = &(sk->tp_pinfo.af_tcp);
 	struct tcp_opt tp;
 	struct open_request *req;
 	__u32 saddr = skb->nh.iph->saddr;
 	__u32 daddr = skb->nh.iph->daddr;
 	__u32 isn = TCP_SKB_CB(skb)->when;
 	struct dst_entry *dst = NULL;
+	int dont_confirm = 0;
 #ifdef CONFIG_SYN_COOKIES
 	int want_cookie = 0;
 #else
@@ -1312,6 +1314,9 @@
 	if (req == NULL)
 		goto drop;
 
+	if (!want_cookie && master_tp->confirm_connect)
+		dont_confirm = 1;
+
 	tcp_clear_options(&tp);
 	tp.mss_clamp = 536;
 	tp.user_mss = sk->tp_pinfo.af_tcp.user_mss;
@@ -1396,11 +1401,31 @@
 	}
 	req->snt_isn = isn;
 
-	if (tcp_v4_send_synack(sk, req, dst))
+	if (!dont_confirm && tcp_v4_send_synack(sk, req, dst))
 		goto drop_and_free;
 
 	if (want_cookie) {
 	   	tcp_openreq_free(req); 
+	} else if (dont_confirm) {
+		struct sock *child;
+		__u8 rcv_wscale;
+
+		req->window_clamp = dst?dst->window:0;
+		tcp_select_initial_window(tcp_full_space(sk), req->mss,
+				&req->rcv_wnd, &req->window_clamp,
+				0, &rcv_wscale);
+		req->rcv_wscale = rcv_wscale;
+
+		child = tcp_v4_syn_recv_sock(sk, skb, req, NULL);
+		if (child != NULL) {
+			req->unconfirmed = 1;
+			child->tp_pinfo.af_tcp.unconfirmed_openreq = req;
+			tcp_acceptq_queue(sk, req, child);
+			sk->data_ready(sk, 0);
+			sock_put(child);
+		} else {
+			tcp_openreq_free(req);
+		}
 	} else {
 		tcp_v4_synq_add(sk, req);
 	}
net/ipv4/tcp_timer.c renamed from net/ipv4/tcp_timer.c.orig +2 -1
@@ -512,7 +512,8 @@
 			if ((long)(now - req->expires) >= 0) {
 				if ((req->retrans < thresh ||
 				     (req->acked && req->retrans < max_retries))
-				    && !req->class->rtx_syn_ack(sk, req, NULL)) {
+				    && (req->unconfirmed ||
+					!req->class->rtx_syn_ack(sk, req, NULL))) {
 					unsigned long timeo;
 
 					if (req->retrans++ == 0)
net/ipv4/af_inet.c renamed from net/ipv4/af_inet.c.orig +1 -1
@@ -693,7 +693,7 @@
 
 	lock_sock(sk2);
 
-	BUG_TRAP((1<<sk2->state)&(TCPF_ESTABLISHED|TCPF_CLOSE_WAIT|TCPF_CLOSE));
+	BUG_TRAP((1<<sk2->state)&(TCPF_SYN_RECV|TCPF_ESTABLISHED|TCPF_CLOSE_WAIT|TCPF_CLOSE));
 
 	sock_graft(sk2, newsock);
 
#1 lore raw
Quoted 5 lines · Lennert Buytenhek

- Sockets returned from accept() on this socket after this will be sockets in the SYN_RECV state instead of the ESTABLISHED state (unless syncookies had to be used). By writing to the socket, you cause a SYN-ACK to be sent, and by immediately closing the socket you cause a RST to be sent.

And reading, like a webserver would do? I think this approach smells, btw - doesn't this mean that processes will now be woken up on receiving a SYN instead of after completion of the handshake?

Would make a synflood all the more interesting..

Regards,

bert

Signature · 2 lines
http://www.PowerDNS.com          Versatile DNS Software & Services
http://lartc.org           Linux Advanced Routing & Traffic Control HOWTO
#2 lore raw
Quoted 7 lines · bert hubert
Quoted 5 lines

- Sockets returned from accept() on this socket after this will be sockets in the SYN_RECV state instead of the ESTABLISHED state (unless syncookies had to be used). By writing to the socket, you cause a SYN-ACK to be sent, and by immediately closing the socket you cause a RST to be sent.

And reading, like a webserver would do?

Will do nothing, but this can easily be changed.

I remind you of the fact that this option has to be explicitly enabled on a listening socket, so that apps have to be adapted to use the new interface anyway.

Quoted 3 lines

I think this approach smells, btw - doesn't this mean that processes will now be woken up on receiving a SYN instead of after completion of the handshake?

Yes, it does mean this. You are free to suggest alternatives.

Quoted 1 line

Would make a synflood all the more interesting..

In case of a synflood, the TCP stack will fall back to sending syncookies as it normally does.

cheers,
Lennert

#3 lore raw

Could you not have used netfilter for this? You have the app sending controls to add netfilter policies and delete them when not needed.

cheers,
jamal

#4 lore raw

Hi,

netfilter, yeah, sure, 'could have', but please.

'Make it a netfilter module' is generally what people say when they are confronted with a feature they don't like.

There was a thread about this in private mail round April this year, in which some good points were raised.

- From the kernel point of view, doing it in netfilter would require more state tracking and access to the socket hashes and would be uglier.

- From the application writer's point of view, doing it via a socket option is much more intuitive, since this flag is really a socket property, than doing it via some extra API which would make it way too difficult/complex to use in existing apps.

It's worth noting that selective TCP connection acceptance was also intended to be implemented as a socket option by the original BSD developers. See http://www.kohala.com/start/vanj.94jun27.txt
(link thanks to Marc Boucher).

Quoted 1 line

From the accept(2) man page on Red Hat Linux (again thanks to Marc

Boucher):

For certain protocols which require an explicit confirmation, such as DECNet, accept can be thought of as merely dequeuing the next connec-
tion request and not implying confirmation. Confirmation can be
implied by a normal read or write on the new file descriptor, and rejection can be implied by closing the new socket. Currently only DEC- Net has these semantics on Linux.

cheers,
Lennert

Quoted 9 lines · jamal

Could you not have used netfilter for this? You have the app sending controls to add netfilter policies and delete them when not needed.

cheers,
jamal

#5 lore raw
Quoted 3 lines · Lennert Buytenhek

Hi,

netfilter, yeah, sure, 'could have', but please.

apology if i sounded like one of those adolescent netfilter dangerous fools who show up with "mama, look what i can do with a packet now that ive read netfilter docs"

Quoted 3 lines

'Make it a netfilter module' is generally what people say when they are confronted with a feature they don't like.

My angle was to avoid being intrusive to the tcp code. you might get a fish sent to you in .nl in an armani suit;->

Quoted 4 lines

There was a thread about this in private mail round April this year, in which some good points were raised.

There are some good points; however, whats the app for this feature?

cheers,
jamal

#6 lore raw
Quoted 4 lines · jamal
Quoted 2 lines

There was a thread about this in private mail round April this year, in which some good points were raised.

There are some good points; however, whats the app for this feature?

This came up a long time ago on bugtraq in a discussion how to easily prevent certain IP addresses from DoSsing your TCP daemon. Right now, userspace is always forced to complete the threeway handshake, and can only then close the socket.

Even rather small amounts of SYN packets can thus easily saturate a server which has decided to handle only 100 connections AND has decided to ignore a certain IP address. Some inetd superservers contain code to ratelimit IP addresses which sadly is not as effective from userspace as it could be with the ability to RST a connection immediately.

It also allows userspace to simulate that a service isn't even there, without root capabilities.

Regards,

bert

Signature · 2 lines
http://www.PowerDNS.com          Versatile DNS Software & Services
http://lartc.org           Linux Advanced Routing & Traffic Control HOWTO
#7 lore raw

it would also be useful for transparent proxying. presently all connections diverted to a proxy are immediately accepted, regardless of whether the second connection (proxy->real destination) succeeds or not.

Quoted 29 lines · bert hubert
Quoted 4 lines · jamal
Quoted 2 lines

There was a thread about this in private mail round April this year, in which some good points were raised.

There are some good points; however, whats the app for this feature?

This came up a long time ago on bugtraq in a discussion how to easily prevent certain IP addresses from DoSsing your TCP daemon. Right now, userspace is always forced to complete the threeway handshake, and can only then close the socket.

Even rather small amounts of SYN packets can thus easily saturate a server which has decided to handle only 100 connections AND has decided to ignore a certain IP address. Some inetd superservers contain code to ratelimit IP addresses which sadly is not as effective from userspace as it could be with the ability to RST a connection immediately.

It also allows userspace to simulate that a service isn't even there, without root capabilities.

Regards,

bert

Signature · 2 lines
http://www.PowerDNS.com          Versatile DNS Software & Services
http://lartc.org           Linux Advanced Routing & Traffic Control HOWTO
#8 lore raw
In reply to jamal
Quoted 5 lines · jamal
Quoted 1 line

netfilter, yeah, sure, 'could have', but please.

apology if i sounded like one of those adolescent netfilter dangerous fools who show up with "mama, look what i can do with a packet now that ive read netfilter docs"

No, you don't sound such, sorry for reacting the way i did.

Quoted 5 lines
Quoted 2 lines

'Make it a netfilter module' is generally what people say when they are confronted with a feature they don't like.

My angle was to avoid being intrusive to the tcp code. you might get a fish sent to you in .nl in an armani suit;->

Sorry but I don't like fish nor armani suits :-)

Quoted 4 lines
Quoted 2 lines

There was a thread about this in private mail round April this year, in which some good points were raised.

There are some good points; however, whats the app for this feature?

My specific application is a proxy application that replaces the in-kernel IP masquerading functionality, using a wildcard REDIRECT rule plus SO_ORIGINAL_DST. The main reason I'm doing it in userspace is because downstream bandwidth limiting becomes a whole lot easier this way than doing it in-kernel -- it would need complicated state tracking and nonobvious window field manipulations if done there.

The applications that Bert and Marc named sound sane too. There's just a whole lot of things this thing can be used for.

cheers,
Lennert

#9 lore raw
In reply to Lennert Buytenhek
Quoted 5 lines · Lennert Buytenhek
Quoted 3 lines

I think this approach smells, btw - doesn't this mean that processes will now be woken up on receiving a SYN instead of after completion of the handshake?

Yes, it does mean this. You are free to suggest alternatives.

I like having this ability - I dislike offering it to an unsuspecting userspace.

Quoted 4 lines
Quoted 1 line

Would make a synflood all the more interesting..

In case of a synflood, the TCP stack will fall back to sending syncookies as it normally does.

Yes, but in your setup, a spoofable SYN packet will spawn a process for many daemons, unless they are modified to first try to read/write to the socket (which might block!) before forking/pthread_create()ing.

Regards,

bert

Signature · 2 lines
http://www.PowerDNS.com          Versatile DNS Software & Services
http://lartc.org           Linux Advanced Routing & Traffic Control HOWTO
#10 lore raw
Quoted 8 lines · bert hubert
Quoted 5 lines
Quoted 3 lines

I think this approach smells, btw - doesn't this mean that processes will now be woken up on receiving a SYN instead of after completion of the handshake?

Yes, it does mean this. You are free to suggest alternatives.

I like having this ability - I dislike offering it to an unsuspecting userspace.

Userspace needs to turn on the option first, so your 'unsuspecting' does not apply.

Quoted 8 lines
Quoted 4 lines
Quoted 1 line

Would make a synflood all the more interesting..

In case of a synflood, the TCP stack will fall back to sending syncookies as it normally does.

Yes, but in your setup, a spoofable SYN packet will spawn a process for many daemons, unless they are modified to first try to read/write to the socket (which might block!) before forking/pthread_create()ing.

Again, if the app decides to turn on TCP_CONFIRM_CONNECT, then it's up to the app to deal with it properly. There are very good reasons for not turning on TCP_CONFIRM_CONNECT by default, which is why it is not on by default, and why grafting a setsockopt onto every daemon there is out there is definitely not a good idea.

cheers,
Lennert

#11 lore raw
Quoted 5 lines · Lennert Buytenhek
Quoted 2 lines

I like having this ability - I dislike offering it to an unsuspecting userspace.

Userspace needs to turn on the option first, so your 'unsuspecting' does not apply.

Ah ok, I thought 'TCP_CONFIRM_CONNECT' was a TCP connection state like SYN_RECV.

Quoted 5 lines

Again, if the app decides to turn on TCP_CONFIRM_CONNECT, then it's up to the app to deal with it properly. There are very good reasons for not turning on TCP_CONFIRM_CONNECT by default, which is why it is not on by default, and why grafting a setsockopt onto every daemon there is out there is definitely not a good idea.

Exactly. Ok, cool.

Regards,

bert

Signature · 2 lines
http://www.PowerDNS.com          Versatile DNS Software & Services
http://lartc.org           Linux Advanced Routing & Traffic Control HOWTO
#12 lore raw

RFC PATCH 1/1 explicit connection confirmation

In reply to Lennert Buytenhek

Hi,

Below is the original email I sent to netdev about nine months ago announcing selective connection acceptance support for TCP sockets. I have forward-ported the 2.4.18 patch to 2.6.0-test2, included below. No functional changes have been made.

Could someone have a look at it?

cheers,
Lennert

Quoted 21 lines · buytenh

(please CC on replies, I am not on this list)

Hi,

This patch gives userland the ability to decide whether to react with an incoming TCP SYN with a SYN-ACK or a RST. It was hacked up after Linux Kongress 2001 and has been sitting on my patch pile since april this year or something.

The basic idea is this:
- Put the listening TCP socket in TCP_CONFIRM_CONNECT mode.
- Sockets returned from accept() on this socket after this will be sockets in the SYN_RECV state instead of the ESTABLISHED state (unless syncookies had to be used). By writing to the socket, you cause a SYN-ACK to be sent, and by immediately closing the socket you cause a RST to be sent.

There are two issues left, AFAICS:
- SYN_RECV sockets currently don't time out for some reason
- it deadlocks instantly on SMP

Quoted 9 lines

It's against 2.4.18. Could someone have a look at it please? I unfortunately haven't had any time at all lately, so I would be really happy if someone else could take this over. (Well, I can dream, can't I?)

cheers,
Lennert

include/linux/tcp.h renamed from include/linux/tcp.h.orig +7 -0
@@ -127,6 +127,7 @@
 #define TCP_WINDOW_CLAMP	10	/* Bound advertised window */
 #define TCP_INFO		11	/* Information about this connection. */
 #define TCP_QUICKACK		12	/* Block/reenable quick acks */
+#define TCP_CONFIRM_CONNECT	13	/* Let user control connection acceptance */
 
 #define TCPI_OPT_TIMESTAMPS	1
 #define TCPI_OPT_SACK		2
@@ -257,6 +258,7 @@
 	__u8	reordering;	/* Packet reordering metric.		*/
 	__u8	queue_shrunk;	/* Write queue has been shrunk recently.*/
 	__u8	defer_accept;	/* User waits for some data after accept() */
+	__u8	confirm_connect;/* User wants control over conn. acceptance */
 
 /* RTT measurement */
 	__u8	backoff;	/* backoff				*/
@@ -364,6 +366,11 @@
 	struct open_request	*accept_queue;
 	struct open_request	*accept_queue_tail;
 
+	/* Our corresponding open_request if this socket is unconfirmed
+	 * (i.e. if we haven't sent SYN-ACK or RST yet)
+	 */
+	struct open_request     *unconfirmed_openreq;
+
 	int			write_pending;	/* A write to socket waits to start. */
 
 	unsigned int		keepalive_time;	  /* time before keep alive takes place */
include/net/tcp.h renamed from include/net/tcp.h.orig +14 -1
@@ -591,7 +591,8 @@
 		sack_ok : 1,
 		wscale_ok : 1,
 		ecn_ok : 1,
-		acked : 1;
+		acked : 1,
+		unconfirmed : 1;
 	/* The following two fields can be easily recomputed I think -AK */
 	__u32			window_clamp;	/* window clamp at creation time */
 	__u32			rcv_wnd;	/* rcv_wnd offered first time */
@@ -619,6 +620,17 @@
 	tcp_openreq_fastfree(req);
 }
 
+static inline int tcp_is_unconfirmed(struct tcp_opt *tp)
+{
+	struct open_request *req;
+
+	req = tp->unconfirmed_openreq;
+	if (req != NULL && req->unconfirmed)
+		return 1;
+
+	return 0;
+}
+
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 #define TCP_INET_FAMILY(fam) ((fam) == AF_INET)
 #else
@@ -1762,6 +1774,7 @@
 	req->acked = 0;
 	req->ecn_ok = 0;
 	req->rmt_port = skb->h.th->source;
+	req->unconfirmed = 0;
 }
 
 #define TCP_MEM_QUANTUM	((int)PAGE_SIZE)
net/ipv4/af_inet.c renamed from net/ipv4/af_inet.c.orig +2 -2
@@ -685,8 +685,8 @@
 
 	lock_sock(sk2);
 
-	BUG_TRAP((1 << sk2->sk_state) &
-		 (TCPF_ESTABLISHED | TCPF_CLOSE_WAIT | TCPF_CLOSE));
+	BUG_TRAP((1 << sk2->sk_state) & (TCPF_SYN_RECV | TCPF_ESTABLISHED |
+		TCPF_CLOSE_WAIT | TCPF_CLOSE));
 
 	sock_graft(sk2, newsock);
 
net/ipv4/tcp.c renamed from net/ipv4/tcp.c.orig +39 -5

109 lines not on this page · raw message

net/ipv4/tcp_input.c renamed from net/ipv4/tcp_input.c.orig +5 -0
@@ -3938,6 +3938,11 @@
 		switch(sk->sk_state) {
 		case TCP_SYN_RECV:
 			if (acceptable) {
+				if (tp->unconfirmed_openreq != NULL) {
+					tcp_openreq_free(tp->unconfirmed_openreq);
+					tp->unconfirmed_openreq = NULL;
+				}
+
 				tp->copied_seq = tp->rcv_nxt;
 				mb();
 				tcp_set_state(sk, TCP_ESTABLISHED);
net/ipv4/tcp_ipv4.c renamed from net/ipv4/tcp_ipv4.c.orig +26 -1
@@ -1403,12 +1403,14 @@
 
 int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
 {
+	struct tcp_opt *master_tp = tcp_sk(sk);
 	struct tcp_opt tp;
 	struct open_request *req;
 	__u32 saddr = skb->nh.iph->saddr;
 	__u32 daddr = skb->nh.iph->daddr;
 	__u32 isn = TCP_SKB_CB(skb)->when;
 	struct dst_entry *dst = NULL;
+	int dont_confirm = 0;
 #ifdef CONFIG_SYN_COOKIES
 	int want_cookie = 0;
 #else
@@ -1445,6 +1447,9 @@
 	if (!req)
 		goto drop;
 
+	if (!want_cookie && master_tp->confirm_connect)
+		dont_confirm = 1;
+
 	tcp_clear_options(&tp);
 	tp.mss_clamp = 536;
 	tp.user_mss  = tcp_sk(sk)->user_mss;
@@ -1533,11 +1538,31 @@
 	}
 	req->snt_isn = isn;
 
-	if (tcp_v4_send_synack(sk, req, dst))
+	if (!dont_confirm && tcp_v4_send_synack(sk, req, dst))
 		goto drop_and_free;
 
 	if (want_cookie) {
 	   	tcp_openreq_free(req);
+	} else if (dont_confirm) {
+		struct sock *child;
+		__u8 rcv_wscale;
+
+		req->window_clamp = dst ? dst_metric(dst, RTAX_WINDOW) : 0;
+		tcp_select_initial_window(tcp_full_space(sk), req->mss,
+				&req->rcv_wnd, &req->window_clamp,
+				0, &rcv_wscale);
+		req->rcv_wscale = rcv_wscale;
+
+		child = tcp_v4_syn_recv_sock(sk, skb, req, NULL);
+		if (child != NULL) {
+			req->unconfirmed = 1;
+			tcp_sk(child)->unconfirmed_openreq = req;
+			tcp_acceptq_queue(sk, req, child);
+			sk->sk_data_ready(sk, 0);
+			sock_put(child);
+		} else {
+			tcp_openreq_free(req);
+		}
 	} else {
 		tcp_v4_synq_add(sk, req);
 	}
net/ipv4/tcp_minisocks.c renamed from net/ipv4/tcp_minisocks.c.orig +10 -2
@@ -732,6 +732,7 @@
 		tcp_init_wl(newtp, req->snt_isn, req->rcv_isn);
 
 		newtp->retransmits = 0;
+		newtp->confirm_connect = 0;
 		newtp->backoff = 0;
 		newtp->srtt = 0;
 		newtp->mdev = TCP_TIMEOUT_INIT;
@@ -884,7 +885,8 @@
 		 * Enforce "SYN-ACK" according to figure 8, figure 6
 		 * of RFC793, fixed by RFC1122.
 		 */
-		req->class->rtx_syn_ack(sk, req, NULL);
+		if (!req->unconfirmed)
+			req->class->rtx_syn_ack(sk, req, NULL);
 		return NULL;
 	}
 
@@ -955,7 +957,7 @@
 	if (paws_reject || !tcp_in_window(TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq,
 					  req->rcv_isn+1, req->rcv_isn+1+req->rcv_wnd)) {
 		/* Out of window: send ACK and drop. */
-		if (!(flg & TCP_FLAG_RST))
+		if (!req->unconfirmed && !(flg & TCP_FLAG_RST))
 			req->class->send_ack(skb, req);
 		if (paws_reject)
 			NET_INC_STATS_BH(PAWSEstabRejected);
@@ -991,6 +993,12 @@
 		return NULL;
 	}
 
+	/* @@@ If we are in SYN_RECV and haven't confirmed/rejected
+	 * the connection yet, this ACK is acking a never-sent packet.
+	 */
+	if (tcp_is_unconfirmed(tp))
+		return NULL;
+
 	/* OK, ACK is valid, create big socket and
 	 * feed this segment to it. It will repeat all
 	 * the tests. THIS SEGMENT MUST MOVE SOCKET TO
net/ipv4/tcp_timer.c renamed from net/ipv4/tcp_timer.c.orig +2 -1
@@ -519,7 +519,8 @@
 			if (time_after_eq(now, req->expires)) {
 				if ((req->retrans < thresh ||
 				     (req->acked && req->retrans < max_retries))
-				    && !req->class->rtx_syn_ack(sk, req, NULL)) {
+				    && (req->unconfirmed ||
+					!req->class->rtx_syn_ack(sk, req, NULL))) {
 					unsigned long timeo;
 
 					if (req->retrans++ == 0)
#13 lore raw
Quoted 6 lines · Lennert Buytenhek

Hi,

Below is the original email I sent to netdev about nine months ago announcing selective connection acceptance support for TCP sockets. I have forward-ported the 2.4.18 patch to 2.6.0-test2, included below. No functional changes have been made.

That is great! Thanks for not forgetting about this issue.

I think this feature would really be great to develop _really_ transparent proxies.

Quoted 1 line

Could someone have a look at it?

I'm not a core networking guy, but it looks fine to me.

Dave, Andi, Alexey, James: Would this be a candidate for kernel inclusion at some point?

Quoted 1 line

Lennert

Signature · 3 lines
- Harald Welte <[email protected]>               http://www.gnumonks.org/
============================================================================
Programming is like sex: One mistake and you have to support it your lifetime