Skip to contentSkip to conversation

Introduce the pickup facilities to take over TCP connections

PATCH In review Pablo Neira Ayuso 3 participants 6 messages Aug 21, 2006 – Sep 24, 2006

Subsystems (2)
lore
Share & tools
lore

Pablo Neira Ayuso

author maintainer
#0 lore raw

PATCH 2/3 Introduce the pickup facilities to take over TCP connections

This patch introduces a new flag called IPS_PICKUP that forces the protocol handler to pick up the window of valid TCP packets. Moreover, four new attributes to inject the window scale factor and enable SACK are introduced.

These new facilities provide the appropiate mechanisms to take over TCP connections in failover settings with TCP tracking enabled.

Signature · 1 line
The dawn of the fourth age of Linux firewalling is coming; a time of great struggle and heroic deeds -- J.Kadlecsik got inspired by J.Morris

[CONNTRACK] Introduce the pickup facilities to take over TCP connections

This patch introduces a new flag called IPS_PICKUP that forces the protocol handler to pick up the window of valid TCP packets. Moreover, four new attributes to inject the window scale factor and enable SACK are introduced. These new facilities provide the appropiate mechanisms to take over TCP connections in failover settings with TCP tracking enabled.

net/ipv4/netfilter/ip_conntrack_proto_tcp.c +82 -16
Index: net-2.6/net/ipv4/netfilter/ip_conntrack_proto_tcp.c
===================================================================
@@ -341,11 +341,24 @@ static int tcp_to_nfattr(struct sk_buff 
 			 const struct ip_conntrack *ct)
 {
 	struct nfattr *nest_parms;
-	
+	u_int8_t sack;
+
 	read_lock_bh(&tcp_lock);
 	nest_parms = NFA_NEST(skb, CTA_PROTOINFO_TCP);
 	NFA_PUT(skb, CTA_PROTOINFO_TCP_STATE, sizeof(u_int8_t),
 		&ct->proto.tcp.state);
+	/* window scale factor: original direction (SYN) */
+	NFA_PUT(skb, CTA_PROTOINFO_TCP_WSCALE_ORIGINAL, sizeof(u_int8_t),
+		&ct->proto.tcp.seen[0].td_scale);
+	/* window scale factor: reply direction (SYN+ACK) */
+	NFA_PUT(skb, CTA_PROTOINFO_TCP_WSCALE_REPLY, sizeof(u_int8_t),
+		&ct->proto.tcp.seen[1].td_scale);
+	/* SACK: original direction */
+	sack = ct->proto.tcp.seen[0].flags & IP_CT_TCP_FLAG_SACK_PERM;
+	NFA_PUT(skb, CTA_PROTOINFO_TCP_SACK_ORIGINAL, sizeof(u_int8_t), &sack);
+	/* SACK: reply direction */
+	sack = ct->proto.tcp.seen[1].flags & IP_CT_TCP_FLAG_SACK_PERM;
+	NFA_PUT(skb, CTA_PROTOINFO_TCP_SACK_REPLY, sizeof(u_int8_t), &sack);
 	read_unlock_bh(&tcp_lock);
 
 	NFA_NEST_END(skb, nest_parms);
@@ -358,7 +371,11 @@ nfattr_failure:
 }
 
 static const size_t cta_min_tcp[CTA_PROTOINFO_TCP_MAX] = {
-	[CTA_PROTOINFO_TCP_STATE-1]	= sizeof(u_int8_t),
+	[CTA_PROTOINFO_TCP_STATE-1]		= sizeof(u_int8_t),
+	[CTA_PROTOINFO_TCP_WSCALE_ORIGINAL-1]	= sizeof(u_int8_t),
+	[CTA_PROTOINFO_TCP_WSCALE_REPLY-1]	= sizeof(u_int8_t),
+	[CTA_PROTOINFO_TCP_SACK_ORIGINAL-1]	= sizeof(u_int8_t),
+	[CTA_PROTOINFO_TCP_SACK_REPLY-1]	= sizeof(u_int8_t)
 };
 
 static int nfattr_to_tcp(struct nfattr *cda[], struct ip_conntrack *ct)
