Commit a3a9f79e361e864f0e9d75ebe2a0cb43d17c4272

Authored by Patrick McHardy
1 parent 308ff823eb

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>

Showing 3 changed files with 16 additions and 11 deletions Side-by-side Diff

include/net/netfilter/nf_conntrack.h
... ... @@ -258,8 +258,8 @@
258 258 /* Update TCP window tracking data when NAT mangles the packet */
259 259 extern void nf_conntrack_tcp_update(const struct sk_buff *skb,
260 260 unsigned int dataoff,
261   - struct nf_conn *ct,
262   - int dir);
  261 + struct nf_conn *ct, int dir,
  262 + s16 offset);
263 263  
264 264 /* Fake conntrack entry for untracked connections */
265 265 extern struct nf_conn nf_conntrack_untracked;
net/ipv4/netfilter/nf_nat_helper.c
... ... @@ -191,7 +191,8 @@
191 191 ct, ctinfo);
192 192 /* Tell TCP window tracking about seq change */
193 193 nf_conntrack_tcp_update(skb, ip_hdrlen(skb),
194   - ct, CTINFO2DIR(ctinfo));
  194 + ct, CTINFO2DIR(ctinfo),
  195 + (int)rep_len - (int)match_len);
195 196  
196 197 nf_conntrack_event_cache(IPCT_NATSEQADJ, ct);
197 198 }
... ... @@ -377,6 +378,7 @@
377 378 struct tcphdr *tcph;
378 379 int dir;
379 380 __be32 newseq, newack;
  381 + s16 seqoff, ackoff;
380 382 struct nf_conn_nat *nat = nfct_nat(ct);
381 383 struct nf_nat_seq *this_way, *other_way;
382 384  
383 385  
384 386  
385 387  
386 388  
... ... @@ -390,16 +392,19 @@
390 392  
391 393 tcph = (void *)skb->data + ip_hdrlen(skb);
392 394 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;
394 396 else
395   - newseq = htonl(ntohl(tcph->seq) + this_way->offset_before);
  397 + seqoff = this_way->offset_before;
396 398  
397 399 if (after(ntohl(tcph->ack_seq) - other_way->offset_before,
398 400 other_way->correction_pos))
399   - newack = htonl(ntohl(tcph->ack_seq) - other_way->offset_after);
  401 + ackoff = other_way->offset_after;
400 402 else
401   - newack = htonl(ntohl(tcph->ack_seq) - other_way->offset_before);
  403 + ackoff = other_way->offset_before;
402 404  
  405 + newseq = htonl(ntohl(tcph->seq) + seqoff);
  406 + newack = htonl(ntohl(tcph->ack_seq) - ackoff);
  407 +
403 408 inet_proto_csum_replace4(&tcph->check, skb, tcph->seq, newseq, 0);
404 409 inet_proto_csum_replace4(&tcph->check, skb, tcph->ack_seq, newack, 0);
405 410  
... ... @@ -413,7 +418,7 @@
413 418 if (!nf_nat_sack_adjust(skb, tcph, ct, ctinfo))
414 419 return 0;
415 420  
416   - nf_conntrack_tcp_update(skb, ip_hdrlen(skb), ct, dir);
  421 + nf_conntrack_tcp_update(skb, ip_hdrlen(skb), ct, dir, seqoff);
417 422  
418 423 return 1;
419 424 }
net/netfilter/nf_conntrack_proto_tcp.c
... ... @@ -720,8 +720,8 @@
720 720 /* Caller must linearize skb at tcp header. */
721 721 void nf_conntrack_tcp_update(const struct sk_buff *skb,
722 722 unsigned int dataoff,
723   - struct nf_conn *ct,
724   - int dir)
  723 + struct nf_conn *ct, int dir,
  724 + s16 offset)
725 725 {
726 726 const struct tcphdr *tcph = (const void *)skb->data + dataoff;
727 727 const struct ip_ct_tcp_state *sender = &ct->proto.tcp.seen[dir];
... ... @@ -734,7 +734,7 @@
734 734 /*
735 735 * We have to worry for the ack in the reply packet only...
736 736 */
737   - if (after(end, ct->proto.tcp.seen[dir].td_end))
  737 + if (ct->proto.tcp.seen[dir].td_end + offset == end)
738 738 ct->proto.tcp.seen[dir].td_end = end;
739 739 ct->proto.tcp.last_end = end;
740 740 spin_unlock_bh(&ct->lock);