Skip to content

Commit 9c6621b

Browse files
author
CKI Backport Bot
committed
netfilter: nf_tables: nft_fib: consistent l3mdev handling
JIRA: https://issues.redhat.com/browse/RHEL-88574 commit 9a11966 Author: Florian Westphal <fw@strlen.de> Date: Wed May 21 11:38:48 2025 +0200 netfilter: nf_tables: nft_fib: consistent l3mdev handling fib has two modes: 1. Obtain output device according to source or destination address 2. Obtain the type of the address, e.g. local, unicast, multicast. 'fib daddr type' should return 'local' if the address is configured in this netns or unicast otherwise. 'fib daddr . iif type' should return 'local' if the address is configured on the input interface or unicast otherwise, i.e. more restrictive. However, if the interface is part of a VRF, then 'fib daddr type' returns unicast even if the address is configured on the incoming interface. This is broken for both ipv4 and ipv6. In the ipv4 case, inet_dev_addr_type must only be used if the 'iif' or 'oif' (strict mode) was requested. Else inet_addr_type_dev_table() needs to be used and the correct dev argument must be passed as well so the correct fib (vrf) table is used. In the ipv6 case, the bug is similar, without strict mode, dev is NULL so .flowi6_l3mdev will be set to 0. Add a new 'nft_fib_l3mdev_master_ifindex_rcu()' helper and use that to init the .l3mdev structure member. For ipv6, use it from nft_fib6_flowi_init() which gets called from both the 'type' and the 'route' mode eval functions. This provides consistent behaviour for all modes for both ipv4 and ipv6: If strict matching is requested, the input respectively output device of the netfilter hooks is used. Otherwise, use skb->dev to obtain the l3mdev ifindex. Without this, most type checks in updated nft_fib.sh selftest fail: FAIL: did not find veth0 . 10.9.9.1 . local in fibtype4 FAIL: did not find veth0 . dead:1::1 . local in fibtype6 FAIL: did not find veth0 . dead:9::1 . local in fibtype6 FAIL: did not find tvrf . 10.0.1.1 . local in fibtype4 FAIL: did not find tvrf . 10.9.9.1 . local in fibtype4 FAIL: did not find tvrf . dead:1::1 . local in fibtype6 FAIL: did not find tvrf . dead:9::1 . local in fibtype6 FAIL: fib expression address types match (iif in vrf) (fib errounously returns 'unicast' for all of them, even though all of these addresses are local to the vrf). Fixes: f6d0cbc ("netfilter: nf_tables: add fib expression") Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> Signed-off-by: CKI Backport Bot <cki-ci-bot+cki-gitlab-backport-bot@redhat.com>
1 parent 997ebfd commit 9c6621b

File tree

3 files changed

+19
-5
lines changed

3 files changed

+19
-5
lines changed

include/net/netfilter/nft_fib.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#ifndef _NFT_FIB_H_
33
#define _NFT_FIB_H_
44

5+
#include <net/l3mdev.h>
56
#include <net/netfilter/nf_tables.h>
67

78
struct nft_fib {
@@ -39,6 +40,14 @@ static inline bool nft_fib_can_skip(const struct nft_pktinfo *pkt)
3940
return nft_fib_is_loopback(pkt->skb, indev);
4041
}
4142

43+
static inline int nft_fib_l3mdev_master_ifindex_rcu(const struct nft_pktinfo *pkt,
44+
const struct net_device *iif)
45+
{
46+
const struct net_device *dev = iif ? iif : pkt->skb->dev;
47+
48+
return l3mdev_master_ifindex_rcu(dev);
49+
}
50+
4251
int nft_fib_dump(struct sk_buff *skb, const struct nft_expr *expr, bool reset);
4352
int nft_fib_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
4453
const struct nlattr * const tb[]);

net/ipv4/netfilter/nft_fib_ipv4.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,12 @@ void nft_fib4_eval_type(const struct nft_expr *expr, struct nft_regs *regs,
4949
else
5050
addr = iph->saddr;
5151

52-
*dst = inet_dev_addr_type(nft_net(pkt), dev, addr);
52+
if (priv->flags & (NFTA_FIB_F_IIF | NFTA_FIB_F_OIF)) {
53+
*dst = inet_dev_addr_type(nft_net(pkt), dev, addr);
54+
return;
55+
}
56+
57+
*dst = inet_addr_type_dev_table(nft_net(pkt), pkt->skb->dev, addr);
5358
}
5459
EXPORT_SYMBOL_GPL(nft_fib4_eval_type);
5560

@@ -64,8 +69,8 @@ void nft_fib4_eval(const struct nft_expr *expr, struct nft_regs *regs,
6469
struct flowi4 fl4 = {
6570
.flowi4_scope = RT_SCOPE_UNIVERSE,
6671
.flowi4_iif = LOOPBACK_IFINDEX,
72+
.flowi4_proto = pkt->tprot,
6773
.flowi4_uid = sock_net_uid(nft_net(pkt), NULL),
68-
.flowi4_l3mdev = l3mdev_master_ifindex_rcu(nft_in(pkt)),
6974
};
7075
const struct net_device *oif;
7176
const struct net_device *found;
@@ -89,6 +94,8 @@ void nft_fib4_eval(const struct nft_expr *expr, struct nft_regs *regs,
8994
else
9095
oif = NULL;
9196

97+
fl4.flowi4_l3mdev = nft_fib_l3mdev_master_ifindex_rcu(pkt, oif);
98+
9299
iph = skb_header_pointer(pkt->skb, noff, sizeof(_iph), &_iph);
93100
if (!iph) {
94101
regs->verdict.code = NFT_BREAK;

net/ipv6/netfilter/nft_fib_ipv6.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ static int nft_fib6_flowi_init(struct flowi6 *fl6, const struct nft_fib *priv,
5050
fl6->flowi6_mark = pkt->skb->mark;
5151

5252
fl6->flowlabel = (*(__be32 *)iph) & IPV6_FLOWINFO_MASK;
53+
fl6->flowi6_l3mdev = nft_fib_l3mdev_master_ifindex_rcu(pkt, dev);
5354

5455
return lookup_flags;
5556
}
@@ -73,8 +74,6 @@ static u32 __nft_fib6_eval_type(const struct nft_fib *priv,
7374
else if (priv->flags & NFTA_FIB_F_OIF)
7475
dev = nft_out(pkt);
7576

76-
fl6.flowi6_l3mdev = l3mdev_master_ifindex_rcu(dev);
77-
7877
nft_fib6_flowi_init(&fl6, priv, pkt, dev, iph);
7978

8079
if (dev && nf_ipv6_chk_addr(nft_net(pkt), &fl6.daddr, dev, true))
@@ -166,7 +165,6 @@ void nft_fib6_eval(const struct nft_expr *expr, struct nft_regs *regs,
166165
.flowi6_iif = LOOPBACK_IFINDEX,
167166
.flowi6_proto = pkt->tprot,
168167
.flowi6_uid = sock_net_uid(nft_net(pkt), NULL),
169-
.flowi6_l3mdev = l3mdev_master_ifindex_rcu(nft_in(pkt)),
170168
};
171169
struct rt6_info *rt;
172170
int lookup_flags;

0 commit comments

Comments
 (0)