@@ -382,6 +399,40 @@ static int nfattr_to_tcp(struct nfattr *
 	write_lock_bh(&tcp_lock);
 	ct->proto.tcp.state = 
 		*(u_int8_t *)NFA_DATA(tb[CTA_PROTOINFO_TCP_STATE-1]);
+	/* window scale factor: original direction (SYN) */
+	if (tb[CTA_PROTOINFO_TCP_WSCALE_ORIGINAL-1]) {
+		ct->proto.tcp.seen[0].td_scale = 
+		*(u_int8_t *)NFA_DATA(tb[CTA_PROTOINFO_TCP_WSCALE_ORIGINAL-1]);
+		ct->proto.tcp.seen[0].flags |= IP_CT_TCP_FLAG_WINDOW_SCALE;
+	}
+	/* window scale factor: reply direction (SYN+ACK) */
+	if (tb[CTA_PROTOINFO_TCP_WSCALE_REPLY-1]) {
+		ct->proto.tcp.seen[1].td_scale = 
+		*(u_int8_t *)NFA_DATA(tb[CTA_PROTOINFO_TCP_WSCALE_REPLY-1]);
+		ct->proto.tcp.seen[1].flags |= IP_CT_TCP_FLAG_WINDOW_SCALE;
+	}
+	/* enable/disable SACK: original direction */
+	if (tb[CTA_PROTOINFO_TCP_SACK_ORIGINAL-1]) {
+		u_int8_t enable = 
+		*(u_int8_t *)NFA_DATA(tb[CTA_PROTOINFO_TCP_SACK_ORIGINAL-1]);
+		if (enable)
+			ct->proto.tcp.seen[0].flags |= 
+				IP_CT_TCP_FLAG_SACK_PERM;
+		else
+			ct->proto.tcp.seen[0].flags &= 
+				~IP_CT_TCP_FLAG_SACK_PERM;
+	}
+        /* enable/disable SACK: reply direction */
+	if (tb[CTA_PROTOINFO_TCP_SACK_REPLY-1]) {
+		u_int8_t enable = 
+		*(u_int8_t *)NFA_DATA(tb[CTA_PROTOINFO_TCP_SACK_REPLY-1]);
+		if (enable)
+			ct->proto.tcp.seen[1].flags |=
+				IP_CT_TCP_FLAG_SACK_PERM;
+		else
+			ct->proto.tcp.seen[1].flags &=
+				~IP_CT_TCP_FLAG_SACK_PERM;
+	}
 	write_unlock_bh(&tcp_lock);
 
 	return 0;
@@ -425,10 +476,10 @@ static unsigned int get_conntrack_index(
    we doesn't have to deal with fragments. 
 */
 
-static inline __u32 segment_seq_plus_len(__u32 seq,
-					 size_t len,
-					 struct iphdr *iph,
-					 struct tcphdr *tcph)
+static inline __u32 segment_seq_plus_len(const __u32 seq,
+					 const size_t len,
+					 const struct iphdr *iph,
+					 const struct tcphdr *tcph)
 {
 	return (seq + len - (iph->ihl + tcph->doff)*4
 		+ (tcph->syn ? 1 : 0) + (tcph->fin ? 1 : 0));
@@ -890,6 +941,22 @@ static int tcp_error(struct sk_buff *skb
 	return NF_ACCEPT;
 }
 
+static void tcp_pickup_window(struct ip_conntrack *conntrack,
+			      const struct sk_buff *skb,
+			      const struct iphdr *iph,
+			      const struct tcphdr *th)
+{
+	conntrack->proto.tcp.seen[0].td_end =
+		segment_seq_plus_len(ntohl(th->seq), skb->len, iph, th);
+	conntrack->proto.tcp.seen[0].td_maxwin = ntohs(th->window);
+	if (conntrack->proto.tcp.seen[0].td_maxwin == 0)
+		conntrack->proto.tcp.seen[0].td_maxwin = 1;
+	conntrack->proto.tcp.seen[0].td_maxend =
+		conntrack->proto.tcp.seen[0].td_end + 
+		conntrack->proto.tcp.seen[0].td_maxwin;
+		conntrack->proto.tcp.seen[0].td_scale = 0;
+}
+
 /* Returns verdict for packet, or -1 for invalid. */
 static int tcp_packet(struct ip_conntrack *conntrack,
 		      const struct sk_buff *skb,
@@ -907,6 +974,14 @@ static int tcp_packet(struct ip_conntrac
 	BUG_ON(th == NULL);
 	
 	write_lock_bh(&tcp_lock);
+
+	/*
+	 * This conntrack was added via ctnetlink or ct_sync and needs to
+	 * take over sequence tracking in order to work properly.
+	 */
+	if (test_and_clear_bit(IPS_PICKUP, &conntrack->status))
+		tcp_pickup_window(conntrack, skb, iph, th);
+
 	old_state = conntrack->proto.tcp.state;
 	dir = CTINFO2DIR(ctinfo);
 	index = get_conntrack_index(th);
@@ -1116,16 +1191,7 @@ static int tcp_new(struct ip_conntrack *
 		 * its history is lost for us.
 		 * Let's try to use the data from the packet.
 		 */
-		conntrack->proto.tcp.seen[0].td_end =
-			segment_seq_plus_len(ntohl(th->seq), skb->len,
-					     iph, th);
-		conntrack->proto.tcp.seen[0].td_maxwin = ntohs(th->window);
-		if (conntrack->proto.tcp.seen[0].td_maxwin == 0)
-			conntrack->proto.tcp.seen[0].td_maxwin = 1;
-		conntrack->proto.tcp.seen[0].td_maxend =
-			conntrack->proto.tcp.seen[0].td_end + 
-			conntrack->proto.tcp.seen[0].td_maxwin;
-		conntrack->proto.tcp.seen[0].td_scale = 0;
+		tcp_pickup_window(conntrack, skb, iph, th);
 
 		/* We assume SACK. Should we assume window scaling too? */
 		conntrack->proto.tcp.seen[0].flags =
include/linux/netfilter/nf_conntrack_common.h +4 -0
Index: net-2.6/include/linux/netfilter/nf_conntrack_common.h
===================================================================
@@ -73,6 +73,10 @@ enum ip_conntrack_status {
 	/* Connection has fixed timeout. */
 	IPS_FIXED_TIMEOUT_BIT = 10,
 	IPS_FIXED_TIMEOUT = (1 << IPS_FIXED_TIMEOUT_BIT),
+
+	/* Pick up connection information if required */
+	IPS_PICKUP_BIT = 11,
+	IPS_PICKUP = (1 << IPS_PICKUP_BIT),
 };
 
 /* Connection tracking event bits */
net/netfilter/nf_conntrack_proto_tcp.c +82 -16

144 lines not on this page · raw message

include/linux/netfilter/nfnetlink_conntrack.h +4 -0
Index: net-2.6/include/linux/netfilter/nfnetlink_conntrack.h
===================================================================
@@ -83,6 +83,10 @@ enum ctattr_protoinfo {
 enum ctattr_protoinfo_tcp {
 	CTA_PROTOINFO_TCP_UNSPEC,
 	CTA_PROTOINFO_TCP_STATE,
+	CTA_PROTOINFO_TCP_WSCALE_ORIGINAL,
+	CTA_PROTOINFO_TCP_WSCALE_REPLY,
+	CTA_PROTOINFO_TCP_SACK_ORIGINAL,
+	CTA_PROTOINFO_TCP_SACK_REPLY,
 	__CTA_PROTOINFO_TCP_MAX
 };
 #define CTA_PROTOINFO_TCP_MAX (__CTA_PROTOINFO_TCP_MAX - 1)
#1 lore raw
Quoted 4 lines · Pablo Neira Ayuso

This patch introduces a new flag called IPS_PICKUP that forces the protocol handler to pick up the window of valid TCP packets. Moreover, four new attributes to inject the window scale factor and enable SACK are introduced.

These new facilities provide the appropiate mechanisms to take over TCP connections in failover settings with TCP tracking enabled.

Are there any plans for active-active synchronization? This requires online TCP SEQ sync or to keep connections in IPS_PICKUP state forever, doesn't it?

Best regards,

Krzysztof Olędzki

author maintainer
#2 lore raw
Quoted 8 lines · Krzysztof Oledzki
Quoted 4 lines · Pablo Neira Ayuso

This patch introduces a new flag called IPS_PICKUP that forces the protocol handler to pick up the window of valid TCP packets. Moreover, four new attributes to inject the window scale factor and enable SACK are introduced.

These new facilities provide the appropiate mechanisms to take over TCP connections in failover settings with TCP tracking enabled.

Are there any plans for active-active synchronization? This requires online TCP SEQ sync or to keep connections in IPS_PICKUP state forever, doesn't it?

Hm, you mean the active-active setting for conntrackd? The current architecture already supports it.

You seem to be confused with the IPS_PICKUP flag: this flag must be set for conntracks created from userspace via ctnetlink, thus the TCP window tracking knows that it has to take over the valid window of TCP sequences, once that happens this flag is unset.

Signature · 1 line
The dawn of the fourth age of Linux firewalling is coming; a time of great struggle and heroic deeds -- J.Kadlecsik got inspired by J.Morris
#3 lore raw
Quoted 11 lines · Pablo Neira Ayuso
Quoted 8 lines · Krzysztof Oledzki
Quoted 4 lines · Pablo Neira Ayuso

This patch introduces a new flag called IPS_PICKUP that forces the protocol handler to pick up the window of valid TCP packets. Moreover, four new attributes to inject the window scale factor and enable SACK are introduced.

These new facilities provide the appropiate mechanisms to take over TCP connections in failover settings with TCP tracking enabled.

Are there any plans for active-active synchronization? This requires online TCP SEQ sync or to keep connections in IPS_PICKUP state forever, doesn't it?

Hm, you mean the active-active setting for conntrackd? The current architecture already supports it.

OK.

Quoted 1 line

You seem to be confused with the IPS_PICKUP flag: this flag must be set for conntracks created from userspace via ctnetlink, thus the TCP window tracking knows that it has to take over the valid window of TCP sequences, once that happens this flag is unset.

Hm, lets assume we have two firewalls for active-active configuration. How does the second firewall know that it should accept/drop a packet with specific seq number from connection that was previously handled by the first one?

Plase excuse me if this is obvious, but I wasn't able to find any information about how it was solved. All I found is the TODO file with:

o support for TCP window tracking
- at the moment you have to disable it:
echo 1 > /proc/sys/net/ipv4/netfilter/ip_conntrack_tcp_be_liberal

Best regards,

Krzysztof Olędzki

#4 lore raw
Quoted 13 lines · Krzysztof Oledzki
Quoted 4 lines · Pablo Neira Ayuso

You seem to be confused with the IPS_PICKUP flag: this flag must be set for conntracks created from userspace via ctnetlink, thus the TCP window tracking knows that it has to take over the valid window of TCP sequences, once that happens this flag is unset.

Hm, lets assume we have two firewalls for active-active configuration. How does the second firewall know that it should accept/drop a packet with specific seq number from connection that was previously handled by the first one?

Quoted 7 lines

Plase excuse me if this is obvious, but I wasn't able to find any information about how it was solved. All I found is the TODO file with:

o support for TCP window tracking
- at the moment you have to disable it:
echo 1 > /proc/sys/net/ipv4/netfilter/ip_conntrack_tcp_be_liberal

I think the question is whether connections will bounce between nodes multiple times or just once when a node fail. The former would need to have the IPS_PICKUP flag constantly enabled.

author maintainer
#5 lore raw

Sorry for the delay in the reply, I've been overloaded with work. This thread is important for conntrackd

Quoted 17 lines · Patrick McHardy
Quoted 16 lines · Krzysztof Oledzki
Quoted 4 lines · Pablo Neira Ayuso

You seem to be confused with the IPS_PICKUP flag: this flag must be set for conntracks created from userspace via ctnetlink, thus the TCP window tracking knows that it has to take over the valid window of TCP sequences, once that happens this flag is unset.

Hm, lets assume we have two firewalls for active-active configuration. How does the second firewall know that it should accept/drop a packet with specific seq number from connection that was previously handled by the first one?

Plase excuse me if this is obvious, but I wasn't able to find any information about how it was solved. All I found is the TODO file with:

Quoted 8 lines
Quoted 3 lines

o support for TCP window tracking
- at the moment you have to disable it:
echo 1 > /proc/sys/net/ipv4/netfilter/ip_conntrack_tcp_be_liberal

I think the question is whether connections will bounce between nodes multiple times or just once when a node fail. The former would need to have the IPS_PICKUP flag constantly enabled.

conntrackd bounces the connections to the other node iff a the node fails, so the flag needs to be enabled once in the creation of the entry. BTW, conntrackd supports up to 2 nodes for active-active settings at the moment, although I consider that this limitation is easy to
overcome: we can use a hash based approach similar to clusterip. Say we have three nodes:

node A, id 0
node B, id 1
node C, id 2

x = hash(tuple) % number of nodes
if (x == my_id)
process connection
else
ignore connection
fi

@Patrick: BTW, thanks for mangling the cleanup patch for ctnetlink (dump [+ clear counter] code), I was about to resend but you were faster, I really get annoyed myself with these little mistakes :(

Signature · 2 lines
The dawn of the fourth age of Linux firewalling is coming; a time of
great struggle and heroic deeds -- J.Kadlecsik got inspired by J.Morris