Skip to content

Commit 80abbe8

Browse files
Florian Westphalummakynes
authored andcommitted
netfilter: nat: fix ipv6 nat redirect with mapped and scoped addresses
The ipv6 redirect target was derived from the ipv4 one, i.e. its identical to a 'dnat' with the first (primary) address assigned to the network interface. The code has been moved around to make it usable from nf_tables too, but its still the same as it was back when this was added in 2012. IPv6, however, has different types of addresses, if the 'wrong' address comes first the redirection does not work. In Daniels case, the addresses are: inet6 ::ffff:192 ... inet6 2a01: ... ... so the function attempts to redirect to the mapped address. Add more checks before the address is deemed correct: 1. If the packets' daddr is scoped, search for a scoped address too 2. skip tentative addresses 3. skip mapped addresses Use the first address that appears to match our needs. Reported-by: Daniel Huhardeaux <tech@tootai.net> Closes: https://lore.kernel.org/netfilter/71be06b8-6aa0-4cf9-9e0b-e2839b01b22f@tootai.net/ Fixes: 115e23a ("netfilter: ip6tables: add REDIRECT target") Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
1 parent 7b308fe commit 80abbe8

File tree

1 file changed

+26
-1
lines changed

1 file changed

+26
-1
lines changed

net/netfilter/nf_nat_redirect.c

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,26 @@ EXPORT_SYMBOL_GPL(nf_nat_redirect_ipv4);
8080

8181
static const struct in6_addr loopback_addr = IN6ADDR_LOOPBACK_INIT;
8282

83+
static bool nf_nat_redirect_ipv6_usable(const struct inet6_ifaddr *ifa, unsigned int scope)
84+
{
85+
unsigned int ifa_addr_type = ipv6_addr_type(&ifa->addr);
86+
87+
if (ifa_addr_type & IPV6_ADDR_MAPPED)
88+
return false;
89+
90+
if ((ifa->flags & IFA_F_TENTATIVE) && (!(ifa->flags & IFA_F_OPTIMISTIC)))
91+
return false;
92+
93+
if (scope) {
94+
unsigned int ifa_scope = ifa_addr_type & IPV6_ADDR_SCOPE_MASK;
95+
96+
if (!(scope & ifa_scope))
97+
return false;
98+
}
99+
100+
return true;
101+
}
102+
83103
unsigned int
84104
nf_nat_redirect_ipv6(struct sk_buff *skb, const struct nf_nat_range2 *range,
85105
unsigned int hooknum)
@@ -89,14 +109,19 @@ nf_nat_redirect_ipv6(struct sk_buff *skb, const struct nf_nat_range2 *range,
89109
if (hooknum == NF_INET_LOCAL_OUT) {
90110
newdst.in6 = loopback_addr;
91111
} else {
112+
unsigned int scope = ipv6_addr_scope(&ipv6_hdr(skb)->daddr);
92113
struct inet6_dev *idev;
93-
struct inet6_ifaddr *ifa;
94114
bool addr = false;
95115

96116
idev = __in6_dev_get(skb->dev);
97117
if (idev != NULL) {
118+
const struct inet6_ifaddr *ifa;
119+
98120
read_lock_bh(&idev->lock);
99121
list_for_each_entry(ifa, &idev->addr_list, if_list) {
122+
if (!nf_nat_redirect_ipv6_usable(ifa, scope))
123+
continue;
124+
100125
newdst.in6 = ifa->addr;
101126
addr = true;
102127
break;

0 commit comments

Comments
 (0)