Skip to content

Commit c7aab4f

Browse files
Florian Westphalummakynes
authored andcommitted
netfilter: nf_conntrack_tcp: re-init for syn packets only
Jaco Kroon reported tcp problems that Eric Dumazet and Neal Cardwell pinpointed to nf_conntrack tcp_in_window() bug. tcp trace shows following sequence: I > R Flags [S], seq 3451342529, win 62580, options [.. tfo [|tcp]> R > I Flags [S.], seq 2699962254, ack 3451342530, win 65535, options [..] R > I Flags [P.], seq 1:89, ack 1, [..] Note 3rd ACK is from responder to initiator so following branch is taken: } else if (((state->state == TCP_CONNTRACK_SYN_SENT && dir == IP_CT_DIR_ORIGINAL) || (state->state == TCP_CONNTRACK_SYN_RECV && dir == IP_CT_DIR_REPLY)) && after(end, sender->td_end)) { ... because state == TCP_CONNTRACK_SYN_RECV and dir is REPLY. This causes the scaling factor to be reset to 0: window scale option is only present in syn(ack) packets. This in turn makes nf_conntrack mark valid packets as out-of-window. This was always broken, it exists even in original commit where window tracking was added to ip_conntrack (nf_conntrack predecessor) in 2.6.9-rc1 kernel. Restrict to 'tcph->syn', just like the 3rd condtional added in commit 82b72cb ("netfilter: conntrack: re-init state for retransmitted syn-ack"). Upon closer look, those conditionals/branches can be merged: Because earlier checks prevent syn-ack from showing up in original direction, the 'dir' checks in the conditional quoted above are redundant, remove them. Return early for pure syn retransmitted in reply direction (simultaneous open). Fixes: 9fb9cbb ("[NETFILTER]: Add nf_conntrack subsystem.") Reported-by: Jaco Kroon <[email protected]> Signed-off-by: Florian Westphal <[email protected]> Acked-by: Jozsef Kadlecsik <[email protected]> Signed-off-by: Pablo Neira Ayuso <[email protected]>
1 parent a1bde8c commit c7aab4f

File tree

1 file changed

+6
-15
lines changed

1 file changed

+6
-15
lines changed

net/netfilter/nf_conntrack_proto_tcp.c

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -556,32 +556,23 @@ static bool tcp_in_window(struct nf_conn *ct,
556556
}
557557

558558
}
559-
} else if (((state->state == TCP_CONNTRACK_SYN_SENT
560-
&& dir == IP_CT_DIR_ORIGINAL)
561-
|| (state->state == TCP_CONNTRACK_SYN_RECV
562-
&& dir == IP_CT_DIR_REPLY))
563-
&& after(end, sender->td_end)) {
559+
} else if (tcph->syn &&
560+
after(end, sender->td_end) &&
561+
(state->state == TCP_CONNTRACK_SYN_SENT ||
562+
state->state == TCP_CONNTRACK_SYN_RECV)) {
564563
/*
565564
* RFC 793: "if a TCP is reinitialized ... then it need
566565
* not wait at all; it must only be sure to use sequence
567566
* numbers larger than those recently used."
568-
*/
569-
sender->td_end =
570-
sender->td_maxend = end;
571-
sender->td_maxwin = (win == 0 ? 1 : win);
572-
573-
tcp_options(skb, dataoff, tcph, sender);
574-
} else if (tcph->syn && dir == IP_CT_DIR_REPLY &&
575-
state->state == TCP_CONNTRACK_SYN_SENT) {
576-
/* Retransmitted syn-ack, or syn (simultaneous open).
577567
*
578568
* Re-init state for this direction, just like for the first
579569
* syn(-ack) reply, it might differ in seq, ack or tcp options.
580570
*/
581571
tcp_init_sender(sender, receiver,
582572
skb, dataoff, tcph,
583573
end, win);
584-
if (!tcph->ack)
574+
575+
if (dir == IP_CT_DIR_REPLY && !tcph->ack)
585576
return true;
586577
}
587578

0 commit comments

Comments
 (0)