DCCP conntrack/NAT
Subsystems (2)
These two patches contain my old conntrack/NAT helper for DCCP, updated to net-2.6.26.git and the missing parts (almost entirely) added.
They both depend on some other netfilter patches, I've attached them only hoping for some review :) A git tree which contains the full set of patches is (once upload finishes) located at:
git://git.kernel.org/pub/scm/linux/kernel/git/kaber/nf-2.6.26.git
A few words on the patches:
The NAT part is pretty uninteresting, it simply rewrites the ports and updates the checksum. The connection tracking module performs tracking of the connection, its states (mainly for appropriate timeout selection) and sequence number validation for Response/Ack packets during connection establishment. Window tracking and full sequence number validation is not implemented yet, connection pickup (creating valid states for connections not tracked from the beginning) is also not working yet. The connection tracker is comparable to the 2.4 TCP connection tracking helper.
The part that could really use some review by someone more familiar with DCCP than me is the state transistion table. The transistions should be mostly similar to those of a DCCP endpoint, the acceptable packets in each state differ slightly since the firewall sits in the middle and packets might get lost after passing though it. If someone spots incorrect transistions, please let me know since chances are good that its a bug.
For testing simply load the nf_conntrack_proto_dccp and nf_nat_proto_dccp modules and set up NAT rules as usual.
Comments welcome.
commit f448af3823a8de4260b0f45d7acc19db513a70fd
Author: Patrick McHardy <[email protected]> Date: Thu Mar 20 15:15:55 2008 +0100
[NETFILTER]: nf_conntrack: add DCCP protocol support
include/linux/netfilter/nf_conntrack_dccp.h new file +27 -0
@@ -0,0 +1,27 @@ +#ifndef _NF_CONNTRACK_DCCP_H +#define _NF_CONNTRACK_DCCP_H + +/* Exposed to userspace over nfnetlink */ +enum ct_dccp_states { +CT_DCCP_NONE , +CT_DCCP_REQUEST , +CT_DCCP_RESPOND , +CT_DCCP_PARTOPEN , +CT_DCCP_OPEN , +CT_DCCP_CLOSEREQ , +CT_DCCP_CLOSING , +CT_DCCP_TIMEWAIT , +CT_DCCP_IGNORE , +CT_DCCP_MAX , +}; + +#ifdef __KERNEL__ + +struct nf_ct_dccp { +u_int8_t state; +u_int64_t handshake_seq; +}; + +#endif /* __KERNEL__ */ + +#endif /* _NF_CONNTRACK_DCCP_H */
include/linux/netfilter/nfnetlink_conntrack.h +8 -0
@@ -80,6 +80,7 @@ enum ctattr_l4proto {enum ctattr_protoinfo {CTA_PROTOINFO_UNSPEC ,CTA_PROTOINFO_TCP , +CTA_PROTOINFO_DCCP , __CTA_PROTOINFO_MAX };#define CTA_PROTOINFO_MAX (__CTA_PROTOINFO_MAX - 1)
@@ -95,6 +96,13 @@ enum ctattr_protoinfo_tcp { };#define CTA_PROTOINFO_TCP_MAX (__CTA_PROTOINFO_TCP_MAX - 1) +enum ctattr_protoinfo_dccp { +CTA_PROTOINFO_DCCP_UNSPEC , +CTA_PROTOINFO_DCCP_STATE , + __CTA_PROTOINFO_DCCP_MAX, +}; +#define CTA_PROTOINFO_DCCP_MAX (__CTA_PROTOINFO_DCCP_MAX - 1) +enum ctattr_counters {CTA_COUNTERS_UNSPEC ,CTA_COUNTERS_PACKETS ,/* old 64bit counters */
include/net/netfilter/nf_conntrack.h +2 -0
@@ -20,6 +20,7 @@#include <asm/atomic.h> #include <linux/netfilter/nf_conntrack_tcp.h> +#include <linux/netfilter/nf_conntrack_dccp.h> #include <linux/netfilter/nf_conntrack_sctp.h> #include <linux/netfilter/nf_conntrack_proto_gre.h> #include <net/netfilter/ipv4/nf_conntrack_icmp.h>
@@ -30,6 +31,7 @@/* per conntrack: protocol private data */ union nf_conntrack_proto {/* insert conntrack proto private data here */ +struct nf_ct_dccp dccp;struct ip_ct_sctp sctp;struct ip_ct_tcp tcp;struct ip_ct_icmp icmp;
include/net/netfilter/nf_conntrack_tuple.h +6 -0
@@ -41,6 +41,9 @@ union nf_conntrack_man_proto } icmp;struct {__be16 port; + } dccp; +struct { +__be16 port; } sctp;struct {__be16 key;/* GRE key is 32bit, PPtP only uses 16bit */
@@ -79,6 +82,9 @@ struct nf_conntrack_tuple } icmp;struct {__be16 port; + } dccp; +struct { +__be16 port; } sctp;struct {__be16 key;
net/netfilter/Kconfig +10 -0
@@ -86,6 +86,16 @@ config NF_CONNTRACK_EVENTS If unsure, say `N'. +config NF_CT_PROTO_DCCP + tristate 'DCCP protocol connection tracking support (EXPERIMENTAL)' + depends on EXPERIMENTAL && NF_CONNTRACK + depends on NETFILTER_ADVANCED + help + With this option enabled, the layer 3 independent connection + tracking code will be able to do state tracking on DCCP connections. + + If unsure, say 'N'. + config NF_CT_PROTO_GRE tristate depends on NF_CONNTRACK
net/netfilter/Makefile +1 -0
@@ -13,6 +13,7 @@ obj-$(CONFIG_NETFILTER_NETLINK_LOG) += nfnetlink_log.o obj-$(CONFIG_NF_CONNTRACK) += nf_conntrack.o # SCTP protocol connection tracking +obj-$(CONFIG_NF_CT_PROTO_DCCP) += nf_conntrack_proto_dccp.o obj-$(CONFIG_NF_CT_PROTO_GRE) += nf_conntrack_proto_gre.o obj-$(CONFIG_NF_CT_PROTO_SCTP) += nf_conntrack_proto_sctp.o obj-$(CONFIG_NF_CT_PROTO_UDPLITE) += nf_conntrack_proto_udplite.o
net/netfilter/nf_conntrack_proto_dccp.c new file +761 -0
761 lines not on this page · raw message
commit d014df2fba76bae3affb8a9c5a91de704306d4a1
Author: Patrick McHardy <[email protected]> Date: Thu Mar 20 15:15:57 2008 +0100
[NETFILTER]: nf_nat: add DCCP protocol support
net/ipv4/netfilter/Kconfig +5 -0
@@ -241,6 +241,11 @@ config NF_NAT_SNMP_BASIC # <expr> '&&' <expr> (6) # # (6) Returns the result of min(/expr/, /expr/). +config NF_NAT_PROTO_DCCP + tristate + depends on NF_NAT && NF_CT_PROTO_DCCP + default NF_NAT && NF_CT_PROTO_DCCP + config NF_NAT_PROTO_GRE tristate depends on NF_NAT && NF_CT_PROTO_GRE
net/ipv4/netfilter/Makefile +1 -0
@@ -29,6 +29,7 @@ obj-$(CONFIG_NF_NAT_SNMP_BASIC) += nf_nat_snmp_basic.o obj-$(CONFIG_NF_NAT_TFTP) += nf_nat_tftp.o # NAT protocols (nf_nat) +obj-$(CONFIG_NF_NAT_PROTO_DCCP) += nf_nat_proto_dccp.o obj-$(CONFIG_NF_NAT_PROTO_GRE) += nf_nat_proto_gre.o obj-$(CONFIG_NF_NAT_PROTO_UDPLITE) += nf_nat_proto_udplite.o
net/ipv4/netfilter/nf_nat_proto_dccp.c new file +108 -0
108 lines not on this page · raw message
net/ipv4/netfilter/nf_nat_standalone.c +4 -2
@@ -51,7 +51,8 @@ static void nat_decode_session(struct sk_buff *skb, struct flowi *fl) fl->fl4_dst = t->dst.u3.ip;if (t->dst.protonum ==IPPROTO_TCP || t->dst.protonum ==IPPROTO_UDP || - t->dst.protonum ==IPPROTO_UDPLITE ) + t->dst.protonum ==IPPROTO_UDPLITE || + t->dst.protonum ==IPPROTO_DCCP ) fl->fl_ip_dport = t->dst.u.tcp.port; }
@@ -61,7 +62,8 @@ static void nat_decode_session(struct sk_buff *skb, struct flowi *fl) fl->fl4_src = t->src.u3.ip;if (t->dst.protonum ==IPPROTO_TCP || t->dst.protonum ==IPPROTO_UDP || - t->dst.protonum ==IPPROTO_UDPLITE ) + t->dst.protonum ==IPPROTO_UDPLITE || + t->dst.protonum ==IPPROTO_DCCP ) fl->fl_ip_sport = t->src.u.tcp.port; } }
Quoted 9 lines · Patrick McHardy
These two patches contain my old conntrack/NAT helper for DCCP, updated to net-2.6.26.git and the missing parts (almost entirely) added.
They both depend on some other netfilter patches, I've attached them only hoping for some review :) A git tree which contains the full set of patches is (once upload finishes) located at:
git://git.kernel.org/pub/scm/linux/kernel/git/kaber/nf-2.6.26.git
Speaking of git... I noticed people.netfilter.org has a git-daemon, so that would be fine for iptables, no?
Quoted 1 line
A few words on the patches:
(Where's the SCTP patch for review? :)
net/netfilter/nf_conntrack_proto_dccp.c line 386
Quoted 13 lines
net/netfilter/nf_conntrack_proto_dccp.c new file +13
-0+static int dccp_pkt_to_tuple (const struct sk_buff *skb,unsigned int dataoff, +struct nf_conntrack_tuple *tuple) +{ +struct dccp_hdr _hdr, *dh; + + dh =skb_header_pointer (skb, dataoff,sizeof (_hdr), &_hdr); +if (dh =NULL ) +return 0 ; + + tuple->src.u.dccp.port = dh->dccph_sport; + tuple->dst.u.dccp.port = dh->dccph_dport; +return 1 ; +}
Something related I have been wondering about ...
(actually nf_conntrack_l3proto_ipv4)
skb_header_pointer() is used for the case of a non-linear skb (has to do with IP fragments?).
In ipv4_pkt_to_tuple in nf_conntrack_l3proto_ipv4.c, skb_header_pointer() is used to get the [source address of the] IP header. Since I figured the layer-3 header must always be unfragmented, would not it be simpler to use ip_hdr(), or is there something that mandates use of skb_header_pointer?
Pablo Neira Ayuso
maintainerQuoted 29 lines · Jan Engelhardt
Quoted 9 lines · Patrick McHardy
These two patches contain my old conntrack/NAT helper for DCCP, updated to net-2.6.26.git and the missing parts (almost entirely) added.
They both depend on some other netfilter patches, I've attached them only hoping for some review :) A git tree which contains the full set of patches is (once upload finishes) located at:
git://git.kernel.org/pub/scm/linux/kernel/git/kaber/nf-2.6.26.gitSpeaking of git... I noticed people.netfilter.org has a git-daemon, so that would be fine for iptables, no?
Quoted 1 line
A few words on the patches:
(Where's the SCTP patch for review? :)
Quoted 9 lines
+static int dccp_pkt_to_tuple(const struct
sk_buff*skb, unsigned int dataoff,net/netfilter/nf_conntrack_proto_dccp.c new file +7
-0+ struct nf_conntrack_tuple *tuple) +{ +struct dccp_hdr _hdr, *dh; + + dh =skb_header_pointer (skb, dataoff,sizeof (_hdr), &_hdr); +if (dh =NULL ) +return 0 ;
Quoted 8 lines
net/netfilter/nf_conntrack_proto_dccp.c line 386
Quoted 5 lines
net/netfilter/nf_conntrack_proto_dccp.c new file +5
-0+ + tuple->src.u.dccp.port = dh->dccph_sport; + tuple->dst.u.dccp.port = dh->dccph_dport; +return 1 ; +}Something related I have been wondering about ...
(actuallynf_conntrack_l3proto_ipv4)
Well, this is not really related with this patch, I think that it would be a different thread since it has nothing to do with the DCCP friends. Anyway...
Quoted 2 lines
skb_header_pointer()is used for the case of a non-linear skb (has to do with IP fragments?).
Indeed.
Quoted 5 lines
In
ipv4_pkt_to_tupleinnf_conntrack_l3proto_ipv4.c,skb_header_pointer()is used to get the [source address of the] IP header. Since I figured the layer-3 header must always be unfragmented, would not it be simpler to useip_hdr(), or is there something that mandates use ofskb_header_pointer?
Right. I think that we can assume that the IP header is always linear (I remember this from a conversation with Davem or Rusty), Patrick?
Signature · 1 line
"Los honestos son inadaptados sociales" -- Les Luthiers
Pablo Neira Ayuso
maintainerQuoted 6 lines · Jan Engelhardt
Quoted 4 lines · Patrick McHardy
These two patches contain my old conntrack/NAT helper for DCCP, updated to net-2.6.26.git and the missing parts (almost entirely) added.
Quoted 8 lines
Quoted 5 lines
They both depend on some other netfilter patches, I've attached them only hoping for some review :) A git tree which contains the full set of patches is (once upload finishes) located at:
git://git.kernel.org/pub/scm/linux/kernel/git/kaber/nf-2.6.26.gitSpeaking of git... I noticed people.netfilter.org has a git-daemon, so that would be fine for iptables, no?
No, that's for a different purpose. We are setting up another git repository in Vishnu that will keep soon iptables.
Signature · 1 line
"Los honestos son inadaptados sociales" -- Les Luthiers
Quoted 26 lines · Pablo Neira Ayuso
Quoted 4 lines · Jan Engelhardt
Quoted 4 lines
+static int dccp_pkt_to_tuple(const struct
sk_buff*skb, unsigned int dataoff,(quoted diff) +2
-0+ struct nf_conntrack_tuple *tuple) +{Quoted 9 lines
net/netfilter/nf_conntrack_proto_dccp.c line 382
Quoted 6 lines
net/netfilter/nf_conntrack_proto_dccp.c new file +6
-0+struct dccp_hdr _hdr, *dh; + + dh =skb_header_pointer (skb, dataoff,sizeof (_hdr), &_hdr); +if (dh =NULL ) +return 0 ; +
skb_header_pointer()is used for the case of a non-linear skb (has to do with IP fragments?).Indeed.
Quoted 6 lines
In
ipv4_pkt_to_tupleinnf_conntrack_l3proto_ipv4.c,skb_header_pointer()is used to get the [source address of the] IP header. Since I figured the layer-3 header must always be unfragmented, would not it be simpler to useip_hdr(), or is there something that mandates use ofskb_header_pointer?
Quoted 3 lines
Right. I think that we can assume that the IP header is always linear (I remember this from a conversation with Davem or Rusty), Patrick?
Right, the IP header is linearized by ip_rcv() (or bridge netfilter). The pkt_to_tuple functions may be called for the inner packet of an ICMP message however, which might be in the non-linear area.
I have had a look at the connection-tracker and it proved a valuable exercise in re-reading the specs.
Some comments itemised below.
* nf_conntrack_proto_dccp.c:
- 8.1.2. Client Request
o reference is 8.1.1, not 8.1.2
o the Linux implementation uses same method as TCP handshake (and is currently the only available DCCP implementation), so can probably reuse TCP timeout values here -- the rules in RFC 4340 are not exactly clear.
- typo: state transistion table
OPEN: RPcket
* just curious about timeout for OPEN state: it is set to a full working week (5 * 24 * 3600 seconds). There is this wrap-around of DCCP timestamps after 11.2 hours, so maybe the Open state can be curtailed.
* Ignoring Sync/SyncAck packets: if this means they can get through, then it is good, since for instance CCID-2 may use a Sync for out-of-band information; RFC 4340 mentions Syncs for similar purposes (e.g. feature negotiation); and Syncs can appear in both directions.
* for the timeout sysctls, use proc_dointvec_ms_jiffies ?
* the state table has dimension DCCP_PKT_SYNCACK + 1, but what if a value greater than that appears in the dccp header in the statement state = dccp_state_table[IP_CT_DIR_ORIGINAL][dh->dccph_type][CT_DCCP_NONE];
* dccp_ack_seq() duplicates code from include/linux/dccp.h:
could use dccp_hdr_ack_seq() instead.
State Transitions in the original direction =====================
* DCCP-Request:
- in state Respond (sRS -> sRS), the Request is illegal (Respond is server state)
- also, the CLOSEREQ state transition (sCR -> sIG) is illegal: Requests are sent by clients only, and CLOSEREQ can only be entered by servers
- timewait transition -- question: is it possible to re-incarnate a new connection here instead of ignoring the (new) Request?
* DCCP-DataAck:
- the transition sRS should go to sPO (Partopen). This is because the client can send data when it has received the Response from the server, i.e. it is the same rule as for DCCP-Ack in state sRS (cf. RFC 4340, 8.1.5). The "Ack" in the DataAck has the same effect as an Ack in sRS, it acknowledges the Response and thus triggers transition to Partopen.
State Transitions in the reply direction
====================
* DCCP-CloseReq:
- the transition from sCG is a simultaneous-close, which is possible (both sides performing active-close, server sends CloseReq after client has sent a Close) and has been seen on the wire, cf.
http://www.erg.abdn.ac.uk/users/gerrit/dccp/notes/closing_states/ )
- use "ignore" here?
* DCCP-Close:
- the transition sCR -> sCG: I wonder if that is possible --
o if the client is behind the NAT, it means the server sent a Close after a CloseReq, which is invalid
o but if (hopefully soon) a server is behind a NAT, this would mean that the server had previously sent a CloseReq which now crosses paths with a Close from the client in the reverse direction -- again a simultaneous close, in this case sCR -> sCR would be possible
o simplest option - maybe better to use sCR -> sIG or drop packet.
The University of Aberdeen is a charity registered in Scotland, No SC013683.
Quoted 3 lines · Gerrit Renker
I have had a look at the connection-tracker and it proved a valuable exercise in re-reading the specs.
Some comments itemised below.
Thanks a lot for your review :)
Quoted 3 lines
*
nf_conntrack_proto_dccp.c:
- 8.1.2. Client Request
o reference is 8.1.1, not 8.1.2
Fixed, thanks.
Quoted 4 lines
o the Linux implementation uses same method as TCP handshake (and is currently the only available DCCP implementation), so can probably reuse TCP timeout values here -- the rules in RFC 4340 are not exactly clear.
We try not to be implementation specific (people might still be using this once more implementations have appeared), so keeping the larger timeout seems safer. It doesn't really hurt since the connection will get evicted under pressure as long as it hasn't moved to PARTOPEN with a correct ACK sequence number. Something more specific in the RFC would be nice though.
Quoted 2 lines
- typo: state transistion table
OPEN: RPcket
Thanks, fixed already in my tree.
Quoted 4 lines
* just curious about timeout for OPEN state: it is set to a full working week (5 * 24 * 3600 seconds). There is this wrap-around of DCCP timestamps after 11.2 hours, so maybe the Open state can be curtailed.
I just copied this part from the TCP helper because I didn't find a better value. Does the wraparound affect the maximum lifetime of a connection? Maybe it would make sense to decrease it in any case, I would expect applications using DCCP not to idle as long as TCP connections might.
Quoted 5 lines
* Ignoring Sync/SyncAck packets: if this means they can get through, then it is good, since for instance CCID-2 may use a Sync for out-of-band information; RFC 4340 mentions Syncs for similar purposes (e.g. feature negotiation); and Syncs can appear in both directions.
Yes, ignored packets just never cause state transistions.
Quoted 1 line
* for the timeout sysctls, use
proc_dointvec_ms_jiffies?
I chose seconds because its consistent with what other conntrack protocols use and less likely to confuse users.
Quoted 3 lines
* the state table has dimension
DCCP_PKT_SYNCACK+ 1, but what if a value greater than that appears in the dccp header in the statement state = dccp_state_table[IP_CT_DIR_ORIGINAL][dh->dccph_type][CT_DCCP_NONE];
All packets go through dccp_error() first, which catches invalid packet types.
Quoted 2 lines
*
dccp_ack_seq()duplicates code frominclude/linux/dccp.h:
could usedccp_hdr_ack_seq()instead.
Yes, this is something I still wanted to look into. The reason why its duplicated is that the DCCP protocol functions assume skb->transport_header to point to the DCCP header. This is only true for packets in the protocol layer.
Quoted 4 lines
State Transitions in the original direction =====================>* DCCP-Request:
- in state Respond (sRS -> sRS), the Request is illegal (Respond is server state)
Yes, this is one of the differences that comes from sitting in the middle :) In the reply direction we transition from sRQ to sRS when receiving a Response. However, that response might not make it to the client or simply be late, in which case the request is retransmitted.
Generally, for all states that exist only on one side (and we transistion to it even if the packet might never reach the other side), we must accept all packets in the other direction as in the previous state.
Quoted 2 lines
- also, the CLOSEREQ state transition (sCR -> sIG) is illegal: Requests are sent by clients only, and CLOSEREQ can only be entered by servers
We track both sides, so we must also define which client packets are valid in which server state. This particular one is part of the unfinished resync feature. The firewall might be out of sync with both endpoints. If connection pickup is enabled it should let packets that might establish a new connection pass and resync when the other side responds with a valid Response. I need to think about this a bit more, but I've marked it with FIXME for now :)
Quoted 2 lines
- timewait transition -- question: is it possible to re-incarnate a new connection here instead of ignoring the (new) Request?
Yes, I'll change this. The tricker case is a reincarnation in the reverse direction. The conntrack entry must be killed and recreated since the state table is directional and the client/server roles change. Also needs a bit more thought.
Quoted 6 lines
* DCCP-DataAck:
- the transition sRS should go to sPO (Partopen). This is because the client can send data when it has received the Response from the server, i.e. it is the same rule as for DCCP-Ack in state sRS (cf. RFC 4340, 8.1.5). The "Ack" in the DataAck has the same effect as an Ack in sRS, it acknowledges the Response and thus triggers transition to Partopen.
Thanks, fixed.
Quoted 9 lines
State Transitions in the reply direction ====================* DCCP-CloseReq:
- the transition from sCG is a simultaneous-close, which is possible (both sides performing active-close, server sends CloseReq after client has sent a Close) and has been seen on the wire, cf.
http://www.erg.abdn.ac.uk/users/gerrit/dccp/notes/closing_states/ )
- use "ignore" here?
In case the client needs to respond with another Close it should probably move to sCR. Otherwise I'd change it to stay (explicitly) in sCG. Ignore is mainly for resyncing.
Quoted 8 lines
* DCCP-Close:
- the transition sCR -> sCG: I wonder if that is possible --
o if the client is behind the NAT, it means the server sent a Close after a CloseReq, which is invalid
o but if (hopefully soon) a server is behind a NAT, this would mean that the server had previously sent a CloseReq which now crosses paths with a Close from the client in the reverse direction -- again a simultaneous close, in this case sCR -> sCR would be possible
NAT shouldn't make any difference for the states. The table is directional, so the Close transistion here is always a Close after a CloseReq from the server, so its also invalid.
Quoted 1 line
o simplest option - maybe better to use sCR -> sIG or drop packet.
IIRC I used sCG because I couldn't figure out whether its valid :) I'll change it to INVALID.
Thanks again for your review.
I have not a big understanding of netfilter and so got several things wrong in the last posting. Thank you for patience in clarifying these.
Quoted 11 lines
Quoted 4 lines
* just curious about timeout for OPEN state: it is set to a full
working week (5 * 24 * 3600 seconds). There is this
wrap-around of DCCP timestamps after 11.2 hours, so maybe the Open state can be curtailed.I just copied this part from the TCP helper because I didn't find a better value. Does the wraparound affect the maximum lifetime of a connection? Maybe it would make sense to decrease it in any case, I would expect applications using DCCP not to idle as long as TCP connections might.
DCCP uses a clock with a resolution of 0.00001 seconds (RFC 4340, 13.1). It thus wraps around much faster than the TCP suggestion of using 1ms timestamps (RFC 1323, 4.2.2(b)) that wrap around every 24.8 days.
It seems like this: timestamp is a 4-byte number, the 2^32 numbers need to be split into two halves ("before", "after"), each number stands for 10 microseconds, so the maximum timespan without wrap-around is about 5.96 hours. When the timespan is longer than that, "after" can become "before", i.e. there will be a glitch in RTT estimation and other parts that rely on timestamps. The full wrap-around, where the clock reaches the same value again, is after 11.9 hours.
However, the question is already resolved by the module's sysctl for the Open state.
Quoted 11 lines
Quoted 4 lines
State Transitions in the original direction =====================>>* DCCP-Request:
- in state Respond (sRS -> sRS), the Request is illegal (Respond is server state)Yes, this is one of the differences that comes from sitting in the middle :) In the reply direction we transition from sRQ to sRS when receiving a Response. However, that response might not make it to the client or simply be late, in which case the request is retransmitted.
Yes that was my error and the transition is clearly correct.
I have a question regarding the original direction - currently it is linked to the client which actively initiates a connection. DCCP suffers from the problem that peer-to-peer NAT traversal is not really possible just because of this client/server division. There is a proposal which effects a pseudo simultaneous open, by letting the server send an initiation packet, to fix this problem (TCP peer-to-peer NAT traversal also favours simultaneous-open). I wonder if this would be possible, but it is really a future-work question.
Quoted 10 lines
Quoted 9 lines
State Transitions in the reply direction ====================* DCCP-CloseReq:
- the transition from sCG is a simultaneous-close, which is possible (both sides performing active-close, server sends CloseReq after client has sent a Close) and has been seen on the wire, cf.
http://www.erg.abdn.ac.uk/users/gerrit/dccp/notes/closing_states/ )
- use "ignore" here?
Quoted 4 lines
In case the client needs to respond with another Close it should probably move to sCR. Otherwise I'd change it to stay (explicitly) in sCG. Ignore is mainly for resyncing.
Staying in sCG makes sense, in particular since RFC4340, 8.3 asks that a Close must be sent in reply to each CloseReq (even when in state Closing). So the client would retransmit its Close, which again would leave it in sCG. When the server gets the second Close, it may already have received the first one, thus it will respond with a Reset, Code 3 ("No Connection"), which would then resolve the simultaneous-close into sTW.
Gerrit
The University of Aberdeen is a charity registered in Scotland, No SC013683.
Quoted 25 lines · Gerrit Renker
I have not a big understanding of netfilter and so got several things wrong in the last posting. Thank you for patience in clarifying these.
Quoted 10 lines
Quoted 4 lines
* just curious about timeout for OPEN state: it is set to a full
working week (5 * 24 * 3600 seconds). There is this
wrap-around of DCCP timestamps after 11.2 hours, so maybe the Open state can be curtailed.I just copied this part from the TCP helper because I didn't find a better value. Does the wraparound affect the maximum lifetime of a connection? Maybe it would make sense to decrease it in any case, I would expect applications using DCCP not to idle as long as TCP connections might.
DCCP uses a clock with a resolution of 0.00001 seconds (RFC 4340, 13.1). It thus wraps around much faster than the TCP suggestion of using 1ms timestamps (RFC 1323, 4.2.2(b)) that wrap around every 24.8 days.
It seems like this: timestamp is a 4-byte number, the 2^32 numbers need to be split into two halves ("before", "after"), each number stands for 10 microseconds, so the maximum timespan without wrap-around is about 5.96 hours. When the timespan is longer than that, "after" can become "before", i.e. there will be a glitch in RTT estimation and other parts that rely on timestamps. The full wrap-around, where the clock reaches the same value again, is after 11.9 hours.
Quoted 2 lines
However, the question is already resolved by the module's sysctl for the Open state.
I've changed the default timeout for OPEN to 12 hours.
Quoted 13 lines
Quoted 11 lines
Quoted 5 lines
State Transitions in the original direction ===========================================* DCCP-Request:
- in state Respond (sRS -> sRS), the Request is illegal (Respond is server state)Yes, this is one of the differences that comes from sitting in the middle :) In the reply direction we transition from sRQ to sRS when receiving a Response. However, that response might not make it to the client or simply be late, in which case the request is retransmitted.
Yes that was my error and the transition is clearly correct.
Quoted 7 lines
I have a question regarding the original direction - currently it is
linked to the client which actively initiates a connection. DCCP suffers from the problem that peer-to-peer NAT traversal is not really possible just because of this client/server division. There
is a proposal which effects a pseudo simultaneous open, by letting
the server send an initiation packet, to fix this problem (TCP
peer-to-peer NAT traversal also favours simultaneous-open). I wonder
if this would be possible, but it is really a future-work question.
Yes, that should be possible. But how does the server know that the client intends to initiate a connection?
Quoted 13 lines
Quoted 9 lines
Quoted 9 lines
State Transitions in the reply direction ========================================* DCCP-CloseReq:
- the transition from sCG is a simultaneous-close, which is possible (both sides performing active-close, server sends CloseReq after client has sent a Close) and has been seen on the wire, cf.
http://www.erg.abdn.ac.uk/users/gerrit/dccp/notes/closing_states/ )
- use "ignore" here?Quoted 4 lines
In case the client needs to respond with another Close it should probably move to sCR. Otherwise I'd change it to stay (explicitly) in sCG. Ignore is mainly for resyncing.
Quoted 6 lines
Staying in sCG makes sense, in particular since RFC4340, 8.3 asks that a Close must be sent in reply to each CloseReq (even when in state Closing). So the client would retransmit its Close, which again would leave it in sCG. When the server gets the second Close, it may already have received the first one, thus it will respond with a Reset, Code 3 ("No Connection"), which would then resolve the simultaneous-close into sTW.
In that case sCR makes most sense since in that state we're expecting a Close from the client.
The attached patch contains the changes I've made so far based on your review. I'll go through the remaining points now.
net/netfilter/nf_conntrack_proto_dccp.c +8 -8
@@ -70,7 +70,7 @@ static unsigned int dccp_timeout[CT_DCCP_MAX + 1] __read_mostly = { [CT_DCCP_REQUEST] = 2 * DCCP_MSL, [CT_DCCP_RESPOND] = 4 * DCCP_MSL, [CT_DCCP_PARTOPEN] = 4 * DCCP_MSL, - [CT_DCCP_OPEN] = 5 * 86400 * HZ, + [CT_DCCP_OPEN] = 12 * 3600 * HZ, [CT_DCCP_CLOSEREQ] = 64 * HZ, [CT_DCCP_CLOSING] = 64 * HZ, [CT_DCCP_TIMEWAIT] = 2 * DCCP_MSL,
@@ -142,12 +142,12 @@ dccp_state_table[IP_CT_DIR_MAX + 1][DCCP_PKT_SYNCACK + 1][CT_DCCP_MAX + 1] = {* got lost after we saw it) or reincarnation * sPO -> sIG Request during PARTOPEN state, server will ignore it * sOP -> sIG Request during OPEN state: server will ignore it -* sCR -> sIG MUST respond with Close to CloseReq (8.3.) +* sCR -> sIG FIXME MUST respond with Close to CloseReq (8.3.) * sCG -> sIG -* sTW -> sIG Time-wait +* sTW -> sRQ Reincarnation * * sNO, sRQ, sRS, sPO. sOP, sCR, sCG, sTW, */ - sRQ, sRQ, sRS, sIG, sIG, sIG, sIG, sIG, + sRQ, sRQ, sRS, sIG, sIG, sIG, sIG, sRQ, }, [DCCP_PKT_RESPONSE] = {/*
@@ -188,7 +188,7 @@ dccp_state_table[IP_CT_DIR_MAX + 1][DCCP_PKT_SYNCACK + 1][CT_DCCP_MAX + 1] = {/* * sNO -> sIV No connection * sRQ -> sIV No connection -* sRS -> sIV No connection +* sRS -> sPO Ack for Response, move to PARTOPEN (8.1.5.) * sPO -> sPO Remain in PARTOPEN state * sOP -> sOP Regular DataAck packet in OPEN state * sCR -> sCR DataAck in CLOSEREQ MAY be processed (8.3.)
@@ -196,7 +196,7 @@ dccp_state_table[IP_CT_DIR_MAX + 1][DCCP_PKT_SYNCACK + 1][CT_DCCP_MAX + 1] = {* sTW -> sIV * * sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */ - sIV, sIV, sIV, sPO, sOP, sCR, sCG, sIV + sIV, sIV, sPO, sPO, sOP, sCR, sCG, sIV }, [DCCP_PKT_CLOSEREQ] = {/*
@@ -320,7 +320,7 @@ dccp_state_table[IP_CT_DIR_MAX + 1][DCCP_PKT_SYNCACK + 1][CT_DCCP_MAX + 1] = {* sPO -> sOP -> sCR Move directly to CLOSEREQ (8.1.5.) * sOP -> sCR CloseReq in OPEN state * sCR -> sCR Retransmit -* sCG -> sIV Already closing +* sCG -> sCR Simultaneous close, client sends another Close * sTW -> sIV Already closed * * sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */
@@ -333,7 +333,7 @@ dccp_state_table[IP_CT_DIR_MAX + 1][DCCP_PKT_SYNCACK + 1][CT_DCCP_MAX + 1] = {* sRS -> sIV No connection * sPO -> sOP -> sCG Move direcly to CLOSING * sOP -> sCG Move to CLOSING -* sCR -> sCG Waiting for close from client +* sCR -> sIV Close after CloseReq is invalid * sCG -> sCG Retransmit * sTW -> sIV Already closed *
Quoted 4 lines · Patrick McHardy
The attached patch contains the changes I've made so far based on your review. I'll go through the remaining points now.
New version attached that doesn't only update the comments but also the actual state transitions :)
net/netfilter/nf_conntrack_proto_dccp.c +10 -10
@@ -70,7 +70,7 @@ static unsigned int dccp_timeout[CT_DCCP_MAX + 1] __read_mostly = { [CT_DCCP_REQUEST] = 2 * DCCP_MSL, [CT_DCCP_RESPOND] = 4 * DCCP_MSL, [CT_DCCP_PARTOPEN] = 4 * DCCP_MSL, - [CT_DCCP_OPEN] = 5 * 86400 * HZ, + [CT_DCCP_OPEN] = 12 * 3600 * HZ, [CT_DCCP_CLOSEREQ] = 64 * HZ, [CT_DCCP_CLOSING] = 64 * HZ, [CT_DCCP_TIMEWAIT] = 2 * DCCP_MSL,
@@ -142,12 +142,12 @@ dccp_state_table[IP_CT_DIR_MAX + 1][DCCP_PKT_SYNCACK + 1][CT_DCCP_MAX + 1] = {* got lost after we saw it) or reincarnation * sPO -> sIG Request during PARTOPEN state, server will ignore it * sOP -> sIG Request during OPEN state: server will ignore it -* sCR -> sIG MUST respond with Close to CloseReq (8.3.) +* sCR -> sIG FIXME MUST respond with Close to CloseReq (8.3.) * sCG -> sIG -* sTW -> sIG Time-wait +* sTW -> sRQ Reincarnation * * sNO, sRQ, sRS, sPO. sOP, sCR, sCG, sTW, */ - sRQ, sRQ, sRS, sIG, sIG, sIG, sIG, sIG, + sRQ, sRQ, sRS, sIG, sIG, sIG, sIG, sRQ, }, [DCCP_PKT_RESPONSE] = {/*
@@ -188,7 +188,7 @@ dccp_state_table[IP_CT_DIR_MAX + 1][DCCP_PKT_SYNCACK + 1][CT_DCCP_MAX + 1] = {/* * sNO -> sIV No connection * sRQ -> sIV No connection -* sRS -> sIV No connection +* sRS -> sPO Ack for Response, move to PARTOPEN (8.1.5.) * sPO -> sPO Remain in PARTOPEN state * sOP -> sOP Regular DataAck packet in OPEN state * sCR -> sCR DataAck in CLOSEREQ MAY be processed (8.3.)
@@ -196,7 +196,7 @@ dccp_state_table[IP_CT_DIR_MAX + 1][DCCP_PKT_SYNCACK + 1][CT_DCCP_MAX + 1] = {* sTW -> sIV * * sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */ - sIV, sIV, sIV, sPO, sOP, sCR, sCG, sIV + sIV, sIV, sPO, sPO, sOP, sCR, sCG, sIV }, [DCCP_PKT_CLOSEREQ] = {/*
@@ -320,11 +320,11 @@ dccp_state_table[IP_CT_DIR_MAX + 1][DCCP_PKT_SYNCACK + 1][CT_DCCP_MAX + 1] = {* sPO -> sOP -> sCR Move directly to CLOSEREQ (8.1.5.) * sOP -> sCR CloseReq in OPEN state * sCR -> sCR Retransmit -* sCG -> sIV Already closing +* sCG -> sCR Simultaneous close, client sends another Close * sTW -> sIV Already closed * * sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */ - sIV, sIV, sIV, sCR, sCR, sCR, sIV, sIV + sIV, sIV, sIV, sCR, sCR, sCR, sCR, sIV }, [DCCP_PKT_CLOSE] = {/*
@@ -333,12 +333,12 @@ dccp_state_table[IP_CT_DIR_MAX + 1][DCCP_PKT_SYNCACK + 1][CT_DCCP_MAX + 1] = {* sRS -> sIV No connection * sPO -> sOP -> sCG Move direcly to CLOSING * sOP -> sCG Move to CLOSING -* sCR -> sCG Waiting for close from client +* sCR -> sIV Close after CloseReq is invalid * sCG -> sCG Retransmit * sTW -> sIV Already closed * * sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */ - sIV, sIV, sIV, sCG, sCG, sCG, sCG, sIV + sIV, sIV, sIV, sCG, sCG, sIV, sCG, sIV }, [DCCP_PKT_RESET] = {/*
Quoted 10 lines · Patrick McHardy
Quoted 4 lines · Gerrit Renker
- timewait transition -- question: is it possible to re-incarnate a
new connection
here instead of ignoring the (new) Request?Yes, I'll change this. The tricker case is a reincarnation in the reverse direction. The conntrack entry must be killed and recreated since the state table is directional and the client/server roles change. Also needs a bit more thought.
The last patch handled reincarnations in the original direction, this one adds support for reopening a connection in the reverse direction. I used role reversal instead of recreating the conntrack entry since that should make it easier to add DCCP_LISTEN support later on.
(Patch might not apply because of minor cleanups I made locally, I'll push out a new tree later).
include/linux/netfilter/nf_conntrack_dccp.h +10 -1
@@ -15,12 +15,21 @@ enum ct_dccp_states {CT_DCCP_INVALID , __CT_DCCP_MAX }; -#define CT_DCCP_MAX (__CT_DCCP_MAX - 1) +#define CT_DCCP_MAX (__CT_DCCP_MAX - 1) + +enum ct_dccp_roles { +CT_DCCP_ROLE_CLIENT , +CT_DCCP_ROLE_SERVER , + __CT_DCCP_ROLE_MAX +}; +#define CT_DCCP_ROLE_MAX (__CT_DCCP_ROLE_MAX - 1)#ifdef __KERNEL__ +#include <net/netfilter/nf_conntrack_tuple.h> struct nf_ct_dccp {u_int8_t state; +u_int8_t role[IP_CT_DIR_MAX ];u_int64_t handshake_seq; };
net/netfilter/nf_conntrack_proto_dccp.c +32 -8
@@ -138,8 +138,8 @@ static const char * const dccp_state_names[] = {* or a DCCP_RESPONSE. */ static const u_int8_t -dccp_state_table[IP_CT_DIR_MAX +1 ][DCCP_PKT_SYNCACK +1 ][CT_DCCP_MAX +1 ] = { - [IP_CT_DIR_ORIGINAL ] = { +dccp_state_table[CT_DCCP_ROLE_MAX +1 ][DCCP_PKT_SYNCACK +1 ][CT_DCCP_MAX +1 ] = { + [CT_DCCP_ROLE_CLIENT ] = { [DCCP_PKT_REQUEST ] = {/* * sNO -> sRQ Regular Request
@@ -157,7 +157,7 @@ dccp_state_table[IP_CT_DIR_MAX + 1][DCCP_PKT_SYNCACK + 1][CT_DCCP_MAX + 1] = { }, [DCCP_PKT_RESPONSE] = {/* -* A Response in the original direction is always invalid. +* A Response from the client is always invalid. * * sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */ sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV,
@@ -254,13 +254,23 @@ dccp_state_table[IP_CT_DIR_MAX + 1][DCCP_PKT_SYNCACK + 1][CT_DCCP_MAX + 1] = { sIG, sIG, sIG, sIG, sIG, sIG, sIG, sIG, }, }, - [IP_CT_DIR_REPLY] = { + [CT_DCCP_ROLE_SERVER] = { [DCCP_PKT_REQUEST ] = {/* -* A Request in the reply direction is always invalid. +* A Request from the server is only valid for reopening a +* connection in TIMEWAIT state. +* +* sNO -> sIV +* sRQ -> sIV +* sRS -> sIV +* sPO -> sIV +* sOP -> sIV +* sCR -> sIV +* sCG -> sIV +* sTW -> sRQ Reincarnation, must reverse roles * * sNO, sRQ, sRS, sPO, sOP, sCR, sCG, sTW */ - sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV + sIV, sIV, sIV, sIV, sIV, sIV, sIV, sRQ }, [DCCP_PKT_RESPONSE ] = {/*
@@ -410,7 +420,7 @@ static int dccp_new(struct nf_conn *ct, const struct sk_buff *skb, dh =skb_header_pointer (skb, dataoff,sizeof (_dh), &dh);BUG_ON (dh ==NULL ); - state = dccp_state_table[IP_CT_DIR_ORIGINAL ][dh->dccph_type][CT_DCCP_NONE ]; + state = dccp_state_table[CT_DCCP_ROLE_CLIENT ][dh->dccph_type][CT_DCCP_NONE ];switch (state) {default :if (nf_ct_dccp_loose ==0 ) {
@@ -425,6 +435,8 @@ static int dccp_new(struct nf_conn *ct, const struct sk_buff *skb, } ct->proto.dccp.state =CT_DCCP_NONE ; + ct->proto.dccp.role[IP_CT_DIR_ORIGINAL ] =CT_DCCP_ROLE_CLIENT ; + ct->proto.dccp.role[IP_CT_DIR_REPLY ] =CT_DCCP_ROLE_SERVER ;return 1 ; out_invalid:
@@ -446,8 +458,10 @@ static int dccp_packet(struct nf_conn *ct, const struct sk_buff *skb, unsigned int dataoff, enum ip_conntrack_info ctinfo, int pf, unsigned int hooknum) { + enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);struct dccp_hdr _dh, *dh;u_int8_t type, old_state, new_state; +enum ct_dccp_roles role; dh =skb_header_pointer (skb, dataoff,sizeof (_dh), &dh);BUG_ON (dh ==NULL );
@@ -463,10 +477,20 @@ static int dccp_packet(struct nf_conn *ct, const struct sk_buff *skb,write_lock_bh (&dccp_lock); + role = ct->proto.dccp.role[dir]; old_state = ct->proto.dccp.state; - new_state = dccp_state_table[CTINFO2DIR (ctinfo)][type][old_state]; + new_state = dccp_state_table[role][type][old_state];switch (new_state) { +case CT_DCCP_REQUEST : +if (old_state ==CT_DCCP_TIMEWAIT && + role ==CT_DCCP_ROLE_SERVER ) { +/* Reincarnation in the reverse direction: reopen and +* reverse client/server roles. */ + ct->proto.dccp.role[dir] =CT_DCCP_ROLE_CLIENT ; + ct->proto.dccp.role[!dir] =CT_DCCP_ROLE_SERVER ; + } +break ;case CT_DCCP_RESPOND :if (old_state ==CT_DCCP_REQUEST ) ct->proto.dccp.handshake_seq =dccp_hdr_seq (dh);
Quoted 9 lines
Quoted 8 lines
I have a question regarding the original direction - currently it is linked to the client which actively initiates a connection. DCCP suffers from the problem that peer-to-peer NAT traversal is not really possible just because of this client/server division. There is a proposal which effects a pseudo simultaneous open, by letting the server send an initiation packet, to fix this problem (TCP peer-to-peer NAT traversal also favours simultaneous-open). I wonder if this would be possible, but it is really a future-work question.
Quoted 3 lines
Yes, that should be possible. But how does the server know that the client intends to initiate a connection?
This is outside the actual NAT implementation, via out-of-band, e.g. using SIP or Session Traversal Utilities for NAT (draft-ietf-behave-rfc3489bis).
In this regard, the role-reversal patch will be a great help, since it will allow support for peer-to-peer NAT traversal (i.e. NAT-ed server).
The University of Aberdeen is a charity registered in Scotland, No SC013683.
Quoted 11 lines · Patrick McHardy
Quoted 9 lines · Gerrit Renker
State Transitions in the original direction
===========================================* DCCP-Request:
- in state Respond (sRS -> sRS), the Request is illegal (Respond
is server state)- also, the CLOSEREQ state transition (sCR -> sIG) is illegal:
Requests are sent by clients only, and CLOSEREQ can only be entered by servers
Quoted 8 lines
We track both sides, so we must also define which client packets are valid in which server state. This particular one is part of the unfinished resync feature. The firewall might be out of sync with both endpoints. If connection pickup is enabled it should let packets that might establish a new connection pass and resync when the other side responds with a valid Response. I need to think about this a bit more, but I've marked it with FIXME for now :)
I've added this patch on top to handle the out-of-sync case by letting Requests pass in (almost) any state and resyncing when seeing a valid Response.
Last TODO before the DCCP_LISTEN support is to fix connection pickup for established connections. Since it doesn't see the initial Request/Response it doesn't know which side has which role and also can't properly pick an inital state.
include/linux/netfilter/nf_conntrack_dccp.h +2 -0
@@ -30,6 +30,8 @@ enum ct_dccp_roles {struct nf_ct_dccp {u_int8_t role[IP_CT_DIR_MAX ];u_int8_t state; +u_int8_t last_pkt; +u_int8_t last_dir;u_int64_t handshake_seq; };
net/netfilter/nf_conntrack_proto_dccp.c +43 -22
108 lines not on this page · raw message
Quoted 13 lines · Gerrit Renker
Quoted 9 lines
Quoted 9 lines
I have a question regarding the original direction - currently it is linked to the client which actively initiates a connection. DCCP suffers from the problem that peer-to-peer NAT traversal is not really possible just because of this client/server division. There is a proposal which effects a pseudo simultaneous open, by letting the server send an initiation packet, to fix this problem (TCP peer-to-peer NAT traversal also favours simultaneous-open). I wonder if this would be possible, but it is really a future-work question.
Quoted 4 lines
Yes, that should be possible. But how does the server know that the client intends to initiate a connection?
Quoted 5 lines
This is outside the actual NAT implementation, via out-of-band, e.g. using SIP or Session Traversal Utilities for NAT (draft-ietf-behave-rfc3489bis).
In this regard, the role-reversal patch will be a great help, since it will allow support for peer-to-peer NAT traversal (i.e. NAT-ed server).
This sounds like it would already work when using the netfilter SIP helper. But the DCCP_LISTEN method is a lot simpler.
Quoted 8 lines · Patrick McHardy
I've added this patch on top to handle the out-of-sync case by letting Requests pass in (almost) any state and resyncing when seeing a valid Response.
Last TODO before the
DCCP_LISTENsupport is to fix connection pickup for established connections. Since it doesn't see the initial Request/Response it doesn't know which side has which role and also can't properly pick an inital state.
I skipped this for now since it gets a bit ugly and is not very important for an initial version. I'm uploading my current tree to kernel.org now, should be there within the next 20 minutes.
I'll post a patch for DCCP_LISTEN soon (not sure if I will manage to get it ready today though).