conntrack: RFC5961 challenge ACK confuse conntrack LAST-ACK transition
Subsystems (3)
PATCH 1/1 conntrack: RFC5961 challenge ACK confuse conntrack LAST-ACK transition
In compliance with RFC5961, the network stack send challenge ACK in response to spurious SYN packets, since commit 0c228e833c88 ("tcp:
Restore RFC5961-compliant behavior for SYN packets").
This pose a problem for netfilter conntrack in state LAST_ACK, because this challenge ACK is (falsely) seen as ACKing last FIN, causing a false state transition (into TIME_WAIT).
The challenge ACK is hard to distinguish from real last ACK. Thus, solution introduce a flag that tracks the potential for seeing a challenge ACK, in case a SYN packet is let through and current state is LAST_ACK.
When conntrack transition LAST_ACK to TIME_WAIT happens, this flag is used for determining if we are expecting a challenge ACK.
Scapy based reproducer script avail here:
https://github.com/netoptimizer/network-testing/blob/master/scapy/tcp_hacks_3WHS_LAST_ACK.py
---
include/uapi/linux/netfilter/nf_conntrack_tcp.h | 3 ++ net/netfilter/nf_conntrack_proto_tcp.c | 35 +++++++++++++++++++++-- 2 files changed, 35 insertions(+), 3 deletions(-)
include/uapi/linux/netfilter/nf_conntrack_tcp.h +3 -0
@@ -42,6 +42,9 @@ enum tcp_conntrack {/* The field td_maxack has been set */ #define IP_CT_TCP_FLAG_MAXACK_SET 0x20 +/* Marks possibility for expected RFC5961 challenge ACK */ +#define IP_CT_EXP_CHALLENGE_ACK 0x40 +struct nf_ct_tcp_flags {__u8 flags;__u8 mask;
net/netfilter/nf_conntrack_proto_tcp.c +32 -3
@@ -202,7 +202,7 @@ static const u8 tcp_conntracks[2][6][TCP_CONNTRACK_MAX] = {* sES -> sES :-) * sFW -> sCW Normal close request answered by ACK. * sCW -> sCW -* sLA -> sTW Last ACK detected. +* sLA -> sTW Last ACK detected (RFC5961 challenged) * sTW -> sTW Retransmitted last ACK. Remain in the same state. * sCL -> sCL */
@@ -261,7 +261,7 @@ static const u8 tcp_conntracks[2][6][TCP_CONNTRACK_MAX] = {* sES -> sES :-) * sFW -> sCW Normal close request answered by ACK. * sCW -> sCW -* sLA -> sTW Last ACK detected. +* sLA -> sTW Last ACK detected (RFC5961 challenged) * sTW -> sTW Retransmitted last ACK. * sCL -> sCL */
@@ -906,6 +906,7 @@ static int tcp_packet(struct nf_conn *ct, 1 : ct->proto.tcp.last_win; ct->proto.tcp.seen[ct->proto.tcp.last_dir].td_scale = ct->proto.tcp.last_wscale; + ct->proto.tcp.last_flags &= ~IP_CT_EXP_CHALLENGE_ACK ; ct->proto.tcp.seen[ct->proto.tcp.last_dir].flags = ct->proto.tcp.last_flags; memset(&ct->proto.tcp.seen[dir], 0,
@@ -923,7 +924,9 @@ static int tcp_packet(struct nf_conn *ct,* may be in sync but we are not. In that case, we annotate * the TCP options and let the packet go through. If it is a * valid SYN packet, the server will reply with a SYN/ACK, and -* then we'll get in sync. Otherwise, the server ignores it. */ +* then we'll get in sync. Otherwise, the server potentially +* respons with a challenge ACK if implementing RFC5961. +*/ if (index ==TCP_SYN_SET && dir ==IP_CT_DIR_ORIGINAL ) {struct ip_ct_tcp_state seen = {};
@@ -939,6 +942,13 @@ static int tcp_packet(struct nf_conn *ct, ct->proto.tcp.last_flags |=IP_CT_TCP_FLAG_SACK_PERM ; } +/* Mark the potential for RFC5961 challenge ACK, +* this pose a special problem for LAST_ACK state +* as ACK is intrepretated as ACKing last FIN. +*/ +if (old_state ==TCP_CONNTRACK_LAST_ACK ) + ct->proto.tcp.last_flags |= +IP_CT_EXP_CHALLENGE_ACK ; }spin_unlock_bh (&ct->lock);if (LOG_INVALID (net,IPPROTO_TCP ))
@@ -970,6 +980,25 @@ static int tcp_packet(struct nf_conn *ct,nf_log_packet (net, pf,0 , skb,NULL ,NULL ,NULL ,"nf_ct_tcp: invalid state " );return -NF_ACCEPT ; +case TCP_CONNTRACK_TIME_WAIT : +/* RFC5961 compliance cause stack to send "challenge-ACK" +* e.g. in response to spurious SYNs. Conntrack MUST +* not believe this ACK is acking last FIN. +*/ +if (old_state ==TCP_CONNTRACK_LAST_ACK + && index ==TCP_ACK_SET + && ct->proto.tcp.last_dir != dir + && ct->proto.tcp.last_index ==TCP_SYN_SET + && (ct->proto.tcp.last_flags &IP_CT_EXP_CHALLENGE_ACK )) { +/* Detected RFC5961 challenge ACK */ + ct->proto.tcp.last_flags &= ~IP_CT_EXP_CHALLENGE_ACK ; +spin_unlock_bh (&ct->lock); +if (LOG_INVALID (net,IPPROTO_TCP )) +nf_log_packet (net, pf,0 , skb,NULL ,NULL ,NULL , +"nf_ct_tcp: challenge-ACK ignored " ); +return NF_ACCEPT ;/* Don't change state */ + } +break ;case TCP_CONNTRACK_CLOSE :if (index ==TCP_RST_SET && (ct->proto.tcp.seen[!dir].flags & IP_CT_TCP_FLAG_MAXACK_SET)
Hi,
Quoted 13 lines · Jesper Dangaard Brouer
In compliance with RFC5961, the network stack send challenge ACK in response to spurious SYN packets, since commit
0c228e833c88("tcp:
Restore RFC5961-compliant behavior for SYN packets").This pose a problem for netfilter conntrack in state
LAST_ACK, because this challenge ACK is (falsely) seen as ACKing last FIN, causing a false state transition (intoTIME_WAIT).The challenge ACK is hard to distinguish from real last ACK. Thus, solution introduce a flag that tracks the potential for seeing a challenge ACK, in case a SYN packet is let through and current state is
LAST_ACK.
Quoted 8 lines
When conntrack transition
LAST_ACKtoTIME_WAIThappens, this flag is used for determining if we are expecting a challenge ACK.Scapy based reproducer script avail here:
https://github.com/netoptimizer/network-testing/blob/master/scapy/tcp_hacks_3WHS_LAST_ACK.pyFixes 0c228e833c88 ("tcp: Restore RFC5961-compliant behavior for SYN packets")Signed-off-by Jesper Dangaard Brouer <[email protected]>
The patch looks OK to me, thanks Jesper.
Best regards,
Jozsef
Quoted 103 lines
---
include/uapi/linux/netfilter/nf_conntrack_tcp.h | 3 ++ net/netfilter/nf_conntrack_proto_tcp.c | 35 +++++++++++++++++++++-- 2 files changed, 35 insertions(+), 3 deletions(-)include/uapi/linux/netfilter/nf_conntrack_tcp.h +3
-0@@ -42,6 +42,9 @@ enum tcp_conntrack {/* The field td_maxack has been set */ #define IP_CT_TCP_FLAG_MAXACK_SET 0x20 +/* Marks possibility for expected RFC5961 challenge ACK */ +#define IP_CT_EXP_CHALLENGE_ACK 0x40 +struct nf_ct_tcp_flags {__u8 flags;__u8 mask;net/netfilter/nf_conntrack_proto_tcp.c +32
-4@@ -202,7 +202,7 @@ static const u8 tcp_conntracks[2][6][TCP_CONNTRACK_MAX] = {* sES -> sES :-) * sFW -> sCW Normal close request answered by ACK. * sCW -> sCW -* sLA -> sTW Last ACK detected. +* sLA -> sTW Last ACK detected (RFC5961 challenged) * sTW -> sTW Retransmitted last ACK. Remain in the same state. * sCL -> sCL */ @@ -261,7 +261,7 @@ static const u8 tcp_conntracks[2][6][TCP_CONNTRACK_MAX] = {* sES -> sES :-) * sFW -> sCW Normal close request answered by ACK. * sCW -> sCW -* sLA -> sTW Last ACK detected. +* sLA -> sTW Last ACK detected (RFC5961 challenged) * sTW -> sTW Retransmitted last ACK. * sCL -> sCL */ @@ -906,6 +906,7 @@ static int tcp_packet(struct nf_conn *ct, 1 : ct->proto.tcp.last_win; ct->proto.tcp.seen[ct->proto.tcp.last_dir].td_scale = ct->proto.tcp.last_wscale; + ct->proto.tcp.last_flags &= ~IP_CT_EXP_CHALLENGE_ACK ; ct->proto.tcp.seen[ct->proto.tcp.last_dir].flags = ct->proto.tcp.last_flags; memset(&ct->proto.tcp.seen[dir], 0,@@ -923,7 +924,9 @@ static int tcp_packet(struct nf_conn *ct,* may be in sync but we are not. In that case, we annotate * the TCP options and let the packet go through. If it is a * valid SYN packet, the server will reply with a SYN/ACK, and -* then we'll get in sync. Otherwise, the server ignores it. */ +* then we'll get in sync. Otherwise, the server potentially +* respons with a challenge ACK if implementing RFC5961. +*/ if (index ==TCP_SYN_SET && dir ==IP_CT_DIR_ORIGINAL ) {struct ip_ct_tcp_state seen = {};@@ -939,6 +942,13 @@ static int tcp_packet(struct nf_conn *ct, ct->proto.tcp.last_flags |=IP_CT_TCP_FLAG_SACK_PERM ; } +/* Mark the potential for RFC5961 challenge ACK, +* this pose a special problem for LAST_ACK state +* as ACK is intrepretated as ACKing last FIN. +*/ +if (old_state ==TCP_CONNTRACK_LAST_ACK ) + ct->proto.tcp.last_flags |= +IP_CT_EXP_CHALLENGE_ACK ; }spin_unlock_bh (&ct->lock);if (LOG_INVALID (net,IPPROTO_TCP ))@@ -970,6 +980,25 @@ static int tcp_packet(struct nf_conn *ct,nf_log_packet (net, pf,0 , skb,NULL ,NULL ,NULL ,"nf_ct_tcp: invalid state " );return -NF_ACCEPT ; +case TCP_CONNTRACK_TIME_WAIT : +/* RFC5961 compliance cause stack to send "challenge-ACK" +* e.g. in response to spurious SYNs. Conntrack MUST +* not believe this ACK is acking last FIN. +*/ +if (old_state ==TCP_CONNTRACK_LAST_ACK + && index ==TCP_ACK_SET + && ct->proto.tcp.last_dir != dir + && ct->proto.tcp.last_index ==TCP_SYN_SET + && (ct->proto.tcp.last_flags &IP_CT_EXP_CHALLENGE_ACK )) { +/* Detected RFC5961 challenge ACK */ + ct->proto.tcp.last_flags &= ~IP_CT_EXP_CHALLENGE_ACK ; +spin_unlock_bh (&ct->lock); +if (LOG_INVALID (net,IPPROTO_TCP )) +nf_log_packet (net, pf,0 , skb,NULL ,NULL ,NULL , +"nf_ct_tcp: challenge-ACK ignored " ); +return NF_ACCEPT ;/* Don't change state */ + } +break ;case TCP_CONNTRACK_CLOSE :if (index ==TCP_RST_SET && (ct->proto.tcp.seen[!dir].flags & IP_CT_TCP_FLAG_MAXACK_SET) --
Quoted 4 lines
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
-
E-mail : [email protected], [email protected] PGP key : http://www.kfki.hu/~kadlec/pgp_public_key.txt
Address : Wigner Research Centre for Physics, Hungarian Academy of Sciences
H-1525 Budapest 114, POB. 49, Hungary
Quoted 23 lines · Jozsef Kadlecsik
Hi,
Quoted 19 lines · Jesper Dangaard Brouer
In compliance with RFC5961, the network stack send challenge ACK in response to spurious SYN packets, since commit
0c228e833c88("tcp:
Restore RFC5961-compliant behavior for SYN packets").This pose a problem for netfilter conntrack in state
LAST_ACK, because this challenge ACK is (falsely) seen as ACKing last FIN, causing a false state transition (intoTIME_WAIT).The challenge ACK is hard to distinguish from real last ACK. Thus, solution introduce a flag that tracks the potential for seeing a challenge ACK, in case a SYN packet is let through and current state is
LAST_ACK.When conntrack transition
LAST_ACKtoTIME_WAIThappens, this flag is used for determining if we are expecting a challenge ACK.Scapy based reproducer script avail here:
https://github.com/netoptimizer/network-testing/blob/master/scapy/tcp_hacks_3WHS_LAST_ACK.py
Quoted 6 lines
Quoted 2 lines
Fixes 0c228e833c88 ("tcp: Restore RFC5961-compliant behavior for SYN packets")Signed-off-by Jesper Dangaard Brouer <[email protected]>The patch looks OK to me, thanks Jesper.
Acked-by Jozsef Kadlecsik <[email protected]>
Applied, thanks.