* [PATCH net-next 1/9] vxlan: initialize _md in vxlan_xmit_one()
2026-09-03 12:08 [PATCH net-next 0/9] vxlan: convert configuration to RCU and drop RTNL in vxlan_fill_info() Eric Dumazet
@ 2026-09-03 12:08 ` Eric Dumazet
2026-09-05 3:42 ` Kuniyuki Iwashima
2026-09-03 12:08 ` [PATCH net-next 2/9] vxlan: vnifilter: free vxlan_vni_group via RCU in vxlan_vnigroup_uninit() Eric Dumazet
` (7 subsequent siblings)
8 siblings, 1 reply; 28+ messages in thread
From: Eric Dumazet @ 2026-09-03 12:08 UTC (permalink / raw)
To: David S . Miller, Jakub Kicinski, Paolo Abeni
Cc: Simon Horman, Kuniyuki Iwashima, Ido Schimmel, Andrew Lunn,
netdev, eric.dumazet, Eric Dumazet
If a VXLAN device is configured with both VXLAN_F_COLLECT_METADATA and
VXLAN_F_GBP, and a packet is transmitted through it using an external
ip_tunnel_info that lacks the IP_TUNNEL_VXLAN_OPT_BIT flag, md is left
pointing to the uninitialized _md stack variable:
if (test_bit(IP_TUNNEL_VXLAN_OPT_BIT, info->key.tun_flags)) {
if (info->options_len < sizeof(*md))
goto drop;
md = ip_tunnel_info_opts(info);
}
Because IP_TUNNEL_VXLAN_OPT_BIT is not set, md is not updated and remains
pointing to _md. Later, vxlan_build_skb() is called with md, which
eventually calls vxlan_build_gbp_hdr():
if (vxflags & VXLAN_F_GBP)
vxlan_build_gbp_hdr(vxh, md);
Inside vxlan_build_gbp_hdr(), md->gbp is read:
if (!md->gbp)
return;
gbp = (struct vxlanhdr_gbp *)vxh;
...
if (md->gbp & VXLAN_GBP_DONT_LEARN)
gbp->dont_learn = 1;
If the stack contains garbage, this causes:
1) VXLAN_HF_GBP flag to be spuriously set in the VXLAN header.
2) gbp->dont_learn and gbp->policy_applied to be set from stack bits.
3) gbp->policy_id to receive 16 bits of uninitialized kernel stack data,
leaking it onto the wire.
Fix this by zero-initializing _md. If IP_TUNNEL_VXLAN_OPT_BIT is not
present, md->gbp remains 0, and vxlan_build_gbp_hdr() returns early
without modifying the VXLAN header.
Fixes: ee122c79d422 ("vxlan: Flow based tunneling")
Signed-off-by: Eric Dumazet <edumazet@google.com>
---
drivers/net/vxlan/vxlan_core.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c
index 459f19f7071e5bafe9e4c57ef8819645c3da7121..6d886b6f2dc1d62f0eb26c9ba310c4feaed9cafd 100644
--- a/drivers/net/vxlan/vxlan_core.c
+++ b/drivers/net/vxlan/vxlan_core.c
@@ -2362,7 +2362,7 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
struct ip_tunnel_key key;
struct vxlan_dev *vxlan = netdev_priv(dev);
const struct iphdr *old_iph;
- struct vxlan_metadata _md;
+ struct vxlan_metadata _md = {};
struct vxlan_metadata *md = &_md;
unsigned int pkt_len = skb->len;
__be16 src_port = 0, dst_port;
--
2.55.0.970.g62bdec98f9-goog
^ permalink raw reply related [flat|nested] 28+ messages in thread* Re: [PATCH net-next 1/9] vxlan: initialize _md in vxlan_xmit_one()
2026-09-03 12:08 ` [PATCH net-next 1/9] vxlan: initialize _md in vxlan_xmit_one() Eric Dumazet
@ 2026-09-05 3:42 ` Kuniyuki Iwashima
0 siblings, 0 replies; 28+ messages in thread
From: Kuniyuki Iwashima @ 2026-09-05 3:42 UTC (permalink / raw)
To: Eric Dumazet
Cc: David S . Miller, Jakub Kicinski, Paolo Abeni, Simon Horman,
Ido Schimmel, Andrew Lunn, netdev, eric.dumazet
On Thu, Sep 3, 2026 at 5:08 AM Eric Dumazet <edumazet@google.com> wrote:
>
> If a VXLAN device is configured with both VXLAN_F_COLLECT_METADATA and
> VXLAN_F_GBP, and a packet is transmitted through it using an external
> ip_tunnel_info that lacks the IP_TUNNEL_VXLAN_OPT_BIT flag, md is left
> pointing to the uninitialized _md stack variable:
>
> if (test_bit(IP_TUNNEL_VXLAN_OPT_BIT, info->key.tun_flags)) {
> if (info->options_len < sizeof(*md))
> goto drop;
> md = ip_tunnel_info_opts(info);
> }
>
> Because IP_TUNNEL_VXLAN_OPT_BIT is not set, md is not updated and remains
> pointing to _md. Later, vxlan_build_skb() is called with md, which
> eventually calls vxlan_build_gbp_hdr():
>
> if (vxflags & VXLAN_F_GBP)
> vxlan_build_gbp_hdr(vxh, md);
>
> Inside vxlan_build_gbp_hdr(), md->gbp is read:
>
> if (!md->gbp)
> return;
> gbp = (struct vxlanhdr_gbp *)vxh;
> ...
> if (md->gbp & VXLAN_GBP_DONT_LEARN)
> gbp->dont_learn = 1;
>
> If the stack contains garbage, this causes:
> 1) VXLAN_HF_GBP flag to be spuriously set in the VXLAN header.
> 2) gbp->dont_learn and gbp->policy_applied to be set from stack bits.
> 3) gbp->policy_id to receive 16 bits of uninitialized kernel stack data,
> leaking it onto the wire.
>
> Fix this by zero-initializing _md. If IP_TUNNEL_VXLAN_OPT_BIT is not
> present, md->gbp remains 0, and vxlan_build_gbp_hdr() returns early
> without modifying the VXLAN header.
>
> Fixes: ee122c79d422 ("vxlan: Flow based tunneling")
> Signed-off-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com>
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH net-next 2/9] vxlan: vnifilter: free vxlan_vni_group via RCU in vxlan_vnigroup_uninit()
2026-09-03 12:08 [PATCH net-next 0/9] vxlan: convert configuration to RCU and drop RTNL in vxlan_fill_info() Eric Dumazet
2026-09-03 12:08 ` [PATCH net-next 1/9] vxlan: initialize _md in vxlan_xmit_one() Eric Dumazet
@ 2026-09-03 12:08 ` Eric Dumazet
2026-09-05 4:51 ` Kuniyuki Iwashima
2026-09-07 6:11 ` netdev-bot+sashiko
2026-09-03 12:08 ` [PATCH net-next 3/9] vxlan: vnifilter: use list_for_each_entry_rcu() in vxlan_vnifilter_dump_dev() Eric Dumazet
` (6 subsequent siblings)
8 siblings, 2 replies; 28+ messages in thread
From: Eric Dumazet @ 2026-09-03 12:08 UTC (permalink / raw)
To: David S . Miller, Jakub Kicinski, Paolo Abeni
Cc: Simon Horman, Kuniyuki Iwashima, Ido Schimmel, Andrew Lunn,
netdev, eric.dumazet, Eric Dumazet
vxlan->vnigrp is an RCU-protected pointer accessed locklessly under
rcu_read_lock() in vxlan_vnifilter_dump_dev().
Currently, vxlan_vnigroup_uninit() frees struct vxlan_vni_group
synchronously via kfree(vg). If a VXLAN device is deleted concurrently
with an RTM_GETTUNNEL dump, vxlan_vnifilter_dump_dev() can suffer a
use-after-free when reading vg->num_vnis or walking vg->vni_list.
Fix this by clearing vxlan->vnigrp with rcu_assign_pointer() and freeing
vg after an RCU grace period using kfree_rcu().
Fixes: f9c4bb0b245c ("vxlan: vni filtering support on collect metadata device")
Signed-off-by: Eric Dumazet <edumazet@google.com>
---
drivers/net/vxlan/vxlan_vnifilter.c | 3 ++-
include/net/vxlan.h | 1 +
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/net/vxlan/vxlan_vnifilter.c b/drivers/net/vxlan/vxlan_vnifilter.c
index dd94085e088656d27b62420a5c8c95c609510a4c..ddfa24ad16f9303d7796e4a199b16dd5cc62047c 100644
--- a/drivers/net/vxlan/vxlan_vnifilter.c
+++ b/drivers/net/vxlan/vxlan_vnifilter.c
@@ -902,6 +902,7 @@ void vxlan_vnigroup_uninit(struct vxlan_dev *vxlan)
struct vxlan_vni_group *vg;
vg = rtnl_dereference(vxlan->vnigrp);
+ rcu_assign_pointer(vxlan->vnigrp, NULL);
list_for_each_entry_safe(v, tmp, &vg->vni_list, vlist) {
rhashtable_remove_fast(&vg->vni_hash, &v->vnode,
vxlan_vni_rht_params);
@@ -914,7 +915,7 @@ void vxlan_vnigroup_uninit(struct vxlan_dev *vxlan)
call_rcu(&v->rcu, vxlan_vni_node_rcu_free);
}
rhashtable_destroy(&vg->vni_hash);
- kfree(vg);
+ kfree_rcu(vg, rcu);
}
int vxlan_vnigroup_init(struct vxlan_dev *vxlan)
diff --git a/include/net/vxlan.h b/include/net/vxlan.h
index 7b82075055237058d231d636698f640c75c521af..f41db72e9229d9cc8460ddbe265c6cd07890032d 100644
--- a/include/net/vxlan.h
+++ b/include/net/vxlan.h
@@ -279,6 +279,7 @@ struct vxlan_vni_group {
struct rhashtable vni_hash;
struct list_head vni_list;
u32 num_vnis;
+ struct rcu_head rcu;
};
/* Pseudo network device */
--
2.55.0.970.g62bdec98f9-goog
^ permalink raw reply related [flat|nested] 28+ messages in thread* Re: [PATCH net-next 2/9] vxlan: vnifilter: free vxlan_vni_group via RCU in vxlan_vnigroup_uninit()
2026-09-03 12:08 ` [PATCH net-next 2/9] vxlan: vnifilter: free vxlan_vni_group via RCU in vxlan_vnigroup_uninit() Eric Dumazet
@ 2026-09-05 4:51 ` Kuniyuki Iwashima
2026-09-06 15:40 ` Eric Dumazet
2026-09-07 6:11 ` netdev-bot+sashiko
1 sibling, 1 reply; 28+ messages in thread
From: Kuniyuki Iwashima @ 2026-09-05 4:51 UTC (permalink / raw)
To: Eric Dumazet
Cc: David S . Miller, Jakub Kicinski, Paolo Abeni, Simon Horman,
Ido Schimmel, Andrew Lunn, netdev, eric.dumazet
On Thu, Sep 3, 2026 at 5:08 AM Eric Dumazet <edumazet@google.com> wrote:
>
> vxlan->vnigrp is an RCU-protected pointer accessed locklessly under
> rcu_read_lock() in vxlan_vnifilter_dump_dev().
>
> Currently, vxlan_vnigroup_uninit() frees struct vxlan_vni_group
> synchronously via kfree(vg). If a VXLAN device is deleted concurrently
> with an RTM_GETTUNNEL dump, vxlan_vnifilter_dump_dev() can suffer a
> use-after-free when reading vg->num_vnis or walking vg->vni_list.
In unregister_netdevice_many_notify(), does synchronize_net() after
unlist_netdevice() wait for the reader to complete before ->ndo_uninit() ?
>
> Fix this by clearing vxlan->vnigrp with rcu_assign_pointer() and freeing
> vg after an RCU grace period using kfree_rcu().
>
> Fixes: f9c4bb0b245c ("vxlan: vni filtering support on collect metadata device")
> Signed-off-by: Eric Dumazet <edumazet@google.com>
> ---
> drivers/net/vxlan/vxlan_vnifilter.c | 3 ++-
> include/net/vxlan.h | 1 +
> 2 files changed, 3 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/net/vxlan/vxlan_vnifilter.c b/drivers/net/vxlan/vxlan_vnifilter.c
> index dd94085e088656d27b62420a5c8c95c609510a4c..ddfa24ad16f9303d7796e4a199b16dd5cc62047c 100644
> --- a/drivers/net/vxlan/vxlan_vnifilter.c
> +++ b/drivers/net/vxlan/vxlan_vnifilter.c
> @@ -902,6 +902,7 @@ void vxlan_vnigroup_uninit(struct vxlan_dev *vxlan)
> struct vxlan_vni_group *vg;
>
> vg = rtnl_dereference(vxlan->vnigrp);
> + rcu_assign_pointer(vxlan->vnigrp, NULL);
> list_for_each_entry_safe(v, tmp, &vg->vni_list, vlist) {
> rhashtable_remove_fast(&vg->vni_hash, &v->vnode,
> vxlan_vni_rht_params);
> @@ -914,7 +915,7 @@ void vxlan_vnigroup_uninit(struct vxlan_dev *vxlan)
> call_rcu(&v->rcu, vxlan_vni_node_rcu_free);
> }
> rhashtable_destroy(&vg->vni_hash);
> - kfree(vg);
> + kfree_rcu(vg, rcu);
> }
>
> int vxlan_vnigroup_init(struct vxlan_dev *vxlan)
> diff --git a/include/net/vxlan.h b/include/net/vxlan.h
> index 7b82075055237058d231d636698f640c75c521af..f41db72e9229d9cc8460ddbe265c6cd07890032d 100644
> --- a/include/net/vxlan.h
> +++ b/include/net/vxlan.h
> @@ -279,6 +279,7 @@ struct vxlan_vni_group {
> struct rhashtable vni_hash;
> struct list_head vni_list;
> u32 num_vnis;
> + struct rcu_head rcu;
> };
>
> /* Pseudo network device */
> --
> 2.55.0.970.g62bdec98f9-goog
>
^ permalink raw reply [flat|nested] 28+ messages in thread* Re: [PATCH net-next 2/9] vxlan: vnifilter: free vxlan_vni_group via RCU in vxlan_vnigroup_uninit()
2026-09-05 4:51 ` Kuniyuki Iwashima
@ 2026-09-06 15:40 ` Eric Dumazet
0 siblings, 0 replies; 28+ messages in thread
From: Eric Dumazet @ 2026-09-06 15:40 UTC (permalink / raw)
To: Kuniyuki Iwashima
Cc: David S . Miller, Jakub Kicinski, Paolo Abeni, Simon Horman,
Ido Schimmel, Andrew Lunn, netdev, eric.dumazet
On Sat, Sep 5, 2026 at 6:52 AM Kuniyuki Iwashima <kuniyu@google.com> wrote:
>
> On Thu, Sep 3, 2026 at 5:08 AM Eric Dumazet <edumazet@google.com> wrote:
> >
> > vxlan->vnigrp is an RCU-protected pointer accessed locklessly under
> > rcu_read_lock() in vxlan_vnifilter_dump_dev().
> >
> > Currently, vxlan_vnigroup_uninit() frees struct vxlan_vni_group
> > synchronously via kfree(vg). If a VXLAN device is deleted concurrently
> > with an RTM_GETTUNNEL dump, vxlan_vnifilter_dump_dev() can suffer a
> > use-after-free when reading vg->num_vnis or walking vg->vni_list.
>
> In unregister_netdevice_many_notify(), does synchronize_net() after
> unlist_netdevice() wait for the reader to complete before ->ndo_uninit() ?
>
Good point. I added this patch after sashiko had a false positive:
<quote>
@@ -2979,10 +3007,13 @@ static int vxlan_init(struct net_device *dev)
> static void vxlan_uninit(struct net_device *dev)
> {
> struct vxlan_dev *vxlan = netdev_priv(dev);
> + const struct vxlan_config *cfg;
> +
> + cfg = rtnl_dereference(vxlan->cfg);
>
> vxlan_mdb_fini(vxlan);
>
> - if (vxlan->cfg.flags & VXLAN_F_VNIFILTER)
> + if (cfg && (cfg->flags & VXLAN_F_VNIFILTER))
> vxlan_vnigroup_uninit(vxlan);
This is a pre-existing issue, but does this synchronous cleanup cause a
use-after-free for concurrent RCU readers?
Looking at vxlan_vnigroup_uninit() in drivers/net/vxlan/vxlan_vnifilter.c
around line 925, it destroys and frees the RCU-protected vxlan_vni_group
synchronously:
drivers/net/vxlan/vxlan_vnifilter.c:vxlan_vnigroup_uninit() {
...
rhashtable_destroy(&vg->vni_hash);
kfree(vg);
}
Concurrently, a netlink dump for RTM_GETTUNNEL could call
vxlan_vnifilter_dump_dev(), which retrieves the device under RCU and
accesses vxlan->vnigrp via rcu_dereference(). If the vxlan device is
deleted at the same time, unregister_netdevice_many() calls ndo_uninit() ->
vxlan_uninit() -> vxlan_vnigroup_uninit(), which frees vg without waiting
for an RCU grace period.
Could this lead to memory corruption, and should it be using kfree_rcu()
or synchronize_rcu() instead?
</quote>
Sashiko saw vxlan->vnigrp annotated as __rcu and read under
rcu_read_lock() in vxlan_vnifilter_dump_dev(),
and assumed that because vxlan_vnigroup_uninit() directly does
kfree(vg) without an RCU grace period,
a concurrent netlink dump could hit a UAF.
However, Sashiko missed the netdevice unregistration sequence in
net/core/dev.c: unlist_netdevice(dev)
unlinks the device from lookup structures and synchronize_net() waits
for all active readers to complete
before dev->netdev_ops->ndo_uninit() is ever called.
I will drop this patch from V2 then.
Thanks.
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH net-next 2/9] vxlan: vnifilter: free vxlan_vni_group via RCU in vxlan_vnigroup_uninit()
2026-09-03 12:08 ` [PATCH net-next 2/9] vxlan: vnifilter: free vxlan_vni_group via RCU in vxlan_vnigroup_uninit() Eric Dumazet
2026-09-05 4:51 ` Kuniyuki Iwashima
@ 2026-09-07 6:11 ` netdev-bot+sashiko
2026-09-07 6:33 ` Eric Dumazet
1 sibling, 1 reply; 28+ messages in thread
From: netdev-bot+sashiko @ 2026-09-07 6:11 UTC (permalink / raw)
To: edumazet
Cc: davem, kuba, pabeni, horms, kuniyu, idosch, andrew+netdev, netdev,
eric.dumazet
Thank you for your contribution! Sashiko AI review found 1 potential
issue(s) to consider:
Critical: 0 · High: 0 · Medium: 1 · Low: 0
- [Medium] Changelog premise is not supported by the code: commit "vxlan:
vnifilter: free vxlan_vni_group via RCU in vxlan_vnigroup_uninit()"…
--
vxlan: vnifilter: free vxlan_vni_group via RCU in vxlan_vnigroup_uninit()
This clears vxlan->vnigrp with rcu_assign_pointer() in
vxlan_vnigroup_uninit() and defers the freeing of struct vxlan_vni_group
to kfree_rcu(), adding an rcu_head to the structure. It is described as
fixing a use-after-free between device deletion and an RTM_GETTUNNEL dump.
> vxlan->vnigrp is an RCU-protected pointer accessed locklessly under
> rcu_read_lock() in vxlan_vnifilter_dump_dev().
>
> Currently, vxlan_vnigroup_uninit() frees struct vxlan_vni_group
> synchronously via kfree(vg). If a VXLAN device is deleted concurrently
> with an RTM_GETTUNNEL dump, vxlan_vnifilter_dump_dev() can suffer a
> use-after-free when reading vg->num_vnis or walking vg->vni_list.
>
> Fixes: f9c4bb0b245c ("vxlan: vni filtering support on collect metadata device")
[Severity: Medium]
Is the described race reachable in this tree? The RTM_GETTUNNEL dump is
registered without RTNL_FLAG_DUMP_UNLOCKED:
drivers/net/vxlan/vxlan_vnifilter.c:
static const struct rtnl_msg_handler vxlan_vnifilter_rtnl_msg_handlers[] = {
{THIS_MODULE, PF_BRIDGE, RTM_GETTUNNEL, NULL, vxlan_vnifilter_dump, 0},
With flags == 0, rtnl_dumpit() wraps the whole callback in RTNL:
net/core/rtnetlink.c:rtnl_dumpit() {
const bool needs_lock = !(cb->flags & RTNL_FLAG_DUMP_UNLOCKED);
...
if (needs_lock)
rtnl_lock();
err = dumpit(skb, cb);
if (needs_lock)
rtnl_unlock();
}
so vxlan_vnifilter_dump() and vxlan_vnifilter_dump_dev() run with RTNL held,
not "locklessly" as the changelog states.
On the freeing side, vxlan_vnigroup_uninit() is reached only from the
vxlan_init() error path and from vxlan_uninit() (ndo_uninit), both of which
run under RTNL via register_netdevice() and
unregister_netdevice_many_notify(). Doesn't that make the dump and the free
mutually exclusive today?
The dump also does not cache a device or vnigrp pointer across paginated
recvmsg iterations: it stores only an ifindex in cb->args[0] and re-resolves
through dev_get_by_index_rcu()/for_each_netdev_rcu(), so a stale vnigrp does
not appear to be observable between iterations either.
The code change itself looks harmless, since the remaining readers already
tolerate a NULL vnigrp:
drivers/net/vxlan/vxlan_vnifilter.c:vxlan_vnifilter_dump_dev() {
/* RCU needed because of the vni locking rules (rcu || rtnl) */
vg = rcu_dereference(vxlan->vnigrp);
if (!vg || !vg->num_vnis)
return 0;
and vxlan_vnifilter_lookup() in drivers/net/vxlan/vxlan_private.h does the
same NULL check.
Would it be more accurate to describe this as preparation for an
RTNL-unlocked RTM_GETTUNNEL dump, and to drop the Fixes: tag (or justify it)
so this is not backported to stable as a use-after-free fix?
The next patch in the series, "vxlan: vnifilter: use
list_for_each_entry_rcu() in vxlan_vnifilter_dump_dev()", repeats the same
premise ("runs under rcu_read_lock() without RTNL"), and the RTM_GETTUNNEL
handler is still registered with flags == 0 at the end of the series, so the
same question applies there.
> diff --git a/drivers/net/vxlan/vxlan_vnifilter.c b/drivers/net/vxlan/vxlan_vnifilter.c
> index dd94085e0886..ddfa24ad16f9 100644
> --- a/drivers/net/vxlan/vxlan_vnifilter.c
> +++ b/drivers/net/vxlan/vxlan_vnifilter.c
> @@ -902,6 +902,7 @@ void vxlan_vnigroup_uninit(struct vxlan_dev *vxlan)
> struct vxlan_vni_group *vg;
>
> vg = rtnl_dereference(vxlan->vnigrp);
> + rcu_assign_pointer(vxlan->vnigrp, NULL);
> list_for_each_entry_safe(v, tmp, &vg->vni_list, vlist) {
> rhashtable_remove_fast(&vg->vni_hash, &v->vnode,
> vxlan_vni_rht_params);
> @@ -914,7 +915,7 @@ void vxlan_vnigroup_uninit(struct vxlan_dev *vxlan)
> call_rcu(&v->rcu, vxlan_vni_node_rcu_free);
> }
> rhashtable_destroy(&vg->vni_hash);
> - kfree(vg);
> + kfree_rcu(vg, rcu);
> }
[ ... ]
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260903120840.1024153-1-edumazet%40google.com
^ permalink raw reply [flat|nested] 28+ messages in thread* Re: [PATCH net-next 2/9] vxlan: vnifilter: free vxlan_vni_group via RCU in vxlan_vnigroup_uninit()
2026-09-07 6:11 ` netdev-bot+sashiko
@ 2026-09-07 6:33 ` Eric Dumazet
0 siblings, 0 replies; 28+ messages in thread
From: Eric Dumazet @ 2026-09-07 6:33 UTC (permalink / raw)
To: netdev-bot+sashiko
Cc: davem, kuba, pabeni, horms, kuniyu, idosch, andrew+netdev, netdev,
eric.dumazet
On Mon, Sep 7, 2026 at 8:11 AM <netdev-bot+sashiko@kernel.org> wrote:
>
> Thank you for your contribution! Sashiko AI review found 1 potential
> issue(s) to consider:
>
> Critical: 0 · High: 0 · Medium: 1 · Low: 0
>
> - [Medium] Changelog premise is not supported by the code: commit "vxlan:
> vnifilter: free vxlan_vni_group via RCU in vxlan_vnigroup_uninit()"…
>
> --
>
> vxlan: vnifilter: free vxlan_vni_group via RCU in vxlan_vnigroup_uninit()
>
> This clears vxlan->vnigrp with rcu_assign_pointer() in
> vxlan_vnigroup_uninit() and defers the freeing of struct vxlan_vni_group
> to kfree_rcu(), adding an rcu_head to the structure. It is described as
> fixing a use-after-free between device deletion and an RTM_GETTUNNEL dump.
>
> > vxlan->vnigrp is an RCU-protected pointer accessed locklessly under
> > rcu_read_lock() in vxlan_vnifilter_dump_dev().
> >
> > Currently, vxlan_vnigroup_uninit() frees struct vxlan_vni_group
> > synchronously via kfree(vg). If a VXLAN device is deleted concurrently
> > with an RTM_GETTUNNEL dump, vxlan_vnifilter_dump_dev() can suffer a
> > use-after-free when reading vg->num_vnis or walking vg->vni_list.
> >
> > Fixes: f9c4bb0b245c ("vxlan: vni filtering support on collect metadata device")
>
> [Severity: Medium]
> Is the described race reachable in this tree? The RTM_GETTUNNEL dump is
> registered without RTNL_FLAG_DUMP_UNLOCKED:
>
> drivers/net/vxlan/vxlan_vnifilter.c:
> static const struct rtnl_msg_handler vxlan_vnifilter_rtnl_msg_handlers[] = {
> {THIS_MODULE, PF_BRIDGE, RTM_GETTUNNEL, NULL, vxlan_vnifilter_dump, 0},
>
> With flags == 0, rtnl_dumpit() wraps the whole callback in RTNL:
>
> net/core/rtnetlink.c:rtnl_dumpit() {
> const bool needs_lock = !(cb->flags & RTNL_FLAG_DUMP_UNLOCKED);
> ...
> if (needs_lock)
> rtnl_lock();
> err = dumpit(skb, cb);
> if (needs_lock)
> rtnl_unlock();
> }
>
> so vxlan_vnifilter_dump() and vxlan_vnifilter_dump_dev() run with RTNL held,
> not "locklessly" as the changelog states.
>
> On the freeing side, vxlan_vnigroup_uninit() is reached only from the
> vxlan_init() error path and from vxlan_uninit() (ndo_uninit), both of which
> run under RTNL via register_netdevice() and
> unregister_netdevice_many_notify(). Doesn't that make the dump and the free
> mutually exclusive today?
>
> The dump also does not cache a device or vnigrp pointer across paginated
> recvmsg iterations: it stores only an ifindex in cb->args[0] and re-resolves
> through dev_get_by_index_rcu()/for_each_netdev_rcu(), so a stale vnigrp does
> not appear to be observable between iterations either.
>
> The code change itself looks harmless, since the remaining readers already
> tolerate a NULL vnigrp:
>
> drivers/net/vxlan/vxlan_vnifilter.c:vxlan_vnifilter_dump_dev() {
> /* RCU needed because of the vni locking rules (rcu || rtnl) */
> vg = rcu_dereference(vxlan->vnigrp);
> if (!vg || !vg->num_vnis)
> return 0;
>
> and vxlan_vnifilter_lookup() in drivers/net/vxlan/vxlan_private.h does the
> same NULL check.
>
> Would it be more accurate to describe this as preparation for an
> RTNL-unlocked RTM_GETTUNNEL dump, and to drop the Fixes: tag (or justify it)
> so this is not backported to stable as a use-after-free fix?
>
> The next patch in the series, "vxlan: vnifilter: use
> list_for_each_entry_rcu() in vxlan_vnifilter_dump_dev()", repeats the same
> premise ("runs under rcu_read_lock() without RTNL"), and the RTM_GETTUNNEL
> handler is still registered with flags == 0 at the end of the series, so the
> same question applies there.
Not that Kuniyuki pointed out that the patch was not needed, it was
dropped from V2.
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH net-next 3/9] vxlan: vnifilter: use list_for_each_entry_rcu() in vxlan_vnifilter_dump_dev()
2026-09-03 12:08 [PATCH net-next 0/9] vxlan: convert configuration to RCU and drop RTNL in vxlan_fill_info() Eric Dumazet
2026-09-03 12:08 ` [PATCH net-next 1/9] vxlan: initialize _md in vxlan_xmit_one() Eric Dumazet
2026-09-03 12:08 ` [PATCH net-next 2/9] vxlan: vnifilter: free vxlan_vni_group via RCU in vxlan_vnigroup_uninit() Eric Dumazet
@ 2026-09-03 12:08 ` Eric Dumazet
2026-09-05 3:56 ` Kuniyuki Iwashima
2026-09-07 6:11 ` netdev-bot+sashiko
2026-09-03 12:08 ` [PATCH net-next 4/9] vxlan: pass vxlan_config pointer to helper functions Eric Dumazet
` (5 subsequent siblings)
8 siblings, 2 replies; 28+ messages in thread
From: Eric Dumazet @ 2026-09-03 12:08 UTC (permalink / raw)
To: David S . Miller, Jakub Kicinski, Paolo Abeni
Cc: Simon Horman, Kuniyuki Iwashima, Ido Schimmel, Andrew Lunn,
netdev, eric.dumazet, Eric Dumazet
vxlan_vnifilter_dump_dev() runs under rcu_read_lock() without RTNL when
dumping VNI filter entries via RTM_GETTUNNEL.
1) Currently it traverses vg->vni_list using list_for_each_entry_safe(),
which performs raw pointer accesses without RCU dereference barriers.
Since concurrent RTNL writers modify vg->vni_list using list_add_rcu()
and list_del_rcu(), use list_for_each_entry_rcu() instead.
2) If vxlan_vnifilter_dump_dev() returns early because VXLAN_F_VNIFILTER
is not set or vg has no VNIs, cb->args[1] was not cleared. If a
paginated dump was in progress, this leaked a non-zero cb->args[1]
to the next device in vxlan_vnifilter_dump(), silently skipping its
first N VNIs. Clear cb->args[1] on early returns.
Fixes: f9c4bb0b245c ("vxlan: vni filtering support on collect metadata device")
Signed-off-by: Eric Dumazet <edumazet@google.com>
---
drivers/net/vxlan/vxlan_vnifilter.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/drivers/net/vxlan/vxlan_vnifilter.c b/drivers/net/vxlan/vxlan_vnifilter.c
index ddfa24ad16f9303d7796e4a199b16dd5cc62047c..53213542fa3ecf67d66ba6bf41f7ac51cf8fb471 100644
--- a/drivers/net/vxlan/vxlan_vnifilter.c
+++ b/drivers/net/vxlan/vxlan_vnifilter.c
@@ -333,7 +333,7 @@ static int vxlan_vnifilter_dump_dev(const struct net_device *dev,
struct sk_buff *skb,
struct netlink_callback *cb)
{
- struct vxlan_vni_node *tmp, *v, *vbegin = NULL, *vend = NULL;
+ struct vxlan_vni_node *v, *vbegin = NULL, *vend = NULL;
struct vxlan_dev *vxlan = netdev_priv(dev);
struct tunnel_msg *new_tmsg, *tmsg;
int idx = 0, s_idx = cb->args[1];
@@ -342,13 +342,17 @@ static int vxlan_vnifilter_dump_dev(const struct net_device *dev,
bool dump_stats;
int err = 0;
- if (!(vxlan->cfg.flags & VXLAN_F_VNIFILTER))
+ if (!(vxlan->cfg.flags & VXLAN_F_VNIFILTER)) {
+ cb->args[1] = 0;
return -EINVAL;
+ }
/* RCU needed because of the vni locking rules (rcu || rtnl) */
vg = rcu_dereference(vxlan->vnigrp);
- if (!vg || !vg->num_vnis)
+ if (!vg || !vg->num_vnis) {
+ cb->args[1] = 0;
return 0;
+ }
tmsg = nlmsg_data(cb->nlh);
dump_stats = !!(tmsg->flags & TUNNEL_MSG_FLAG_STATS);
@@ -362,7 +366,7 @@ static int vxlan_vnifilter_dump_dev(const struct net_device *dev,
new_tmsg->family = PF_BRIDGE;
new_tmsg->ifindex = dev->ifindex;
- list_for_each_entry_safe(v, tmp, &vg->vni_list, vlist) {
+ list_for_each_entry_rcu(v, &vg->vni_list, vlist) {
if (idx < s_idx) {
idx++;
continue;
--
2.55.0.970.g62bdec98f9-goog
^ permalink raw reply related [flat|nested] 28+ messages in thread* Re: [PATCH net-next 3/9] vxlan: vnifilter: use list_for_each_entry_rcu() in vxlan_vnifilter_dump_dev()
2026-09-03 12:08 ` [PATCH net-next 3/9] vxlan: vnifilter: use list_for_each_entry_rcu() in vxlan_vnifilter_dump_dev() Eric Dumazet
@ 2026-09-05 3:56 ` Kuniyuki Iwashima
2026-09-07 6:11 ` netdev-bot+sashiko
1 sibling, 0 replies; 28+ messages in thread
From: Kuniyuki Iwashima @ 2026-09-05 3:56 UTC (permalink / raw)
To: Eric Dumazet
Cc: David S . Miller, Jakub Kicinski, Paolo Abeni, Simon Horman,
Ido Schimmel, Andrew Lunn, netdev, eric.dumazet
On Thu, Sep 3, 2026 at 5:08 AM Eric Dumazet <edumazet@google.com> wrote:
>
> vxlan_vnifilter_dump_dev() runs under rcu_read_lock() without RTNL when
> dumping VNI filter entries via RTM_GETTUNNEL.
>
> 1) Currently it traverses vg->vni_list using list_for_each_entry_safe(),
> which performs raw pointer accesses without RCU dereference barriers.
> Since concurrent RTNL writers modify vg->vni_list using list_add_rcu()
> and list_del_rcu(), use list_for_each_entry_rcu() instead.
>
> 2) If vxlan_vnifilter_dump_dev() returns early because VXLAN_F_VNIFILTER
> is not set or vg has no VNIs, cb->args[1] was not cleared. If a
> paginated dump was in progress, this leaked a non-zero cb->args[1]
> to the next device in vxlan_vnifilter_dump(), silently skipping its
> first N VNIs. Clear cb->args[1] on early returns.
>
> Fixes: f9c4bb0b245c ("vxlan: vni filtering support on collect metadata device")
> Signed-off-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com>
^ permalink raw reply [flat|nested] 28+ messages in thread* Re: [PATCH net-next 3/9] vxlan: vnifilter: use list_for_each_entry_rcu() in vxlan_vnifilter_dump_dev()
2026-09-03 12:08 ` [PATCH net-next 3/9] vxlan: vnifilter: use list_for_each_entry_rcu() in vxlan_vnifilter_dump_dev() Eric Dumazet
2026-09-05 3:56 ` Kuniyuki Iwashima
@ 2026-09-07 6:11 ` netdev-bot+sashiko
1 sibling, 0 replies; 28+ messages in thread
From: netdev-bot+sashiko @ 2026-09-07 6:11 UTC (permalink / raw)
To: edumazet
Cc: davem, kuba, pabeni, horms, kuniyu, idosch, andrew+netdev, netdev,
eric.dumazet
Thank you for your contribution! Sashiko AI review found 2 potential
issue(s) to consider:
Critical: 0 · High: 0 · Medium: 2 · Low: 0 (1)
- [Medium] Commit message misstates the locking context that justifies
change 1.
- [Medium] The fix is incomplete: the exact symptom the commit message
claims to close — a non-zero cb->args[1] being applied to a device it…
Pre-existing issues:
- [Low] Claimed unsynchronized read of multi-word state (vg->num_vnis and
vxlan_vni_node::remote_ip) by the RCU-only dump path while RTNL-only…
--
vxlan: vnifilter: use list_for_each_entry_rcu() in vxlan_vnifilter_dump_dev()
This converts the vg->vni_list traversal in vxlan_vnifilter_dump_dev() from
list_for_each_entry_safe() to list_for_each_entry_rcu(), and clears
cb->args[1] on the two early returns (no VXLAN_F_VNIFILTER, and no VNIs in
the group) so a paginated dump does not carry a stale index to the next
device.
A couple of questions about the changelog and about the completeness of the
cb->args[1] fix.
> vxlan_vnifilter_dump_dev() runs under rcu_read_lock() without RTNL when
> dumping VNI filter entries via RTM_GETTUNNEL.
>
> 1) Currently it traverses vg->vni_list using list_for_each_entry_safe(),
> which performs raw pointer accesses without RCU dereference barriers.
> Since concurrent RTNL writers modify vg->vni_list using list_add_rcu()
> and list_del_rcu(), use list_for_each_entry_rcu() instead.
[Severity: Medium]
Is the "without RTNL" premise accurate for this dump at this revision?
The PF_BRIDGE/RTM_GETTUNNEL handler is registered with a zero flags field,
i.e. without RTNL_FLAG_DUMP_UNLOCKED:
drivers/net/vxlan/vxlan_vnifilter.c:
static const struct rtnl_msg_handler vxlan_vnifilter_rtnl_msg_handlers[] = {
{THIS_MODULE, PF_BRIDGE, RTM_GETTUNNEL, NULL, vxlan_vnifilter_dump, 0},
so rtnetlink_dump_start() installs rtnl_dumpit() as the dump callback:
net/core/rtnetlink.c:rtnl_dumpit() {
const bool needs_lock = !(cb->flags & RTNL_FLAG_DUMP_UNLOCKED);
...
if (needs_lock)
rtnl_lock();
err = dumpit(skb, cb);
...
}
All writers of vg->vni_list (__vxlan_vni_add_list()/__vxlan_vni_del_list(),
vxlan_vs_add_vnigrp()/vxlan_vs_del_vnigrp(), vxlan_vnigroup_uninit()) run
under RTNL with rtnl_dereference()/ASSERT_RTNL(). With RTNL held around
vxlan_vnifilter_dump() the traversal cannot observe a concurrent
list_add_rcu()/list_del_rcu(), which would make part 1 an annotation
improvement rather than a fix for a reachable race. I also checked the end
of the series and the handler flags are still 0 there.
Would it make sense to describe part 1 as an annotation/lockdep improvement
(and preparation for an RCU-only dump), and keep the Fixes: tag tied to the
cb->args[1] problem in part 2?
> diff --git a/drivers/net/vxlan/vxlan_vnifilter.c b/drivers/net/vxlan/vxlan_vnifilter.c
> index ddfa24ad16f9..53213542fa3e 100644
> --- a/drivers/net/vxlan/vxlan_vnifilter.c
> +++ b/drivers/net/vxlan/vxlan_vnifilter.c
[ ... ]
> @@ -342,13 +342,17 @@ static int vxlan_vnifilter_dump_dev(const struct net_device *dev,
> bool dump_stats;
> int err = 0;
>
> - if (!(vxlan->cfg.flags & VXLAN_F_VNIFILTER))
> + if (!(vxlan->cfg.flags & VXLAN_F_VNIFILTER)) {
> + cb->args[1] = 0;
> return -EINVAL;
> + }
>
> /* RCU needed because of the vni locking rules (rcu || rtnl) */
> vg = rcu_dereference(vxlan->vnigrp);
> - if (!vg || !vg->num_vnis)
> + if (!vg || !vg->num_vnis) {
> + cb->args[1] = 0;
> return 0;
> + }
[Severity: Medium]
Do these two clears cover the symptom described in the changelog?
vxlan_vnifilter_dump() resumes the multi-device walk by ordinal position,
and both RTNL and RCU are dropped between netlink_dump() invocations:
drivers/net/vxlan/vxlan_vnifilter.c:vxlan_vnifilter_dump() {
for_each_netdev_rcu(net, dev) {
if (!netif_is_vxlan(dev))
continue;
if (idx < s_idx)
goto skip;
err = vxlan_vnifilter_dump_dev(dev, skb, cb);
if (err == -EMSGSIZE)
break;
skip:
idx++;
}
}
cb->args[0] = idx;
rcu_read_unlock();
If a vxlan device positioned before the paused one is unregistered in that
gap, the device now sitting at idx == s_idx is a different device. That
device has VXLAN_F_VNIFILTER set and a non-empty group, so neither new
cb->args[1] = 0 path is taken, and vxlan_vnifilter_dump_dev() consumes the
stale index:
int idx = 0, s_idx = cb->args[1];
...
list_for_each_entry_rcu(v, &vg->vni_list, vlist) {
if (idx < s_idx) {
idx++;
continue;
}
which skips that device's first N VNIs — the exact case the changelog says
is being fixed. A device inserted at the head produces the mirror case, a
device re-dumped from a bogus VNI offset. The same positional resume inside
one device's vni_list can skip or repeat entries when VNIs are added or
removed via __vxlan_vni_add_list()/__vxlan_vni_del_list() between
invocations.
Would resetting cb->args[1] in vxlan_vnifilter_dump() whenever it moves on
to a device other than the paused one (or resuming by ifindex and VNI value
instead of by position) close the remaining cases? And since the iteration
can still skip or repeat objects, should the dump record a generation
counter in cb->seq so NLM_F_DUMP_INTR is reported to user space?
>
> tmsg = nlmsg_data(cb->nlh);
> dump_stats = !!(tmsg->flags & TUNNEL_MSG_FLAG_STATS);
[ ... ]
> @@ -362,7 +366,7 @@ static int vxlan_vnifilter_dump_dev(const struct net_device *dev,
> new_tmsg->family = PF_BRIDGE;
> new_tmsg->ifindex = dev->ifindex;
>
> - list_for_each_entry_safe(v, tmp, &vg->vni_list, vlist) {
> + list_for_each_entry_rcu(v, &vg->vni_list, vlist) {
> if (idx < s_idx) {
> idx++;
> continue;
[Severity: Low]
This isn't a bug introduced by this patch, but if the dump is later made
RTNL-less, are the per-node fields read in this loop safe for an RCU-only
reader?
The loop and vxlan_fill_vni_filter_entry() read the multi-word
vxlan_vni_node::remote_ip union, and the early return above reads
vg->num_vnis, while the writers update them in place under RTNL only:
drivers/net/vxlan/vxlan_vnifilter.c:vxlan_vni_update_group() {
if (group)
memcpy(&vninode->remote_ip, group, sizeof(vninode->remote_ip));
}
and vg->num_vnis++/-- in __vxlan_vni_add_list()/__vxlan_vni_del_list().
Today this is serialized because rtnl_dumpit() holds RTNL for this handler,
so there is no torn read. If the plan is to drop RTNL from this dump, would
these fields need their own publish/read discipline as well?
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260903120840.1024153-1-edumazet%40google.com
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH net-next 4/9] vxlan: pass vxlan_config pointer to helper functions
2026-09-03 12:08 [PATCH net-next 0/9] vxlan: convert configuration to RCU and drop RTNL in vxlan_fill_info() Eric Dumazet
` (2 preceding siblings ...)
2026-09-03 12:08 ` [PATCH net-next 3/9] vxlan: vnifilter: use list_for_each_entry_rcu() in vxlan_vnifilter_dump_dev() Eric Dumazet
@ 2026-09-03 12:08 ` Eric Dumazet
2026-09-05 4:09 ` Kuniyuki Iwashima
2026-09-03 12:08 ` [PATCH net-next 5/9] vxlan: move VXLAN_F_MDB to struct vxlan_dev flags Eric Dumazet
` (4 subsequent siblings)
8 siblings, 1 reply; 28+ messages in thread
From: Eric Dumazet @ 2026-09-03 12:08 UTC (permalink / raw)
To: David S . Miller, Jakub Kicinski, Paolo Abeni
Cc: Simon Horman, Kuniyuki Iwashima, Ido Schimmel, Andrew Lunn,
netdev, eric.dumazet, Eric Dumazet
In preparation for converting vxlan->cfg to an RCU-protected pointer,
refactor internal helper functions in the RX, TX, MDB, and VNIFILTER
paths to accept a pointer to struct vxlan_config (or pass flags/
saddr_family where appropriate) rather than directly accessing
vxlan->cfg.
No functional changes.
Signed-off-by: Eric Dumazet <edumazet@google.com>
---
drivers/net/vxlan/vxlan_core.c | 345 +++++++++++++++-------------
drivers/net/vxlan/vxlan_mdb.c | 21 +-
drivers/net/vxlan/vxlan_private.h | 8 +-
drivers/net/vxlan/vxlan_vnifilter.c | 5 +-
4 files changed, 208 insertions(+), 171 deletions(-)
diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c
index 6d886b6f2dc1d62f0eb26c9ba310c4feaed9cafd..2627e26f3699ff39e7e907b6f5f8f684d2d1235a 100644
--- a/drivers/net/vxlan/vxlan_core.c
+++ b/drivers/net/vxlan/vxlan_core.c
@@ -377,14 +377,15 @@ static void vxlan_fdb_miss(struct vxlan_dev *vxlan, const u8 eth_addr[ETH_ALEN])
/* Look up Ethernet address in forwarding table */
static struct vxlan_fdb *vxlan_find_mac_rcu(struct vxlan_dev *vxlan,
+ const struct vxlan_config *cfg,
const u8 *mac, __be32 vni)
{
struct vxlan_fdb_key key;
memset(&key, 0, sizeof(key));
memcpy(key.eth_addr, mac, sizeof(key.eth_addr));
- if (!(vxlan->cfg.flags & VXLAN_F_COLLECT_METADATA))
- key.vni = vxlan->default_dst.remote_vni;
+ if (!(cfg->flags & VXLAN_F_COLLECT_METADATA))
+ key.vni = cfg->vni;
else
key.vni = vni;
@@ -393,11 +394,12 @@ static struct vxlan_fdb *vxlan_find_mac_rcu(struct vxlan_dev *vxlan,
}
static struct vxlan_fdb *vxlan_find_mac_tx(struct vxlan_dev *vxlan,
+ const struct vxlan_config *cfg,
const u8 *mac, __be32 vni)
{
struct vxlan_fdb *f;
- f = vxlan_find_mac_rcu(vxlan, mac, vni);
+ f = vxlan_find_mac_rcu(vxlan, cfg, mac, vni);
if (f) {
unsigned long now = jiffies;
@@ -416,7 +418,7 @@ static struct vxlan_fdb *vxlan_find_mac(struct vxlan_dev *vxlan,
lockdep_assert_held_once(&vxlan->hash_lock);
rcu_read_lock();
- f = vxlan_find_mac_rcu(vxlan, mac, vni);
+ f = vxlan_find_mac_rcu(vxlan, &vxlan->cfg, mac, vni);
rcu_read_unlock();
return f;
@@ -457,7 +459,7 @@ int vxlan_fdb_find_uc(struct net_device *dev, const u8 *mac, __be32 vni,
rcu_read_lock();
- f = vxlan_find_mac_rcu(vxlan, eth_addr, vni);
+ f = vxlan_find_mac_rcu(vxlan, &vxlan->cfg, eth_addr, vni);
if (f)
rdst = first_remote_rcu(f);
if (!rdst) {
@@ -1405,7 +1407,7 @@ static int vxlan_fdb_get(struct sk_buff *skb,
rcu_read_lock();
- f = vxlan_find_mac_rcu(vxlan, addr, vni);
+ f = vxlan_find_mac_rcu(vxlan, &vxlan->cfg, addr, vni);
if (!f) {
NL_SET_ERR_MSG(extack, "Fdb entry not found");
err = -ENOENT;
@@ -1423,6 +1425,7 @@ static int vxlan_fdb_get(struct sk_buff *skb,
* and Tunnel endpoint.
*/
static enum skb_drop_reason vxlan_snoop(struct net_device *dev,
+ const struct vxlan_config *cfg,
union vxlan_addr *src_ip,
const u8 *src_mac, u32 src_ifindex,
__be32 vni)
@@ -1441,7 +1444,7 @@ static enum skb_drop_reason vxlan_snoop(struct net_device *dev,
ifindex = src_ifindex;
#endif
- f = vxlan_find_mac_rcu(vxlan, src_mac, vni);
+ f = vxlan_find_mac_rcu(vxlan, cfg, src_mac, vni);
if (likely(f)) {
struct vxlan_rdst *rdst = first_remote_rcu(f);
unsigned long now = jiffies;
@@ -1477,9 +1480,9 @@ static enum skb_drop_reason vxlan_snoop(struct net_device *dev,
vxlan_fdb_update(vxlan, src_mac, src_ip,
NUD_REACHABLE,
NLM_F_EXCL|NLM_F_CREATE,
- vxlan->cfg.dst_port,
+ cfg->dst_port,
vni,
- vxlan->default_dst.remote_vni,
+ cfg->vni,
ifindex, NTF_SELF, 0, true, NULL);
spin_unlock(&vxlan->hash_lock);
}
@@ -1587,6 +1590,7 @@ static void vxlan_parse_gbp_hdr(struct sk_buff *skb, u32 vxflags,
}
static enum skb_drop_reason vxlan_set_mac(struct vxlan_dev *vxlan,
+ const struct vxlan_config *cfg,
struct vxlan_sock *vs,
struct sk_buff *skb, __be32 vni)
{
@@ -1612,10 +1616,10 @@ static enum skb_drop_reason vxlan_set_mac(struct vxlan_dev *vxlan,
#endif
}
- if (!(vxlan->cfg.flags & VXLAN_F_LEARN))
+ if (!(cfg->flags & VXLAN_F_LEARN))
return SKB_NOT_DROPPED_YET;
- return vxlan_snoop(skb->dev, &saddr, eth_hdr(skb)->h_source,
+ return vxlan_snoop(skb->dev, cfg, &saddr, eth_hdr(skb)->h_source,
ifindex, vni);
}
@@ -1646,18 +1650,21 @@ static bool vxlan_ecn_decapsulate(struct vxlan_sock *vs, void *oiph,
static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
{
struct vxlan_vni_node *vninode = NULL;
- const struct vxlanhdr *vh;
- struct vxlan_dev *vxlan;
- struct vxlan_sock *vs;
- struct vxlan_metadata _md;
- struct vxlan_metadata *md = &_md;
__be16 protocol = htons(ETH_P_TEB);
+ const struct vxlan_config *cfg;
enum skb_drop_reason reason;
+ const struct vxlanhdr *vh;
+ struct vxlan_metadata *md;
+ struct vxlan_metadata _md;
+ struct vxlan_dev *vxlan;
bool raw_proto = false;
- void *oiph;
+ struct vxlan_sock *vs;
__be32 vni = 0;
+ void *oiph;
int nh;
+ md = &_md;
+
/* Need UDP and VXLAN header to be present */
reason = pskb_may_pull_reason(skb, VXLAN_HLEN);
if (reason)
@@ -1685,8 +1692,9 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
goto drop;
}
- if (vh->vx_flags & vxlan->cfg.reserved_bits.vx_flags ||
- vh->vx_vni & vxlan->cfg.reserved_bits.vx_vni) {
+ cfg = &vxlan->cfg;
+ if (vh->vx_flags & cfg->reserved_bits.vx_flags ||
+ vh->vx_vni & cfg->reserved_bits.vx_vni) {
/* If the header uses bits besides those enabled by the
* netdevice configuration, treat this as a malformed packet.
* This behavior diverges from VXLAN RFC (RFC7348) which
@@ -1698,12 +1706,12 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
reason = SKB_DROP_REASON_VXLAN_INVALID_HDR;
DEV_STATS_INC(vxlan->dev, rx_frame_errors);
DEV_STATS_INC(vxlan->dev, rx_errors);
- vxlan_vnifilter_count(vxlan, vni, vninode,
+ vxlan_vnifilter_count(vxlan, cfg, vni, vninode,
VXLAN_VNI_STATS_RX_ERRORS, 0);
goto drop;
}
- if (vxlan->cfg.flags & VXLAN_F_GPE) {
+ if (cfg->flags & VXLAN_F_GPE) {
if (!vxlan_parse_gpe_proto(vh, &protocol))
goto drop;
raw_proto = true;
@@ -1715,8 +1723,8 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
goto drop;
}
- if (vxlan->cfg.flags & VXLAN_F_REMCSUM_RX) {
- reason = vxlan_remcsum(skb, vxlan->cfg.flags);
+ if (cfg->flags & VXLAN_F_REMCSUM_RX) {
+ reason = vxlan_remcsum(skb, cfg->flags);
if (unlikely(reason))
goto drop;
}
@@ -1741,14 +1749,14 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
memset(md, 0, sizeof(*md));
}
- if (vxlan->cfg.flags & VXLAN_F_GBP)
- vxlan_parse_gbp_hdr(skb, vxlan->cfg.flags, md);
+ if (cfg->flags & VXLAN_F_GBP)
+ vxlan_parse_gbp_hdr(skb, cfg->flags, md);
/* Note that GBP and GPE can never be active together. This is
* ensured in vxlan_dev_configure.
*/
if (!raw_proto) {
- reason = vxlan_set_mac(vxlan, vs, skb, vni);
+ reason = vxlan_set_mac(vxlan, cfg, vs, skb, vni);
if (reason)
goto drop;
} else {
@@ -1769,7 +1777,7 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
if (reason) {
DEV_STATS_INC(vxlan->dev, rx_length_errors);
DEV_STATS_INC(vxlan->dev, rx_errors);
- vxlan_vnifilter_count(vxlan, vni, vninode,
+ vxlan_vnifilter_count(vxlan, cfg, vni, vninode,
VXLAN_VNI_STATS_RX_ERRORS, 0);
goto drop;
}
@@ -1781,7 +1789,7 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
reason = SKB_DROP_REASON_IP_TUNNEL_ECN;
DEV_STATS_INC(vxlan->dev, rx_frame_errors);
DEV_STATS_INC(vxlan->dev, rx_errors);
- vxlan_vnifilter_count(vxlan, vni, vninode,
+ vxlan_vnifilter_count(vxlan, cfg, vni, vninode,
VXLAN_VNI_STATS_RX_ERRORS, 0);
goto drop;
}
@@ -1791,14 +1799,15 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
if (unlikely(!(vxlan->dev->flags & IFF_UP))) {
rcu_read_unlock();
dev_dstats_rx_dropped(vxlan->dev);
- vxlan_vnifilter_count(vxlan, vni, vninode,
+ vxlan_vnifilter_count(vxlan, cfg, vni, vninode,
VXLAN_VNI_STATS_RX_DROPS, 0);
reason = SKB_DROP_REASON_DEV_READY;
goto drop;
}
dev_dstats_rx_add(vxlan->dev, skb->len);
- vxlan_vnifilter_count(vxlan, vni, vninode, VXLAN_VNI_STATS_RX, skb->len);
+ vxlan_vnifilter_count(vxlan, cfg, vni, vninode, VXLAN_VNI_STATS_RX,
+ skb->len);
gro_cells_receive(&vxlan->gro_cells, skb);
rcu_read_unlock();
@@ -1839,7 +1848,7 @@ static int vxlan_err_lookup(struct sock *sk, struct sk_buff *skb)
return 0;
}
-static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)
+static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni, u32 flags)
{
struct vxlan_dev *vxlan = netdev_priv(dev);
struct arphdr *parp;
@@ -1852,7 +1861,7 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)
if (!pskb_network_may_pull(skb, arp_hdr_len(dev))) {
dev_dstats_tx_dropped(dev);
- vxlan_vnifilter_count(vxlan, vni, NULL,
+ vxlan_vnifilter_count(vxlan, &vxlan->cfg, vni, NULL,
VXLAN_VNI_STATS_TX_DROPS, 0);
goto out;
}
@@ -1893,7 +1902,7 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)
neigh_ha_snapshot(ha, n, n->dev);
rcu_read_lock();
- f = vxlan_find_mac_tx(vxlan, ha, vni);
+ f = vxlan_find_mac_tx(vxlan, &vxlan->cfg, ha, vni);
if (f)
rdst = first_remote_rcu(f);
if (rdst && vxlan_addr_any(&rdst->remote_ip)) {
@@ -1919,11 +1928,11 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)
if (netif_rx(reply) == NET_RX_DROP) {
dev_dstats_rx_dropped(dev);
- vxlan_vnifilter_count(vxlan, vni, NULL,
+ vxlan_vnifilter_count(vxlan, &vxlan->cfg, vni, NULL,
VXLAN_VNI_STATS_RX_DROPS, 0);
}
- } else if (vxlan->cfg.flags & VXLAN_F_L3MISS) {
+ } else if (flags & VXLAN_F_L3MISS) {
union vxlan_addr ipa = {
.sin.sin_addr.s_addr = tip,
.sin.sin_family = AF_INET,
@@ -2031,7 +2040,7 @@ static struct sk_buff *vxlan_na_create(struct sk_buff *request,
return reply;
}
-static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)
+static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni, u32 flags)
{
struct vxlan_dev *vxlan = netdev_priv(dev);
const struct in6_addr *daddr;
@@ -2065,7 +2074,7 @@ static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)
}
neigh_ha_snapshot(ha, n, n->dev);
- f = vxlan_find_mac_tx(vxlan, ha, vni);
+ f = vxlan_find_mac_tx(vxlan, &vxlan->cfg, ha, vni);
if (f)
rdst = first_remote_rcu(f);
if (rdst && vxlan_addr_any(&rdst->remote_ip)) {
@@ -2084,10 +2093,10 @@ static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)
if (netif_rx(reply) == NET_RX_DROP) {
dev_dstats_rx_dropped(dev);
- vxlan_vnifilter_count(vxlan, vni, NULL,
+ vxlan_vnifilter_count(vxlan, &vxlan->cfg, vni, NULL,
VXLAN_VNI_STATS_RX_DROPS, 0);
}
- } else if (vxlan->cfg.flags & VXLAN_F_L3MISS) {
+ } else if (flags & VXLAN_F_L3MISS) {
union vxlan_addr ipa = {
.sin6.sin6_addr = msg->target,
.sin6.sin6_family = AF_INET6,
@@ -2103,9 +2112,9 @@ static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)
}
#endif
-static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb)
+static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb,
+ const struct vxlan_config *cfg)
{
- struct vxlan_dev *vxlan = netdev_priv(dev);
struct neighbour *n;
if (is_multicast_ether_addr(eth_hdr(skb)->h_dest))
@@ -2121,7 +2130,7 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb)
return false;
pip = ip_hdr(skb);
n = neigh_lookup(&arp_tbl, &pip->daddr, dev);
- if (!n && (vxlan->cfg.flags & VXLAN_F_L3MISS)) {
+ if (!n && (cfg->flags & VXLAN_F_L3MISS)) {
union vxlan_addr ipa = {
.sin.sin_addr.s_addr = pip->daddr,
.sin.sin_family = AF_INET,
@@ -2147,7 +2156,7 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb)
return false;
pip6 = ipv6_hdr(skb);
n = neigh_lookup(&nd_tbl, &pip6->daddr, dev);
- if (!n && (vxlan->cfg.flags & VXLAN_F_L3MISS)) {
+ if (!n && (cfg->flags & VXLAN_F_L3MISS)) {
union vxlan_addr ipa = {
.sin6.sin6_addr = pip6->daddr,
.sin6.sin6_family = AF_INET6,
@@ -2265,20 +2274,21 @@ static int vxlan_build_skb(struct sk_buff *skb, struct dst_entry *dst,
/* Bypass encapsulation if the destination is local */
static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan,
- struct vxlan_dev *dst_vxlan, __be32 vni,
- bool snoop)
+ struct vxlan_dev *dst_vxlan,
+ const struct vxlan_config *src_cfg,
+ __be32 vni, bool snoop)
{
+ const struct vxlan_config *dst_cfg = &dst_vxlan->cfg;
union vxlan_addr loopback;
- union vxlan_addr *remote_ip = &dst_vxlan->default_dst.remote_ip;
unsigned int len = skb->len;
- struct net_device *dev;
+ struct net_device *dev = dst_vxlan->dev;
skb->pkt_type = PACKET_HOST;
skb->encapsulation = 0;
- skb->dev = dst_vxlan->dev;
+ skb->dev = dev;
__skb_pull(skb, skb_network_offset(skb));
- if (remote_ip->sa.sa_family == AF_INET) {
+ if (dst_vxlan->default_dst.remote_ip.sa.sa_family == AF_INET) {
loopback.sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
loopback.sa.sa_family = AF_INET;
#if IS_ENABLED(CONFIG_IPV6)
@@ -2289,26 +2299,25 @@ static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan,
}
rcu_read_lock();
- dev = skb->dev;
if (unlikely(!(dev->flags & IFF_UP))) {
kfree_skb_reason(skb, SKB_DROP_REASON_DEV_READY);
goto drop;
}
- if ((dst_vxlan->cfg.flags & VXLAN_F_LEARN) && snoop)
- vxlan_snoop(dev, &loopback, eth_hdr(skb)->h_source, 0, vni);
+ if ((dst_cfg->flags & VXLAN_F_LEARN) && snoop)
+ vxlan_snoop(dev, dst_cfg, &loopback, eth_hdr(skb)->h_source, 0, vni);
dev_dstats_tx_add(src_vxlan->dev, len);
- vxlan_vnifilter_count(src_vxlan, vni, NULL, VXLAN_VNI_STATS_TX, len);
+ vxlan_vnifilter_count(src_vxlan, src_cfg, vni, NULL, VXLAN_VNI_STATS_TX, len);
if (__netif_rx(skb) == NET_RX_SUCCESS) {
dev_dstats_rx_add(dst_vxlan->dev, len);
- vxlan_vnifilter_count(dst_vxlan, vni, NULL, VXLAN_VNI_STATS_RX,
+ vxlan_vnifilter_count(dst_vxlan, dst_cfg, vni, NULL, VXLAN_VNI_STATS_RX,
len);
} else {
drop:
dev_dstats_rx_dropped(dev);
- vxlan_vnifilter_count(dst_vxlan, vni, NULL,
+ vxlan_vnifilter_count(dst_vxlan, dst_cfg, vni, NULL,
VXLAN_VNI_STATS_RX_DROPS, 0);
}
rcu_read_unlock();
@@ -2316,6 +2325,7 @@ static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan,
static int encap_bypass_if_local(struct sk_buff *skb, struct net_device *dev,
struct vxlan_dev *vxlan,
+ const struct vxlan_config *cfg,
int addr_family,
__be16 dst_port, int dst_ifindex, __be32 vni,
struct dst_entry *dst,
@@ -2331,22 +2341,22 @@ static int encap_bypass_if_local(struct sk_buff *skb, struct net_device *dev,
/* Bypass encapsulation if the destination is local */
if (rt_flags & RTCF_LOCAL &&
!(rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST)) &&
- vxlan->cfg.flags & VXLAN_F_LOCALBYPASS) {
+ cfg->flags & VXLAN_F_LOCALBYPASS) {
struct vxlan_dev *dst_vxlan;
dst_release(dst);
dst_vxlan = vxlan_find_vni(vxlan->net, dst_ifindex, vni,
addr_family, dst_port,
- vxlan->cfg.flags);
+ cfg->flags);
if (!dst_vxlan) {
DEV_STATS_INC(dev, tx_errors);
- vxlan_vnifilter_count(vxlan, vni, NULL,
+ vxlan_vnifilter_count(vxlan, cfg, vni, NULL,
VXLAN_VNI_STATS_TX_ERRORS, 0);
kfree_skb_reason(skb, SKB_DROP_REASON_VXLAN_VNI_NOT_FOUND);
return -ENOENT;
}
- vxlan_encap_bypass(skb, vxlan, dst_vxlan, vni, true);
+ vxlan_encap_bypass(skb, vxlan, dst_vxlan, cfg, vni, true);
return 1;
}
@@ -2354,30 +2364,35 @@ static int encap_bypass_if_local(struct sk_buff *skb, struct net_device *dev,
}
void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
+ const struct vxlan_config *cfg,
__be32 default_vni, struct vxlan_rdst *rdst, bool did_rsc)
{
+ unsigned int pkt_len = skb->len;
+ struct vxlan_metadata _md = {};
+ __be16 src_port = 0, dst_port;
+ struct dst_entry *ndst = NULL;
+ enum skb_drop_reason reason;
struct dst_cache *dst_cache;
+ const struct iphdr *old_iph;
struct ip_tunnel_info *info;
struct ip_tunnel_key *pkey;
+ struct vxlan_metadata *md;
struct ip_tunnel_key key;
- struct vxlan_dev *vxlan = netdev_priv(dev);
- const struct iphdr *old_iph;
- struct vxlan_metadata _md = {};
- struct vxlan_metadata *md = &_md;
- unsigned int pkt_len = skb->len;
- __be16 src_port = 0, dst_port;
- struct dst_entry *ndst = NULL;
+ struct vxlan_dev *vxlan;
+ u32 flags = cfg->flags;
+ bool udp_sum = false;
+ bool no_eth_encap;
int addr_family;
+ bool use_cache;
+ __be32 vni = 0;
__u8 tos, ttl;
int ifindex;
int err = 0;
- u32 flags = vxlan->cfg.flags;
- bool use_cache;
- bool udp_sum = false;
- bool xnet = !net_eq(vxlan->net, dev_net(vxlan->dev));
- enum skb_drop_reason reason;
- bool no_eth_encap;
- __be32 vni = 0;
+ bool xnet;
+
+ vxlan = netdev_priv(dev);
+ xnet = !net_eq(vxlan->net, dev_net(vxlan->dev));
+ md = &_md;
no_eth_encap = flags & VXLAN_F_GPE && skb->protocol != htons(ETH_P_TEB);
reason = skb_vlan_inet_prepare(skb, no_eth_encap);
@@ -2397,23 +2412,23 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
if (vxlan_addr_any(&rdst->remote_ip)) {
if (did_rsc) {
/* short-circuited back to local bridge */
- vxlan_encap_bypass(skb, vxlan, vxlan,
+ vxlan_encap_bypass(skb, vxlan, vxlan, cfg,
default_vni, true);
return;
}
goto drop;
}
- addr_family = vxlan->cfg.saddr.sa.sa_family;
- dst_port = rdst->remote_port ? rdst->remote_port : vxlan->cfg.dst_port;
+ addr_family = cfg->saddr.sa.sa_family;
+ dst_port = rdst->remote_port ? rdst->remote_port : cfg->dst_port;
vni = (rdst->remote_vni) ? : default_vni;
ifindex = rdst->remote_ifindex;
if (addr_family == AF_INET) {
- key.u.ipv4.src = vxlan->cfg.saddr.sin.sin_addr.s_addr;
+ key.u.ipv4.src = cfg->saddr.sin.sin_addr.s_addr;
key.u.ipv4.dst = rdst->remote_ip.sin.sin_addr.s_addr;
} else {
- key.u.ipv6.src = vxlan->cfg.saddr.sin6.sin6_addr;
+ key.u.ipv6.src = cfg->saddr.sin6.sin6_addr;
key.u.ipv6.dst = rdst->remote_ip.sin6.sin6_addr;
}
@@ -2422,11 +2437,11 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
if (flags & VXLAN_F_TTL_INHERIT) {
ttl = ip_tunnel_get_ttl(old_iph, skb);
} else {
- ttl = vxlan->cfg.ttl;
+ ttl = cfg->ttl;
if (!ttl && vxlan_addr_multicast(&rdst->remote_ip))
ttl = 1;
}
- tos = vxlan->cfg.tos;
+ tos = cfg->tos;
if (tos == 1)
tos = ip_tunnel_get_dsfield(old_iph, skb);
if (tos && !info)
@@ -2437,9 +2452,9 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
else
udp_sum = !(flags & VXLAN_F_UDP_ZERO_CSUM6_TX);
#if IS_ENABLED(CONFIG_IPV6)
- switch (vxlan->cfg.label_policy) {
+ switch (cfg->label_policy) {
case VXLAN_LABEL_FIXED:
- key.label = vxlan->cfg.label;
+ key.label = cfg->label;
break;
case VXLAN_LABEL_INHERIT:
key.label = ip_tunnel_get_flowlabel(old_iph, skb);
@@ -2457,7 +2472,7 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
}
pkey = &info->key;
addr_family = ip_tunnel_info_af(info);
- dst_port = info->key.tp_dst ? : vxlan->cfg.dst_port;
+ dst_port = info->key.tp_dst ? : cfg->dst_port;
vni = tunnel_id_to_key32(info->key.tun_id);
ifindex = 0;
dst_cache = &info->dst_cache;
@@ -2470,8 +2485,8 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
tos = info->key.tos;
udp_sum = test_bit(IP_TUNNEL_CSUM_BIT, info->key.tun_flags);
}
- src_port = udp_flow_src_port(dev_net(dev), skb, vxlan->cfg.port_min,
- vxlan->cfg.port_max, true);
+ src_port = udp_flow_src_port(dev_net(dev), skb, cfg->port_min,
+ cfg->port_max, true);
rcu_read_lock();
if (addr_family == AF_INET) {
@@ -2504,15 +2519,15 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
if (!info) {
/* Bypass encapsulation if the destination is local */
- err = encap_bypass_if_local(skb, dev, vxlan, AF_INET,
+ err = encap_bypass_if_local(skb, dev, vxlan, cfg, AF_INET,
dst_port, ifindex, vni,
&rt->dst, rt->rt_flags);
if (err)
goto out_unlock;
- if (vxlan->cfg.df == VXLAN_DF_SET) {
+ if (cfg->df == VXLAN_DF_SET) {
df = htons(IP_DF);
- } else if (vxlan->cfg.df == VXLAN_DF_INHERIT) {
+ } else if (cfg->df == VXLAN_DF_INHERIT) {
struct ethhdr *eth = eth_hdr(skb);
if (ntohs(eth->h_proto) == ETH_P_IPV6 ||
@@ -2541,7 +2556,7 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
unclone->key.u.ipv4.src = pkey->u.ipv4.dst;
unclone->key.u.ipv4.dst = saddr;
}
- vxlan_encap_bypass(skb, vxlan, vxlan, vni, false);
+ vxlan_encap_bypass(skb, vxlan, vxlan, cfg, vni, false);
dst_release(ndst);
goto out_unlock;
}
@@ -2591,7 +2606,7 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
if (!info) {
u32 rt6i_flags = dst_rt6_info(ndst)->rt6i_flags;
- err = encap_bypass_if_local(skb, dev, vxlan, AF_INET6,
+ err = encap_bypass_if_local(skb, dev, vxlan, cfg, AF_INET6,
dst_port, ifindex, vni,
ndst, rt6i_flags);
if (err)
@@ -2615,7 +2630,7 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
unclone->key.u.ipv6.dst = saddr;
}
- vxlan_encap_bypass(skb, vxlan, vxlan, vni, false);
+ vxlan_encap_bypass(skb, vxlan, vxlan, cfg, vni, false);
dst_release(ndst);
goto out_unlock;
}
@@ -2636,14 +2651,14 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
ip6cb_flags);
#endif
}
- vxlan_vnifilter_count(vxlan, vni, NULL, VXLAN_VNI_STATS_TX, pkt_len);
+ vxlan_vnifilter_count(vxlan, cfg, vni, NULL, VXLAN_VNI_STATS_TX, pkt_len);
out_unlock:
rcu_read_unlock();
return;
drop:
dev_dstats_tx_dropped(dev);
- vxlan_vnifilter_count(vxlan, vni, NULL, VXLAN_VNI_STATS_TX_DROPS, 0);
+ vxlan_vnifilter_count(vxlan, cfg, vni, NULL, VXLAN_VNI_STATS_TX_DROPS, 0);
kfree_skb_reason(skb, reason);
return;
@@ -2655,11 +2670,12 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
DEV_STATS_INC(dev, tx_carrier_errors);
dst_release(ndst);
DEV_STATS_INC(dev, tx_errors);
- vxlan_vnifilter_count(vxlan, vni, NULL, VXLAN_VNI_STATS_TX_ERRORS, 0);
+ vxlan_vnifilter_count(vxlan, cfg, vni, NULL, VXLAN_VNI_STATS_TX_ERRORS, 0);
kfree_skb_reason(skb, reason);
}
static void vxlan_xmit_nh(struct sk_buff *skb, struct net_device *dev,
+ const struct vxlan_config *cfg,
struct vxlan_fdb *f, __be32 vni, bool did_rsc)
{
struct vxlan_rdst nh_rdst;
@@ -2676,7 +2692,7 @@ static void vxlan_xmit_nh(struct sk_buff *skb, struct net_device *dev,
do_xmit = vxlan_fdb_nh_path_select(nh, hash, &nh_rdst);
if (likely(do_xmit))
- vxlan_xmit_one(skb, dev, vni, &nh_rdst, did_rsc);
+ vxlan_xmit_one(skb, dev, cfg, vni, &nh_rdst, did_rsc);
else
goto drop;
@@ -2684,15 +2700,15 @@ static void vxlan_xmit_nh(struct sk_buff *skb, struct net_device *dev,
drop:
dev_dstats_tx_dropped(dev);
- vxlan_vnifilter_count(netdev_priv(dev), vni, NULL,
+ vxlan_vnifilter_count(netdev_priv(dev), cfg, vni, NULL,
VXLAN_VNI_STATS_TX_DROPS, 0);
dev_kfree_skb(skb);
}
static netdev_tx_t vxlan_xmit_nhid(struct sk_buff *skb, struct net_device *dev,
- u32 nhid, __be32 vni)
+ u32 nhid, __be32 vni, int saddr_family,
+ const struct vxlan_config *cfg)
{
- struct vxlan_dev *vxlan = netdev_priv(dev);
struct vxlan_rdst nh_rdst;
struct nexthop *nh;
bool do_xmit;
@@ -2710,11 +2726,11 @@ static netdev_tx_t vxlan_xmit_nhid(struct sk_buff *skb, struct net_device *dev,
do_xmit = vxlan_fdb_nh_path_select(nh, hash, &nh_rdst);
rcu_read_unlock();
- if (vxlan->cfg.saddr.sa.sa_family != nh_rdst.remote_ip.sa.sa_family)
+ if (saddr_family != nh_rdst.remote_ip.sa.sa_family)
goto drop;
if (likely(do_xmit))
- vxlan_xmit_one(skb, dev, vni, &nh_rdst, false);
+ vxlan_xmit_one(skb, dev, cfg, vni, &nh_rdst, false);
else
goto drop;
@@ -2722,7 +2738,7 @@ static netdev_tx_t vxlan_xmit_nhid(struct sk_buff *skb, struct net_device *dev,
drop:
dev_dstats_tx_dropped(dev);
- vxlan_vnifilter_count(netdev_priv(dev), vni, NULL,
+ vxlan_vnifilter_count(netdev_priv(dev), cfg, vni, NULL,
VXLAN_VNI_STATS_TX_DROPS, 0);
dev_kfree_skb(skb);
return NETDEV_TX_OK;
@@ -2739,34 +2755,43 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
struct vxlan_dev *vxlan = netdev_priv(dev);
struct vxlan_rdst *rdst, *fdst = NULL;
const struct ip_tunnel_info *info;
+ const struct vxlan_config *cfg;
+ __be32 default_vni;
struct vxlan_fdb *f;
struct ethhdr *eth;
+ int saddr_family;
__be32 vni = 0;
- u32 nhid = 0;
bool did_rsc;
+ u32 nhid = 0;
+ u32 flags;
+
+ cfg = &vxlan->cfg;
+ flags = cfg->flags;
+ default_vni = cfg->vni;
+ saddr_family = cfg->saddr.sa.sa_family;
info = skb_tunnel_info(skb);
skb_reset_mac_header(skb);
- if (vxlan->cfg.flags & VXLAN_F_COLLECT_METADATA) {
+ if (flags & VXLAN_F_COLLECT_METADATA) {
if (info && info->mode & IP_TUNNEL_INFO_BRIDGE &&
info->mode & IP_TUNNEL_INFO_TX) {
vni = tunnel_id_to_key32(info->key.tun_id);
nhid = info->key.nhid;
} else {
if (info && info->mode & IP_TUNNEL_INFO_TX)
- vxlan_xmit_one(skb, dev, vni, NULL, false);
+ vxlan_xmit_one(skb, dev, cfg, vni, NULL, false);
else
kfree_skb_reason(skb, SKB_DROP_REASON_TUNNEL_TXINFO);
return NETDEV_TX_OK;
}
}
- if (vxlan->cfg.flags & VXLAN_F_PROXY) {
+ if (flags & VXLAN_F_PROXY) {
eth = eth_hdr(skb);
if (ntohs(eth->h_proto) == ETH_P_ARP)
- return arp_reduce(dev, skb, vni);
+ return arp_reduce(dev, skb, vni, flags);
#if IS_ENABLED(CONFIG_IPV6)
else if (ntohs(eth->h_proto) == ETH_P_IPV6 &&
pskb_network_may_pull(skb, sizeof(struct ipv6hdr) +
@@ -2776,23 +2801,23 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
if (m->icmph.icmp6_code == 0 &&
m->icmph.icmp6_type == NDISC_NEIGHBOUR_SOLICITATION)
- return neigh_reduce(dev, skb, vni);
+ return neigh_reduce(dev, skb, vni, flags);
}
#endif
}
if (nhid)
- return vxlan_xmit_nhid(skb, dev, nhid, vni);
+ return vxlan_xmit_nhid(skb, dev, nhid, vni, saddr_family, cfg);
- if (vxlan->cfg.flags & VXLAN_F_MDB) {
+ if (flags & VXLAN_F_MDB) {
struct vxlan_mdb_entry *mdb_entry;
rcu_read_lock();
- mdb_entry = vxlan_mdb_entry_skb_get(vxlan, skb, vni);
+ mdb_entry = vxlan_mdb_entry_skb_get(vxlan, cfg, skb, vni);
if (mdb_entry) {
netdev_tx_t ret;
- ret = vxlan_mdb_xmit(vxlan, mdb_entry, skb);
+ ret = vxlan_mdb_xmit(vxlan, cfg, mdb_entry, skb);
rcu_read_unlock();
return ret;
}
@@ -2801,27 +2826,27 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
eth = eth_hdr(skb);
rcu_read_lock();
- f = vxlan_find_mac_tx(vxlan, eth->h_dest, vni);
+ f = vxlan_find_mac_tx(vxlan, cfg, eth->h_dest, vni);
did_rsc = false;
- if (f && (f->flags & NTF_ROUTER) && (vxlan->cfg.flags & VXLAN_F_RSC) &&
+ if (f && (f->flags & NTF_ROUTER) && (flags & VXLAN_F_RSC) &&
(ntohs(eth->h_proto) == ETH_P_IP ||
ntohs(eth->h_proto) == ETH_P_IPV6)) {
- did_rsc = route_shortcircuit(dev, skb);
+ did_rsc = route_shortcircuit(dev, skb, cfg);
eth = eth_hdr(skb);
if (did_rsc)
- f = vxlan_find_mac_tx(vxlan, eth->h_dest, vni);
+ f = vxlan_find_mac_tx(vxlan, cfg, eth->h_dest, vni);
}
if (f == NULL) {
- f = vxlan_find_mac_tx(vxlan, all_zeros_mac, vni);
+ f = vxlan_find_mac_tx(vxlan, cfg, all_zeros_mac, vni);
if (f == NULL) {
- if ((vxlan->cfg.flags & VXLAN_F_L2MISS) &&
+ if ((flags & VXLAN_F_L2MISS) &&
!is_multicast_ether_addr(eth->h_dest))
vxlan_fdb_miss(vxlan, eth->h_dest);
dev_dstats_tx_dropped(dev);
- vxlan_vnifilter_count(vxlan, vni, NULL,
+ vxlan_vnifilter_count(vxlan, cfg, vni, NULL,
VXLAN_VNI_STATS_TX_DROPS, 0);
kfree_skb_reason(skb, SKB_DROP_REASON_NO_TX_TARGET);
goto out;
@@ -2829,8 +2854,8 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
}
if (rcu_access_pointer(f->nh)) {
- vxlan_xmit_nh(skb, dev, f,
- (vni ? : vxlan->default_dst.remote_vni), did_rsc);
+ vxlan_xmit_nh(skb, dev, cfg, f,
+ (vni ? : default_vni), did_rsc);
} else {
list_for_each_entry_rcu(rdst, &f->remotes, list) {
struct sk_buff *skb1;
@@ -2841,10 +2866,10 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
}
skb1 = skb_clone(skb, GFP_ATOMIC);
if (skb1)
- vxlan_xmit_one(skb1, dev, vni, rdst, did_rsc);
+ vxlan_xmit_one(skb1, dev, cfg, vni, rdst, did_rsc);
}
if (fdst)
- vxlan_xmit_one(skb, dev, vni, fdst, did_rsc);
+ vxlan_xmit_one(skb, dev, cfg, vni, fdst, did_rsc);
else
kfree_skb_reason(skb, SKB_DROP_REASON_NO_TX_TARGET);
}
@@ -3717,7 +3742,7 @@ static int vxlan_sock_add(struct vxlan_dev *vxlan)
}
int vxlan_vni_in_use(struct net *src_net, struct vxlan_dev *vxlan,
- struct vxlan_config *conf, __be32 vni)
+ const struct vxlan_config *conf, __be32 vni)
{
struct vxlan_net *vn = net_generic(src_net, vxlan_net_id);
struct vxlan_dev *tmp;
@@ -4579,10 +4604,10 @@ static int vxlan_fill_info(struct sk_buff *skb, const struct net_device *dev)
{
const struct vxlan_dev *vxlan = netdev_priv(dev);
const struct vxlan_rdst *dst = &vxlan->default_dst;
- struct ifla_vxlan_port_range ports = {
- .low = htons(vxlan->cfg.port_min),
- .high = htons(vxlan->cfg.port_max),
- };
+ struct ifla_vxlan_port_range ports;
+ const struct vxlan_config *cfg;
+
+ cfg = &vxlan->cfg;
if (nla_put_u32(skb, IFLA_VXLAN_ID, be32_to_cpu(dst->remote_vni)))
goto nla_put_failure;
@@ -4604,79 +4629,81 @@ static int vxlan_fill_info(struct sk_buff *skb, const struct net_device *dev)
if (dst->remote_ifindex && nla_put_u32(skb, IFLA_VXLAN_LINK, dst->remote_ifindex))
goto nla_put_failure;
- if (!vxlan_addr_any(&vxlan->cfg.saddr)) {
- if (vxlan->cfg.saddr.sa.sa_family == AF_INET) {
+ if (!vxlan_addr_any(&cfg->saddr)) {
+ if (cfg->saddr.sa.sa_family == AF_INET) {
if (nla_put_in_addr(skb, IFLA_VXLAN_LOCAL,
- vxlan->cfg.saddr.sin.sin_addr.s_addr))
+ cfg->saddr.sin.sin_addr.s_addr))
goto nla_put_failure;
#if IS_ENABLED(CONFIG_IPV6)
} else {
if (nla_put_in6_addr(skb, IFLA_VXLAN_LOCAL6,
- &vxlan->cfg.saddr.sin6.sin6_addr))
+ &cfg->saddr.sin6.sin6_addr))
goto nla_put_failure;
#endif
}
}
- if (nla_put_u8(skb, IFLA_VXLAN_TTL, vxlan->cfg.ttl) ||
+ if (nla_put_u8(skb, IFLA_VXLAN_TTL, cfg->ttl) ||
nla_put_u8(skb, IFLA_VXLAN_TTL_INHERIT,
- !!(vxlan->cfg.flags & VXLAN_F_TTL_INHERIT)) ||
- nla_put_u8(skb, IFLA_VXLAN_TOS, vxlan->cfg.tos) ||
- nla_put_u8(skb, IFLA_VXLAN_DF, vxlan->cfg.df) ||
- nla_put_be32(skb, IFLA_VXLAN_LABEL, vxlan->cfg.label) ||
- nla_put_u32(skb, IFLA_VXLAN_LABEL_POLICY, vxlan->cfg.label_policy) ||
+ !!(cfg->flags & VXLAN_F_TTL_INHERIT)) ||
+ nla_put_u8(skb, IFLA_VXLAN_TOS, cfg->tos) ||
+ nla_put_u8(skb, IFLA_VXLAN_DF, cfg->df) ||
+ nla_put_be32(skb, IFLA_VXLAN_LABEL, cfg->label) ||
+ nla_put_u32(skb, IFLA_VXLAN_LABEL_POLICY, cfg->label_policy) ||
nla_put_u8(skb, IFLA_VXLAN_LEARNING,
- !!(vxlan->cfg.flags & VXLAN_F_LEARN)) ||
+ !!(cfg->flags & VXLAN_F_LEARN)) ||
nla_put_u8(skb, IFLA_VXLAN_PROXY,
- !!(vxlan->cfg.flags & VXLAN_F_PROXY)) ||
+ !!(cfg->flags & VXLAN_F_PROXY)) ||
nla_put_u8(skb, IFLA_VXLAN_RSC,
- !!(vxlan->cfg.flags & VXLAN_F_RSC)) ||
+ !!(cfg->flags & VXLAN_F_RSC)) ||
nla_put_u8(skb, IFLA_VXLAN_L2MISS,
- !!(vxlan->cfg.flags & VXLAN_F_L2MISS)) ||
+ !!(cfg->flags & VXLAN_F_L2MISS)) ||
nla_put_u8(skb, IFLA_VXLAN_L3MISS,
- !!(vxlan->cfg.flags & VXLAN_F_L3MISS)) ||
+ !!(cfg->flags & VXLAN_F_L3MISS)) ||
nla_put_u8(skb, IFLA_VXLAN_COLLECT_METADATA,
- !!(vxlan->cfg.flags & VXLAN_F_COLLECT_METADATA)) ||
- nla_put_u32(skb, IFLA_VXLAN_AGEING, vxlan->cfg.age_interval) ||
- nla_put_u32(skb, IFLA_VXLAN_LIMIT, vxlan->cfg.addrmax) ||
- nla_put_be16(skb, IFLA_VXLAN_PORT, vxlan->cfg.dst_port) ||
+ !!(cfg->flags & VXLAN_F_COLLECT_METADATA)) ||
+ nla_put_u32(skb, IFLA_VXLAN_AGEING, cfg->age_interval) ||
+ nla_put_u32(skb, IFLA_VXLAN_LIMIT, cfg->addrmax) ||
+ nla_put_be16(skb, IFLA_VXLAN_PORT, cfg->dst_port) ||
nla_put_u8(skb, IFLA_VXLAN_UDP_CSUM,
- !(vxlan->cfg.flags & VXLAN_F_UDP_ZERO_CSUM_TX)) ||
+ !(cfg->flags & VXLAN_F_UDP_ZERO_CSUM_TX)) ||
nla_put_u8(skb, IFLA_VXLAN_UDP_ZERO_CSUM6_TX,
- !!(vxlan->cfg.flags & VXLAN_F_UDP_ZERO_CSUM6_TX)) ||
+ !!(cfg->flags & VXLAN_F_UDP_ZERO_CSUM6_TX)) ||
nla_put_u8(skb, IFLA_VXLAN_UDP_ZERO_CSUM6_RX,
- !!(vxlan->cfg.flags & VXLAN_F_UDP_ZERO_CSUM6_RX)) ||
+ !!(cfg->flags & VXLAN_F_UDP_ZERO_CSUM6_RX)) ||
nla_put_u8(skb, IFLA_VXLAN_REMCSUM_TX,
- !!(vxlan->cfg.flags & VXLAN_F_REMCSUM_TX)) ||
+ !!(cfg->flags & VXLAN_F_REMCSUM_TX)) ||
nla_put_u8(skb, IFLA_VXLAN_REMCSUM_RX,
- !!(vxlan->cfg.flags & VXLAN_F_REMCSUM_RX)) ||
+ !!(cfg->flags & VXLAN_F_REMCSUM_RX)) ||
nla_put_u8(skb, IFLA_VXLAN_LOCALBYPASS,
- !!(vxlan->cfg.flags & VXLAN_F_LOCALBYPASS)))
+ !!(cfg->flags & VXLAN_F_LOCALBYPASS)))
goto nla_put_failure;
+ ports.low = htons(cfg->port_min);
+ ports.high = htons(cfg->port_max);
if (nla_put(skb, IFLA_VXLAN_PORT_RANGE, sizeof(ports), &ports))
goto nla_put_failure;
- if (vxlan->cfg.flags & VXLAN_F_GBP &&
+ if (cfg->flags & VXLAN_F_GBP &&
nla_put_flag(skb, IFLA_VXLAN_GBP))
goto nla_put_failure;
- if (vxlan->cfg.flags & VXLAN_F_GPE &&
+ if (cfg->flags & VXLAN_F_GPE &&
nla_put_flag(skb, IFLA_VXLAN_GPE))
goto nla_put_failure;
- if (vxlan->cfg.flags & VXLAN_F_REMCSUM_NOPARTIAL &&
+ if (cfg->flags & VXLAN_F_REMCSUM_NOPARTIAL &&
nla_put_flag(skb, IFLA_VXLAN_REMCSUM_NOPARTIAL))
goto nla_put_failure;
- if (vxlan->cfg.flags & VXLAN_F_VNIFILTER &&
+ if (cfg->flags & VXLAN_F_VNIFILTER &&
nla_put_u8(skb, IFLA_VXLAN_VNIFILTER,
- !!(vxlan->cfg.flags & VXLAN_F_VNIFILTER)))
+ !!(cfg->flags & VXLAN_F_VNIFILTER)))
goto nla_put_failure;
if (nla_put(skb, IFLA_VXLAN_RESERVED_BITS,
- sizeof(vxlan->cfg.reserved_bits),
- &vxlan->cfg.reserved_bits))
+ sizeof(cfg->reserved_bits),
+ &cfg->reserved_bits))
goto nla_put_failure;
return 0;
diff --git a/drivers/net/vxlan/vxlan_mdb.c b/drivers/net/vxlan/vxlan_mdb.c
index d71e1925ecfdbd6fa7b78b38cb4168b51c85a553..6e38acbc8fea9e076aa6c50db1d273aeb65788e4 100644
--- a/drivers/net/vxlan/vxlan_mdb.c
+++ b/drivers/net/vxlan/vxlan_mdb.c
@@ -165,6 +165,7 @@ static int vxlan_mdb_entry_info_fill(const struct vxlan_dev *vxlan,
const struct vxlan_mdb_entry *mdb_entry,
const struct vxlan_mdb_remote *remote)
{
+ const struct vxlan_config *cfg = &vxlan->cfg;
struct vxlan_rdst *rd = rtnl_dereference(remote->rd);
struct br_mdb_entry e;
struct nlattr *nest;
@@ -189,7 +190,7 @@ static int vxlan_mdb_entry_info_fill(const struct vxlan_dev *vxlan,
vxlan_nla_put_addr(skb, MDBA_MDB_EATTR_DST, &rd->remote_ip))
goto nest_err;
- if (rd->remote_port && rd->remote_port != vxlan->cfg.dst_port &&
+ if (rd->remote_port && rd->remote_port != cfg->dst_port &&
nla_put_u16(skb, MDBA_MDB_EATTR_DST_PORT,
be16_to_cpu(rd->remote_port)))
goto nest_err;
@@ -202,7 +203,7 @@ static int vxlan_mdb_entry_info_fill(const struct vxlan_dev *vxlan,
nla_put_u32(skb, MDBA_MDB_EATTR_IFINDEX, rd->remote_ifindex))
goto nest_err;
- if ((vxlan->cfg.flags & VXLAN_F_COLLECT_METADATA) &&
+ if ((cfg->flags & VXLAN_F_COLLECT_METADATA) &&
mdb_entry->key.vni && nla_put_u32(skb, MDBA_MDB_EATTR_SRC_VNI,
be32_to_cpu(mdb_entry->key.vni)))
goto nest_err;
@@ -605,6 +606,7 @@ static int vxlan_mdb_config_init(struct vxlan_mdb_config *cfg,
{
struct br_mdb_entry *entry = nla_data(tb[MDBA_SET_ENTRY]);
struct vxlan_dev *vxlan = netdev_priv(dev);
+ const struct vxlan_config *vcfg = &vxlan->cfg;
memset(cfg, 0, sizeof(*cfg));
cfg->vxlan = vxlan;
@@ -614,7 +616,7 @@ static int vxlan_mdb_config_init(struct vxlan_mdb_config *cfg,
cfg->filter_mode = MCAST_EXCLUDE;
cfg->rt_protocol = RTPROT_STATIC;
cfg->remote_vni = vxlan->default_dst.remote_vni;
- cfg->remote_port = vxlan->cfg.dst_port;
+ cfg->remote_port = vcfg->dst_port;
if (entry->ifindex != dev->ifindex) {
NL_SET_ERR_MSG_MOD(extack, "Port net device must be the VXLAN net device");
@@ -949,6 +951,7 @@ vxlan_mdb_nlmsg_remote_size(const struct vxlan_dev *vxlan,
const struct vxlan_mdb_entry *mdb_entry,
const struct vxlan_mdb_remote *remote)
{
+ const struct vxlan_config *cfg = &vxlan->cfg;
const struct vxlan_mdb_entry_key *group = &mdb_entry->key;
struct vxlan_rdst *rd = rtnl_dereference(remote->rd);
size_t nlmsg_size;
@@ -970,7 +973,7 @@ vxlan_mdb_nlmsg_remote_size(const struct vxlan_dev *vxlan,
/* MDBA_MDB_EATTR_DST */
nlmsg_size += nla_total_size(vxlan_addr_size(&rd->remote_ip));
/* MDBA_MDB_EATTR_DST_PORT */
- if (rd->remote_port && rd->remote_port != vxlan->cfg.dst_port)
+ if (rd->remote_port && rd->remote_port != cfg->dst_port)
nlmsg_size += nla_total_size(sizeof(u16));
/* MDBA_MDB_EATTR_VNI */
if (rd->remote_vni != vxlan->default_dst.remote_vni)
@@ -979,7 +982,7 @@ vxlan_mdb_nlmsg_remote_size(const struct vxlan_dev *vxlan,
if (rd->remote_ifindex)
nlmsg_size += nla_total_size(sizeof(u32));
/* MDBA_MDB_EATTR_SRC_VNI */
- if ((vxlan->cfg.flags & VXLAN_F_COLLECT_METADATA) && group->vni)
+ if ((cfg->flags & VXLAN_F_COLLECT_METADATA) && group->vni)
nlmsg_size += nla_total_size(sizeof(u32));
return nlmsg_size;
@@ -1613,6 +1616,7 @@ int vxlan_mdb_get(struct net_device *dev, struct nlattr *tb[], u32 portid,
}
struct vxlan_mdb_entry *vxlan_mdb_entry_skb_get(struct vxlan_dev *vxlan,
+ const struct vxlan_config *cfg,
struct sk_buff *skb,
__be32 src_vni)
{
@@ -1626,7 +1630,7 @@ struct vxlan_mdb_entry *vxlan_mdb_entry_skb_get(struct vxlan_dev *vxlan,
/* When not in collect metadata mode, 'src_vni' is zero, but MDB
* entries are stored with the VNI of the VXLAN device.
*/
- if (!(vxlan->cfg.flags & VXLAN_F_COLLECT_METADATA))
+ if (!(cfg->flags & VXLAN_F_COLLECT_METADATA))
src_vni = vxlan->default_dst.remote_vni;
memset(&group, 0, sizeof(group));
@@ -1692,6 +1696,7 @@ struct vxlan_mdb_entry *vxlan_mdb_entry_skb_get(struct vxlan_dev *vxlan,
}
netdev_tx_t vxlan_mdb_xmit(struct vxlan_dev *vxlan,
+ const struct vxlan_config *cfg,
const struct vxlan_mdb_entry *mdb_entry,
struct sk_buff *skb)
{
@@ -1713,12 +1718,12 @@ netdev_tx_t vxlan_mdb_xmit(struct vxlan_dev *vxlan,
skb1 = skb_clone(skb, GFP_ATOMIC);
if (skb1)
- vxlan_xmit_one(skb1, vxlan->dev, src_vni,
+ vxlan_xmit_one(skb1, vxlan->dev, cfg, src_vni,
rcu_dereference(remote->rd), false);
}
if (fremote)
- vxlan_xmit_one(skb, vxlan->dev, src_vni,
+ vxlan_xmit_one(skb, vxlan->dev, cfg, src_vni,
rcu_dereference(fremote->rd), false);
else
kfree_skb_reason(skb, SKB_DROP_REASON_NO_TX_TARGET);
diff --git a/drivers/net/vxlan/vxlan_private.h b/drivers/net/vxlan/vxlan_private.h
index b1eec221636088aa1c1674221d5ef0f13698b53f..ab7216c4e41011b99f8bf72de50fc2230b43c405 100644
--- a/drivers/net/vxlan/vxlan_private.h
+++ b/drivers/net/vxlan/vxlan_private.h
@@ -195,9 +195,10 @@ int vxlan_fdb_update(struct vxlan_dev *vxlan,
__u32 ifindex, __u16 ndm_flags, u32 nhid,
bool swdev_notify, struct netlink_ext_ack *extack);
void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
+ const struct vxlan_config *cfg,
__be32 default_vni, struct vxlan_rdst *rdst, bool did_rsc);
int vxlan_vni_in_use(struct net *src_net, struct vxlan_dev *vxlan,
- struct vxlan_config *conf, __be32 vni);
+ const struct vxlan_config *conf, __be32 vni);
/* vxlan_vnifilter.c */
int vxlan_vnigroup_init(struct vxlan_dev *vxlan);
@@ -205,7 +206,8 @@ void vxlan_vnigroup_uninit(struct vxlan_dev *vxlan);
int vxlan_vnifilter_init(void);
void vxlan_vnifilter_uninit(void);
-void vxlan_vnifilter_count(struct vxlan_dev *vxlan, __be32 vni,
+void vxlan_vnifilter_count(struct vxlan_dev *vxlan,
+ const struct vxlan_config *cfg, __be32 vni,
struct vxlan_vni_node *vninode,
int type, unsigned int len);
@@ -241,9 +243,11 @@ int vxlan_mdb_del_bulk(struct net_device *dev, struct nlattr *tb[],
int vxlan_mdb_get(struct net_device *dev, struct nlattr *tb[], u32 portid,
u32 seq, struct netlink_ext_ack *extack);
struct vxlan_mdb_entry *vxlan_mdb_entry_skb_get(struct vxlan_dev *vxlan,
+ const struct vxlan_config *cfg,
struct sk_buff *skb,
__be32 src_vni);
netdev_tx_t vxlan_mdb_xmit(struct vxlan_dev *vxlan,
+ const struct vxlan_config *cfg,
const struct vxlan_mdb_entry *mdb_entry,
struct sk_buff *skb);
int vxlan_mdb_init(struct vxlan_dev *vxlan);
diff --git a/drivers/net/vxlan/vxlan_vnifilter.c b/drivers/net/vxlan/vxlan_vnifilter.c
index 53213542fa3ecf67d66ba6bf41f7ac51cf8fb471..c46d5716493695b0651570a2686c6b0a150f61e6 100644
--- a/drivers/net/vxlan/vxlan_vnifilter.c
+++ b/drivers/net/vxlan/vxlan_vnifilter.c
@@ -171,13 +171,14 @@ static void vxlan_vnifilter_stats_add(struct vxlan_vni_node *vninode,
u64_stats_update_end(&pstats->syncp);
}
-void vxlan_vnifilter_count(struct vxlan_dev *vxlan, __be32 vni,
+void vxlan_vnifilter_count(struct vxlan_dev *vxlan,
+ const struct vxlan_config *cfg, __be32 vni,
struct vxlan_vni_node *vninode,
int type, unsigned int len)
{
struct vxlan_vni_node *vnode;
- if (!(vxlan->cfg.flags & VXLAN_F_VNIFILTER))
+ if (!cfg || !(cfg->flags & VXLAN_F_VNIFILTER))
return;
if (vninode) {
--
2.55.0.970.g62bdec98f9-goog
^ permalink raw reply related [flat|nested] 28+ messages in thread* Re: [PATCH net-next 4/9] vxlan: pass vxlan_config pointer to helper functions
2026-09-03 12:08 ` [PATCH net-next 4/9] vxlan: pass vxlan_config pointer to helper functions Eric Dumazet
@ 2026-09-05 4:09 ` Kuniyuki Iwashima
0 siblings, 0 replies; 28+ messages in thread
From: Kuniyuki Iwashima @ 2026-09-05 4:09 UTC (permalink / raw)
To: Eric Dumazet
Cc: David S . Miller, Jakub Kicinski, Paolo Abeni, Simon Horman,
Ido Schimmel, Andrew Lunn, netdev, eric.dumazet
On Thu, Sep 3, 2026 at 5:08 AM Eric Dumazet <edumazet@google.com> wrote:
>
> In preparation for converting vxlan->cfg to an RCU-protected pointer,
> refactor internal helper functions in the RX, TX, MDB, and VNIFILTER
> paths to accept a pointer to struct vxlan_config (or pass flags/
> saddr_family where appropriate) rather than directly accessing
> vxlan->cfg.
>
> No functional changes.
>
> Signed-off-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com>
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH net-next 5/9] vxlan: move VXLAN_F_MDB to struct vxlan_dev flags
2026-09-03 12:08 [PATCH net-next 0/9] vxlan: convert configuration to RCU and drop RTNL in vxlan_fill_info() Eric Dumazet
` (3 preceding siblings ...)
2026-09-03 12:08 ` [PATCH net-next 4/9] vxlan: pass vxlan_config pointer to helper functions Eric Dumazet
@ 2026-09-03 12:08 ` Eric Dumazet
2026-09-05 4:13 ` Kuniyuki Iwashima
2026-09-07 6:11 ` netdev-bot+sashiko
2026-09-03 12:08 ` [PATCH net-next 6/9] vxlan: dynamically allocate struct vxlan_config Eric Dumazet
` (3 subsequent siblings)
8 siblings, 2 replies; 28+ messages in thread
From: Eric Dumazet @ 2026-09-03 12:08 UTC (permalink / raw)
To: David S . Miller, Jakub Kicinski, Paolo Abeni
Cc: Simon Horman, Kuniyuki Iwashima, Ido Schimmel, Andrew Lunn,
netdev, eric.dumazet, Eric Dumazet
VXLAN_F_MDB is an internal runtime state flag indicating whether any
MDB entries are configured on the device, rather than a netlink
configuration attribute.
In preparation for converting vxlan->cfg to an RCU-protected pointer,
move VXLAN_F_MDB from struct vxlan_config to a dedicated 'flags' field
in struct vxlan_dev as VXLAN_DEV_F_MDB, using atomic bitops (set_bit(),
clear_bit(), test_bit()) to avoid KCSAN data races between the TX path
and RTNL operations.
This avoids having to dynamically reallocate and publish a new
vxlan_config structure via RCU whenever the first MDB entry is added
or the last one is removed, and prevents potential memory allocation
failures during MDB teardown under memory pressure.
Signed-off-by: Eric Dumazet <edumazet@google.com>
---
drivers/net/vxlan/vxlan_core.c | 2 +-
drivers/net/vxlan/vxlan_mdb.c | 6 +++---
include/net/vxlan.h | 6 +++++-
3 files changed, 9 insertions(+), 5 deletions(-)
diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c
index 2627e26f3699ff39e7e907b6f5f8f684d2d1235a..0fcc7282e69d08c4b94ce89ce99feeee576fabff 100644
--- a/drivers/net/vxlan/vxlan_core.c
+++ b/drivers/net/vxlan/vxlan_core.c
@@ -2809,7 +2809,7 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
if (nhid)
return vxlan_xmit_nhid(skb, dev, nhid, vni, saddr_family, cfg);
- if (flags & VXLAN_F_MDB) {
+ if (test_bit(VXLAN_DEV_F_MDB, &vxlan->flags)) {
struct vxlan_mdb_entry *mdb_entry;
rcu_read_lock();
diff --git a/drivers/net/vxlan/vxlan_mdb.c b/drivers/net/vxlan/vxlan_mdb.c
index 6e38acbc8fea9e076aa6c50db1d273aeb65788e4..fe079d6abc5fcfb8bdac4d27b95dabd30aa10526 100644
--- a/drivers/net/vxlan/vxlan_mdb.c
+++ b/drivers/net/vxlan/vxlan_mdb.c
@@ -1211,7 +1211,7 @@ vxlan_mdb_entry_get(struct vxlan_dev *vxlan,
goto err_free_entry;
if (hlist_is_singular_node(&mdb_entry->mdb_node, &vxlan->mdb_list))
- vxlan->cfg.flags |= VXLAN_F_MDB;
+ set_bit(VXLAN_DEV_F_MDB, &vxlan->flags);
return mdb_entry;
@@ -1228,7 +1228,7 @@ static void vxlan_mdb_entry_put(struct vxlan_dev *vxlan,
return;
if (hlist_is_singular_node(&mdb_entry->mdb_node, &vxlan->mdb_list))
- vxlan->cfg.flags &= ~VXLAN_F_MDB;
+ clear_bit(VXLAN_DEV_F_MDB, &vxlan->flags);
rhashtable_remove_fast(&vxlan->mdb_tbl, &mdb_entry->rhnode,
vxlan_mdb_rht_params);
@@ -1754,7 +1754,7 @@ void vxlan_mdb_fini(struct vxlan_dev *vxlan)
struct vxlan_mdb_flush_desc desc = {};
vxlan_mdb_flush(vxlan, &desc);
- WARN_ON_ONCE(vxlan->cfg.flags & VXLAN_F_MDB);
+ WARN_ON_ONCE(test_bit(VXLAN_DEV_F_MDB, &vxlan->flags));
rhashtable_free_and_destroy(&vxlan->mdb_tbl, vxlan_mdb_check_empty,
NULL);
}
diff --git a/include/net/vxlan.h b/include/net/vxlan.h
index f41db72e9229d9cc8460ddbe265c6cd07890032d..f4f519a365f524e1301d769f75690d836b9e4432 100644
--- a/include/net/vxlan.h
+++ b/include/net/vxlan.h
@@ -301,6 +301,7 @@ struct vxlan_dev {
spinlock_t hash_lock;
unsigned int addrcnt;
struct gro_cells gro_cells;
+ unsigned long flags;
struct vxlan_config cfg;
@@ -314,6 +315,10 @@ struct vxlan_dev {
unsigned int mdb_seq;
};
+enum vxlan_dev_flags {
+ VXLAN_DEV_F_MDB,
+};
+
#define VXLAN_F_LEARN 0x01
#define VXLAN_F_PROXY 0x02
#define VXLAN_F_RSC 0x04
@@ -332,7 +337,6 @@ struct vxlan_dev {
#define VXLAN_F_IPV6_LINKLOCAL 0x8000
#define VXLAN_F_TTL_INHERIT 0x10000
#define VXLAN_F_VNIFILTER 0x20000
-#define VXLAN_F_MDB 0x40000
#define VXLAN_F_LOCALBYPASS 0x80000
#define VXLAN_F_MC_ROUTE 0x100000
--
2.55.0.970.g62bdec98f9-goog
^ permalink raw reply related [flat|nested] 28+ messages in thread* Re: [PATCH net-next 5/9] vxlan: move VXLAN_F_MDB to struct vxlan_dev flags
2026-09-03 12:08 ` [PATCH net-next 5/9] vxlan: move VXLAN_F_MDB to struct vxlan_dev flags Eric Dumazet
@ 2026-09-05 4:13 ` Kuniyuki Iwashima
2026-09-07 6:11 ` netdev-bot+sashiko
1 sibling, 0 replies; 28+ messages in thread
From: Kuniyuki Iwashima @ 2026-09-05 4:13 UTC (permalink / raw)
To: Eric Dumazet
Cc: David S . Miller, Jakub Kicinski, Paolo Abeni, Simon Horman,
Ido Schimmel, Andrew Lunn, netdev, eric.dumazet
On Thu, Sep 3, 2026 at 5:08 AM Eric Dumazet <edumazet@google.com> wrote:
>
> VXLAN_F_MDB is an internal runtime state flag indicating whether any
> MDB entries are configured on the device, rather than a netlink
> configuration attribute.
>
> In preparation for converting vxlan->cfg to an RCU-protected pointer,
> move VXLAN_F_MDB from struct vxlan_config to a dedicated 'flags' field
> in struct vxlan_dev as VXLAN_DEV_F_MDB, using atomic bitops (set_bit(),
> clear_bit(), test_bit()) to avoid KCSAN data races between the TX path
> and RTNL operations.
>
> This avoids having to dynamically reallocate and publish a new
> vxlan_config structure via RCU whenever the first MDB entry is added
> or the last one is removed, and prevents potential memory allocation
> failures during MDB teardown under memory pressure.
>
> Signed-off-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com>
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH net-next 5/9] vxlan: move VXLAN_F_MDB to struct vxlan_dev flags
2026-09-03 12:08 ` [PATCH net-next 5/9] vxlan: move VXLAN_F_MDB to struct vxlan_dev flags Eric Dumazet
2026-09-05 4:13 ` Kuniyuki Iwashima
@ 2026-09-07 6:11 ` netdev-bot+sashiko
1 sibling, 0 replies; 28+ messages in thread
From: netdev-bot+sashiko @ 2026-09-07 6:11 UTC (permalink / raw)
To: edumazet
Cc: davem, kuba, pabeni, horms, kuniyu, idosch, andrew+netdev, netdev,
eric.dumazet
Thank you for your contribution! Sashiko AI review found 1 potential
issue(s) to consider:
Critical: 0 · High: 0 · Medium: 1 · Low: 0
- [Medium] Moving VXLAN_F_MDB out of `struct vxlan_config.flags` into the
new `vxlan->flags` bitmap silently loosens mlxsw's VXLAN offload…
--
vxlan: move VXLAN_F_MDB to struct vxlan_dev flags
VXLAN_F_MDB is moved out of struct vxlan_config.flags into a new
unsigned long flags field in struct vxlan_dev as VXLAN_DEV_F_MDB,
manipulated with set_bit()/clear_bit()/test_bit(). This is preparation
for turning vxlan->cfg into an RCU-protected pointer.
> diff --git a/drivers/net/vxlan/vxlan_mdb.c b/drivers/net/vxlan/vxlan_mdb.c
> index 56ca92832833..cf606256d092 100644
> --- a/drivers/net/vxlan/vxlan_mdb.c
> +++ b/drivers/net/vxlan/vxlan_mdb.c
> @@ -1219,7 +1219,7 @@ vxlan_mdb_entry_get(struct vxlan_dev *vxlan,
> goto err_free_entry;
>
> if (hlist_is_singular_node(&mdb_entry->mdb_node, &vxlan->mdb_list))
> - vxlan->cfg.flags |= VXLAN_F_MDB;
> + set_bit(VXLAN_DEV_F_MDB, &vxlan->flags);
>
> return mdb_entry;
>
[ ... ]
> diff --git a/include/net/vxlan.h b/include/net/vxlan.h
> index f41db72e9229..f4f519a365f5 100644
> --- a/include/net/vxlan.h
> +++ b/include/net/vxlan.h
[ ... ]
> @@ -332,7 +337,6 @@ struct vxlan_dev {
> #define VXLAN_F_IPV6_LINKLOCAL 0x8000
> #define VXLAN_F_TTL_INHERIT 0x10000
> #define VXLAN_F_VNIFILTER 0x20000
> -#define VXLAN_F_MDB 0x40000
[Severity: Medium]
Does removing this bit from the vxlan_config.flags space also change mlxsw
offload admission? vxlan_config.flags is read outside the vxlan driver by
mlxsw, which rejects any flag it does not know about:
drivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c
#define MLXSW_SP_NVE_VXLAN_IPV4_SUPPORTED_FLAGS (VXLAN_F_UDP_ZERO_CSUM_TX | \
VXLAN_F_LEARN | \
VXLAN_F_LOCALBYPASS)
...
static bool mlxsw_sp_nve_vxlan_ipv4_flags_check(const struct vxlan_config *cfg,
struct netlink_ext_ack *extack)
{
...
if (cfg->flags & ~MLXSW_SP_NVE_VXLAN_IPV4_SUPPORTED_FLAGS) {
NL_SET_ERR_MSG_MOD(extack, "VxLAN: Unsupported flag");
return false;
}
The same catch-all exists in mlxsw_sp_nve_vxlan_ipv6_flags_check(), and both
are reached from mlxsw_sp_nve_vxlan_can_offload(), which takes its cfg
straight from netdev_priv(params->dev):
mlxsw_sp_netdevice_vxlan_event() -> mlxsw_sp_bridge_vxlan_join() ->
mlxsw_sp_nve_fid_enable() -> ops->can_offload() -> flags_check()
Before this patch, "bridge mdb add dev vxlan0 ..." made
vxlan_mdb_entry_get() set VXLAN_F_MDB (0x40000) in vxlan->cfg.flags, so a
subsequent "ip link set vxlan0 master br0" on an otherwise offloadable
device failed with the "VxLAN: Unsupported flag" extack. After the move the
bit lives in vxlan->flags, which mlxsw never inspects, so that enslavement
now succeeds.
mlxsw has no VXLAN MDB handling (vxlan_mdb.c emits no switchdev
notification), so in that ordering the MDB entries would be silently not
honoured in hardware and multicast would follow FDB-based hardware flooding
instead.
The pre-patch rejection was ordering dependent (MDB entries added after
enslavement were never revalidated, so the same unoffloaded state was
already reachable), and commit bc6c6b013ffe ("vxlan: mdb: Add an internal
flag to indicate MDB usage") describes VXLAN_F_MDB purely as an internal
data path flag, so the mlxsw behaviour looks accidental. Still, the
changelog presents this as pure preparation for the RCU cfg conversion.
Could the cross-driver effect be mentioned in the changelog, and could the
mlxsw side confirm it? Checking the end of the series (9c4524e8ff6c), the
bit stays in vxlan->flags and the mlxsw supported-flags masks are unchanged,
so no later patch addresses this.
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260903120840.1024153-1-edumazet%40google.com
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH net-next 6/9] vxlan: dynamically allocate struct vxlan_config
2026-09-03 12:08 [PATCH net-next 0/9] vxlan: convert configuration to RCU and drop RTNL in vxlan_fill_info() Eric Dumazet
` (4 preceding siblings ...)
2026-09-03 12:08 ` [PATCH net-next 5/9] vxlan: move VXLAN_F_MDB to struct vxlan_dev flags Eric Dumazet
@ 2026-09-03 12:08 ` Eric Dumazet
2026-09-05 4:29 ` Kuniyuki Iwashima
2026-09-07 6:11 ` netdev-bot+sashiko
2026-09-03 12:08 ` [PATCH net-next 7/9] vxlan: convert configuration to RCU protection Eric Dumazet
` (2 subsequent siblings)
8 siblings, 2 replies; 28+ messages in thread
From: Eric Dumazet @ 2026-09-03 12:08 UTC (permalink / raw)
To: David S . Miller, Jakub Kicinski, Paolo Abeni
Cc: Simon Horman, Kuniyuki Iwashima, Ido Schimmel, Andrew Lunn,
netdev, eric.dumazet, Eric Dumazet
Move 'struct vxlan_config' from an embedded structure inside
'struct vxlan_dev' to a dynamically allocated pointer 'vxlan->cfg'.
Updating configuration via vxlan_changelink() or vxlan_dev_configure()
now allocates a new struct vxlan_config and frees the previous one.
This prepares the ground for converting vxlan->cfg to RCU protection
in the following patch.
Signed-off-by: Eric Dumazet <edumazet@google.com>
---
.../mellanox/mlxsw/spectrum_nve_vxlan.c | 4 +-
.../mellanox/mlxsw/spectrum_switchdev.c | 16 +-
drivers/net/vxlan/vxlan_core.c | 244 ++++++++++++------
drivers/net/vxlan/vxlan_mdb.c | 6 +-
drivers/net/vxlan/vxlan_multicast.c | 6 +-
drivers/net/vxlan/vxlan_vnifilter.c | 14 +-
include/net/vxlan.h | 2 +-
7 files changed, 187 insertions(+), 105 deletions(-)
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c
index 52c2fe3644d4b9b27f1d589d9f7f597748339782..4db0efc376a8ef4d373b693240e041a708278b96 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c
@@ -60,7 +60,7 @@ static bool mlxsw_sp_nve_vxlan_can_offload(const struct mlxsw_sp_nve *nve,
struct netlink_ext_ack *extack)
{
struct vxlan_dev *vxlan = netdev_priv(params->dev);
- struct vxlan_config *cfg = &vxlan->cfg;
+ struct vxlan_config *cfg = vxlan->cfg;
if (vxlan_addr_multicast(&cfg->remote_ip)) {
NL_SET_ERR_MSG_MOD(extack, "VxLAN: Multicast destination IP is not supported");
@@ -149,7 +149,7 @@ static void mlxsw_sp_nve_vxlan_config(const struct mlxsw_sp_nve *nve,
struct mlxsw_sp_nve_config *config)
{
struct vxlan_dev *vxlan = netdev_priv(params->dev);
- struct vxlan_config *cfg = &vxlan->cfg;
+ struct vxlan_config *cfg = vxlan->cfg;
config->type = MLXSW_SP_NVE_TYPE_VXLAN;
config->ttl = cfg->ttl;
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
index fe45e533a4b2efb532b85960009c586a53ade340..a22228bd95c90ae8a481ba2f07f2a38689266b78 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
@@ -2515,7 +2515,7 @@ mlxsw_sp_bridge_vlan_aware_vxlan_join(struct mlxsw_sp_bridge_device *bridge_devi
struct vxlan_dev *vxlan = netdev_priv(vxlan_dev);
struct mlxsw_sp_nve_params params = {
.type = MLXSW_SP_NVE_TYPE_VXLAN,
- .vni = vxlan->cfg.vni,
+ .vni = vxlan->cfg->vni,
.dev = vxlan_dev,
.ethertype = ethertype,
};
@@ -2706,7 +2706,7 @@ mlxsw_sp_bridge_8021d_vxlan_join(struct mlxsw_sp_bridge_device *bridge_device,
struct vxlan_dev *vxlan = netdev_priv(vxlan_dev);
struct mlxsw_sp_nve_params params = {
.type = MLXSW_SP_NVE_TYPE_VXLAN,
- .vni = vxlan->cfg.vni,
+ .vni = vxlan->cfg->vni,
.dev = vxlan_dev,
.ethertype = ETH_P_8021Q,
};
@@ -2936,7 +2936,7 @@ static void __mlxsw_sp_bridge_vxlan_leave(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_fid *fid;
/* If the VxLAN device is down, then the FID does not have a VNI */
- fid = mlxsw_sp_fid_lookup_by_vni(mlxsw_sp, vxlan->cfg.vni);
+ fid = mlxsw_sp_fid_lookup_by_vni(mlxsw_sp, vxlan->cfg->vni);
if (!fid)
return;
@@ -3033,7 +3033,7 @@ static void mlxsw_sp_fdb_vxlan_call_notifiers(struct net_device *dev,
type = adding ? SWITCHDEV_VXLAN_FDB_ADD_TO_BRIDGE :
SWITCHDEV_VXLAN_FDB_DEL_TO_BRIDGE;
mlxsw_sp_switchdev_addr_vxlan_convert(proto, addr, &info.remote_ip);
- info.remote_port = vxlan->cfg.dst_port;
+ info.remote_port = vxlan->cfg->dst_port;
info.remote_vni = vni;
info.remote_ifindex = 0;
ether_addr_copy(info.eth_addr, mac);
@@ -3237,7 +3237,7 @@ __mlxsw_sp_fdb_notify_mac_uc_tunnel_process(struct mlxsw_sp *mlxsw_sp,
if (adding && netif_is_vxlan(dev)) {
struct vxlan_dev *vxlan = netdev_priv(dev);
- if (!(vxlan->cfg.flags & VXLAN_F_LEARN))
+ if (!(vxlan->cfg->flags & VXLAN_F_LEARN))
return -EINVAL;
}
@@ -3722,7 +3722,7 @@ mlxsw_sp_switchdev_vxlan_work_prepare(struct mlxsw_sp_switchdev_event_work *
{
struct vxlan_dev *vxlan = netdev_priv(switchdev_work->dev);
struct switchdev_notifier_vxlan_fdb_info *vxlan_fdb_info;
- struct vxlan_config *cfg = &vxlan->cfg;
+ struct vxlan_config *cfg = vxlan->cfg;
struct netlink_ext_ack *extack;
extack = switchdev_notifier_info_to_extack(info);
@@ -3851,7 +3851,7 @@ mlxsw_sp_switchdev_vxlan_vlan_add(struct mlxsw_sp *mlxsw_sp,
struct netlink_ext_ack *extack)
{
struct vxlan_dev *vxlan = netdev_priv(vxlan_dev);
- __be32 vni = vxlan->cfg.vni;
+ __be32 vni = vxlan->cfg->vni;
struct mlxsw_sp_fid *fid;
u16 old_vid;
int err;
@@ -3935,7 +3935,7 @@ mlxsw_sp_switchdev_vxlan_vlan_del(struct mlxsw_sp *mlxsw_sp,
const struct net_device *vxlan_dev, u16 vid)
{
struct vxlan_dev *vxlan = netdev_priv(vxlan_dev);
- __be32 vni = vxlan->cfg.vni;
+ __be32 vni = vxlan->cfg->vni;
struct mlxsw_sp_fid *fid;
if (!netif_running(vxlan_dev))
diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c
index 0fcc7282e69d08c4b94ce89ce99feeee576fabff..af320173a1e7f598dbe81a90beb0e68c7ed8c482 100644
--- a/drivers/net/vxlan/vxlan_core.c
+++ b/drivers/net/vxlan/vxlan_core.c
@@ -110,20 +110,23 @@ static struct vxlan_dev *vxlan_vs_find_vni(struct vxlan_sock *vs,
vni = 0;
hlist_for_each_entry_rcu(node, vni_head(vs, vni), hlist) {
+ const struct vxlan_config *cfg;
+
if (!node->vxlan)
continue;
+
+ cfg = node->vxlan->cfg;
+
vnode = NULL;
- if (node->vxlan->cfg.flags & VXLAN_F_VNIFILTER) {
+ if (cfg->flags & VXLAN_F_VNIFILTER) {
vnode = vxlan_vnifilter_lookup(node->vxlan, vni);
if (!vnode)
continue;
- } else if (node->vxlan->default_dst.remote_vni != vni) {
+ } else if (cfg->vni != vni) {
continue;
}
if (IS_ENABLED(CONFIG_IPV6)) {
- const struct vxlan_config *cfg = &node->vxlan->cfg;
-
if ((cfg->flags & VXLAN_F_IPV6_LINKLOCAL) &&
cfg->remote_ifindex != ifindex)
continue;
@@ -157,6 +160,7 @@ static int vxlan_fdb_info(struct sk_buff *skb, struct vxlan_dev *vxlan,
u32 portid, u32 seq, int type, unsigned int flags,
const struct vxlan_rdst *rdst)
{
+ const struct vxlan_config *cfg = vxlan->cfg;
unsigned long now = jiffies;
struct nda_cacheinfo ci;
bool send_ip, send_eth;
@@ -216,10 +220,10 @@ static int vxlan_fdb_info(struct sk_buff *skb, struct vxlan_dev *vxlan,
goto nla_put_failure;
if (rdst->remote_port &&
- rdst->remote_port != vxlan->cfg.dst_port &&
+ rdst->remote_port != cfg->dst_port &&
nla_put_be16(skb, NDA_PORT, rdst->remote_port))
goto nla_put_failure;
- if (rdst->remote_vni != vxlan->default_dst.remote_vni &&
+ if (rdst->remote_vni != cfg->vni &&
nla_put_u32(skb, NDA_VNI, be32_to_cpu(rdst->remote_vni)))
goto nla_put_failure;
if (rdst->remote_ifindex &&
@@ -227,7 +231,7 @@ static int vxlan_fdb_info(struct sk_buff *skb, struct vxlan_dev *vxlan,
goto nla_put_failure;
}
- if ((vxlan->cfg.flags & VXLAN_F_COLLECT_METADATA) && fdb->key.vni &&
+ if ((cfg->flags & VXLAN_F_COLLECT_METADATA) && fdb->key.vni &&
nla_put_u32(skb, NDA_SRC_VNI,
be32_to_cpu(fdb->key.vni)))
goto nla_put_failure;
@@ -418,7 +422,7 @@ static struct vxlan_fdb *vxlan_find_mac(struct vxlan_dev *vxlan,
lockdep_assert_held_once(&vxlan->hash_lock);
rcu_read_lock();
- f = vxlan_find_mac_rcu(vxlan, &vxlan->cfg, mac, vni);
+ f = vxlan_find_mac_rcu(vxlan, vxlan->cfg, mac, vni);
rcu_read_unlock();
return f;
@@ -459,7 +463,7 @@ int vxlan_fdb_find_uc(struct net_device *dev, const u8 *mac, __be32 vni,
rcu_read_lock();
- f = vxlan_find_mac_rcu(vxlan, &vxlan->cfg, eth_addr, vni);
+ f = vxlan_find_mac_rcu(vxlan, vxlan->cfg, eth_addr, vni);
if (f)
rdst = first_remote_rcu(f);
if (!rdst) {
@@ -865,12 +869,13 @@ int vxlan_fdb_create(struct vxlan_dev *vxlan,
u32 nhid, struct vxlan_fdb **fdb,
struct netlink_ext_ack *extack)
{
+ const struct vxlan_config *cfg = vxlan->cfg;
struct vxlan_rdst *rd = NULL;
struct vxlan_fdb *f;
int rc;
- if (vxlan->cfg.addrmax &&
- vxlan->addrcnt >= vxlan->cfg.addrmax)
+ if (cfg->addrmax &&
+ vxlan->addrcnt >= cfg->addrmax)
return -ENOSPC;
netdev_dbg(vxlan->dev, "add %pM -> %pIS\n", mac, ip);
@@ -1150,6 +1155,7 @@ static int vxlan_fdb_parse(struct nlattr *tb[], struct vxlan_dev *vxlan,
__be32 *vni, u32 *ifindex, u32 *nhid,
struct netlink_ext_ack *extack)
{
+ const struct vxlan_config *cfg = vxlan->cfg;
struct net *net = dev_net(vxlan->dev);
int err;
@@ -1166,7 +1172,7 @@ static int vxlan_fdb_parse(struct nlattr *tb[], struct vxlan_dev *vxlan,
return err;
}
} else {
- union vxlan_addr *remote = &vxlan->default_dst.remote_ip;
+ const union vxlan_addr *remote = &cfg->remote_ip;
if (remote->sa.sa_family == AF_INET) {
ip->sin.sin_addr.s_addr = htonl(INADDR_ANY);
@@ -1186,7 +1192,7 @@ static int vxlan_fdb_parse(struct nlattr *tb[], struct vxlan_dev *vxlan,
}
*port = nla_get_be16(tb[NDA_PORT]);
} else {
- *port = vxlan->cfg.dst_port;
+ *port = cfg->dst_port;
}
if (tb[NDA_VNI]) {
@@ -1196,7 +1202,7 @@ static int vxlan_fdb_parse(struct nlattr *tb[], struct vxlan_dev *vxlan,
}
*vni = cpu_to_be32(nla_get_u32(tb[NDA_VNI]));
} else {
- *vni = vxlan->default_dst.remote_vni;
+ *vni = cfg->vni;
}
if (tb[NDA_SRC_VNI]) {
@@ -1206,7 +1212,7 @@ static int vxlan_fdb_parse(struct nlattr *tb[], struct vxlan_dev *vxlan,
}
*src_vni = cpu_to_be32(nla_get_u32(tb[NDA_SRC_VNI]));
} else {
- *src_vni = vxlan->default_dst.remote_vni;
+ *src_vni = cfg->vni;
}
if (tb[NDA_IFINDEX]) {
@@ -1396,18 +1402,23 @@ static int vxlan_fdb_get(struct sk_buff *skb,
struct netlink_ext_ack *extack)
{
struct vxlan_dev *vxlan = netdev_priv(dev);
+ const struct vxlan_config *cfg;
struct vxlan_fdb *f;
__be32 vni;
int err;
+ cfg = vxlan->cfg;
+
if (tb[NDA_VNI])
vni = cpu_to_be32(nla_get_u32(tb[NDA_VNI]));
+ else if (cfg)
+ vni = cfg->vni;
else
- vni = vxlan->default_dst.remote_vni;
+ return -ENODEV;
rcu_read_lock();
- f = vxlan_find_mac_rcu(vxlan, &vxlan->cfg, addr, vni);
+ f = vxlan_find_mac_rcu(vxlan, cfg, addr, vni);
if (!f) {
NL_SET_ERR_MSG(extack, "Fdb entry not found");
err = -ENOENT;
@@ -1510,6 +1521,7 @@ static bool __vxlan_sock_release_prep(struct vxlan_sock *vs)
static void vxlan_sock_release(struct vxlan_dev *vxlan)
{
+ const struct vxlan_config *cfg = vxlan->cfg;
struct vxlan_sock *sock4 = rtnl_dereference(vxlan->vn4_sock);
#if IS_ENABLED(CONFIG_IPV6)
struct vxlan_sock *sock6 = rtnl_dereference(vxlan->vn6_sock);
@@ -1519,7 +1531,7 @@ static void vxlan_sock_release(struct vxlan_dev *vxlan)
RCU_INIT_POINTER(vxlan->vn4_sock, NULL);
- if (vxlan->cfg.flags & VXLAN_F_VNIFILTER)
+ if (cfg && (cfg->flags & VXLAN_F_VNIFILTER))
vxlan_vs_del_vnigrp(vxlan);
else
vxlan_vs_del_dev(vxlan);
@@ -1692,7 +1704,8 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
goto drop;
}
- cfg = &vxlan->cfg;
+ cfg = vxlan->cfg;
+
if (vh->vx_flags & cfg->reserved_bits.vx_flags ||
vh->vx_vni & cfg->reserved_bits.vx_vni) {
/* If the header uses bits besides those enabled by the
@@ -1848,7 +1861,8 @@ static int vxlan_err_lookup(struct sock *sk, struct sk_buff *skb)
return 0;
}
-static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni, u32 flags)
+static int arp_reduce(struct net_device *dev, struct sk_buff *skb,
+ const struct vxlan_config *cfg, __be32 vni)
{
struct vxlan_dev *vxlan = netdev_priv(dev);
struct arphdr *parp;
@@ -1861,7 +1875,7 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni, u
if (!pskb_network_may_pull(skb, arp_hdr_len(dev))) {
dev_dstats_tx_dropped(dev);
- vxlan_vnifilter_count(vxlan, &vxlan->cfg, vni, NULL,
+ vxlan_vnifilter_count(vxlan, cfg, vni, NULL,
VXLAN_VNI_STATS_TX_DROPS, 0);
goto out;
}
@@ -1902,7 +1916,7 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni, u
neigh_ha_snapshot(ha, n, n->dev);
rcu_read_lock();
- f = vxlan_find_mac_tx(vxlan, &vxlan->cfg, ha, vni);
+ f = vxlan_find_mac_tx(vxlan, cfg, ha, vni);
if (f)
rdst = first_remote_rcu(f);
if (rdst && vxlan_addr_any(&rdst->remote_ip)) {
@@ -1928,11 +1942,11 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni, u
if (netif_rx(reply) == NET_RX_DROP) {
dev_dstats_rx_dropped(dev);
- vxlan_vnifilter_count(vxlan, &vxlan->cfg, vni, NULL,
+ vxlan_vnifilter_count(vxlan, cfg, vni, NULL,
VXLAN_VNI_STATS_RX_DROPS, 0);
}
- } else if (flags & VXLAN_F_L3MISS) {
+ } else if (cfg->flags & VXLAN_F_L3MISS) {
union vxlan_addr ipa = {
.sin.sin_addr.s_addr = tip,
.sin.sin_family = AF_INET,
@@ -2040,7 +2054,8 @@ static struct sk_buff *vxlan_na_create(struct sk_buff *request,
return reply;
}
-static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni, u32 flags)
+static int neigh_reduce(struct net_device *dev, struct sk_buff *skb,
+ const struct vxlan_config *cfg, __be32 vni)
{
struct vxlan_dev *vxlan = netdev_priv(dev);
const struct in6_addr *daddr;
@@ -2074,7 +2089,7 @@ static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni,
}
neigh_ha_snapshot(ha, n, n->dev);
- f = vxlan_find_mac_tx(vxlan, &vxlan->cfg, ha, vni);
+ f = vxlan_find_mac_tx(vxlan, cfg, ha, vni);
if (f)
rdst = first_remote_rcu(f);
if (rdst && vxlan_addr_any(&rdst->remote_ip)) {
@@ -2093,10 +2108,10 @@ static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni,
if (netif_rx(reply) == NET_RX_DROP) {
dev_dstats_rx_dropped(dev);
- vxlan_vnifilter_count(vxlan, &vxlan->cfg, vni, NULL,
+ vxlan_vnifilter_count(vxlan, cfg, vni, NULL,
VXLAN_VNI_STATS_RX_DROPS, 0);
}
- } else if (flags & VXLAN_F_L3MISS) {
+ } else if (cfg->flags & VXLAN_F_L3MISS) {
union vxlan_addr ipa = {
.sin6.sin6_addr = msg->target,
.sin6.sin6_family = AF_INET6,
@@ -2278,7 +2293,7 @@ static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan,
const struct vxlan_config *src_cfg,
__be32 vni, bool snoop)
{
- const struct vxlan_config *dst_cfg = &dst_vxlan->cfg;
+ const struct vxlan_config *dst_cfg;
union vxlan_addr loopback;
unsigned int len = skb->len;
struct net_device *dev = dst_vxlan->dev;
@@ -2299,7 +2314,8 @@ static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan,
}
rcu_read_lock();
- if (unlikely(!(dev->flags & IFF_UP))) {
+ dst_cfg = dst_vxlan->cfg;
+ if (unlikely(!dst_cfg || !(dev->flags & IFF_UP))) {
kfree_skb_reason(skb, SKB_DROP_REASON_DEV_READY);
goto drop;
}
@@ -2765,7 +2781,7 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
u32 nhid = 0;
u32 flags;
- cfg = &vxlan->cfg;
+ cfg = vxlan->cfg;
flags = cfg->flags;
default_vni = cfg->vni;
saddr_family = cfg->saddr.sa.sa_family;
@@ -2791,7 +2807,7 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
if (flags & VXLAN_F_PROXY) {
eth = eth_hdr(skb);
if (ntohs(eth->h_proto) == ETH_P_ARP)
- return arp_reduce(dev, skb, vni, flags);
+ return arp_reduce(dev, skb, cfg, vni);
#if IS_ENABLED(CONFIG_IPV6)
else if (ntohs(eth->h_proto) == ETH_P_IPV6 &&
pskb_network_may_pull(skb, sizeof(struct ipv6hdr) +
@@ -2801,7 +2817,7 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
if (m->icmph.icmp6_code == 0 &&
m->icmph.icmp6_type == NDISC_NEIGHBOUR_SOLICITATION)
- return neigh_reduce(dev, skb, vni, flags);
+ return neigh_reduce(dev, skb, cfg, vni);
}
#endif
}
@@ -2884,12 +2900,15 @@ static void vxlan_cleanup(struct timer_list *t)
{
struct vxlan_dev *vxlan = timer_container_of(vxlan, t, age_timer);
unsigned long next_timer = jiffies + FDB_AGE_INTERVAL;
+ const struct vxlan_config *cfg;
struct vxlan_fdb *f;
if (!netif_running(vxlan->dev))
return;
rcu_read_lock();
+ cfg = vxlan->cfg;
+
hlist_for_each_entry_rcu(f, &vxlan->fdb_list, fdb_node) {
unsigned long timeout;
@@ -2899,7 +2918,7 @@ static void vxlan_cleanup(struct timer_list *t)
if (f->flags & NTF_EXT_LEARNED)
continue;
- timeout = READ_ONCE(f->updated) + vxlan->cfg.age_interval * HZ;
+ timeout = READ_ONCE(f->updated) + cfg->age_interval * HZ;
if (time_before_eq(timeout, jiffies)) {
spin_lock(&vxlan->hash_lock);
if (!hlist_unhashed(&f->fdb_node)) {
@@ -2943,13 +2962,16 @@ static void vxlan_vs_add_dev(struct vxlan_sock *vs, struct vxlan_dev *vxlan,
static int vxlan_init(struct net_device *dev)
{
struct vxlan_dev *vxlan = netdev_priv(dev);
+ const struct vxlan_config *cfg;
int err;
+ cfg = vxlan->cfg;
+
err = rhashtable_init(&vxlan->fdb_hash_tbl, &vxlan_fdb_rht_params);
if (err)
return err;
- if (vxlan->cfg.flags & VXLAN_F_VNIFILTER) {
+ if (cfg->flags & VXLAN_F_VNIFILTER) {
err = vxlan_vnigroup_init(vxlan);
if (err)
goto err_rhashtable_destroy;
@@ -2969,7 +2991,7 @@ static int vxlan_init(struct net_device *dev)
err_gro_cells_destroy:
gro_cells_destroy(&vxlan->gro_cells);
err_vnigroup_uninit:
- if (vxlan->cfg.flags & VXLAN_F_VNIFILTER)
+ if (cfg->flags & VXLAN_F_VNIFILTER)
vxlan_vnigroup_uninit(vxlan);
err_rhashtable_destroy:
rhashtable_destroy(&vxlan->fdb_hash_tbl);
@@ -2979,10 +3001,13 @@ static int vxlan_init(struct net_device *dev)
static void vxlan_uninit(struct net_device *dev)
{
struct vxlan_dev *vxlan = netdev_priv(dev);
+ const struct vxlan_config *cfg;
+
+ cfg = vxlan->cfg;
vxlan_mdb_fini(vxlan);
- if (vxlan->cfg.flags & VXLAN_F_VNIFILTER)
+ if (cfg && (cfg->flags & VXLAN_F_VNIFILTER))
vxlan_vnigroup_uninit(vxlan);
gro_cells_destroy(&vxlan->gro_cells);
@@ -2994,6 +3019,7 @@ static void vxlan_uninit(struct net_device *dev)
static int vxlan_open(struct net_device *dev)
{
struct vxlan_dev *vxlan = netdev_priv(dev);
+ const struct vxlan_config *cfg;
int ret;
ret = vxlan_sock_add(vxlan);
@@ -3006,7 +3032,8 @@ static int vxlan_open(struct net_device *dev)
return ret;
}
- if (vxlan->cfg.age_interval)
+ cfg = vxlan->cfg;
+ if (cfg && cfg->age_interval)
mod_timer(&vxlan->age_timer, jiffies + FDB_AGE_INTERVAL);
return ret;
@@ -3028,8 +3055,10 @@ struct vxlan_fdb_flush_desc {
static bool vxlan_fdb_is_default_entry(const struct vxlan_fdb *f,
const struct vxlan_dev *vxlan)
{
+ const struct vxlan_config *cfg = vxlan->cfg;
+
return is_zero_ether_addr(f->key.eth_addr) &&
- f->key.vni == vxlan->cfg.vni;
+ f->key.vni == cfg->vni;
}
static bool vxlan_fdb_nhid_matches(const struct vxlan_fdb *f, u32 nhid)
@@ -3247,14 +3276,18 @@ static int vxlan_change_mtu(struct net_device *dev, int new_mtu)
{
struct vxlan_dev *vxlan = netdev_priv(dev);
struct vxlan_rdst *dst = &vxlan->default_dst;
- struct net_device *lowerdev = __dev_get_by_index(vxlan->net,
- dst->remote_ifindex);
+ const struct vxlan_config *cfg;
+ struct net_device *lowerdev;
+
+ cfg = vxlan->cfg;
+
+ lowerdev = __dev_get_by_index(vxlan->net, dst->remote_ifindex);
/* This check is different than dev->max_mtu, because it looks at
* the lowerdev->mtu, rather than the static dev->max_mtu
*/
if (lowerdev) {
- int max_mtu = lowerdev->mtu - vxlan_headroom(vxlan->cfg.flags);
+ int max_mtu = lowerdev->mtu - vxlan_headroom(cfg->flags);
if (new_mtu > max_mtu)
return -EINVAL;
}
@@ -3267,11 +3300,14 @@ static int vxlan_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb)
{
struct vxlan_dev *vxlan = netdev_priv(dev);
struct ip_tunnel_info *info = skb_tunnel_info(skb);
+ const struct vxlan_config *cfg;
__be16 sport, dport;
- sport = udp_flow_src_port(dev_net(dev), skb, vxlan->cfg.port_min,
- vxlan->cfg.port_max, true);
- dport = info->key.tp_dst ? : vxlan->cfg.dst_port;
+ cfg = vxlan->cfg;
+
+ sport = udp_flow_src_port(dev_net(dev), skb, cfg->port_min,
+ cfg->port_max, true);
+ dport = info->key.tp_dst ? : cfg->dst_port;
if (ip_tunnel_info_af(info) == AF_INET) {
struct vxlan_sock *sock4 = rcu_dereference(vxlan->vn4_sock);
@@ -3381,6 +3417,14 @@ static void vxlan_offload_rx_ports(struct net_device *dev, bool push)
}
}
+static void vxlan_free_dev(struct net_device *dev)
+{
+ struct vxlan_dev *vxlan = netdev_priv(dev);
+
+ kfree(vxlan->cfg);
+ vxlan->cfg = NULL;
+}
+
/* Initialize the device structure. */
static void vxlan_setup(struct net_device *dev)
{
@@ -3389,6 +3433,8 @@ static void vxlan_setup(struct net_device *dev)
eth_hw_addr_random(dev);
ether_setup(dev);
+ dev->priv_destructor = vxlan_free_dev;
+
dev->needs_free_netdev = true;
SET_NETDEV_DEVTYPE(dev, &vxlan_type);
@@ -3671,21 +3717,22 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, bool ipv6,
static int __vxlan_sock_add(struct vxlan_dev *vxlan, bool ipv6)
{
- bool metadata = vxlan->cfg.flags & VXLAN_F_COLLECT_METADATA;
+ const struct vxlan_config *cfg = vxlan->cfg;
+ bool metadata = cfg->flags & VXLAN_F_COLLECT_METADATA;
struct vxlan_sock *vs = NULL;
struct vxlan_dev_node *node;
int l3mdev_index = 0;
ASSERT_RTNL();
- if (vxlan->cfg.remote_ifindex)
+ if (cfg->remote_ifindex)
l3mdev_index = l3mdev_master_upper_ifindex_by_index(
- vxlan->net, vxlan->cfg.remote_ifindex);
+ vxlan->net, cfg->remote_ifindex);
- if (!vxlan->cfg.no_share) {
+ if (!cfg->no_share) {
rcu_read_lock();
vs = vxlan_find_sock(vxlan->net, ipv6 ? AF_INET6 : AF_INET,
- vxlan->cfg.dst_port, vxlan->cfg.flags,
+ cfg->dst_port, cfg->flags,
l3mdev_index);
if (vs && !refcount_inc_not_zero(&vs->refcnt)) {
rcu_read_unlock();
@@ -3695,7 +3742,7 @@ static int __vxlan_sock_add(struct vxlan_dev *vxlan, bool ipv6)
}
if (!vs)
vs = vxlan_socket_create(vxlan->net, ipv6,
- vxlan->cfg.dst_port, vxlan->cfg.flags,
+ cfg->dst_port, cfg->flags,
l3mdev_index);
if (IS_ERR(vs))
return PTR_ERR(vs);
@@ -3710,7 +3757,7 @@ static int __vxlan_sock_add(struct vxlan_dev *vxlan, bool ipv6)
node = &vxlan->hlist4;
}
- if (metadata && (vxlan->cfg.flags & VXLAN_F_VNIFILTER))
+ if (metadata && (cfg->flags & VXLAN_F_VNIFILTER))
vxlan_vs_add_vnigrp(vxlan, vs, ipv6);
else
vxlan_vs_add_dev(vs, vxlan, node);
@@ -3720,11 +3767,14 @@ static int __vxlan_sock_add(struct vxlan_dev *vxlan, bool ipv6)
static int vxlan_sock_add(struct vxlan_dev *vxlan)
{
- bool metadata = vxlan->cfg.flags & VXLAN_F_COLLECT_METADATA;
- bool ipv6 = vxlan->cfg.flags & VXLAN_F_IPV6 || metadata;
- bool ipv4 = !ipv6 || metadata;
+ const struct vxlan_config *cfg = vxlan->cfg;
+ bool metadata, ipv6, ipv4;
int ret = 0;
+ metadata = cfg->flags & VXLAN_F_COLLECT_METADATA;
+ ipv6 = (cfg->flags & VXLAN_F_IPV6) || metadata;
+ ipv4 = !ipv6 || metadata;
+
RCU_INIT_POINTER(vxlan->vn4_sock, NULL);
#if IS_ENABLED(CONFIG_IPV6)
RCU_INIT_POINTER(vxlan->vn6_sock, NULL);
@@ -3748,22 +3798,27 @@ int vxlan_vni_in_use(struct net *src_net, struct vxlan_dev *vxlan,
struct vxlan_dev *tmp;
list_for_each_entry(tmp, &vn->vxlan_list, next) {
+ const struct vxlan_config *tmp_cfg;
+
if (tmp == vxlan)
continue;
- if (tmp->cfg.flags & VXLAN_F_VNIFILTER) {
+
+ tmp_cfg = tmp->cfg;
+
+ if (tmp_cfg->flags & VXLAN_F_VNIFILTER) {
if (!vxlan_vnifilter_lookup(tmp, vni))
continue;
- } else if (tmp->cfg.vni != vni) {
+ } else if (tmp_cfg->vni != vni) {
continue;
}
- if (tmp->cfg.dst_port != conf->dst_port)
+ if (tmp_cfg->dst_port != conf->dst_port)
continue;
- if ((tmp->cfg.flags & (VXLAN_F_RCV_FLAGS | VXLAN_F_IPV6)) !=
+ if ((tmp_cfg->flags & (VXLAN_F_RCV_FLAGS | VXLAN_F_IPV6)) !=
(conf->flags & (VXLAN_F_RCV_FLAGS | VXLAN_F_IPV6)))
continue;
if ((conf->flags & VXLAN_F_IPV6_LINKLOCAL) &&
- tmp->cfg.remote_ifindex != conf->remote_ifindex)
+ tmp_cfg->remote_ifindex != conf->remote_ifindex)
continue;
return -EEXIST;
@@ -3925,7 +3980,7 @@ static int vxlan_config_validate(struct net *src_net, struct vxlan_config *conf,
}
static void vxlan_config_apply(struct net_device *dev,
- struct vxlan_config *conf,
+ struct vxlan_config *new_cfg,
struct net_device *lowerdev,
struct net *src_net,
bool changelink)
@@ -3933,8 +3988,9 @@ static void vxlan_config_apply(struct net_device *dev,
struct vxlan_dev *vxlan = netdev_priv(dev);
struct vxlan_rdst *dst = &vxlan->default_dst;
unsigned short needed_headroom = ETH_HLEN;
+ struct vxlan_config *old_cfg;
int max_mtu = ETH_MAX_MTU;
- u32 flags = conf->flags;
+ u32 flags = new_cfg->flags;
if (!changelink) {
if (flags & VXLAN_F_GPE)
@@ -3942,18 +3998,18 @@ static void vxlan_config_apply(struct net_device *dev,
else
vxlan_ether_setup(dev);
- if (conf->mtu)
- dev->mtu = conf->mtu;
+ if (new_cfg->mtu)
+ dev->mtu = new_cfg->mtu;
vxlan->net = src_net;
}
- dst->remote_vni = conf->vni;
+ dst->remote_vni = new_cfg->vni;
- memcpy(&dst->remote_ip, &conf->remote_ip, sizeof(conf->remote_ip));
+ memcpy(&dst->remote_ip, &new_cfg->remote_ip, sizeof(new_cfg->remote_ip));
if (lowerdev) {
- dst->remote_ifindex = conf->remote_ifindex;
+ dst->remote_ifindex = new_cfg->remote_ifindex;
netif_inherit_tso_max(dev, lowerdev);
@@ -3966,7 +4022,7 @@ static void vxlan_config_apply(struct net_device *dev,
if (max_mtu < ETH_MIN_MTU)
max_mtu = ETH_MIN_MTU;
- if (!changelink && !conf->mtu)
+ if (!changelink && !new_cfg->mtu)
dev->mtu = max_mtu;
}
@@ -3978,7 +4034,9 @@ static void vxlan_config_apply(struct net_device *dev,
needed_headroom += vxlan_headroom(flags);
dev->needed_headroom = needed_headroom;
- memcpy(&vxlan->cfg, conf, sizeof(*conf));
+ old_cfg = vxlan->cfg;
+ vxlan->cfg = new_cfg;
+ kfree(old_cfg);
}
static int vxlan_dev_configure(struct net *src_net, struct net_device *dev,
@@ -3987,13 +4045,18 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev,
{
struct vxlan_dev *vxlan = netdev_priv(dev);
struct net_device *lowerdev;
+ struct vxlan_config *new_cfg;
int ret;
ret = vxlan_config_validate(src_net, conf, &lowerdev, vxlan, extack);
if (ret)
return ret;
- vxlan_config_apply(dev, conf, lowerdev, src_net, false);
+ new_cfg = kmemdup(conf, sizeof(*conf), GFP_KERNEL);
+ if (!new_cfg)
+ return -ENOMEM;
+
+ vxlan_config_apply(dev, new_cfg, lowerdev, src_net, false);
return 0;
}
@@ -4005,6 +4068,7 @@ static int vxlan_dev_create(struct net *net, struct net_device *dev,
struct vxlan_net *vn = net_generic(net, vxlan_net_id);
struct vxlan_dev *vxlan = netdev_priv(dev);
struct net_device *remote_dev = NULL;
+ const struct vxlan_config *cfg;
struct vxlan_rdst *dst;
int err;
@@ -4013,11 +4077,15 @@ static int vxlan_dev_create(struct net *net, struct net_device *dev,
if (err)
return err;
+ cfg = vxlan->cfg;
+
dev->ethtool_ops = &vxlan_ethtool_ops;
err = register_netdevice(dev);
- if (err)
+ if (err) {
+ vxlan_free_dev(dev);
return err;
+ }
if (dst->remote_ifindex) {
remote_dev = __dev_get_by_index(net, dst->remote_ifindex);
@@ -4044,7 +4112,7 @@ static int vxlan_dev_create(struct net *net, struct net_device *dev,
&dst->remote_ip,
NUD_REACHABLE | NUD_PERMANENT,
NLM_F_EXCL | NLM_F_CREATE,
- vxlan->cfg.dst_port,
+ cfg->dst_port,
dst->remote_vni,
dst->remote_vni,
dst->remote_ifindex,
@@ -4108,8 +4176,12 @@ static int vxlan_nl2conf(struct nlattr *tb[], struct nlattr *data[],
memset(conf, 0, sizeof(*conf));
/* if changelink operation, start with old existing cfg */
- if (changelink)
- memcpy(conf, &vxlan->cfg, sizeof(*conf));
+ if (changelink) {
+ const struct vxlan_config *cfg = vxlan->cfg;
+
+ if (cfg)
+ memcpy(conf, cfg, sizeof(*conf));
+ }
if (data[IFLA_VXLAN_ID]) {
__be32 vni = cpu_to_be32(nla_get_u32(data[IFLA_VXLAN_ID]));
@@ -4456,9 +4528,11 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[],
struct netlink_ext_ack *extack)
{
struct vxlan_dev *vxlan = netdev_priv(dev);
+ const struct vxlan_config *cfg = vxlan->cfg;
bool rem_ip_changed, change_igmp;
struct net_device *lowerdev;
struct vxlan_config conf;
+ struct vxlan_config *new_cfg;
struct vxlan_rdst *dst;
int err;
@@ -4475,13 +4549,19 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[],
if (err)
return err;
+ new_cfg = kmemdup(&conf, sizeof(conf), GFP_KERNEL);
+ if (!new_cfg)
+ return -ENOMEM;
+
if (dst->remote_dev == lowerdev)
lowerdev = NULL;
err = netdev_adjacent_change_prepare(dst->remote_dev, lowerdev, dev,
extack);
- if (err)
+ if (err) {
+ kfree(new_cfg);
return err;
+ }
rem_ip_changed = !vxlan_addr_equal(&conf.remote_ip, &dst->remote_ip);
change_igmp = vxlan->dev->flags & IFF_UP &&
@@ -4496,7 +4576,7 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[],
&conf.remote_ip,
NUD_REACHABLE | NUD_PERMANENT,
NLM_F_APPEND | NLM_F_CREATE,
- vxlan->cfg.dst_port,
+ cfg->dst_port,
conf.vni, conf.vni,
conf.remote_ifindex,
NTF_SELF, 0, true, extack);
@@ -4504,13 +4584,14 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[],
spin_unlock_bh(&vxlan->hash_lock);
netdev_adjacent_change_abort(dst->remote_dev,
lowerdev, dev);
+ kfree(new_cfg);
return err;
}
}
if (!vxlan_addr_any(&dst->remote_ip))
__vxlan_fdb_delete(vxlan, all_zeros_mac,
dst->remote_ip,
- vxlan->cfg.dst_port,
+ cfg->dst_port,
dst->remote_vni,
dst->remote_vni,
dst->remote_ifindex,
@@ -4520,12 +4601,13 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[],
/* If vni filtering device, also update fdb entries of
* all vnis that were using default remote ip
*/
- if (vxlan->cfg.flags & VXLAN_F_VNIFILTER) {
+ if (cfg->flags & VXLAN_F_VNIFILTER) {
err = vxlan_vnilist_update_group(vxlan, &dst->remote_ip,
&conf.remote_ip, extack);
if (err) {
netdev_adjacent_change_abort(dst->remote_dev,
lowerdev, dev);
+ kfree(new_cfg);
return err;
}
}
@@ -4534,13 +4616,13 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[],
if (change_igmp && vxlan_addr_multicast(&dst->remote_ip))
err = vxlan_multicast_leave(vxlan);
- if (netif_running(dev) && conf.age_interval != vxlan->cfg.age_interval)
+ if (netif_running(dev) && conf.age_interval != cfg->age_interval)
mod_timer(&vxlan->age_timer, jiffies);
netdev_adjacent_change_commit(dst->remote_dev, lowerdev, dev);
if (lowerdev && lowerdev != dst->remote_dev)
dst->remote_dev = lowerdev;
- vxlan_config_apply(dev, &conf, lowerdev, vxlan->net, true);
+ vxlan_config_apply(dev, new_cfg, lowerdev, vxlan->net, true);
if (!err && change_igmp &&
vxlan_addr_multicast(&dst->remote_ip))
@@ -4607,7 +4689,7 @@ static int vxlan_fill_info(struct sk_buff *skb, const struct net_device *dev)
struct ifla_vxlan_port_range ports;
const struct vxlan_config *cfg;
- cfg = &vxlan->cfg;
+ cfg = vxlan->cfg;
if (nla_put_u32(skb, IFLA_VXLAN_ID, be32_to_cpu(dst->remote_vni)))
goto nla_put_failure;
diff --git a/drivers/net/vxlan/vxlan_mdb.c b/drivers/net/vxlan/vxlan_mdb.c
index fe079d6abc5fcfb8bdac4d27b95dabd30aa10526..e34f783a192d531a657f631d59ee428945855d4f 100644
--- a/drivers/net/vxlan/vxlan_mdb.c
+++ b/drivers/net/vxlan/vxlan_mdb.c
@@ -165,7 +165,7 @@ static int vxlan_mdb_entry_info_fill(const struct vxlan_dev *vxlan,
const struct vxlan_mdb_entry *mdb_entry,
const struct vxlan_mdb_remote *remote)
{
- const struct vxlan_config *cfg = &vxlan->cfg;
+ const struct vxlan_config *cfg = vxlan->cfg;
struct vxlan_rdst *rd = rtnl_dereference(remote->rd);
struct br_mdb_entry e;
struct nlattr *nest;
@@ -606,7 +606,7 @@ static int vxlan_mdb_config_init(struct vxlan_mdb_config *cfg,
{
struct br_mdb_entry *entry = nla_data(tb[MDBA_SET_ENTRY]);
struct vxlan_dev *vxlan = netdev_priv(dev);
- const struct vxlan_config *vcfg = &vxlan->cfg;
+ const struct vxlan_config *vcfg = vxlan->cfg;
memset(cfg, 0, sizeof(*cfg));
cfg->vxlan = vxlan;
@@ -951,7 +951,7 @@ vxlan_mdb_nlmsg_remote_size(const struct vxlan_dev *vxlan,
const struct vxlan_mdb_entry *mdb_entry,
const struct vxlan_mdb_remote *remote)
{
- const struct vxlan_config *cfg = &vxlan->cfg;
+ const struct vxlan_config *cfg = vxlan->cfg;
const struct vxlan_mdb_entry_key *group = &mdb_entry->key;
struct vxlan_rdst *rd = rtnl_dereference(remote->rd);
size_t nlmsg_size;
diff --git a/drivers/net/vxlan/vxlan_multicast.c b/drivers/net/vxlan/vxlan_multicast.c
index 3b75b48dc726df40cebb233095a8a046ee274c30..6a95b6844cf442e9c9eb2311793c31f4ce824d70 100644
--- a/drivers/net/vxlan/vxlan_multicast.c
+++ b/drivers/net/vxlan/vxlan_multicast.c
@@ -158,7 +158,7 @@ bool vxlan_group_used(struct vxlan_net *vn, struct vxlan_dev *dev,
rtnl_dereference(vxlan->vn6_sock) != sock6)
continue;
#endif
- if (vxlan->cfg.flags & VXLAN_F_VNIFILTER) {
+ if (vxlan->cfg->flags & VXLAN_F_VNIFILTER) {
if (!vxlan_group_used_by_vnifilter(vxlan, ip, ifindex))
continue;
} else {
@@ -244,7 +244,7 @@ int vxlan_multicast_join(struct vxlan_dev *vxlan)
return ret;
}
- if (vxlan->cfg.flags & VXLAN_F_VNIFILTER)
+ if (vxlan->cfg->flags & VXLAN_F_VNIFILTER)
return vxlan_multicast_join_vnigrp(vxlan);
return 0;
@@ -263,7 +263,7 @@ int vxlan_multicast_leave(struct vxlan_dev *vxlan)
return ret;
}
- if (vxlan->cfg.flags & VXLAN_F_VNIFILTER)
+ if (vxlan->cfg->flags & VXLAN_F_VNIFILTER)
return vxlan_multicast_leave_vnigrp(vxlan);
return 0;
diff --git a/drivers/net/vxlan/vxlan_vnifilter.c b/drivers/net/vxlan/vxlan_vnifilter.c
index c46d5716493695b0651570a2686c6b0a150f61e6..8f6c01930ace77776dd6f2d3197e9325fdfd8bbf 100644
--- a/drivers/net/vxlan/vxlan_vnifilter.c
+++ b/drivers/net/vxlan/vxlan_vnifilter.c
@@ -178,7 +178,7 @@ void vxlan_vnifilter_count(struct vxlan_dev *vxlan,
{
struct vxlan_vni_node *vnode;
- if (!cfg || !(cfg->flags & VXLAN_F_VNIFILTER))
+ if (!(cfg->flags & VXLAN_F_VNIFILTER))
return;
if (vninode) {
@@ -343,7 +343,7 @@ static int vxlan_vnifilter_dump_dev(const struct net_device *dev,
bool dump_stats;
int err = 0;
- if (!(vxlan->cfg.flags & VXLAN_F_VNIFILTER)) {
+ if (!(vxlan->cfg->flags & VXLAN_F_VNIFILTER)) {
cb->args[1] = 0;
return -EINVAL;
}
@@ -489,7 +489,7 @@ static int vxlan_update_default_fdb_entry(struct vxlan_dev *vxlan, __be32 vni,
remote_ip,
NUD_REACHABLE | NUD_PERMANENT,
NLM_F_APPEND | NLM_F_CREATE,
- vxlan->cfg.dst_port,
+ vxlan->cfg->dst_port,
vni,
vni,
dst->remote_ifindex,
@@ -503,7 +503,7 @@ static int vxlan_update_default_fdb_entry(struct vxlan_dev *vxlan, __be32 vni,
if (old_remote_ip && !vxlan_addr_any(old_remote_ip)) {
__vxlan_fdb_delete(vxlan, all_zeros_mac,
*old_remote_ip,
- vxlan->cfg.dst_port,
+ vxlan->cfg->dst_port,
vni, vni,
dst->remote_ifindex,
true);
@@ -628,7 +628,7 @@ static void vxlan_vni_delete_group(struct vxlan_dev *vxlan,
__vxlan_fdb_delete(vxlan, all_zeros_mac,
(vxlan_addr_any(&vninode->remote_ip) ?
dst->remote_ip : vninode->remote_ip),
- vxlan->cfg.dst_port,
+ vxlan->cfg->dst_port,
vninode->vni, vninode->vni,
dst->remote_ifindex,
true);
@@ -736,7 +736,7 @@ static int vxlan_vni_add(struct vxlan_dev *vxlan,
if (vxlan_vnifilter_lookup(vxlan, v))
return vxlan_vni_update(vxlan, vg, v, group, &changed, extack);
- err = vxlan_vni_in_use(vxlan->net, vxlan, &vxlan->cfg, v);
+ err = vxlan_vni_in_use(vxlan->net, vxlan, vxlan->cfg, v);
if (err) {
NL_SET_ERR_MSG(extack, "VNI in use");
return err;
@@ -971,7 +971,7 @@ static int vxlan_vnifilter_process(struct sk_buff *skb, struct nlmsghdr *nlh,
vxlan = netdev_priv(dev);
- if (!(vxlan->cfg.flags & VXLAN_F_VNIFILTER))
+ if (!(vxlan->cfg->flags & VXLAN_F_VNIFILTER))
return -EOPNOTSUPP;
nlmsg_for_each_attr_type(attr, VXLAN_VNIFILTER_ENTRY, nlh,
diff --git a/include/net/vxlan.h b/include/net/vxlan.h
index f4f519a365f524e1301d769f75690d836b9e4432..9b39f34b7eada647eb56512ede094555439f97e5 100644
--- a/include/net/vxlan.h
+++ b/include/net/vxlan.h
@@ -303,7 +303,7 @@ struct vxlan_dev {
struct gro_cells gro_cells;
unsigned long flags;
- struct vxlan_config cfg;
+ struct vxlan_config *cfg;
struct vxlan_vni_group __rcu *vnigrp;
--
2.55.0.970.g62bdec98f9-goog
^ permalink raw reply related [flat|nested] 28+ messages in thread* Re: [PATCH net-next 6/9] vxlan: dynamically allocate struct vxlan_config
2026-09-03 12:08 ` [PATCH net-next 6/9] vxlan: dynamically allocate struct vxlan_config Eric Dumazet
@ 2026-09-05 4:29 ` Kuniyuki Iwashima
2026-09-07 6:11 ` netdev-bot+sashiko
1 sibling, 0 replies; 28+ messages in thread
From: Kuniyuki Iwashima @ 2026-09-05 4:29 UTC (permalink / raw)
To: Eric Dumazet
Cc: David S . Miller, Jakub Kicinski, Paolo Abeni, Simon Horman,
Ido Schimmel, Andrew Lunn, netdev, eric.dumazet
On Thu, Sep 3, 2026 at 5:08 AM Eric Dumazet <edumazet@google.com> wrote:
>
> Move 'struct vxlan_config' from an embedded structure inside
> 'struct vxlan_dev' to a dynamically allocated pointer 'vxlan->cfg'.
>
> Updating configuration via vxlan_changelink() or vxlan_dev_configure()
> now allocates a new struct vxlan_config and frees the previous one.
>
> This prepares the ground for converting vxlan->cfg to RCU protection
> in the following patch.
>
> Signed-off-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com>
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH net-next 6/9] vxlan: dynamically allocate struct vxlan_config
2026-09-03 12:08 ` [PATCH net-next 6/9] vxlan: dynamically allocate struct vxlan_config Eric Dumazet
2026-09-05 4:29 ` Kuniyuki Iwashima
@ 2026-09-07 6:11 ` netdev-bot+sashiko
2026-09-07 6:39 ` Eric Dumazet
1 sibling, 1 reply; 28+ messages in thread
From: netdev-bot+sashiko @ 2026-09-07 6:11 UTC (permalink / raw)
To: edumazet
Cc: davem, kuba, pabeni, horms, kuniyu, idosch, andrew+netdev, netdev,
eric.dumazet
Thank you for your contribution! Sashiko AI review found 4 potential
issue(s) to consider:
Critical: 0 · High: 1 · Medium: 1 · Low: 2
- [High] vxlan_config_apply() publishes the newly allocated config with a
plain store and immediately reclaims the old one with a bare kfree()…
- [Medium] The new configuration pointer is published with a plain store
(vxlan_core.c:4038 `vxlan->cfg = new_cfg;`) right after the payload is…
- [Low] vxlan_encap_bypass() adds a `!dst_cfg` NULL test whose error
branch (`goto drop`) lands on the `drop:` label, and that label passes…
- [Low] The patch introduces two mutually exclusive assumptions about
vxlan->cfg for a live/registered netdev, on the same code paths.
--
vxlan: dynamically allocate struct vxlan_config
This moves struct vxlan_config out of struct vxlan_dev into a separately
allocated object referenced by vxlan->cfg, with vxlan_changelink() and
vxlan_dev_configure() allocating a new config and freeing the old one.
It is described as preparation for converting vxlan->cfg to RCU
protection in the following patch.
> diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c
> index 0fcc7282e69d..af320173a1e7 100644
> --- a/drivers/net/vxlan/vxlan_core.c
> +++ b/drivers/net/vxlan/vxlan_core.c
> @@ -110,20 +110,23 @@ static struct vxlan_dev *vxlan_vs_find_vni(struct vxlan_sock *vs,
> vni = 0;
>
> hlist_for_each_entry_rcu(node, vni_head(vs, vni), hlist) {
> + const struct vxlan_config *cfg;
> +
> if (!node->vxlan)
> continue;
> +
> + cfg = node->vxlan->cfg;
> +
> vnode = NULL;
> - if (node->vxlan->cfg.flags & VXLAN_F_VNIFILTER) {
> + if (cfg->flags & VXLAN_F_VNIFILTER) {
> vnode = vxlan_vnifilter_lookup(node->vxlan, vni);
> if (!vnode)
> continue;
> - } else if (node->vxlan->default_dst.remote_vni != vni) {
> + } else if (cfg->vni != vni) {
> continue;
> }
[Severity: High]
This per-packet RX lookup now walks other devices' cfg pointers with a
plain load. Combined with the plain kfree() added in vxlan_config_apply()
below, can this dereference freed memory?
CPU0 (RTNL, ip link set ... type vxlan ...)
vxlan_changelink()
vxlan_config_apply()
old_cfg = vxlan->cfg;
vxlan->cfg = new_cfg;
kfree(old_cfg); <-- no grace period
CPU1 (RX softirq)
vxlan_rcv()
vxlan_vs_find_vni()
cfg = node->vxlan->cfg; <-- may already be old_cfg
if (cfg->flags & VXLAN_F_VNIFILTER) <-- use-after-free
[ ... ]
> @@ -1510,6 +1521,7 @@ static bool __vxlan_sock_release_prep(struct vxlan_sock *vs)
>
> static void vxlan_sock_release(struct vxlan_dev *vxlan)
> {
> + const struct vxlan_config *cfg = vxlan->cfg;
> struct vxlan_sock *sock4 = rtnl_dereference(vxlan->vn4_sock);
> #if IS_ENABLED(CONFIG_IPV6)
> struct vxlan_sock *sock6 = rtnl_dereference(vxlan->vn6_sock);
> @@ -1519,7 +1531,7 @@ static void vxlan_sock_release(struct vxlan_dev *vxlan)
>
> RCU_INIT_POINTER(vxlan->vn4_sock, NULL);
>
> - if (vxlan->cfg.flags & VXLAN_F_VNIFILTER)
> + if (cfg && (cfg->flags & VXLAN_F_VNIFILTER))
> vxlan_vs_del_vnigrp(vxlan);
> else
> vxlan_vs_del_dev(vxlan);
[Severity: Low]
Can cfg actually be NULL here? The caller vxlan_stop() dereferences the
same pointer unconditionally a couple of statements earlier:
vxlan_stop()
vxlan_multicast_leave(vxlan)
if (vxlan->cfg->flags & VXLAN_F_VNIFILTER) <-- no NULL check
timer_delete_sync(&vxlan->age_timer);
vxlan_flush(vxlan, &desc);
vxlan_sock_release(vxlan)
if (cfg && (cfg->flags & VXLAN_F_VNIFILTER)) <-- NULL check
The same disagreement shows up in several other pairs added by this patch:
vxlan_open() tests "cfg && cfg->age_interval" after vxlan_sock_add() and
__vxlan_sock_add() already read cfg->flags unguarded, vxlan_uninit() tests
"cfg &&" while its ndo_init counterpart vxlan_init() uses cfg->flags
directly, and vxlan_fdb_get() returns -ENODEV for a NULL cfg while
vxlan_fdb_parse() on the same registered device dereferences cfg->dst_port
and cfg->vni without a check.
Since vxlan->cfg is cleared only in vxlan_free_dev(), the priv_destructor,
which runs from free_netdev() after the device is unregistered (and
rtnl_newlink_create() calls free_netdev() directly on ->newlink failure
without invoking ndo_uninit), it looks like none of the netdev ops can
observe NULL, making these guards and the new -ENODEV path dead code.
Would it be clearer to drop the checks and keep the invariant uniform?
These mixed assumptions also remain at the end of the series.
[ ... ]
> @@ -1692,7 +1704,8 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
> goto drop;
> }
>
> - cfg = &vxlan->cfg;
> + cfg = vxlan->cfg;
> +
> if (vh->vx_flags & cfg->reserved_bits.vx_flags ||
> vh->vx_vni & cfg->reserved_bits.vx_vni) {
> /* If the header uses bits besides those enabled by the
[Severity: Medium]
The pointer is loaded plainly here and used for many dereferences
(cfg->reserved_bits, cfg->flags) while vxlan_config_apply() publishes the
kmemdup()'ed replacement with a plain store.
Without a release barrier on the writer side (rcu_assign_pointer() or
smp_store_release()) and a dependency-ordering load on the reader side
(rcu_dereference()/READ_ONCE()), can a reader on a weakly ordered CPU see
the new pointer before the kmemdup'ed payload stores are visible, and act
on stale flags/reserved_bits/dst_port/saddr values?
The field is also declared without __rcu in include/net/vxlan.h, so sparse
cannot flag the missing accessors. The following patch, "vxlan: convert
configuration to RCU protection", adds __rcu plus
rcu_assign_pointer()/rcu_dereference() at these sites, so would it be
reasonable to fold the annotation into this patch instead of leaving an
intermediate commit without ordering?
[ ... ]
> @@ -2278,7 +2293,7 @@ static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan,
> const struct vxlan_config *src_cfg,
> __be32 vni, bool snoop)
> {
> - const struct vxlan_config *dst_cfg = &dst_vxlan->cfg;
> + const struct vxlan_config *dst_cfg;
> union vxlan_addr loopback;
> unsigned int len = skb->len;
> struct net_device *dev = dst_vxlan->dev;
> @@ -2299,7 +2314,8 @@ static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan,
> }
>
> rcu_read_lock();
> - if (unlikely(!(dev->flags & IFF_UP))) {
> + dst_cfg = dst_vxlan->cfg;
> + if (unlikely(!dst_cfg || !(dev->flags & IFF_UP))) {
> kfree_skb_reason(skb, SKB_DROP_REASON_DEV_READY);
> goto drop;
> }
[Severity: Low]
If the new !dst_cfg branch is ever taken, where does it land? The goto
jumps to the drop label at the end of the same function, which passes the
same NULL dst_cfg straight into vxlan_vnifilter_count():
drop:
dev_dstats_rx_dropped(dev);
vxlan_vnifilter_count(dst_vxlan, dst_cfg, vni, NULL,
VXLAN_VNI_STATS_RX_DROPS, 0);
and this patch also removes the "!cfg ||" guard from that helper in
vxlan_vnifilter.c, so it now starts with:
if (!(cfg->flags & VXLAN_F_VNIFILTER))
return;
That would be a NULL dereference in softirq context. The check appears to
be unreachable today, since vxlan->cfg is only cleared in vxlan_free_dev()
after the device is gone from the VNI hash, so is the new test needed at
all? A later patch in the series, "vxlan: remove default_dst and use
vxlan_config and lowerdev", wraps the drop-path call in "if (dst_cfg)",
which suggests the intent was for the check to be live.
[ ... ]
> @@ -2765,7 +2781,7 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
> u32 nhid = 0;
> u32 flags;
>
> - cfg = &vxlan->cfg;
> + cfg = vxlan->cfg;
> flags = cfg->flags;
> default_vni = cfg->vni;
> saddr_family = cfg->saddr.sa.sa_family;
[Severity: High]
This pointer is cached at function entry, outside any rcu_read_lock() in
this commit, and then handed to arp_reduce(), neigh_reduce(),
vxlan_xmit_one(), vxlan_xmit_nh(), vxlan_mdb_xmit() and
vxlan_vnifilter_count() for the duration of the transmit. Can a
concurrent changelink free it underneath?
Before this patch the config was embedded in vxlan_dev and updated in
place, so a racing reader could only see a torn config. Now the reader can
see freed slab memory, and vxlan_xmit_one() feeds cfg->saddr, cfg->ttl,
cfg->tos, cfg->label and cfg->dst_port into the encapsulation headers.
[ ... ]
> @@ -2884,12 +2900,15 @@ static void vxlan_cleanup(struct timer_list *t)
> {
> struct vxlan_dev *vxlan = timer_container_of(vxlan, t, age_timer);
> unsigned long next_timer = jiffies + FDB_AGE_INTERVAL;
> + const struct vxlan_config *cfg;
> struct vxlan_fdb *f;
>
> if (!netif_running(vxlan->dev))
> return;
>
> rcu_read_lock();
> + cfg = vxlan->cfg;
> +
> hlist_for_each_entry_rcu(f, &vxlan->fdb_list, fdb_node) {
> unsigned long timeout;
>
> @@ -2899,7 +2918,7 @@ static void vxlan_cleanup(struct timer_list *t)
> if (f->flags & NTF_EXT_LEARNED)
> continue;
>
> - timeout = READ_ONCE(f->updated) + vxlan->cfg.age_interval * HZ;
> + timeout = READ_ONCE(f->updated) + cfg->age_interval * HZ;
[Severity: High]
The rcu_read_lock() here gives no protection against the plain kfree() in
vxlan_config_apply(), so can the timer softirq read cfg->age_interval from
a freed object across the whole FDB walk?
[ ... ]
> @@ -3247,14 +3276,18 @@ static int vxlan_change_mtu(struct net_device *dev, int new_mtu)
[ ... ]
> @@ -3381,6 +3417,14 @@ static void vxlan_offload_rx_ports(struct net_device *dev, bool push)
> }
> }
>
> +static void vxlan_free_dev(struct net_device *dev)
> +{
> + struct vxlan_dev *vxlan = netdev_priv(dev);
> +
> + kfree(vxlan->cfg);
> + vxlan->cfg = NULL;
> +}
> +
[ ... ]
> @@ -3978,7 +4034,9 @@ static void vxlan_config_apply(struct net_device *dev,
> needed_headroom += vxlan_headroom(flags);
> dev->needed_headroom = needed_headroom;
>
> - memcpy(&vxlan->cfg, conf, sizeof(*conf));
> + old_cfg = vxlan->cfg;
> + vxlan->cfg = new_cfg;
> + kfree(old_cfg);
> }
[Severity: High]
This is the writer side of the use-after-free discussed above. RTNL is
held here, but none of the readers (vxlan_xmit(), vxlan_rcv(),
vxlan_vs_find_vni(), vxlan_cleanup(), vxlan_fill_metadata_dst(),
mlxsw_sp_switchdev_vxlan_work_prepare()) hold RTNL, and there is no grace
period, refcount or shared lock between them. Can the old config be freed
while an in-flight packet still dereferences it?
Since the commit message says this prepares for the RCU conversion in the
following patch, would it make sense to use kfree_rcu() (adding the
rcu_head in this patch) so the intermediate commit is not a bisect and
backport hazard?
[Severity: Medium]
Separately from the lifetime problem, this publishes a pointer to freshly
kmemdup()'ed data with a plain store. Should this be rcu_assign_pointer()
(or at least smp_store_release()) so readers cannot observe the pointer
before the payload writes?
[ ... ]
> @@ -3987,13 +4045,18 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev,
> {
> struct vxlan_dev *vxlan = netdev_priv(dev);
> struct net_device *lowerdev;
> + struct vxlan_config *new_cfg;
> int ret;
>
> ret = vxlan_config_validate(src_net, conf, &lowerdev, vxlan, extack);
> if (ret)
> return ret;
>
> - vxlan_config_apply(dev, conf, lowerdev, src_net, false);
> + new_cfg = kmemdup(conf, sizeof(*conf), GFP_KERNEL);
> + if (!new_cfg)
> + return -ENOMEM;
> +
> + vxlan_config_apply(dev, new_cfg, lowerdev, src_net, false);
>
> return 0;
> }
[ ... ]
> diff --git a/drivers/net/vxlan/vxlan_vnifilter.c b/drivers/net/vxlan/vxlan_vnifilter.c
> index c46d57164936..8f6c01930ace 100644
> --- a/drivers/net/vxlan/vxlan_vnifilter.c
> +++ b/drivers/net/vxlan/vxlan_vnifilter.c
> @@ -178,7 +178,7 @@ void vxlan_vnifilter_count(struct vxlan_dev *vxlan,
> {
> struct vxlan_vni_node *vnode;
>
> - if (!cfg || !(cfg->flags & VXLAN_F_VNIFILTER))
> + if (!(cfg->flags & VXLAN_F_VNIFILTER))
> return;
[Severity: Low]
Dropping the "!cfg ||" guard here makes every caller responsible for a
non-NULL cfg, which conflicts with the new !dst_cfg branch added in
vxlan_encap_bypass() that reaches its drop label with dst_cfg still NULL
and then calls this helper. Should the two changes agree one way or the
other?
>
> if (vninode) {
[ ... ]
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260903120840.1024153-1-edumazet%40google.com
^ permalink raw reply [flat|nested] 28+ messages in thread* Re: [PATCH net-next 6/9] vxlan: dynamically allocate struct vxlan_config
2026-09-07 6:11 ` netdev-bot+sashiko
@ 2026-09-07 6:39 ` Eric Dumazet
0 siblings, 0 replies; 28+ messages in thread
From: Eric Dumazet @ 2026-09-07 6:39 UTC (permalink / raw)
To: netdev-bot+sashiko
Cc: davem, kuba, pabeni, horms, kuniyu, idosch, andrew+netdev, netdev,
eric.dumazet
On Mon, Sep 7, 2026 at 8:11 AM <netdev-bot+sashiko@kernel.org> wrote:
>
> Thank you for your contribution! Sashiko AI review found 4 potential
> issue(s) to consider:
>
> Critical: 0 · High: 1 · Medium: 1 · Low: 2
>
> - [High] vxlan_config_apply() publishes the newly allocated config with a
> plain store and immediately reclaims the old one with a bare kfree()…
> - [Medium] The new configuration pointer is published with a plain store
> (vxlan_core.c:4038 `vxlan->cfg = new_cfg;`) right after the payload is…
> - [Low] vxlan_encap_bypass() adds a `!dst_cfg` NULL test whose error
> branch (`goto drop`) lands on the `drop:` label, and that label passes…
> - [Low] The patch introduces two mutually exclusive assumptions about
> vxlan->cfg for a live/registered netdev, on the same code paths.
>
For the record, I am ditching all Medium/Low reports from Sahiko.
One of Sashiko's suggestions led me to write an unnecessary patch,
which was a waste of time.
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH net-next 7/9] vxlan: convert configuration to RCU protection
2026-09-03 12:08 [PATCH net-next 0/9] vxlan: convert configuration to RCU and drop RTNL in vxlan_fill_info() Eric Dumazet
` (5 preceding siblings ...)
2026-09-03 12:08 ` [PATCH net-next 6/9] vxlan: dynamically allocate struct vxlan_config Eric Dumazet
@ 2026-09-03 12:08 ` Eric Dumazet
2026-09-05 4:33 ` Kuniyuki Iwashima
2026-09-07 6:11 ` netdev-bot+sashiko
2026-09-03 12:08 ` [PATCH net-next 8/9] vxlan: remove default_dst and use vxlan_config and lowerdev Eric Dumazet
2026-09-03 12:08 ` [PATCH net-next 9/9] vxlan: no longer rely on RTNL in vxlan_fill_info() Eric Dumazet
8 siblings, 2 replies; 28+ messages in thread
From: Eric Dumazet @ 2026-09-03 12:08 UTC (permalink / raw)
To: David S . Miller, Jakub Kicinski, Paolo Abeni
Cc: Simon Horman, Kuniyuki Iwashima, Ido Schimmel, Andrew Lunn,
netdev, eric.dumazet, Eric Dumazet
In order to allow lockless readers in future patches, convert 'vxlan->cfg'
to an RCU protected pointer.
Updating configuration via vxlan_changelink() or vxlan_dev_configure()
now uses rcu_assign_pointer() to publish it, freeing the previous config
with kfree_rcu().
Signed-off-by: Eric Dumazet <edumazet@google.com>
---
.../mellanox/mlxsw/spectrum_nve_vxlan.c | 14 ++-
.../mellanox/mlxsw/spectrum_switchdev.c | 57 +++++++----
drivers/net/vxlan/vxlan_core.c | 94 +++++++++++--------
drivers/net/vxlan/vxlan_mdb.c | 10 +-
drivers/net/vxlan/vxlan_multicast.c | 12 ++-
drivers/net/vxlan/vxlan_vnifilter.c | 19 ++--
include/net/vxlan.h | 3 +-
7 files changed, 132 insertions(+), 77 deletions(-)
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c
index 4db0efc376a8ef4d373b693240e041a708278b96..50cea39323f570e04067f2f98aff4d97e4a409fc 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c
@@ -59,8 +59,11 @@ static bool mlxsw_sp_nve_vxlan_can_offload(const struct mlxsw_sp_nve *nve,
const struct mlxsw_sp_nve_params *params,
struct netlink_ext_ack *extack)
{
- struct vxlan_dev *vxlan = netdev_priv(params->dev);
- struct vxlan_config *cfg = vxlan->cfg;
+ const struct vxlan_config *cfg;
+ struct vxlan_dev *vxlan;
+
+ vxlan = netdev_priv(params->dev);
+ cfg = rtnl_dereference(vxlan->cfg);
if (vxlan_addr_multicast(&cfg->remote_ip)) {
NL_SET_ERR_MSG_MOD(extack, "VxLAN: Multicast destination IP is not supported");
@@ -148,8 +151,11 @@ static void mlxsw_sp_nve_vxlan_config(const struct mlxsw_sp_nve *nve,
const struct mlxsw_sp_nve_params *params,
struct mlxsw_sp_nve_config *config)
{
- struct vxlan_dev *vxlan = netdev_priv(params->dev);
- struct vxlan_config *cfg = vxlan->cfg;
+ const struct vxlan_config *cfg;
+ struct vxlan_dev *vxlan;
+
+ vxlan = netdev_priv(params->dev);
+ cfg = rtnl_dereference(vxlan->cfg);
config->type = MLXSW_SP_NVE_TYPE_VXLAN;
config->ttl = cfg->ttl;
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
index a22228bd95c90ae8a481ba2f07f2a38689266b78..de60b698bea982593f0f7550571e9a4f434dcceb 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
@@ -2513,15 +2513,17 @@ mlxsw_sp_bridge_vlan_aware_vxlan_join(struct mlxsw_sp_bridge_device *bridge_devi
{
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev);
struct vxlan_dev *vxlan = netdev_priv(vxlan_dev);
- struct mlxsw_sp_nve_params params = {
- .type = MLXSW_SP_NVE_TYPE_VXLAN,
- .vni = vxlan->cfg->vni,
- .dev = vxlan_dev,
- .ethertype = ethertype,
- };
+ struct mlxsw_sp_nve_params params;
+ const struct vxlan_config *cfg;
struct mlxsw_sp_fid *fid;
int err;
+ cfg = rtnl_dereference(vxlan->cfg);
+ params.type = MLXSW_SP_NVE_TYPE_VXLAN;
+ params.vni = cfg->vni;
+ params.dev = vxlan_dev;
+ params.ethertype = ethertype;
+
/* If the VLAN is 0, we need to find the VLAN that is configured as
* PVID and egress untagged on the bridge port of the VxLAN device.
* It is possible no such VLAN exists
@@ -2704,15 +2706,17 @@ mlxsw_sp_bridge_8021d_vxlan_join(struct mlxsw_sp_bridge_device *bridge_device,
{
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev);
struct vxlan_dev *vxlan = netdev_priv(vxlan_dev);
- struct mlxsw_sp_nve_params params = {
- .type = MLXSW_SP_NVE_TYPE_VXLAN,
- .vni = vxlan->cfg->vni,
- .dev = vxlan_dev,
- .ethertype = ETH_P_8021Q,
- };
+ struct mlxsw_sp_nve_params params;
+ const struct vxlan_config *cfg;
struct mlxsw_sp_fid *fid;
int err;
+ cfg = rtnl_dereference(vxlan->cfg);
+ params.type = MLXSW_SP_NVE_TYPE_VXLAN;
+ params.vni = cfg->vni;
+ params.dev = vxlan_dev;
+ params.ethertype = ETH_P_8021Q;
+
fid = mlxsw_sp_fid_8021d_get(mlxsw_sp, bridge_device->dev->ifindex);
if (IS_ERR(fid)) {
NL_SET_ERR_MSG_MOD(extack, "Failed to create 802.1D FID");
@@ -2933,10 +2937,13 @@ static void __mlxsw_sp_bridge_vxlan_leave(struct mlxsw_sp *mlxsw_sp,
const struct net_device *vxlan_dev)
{
struct vxlan_dev *vxlan = netdev_priv(vxlan_dev);
+ const struct vxlan_config *cfg;
struct mlxsw_sp_fid *fid;
+ cfg = rtnl_dereference(vxlan->cfg);
+
/* If the VxLAN device is down, then the FID does not have a VNI */
- fid = mlxsw_sp_fid_lookup_by_vni(mlxsw_sp, vxlan->cfg->vni);
+ fid = mlxsw_sp_fid_lookup_by_vni(mlxsw_sp, cfg->vni);
if (!fid)
return;
@@ -3029,11 +3036,13 @@ static void mlxsw_sp_fdb_vxlan_call_notifiers(struct net_device *dev,
struct switchdev_notifier_vxlan_fdb_info info;
struct vxlan_dev *vxlan = netdev_priv(dev);
enum switchdev_notifier_type type;
+ const struct vxlan_config *cfg;
+ cfg = rtnl_dereference(vxlan->cfg);
type = adding ? SWITCHDEV_VXLAN_FDB_ADD_TO_BRIDGE :
SWITCHDEV_VXLAN_FDB_DEL_TO_BRIDGE;
mlxsw_sp_switchdev_addr_vxlan_convert(proto, addr, &info.remote_ip);
- info.remote_port = vxlan->cfg->dst_port;
+ info.remote_port = cfg->dst_port;
info.remote_vni = vni;
info.remote_ifindex = 0;
ether_addr_copy(info.eth_addr, mac);
@@ -3236,8 +3245,10 @@ __mlxsw_sp_fdb_notify_mac_uc_tunnel_process(struct mlxsw_sp *mlxsw_sp,
if (adding && netif_is_vxlan(dev)) {
struct vxlan_dev *vxlan = netdev_priv(dev);
+ const struct vxlan_config *cfg;
- if (!(vxlan->cfg->flags & VXLAN_F_LEARN))
+ cfg = rtnl_dereference(vxlan->cfg);
+ if (!(cfg->flags & VXLAN_F_LEARN))
return -EINVAL;
}
@@ -3722,9 +3733,11 @@ mlxsw_sp_switchdev_vxlan_work_prepare(struct mlxsw_sp_switchdev_event_work *
{
struct vxlan_dev *vxlan = netdev_priv(switchdev_work->dev);
struct switchdev_notifier_vxlan_fdb_info *vxlan_fdb_info;
- struct vxlan_config *cfg = vxlan->cfg;
+ const struct vxlan_config *cfg;
struct netlink_ext_ack *extack;
+ cfg = rcu_dereference_rtnl(vxlan->cfg);
+
extack = switchdev_notifier_info_to_extack(info);
vxlan_fdb_info = container_of(info,
struct switchdev_notifier_vxlan_fdb_info,
@@ -3851,11 +3864,15 @@ mlxsw_sp_switchdev_vxlan_vlan_add(struct mlxsw_sp *mlxsw_sp,
struct netlink_ext_ack *extack)
{
struct vxlan_dev *vxlan = netdev_priv(vxlan_dev);
- __be32 vni = vxlan->cfg->vni;
+ const struct vxlan_config *cfg;
struct mlxsw_sp_fid *fid;
u16 old_vid;
+ __be32 vni;
int err;
+ cfg = rtnl_dereference(vxlan->cfg);
+ vni = cfg->vni;
+
/* We cannot have the same VLAN as PVID and egress untagged on multiple
* VxLAN devices. Note that we get this notification before the VLAN is
* actually added to the bridge's database, so it is not possible for
@@ -3935,12 +3952,16 @@ mlxsw_sp_switchdev_vxlan_vlan_del(struct mlxsw_sp *mlxsw_sp,
const struct net_device *vxlan_dev, u16 vid)
{
struct vxlan_dev *vxlan = netdev_priv(vxlan_dev);
- __be32 vni = vxlan->cfg->vni;
+ const struct vxlan_config *cfg;
struct mlxsw_sp_fid *fid;
+ __be32 vni;
if (!netif_running(vxlan_dev))
return;
+ cfg = rtnl_dereference(vxlan->cfg);
+ vni = cfg->vni;
+
fid = mlxsw_sp_fid_lookup_by_vni(mlxsw_sp, vni);
if (!fid)
return;
diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c
index af320173a1e7f598dbe81a90beb0e68c7ed8c482..c49d55de020f49b68e322de2a4b0a5c0be0471ed 100644
--- a/drivers/net/vxlan/vxlan_core.c
+++ b/drivers/net/vxlan/vxlan_core.c
@@ -115,7 +115,7 @@ static struct vxlan_dev *vxlan_vs_find_vni(struct vxlan_sock *vs,
if (!node->vxlan)
continue;
- cfg = node->vxlan->cfg;
+ cfg = rcu_dereference(node->vxlan->cfg);
vnode = NULL;
if (cfg->flags & VXLAN_F_VNIFILTER) {
@@ -160,7 +160,7 @@ static int vxlan_fdb_info(struct sk_buff *skb, struct vxlan_dev *vxlan,
u32 portid, u32 seq, int type, unsigned int flags,
const struct vxlan_rdst *rdst)
{
- const struct vxlan_config *cfg = vxlan->cfg;
+ const struct vxlan_config *cfg = rcu_dereference_rtnl(vxlan->cfg);
unsigned long now = jiffies;
struct nda_cacheinfo ci;
bool send_ip, send_eth;
@@ -422,7 +422,7 @@ static struct vxlan_fdb *vxlan_find_mac(struct vxlan_dev *vxlan,
lockdep_assert_held_once(&vxlan->hash_lock);
rcu_read_lock();
- f = vxlan_find_mac_rcu(vxlan, vxlan->cfg, mac, vni);
+ f = vxlan_find_mac_rcu(vxlan, rcu_dereference(vxlan->cfg), mac, vni);
rcu_read_unlock();
return f;
@@ -463,7 +463,7 @@ int vxlan_fdb_find_uc(struct net_device *dev, const u8 *mac, __be32 vni,
rcu_read_lock();
- f = vxlan_find_mac_rcu(vxlan, vxlan->cfg, eth_addr, vni);
+ f = vxlan_find_mac_rcu(vxlan, rcu_dereference(vxlan->cfg), eth_addr, vni);
if (f)
rdst = first_remote_rcu(f);
if (!rdst) {
@@ -869,7 +869,7 @@ int vxlan_fdb_create(struct vxlan_dev *vxlan,
u32 nhid, struct vxlan_fdb **fdb,
struct netlink_ext_ack *extack)
{
- const struct vxlan_config *cfg = vxlan->cfg;
+ const struct vxlan_config *cfg = rcu_dereference_rtnl(vxlan->cfg);
struct vxlan_rdst *rd = NULL;
struct vxlan_fdb *f;
int rc;
@@ -1155,7 +1155,7 @@ static int vxlan_fdb_parse(struct nlattr *tb[], struct vxlan_dev *vxlan,
__be32 *vni, u32 *ifindex, u32 *nhid,
struct netlink_ext_ack *extack)
{
- const struct vxlan_config *cfg = vxlan->cfg;
+ const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg);
struct net *net = dev_net(vxlan->dev);
int err;
@@ -1407,7 +1407,7 @@ static int vxlan_fdb_get(struct sk_buff *skb,
__be32 vni;
int err;
- cfg = vxlan->cfg;
+ cfg = rcu_dereference_rtnl(vxlan->cfg);
if (tb[NDA_VNI])
vni = cpu_to_be32(nla_get_u32(tb[NDA_VNI]));
@@ -1521,7 +1521,7 @@ static bool __vxlan_sock_release_prep(struct vxlan_sock *vs)
static void vxlan_sock_release(struct vxlan_dev *vxlan)
{
- const struct vxlan_config *cfg = vxlan->cfg;
+ const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg);
struct vxlan_sock *sock4 = rtnl_dereference(vxlan->vn4_sock);
#if IS_ENABLED(CONFIG_IPV6)
struct vxlan_sock *sock6 = rtnl_dereference(vxlan->vn6_sock);
@@ -1704,7 +1704,7 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
goto drop;
}
- cfg = vxlan->cfg;
+ cfg = rcu_dereference(vxlan->cfg);
if (vh->vx_flags & cfg->reserved_bits.vx_flags ||
vh->vx_vni & cfg->reserved_bits.vx_vni) {
@@ -2314,7 +2314,7 @@ static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan,
}
rcu_read_lock();
- dst_cfg = dst_vxlan->cfg;
+ dst_cfg = rcu_dereference(dst_vxlan->cfg);
if (unlikely(!dst_cfg || !(dev->flags & IFF_UP))) {
kfree_skb_reason(skb, SKB_DROP_REASON_DEV_READY);
goto drop;
@@ -2781,7 +2781,8 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
u32 nhid = 0;
u32 flags;
- cfg = vxlan->cfg;
+ rcu_read_lock();
+ cfg = rcu_dereference(vxlan->cfg);
flags = cfg->flags;
default_vni = cfg->vni;
saddr_family = cfg->saddr.sa.sa_family;
@@ -2800,14 +2801,19 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
vxlan_xmit_one(skb, dev, cfg, vni, NULL, false);
else
kfree_skb_reason(skb, SKB_DROP_REASON_TUNNEL_TXINFO);
+ rcu_read_unlock();
return NETDEV_TX_OK;
}
}
if (flags & VXLAN_F_PROXY) {
eth = eth_hdr(skb);
- if (ntohs(eth->h_proto) == ETH_P_ARP)
- return arp_reduce(dev, skb, cfg, vni);
+ if (ntohs(eth->h_proto) == ETH_P_ARP) {
+ netdev_tx_t res = arp_reduce(dev, skb, cfg, vni);
+
+ rcu_read_unlock();
+ return res;
+ }
#if IS_ENABLED(CONFIG_IPV6)
else if (ntohs(eth->h_proto) == ETH_P_IPV6 &&
pskb_network_may_pull(skb, sizeof(struct ipv6hdr) +
@@ -2816,32 +2822,36 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
struct nd_msg *m = (struct nd_msg *)(ipv6_hdr(skb) + 1);
if (m->icmph.icmp6_code == 0 &&
- m->icmph.icmp6_type == NDISC_NEIGHBOUR_SOLICITATION)
- return neigh_reduce(dev, skb, cfg, vni);
+ m->icmph.icmp6_type == NDISC_NEIGHBOUR_SOLICITATION) {
+ netdev_tx_t res = neigh_reduce(dev, skb, cfg, vni);
+
+ rcu_read_unlock();
+ return res;
+ }
}
#endif
}
- if (nhid)
- return vxlan_xmit_nhid(skb, dev, nhid, vni, saddr_family, cfg);
+ if (nhid) {
+ netdev_tx_t res = vxlan_xmit_nhid(skb, dev, nhid, vni, saddr_family, cfg);
+
+ rcu_read_unlock();
+ return res;
+ }
if (test_bit(VXLAN_DEV_F_MDB, &vxlan->flags)) {
struct vxlan_mdb_entry *mdb_entry;
- rcu_read_lock();
mdb_entry = vxlan_mdb_entry_skb_get(vxlan, cfg, skb, vni);
if (mdb_entry) {
- netdev_tx_t ret;
+ netdev_tx_t ret = vxlan_mdb_xmit(vxlan, cfg, mdb_entry, skb);
- ret = vxlan_mdb_xmit(vxlan, cfg, mdb_entry, skb);
rcu_read_unlock();
return ret;
}
- rcu_read_unlock();
}
eth = eth_hdr(skb);
- rcu_read_lock();
f = vxlan_find_mac_tx(vxlan, cfg, eth->h_dest, vni);
did_rsc = false;
@@ -2907,7 +2917,7 @@ static void vxlan_cleanup(struct timer_list *t)
return;
rcu_read_lock();
- cfg = vxlan->cfg;
+ cfg = rcu_dereference(vxlan->cfg);
hlist_for_each_entry_rcu(f, &vxlan->fdb_list, fdb_node) {
unsigned long timeout;
@@ -2965,7 +2975,7 @@ static int vxlan_init(struct net_device *dev)
const struct vxlan_config *cfg;
int err;
- cfg = vxlan->cfg;
+ cfg = rtnl_dereference(vxlan->cfg);
err = rhashtable_init(&vxlan->fdb_hash_tbl, &vxlan_fdb_rht_params);
if (err)
@@ -3003,7 +3013,7 @@ static void vxlan_uninit(struct net_device *dev)
struct vxlan_dev *vxlan = netdev_priv(dev);
const struct vxlan_config *cfg;
- cfg = vxlan->cfg;
+ cfg = rtnl_dereference(vxlan->cfg);
vxlan_mdb_fini(vxlan);
@@ -3032,7 +3042,7 @@ static int vxlan_open(struct net_device *dev)
return ret;
}
- cfg = vxlan->cfg;
+ cfg = rtnl_dereference(vxlan->cfg);
if (cfg && cfg->age_interval)
mod_timer(&vxlan->age_timer, jiffies + FDB_AGE_INTERVAL);
@@ -3055,7 +3065,7 @@ struct vxlan_fdb_flush_desc {
static bool vxlan_fdb_is_default_entry(const struct vxlan_fdb *f,
const struct vxlan_dev *vxlan)
{
- const struct vxlan_config *cfg = vxlan->cfg;
+ const struct vxlan_config *cfg = rcu_dereference_rtnl(vxlan->cfg);
return is_zero_ether_addr(f->key.eth_addr) &&
f->key.vni == cfg->vni;
@@ -3279,7 +3289,7 @@ static int vxlan_change_mtu(struct net_device *dev, int new_mtu)
const struct vxlan_config *cfg;
struct net_device *lowerdev;
- cfg = vxlan->cfg;
+ cfg = rtnl_dereference(vxlan->cfg);
lowerdev = __dev_get_by_index(vxlan->net, dst->remote_ifindex);
@@ -3303,7 +3313,7 @@ static int vxlan_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb)
const struct vxlan_config *cfg;
__be16 sport, dport;
- cfg = vxlan->cfg;
+ cfg = rcu_dereference(vxlan->cfg);
sport = udp_flow_src_port(dev_net(dev), skb, cfg->port_min,
cfg->port_max, true);
@@ -3420,9 +3430,10 @@ static void vxlan_offload_rx_ports(struct net_device *dev, bool push)
static void vxlan_free_dev(struct net_device *dev)
{
struct vxlan_dev *vxlan = netdev_priv(dev);
+ struct vxlan_config *cfg = rcu_dereference_protected(vxlan->cfg, 1);
- kfree(vxlan->cfg);
- vxlan->cfg = NULL;
+ RCU_INIT_POINTER(vxlan->cfg, NULL);
+ kfree(cfg);
}
/* Initialize the device structure. */
@@ -3717,7 +3728,7 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, bool ipv6,
static int __vxlan_sock_add(struct vxlan_dev *vxlan, bool ipv6)
{
- const struct vxlan_config *cfg = vxlan->cfg;
+ const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg);
bool metadata = cfg->flags & VXLAN_F_COLLECT_METADATA;
struct vxlan_sock *vs = NULL;
struct vxlan_dev_node *node;
@@ -3767,7 +3778,7 @@ static int __vxlan_sock_add(struct vxlan_dev *vxlan, bool ipv6)
static int vxlan_sock_add(struct vxlan_dev *vxlan)
{
- const struct vxlan_config *cfg = vxlan->cfg;
+ const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg);
bool metadata, ipv6, ipv4;
int ret = 0;
@@ -3803,7 +3814,7 @@ int vxlan_vni_in_use(struct net *src_net, struct vxlan_dev *vxlan,
if (tmp == vxlan)
continue;
- tmp_cfg = tmp->cfg;
+ tmp_cfg = rtnl_dereference(tmp->cfg);
if (tmp_cfg->flags & VXLAN_F_VNIFILTER) {
if (!vxlan_vnifilter_lookup(tmp, vni))
@@ -4034,9 +4045,10 @@ static void vxlan_config_apply(struct net_device *dev,
needed_headroom += vxlan_headroom(flags);
dev->needed_headroom = needed_headroom;
- old_cfg = vxlan->cfg;
- vxlan->cfg = new_cfg;
- kfree(old_cfg);
+ old_cfg = rtnl_dereference(vxlan->cfg);
+ rcu_assign_pointer(vxlan->cfg, new_cfg);
+ if (old_cfg)
+ kfree_rcu(old_cfg, rcu);
}
static int vxlan_dev_configure(struct net *src_net, struct net_device *dev,
@@ -4077,7 +4089,7 @@ static int vxlan_dev_create(struct net *net, struct net_device *dev,
if (err)
return err;
- cfg = vxlan->cfg;
+ cfg = rtnl_dereference(vxlan->cfg);
dev->ethtool_ops = &vxlan_ethtool_ops;
@@ -4177,7 +4189,7 @@ static int vxlan_nl2conf(struct nlattr *tb[], struct nlattr *data[],
/* if changelink operation, start with old existing cfg */
if (changelink) {
- const struct vxlan_config *cfg = vxlan->cfg;
+ const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg);
if (cfg)
memcpy(conf, cfg, sizeof(*conf));
@@ -4528,7 +4540,7 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[],
struct netlink_ext_ack *extack)
{
struct vxlan_dev *vxlan = netdev_priv(dev);
- const struct vxlan_config *cfg = vxlan->cfg;
+ const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg);
bool rem_ip_changed, change_igmp;
struct net_device *lowerdev;
struct vxlan_config conf;
@@ -4689,7 +4701,7 @@ static int vxlan_fill_info(struct sk_buff *skb, const struct net_device *dev)
struct ifla_vxlan_port_range ports;
const struct vxlan_config *cfg;
- cfg = vxlan->cfg;
+ cfg = rtnl_dereference(vxlan->cfg);
if (nla_put_u32(skb, IFLA_VXLAN_ID, be32_to_cpu(dst->remote_vni)))
goto nla_put_failure;
diff --git a/drivers/net/vxlan/vxlan_mdb.c b/drivers/net/vxlan/vxlan_mdb.c
index e34f783a192d531a657f631d59ee428945855d4f..dac224dce9b6ca0c23f9139a7a993a6e061b5c79 100644
--- a/drivers/net/vxlan/vxlan_mdb.c
+++ b/drivers/net/vxlan/vxlan_mdb.c
@@ -165,7 +165,7 @@ static int vxlan_mdb_entry_info_fill(const struct vxlan_dev *vxlan,
const struct vxlan_mdb_entry *mdb_entry,
const struct vxlan_mdb_remote *remote)
{
- const struct vxlan_config *cfg = vxlan->cfg;
+ const struct vxlan_config *cfg = rcu_dereference_rtnl(vxlan->cfg);
struct vxlan_rdst *rd = rtnl_dereference(remote->rd);
struct br_mdb_entry e;
struct nlattr *nest;
@@ -606,7 +606,9 @@ static int vxlan_mdb_config_init(struct vxlan_mdb_config *cfg,
{
struct br_mdb_entry *entry = nla_data(tb[MDBA_SET_ENTRY]);
struct vxlan_dev *vxlan = netdev_priv(dev);
- const struct vxlan_config *vcfg = vxlan->cfg;
+ const struct vxlan_config *vcfg;
+
+ vcfg = rtnl_dereference(vxlan->cfg);
memset(cfg, 0, sizeof(*cfg));
cfg->vxlan = vxlan;
@@ -951,12 +953,12 @@ vxlan_mdb_nlmsg_remote_size(const struct vxlan_dev *vxlan,
const struct vxlan_mdb_entry *mdb_entry,
const struct vxlan_mdb_remote *remote)
{
- const struct vxlan_config *cfg = vxlan->cfg;
+ const struct vxlan_config *cfg = rcu_dereference_rtnl(vxlan->cfg);
const struct vxlan_mdb_entry_key *group = &mdb_entry->key;
struct vxlan_rdst *rd = rtnl_dereference(remote->rd);
size_t nlmsg_size;
- /* MDBA_MDB_ENTRY_INFO */
+ /* MDBA_MDB_ENTRY_INFO */
nlmsg_size = nla_total_size(sizeof(struct br_mdb_entry)) +
/* MDBA_MDB_EATTR_TIMER */
nla_total_size(sizeof(u32));
diff --git a/drivers/net/vxlan/vxlan_multicast.c b/drivers/net/vxlan/vxlan_multicast.c
index 6a95b6844cf442e9c9eb2311793c31f4ce824d70..e2cf10da274f1b608d8bb5020d2b87ebfedeff46 100644
--- a/drivers/net/vxlan/vxlan_multicast.c
+++ b/drivers/net/vxlan/vxlan_multicast.c
@@ -147,6 +147,8 @@ bool vxlan_group_used(struct vxlan_net *vn, struct vxlan_dev *dev,
#endif
list_for_each_entry(vxlan, &vn->vxlan_list, next) {
+ const struct vxlan_config *cfg;
+
if (!netif_running(vxlan->dev) || vxlan == dev)
continue;
@@ -158,7 +160,9 @@ bool vxlan_group_used(struct vxlan_net *vn, struct vxlan_dev *dev,
rtnl_dereference(vxlan->vn6_sock) != sock6)
continue;
#endif
- if (vxlan->cfg->flags & VXLAN_F_VNIFILTER) {
+ cfg = rtnl_dereference(vxlan->cfg);
+
+ if (cfg->flags & VXLAN_F_VNIFILTER) {
if (!vxlan_group_used_by_vnifilter(vxlan, ip, ifindex))
continue;
} else {
@@ -233,6 +237,7 @@ static int vxlan_multicast_leave_vnigrp(struct vxlan_dev *vxlan)
int vxlan_multicast_join(struct vxlan_dev *vxlan)
{
+ const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg);
int ret = 0;
if (vxlan_addr_multicast(&vxlan->default_dst.remote_ip)) {
@@ -244,7 +249,7 @@ int vxlan_multicast_join(struct vxlan_dev *vxlan)
return ret;
}
- if (vxlan->cfg->flags & VXLAN_F_VNIFILTER)
+ if (cfg->flags & VXLAN_F_VNIFILTER)
return vxlan_multicast_join_vnigrp(vxlan);
return 0;
@@ -252,6 +257,7 @@ int vxlan_multicast_join(struct vxlan_dev *vxlan)
int vxlan_multicast_leave(struct vxlan_dev *vxlan)
{
+ const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg);
struct vxlan_net *vn = net_generic(vxlan->net, vxlan_net_id);
int ret = 0;
@@ -263,7 +269,7 @@ int vxlan_multicast_leave(struct vxlan_dev *vxlan)
return ret;
}
- if (vxlan->cfg->flags & VXLAN_F_VNIFILTER)
+ if (cfg->flags & VXLAN_F_VNIFILTER)
return vxlan_multicast_leave_vnigrp(vxlan);
return 0;
diff --git a/drivers/net/vxlan/vxlan_vnifilter.c b/drivers/net/vxlan/vxlan_vnifilter.c
index 8f6c01930ace77776dd6f2d3197e9325fdfd8bbf..1069d6b1b2955c54d0ce0f89860390ebc0ff8f15 100644
--- a/drivers/net/vxlan/vxlan_vnifilter.c
+++ b/drivers/net/vxlan/vxlan_vnifilter.c
@@ -337,13 +337,15 @@ static int vxlan_vnifilter_dump_dev(const struct net_device *dev,
struct vxlan_vni_node *v, *vbegin = NULL, *vend = NULL;
struct vxlan_dev *vxlan = netdev_priv(dev);
struct tunnel_msg *new_tmsg, *tmsg;
+ const struct vxlan_config *cfg;
int idx = 0, s_idx = cb->args[1];
struct vxlan_vni_group *vg;
struct nlmsghdr *nlh;
bool dump_stats;
int err = 0;
- if (!(vxlan->cfg->flags & VXLAN_F_VNIFILTER)) {
+ cfg = rcu_dereference(vxlan->cfg);
+ if (!(cfg->flags & VXLAN_F_VNIFILTER)) {
cb->args[1] = 0;
return -EINVAL;
}
@@ -480,6 +482,7 @@ static int vxlan_update_default_fdb_entry(struct vxlan_dev *vxlan, __be32 vni,
union vxlan_addr *remote_ip,
struct netlink_ext_ack *extack)
{
+ const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg);
struct vxlan_rdst *dst = &vxlan->default_dst;
int err = 0;
@@ -489,7 +492,7 @@ static int vxlan_update_default_fdb_entry(struct vxlan_dev *vxlan, __be32 vni,
remote_ip,
NUD_REACHABLE | NUD_PERMANENT,
NLM_F_APPEND | NLM_F_CREATE,
- vxlan->cfg->dst_port,
+ cfg->dst_port,
vni,
vni,
dst->remote_ifindex,
@@ -503,7 +506,7 @@ static int vxlan_update_default_fdb_entry(struct vxlan_dev *vxlan, __be32 vni,
if (old_remote_ip && !vxlan_addr_any(old_remote_ip)) {
__vxlan_fdb_delete(vxlan, all_zeros_mac,
*old_remote_ip,
- vxlan->cfg->dst_port,
+ cfg->dst_port,
vni, vni,
dst->remote_ifindex,
true);
@@ -617,6 +620,7 @@ static void vxlan_vni_delete_group(struct vxlan_dev *vxlan,
struct vxlan_vni_node *vninode)
{
struct vxlan_net *vn = net_generic(vxlan->net, vxlan_net_id);
+ const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg);
struct vxlan_rdst *dst = &vxlan->default_dst;
/* if per vni remote_ip not present, delete the
@@ -628,7 +632,7 @@ static void vxlan_vni_delete_group(struct vxlan_dev *vxlan,
__vxlan_fdb_delete(vxlan, all_zeros_mac,
(vxlan_addr_any(&vninode->remote_ip) ?
dst->remote_ip : vninode->remote_ip),
- vxlan->cfg->dst_port,
+ cfg->dst_port,
vninode->vni, vninode->vni,
dst->remote_ifindex,
true);
@@ -728,6 +732,7 @@ static int vxlan_vni_add(struct vxlan_dev *vxlan,
u32 vni, union vxlan_addr *group,
struct netlink_ext_ack *extack)
{
+ const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg);
struct vxlan_vni_node *vninode;
__be32 v = cpu_to_be32(vni);
bool changed = false;
@@ -736,7 +741,7 @@ static int vxlan_vni_add(struct vxlan_dev *vxlan,
if (vxlan_vnifilter_lookup(vxlan, v))
return vxlan_vni_update(vxlan, vg, v, group, &changed, extack);
- err = vxlan_vni_in_use(vxlan->net, vxlan, vxlan->cfg, v);
+ err = vxlan_vni_in_use(vxlan->net, vxlan, cfg, v);
if (err) {
NL_SET_ERR_MSG(extack, "VNI in use");
return err;
@@ -946,6 +951,7 @@ static int vxlan_vnifilter_process(struct sk_buff *skb, struct nlmsghdr *nlh,
struct netlink_ext_ack *extack)
{
struct net *net = sock_net(skb->sk);
+ const struct vxlan_config *cfg;
struct tunnel_msg *tmsg;
struct vxlan_dev *vxlan;
struct net_device *dev;
@@ -970,8 +976,9 @@ static int vxlan_vnifilter_process(struct sk_buff *skb, struct nlmsghdr *nlh,
}
vxlan = netdev_priv(dev);
+ cfg = rtnl_dereference(vxlan->cfg);
- if (!(vxlan->cfg->flags & VXLAN_F_VNIFILTER))
+ if (!(cfg->flags & VXLAN_F_VNIFILTER))
return -EOPNOTSUPP;
nlmsg_for_each_attr_type(attr, VXLAN_VNIFILTER_ENTRY, nlh,
diff --git a/include/net/vxlan.h b/include/net/vxlan.h
index 9b39f34b7eada647eb56512ede094555439f97e5..7eb4f8110a84872f1d0451f7ccef3a73ba5c2e7a 100644
--- a/include/net/vxlan.h
+++ b/include/net/vxlan.h
@@ -229,6 +229,7 @@ struct vxlan_config {
bool no_share;
enum ifla_vxlan_df df;
struct vxlanhdr reserved_bits;
+ struct rcu_head rcu;
};
enum {
@@ -303,7 +304,7 @@ struct vxlan_dev {
struct gro_cells gro_cells;
unsigned long flags;
- struct vxlan_config *cfg;
+ struct vxlan_config __rcu *cfg;
struct vxlan_vni_group __rcu *vnigrp;
--
2.55.0.970.g62bdec98f9-goog
^ permalink raw reply related [flat|nested] 28+ messages in thread* Re: [PATCH net-next 7/9] vxlan: convert configuration to RCU protection
2026-09-03 12:08 ` [PATCH net-next 7/9] vxlan: convert configuration to RCU protection Eric Dumazet
@ 2026-09-05 4:33 ` Kuniyuki Iwashima
2026-09-07 6:11 ` netdev-bot+sashiko
1 sibling, 0 replies; 28+ messages in thread
From: Kuniyuki Iwashima @ 2026-09-05 4:33 UTC (permalink / raw)
To: Eric Dumazet
Cc: David S . Miller, Jakub Kicinski, Paolo Abeni, Simon Horman,
Ido Schimmel, Andrew Lunn, netdev, eric.dumazet
On Thu, Sep 3, 2026 at 5:08 AM Eric Dumazet <edumazet@google.com> wrote:
>
> In order to allow lockless readers in future patches, convert 'vxlan->cfg'
> to an RCU protected pointer.
>
> Updating configuration via vxlan_changelink() or vxlan_dev_configure()
> now uses rcu_assign_pointer() to publish it, freeing the previous config
> with kfree_rcu().
>
> Signed-off-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com>
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH net-next 7/9] vxlan: convert configuration to RCU protection
2026-09-03 12:08 ` [PATCH net-next 7/9] vxlan: convert configuration to RCU protection Eric Dumazet
2026-09-05 4:33 ` Kuniyuki Iwashima
@ 2026-09-07 6:11 ` netdev-bot+sashiko
1 sibling, 0 replies; 28+ messages in thread
From: netdev-bot+sashiko @ 2026-09-07 6:11 UTC (permalink / raw)
To: edumazet
Cc: davem, kuba, pabeni, horms, kuniyu, idosch, andrew+netdev, netdev,
eric.dumazet
Thank you for your contribution! Sashiko AI review found 1 potential
issue(s) to consider:
Critical: 0 · High: 0 · Medium: 1 · Low: 0
- [Medium] Commit message mischaracterizes a memory-safety fix as
preparatory refactoring, leaving a transient use-after-free in the…
--
vxlan: convert configuration to RCU protection
This converts vxlan->cfg into an __rcu pointer, switches the readers to
rcu_dereference()/rtnl_dereference() and publishes new configurations with
rcu_assign_pointer() while freeing the old one via kfree_rcu(). The TX path
in vxlan_xmit() also grows an rcu_read_lock() section covering all of its
exit paths.
> diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c
> index af320173a1e7..c49d55de020f 100644
> --- a/drivers/net/vxlan/vxlan_core.c
> +++ b/drivers/net/vxlan/vxlan_core.c
[ ... ]
> @@ -1704,7 +1704,7 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
> goto drop;
> }
>
> - cfg = vxlan->cfg;
> + cfg = rcu_dereference(vxlan->cfg);
>
> if (vh->vx_flags & cfg->reserved_bits.vx_flags ||
> vh->vx_vni & cfg->reserved_bits.vx_vni) {
[ ... ]
> @@ -2781,7 +2781,8 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
> u32 nhid = 0;
> u32 flags;
>
> - cfg = vxlan->cfg;
> + rcu_read_lock();
> + cfg = rcu_dereference(vxlan->cfg);
> flags = cfg->flags;
> default_vni = cfg->vni;
> saddr_family = cfg->saddr.sa.sa_family;
[ ... ]
> @@ -4034,9 +4045,10 @@ static void vxlan_config_apply(struct net_device *dev,
> needed_headroom += vxlan_headroom(flags);
> dev->needed_headroom = needed_headroom;
>
> - old_cfg = vxlan->cfg;
> - vxlan->cfg = new_cfg;
> - kfree(old_cfg);
> + old_cfg = rtnl_dereference(vxlan->cfg);
> + rcu_assign_pointer(vxlan->cfg, new_cfg);
> + if (old_cfg)
> + kfree_rcu(old_cfg, rcu);
> }
[Severity: Medium]
Is this change really just preparation, or is it the patch that closes a
use-after-free opened by the preceding patch in the series?
The changelog says:
In order to allow lockless readers in future patches, convert 'vxlan->cfg'
to an RCU protected pointer.
but the lockless readers seem to exist already. At the parent commit
5a2b432e8977 ("vxlan: dynamically allocate struct vxlan_config"),
vxlan_config_apply() does:
old_cfg = vxlan->cfg;
vxlan->cfg = new_cfg;
kfree(old_cfg);
with no grace period, while vxlan_xmit() (softirq) does a plain
cfg = vxlan->cfg; and hands that pointer down to vxlan_xmit_one(),
arp_reduce(), neigh_reduce() and vxlan_xmit_nhid(), and vxlan_rcv() (UDP
encap softirq) does the same before dereferencing cfg->reserved_bits.
vxlan_vs_find_vni(), the vxlan_cleanup() age timer and
vxlan_fill_metadata_dst() read it the same way.
So at that intermediate commit:
CPU0 (RTNL, RTM_NEWLINK changelink)
vxlan_changelink()
vxlan_config_apply()
kfree(old_cfg);
CPU1 (softirq)
vxlan_xmit()
cfg = vxlan->cfg; /* may already be the freed old_cfg */
flags = cfg->flags;
Can an "ip link set ... type vxlan ..." concurrent with traffic free the
config under a live TX or RX reader at that commit?
If so, would it make sense to squash the two patches, or at least say in
this changelog that the previous patch opens that window and this one closes
it, so a partial backport or a bisect does not land on the unsafe commit?
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260903120840.1024153-1-edumazet%40google.com
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH net-next 8/9] vxlan: remove default_dst and use vxlan_config and lowerdev
2026-09-03 12:08 [PATCH net-next 0/9] vxlan: convert configuration to RCU and drop RTNL in vxlan_fill_info() Eric Dumazet
` (6 preceding siblings ...)
2026-09-03 12:08 ` [PATCH net-next 7/9] vxlan: convert configuration to RCU protection Eric Dumazet
@ 2026-09-03 12:08 ` Eric Dumazet
2026-09-05 4:44 ` Kuniyuki Iwashima
2026-09-07 6:11 ` netdev-bot+sashiko
2026-09-03 12:08 ` [PATCH net-next 9/9] vxlan: no longer rely on RTNL in vxlan_fill_info() Eric Dumazet
8 siblings, 2 replies; 28+ messages in thread
From: Eric Dumazet @ 2026-09-03 12:08 UTC (permalink / raw)
To: David S . Miller, Jakub Kicinski, Paolo Abeni
Cc: Simon Horman, Kuniyuki Iwashima, Ido Schimmel, Andrew Lunn,
netdev, eric.dumazet, Eric Dumazet
Now that vxlan->cfg is an RCU-protected pointer, storing default
destination attributes (remote_ip, remote_vni, remote_ifindex) in
vxlan->default_dst is redundant and creates potential data races for
lockless readers.
Furthermore, several fields of struct vxlan_rdst (remote_port,
offloaded, list, rcu, dst_cache) in default_dst were completely unused.
Replace vxlan->default_dst with a 'struct net_device *lowerdev' pointer
in struct vxlan_dev to track adjacent upper/lower netdev topology under
RTNL, and switch all remaining users over to reading configuration
attributes from vxlan->cfg.
Also update mlx5e_tc_tun_get_remote_ifindex() to read remote_ifindex
from vxlan->cfg under rcu_read_lock().
While updating lowerdev handling in vxlan_changelink(), avoid clobbering
lowerdev to NULL when unchanged, and properly unlink and clear
vxlan->lowerdev if the lower device is removed.
Signed-off-by: Eric Dumazet <edumazet@google.com>
---
.../mellanox/mlx5/core/en/tc_tun_vxlan.c | 11 +-
drivers/net/vxlan/vxlan_core.c | 152 +++++++++---------
drivers/net/vxlan/vxlan_mdb.c | 14 +-
drivers/net/vxlan/vxlan_multicast.c | 68 ++++----
drivers/net/vxlan/vxlan_private.h | 10 +-
drivers/net/vxlan/vxlan_vnifilter.c | 40 ++---
include/net/vxlan.h | 2 +-
7 files changed, 160 insertions(+), 137 deletions(-)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_vxlan.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_vxlan.c
index 7a18a469961db809890d69f7d6d8bc656e560946..467fbe43b89e9bc3d28047a3a17a84495c3875af 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_vxlan.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_vxlan.c
@@ -241,9 +241,16 @@ static bool mlx5e_tc_tun_encap_info_equal_vxlan(struct mlx5e_encap_key *a,
static int mlx5e_tc_tun_get_remote_ifindex(struct net_device *mirred_dev)
{
const struct vxlan_dev *vxlan = netdev_priv(mirred_dev);
- const struct vxlan_rdst *dst = &vxlan->default_dst;
+ const struct vxlan_config *cfg;
+ int ifindex = 0;
- return dst->remote_ifindex;
+ rcu_read_lock();
+ cfg = rcu_dereference(vxlan->cfg);
+ if (cfg)
+ ifindex = cfg->remote_ifindex;
+ rcu_read_unlock();
+
+ return ifindex;
}
struct mlx5e_tc_tunnel vxlan_tunnel = {
diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c
index c49d55de020f49b68e322de2a4b0a5c0be0471ed..453cac5dde67ffe969edf9d3bdafe834c05632ad 100644
--- a/drivers/net/vxlan/vxlan_core.c
+++ b/drivers/net/vxlan/vxlan_core.c
@@ -804,6 +804,7 @@ static int vxlan_fdb_nh_update(struct vxlan_dev *vxlan, struct vxlan_fdb *fdb,
u32 nhid, struct netlink_ext_ack *extack)
{
struct nexthop *old_nh = rtnl_dereference(fdb->nh);
+ const struct vxlan_config *cfg;
struct nexthop *nh;
int err = -EINVAL;
@@ -832,7 +833,8 @@ static int vxlan_fdb_nh_update(struct vxlan_dev *vxlan, struct vxlan_fdb *fdb,
}
/* check nexthop group family */
- switch (vxlan->default_dst.remote_ip.sa.sa_family) {
+ cfg = rtnl_dereference(vxlan->cfg);
+ switch (cfg->remote_ip.sa.sa_family) {
case AF_INET:
if (!nexthop_has_v4(nh)) {
err = -EAFNOSUPPORT;
@@ -1243,6 +1245,7 @@ static int vxlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
const unsigned char *addr, u16 vid, u16 flags,
bool *notified, struct netlink_ext_ack *extack)
{
+ const struct vxlan_config *cfg;
struct vxlan_dev *vxlan = netdev_priv(dev);
/* struct net *net = dev_net(vxlan->dev); */
union vxlan_addr ip;
@@ -1265,7 +1268,8 @@ static int vxlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
if (err)
return err;
- if (vxlan->default_dst.remote_ip.sa.sa_family != ip.sa.sa_family)
+ cfg = rtnl_dereference(vxlan->cfg);
+ if (cfg->remote_ip.sa.sa_family != ip.sa.sa_family)
return -EAFNOSUPPORT;
spin_lock_bh(&vxlan->hash_lock);
@@ -2303,7 +2307,14 @@ static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan,
skb->dev = dev;
__skb_pull(skb, skb_network_offset(skb));
- if (dst_vxlan->default_dst.remote_ip.sa.sa_family == AF_INET) {
+ rcu_read_lock();
+ dst_cfg = rcu_dereference(dst_vxlan->cfg);
+ if (unlikely(!dst_cfg || !(dev->flags & IFF_UP))) {
+ kfree_skb_reason(skb, SKB_DROP_REASON_DEV_READY);
+ goto drop;
+ }
+
+ if (dst_cfg->remote_ip.sa.sa_family == AF_INET) {
loopback.sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
loopback.sa.sa_family = AF_INET;
#if IS_ENABLED(CONFIG_IPV6)
@@ -2313,13 +2324,6 @@ static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan,
#endif
}
- rcu_read_lock();
- dst_cfg = rcu_dereference(dst_vxlan->cfg);
- if (unlikely(!dst_cfg || !(dev->flags & IFF_UP))) {
- kfree_skb_reason(skb, SKB_DROP_REASON_DEV_READY);
- goto drop;
- }
-
if ((dst_cfg->flags & VXLAN_F_LEARN) && snoop)
vxlan_snoop(dev, dst_cfg, &loopback, eth_hdr(skb)->h_source, 0, vni);
@@ -2333,8 +2337,9 @@ static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan,
} else {
drop:
dev_dstats_rx_dropped(dev);
- vxlan_vnifilter_count(dst_vxlan, dst_cfg, vni, NULL,
- VXLAN_VNI_STATS_RX_DROPS, 0);
+ if (dst_cfg)
+ vxlan_vnifilter_count(dst_vxlan, dst_cfg, vni, NULL,
+ VXLAN_VNI_STATS_RX_DROPS, 0);
}
rcu_read_unlock();
}
@@ -2960,10 +2965,14 @@ static void vxlan_vs_del_dev(struct vxlan_dev *vxlan)
static void vxlan_vs_add_dev(struct vxlan_sock *vs, struct vxlan_dev *vxlan,
struct vxlan_dev_node *node)
{
- __be32 vni = vxlan->default_dst.remote_vni;
+ const struct vxlan_config *cfg;
+ __be32 vni;
ASSERT_RTNL();
+ cfg = rtnl_dereference(vxlan->cfg);
+ vni = cfg->vni;
+
node->vxlan = vxlan;
hlist_add_head_rcu(&node->hlist, vni_head(vs, vni));
}
@@ -3285,13 +3294,12 @@ static void vxlan_set_multicast_list(struct net_device *dev)
static int vxlan_change_mtu(struct net_device *dev, int new_mtu)
{
struct vxlan_dev *vxlan = netdev_priv(dev);
- struct vxlan_rdst *dst = &vxlan->default_dst;
const struct vxlan_config *cfg;
struct net_device *lowerdev;
cfg = rtnl_dereference(vxlan->cfg);
- lowerdev = __dev_get_by_index(vxlan->net, dst->remote_ifindex);
+ lowerdev = __dev_get_by_index(vxlan->net, cfg->remote_ifindex);
/* This check is different than dev->max_mtu, because it looks at
* the lowerdev->mtu, rather than the static dev->max_mtu
@@ -3620,9 +3628,11 @@ static int vxlan_get_link_ksettings(struct net_device *dev,
struct ethtool_link_ksettings *cmd)
{
struct vxlan_dev *vxlan = netdev_priv(dev);
- struct vxlan_rdst *dst = &vxlan->default_dst;
- struct net_device *lowerdev = __dev_get_by_index(vxlan->net,
- dst->remote_ifindex);
+ const struct vxlan_config *cfg;
+ struct net_device *lowerdev;
+
+ cfg = rtnl_dereference(vxlan->cfg);
+ lowerdev = __dev_get_by_index(vxlan->net, cfg->remote_ifindex);
if (!lowerdev) {
cmd->base.duplex = DUPLEX_UNKNOWN;
@@ -3997,7 +4007,6 @@ static void vxlan_config_apply(struct net_device *dev,
bool changelink)
{
struct vxlan_dev *vxlan = netdev_priv(dev);
- struct vxlan_rdst *dst = &vxlan->default_dst;
unsigned short needed_headroom = ETH_HLEN;
struct vxlan_config *old_cfg;
int max_mtu = ETH_MAX_MTU;
@@ -4015,13 +4024,7 @@ static void vxlan_config_apply(struct net_device *dev,
vxlan->net = src_net;
}
- dst->remote_vni = new_cfg->vni;
-
- memcpy(&dst->remote_ip, &new_cfg->remote_ip, sizeof(new_cfg->remote_ip));
-
if (lowerdev) {
- dst->remote_ifindex = new_cfg->remote_ifindex;
-
netif_inherit_tso_max(dev, lowerdev);
needed_headroom = lowerdev->hard_header_len;
@@ -4081,10 +4084,8 @@ static int vxlan_dev_create(struct net *net, struct net_device *dev,
struct vxlan_dev *vxlan = netdev_priv(dev);
struct net_device *remote_dev = NULL;
const struct vxlan_config *cfg;
- struct vxlan_rdst *dst;
int err;
- dst = &vxlan->default_dst;
err = vxlan_dev_configure(net, dev, conf, extack);
if (err)
return err;
@@ -4099,8 +4100,8 @@ static int vxlan_dev_create(struct net *net, struct net_device *dev,
return err;
}
- if (dst->remote_ifindex) {
- remote_dev = __dev_get_by_index(net, dst->remote_ifindex);
+ if (cfg->remote_ifindex) {
+ remote_dev = __dev_get_by_index(net, cfg->remote_ifindex);
if (!remote_dev) {
err = -ENODEV;
goto unregister;
@@ -4110,7 +4111,7 @@ static int vxlan_dev_create(struct net *net, struct net_device *dev,
if (err)
goto unregister;
- dst->remote_dev = remote_dev;
+ vxlan->lowerdev = remote_dev;
}
err = rtnl_configure_link(dev, NULL, 0, NULL);
@@ -4118,16 +4119,18 @@ static int vxlan_dev_create(struct net *net, struct net_device *dev,
goto unlink;
/* create an fdb entry for a valid default destination */
- if (!vxlan_addr_any(&dst->remote_ip)) {
+ if (!vxlan_addr_any(&cfg->remote_ip)) {
+ union vxlan_addr rip = cfg->remote_ip;
+
spin_lock_bh(&vxlan->hash_lock);
err = vxlan_fdb_update(vxlan, all_zeros_mac,
- &dst->remote_ip,
+ &rip,
NUD_REACHABLE | NUD_PERMANENT,
NLM_F_EXCL | NLM_F_CREATE,
cfg->dst_port,
- dst->remote_vni,
- dst->remote_vni,
- dst->remote_ifindex,
+ cfg->vni,
+ cfg->vni,
+ cfg->remote_ifindex,
NTF_SELF, 0, true, extack);
spin_unlock_bh(&vxlan->hash_lock);
if (err)
@@ -4545,13 +4548,11 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[],
struct net_device *lowerdev;
struct vxlan_config conf;
struct vxlan_config *new_cfg;
- struct vxlan_rdst *dst;
int err;
if (!rtnl_dev_link_net_capable(dev, vxlan->net))
return -EPERM;
- dst = &vxlan->default_dst;
err = vxlan_nl2conf(tb, data, dev, &conf, true, extack);
if (err)
return err;
@@ -4565,20 +4566,19 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[],
if (!new_cfg)
return -ENOMEM;
- if (dst->remote_dev == lowerdev)
- lowerdev = NULL;
-
- err = netdev_adjacent_change_prepare(dst->remote_dev, lowerdev, dev,
- extack);
- if (err) {
- kfree(new_cfg);
- return err;
+ if (vxlan->lowerdev != lowerdev) {
+ err = netdev_adjacent_change_prepare(vxlan->lowerdev, lowerdev,
+ dev, extack);
+ if (err) {
+ kfree(new_cfg);
+ return err;
+ }
}
- rem_ip_changed = !vxlan_addr_equal(&conf.remote_ip, &dst->remote_ip);
+ rem_ip_changed = !vxlan_addr_equal(&conf.remote_ip, &cfg->remote_ip);
change_igmp = vxlan->dev->flags & IFF_UP &&
(rem_ip_changed ||
- dst->remote_ifindex != conf.remote_ifindex);
+ cfg->remote_ifindex != conf.remote_ifindex);
/* handle default dst entry */
if (rem_ip_changed) {
@@ -4594,19 +4594,20 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[],
NTF_SELF, 0, true, extack);
if (err) {
spin_unlock_bh(&vxlan->hash_lock);
- netdev_adjacent_change_abort(dst->remote_dev,
- lowerdev, dev);
+ if (vxlan->lowerdev != lowerdev)
+ netdev_adjacent_change_abort(vxlan->lowerdev,
+ lowerdev, dev);
kfree(new_cfg);
return err;
}
}
- if (!vxlan_addr_any(&dst->remote_ip))
+ if (!vxlan_addr_any(&cfg->remote_ip))
__vxlan_fdb_delete(vxlan, all_zeros_mac,
- dst->remote_ip,
+ cfg->remote_ip,
cfg->dst_port,
- dst->remote_vni,
- dst->remote_vni,
- dst->remote_ifindex,
+ cfg->vni,
+ cfg->vni,
+ cfg->remote_ifindex,
true);
spin_unlock_bh(&vxlan->hash_lock);
@@ -4614,30 +4615,36 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[],
* all vnis that were using default remote ip
*/
if (cfg->flags & VXLAN_F_VNIFILTER) {
- err = vxlan_vnilist_update_group(vxlan, &dst->remote_ip,
+ err = vxlan_vnilist_update_group(vxlan, &cfg->remote_ip,
&conf.remote_ip, extack);
if (err) {
- netdev_adjacent_change_abort(dst->remote_dev,
- lowerdev, dev);
+ if (vxlan->lowerdev != lowerdev)
+ netdev_adjacent_change_abort(vxlan->lowerdev,
+ lowerdev, dev);
kfree(new_cfg);
return err;
}
}
}
- if (change_igmp && vxlan_addr_multicast(&dst->remote_ip))
+ if (change_igmp && vxlan_addr_multicast(&cfg->remote_ip))
err = vxlan_multicast_leave(vxlan);
if (netif_running(dev) && conf.age_interval != cfg->age_interval)
mod_timer(&vxlan->age_timer, jiffies);
- netdev_adjacent_change_commit(dst->remote_dev, lowerdev, dev);
- if (lowerdev && lowerdev != dst->remote_dev)
- dst->remote_dev = lowerdev;
+ if (vxlan->lowerdev != lowerdev) {
+ if (lowerdev)
+ netdev_adjacent_change_commit(vxlan->lowerdev, lowerdev,
+ dev);
+ else
+ netdev_upper_dev_unlink(vxlan->lowerdev, dev);
+ vxlan->lowerdev = lowerdev;
+ }
vxlan_config_apply(dev, new_cfg, lowerdev, vxlan->net, true);
if (!err && change_igmp &&
- vxlan_addr_multicast(&dst->remote_ip))
+ vxlan_addr_multicast(&new_cfg->remote_ip))
err = vxlan_multicast_join(vxlan);
return err;
@@ -4652,8 +4659,8 @@ static void vxlan_dellink(struct net_device *dev, struct list_head *head)
list_del(&vxlan->next);
unregister_netdevice_queue(dev, head);
- if (vxlan->default_dst.remote_dev)
- netdev_upper_dev_unlink(vxlan->default_dst.remote_dev, dev);
+ if (vxlan->lowerdev)
+ netdev_upper_dev_unlink(vxlan->lowerdev, dev);
}
static size_t vxlan_get_size(const struct net_device *dev)
@@ -4697,30 +4704,29 @@ static size_t vxlan_get_size(const struct net_device *dev)
static int vxlan_fill_info(struct sk_buff *skb, const struct net_device *dev)
{
const struct vxlan_dev *vxlan = netdev_priv(dev);
- const struct vxlan_rdst *dst = &vxlan->default_dst;
struct ifla_vxlan_port_range ports;
const struct vxlan_config *cfg;
cfg = rtnl_dereference(vxlan->cfg);
- if (nla_put_u32(skb, IFLA_VXLAN_ID, be32_to_cpu(dst->remote_vni)))
+ if (nla_put_u32(skb, IFLA_VXLAN_ID, be32_to_cpu(cfg->vni)))
goto nla_put_failure;
- if (!vxlan_addr_any(&dst->remote_ip)) {
- if (dst->remote_ip.sa.sa_family == AF_INET) {
+ if (!vxlan_addr_any(&cfg->remote_ip)) {
+ if (cfg->remote_ip.sa.sa_family == AF_INET) {
if (nla_put_in_addr(skb, IFLA_VXLAN_GROUP,
- dst->remote_ip.sin.sin_addr.s_addr))
+ cfg->remote_ip.sin.sin_addr.s_addr))
goto nla_put_failure;
#if IS_ENABLED(CONFIG_IPV6)
} else {
if (nla_put_in6_addr(skb, IFLA_VXLAN_GROUP6,
- &dst->remote_ip.sin6.sin6_addr))
+ &cfg->remote_ip.sin6.sin6_addr))
goto nla_put_failure;
#endif
}
}
- if (dst->remote_ifindex && nla_put_u32(skb, IFLA_VXLAN_LINK, dst->remote_ifindex))
+ if (cfg->remote_ifindex && nla_put_u32(skb, IFLA_VXLAN_LINK, cfg->remote_ifindex))
goto nla_put_failure;
if (!vxlan_addr_any(&cfg->saddr)) {
@@ -4835,7 +4841,7 @@ static void vxlan_handle_lowerdev_unregister(struct vxlan_net *vn,
LIST_HEAD(list_kill);
list_for_each_entry_safe(vxlan, next, &vn->vxlan_list, next) {
- struct vxlan_rdst *dst = &vxlan->default_dst;
+ const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg);
/* In case we created vxlan device with carrier
* and we loose the carrier due to module unload
@@ -4843,7 +4849,7 @@ static void vxlan_handle_lowerdev_unregister(struct vxlan_net *vn,
* cases, it's not necessary and remote_ifindex
* is 0 here, so no matches.
*/
- if (dst->remote_ifindex == dev->ifindex)
+ if (cfg->remote_ifindex == dev->ifindex)
vxlan_dellink(vxlan->dev, &list_kill);
}
diff --git a/drivers/net/vxlan/vxlan_mdb.c b/drivers/net/vxlan/vxlan_mdb.c
index dac224dce9b6ca0c23f9139a7a993a6e061b5c79..89e522a178ca189a58ec2fd1564ed1220b5b85a3 100644
--- a/drivers/net/vxlan/vxlan_mdb.c
+++ b/drivers/net/vxlan/vxlan_mdb.c
@@ -195,7 +195,7 @@ static int vxlan_mdb_entry_info_fill(const struct vxlan_dev *vxlan,
be16_to_cpu(rd->remote_port)))
goto nest_err;
- if (rd->remote_vni != vxlan->default_dst.remote_vni &&
+ if (rd->remote_vni != cfg->vni &&
nla_put_u32(skb, MDBA_MDB_EATTR_VNI, be32_to_cpu(rd->remote_vni)))
goto nest_err;
@@ -612,12 +612,12 @@ static int vxlan_mdb_config_init(struct vxlan_mdb_config *cfg,
memset(cfg, 0, sizeof(*cfg));
cfg->vxlan = vxlan;
- cfg->group.vni = vxlan->default_dst.remote_vni;
+ cfg->group.vni = vcfg->vni;
INIT_LIST_HEAD(&cfg->src_list);
cfg->nlflags = nlmsg_flags;
cfg->filter_mode = MCAST_EXCLUDE;
cfg->rt_protocol = RTPROT_STATIC;
- cfg->remote_vni = vxlan->default_dst.remote_vni;
+ cfg->remote_vni = vcfg->vni;
cfg->remote_port = vcfg->dst_port;
if (entry->ifindex != dev->ifindex) {
@@ -978,7 +978,7 @@ vxlan_mdb_nlmsg_remote_size(const struct vxlan_dev *vxlan,
if (rd->remote_port && rd->remote_port != cfg->dst_port)
nlmsg_size += nla_total_size(sizeof(u16));
/* MDBA_MDB_EATTR_VNI */
- if (rd->remote_vni != vxlan->default_dst.remote_vni)
+ if (rd->remote_vni != cfg->vni)
nlmsg_size += nla_total_size(sizeof(u32));
/* MDBA_MDB_EATTR_IFINDEX */
if (rd->remote_ifindex)
@@ -1480,11 +1480,13 @@ static int vxlan_mdb_get_parse(struct net_device *dev, struct nlattr *tb[],
{
struct br_mdb_entry *entry = nla_data(tb[MDBA_GET_ENTRY]);
struct nlattr *mdbe_attrs[MDBE_ATTR_MAX + 1];
+ const struct vxlan_config *cfg;
struct vxlan_dev *vxlan = netdev_priv(dev);
int err;
+ cfg = rtnl_dereference(vxlan->cfg);
memset(group, 0, sizeof(*group));
- group->vni = vxlan->default_dst.remote_vni;
+ group->vni = cfg->vni;
if (!tb[MDBA_GET_ENTRY_ATTRS]) {
vxlan_mdb_group_set(group, entry, NULL);
@@ -1633,7 +1635,7 @@ struct vxlan_mdb_entry *vxlan_mdb_entry_skb_get(struct vxlan_dev *vxlan,
* entries are stored with the VNI of the VXLAN device.
*/
if (!(cfg->flags & VXLAN_F_COLLECT_METADATA))
- src_vni = vxlan->default_dst.remote_vni;
+ src_vni = cfg->vni;
memset(&group, 0, sizeof(group));
group.vni = src_vni;
diff --git a/drivers/net/vxlan/vxlan_multicast.c b/drivers/net/vxlan/vxlan_multicast.c
index e2cf10da274f1b608d8bb5020d2b87ebfedeff46..ae0320921c8ca5da8c9fabda5852467f2872ce00 100644
--- a/drivers/net/vxlan/vxlan_multicast.c
+++ b/drivers/net/vxlan/vxlan_multicast.c
@@ -14,11 +14,12 @@
/* Update multicast group membership when first VNI on
* multicast address is brought up
*/
-int vxlan_igmp_join(struct vxlan_dev *vxlan, union vxlan_addr *rip,
+int vxlan_igmp_join(struct vxlan_dev *vxlan, const union vxlan_addr *rip,
int rifindex)
{
- union vxlan_addr *ip = (rip ? : &vxlan->default_dst.remote_ip);
- int ifindex = (rifindex ? : vxlan->default_dst.remote_ifindex);
+ const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg);
+ const union vxlan_addr *ip = (rip ? : &cfg->remote_ip);
+ int ifindex = (rifindex ? : cfg->remote_ifindex);
int ret = -EINVAL;
struct sock *sk;
@@ -47,11 +48,12 @@ int vxlan_igmp_join(struct vxlan_dev *vxlan, union vxlan_addr *rip,
return ret;
}
-int vxlan_igmp_leave(struct vxlan_dev *vxlan, union vxlan_addr *rip,
+int vxlan_igmp_leave(struct vxlan_dev *vxlan, const union vxlan_addr *rip,
int rifindex)
{
- union vxlan_addr *ip = (rip ? : &vxlan->default_dst.remote_ip);
- int ifindex = (rifindex ? : vxlan->default_dst.remote_ifindex);
+ const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg);
+ const union vxlan_addr *ip = (rip ? : &cfg->remote_ip);
+ int ifindex = (rifindex ? : cfg->remote_ifindex);
int ret = -EINVAL;
struct sock *sk;
@@ -80,8 +82,8 @@ int vxlan_igmp_leave(struct vxlan_dev *vxlan, union vxlan_addr *rip,
return ret;
}
-static bool vxlan_group_used_match(union vxlan_addr *ip, int ifindex,
- union vxlan_addr *rip, int rifindex)
+static bool vxlan_group_used_match(const union vxlan_addr *ip, int ifindex,
+ const union vxlan_addr *rip, int rifindex)
{
if (!vxlan_addr_multicast(rip))
return false;
@@ -96,14 +98,16 @@ static bool vxlan_group_used_match(union vxlan_addr *ip, int ifindex,
}
static bool vxlan_group_used_by_vnifilter(struct vxlan_dev *vxlan,
- union vxlan_addr *ip, int ifindex)
+ const struct vxlan_config *cfg,
+ const union vxlan_addr *ip,
+ int ifindex)
{
struct vxlan_vni_group *vg = rtnl_dereference(vxlan->vnigrp);
struct vxlan_vni_node *v, *tmp;
if (vxlan_group_used_match(ip, ifindex,
- &vxlan->default_dst.remote_ip,
- vxlan->default_dst.remote_ifindex))
+ &cfg->remote_ip,
+ cfg->remote_ifindex))
return true;
list_for_each_entry_safe(v, tmp, &vg->vni_list, vlist) {
@@ -112,7 +116,7 @@ static bool vxlan_group_used_by_vnifilter(struct vxlan_dev *vxlan,
if (vxlan_group_used_match(ip, ifindex,
&v->remote_ip,
- vxlan->default_dst.remote_ifindex))
+ cfg->remote_ifindex))
return true;
}
@@ -121,16 +125,17 @@ static bool vxlan_group_used_by_vnifilter(struct vxlan_dev *vxlan,
/* See if multicast group is already in use by other ID */
bool vxlan_group_used(struct vxlan_net *vn, struct vxlan_dev *dev,
- __be32 vni, union vxlan_addr *rip, int rifindex)
+ __be32 vni, const union vxlan_addr *rip, int rifindex)
{
- union vxlan_addr *ip = (rip ? : &dev->default_dst.remote_ip);
- int ifindex = (rifindex ? : dev->default_dst.remote_ifindex);
+ const struct vxlan_config *dev_cfg = rtnl_dereference(dev->cfg);
+ const union vxlan_addr *ip = (rip ? : &dev_cfg->remote_ip);
+ int ifindex = (rifindex ? : dev_cfg->remote_ifindex);
struct vxlan_dev *vxlan;
struct vxlan_sock *sock4;
#if IS_ENABLED(CONFIG_IPV6)
struct vxlan_sock *sock6;
#endif
- unsigned short family = dev->default_dst.remote_ip.sa.sa_family;
+ unsigned short family = dev_cfg->remote_ip.sa.sa_family;
sock4 = rtnl_dereference(dev->vn4_sock);
@@ -153,22 +158,22 @@ bool vxlan_group_used(struct vxlan_net *vn, struct vxlan_dev *dev,
continue;
if (family == AF_INET &&
- rtnl_dereference(vxlan->vn4_sock) != sock4)
+ rtnl_dereference(vxlan->vn4_sock) != sock4)
continue;
#if IS_ENABLED(CONFIG_IPV6)
if (family == AF_INET6 &&
- rtnl_dereference(vxlan->vn6_sock) != sock6)
+ rtnl_dereference(vxlan->vn6_sock) != sock6)
continue;
#endif
cfg = rtnl_dereference(vxlan->cfg);
if (cfg->flags & VXLAN_F_VNIFILTER) {
- if (!vxlan_group_used_by_vnifilter(vxlan, ip, ifindex))
+ if (!vxlan_group_used_by_vnifilter(vxlan, cfg, ip, ifindex))
continue;
} else {
if (!vxlan_group_used_match(ip, ifindex,
- &vxlan->default_dst.remote_ip,
- vxlan->default_dst.remote_ifindex))
+ &cfg->remote_ip,
+ cfg->remote_ifindex))
continue;
}
@@ -178,7 +183,8 @@ bool vxlan_group_used(struct vxlan_net *vn, struct vxlan_dev *dev,
return false;
}
-static int vxlan_multicast_join_vnigrp(struct vxlan_dev *vxlan)
+static int vxlan_multicast_join_vnigrp(struct vxlan_dev *vxlan,
+ const struct vxlan_config *cfg)
{
struct vxlan_vni_group *vg = rtnl_dereference(vxlan->vnigrp);
struct vxlan_vni_node *v, *tmp, *vgood = NULL;
@@ -189,7 +195,7 @@ static int vxlan_multicast_join_vnigrp(struct vxlan_dev *vxlan)
continue;
/* skip if address is same as default address */
if (vxlan_addr_equal(&v->remote_ip,
- &vxlan->default_dst.remote_ip))
+ &cfg->remote_ip))
continue;
ret = vxlan_igmp_join(vxlan, &v->remote_ip, 0);
if (ret == -EADDRINUSE)
@@ -204,7 +210,7 @@ static int vxlan_multicast_join_vnigrp(struct vxlan_dev *vxlan)
if (!vxlan_addr_multicast(&v->remote_ip))
continue;
if (vxlan_addr_equal(&v->remote_ip,
- &vxlan->default_dst.remote_ip))
+ &cfg->remote_ip))
continue;
vxlan_igmp_leave(vxlan, &v->remote_ip, 0);
if (v == vgood)
@@ -240,9 +246,9 @@ int vxlan_multicast_join(struct vxlan_dev *vxlan)
const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg);
int ret = 0;
- if (vxlan_addr_multicast(&vxlan->default_dst.remote_ip)) {
- ret = vxlan_igmp_join(vxlan, &vxlan->default_dst.remote_ip,
- vxlan->default_dst.remote_ifindex);
+ if (vxlan_addr_multicast(&cfg->remote_ip)) {
+ ret = vxlan_igmp_join(vxlan, &cfg->remote_ip,
+ cfg->remote_ifindex);
if (ret == -EADDRINUSE)
ret = 0;
if (ret)
@@ -250,7 +256,7 @@ int vxlan_multicast_join(struct vxlan_dev *vxlan)
}
if (cfg->flags & VXLAN_F_VNIFILTER)
- return vxlan_multicast_join_vnigrp(vxlan);
+ return vxlan_multicast_join_vnigrp(vxlan, cfg);
return 0;
}
@@ -261,10 +267,10 @@ int vxlan_multicast_leave(struct vxlan_dev *vxlan)
struct vxlan_net *vn = net_generic(vxlan->net, vxlan_net_id);
int ret = 0;
- if (vxlan_addr_multicast(&vxlan->default_dst.remote_ip) &&
+ if (vxlan_addr_multicast(&cfg->remote_ip) &&
!vxlan_group_used(vn, vxlan, 0, NULL, 0)) {
- ret = vxlan_igmp_leave(vxlan, &vxlan->default_dst.remote_ip,
- vxlan->default_dst.remote_ifindex);
+ ret = vxlan_igmp_leave(vxlan, &cfg->remote_ip,
+ cfg->remote_ifindex);
if (ret)
return ret;
}
diff --git a/drivers/net/vxlan/vxlan_private.h b/drivers/net/vxlan/vxlan_private.h
index ab7216c4e41011b99f8bf72de50fc2230b43c405..4f70afc502c906ca7361e33fee202d1a63da9506 100644
--- a/drivers/net/vxlan/vxlan_private.h
+++ b/drivers/net/vxlan/vxlan_private.h
@@ -216,8 +216,8 @@ void vxlan_vs_add_vnigrp(struct vxlan_dev *vxlan,
bool ipv6);
void vxlan_vs_del_vnigrp(struct vxlan_dev *vxlan);
int vxlan_vnilist_update_group(struct vxlan_dev *vxlan,
- union vxlan_addr *old_remote_ip,
- union vxlan_addr *new_remote_ip,
+ const union vxlan_addr *old_remote_ip,
+ const union vxlan_addr *new_remote_ip,
struct netlink_ext_ack *extack);
@@ -225,10 +225,10 @@ int vxlan_vnilist_update_group(struct vxlan_dev *vxlan,
int vxlan_multicast_join(struct vxlan_dev *vxlan);
int vxlan_multicast_leave(struct vxlan_dev *vxlan);
bool vxlan_group_used(struct vxlan_net *vn, struct vxlan_dev *dev,
- __be32 vni, union vxlan_addr *rip, int rifindex);
-int vxlan_igmp_join(struct vxlan_dev *vxlan, union vxlan_addr *rip,
+ __be32 vni, const union vxlan_addr *rip, int rifindex);
+int vxlan_igmp_join(struct vxlan_dev *vxlan, const union vxlan_addr *rip,
int rifindex);
-int vxlan_igmp_leave(struct vxlan_dev *vxlan, union vxlan_addr *rip,
+int vxlan_igmp_leave(struct vxlan_dev *vxlan, const union vxlan_addr *rip,
int rifindex);
/* vxlan_mdb.c */
diff --git a/drivers/net/vxlan/vxlan_vnifilter.c b/drivers/net/vxlan/vxlan_vnifilter.c
index 1069d6b1b2955c54d0ce0f89860390ebc0ff8f15..35ec32938004d7fbc33f93345006151f56f4d629 100644
--- a/drivers/net/vxlan/vxlan_vnifilter.c
+++ b/drivers/net/vxlan/vxlan_vnifilter.c
@@ -478,24 +478,25 @@ static const struct nla_policy vni_filter_policy[VXLAN_VNIFILTER_MAX + 1] = {
};
static int vxlan_update_default_fdb_entry(struct vxlan_dev *vxlan, __be32 vni,
- union vxlan_addr *old_remote_ip,
- union vxlan_addr *remote_ip,
+ const union vxlan_addr *old_remote_ip,
+ const union vxlan_addr *remote_ip,
struct netlink_ext_ack *extack)
{
const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg);
- struct vxlan_rdst *dst = &vxlan->default_dst;
int err = 0;
spin_lock_bh(&vxlan->hash_lock);
if (remote_ip && !vxlan_addr_any(remote_ip)) {
+ union vxlan_addr rip = *remote_ip;
+
err = vxlan_fdb_update(vxlan, all_zeros_mac,
- remote_ip,
+ &rip,
NUD_REACHABLE | NUD_PERMANENT,
NLM_F_APPEND | NLM_F_CREATE,
cfg->dst_port,
vni,
vni,
- dst->remote_ifindex,
+ cfg->remote_ifindex,
NTF_SELF, 0, true, extack);
if (err) {
spin_unlock_bh(&vxlan->hash_lock);
@@ -508,7 +509,7 @@ static int vxlan_update_default_fdb_entry(struct vxlan_dev *vxlan, __be32 vni,
*old_remote_ip,
cfg->dst_port,
vni, vni,
- dst->remote_ifindex,
+ cfg->remote_ifindex,
true);
}
spin_unlock_bh(&vxlan->hash_lock);
@@ -523,8 +524,8 @@ static int vxlan_vni_update_group(struct vxlan_dev *vxlan,
struct netlink_ext_ack *extack)
{
struct vxlan_net *vn = net_generic(vxlan->net, vxlan_net_id);
- struct vxlan_rdst *dst = &vxlan->default_dst;
- union vxlan_addr *newrip = NULL, *oldrip = NULL;
+ const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg);
+ const union vxlan_addr *newrip = NULL, *oldrip = NULL;
union vxlan_addr old_remote_ip;
int ret = 0;
@@ -536,8 +537,8 @@ static int vxlan_vni_update_group(struct vxlan_dev *vxlan,
if (group && !vxlan_addr_any(group)) {
newrip = group;
} else {
- if (!vxlan_addr_any(&dst->remote_ip))
- newrip = &dst->remote_ip;
+ if (!vxlan_addr_any(&cfg->remote_ip))
+ newrip = &cfg->remote_ip;
}
/* if old rip exists, and no newrip,
@@ -565,7 +566,7 @@ static int vxlan_vni_update_group(struct vxlan_dev *vxlan,
if (vxlan_addr_multicast(&old_remote_ip) &&
!vxlan_group_used(vn, vxlan, vninode->vni,
&old_remote_ip,
- vxlan->default_dst.remote_ifindex)) {
+ cfg->remote_ifindex)) {
ret = vxlan_igmp_leave(vxlan, &old_remote_ip,
0);
if (ret)
@@ -589,8 +590,8 @@ static int vxlan_vni_update_group(struct vxlan_dev *vxlan,
}
int vxlan_vnilist_update_group(struct vxlan_dev *vxlan,
- union vxlan_addr *old_remote_ip,
- union vxlan_addr *new_remote_ip,
+ const union vxlan_addr *old_remote_ip,
+ const union vxlan_addr *new_remote_ip,
struct netlink_ext_ack *extack)
{
struct list_head *headp, *hpos;
@@ -621,20 +622,19 @@ static void vxlan_vni_delete_group(struct vxlan_dev *vxlan,
{
struct vxlan_net *vn = net_generic(vxlan->net, vxlan_net_id);
const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg);
- struct vxlan_rdst *dst = &vxlan->default_dst;
/* if per vni remote_ip not present, delete the
* default dst remote_ip previously added for this vni
*/
if (!vxlan_addr_any(&vninode->remote_ip) ||
- !vxlan_addr_any(&dst->remote_ip)) {
+ !vxlan_addr_any(&cfg->remote_ip)) {
spin_lock_bh(&vxlan->hash_lock);
__vxlan_fdb_delete(vxlan, all_zeros_mac,
(vxlan_addr_any(&vninode->remote_ip) ?
- dst->remote_ip : vninode->remote_ip),
+ cfg->remote_ip : vninode->remote_ip),
cfg->dst_port,
vninode->vni, vninode->vni,
- dst->remote_ifindex,
+ cfg->remote_ifindex,
true);
spin_unlock_bh(&vxlan->hash_lock);
}
@@ -643,7 +643,7 @@ static void vxlan_vni_delete_group(struct vxlan_dev *vxlan,
if (vxlan_addr_multicast(&vninode->remote_ip) &&
!vxlan_group_used(vn, vxlan, vninode->vni,
&vninode->remote_ip,
- dst->remote_ifindex)) {
+ cfg->remote_ifindex)) {
vxlan_igmp_leave(vxlan, &vninode->remote_ip, 0);
}
}
@@ -854,6 +854,7 @@ static int vxlan_process_vni_filter(struct vxlan_dev *vxlan,
int cmd, struct netlink_ext_ack *extack)
{
struct nlattr *vattrs[VXLAN_VNIFILTER_ENTRY_MAX + 1];
+ const struct vxlan_config *cfg;
u32 vni_start = 0, vni_end = 0;
union vxlan_addr group;
int err;
@@ -891,7 +892,8 @@ static int vxlan_process_vni_filter(struct vxlan_dev *vxlan,
memset(&group, 0, sizeof(group));
}
- if (vxlan_addr_multicast(&group) && !vxlan->default_dst.remote_ifindex) {
+ cfg = rtnl_dereference(vxlan->cfg);
+ if (vxlan_addr_multicast(&group) && !cfg->remote_ifindex) {
NL_SET_ERR_MSG(extack,
"Local interface required for multicast remote group");
diff --git a/include/net/vxlan.h b/include/net/vxlan.h
index 7eb4f8110a84872f1d0451f7ccef3a73ba5c2e7a..8482954bff7692129ed684240c1b94feac7c3c00 100644
--- a/include/net/vxlan.h
+++ b/include/net/vxlan.h
@@ -296,7 +296,7 @@ struct vxlan_dev {
#endif
struct net_device *dev;
struct net *net; /* netns for packet i/o */
- struct vxlan_rdst default_dst; /* default destination */
+ struct net_device *lowerdev;
struct timer_list age_timer;
spinlock_t hash_lock;
--
2.55.0.970.g62bdec98f9-goog
^ permalink raw reply related [flat|nested] 28+ messages in thread* Re: [PATCH net-next 8/9] vxlan: remove default_dst and use vxlan_config and lowerdev
2026-09-03 12:08 ` [PATCH net-next 8/9] vxlan: remove default_dst and use vxlan_config and lowerdev Eric Dumazet
@ 2026-09-05 4:44 ` Kuniyuki Iwashima
2026-09-07 6:11 ` netdev-bot+sashiko
1 sibling, 0 replies; 28+ messages in thread
From: Kuniyuki Iwashima @ 2026-09-05 4:44 UTC (permalink / raw)
To: Eric Dumazet
Cc: David S . Miller, Jakub Kicinski, Paolo Abeni, Simon Horman,
Ido Schimmel, Andrew Lunn, netdev, eric.dumazet
On Thu, Sep 3, 2026 at 5:08 AM Eric Dumazet <edumazet@google.com> wrote:
>
> Now that vxlan->cfg is an RCU-protected pointer, storing default
> destination attributes (remote_ip, remote_vni, remote_ifindex) in
> vxlan->default_dst is redundant and creates potential data races for
> lockless readers.
>
> Furthermore, several fields of struct vxlan_rdst (remote_port,
> offloaded, list, rcu, dst_cache) in default_dst were completely unused.
>
> Replace vxlan->default_dst with a 'struct net_device *lowerdev' pointer
> in struct vxlan_dev to track adjacent upper/lower netdev topology under
> RTNL, and switch all remaining users over to reading configuration
> attributes from vxlan->cfg.
>
> Also update mlx5e_tc_tun_get_remote_ifindex() to read remote_ifindex
> from vxlan->cfg under rcu_read_lock().
>
> While updating lowerdev handling in vxlan_changelink(), avoid clobbering
> lowerdev to NULL when unchanged, and properly unlink and clear
> vxlan->lowerdev if the lower device is removed.
>
> Signed-off-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com>
[...]
> @@ -153,22 +158,22 @@ bool vxlan_group_used(struct vxlan_net *vn, struct vxlan_dev *dev,
> continue;
>
> if (family == AF_INET &&
> - rtnl_dereference(vxlan->vn4_sock) != sock4)
> + rtnl_dereference(vxlan->vn4_sock) != sock4)
> continue;
> #if IS_ENABLED(CONFIG_IPV6)
> if (family == AF_INET6 &&
> - rtnl_dereference(vxlan->vn6_sock) != sock6)
> + rtnl_dereference(vxlan->vn6_sock) != sock6)
nit: if v2 is needed, the two indentation changes above look unnecessary
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH net-next 8/9] vxlan: remove default_dst and use vxlan_config and lowerdev
2026-09-03 12:08 ` [PATCH net-next 8/9] vxlan: remove default_dst and use vxlan_config and lowerdev Eric Dumazet
2026-09-05 4:44 ` Kuniyuki Iwashima
@ 2026-09-07 6:11 ` netdev-bot+sashiko
1 sibling, 0 replies; 28+ messages in thread
From: netdev-bot+sashiko @ 2026-09-07 6:11 UTC (permalink / raw)
To: edumazet
Cc: davem, kuba, pabeni, horms, kuniyu, idosch, andrew+netdev, netdev,
eric.dumazet
Thank you for your contribution! Sashiko AI review found 5 potential
issue(s) to consider:
Critical: 0 · High: 0 · Medium: 2 · Low: 3
- [Medium] vxlan_changelink() bundles two undisclosed/untagged
behavioural repairs into the default_dst refactor.
- [Medium] vxlan_changelink() now keys the default-FDB delete on
cfg->remote_ifindex, which newly breaks the 'lower device detached,…
- [Low] Unrelated whitespace churn/style regression in
vxlan_group_used(): the two socket-comparison continuation lines are…
- [Low] Undisclosed functional fix on the drop path of
vxlan_encap_bypass().
- [Low] `struct vxlan_rdst::remote_dev` (include/net/vxlan.h:207) becomes
dead after this patch: a tree-wide grep for…
--
vxlan: remove default_dst and use vxlan_config and lowerdev
This removes vxlan->default_dst and reads the default destination
attributes (remote_ip, remote_vni, remote_ifindex) from the RCU protected
vxlan->cfg instead. A new vxlan->lowerdev pointer, maintained under RTNL,
takes over the upper/lower netdev adjacency tracking that
default_dst.remote_dev used to do.
> diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c
> index c49d55de020f..453cac5dde67 100644
> --- a/drivers/net/vxlan/vxlan_core.c
> +++ b/drivers/net/vxlan/vxlan_core.c
[ ... ]
> @@ -2303,7 +2307,14 @@ static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan,
> skb->dev = dev;
> __skb_pull(skb, skb_network_offset(skb));
>
> - if (dst_vxlan->default_dst.remote_ip.sa.sa_family == AF_INET) {
> + rcu_read_lock();
> + dst_cfg = rcu_dereference(dst_vxlan->cfg);
> + if (unlikely(!dst_cfg || !(dev->flags & IFF_UP))) {
> + kfree_skb_reason(skb, SKB_DROP_REASON_DEV_READY);
> + goto drop;
> + }
> +
> + if (dst_cfg->remote_ip.sa.sa_family == AF_INET) {
> loopback.sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
> loopback.sa.sa_family = AF_INET;
> #if IS_ENABLED(CONFIG_IPV6)
[ ... ]
> @@ -2333,8 +2337,9 @@ static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan,
> } else {
> drop:
> dev_dstats_rx_dropped(dev);
> - vxlan_vnifilter_count(dst_vxlan, dst_cfg, vni, NULL,
> - VXLAN_VNI_STATS_RX_DROPS, 0);
> + if (dst_cfg)
> + vxlan_vnifilter_count(dst_vxlan, dst_cfg, vni, NULL,
> + VXLAN_VNI_STATS_RX_DROPS, 0);
> }
> rcu_read_unlock();
> }
[Severity: Low]
Should this if (dst_cfg) guard live in the previous patch of the series
instead?
In the parent commit ("vxlan: convert configuration to RCU protection") the
goto drop taken when rcu_dereference(dst_vxlan->cfg) returns NULL lands on
the drop: label which calls vxlan_vnifilter_count() unconditionally, and
that helper dereferences cfg on its very first statement:
drivers/net/vxlan/vxlan_vnifilter.c:vxlan_vnifilter_count() {
if (!(cfg->flags & VXLAN_F_VNIFILTER))
return;
So the parent commit has a NULL dereference on the same path (reachable
when vxlan_free_dev() has already done RCU_INIT_POINTER(vxlan->cfg, NULL)
while a local-bypass transmit is in flight), and this patch quietly repairs
it while only describing the default_dst removal. Would it make sense to
move the guard into the commit that opened the NULL window so bisection
does not land on a broken intermediate commit?
[ ... ]
> @@ -3997,7 +4007,6 @@ static void vxlan_config_apply(struct net_device *dev,
> bool changelink)
> {
> struct vxlan_dev *vxlan = netdev_priv(dev);
> - struct vxlan_rdst *dst = &vxlan->default_dst;
> unsigned short needed_headroom = ETH_HLEN;
> struct vxlan_config *old_cfg;
> int max_mtu = ETH_MAX_MTU;
> @@ -4015,13 +4024,7 @@ static void vxlan_config_apply(struct net_device *dev,
> vxlan->net = src_net;
> }
>
> - dst->remote_vni = new_cfg->vni;
> -
> - memcpy(&dst->remote_ip, &new_cfg->remote_ip, sizeof(new_cfg->remote_ip));
> -
> if (lowerdev) {
> - dst->remote_ifindex = new_cfg->remote_ifindex;
> -
> netif_inherit_tso_max(dev, lowerdev);
>
> needed_headroom = lowerdev->hard_header_len;
[Severity: Medium]
Can this change the MTU of an existing device as a side effect of an
unrelated changelink?
Before this patch, vxlan_changelink() set lowerdev to NULL whenever the
lower device was unchanged, so this if (lowerdev) block never ran on a
plain attribute update. Now the real lowerdev is always passed in, so
netif_inherit_tso_max(), the needed_headroom/needed_tailroom recomputation
and the following clamp all run on every changelink:
max_mtu = lowerdev->mtu - vxlan_headroom(flags);
...
if (dev->mtu > max_mtu)
dev->mtu = max_mtu;
Concretely: create a vxlan over a 9000 byte lower device (vxlan mtu 8950),
then shrink the lower device to 1500. Today the vxlan mtu stays 8950;
after this patch any unrelated changelink (for instance changing only ttl
or tos) silently drops dev->mtu to 1450.
The commit message mentions only "avoid clobbering lowerdev to NULL when
unchanged" - could it spell out this user visible consequence?
[ ... ]
> @@ -4110,7 +4111,7 @@ static int vxlan_dev_create(struct net *net, struct net_device *dev,
> if (err)
> goto unregister;
>
> - dst->remote_dev = remote_dev;
> + vxlan->lowerdev = remote_dev;
> }
[Severity: Low]
This was the only user of struct vxlan_rdst::remote_dev, so should that
field be dropped from include/net/vxlan.h in the same patch?
After this hunk a grep for remote_dev in drivers/net/vxlan/ finds only the
local variable in vxlan_dev_create(). FDB remotes are allocated with
kmalloc_obj() in vxlan_fdb_append() and nothing assigns remote_dev, so
every FDB remote now carries an uninitialized struct net_device * inside an
exported struct.
The commit message enumerates remote_port, offloaded, list, rcu and
dst_cache as "completely unused" - those are all still used by FDB remotes,
while remote_dev, the one field that does become dead, is kept.
[ ... ]
> @@ -4565,20 +4566,19 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[],
> if (!new_cfg)
> return -ENOMEM;
>
> - if (dst->remote_dev == lowerdev)
> - lowerdev = NULL;
> -
> - err = netdev_adjacent_change_prepare(dst->remote_dev, lowerdev, dev,
> - extack);
> - if (err) {
> - kfree(new_cfg);
> - return err;
> + if (vxlan->lowerdev != lowerdev) {
> + err = netdev_adjacent_change_prepare(vxlan->lowerdev, lowerdev,
> + dev, extack);
> + if (err) {
> + kfree(new_cfg);
> + return err;
> + }
> }
>
> - rem_ip_changed = !vxlan_addr_equal(&conf.remote_ip, &dst->remote_ip);
> + rem_ip_changed = !vxlan_addr_equal(&conf.remote_ip, &cfg->remote_ip);
> change_igmp = vxlan->dev->flags & IFF_UP &&
> (rem_ip_changed ||
> - dst->remote_ifindex != conf.remote_ifindex);
> + cfg->remote_ifindex != conf.remote_ifindex);
[ ... ]
> - if (!vxlan_addr_any(&dst->remote_ip))
> + if (!vxlan_addr_any(&cfg->remote_ip))
> __vxlan_fdb_delete(vxlan, all_zeros_mac,
> - dst->remote_ip,
> + cfg->remote_ip,
> cfg->dst_port,
> - dst->remote_vni,
> - dst->remote_vni,
> - dst->remote_ifindex,
> + cfg->vni,
> + cfg->vni,
> + cfg->remote_ifindex,
> true);
> spin_unlock_bh(&vxlan->hash_lock);
[Severity: Medium]
Does keying this delete on cfg->remote_ifindex leave a stale default FDB
remote behind after the lower device has been detached?
Sequence, all via changelink:
ip link add vxlan0 type vxlan id 10 dev eth0 remote 10.0.0.1
vxlan_dev_create() creates the all-zeros-MAC remote with
rdst->remote_ifindex = ifindex(eth0)
ip link set vxlan0 type vxlan nolink (IFLA_VXLAN_LINK = 0)
rem_ip_changed is false, so no FDB refresh happens, but the newly
published cfg->remote_ifindex is now 0
ip link set vxlan0 type vxlan remote 10.0.0.2
rem_ip_changed is true, vxlan_fdb_update() with NLM_F_APPEND appends a
second rdst, then the __vxlan_fdb_delete() above runs with
cfg->remote_ifindex == 0
vxlan_fdb_find_rdst() requires an exact ifindex match:
if (vxlan_addr_equal(&rd->remote_ip, ip) &&
rd->remote_port == port &&
rd->remote_vni == vni &&
rd->remote_ifindex == ifindex)
return rd;
so rd is NULL, __vxlan_fdb_delete() jumps to out: and returns 0 with no
diagnostic, and the old remote 10.0.0.1 with ifindex(eth0) stays appended.
vxlan_xmit_one() picks the egress interface from rdst->remote_ifindex, so
BUM traffic keeps being replicated to the obsolete remote over the old
lower device.
In the parent commit default_dst.remote_ifindex was only written inside
if (lowerdev) in vxlan_config_apply(), so it still held ifindex(eth0) at
the third step and the delete matched. Is this detach case expected to
keep working?
[ ... ]
> @@ -4614,30 +4615,36 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[],
> - netdev_adjacent_change_commit(dst->remote_dev, lowerdev, dev);
> - if (lowerdev && lowerdev != dst->remote_dev)
> - dst->remote_dev = lowerdev;
> + if (vxlan->lowerdev != lowerdev) {
> + if (lowerdev)
> + netdev_adjacent_change_commit(vxlan->lowerdev, lowerdev,
> + dev);
> + else
> + netdev_upper_dev_unlink(vxlan->lowerdev, dev);
> + vxlan->lowerdev = lowerdev;
> + }
> vxlan_config_apply(dev, new_cfg, lowerdev, vxlan->net, true);
[Severity: Medium]
Is this new netdev_upper_dev_unlink() call a fix that deserves its own
patch with a Fixes: tag?
Previously, an IFLA_VXLAN_LINK = 0 changelink ended up calling
netdev_adjacent_change_prepare() and netdev_adjacent_change_commit() with
new_dev == NULL, and both of those return immediately in that case, so the
upper/lower adjacency was left in place and default_dst.remote_ifindex
stayed stale. That means vxlan_fill_info() kept reporting the removed
IFLA_VXLAN_LINK, and a later unregister of the no longer referenced lower
device still tore the vxlan device down via
vxlan_handle_lowerdev_unregister().
This hunk repairs both, but it is bundled into a refactor with no Fixes:
tag, so it cannot be backported on its own. Would splitting the adjacency
repair out as a separate fix be preferable?
> diff --git a/drivers/net/vxlan/vxlan_multicast.c b/drivers/net/vxlan/vxlan_multicast.c
> index e2cf10da274f..ae0320921c8c 100644
> --- a/drivers/net/vxlan/vxlan_multicast.c
> +++ b/drivers/net/vxlan/vxlan_multicast.c
[ ... ]
> @@ -153,22 +158,22 @@ bool vxlan_group_used(struct vxlan_net *vn, struct vxlan_dev *dev,
> continue;
>
> if (family == AF_INET &&
> - rtnl_dereference(vxlan->vn4_sock) != sock4)
> + rtnl_dereference(vxlan->vn4_sock) != sock4)
> continue;
> #if IS_ENABLED(CONFIG_IPV6)
> if (family == AF_INET6 &&
> - rtnl_dereference(vxlan->vn6_sock) != sock6)
> + rtnl_dereference(vxlan->vn6_sock) != sock6)
> continue;
> #endif
[Severity: Low]
This isn't a bug, but these two continuation lines are re-indented away
from the open parenthesis alignment (checkpatch: "Alignment should match
open parenthesis") and now read like nested statements. Neither line has
anything to do with removing default_dst - was this intentional?
> diff --git a/include/net/vxlan.h b/include/net/vxlan.h
> index 7eb4f8110a84..8482954bff76 100644
> --- a/include/net/vxlan.h
> +++ b/include/net/vxlan.h
> @@ -296,7 +296,7 @@ struct vxlan_dev {
> #endif
> struct net_device *dev;
> struct net *net; /* netns for packet i/o */
> - struct vxlan_rdst default_dst; /* default destination */
> + struct net_device *lowerdev;
>
> struct timer_list age_timer;
> spinlock_t hash_lock;
[Severity: Low]
With default_dst gone, struct vxlan_rdst::remote_dev in this same header no
longer has any writer or reader. Should it be removed here too?
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260903120840.1024153-1-edumazet%40google.com
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH net-next 9/9] vxlan: no longer rely on RTNL in vxlan_fill_info()
2026-09-03 12:08 [PATCH net-next 0/9] vxlan: convert configuration to RCU and drop RTNL in vxlan_fill_info() Eric Dumazet
` (7 preceding siblings ...)
2026-09-03 12:08 ` [PATCH net-next 8/9] vxlan: remove default_dst and use vxlan_config and lowerdev Eric Dumazet
@ 2026-09-03 12:08 ` Eric Dumazet
2026-09-05 4:45 ` Kuniyuki Iwashima
8 siblings, 1 reply; 28+ messages in thread
From: Eric Dumazet @ 2026-09-03 12:08 UTC (permalink / raw)
To: David S . Miller, Jakub Kicinski, Paolo Abeni
Cc: Simon Horman, Kuniyuki Iwashima, Ido Schimmel, Andrew Lunn,
netdev, eric.dumazet, Eric Dumazet
Now that vxlan->cfg is RCU-protected, we can update vxlan_fill_info()
to run under RCU read lock instead of relying on RTNL.
This completes the transition to RTNL-less link info dumping for VXLAN.
Signed-off-by: Eric Dumazet <edumazet@google.com>
---
drivers/net/vxlan/vxlan_core.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c
index 453cac5dde67ffe969edf9d3bdafe834c05632ad..d197f9bfd6c3f6a9ce0f23782f665ed7d75c026d 100644
--- a/drivers/net/vxlan/vxlan_core.c
+++ b/drivers/net/vxlan/vxlan_core.c
@@ -4706,8 +4706,14 @@ static int vxlan_fill_info(struct sk_buff *skb, const struct net_device *dev)
const struct vxlan_dev *vxlan = netdev_priv(dev);
struct ifla_vxlan_port_range ports;
const struct vxlan_config *cfg;
+ int err = 0;
- cfg = rtnl_dereference(vxlan->cfg);
+ rcu_read_lock();
+ cfg = rcu_dereference(vxlan->cfg);
+ if (!cfg) {
+ err = -ENODEV;
+ goto out;
+ }
if (nla_put_u32(skb, IFLA_VXLAN_ID, be32_to_cpu(cfg->vni)))
goto nla_put_failure;
@@ -4806,9 +4812,12 @@ static int vxlan_fill_info(struct sk_buff *skb, const struct net_device *dev)
&cfg->reserved_bits))
goto nla_put_failure;
- return 0;
+out:
+ rcu_read_unlock();
+ return err;
nla_put_failure:
+ rcu_read_unlock();
return -EMSGSIZE;
}
--
2.55.0.970.g62bdec98f9-goog
^ permalink raw reply related [flat|nested] 28+ messages in thread* Re: [PATCH net-next 9/9] vxlan: no longer rely on RTNL in vxlan_fill_info()
2026-09-03 12:08 ` [PATCH net-next 9/9] vxlan: no longer rely on RTNL in vxlan_fill_info() Eric Dumazet
@ 2026-09-05 4:45 ` Kuniyuki Iwashima
0 siblings, 0 replies; 28+ messages in thread
From: Kuniyuki Iwashima @ 2026-09-05 4:45 UTC (permalink / raw)
To: Eric Dumazet
Cc: David S . Miller, Jakub Kicinski, Paolo Abeni, Simon Horman,
Ido Schimmel, Andrew Lunn, netdev, eric.dumazet
On Thu, Sep 3, 2026 at 5:08 AM Eric Dumazet <edumazet@google.com> wrote:
>
> Now that vxlan->cfg is RCU-protected, we can update vxlan_fill_info()
> to run under RCU read lock instead of relying on RTNL.
>
> This completes the transition to RTNL-less link info dumping for VXLAN.
>
> Signed-off-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com>
^ permalink raw reply [flat|nested] 28+ messages in thread