mirror of
https://kernel.googlesource.com/pub/scm/linux/kernel/git/stable/linux-stable.git
synced 2025-09-14 11:19:08 +10:00
icmp: fix icmp_ndo_send address translation for reply direction
The icmp_ndo_send function was originally introduced to ensure proper
rate limiting when icmp_send is called by a network device driver,
where the packet's source address may have already been transformed
by SNAT.
However, the original implementation only considers the
IP_CT_DIR_ORIGINAL direction for SNAT and always replaced the packet's
source address with that of the original-direction tuple. This causes
two problems:
1. For SNAT:
Reply-direction packets were incorrectly translated using the source
address of the CT original direction, even though no translation is
required.
2. For DNAT:
Reply-direction packets were not handled at all. In DNAT, the original
direction's destination is translated. Therefore, in the reply
direction the source address must be set to the reply-direction
source, so rate limiting works as intended.
Fix this by using the connection direction to select the correct tuple
for source address translation, and adjust the pre-checks to handle
reply-direction packets in case of DNAT.
Additionally, wrap the `ct->status` access in READ_ONCE(). This avoids
possible KCSAN reports about concurrent updates to `ct->status`.
Fixes: 0b41713b60
("icmp: introduce helper for nat'd source address in network device context")
Signed-off-by: Fabian Bläse <fabian@blaese.de>
Cc: Jason A. Donenfeld <Jason@zx2c4.com>
Reviewed-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
7000f4fa9b
commit
c6dd1aa2cb
@ -799,11 +799,12 @@ void icmp_ndo_send(struct sk_buff *skb_in, int type, int code, __be32 info)
|
||||
struct sk_buff *cloned_skb = NULL;
|
||||
struct ip_options opts = { 0 };
|
||||
enum ip_conntrack_info ctinfo;
|
||||
enum ip_conntrack_dir dir;
|
||||
struct nf_conn *ct;
|
||||
__be32 orig_ip;
|
||||
|
||||
ct = nf_ct_get(skb_in, &ctinfo);
|
||||
if (!ct || !(ct->status & IPS_SRC_NAT)) {
|
||||
if (!ct || !(READ_ONCE(ct->status) & IPS_NAT_MASK)) {
|
||||
__icmp_send(skb_in, type, code, info, &opts);
|
||||
return;
|
||||
}
|
||||
@ -818,7 +819,8 @@ void icmp_ndo_send(struct sk_buff *skb_in, int type, int code, __be32 info)
|
||||
goto out;
|
||||
|
||||
orig_ip = ip_hdr(skb_in)->saddr;
|
||||
ip_hdr(skb_in)->saddr = ct->tuplehash[0].tuple.src.u3.ip;
|
||||
dir = CTINFO2DIR(ctinfo);
|
||||
ip_hdr(skb_in)->saddr = ct->tuplehash[dir].tuple.src.u3.ip;
|
||||
__icmp_send(skb_in, type, code, info, &opts);
|
||||
ip_hdr(skb_in)->saddr = orig_ip;
|
||||
out:
|
||||
|
@ -54,11 +54,12 @@ void icmpv6_ndo_send(struct sk_buff *skb_in, u8 type, u8 code, __u32 info)
|
||||
struct inet6_skb_parm parm = { 0 };
|
||||
struct sk_buff *cloned_skb = NULL;
|
||||
enum ip_conntrack_info ctinfo;
|
||||
enum ip_conntrack_dir dir;
|
||||
struct in6_addr orig_ip;
|
||||
struct nf_conn *ct;
|
||||
|
||||
ct = nf_ct_get(skb_in, &ctinfo);
|
||||
if (!ct || !(ct->status & IPS_SRC_NAT)) {
|
||||
if (!ct || !(READ_ONCE(ct->status) & IPS_NAT_MASK)) {
|
||||
__icmpv6_send(skb_in, type, code, info, &parm);
|
||||
return;
|
||||
}
|
||||
@ -73,7 +74,8 @@ void icmpv6_ndo_send(struct sk_buff *skb_in, u8 type, u8 code, __u32 info)
|
||||
goto out;
|
||||
|
||||
orig_ip = ipv6_hdr(skb_in)->saddr;
|
||||
ipv6_hdr(skb_in)->saddr = ct->tuplehash[0].tuple.src.u3.in6;
|
||||
dir = CTINFO2DIR(ctinfo);
|
||||
ipv6_hdr(skb_in)->saddr = ct->tuplehash[dir].tuple.src.u3.in6;
|
||||
__icmpv6_send(skb_in, type, code, info, &parm);
|
||||
ipv6_hdr(skb_in)->saddr = orig_ip;
|
||||
out:
|
||||
|
Loading…
Reference in New Issue
Block a user