netfilter: nf_nat: fix NAT issue in 2.6.30.4+
Vitezslav Samel discovered that since 2.6.30.4+ active FTP can not work
over NAT. The "cause" of the problem was a fix of unacknowledged data
detection with NAT (commit a3a9f79e36
).
However, actually, that fix uncovered a long standing bug in TCP conntrack:
when NAT was enabled, we simply updated the max of the right edge of
the segments we have seen (td_end), by the offset NAT produced with
changing IP/port in the data. However, we did not update the other parameter
(td_maxend) which is affected by the NAT offset. Thus that could drift
away from the correct value and thus resulted breaking active FTP.
The patch below fixes the issue by *not* updating the conntrack parameters
from NAT, but instead taking into account the NAT offsets in conntrack in a
consistent way. (Updating from NAT would be more harder and expensive because
it'd need to re-calculate parameters we already calculated in conntrack.)
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>
Этот коммит содержится в:

коммит произвёл
David S. Miller

родитель
f5209b4446
Коммит
f9dd09c7f7
@@ -750,6 +750,8 @@ static int __init nf_nat_init(void)
|
||||
BUG_ON(nfnetlink_parse_nat_setup_hook != NULL);
|
||||
rcu_assign_pointer(nfnetlink_parse_nat_setup_hook,
|
||||
nfnetlink_parse_nat_setup);
|
||||
BUG_ON(nf_ct_nat_offset != NULL);
|
||||
rcu_assign_pointer(nf_ct_nat_offset, nf_nat_get_offset);
|
||||
return 0;
|
||||
|
||||
cleanup_extend:
|
||||
@@ -764,6 +766,7 @@ static void __exit nf_nat_cleanup(void)
|
||||
nf_ct_extend_unregister(&nat_extend);
|
||||
rcu_assign_pointer(nf_nat_seq_adjust_hook, NULL);
|
||||
rcu_assign_pointer(nfnetlink_parse_nat_setup_hook, NULL);
|
||||
rcu_assign_pointer(nf_ct_nat_offset, NULL);
|
||||
synchronize_net();
|
||||
}
|
||||
|
||||
|
@@ -73,6 +73,28 @@ adjust_tcp_sequence(u32 seq,
|
||||
DUMP_OFFSET(this_way);
|
||||
}
|
||||
|
||||
/* Get the offset value, for conntrack */
|
||||
s16 nf_nat_get_offset(const struct nf_conn *ct,
|
||||
enum ip_conntrack_dir dir,
|
||||
u32 seq)
|
||||
{
|
||||
struct nf_conn_nat *nat = nfct_nat(ct);
|
||||
struct nf_nat_seq *this_way;
|
||||
s16 offset;
|
||||
|
||||
if (!nat)
|
||||
return 0;
|
||||
|
||||
this_way = &nat->seq[dir];
|
||||
spin_lock_bh(&nf_nat_seqofs_lock);
|
||||
offset = after(seq, this_way->correction_pos)
|
||||
? this_way->offset_after : this_way->offset_before;
|
||||
spin_unlock_bh(&nf_nat_seqofs_lock);
|
||||
|
||||
return offset;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_nat_get_offset);
|
||||
|
||||
/* Frobs data inside this packet, which is linear. */
|
||||
static void mangle_contents(struct sk_buff *skb,
|
||||
unsigned int dataoff,
|
||||
@@ -189,11 +211,6 @@ nf_nat_mangle_tcp_packet(struct sk_buff *skb,
|
||||
adjust_tcp_sequence(ntohl(tcph->seq),
|
||||
(int)rep_len - (int)match_len,
|
||||
ct, ctinfo);
|
||||
/* Tell TCP window tracking about seq change */
|
||||
nf_conntrack_tcp_update(skb, ip_hdrlen(skb),
|
||||
ct, CTINFO2DIR(ctinfo),
|
||||
(int)rep_len - (int)match_len);
|
||||
|
||||
nf_conntrack_event_cache(IPCT_NATSEQADJ, ct);
|
||||
}
|
||||
return 1;
|
||||
@@ -415,12 +432,7 @@ nf_nat_seq_adjust(struct sk_buff *skb,
|
||||
tcph->seq = newseq;
|
||||
tcph->ack_seq = newack;
|
||||
|
||||
if (!nf_nat_sack_adjust(skb, tcph, ct, ctinfo))
|
||||
return 0;
|
||||
|
||||
nf_conntrack_tcp_update(skb, ip_hdrlen(skb), ct, dir, seqoff);
|
||||
|
||||
return 1;
|
||||
return nf_nat_sack_adjust(skb, tcph, ct, ctinfo);
|
||||
}
|
||||
|
||||
/* Setup NAT on this expected conntrack so it follows master. */
|
||||
|
Ссылка в новой задаче
Block a user