Commit a3a9f79e authored by Patrick McHardy's avatar Patrick McHardy

netfilter: tcp conntrack: fix unacknowledged data detection with NAT

When NAT helpers change the TCP packet size, the highest seen sequence
number needs to be corrected. This is currently only done upwards, when
the packet size is reduced the sequence number is unchanged. This causes
TCP conntrack to falsely detect unacknowledged data and decrease the
timeout.

Fix by updating the highest seen sequence number in both directions after
packet mangling.
Tested-by: default avatarKrzysztof Piotr Oledzki <ole@ans.pl>
Signed-off-by: default avatarPatrick McHardy <kaber@trash.net>
parent 308ff823
...@@ -258,8 +258,8 @@ static inline bool nf_ct_kill(struct nf_conn *ct) ...@@ -258,8 +258,8 @@ static inline bool nf_ct_kill(struct nf_conn *ct)
/* Update TCP window tracking data when NAT mangles the packet */ /* Update TCP window tracking data when NAT mangles the packet */
extern void nf_conntrack_tcp_update(const struct sk_buff *skb, extern void nf_conntrack_tcp_update(const struct sk_buff *skb,
unsigned int dataoff, unsigned int dataoff,
struct nf_conn *ct, struct nf_conn *ct, int dir,
int dir); s16 offset);
/* Fake conntrack entry for untracked connections */ /* Fake conntrack entry for untracked connections */
extern struct nf_conn nf_conntrack_untracked; extern struct nf_conn nf_conntrack_untracked;
......
...@@ -191,7 +191,8 @@ nf_nat_mangle_tcp_packet(struct sk_buff *skb, ...@@ -191,7 +191,8 @@ nf_nat_mangle_tcp_packet(struct sk_buff *skb,
ct, ctinfo); ct, ctinfo);
/* Tell TCP window tracking about seq change */ /* Tell TCP window tracking about seq change */
nf_conntrack_tcp_update(skb, ip_hdrlen(skb), nf_conntrack_tcp_update(skb, ip_hdrlen(skb),
ct, CTINFO2DIR(ctinfo)); ct, CTINFO2DIR(ctinfo),
(int)rep_len - (int)match_len);
nf_conntrack_event_cache(IPCT_NATSEQADJ, ct); nf_conntrack_event_cache(IPCT_NATSEQADJ, ct);
} }
...@@ -377,6 +378,7 @@ nf_nat_seq_adjust(struct sk_buff *skb, ...@@ -377,6 +378,7 @@ nf_nat_seq_adjust(struct sk_buff *skb,
struct tcphdr *tcph; struct tcphdr *tcph;
int dir; int dir;
__be32 newseq, newack; __be32 newseq, newack;
s16 seqoff, ackoff;
struct nf_conn_nat *nat = nfct_nat(ct); struct nf_conn_nat *nat = nfct_nat(ct);
struct nf_nat_seq *this_way, *other_way; struct nf_nat_seq *this_way, *other_way;
...@@ -390,15 +392,18 @@ nf_nat_seq_adjust(struct sk_buff *skb, ...@@ -390,15 +392,18 @@ nf_nat_seq_adjust(struct sk_buff *skb,
tcph = (void *)skb->data + ip_hdrlen(skb); tcph = (void *)skb->data + ip_hdrlen(skb);
if (after(ntohl(tcph->seq), this_way->correction_pos)) if (after(ntohl(tcph->seq), this_way->correction_pos))
newseq = htonl(ntohl(tcph->seq) + this_way->offset_after); seqoff = this_way->offset_after;
else else
newseq = htonl(ntohl(tcph->seq) + this_way->offset_before); seqoff = this_way->offset_before;
if (after(ntohl(tcph->ack_seq) - other_way->offset_before, if (after(ntohl(tcph->ack_seq) - other_way->offset_before,
other_way->correction_pos)) other_way->correction_pos))
newack = htonl(ntohl(tcph->ack_seq) - other_way->offset_after); ackoff = other_way->offset_after;
else else
newack = htonl(ntohl(tcph->ack_seq) - other_way->offset_before); ackoff = other_way->offset_before;
newseq = htonl(ntohl(tcph->seq) + seqoff);
newack = htonl(ntohl(tcph->ack_seq) - ackoff);
inet_proto_csum_replace4(&tcph->check, skb, tcph->seq, newseq, 0); inet_proto_csum_replace4(&tcph->check, skb, tcph->seq, newseq, 0);
inet_proto_csum_replace4(&tcph->check, skb, tcph->ack_seq, newack, 0); inet_proto_csum_replace4(&tcph->check, skb, tcph->ack_seq, newack, 0);
...@@ -413,7 +418,7 @@ nf_nat_seq_adjust(struct sk_buff *skb, ...@@ -413,7 +418,7 @@ nf_nat_seq_adjust(struct sk_buff *skb,
if (!nf_nat_sack_adjust(skb, tcph, ct, ctinfo)) if (!nf_nat_sack_adjust(skb, tcph, ct, ctinfo))
return 0; return 0;
nf_conntrack_tcp_update(skb, ip_hdrlen(skb), ct, dir); nf_conntrack_tcp_update(skb, ip_hdrlen(skb), ct, dir, seqoff);
return 1; return 1;
} }
......
...@@ -720,8 +720,8 @@ static bool tcp_in_window(const struct nf_conn *ct, ...@@ -720,8 +720,8 @@ static bool tcp_in_window(const struct nf_conn *ct,
/* Caller must linearize skb at tcp header. */ /* Caller must linearize skb at tcp header. */
void nf_conntrack_tcp_update(const struct sk_buff *skb, void nf_conntrack_tcp_update(const struct sk_buff *skb,
unsigned int dataoff, unsigned int dataoff,
struct nf_conn *ct, struct nf_conn *ct, int dir,
int dir) s16 offset)
{ {
const struct tcphdr *tcph = (const void *)skb->data + dataoff; const struct tcphdr *tcph = (const void *)skb->data + dataoff;
const struct ip_ct_tcp_state *sender = &ct->proto.tcp.seen[dir]; const struct ip_ct_tcp_state *sender = &ct->proto.tcp.seen[dir];
...@@ -734,7 +734,7 @@ void nf_conntrack_tcp_update(const struct sk_buff *skb, ...@@ -734,7 +734,7 @@ void nf_conntrack_tcp_update(const struct sk_buff *skb,
/* /*
* We have to worry for the ack in the reply packet only... * We have to worry for the ack in the reply packet only...
*/ */
if (after(end, ct->proto.tcp.seen[dir].td_end)) if (ct->proto.tcp.seen[dir].td_end + offset == end)
ct->proto.tcp.seen[dir].td_end = end; ct->proto.tcp.seen[dir].td_end = end;
ct->proto.tcp.last_end = end; ct->proto.tcp.last_end = end;
spin_unlock_bh(&ct->lock); spin_unlock_bh(&ct->lock);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment