Skip to content

Commit a3a9f79

Browse files
committed
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: Krzysztof Piotr Oledzki <ole@ans.pl> Signed-off-by: Patrick McHardy <kaber@trash.net>
1 parent 308ff82 commit a3a9f79

File tree

3 files changed

+16
-11
lines changed

3 files changed

+16
-11
lines changed

include/net/netfilter/nf_conntrack.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -258,8 +258,8 @@ static inline bool nf_ct_kill(struct nf_conn *ct)
258258
/* Update TCP window tracking data when NAT mangles the packet */
259259
extern void nf_conntrack_tcp_update(const struct sk_buff *skb,
260260
unsigned int dataoff,
261-
struct nf_conn *ct,
262-
int dir);
261+
struct nf_conn *ct, int dir,
262+
s16 offset);
263263

264264
/* Fake conntrack entry for untracked connections */
265265
extern struct nf_conn nf_conntrack_untracked;

net/ipv4/netfilter/nf_nat_helper.c

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,8 @@ nf_nat_mangle_tcp_packet(struct sk_buff *skb,
191191
ct, ctinfo);
192192
/* Tell TCP window tracking about seq change */
193193
nf_conntrack_tcp_update(skb, ip_hdrlen(skb),
194-
ct, CTINFO2DIR(ctinfo));
194+
ct, CTINFO2DIR(ctinfo),
195+
(int)rep_len - (int)match_len);
195196

196197
nf_conntrack_event_cache(IPCT_NATSEQADJ, ct);
197198
}
@@ -377,6 +378,7 @@ nf_nat_seq_adjust(struct sk_buff *skb,
377378
struct tcphdr *tcph;
378379
int dir;
379380
__be32 newseq, newack;
381+
s16 seqoff, ackoff;
380382
struct nf_conn_nat *nat = nfct_nat(ct);
381383
struct nf_nat_seq *this_way, *other_way;
382384

@@ -390,15 +392,18 @@ nf_nat_seq_adjust(struct sk_buff *skb,
390392

391393
tcph = (void *)skb->data + ip_hdrlen(skb);
392394
if (after(ntohl(tcph->seq), this_way->correction_pos))
393-
newseq = htonl(ntohl(tcph->seq) + this_way->offset_after);
395+
seqoff = this_way->offset_after;
394396
else
395-
newseq = htonl(ntohl(tcph->seq) + this_way->offset_before);
397+
seqoff = this_way->offset_before;
396398

397399
if (after(ntohl(tcph->ack_seq) - other_way->offset_before,
398400
other_way->correction_pos))
399-
newack = htonl(ntohl(tcph->ack_seq) - other_way->offset_after);
401+
ackoff = other_way->offset_after;
400402
else
401-
newack = htonl(ntohl(tcph->ack_seq) - other_way->offset_before);
403+
ackoff = other_way->offset_before;
404+
405+
newseq = htonl(ntohl(tcph->seq) + seqoff);
406+
newack = htonl(ntohl(tcph->ack_seq) - ackoff);
402407

403408
inet_proto_csum_replace4(&tcph->check, skb, tcph->seq, newseq, 0);
404409
inet_proto_csum_replace4(&tcph->check, skb, tcph->ack_seq, newack, 0);
@@ -413,7 +418,7 @@ nf_nat_seq_adjust(struct sk_buff *skb,
413418
if (!nf_nat_sack_adjust(skb, tcph, ct, ctinfo))
414419
return 0;
415420

416-
nf_conntrack_tcp_update(skb, ip_hdrlen(skb), ct, dir);
421+
nf_conntrack_tcp_update(skb, ip_hdrlen(skb), ct, dir, seqoff);
417422

418423
return 1;
419424
}

net/netfilter/nf_conntrack_proto_tcp.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -720,8 +720,8 @@ static bool tcp_in_window(const struct nf_conn *ct,
720720
/* Caller must linearize skb at tcp header. */
721721
void nf_conntrack_tcp_update(const struct sk_buff *skb,
722722
unsigned int dataoff,
723-
struct nf_conn *ct,
724-
int dir)
723+
struct nf_conn *ct, int dir,
724+
s16 offset)
725725
{
726726
const struct tcphdr *tcph = (const void *)skb->data + dataoff;
727727
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,
734734
/*
735735
* We have to worry for the ack in the reply packet only...
736736
*/
737-
if (after(end, ct->proto.tcp.seen[dir].td_end))
737+
if (ct->proto.tcp.seen[dir].td_end + offset == end)
738738
ct->proto.tcp.seen[dir].td_end = end;
739739
ct->proto.tcp.last_end = end;
740740
spin_unlock_bh(&ct->lock);

0 commit comments

Comments
 (0)