[NETFILTER]: Ignore ACKs ACKs on half open connections in TCP conntrack

Mounting NFS file systems after a (warm) reboot could take a long time if
firewalling and connection tracking was enabled.

The reason is that the NFS clients tends to use the same ports (800 and
counting down). Now on reboot, the server would still have a TCB for an
existing TCP connection client:800 -> server:2049. The client sends a
SYN from port 800 to server:2049, which elicits an ACK from the server.
The firewall on the client drops the ACK because (from its point of
view) the connection is still in half-open state, and it expects to see
a SYNACK.

The client will eventually time out after several minutes.

The following patch corrects this, by accepting ACKs on half open
connections as well.

Signed-off-by: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by Jozsef Kadlecsik and committed by David S. Miller 73f30602 5666c094

+40 -18
+20 -9
net/ipv4/netfilter/ip_conntrack_proto_tcp.c
··· 272 272 * sCL -> sCL 273 273 */ 274 274 /* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */ 275 - /*ack*/ { sIV, sIV, sSR, sES, sCW, sCW, sTW, sTW, sCL, sIV }, 275 + /*ack*/ { sIV, sIG, sSR, sES, sCW, sCW, sTW, sTW, sCL, sIV }, 276 276 /* 277 - * sSS -> sIV Might be a half-open connection. 277 + * sSS -> sIG Might be a half-open connection. 278 278 * sSR -> sSR Might answer late resent SYN. 279 279 * sES -> sES :-) 280 280 * sFW -> sCW Normal close request answered by ACK. ··· 917 917 918 918 switch (new_state) { 919 919 case TCP_CONNTRACK_IGNORE: 920 - /* Either SYN in ORIGINAL 921 - * or SYN/ACK in REPLY. */ 920 + /* Ignored packets: 921 + * 922 + * a) SYN in ORIGINAL 923 + * b) SYN/ACK in REPLY 924 + * c) ACK in reply direction after initial SYN in original. 925 + */ 922 926 if (index == TCP_SYNACK_SET 923 927 && conntrack->proto.tcp.last_index == TCP_SYN_SET 924 928 && conntrack->proto.tcp.last_dir != dir ··· 989 985 } 990 986 case TCP_CONNTRACK_CLOSE: 991 987 if (index == TCP_RST_SET 992 - && test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status) 993 - && conntrack->proto.tcp.last_index == TCP_SYN_SET 988 + && ((test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status) 989 + && conntrack->proto.tcp.last_index == TCP_SYN_SET) 990 + || (!test_bit(IPS_ASSURED_BIT, &conntrack->status) 991 + && conntrack->proto.tcp.last_index == TCP_ACK_SET)) 994 992 && ntohl(th->ack_seq) == conntrack->proto.tcp.last_end) { 995 - /* RST sent to invalid SYN we had let trough 996 - * SYN was in window then, tear down connection. 993 + /* RST sent to invalid SYN or ACK we had let trough 994 + * at a) and c) above: 995 + * 996 + * a) SYN was in window then 997 + * c) we hold a half-open connection. 998 + * 999 + * Delete our connection entry. 997 1000 * We skip window checking, because packet might ACK 998 - * segments we ignored in the SYN. */ 1001 + * segments we ignored. */ 999 1002 goto in_window; 1000 1003 } 1001 1004 /* Just fall trough */
+20 -9
net/netfilter/nf_conntrack_proto_tcp.c
··· 280 280 * sCL -> sCL 281 281 */ 282 282 /* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */ 283 - /*ack*/ { sIV, sIV, sSR, sES, sCW, sCW, sTW, sTW, sCL, sIV }, 283 + /*ack*/ { sIV, sIG, sSR, sES, sCW, sCW, sTW, sTW, sCL, sIV }, 284 284 /* 285 - * sSS -> sIV Might be a half-open connection. 285 + * sSS -> sIG Might be a half-open connection. 286 286 * sSR -> sSR Might answer late resent SYN. 287 287 * sES -> sES :-) 288 288 * sFW -> sCW Normal close request answered by ACK. ··· 912 912 913 913 switch (new_state) { 914 914 case TCP_CONNTRACK_IGNORE: 915 - /* Either SYN in ORIGINAL 916 - * or SYN/ACK in REPLY. */ 915 + /* Ignored packets: 916 + * 917 + * a) SYN in ORIGINAL 918 + * b) SYN/ACK in REPLY 919 + * c) ACK in reply direction after initial SYN in original. 920 + */ 917 921 if (index == TCP_SYNACK_SET 918 922 && conntrack->proto.tcp.last_index == TCP_SYN_SET 919 923 && conntrack->proto.tcp.last_dir != dir ··· 983 979 } 984 980 case TCP_CONNTRACK_CLOSE: 985 981 if (index == TCP_RST_SET 986 - && test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status) 987 - && conntrack->proto.tcp.last_index == TCP_SYN_SET 982 + && ((test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status) 983 + && conntrack->proto.tcp.last_index == TCP_SYN_SET) 984 + || (!test_bit(IPS_ASSURED_BIT, &conntrack->status) 985 + && conntrack->proto.tcp.last_index == TCP_ACK_SET)) 988 986 && ntohl(th->ack_seq) == conntrack->proto.tcp.last_end) { 989 - /* RST sent to invalid SYN we had let trough 990 - * SYN was in window then, tear down connection. 987 + /* RST sent to invalid SYN or ACK we had let trough 988 + * at a) and c) above: 989 + * 990 + * a) SYN was in window then 991 + * c) we hold a half-open connection. 992 + * 993 + * Delete our connection entry. 991 994 * We skip window checking, because packet might ACK 992 - * segments we ignored in the SYN. */ 995 + * segments we ignored. */ 993 996 goto in_window; 994 997 } 995 998 /* Just fall trough */