AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/09/07 02:23 flow patch-triage
0m Args:
null

Results:
{
  "EnableConfigs": [
    "VXLAN",
    "INET"
  ],
  "FocusSymbols": [
    "vxlan_xmit",
    "vxlan_rcv",
    "vxlan_changelink",
    "vxlan_dev_configure",
    "vxlan_fdb_add",
    "vxlan_fdb_get",
    "vxlan_fill_info",
    "vxlan_mdb_xmit",
    "vxlan_mdb_get",
    "vxlan_vnifilter_process",
    "vxlan_multicast_join",
    "vxlan_multicast_leave"
  ],
  "KMSANReasoning": "The patch refactors VXLAN configuration management by converting `vxlan-\u003ecfg` from an embedded struct to a dynamically-allocated, RCU-protected pointer (`struct vxlan_config __rcu *cfg`), removing duplicate fields previously mirrored in `vxlan-\u003edefault_dst`, and moving the MDB flag to `vxlan-\u003eflags`.\n\nAll new allocations (`kmemdup`) operate on fully initialized structures (cleared with `memset` in `vxlan_nl2conf`), and local stack structures (`struct ifla_vxlan_port_range ports`, `struct mlxsw_sp_nve_params params`, `struct vxlan_metadata _md`) have all their fields fully initialized before use. No uninitialized struct padding or uninitialized memory is read or exposed across security boundaries (e.g. copied to user space via netlink).\n\nAny potential bugs in this patch pertain to pointer lifetime, concurrency, RCU synchronization, or use-after-free, which are effectively detected by KASAN and LOCKDEP rather than KMSAN. Dedicated KMSAN fuzzing is not required.",
  "NeedsKMSAN": false,
  "Reasoning": "The patch refactors the VXLAN driver to manage struct vxlan_config dynamically via RCU pointers (rcu_assign_pointer / kfree_rcu) and updates RCU/RTNL dereferencing across both control path (changelink, FDB, MDB, vnifilter) and data path (xmit, rcv, encap bypass). VXLAN devices are virtual network interfaces fully reachable without dedicated hardware in standard QEMU/syzkaller environments. Fuzzing is warranted to test for potential NULL dereferences, RCU race conditions, and use-after-free bugs.",
  "WorthFuzzing": true
}

1/1 2026/09/07 02:23 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit d979d7f8eafee3d72098e74d55fabd826fb15eb1\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Mon Sep 7 02:23:27 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --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\nindex 7a18a469961db..467fbe43b89e9 100644\n--- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_vxlan.c\n+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_vxlan.c\n@@ -241,9 +241,16 @@ static bool mlx5e_tc_tun_encap_info_equal_vxlan(struct mlx5e_encap_key *a,\n static int mlx5e_tc_tun_get_remote_ifindex(struct net_device *mirred_dev)\n {\n \tconst struct vxlan_dev *vxlan = netdev_priv(mirred_dev);\n-\tconst struct vxlan_rdst *dst = \u0026vxlan-\u003edefault_dst;\n+\tconst struct vxlan_config *cfg;\n+\tint ifindex = 0;\n \n-\treturn dst-\u003eremote_ifindex;\n+\trcu_read_lock();\n+\tcfg = rcu_dereference(vxlan-\u003ecfg);\n+\tif (cfg)\n+\t\tifindex = cfg-\u003eremote_ifindex;\n+\trcu_read_unlock();\n+\n+\treturn ifindex;\n }\n \n struct mlx5e_tc_tunnel vxlan_tunnel = {\ndiff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c\nindex 52c2fe3644d4b..50cea39323f57 100644\n--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c\n+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c\n@@ -59,8 +59,11 @@ static bool mlxsw_sp_nve_vxlan_can_offload(const struct mlxsw_sp_nve *nve,\n \t\t\t\t\t   const struct mlxsw_sp_nve_params *params,\n \t\t\t\t\t   struct netlink_ext_ack *extack)\n {\n-\tstruct vxlan_dev *vxlan = netdev_priv(params-\u003edev);\n-\tstruct vxlan_config *cfg = \u0026vxlan-\u003ecfg;\n+\tconst struct vxlan_config *cfg;\n+\tstruct vxlan_dev *vxlan;\n+\n+\tvxlan = netdev_priv(params-\u003edev);\n+\tcfg = rtnl_dereference(vxlan-\u003ecfg);\n \n \tif (vxlan_addr_multicast(\u0026cfg-\u003eremote_ip)) {\n \t\tNL_SET_ERR_MSG_MOD(extack, \"VxLAN: Multicast destination IP is not supported\");\n@@ -148,8 +151,11 @@ static void mlxsw_sp_nve_vxlan_config(const struct mlxsw_sp_nve *nve,\n \t\t\t\t      const struct mlxsw_sp_nve_params *params,\n \t\t\t\t      struct mlxsw_sp_nve_config *config)\n {\n-\tstruct vxlan_dev *vxlan = netdev_priv(params-\u003edev);\n-\tstruct vxlan_config *cfg = \u0026vxlan-\u003ecfg;\n+\tconst struct vxlan_config *cfg;\n+\tstruct vxlan_dev *vxlan;\n+\n+\tvxlan = netdev_priv(params-\u003edev);\n+\tcfg = rtnl_dereference(vxlan-\u003ecfg);\n \n \tconfig-\u003etype = MLXSW_SP_NVE_TYPE_VXLAN;\n \tconfig-\u003ettl = cfg-\u003ettl;\ndiff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c\nindex fe45e533a4b2e..de60b698bea98 100644\n--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c\n+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c\n@@ -2513,15 +2513,17 @@ mlxsw_sp_bridge_vlan_aware_vxlan_join(struct mlxsw_sp_bridge_device *bridge_devi\n {\n \tstruct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device-\u003edev);\n \tstruct vxlan_dev *vxlan = netdev_priv(vxlan_dev);\n-\tstruct mlxsw_sp_nve_params params = {\n-\t\t.type = MLXSW_SP_NVE_TYPE_VXLAN,\n-\t\t.vni = vxlan-\u003ecfg.vni,\n-\t\t.dev = vxlan_dev,\n-\t\t.ethertype = ethertype,\n-\t};\n+\tstruct mlxsw_sp_nve_params params;\n+\tconst struct vxlan_config *cfg;\n \tstruct mlxsw_sp_fid *fid;\n \tint err;\n \n+\tcfg = rtnl_dereference(vxlan-\u003ecfg);\n+\tparams.type = MLXSW_SP_NVE_TYPE_VXLAN;\n+\tparams.vni = cfg-\u003evni;\n+\tparams.dev = vxlan_dev;\n+\tparams.ethertype = ethertype;\n+\n \t/* If the VLAN is 0, we need to find the VLAN that is configured as\n \t * PVID and egress untagged on the bridge port of the VxLAN device.\n \t * It is possible no such VLAN exists\n@@ -2704,15 +2706,17 @@ mlxsw_sp_bridge_8021d_vxlan_join(struct mlxsw_sp_bridge_device *bridge_device,\n {\n \tstruct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device-\u003edev);\n \tstruct vxlan_dev *vxlan = netdev_priv(vxlan_dev);\n-\tstruct mlxsw_sp_nve_params params = {\n-\t\t.type = MLXSW_SP_NVE_TYPE_VXLAN,\n-\t\t.vni = vxlan-\u003ecfg.vni,\n-\t\t.dev = vxlan_dev,\n-\t\t.ethertype = ETH_P_8021Q,\n-\t};\n+\tstruct mlxsw_sp_nve_params params;\n+\tconst struct vxlan_config *cfg;\n \tstruct mlxsw_sp_fid *fid;\n \tint err;\n \n+\tcfg = rtnl_dereference(vxlan-\u003ecfg);\n+\tparams.type = MLXSW_SP_NVE_TYPE_VXLAN;\n+\tparams.vni = cfg-\u003evni;\n+\tparams.dev = vxlan_dev;\n+\tparams.ethertype = ETH_P_8021Q;\n+\n \tfid = mlxsw_sp_fid_8021d_get(mlxsw_sp, bridge_device-\u003edev-\u003eifindex);\n \tif (IS_ERR(fid)) {\n \t\tNL_SET_ERR_MSG_MOD(extack, \"Failed to create 802.1D FID\");\n@@ -2933,10 +2937,13 @@ static void __mlxsw_sp_bridge_vxlan_leave(struct mlxsw_sp *mlxsw_sp,\n \t\t\t\t\t  const struct net_device *vxlan_dev)\n {\n \tstruct vxlan_dev *vxlan = netdev_priv(vxlan_dev);\n+\tconst struct vxlan_config *cfg;\n \tstruct mlxsw_sp_fid *fid;\n \n+\tcfg = rtnl_dereference(vxlan-\u003ecfg);\n+\n \t/* If the VxLAN device is down, then the FID does not have a VNI */\n-\tfid = mlxsw_sp_fid_lookup_by_vni(mlxsw_sp, vxlan-\u003ecfg.vni);\n+\tfid = mlxsw_sp_fid_lookup_by_vni(mlxsw_sp, cfg-\u003evni);\n \tif (!fid)\n \t\treturn;\n \n@@ -3029,11 +3036,13 @@ static void mlxsw_sp_fdb_vxlan_call_notifiers(struct net_device *dev,\n \tstruct switchdev_notifier_vxlan_fdb_info info;\n \tstruct vxlan_dev *vxlan = netdev_priv(dev);\n \tenum switchdev_notifier_type type;\n+\tconst struct vxlan_config *cfg;\n \n+\tcfg = rtnl_dereference(vxlan-\u003ecfg);\n \ttype = adding ? SWITCHDEV_VXLAN_FDB_ADD_TO_BRIDGE :\n \t\t\tSWITCHDEV_VXLAN_FDB_DEL_TO_BRIDGE;\n \tmlxsw_sp_switchdev_addr_vxlan_convert(proto, addr, \u0026info.remote_ip);\n-\tinfo.remote_port = vxlan-\u003ecfg.dst_port;\n+\tinfo.remote_port = cfg-\u003edst_port;\n \tinfo.remote_vni = vni;\n \tinfo.remote_ifindex = 0;\n \tether_addr_copy(info.eth_addr, mac);\n@@ -3236,8 +3245,10 @@ __mlxsw_sp_fdb_notify_mac_uc_tunnel_process(struct mlxsw_sp *mlxsw_sp,\n \n \tif (adding \u0026\u0026 netif_is_vxlan(dev)) {\n \t\tstruct vxlan_dev *vxlan = netdev_priv(dev);\n+\t\tconst struct vxlan_config *cfg;\n \n-\t\tif (!(vxlan-\u003ecfg.flags \u0026 VXLAN_F_LEARN))\n+\t\tcfg = rtnl_dereference(vxlan-\u003ecfg);\n+\t\tif (!(cfg-\u003eflags \u0026 VXLAN_F_LEARN))\n \t\t\treturn -EINVAL;\n \t}\n \n@@ -3722,9 +3733,11 @@ mlxsw_sp_switchdev_vxlan_work_prepare(struct mlxsw_sp_switchdev_event_work *\n {\n \tstruct vxlan_dev *vxlan = netdev_priv(switchdev_work-\u003edev);\n \tstruct switchdev_notifier_vxlan_fdb_info *vxlan_fdb_info;\n-\tstruct vxlan_config *cfg = \u0026vxlan-\u003ecfg;\n+\tconst struct vxlan_config *cfg;\n \tstruct netlink_ext_ack *extack;\n \n+\tcfg = rcu_dereference_rtnl(vxlan-\u003ecfg);\n+\n \textack = switchdev_notifier_info_to_extack(info);\n \tvxlan_fdb_info = container_of(info,\n \t\t\t\t      struct switchdev_notifier_vxlan_fdb_info,\n@@ -3851,11 +3864,15 @@ mlxsw_sp_switchdev_vxlan_vlan_add(struct mlxsw_sp *mlxsw_sp,\n \t\t\t\t  struct netlink_ext_ack *extack)\n {\n \tstruct vxlan_dev *vxlan = netdev_priv(vxlan_dev);\n-\t__be32 vni = vxlan-\u003ecfg.vni;\n+\tconst struct vxlan_config *cfg;\n \tstruct mlxsw_sp_fid *fid;\n \tu16 old_vid;\n+\t__be32 vni;\n \tint err;\n \n+\tcfg = rtnl_dereference(vxlan-\u003ecfg);\n+\tvni = cfg-\u003evni;\n+\n \t/* We cannot have the same VLAN as PVID and egress untagged on multiple\n \t * VxLAN devices. Note that we get this notification before the VLAN is\n \t * actually added to the bridge's database, so it is not possible for\n@@ -3935,12 +3952,16 @@ mlxsw_sp_switchdev_vxlan_vlan_del(struct mlxsw_sp *mlxsw_sp,\n \t\t\t\t  const struct net_device *vxlan_dev, u16 vid)\n {\n \tstruct vxlan_dev *vxlan = netdev_priv(vxlan_dev);\n-\t__be32 vni = vxlan-\u003ecfg.vni;\n+\tconst struct vxlan_config *cfg;\n \tstruct mlxsw_sp_fid *fid;\n+\t__be32 vni;\n \n \tif (!netif_running(vxlan_dev))\n \t\treturn;\n \n+\tcfg = rtnl_dereference(vxlan-\u003ecfg);\n+\tvni = cfg-\u003evni;\n+\n \tfid = mlxsw_sp_fid_lookup_by_vni(mlxsw_sp, vni);\n \tif (!fid)\n \t\treturn;\ndiff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c\nindex 0f0dfb4e176a9..fd993a95501b0 100644\n--- a/drivers/net/vxlan/vxlan_core.c\n+++ b/drivers/net/vxlan/vxlan_core.c\n@@ -110,20 +110,23 @@ static struct vxlan_dev *vxlan_vs_find_vni(struct vxlan_sock *vs,\n \t\tvni = 0;\n \n \thlist_for_each_entry_rcu(node, vni_head(vs, vni), hlist) {\n+\t\tconst struct vxlan_config *cfg;\n+\n \t\tif (!node-\u003evxlan)\n \t\t\tcontinue;\n+\n+\t\tcfg = rcu_dereference(node-\u003evxlan-\u003ecfg);\n+\n \t\tvnode = NULL;\n-\t\tif (node-\u003evxlan-\u003ecfg.flags \u0026 VXLAN_F_VNIFILTER) {\n+\t\tif (cfg-\u003eflags \u0026 VXLAN_F_VNIFILTER) {\n \t\t\tvnode = vxlan_vnifilter_lookup(node-\u003evxlan, vni);\n \t\t\tif (!vnode)\n \t\t\t\tcontinue;\n-\t\t} else if (node-\u003evxlan-\u003edefault_dst.remote_vni != vni) {\n+\t\t} else if (cfg-\u003evni != vni) {\n \t\t\tcontinue;\n \t\t}\n \n \t\tif (IS_ENABLED(CONFIG_IPV6)) {\n-\t\t\tconst struct vxlan_config *cfg = \u0026node-\u003evxlan-\u003ecfg;\n-\n \t\t\tif ((cfg-\u003eflags \u0026 VXLAN_F_IPV6_LINKLOCAL) \u0026\u0026\n \t\t\t    cfg-\u003eremote_ifindex != ifindex)\n \t\t\t\tcontinue;\n@@ -157,6 +160,7 @@ static int vxlan_fdb_info(struct sk_buff *skb, struct vxlan_dev *vxlan,\n \t\t\t  u32 portid, u32 seq, int type, unsigned int flags,\n \t\t\t  const struct vxlan_rdst *rdst)\n {\n+\tconst struct vxlan_config *cfg = rcu_dereference_rtnl(vxlan-\u003ecfg);\n \tunsigned long now = jiffies;\n \tstruct nda_cacheinfo ci;\n \tbool send_ip, send_eth;\n@@ -216,10 +220,10 @@ static int vxlan_fdb_info(struct sk_buff *skb, struct vxlan_dev *vxlan,\n \t\t\tgoto nla_put_failure;\n \n \t\tif (rdst-\u003eremote_port \u0026\u0026\n-\t\t    rdst-\u003eremote_port != vxlan-\u003ecfg.dst_port \u0026\u0026\n+\t\t    rdst-\u003eremote_port != cfg-\u003edst_port \u0026\u0026\n \t\t    nla_put_be16(skb, NDA_PORT, rdst-\u003eremote_port))\n \t\t\tgoto nla_put_failure;\n-\t\tif (rdst-\u003eremote_vni != vxlan-\u003edefault_dst.remote_vni \u0026\u0026\n+\t\tif (rdst-\u003eremote_vni != cfg-\u003evni \u0026\u0026\n \t\t    nla_put_u32(skb, NDA_VNI, be32_to_cpu(rdst-\u003eremote_vni)))\n \t\t\tgoto nla_put_failure;\n \t\tif (rdst-\u003eremote_ifindex \u0026\u0026\n@@ -227,7 +231,7 @@ static int vxlan_fdb_info(struct sk_buff *skb, struct vxlan_dev *vxlan,\n \t\t\tgoto nla_put_failure;\n \t}\n \n-\tif ((vxlan-\u003ecfg.flags \u0026 VXLAN_F_COLLECT_METADATA) \u0026\u0026 fdb-\u003ekey.vni \u0026\u0026\n+\tif ((cfg-\u003eflags \u0026 VXLAN_F_COLLECT_METADATA) \u0026\u0026 fdb-\u003ekey.vni \u0026\u0026\n \t    nla_put_u32(skb, NDA_SRC_VNI,\n \t\t\tbe32_to_cpu(fdb-\u003ekey.vni)))\n \t\tgoto nla_put_failure;\n@@ -377,14 +381,15 @@ static void vxlan_fdb_miss(struct vxlan_dev *vxlan, const u8 eth_addr[ETH_ALEN])\n \n /* Look up Ethernet address in forwarding table */\n static struct vxlan_fdb *vxlan_find_mac_rcu(struct vxlan_dev *vxlan,\n+\t\t\t\t\t    const struct vxlan_config *cfg,\n \t\t\t\t\t    const u8 *mac, __be32 vni)\n {\n \tstruct vxlan_fdb_key key;\n \n \tmemset(\u0026key, 0, sizeof(key));\n \tmemcpy(key.eth_addr, mac, sizeof(key.eth_addr));\n-\tif (!(vxlan-\u003ecfg.flags \u0026 VXLAN_F_COLLECT_METADATA))\n-\t\tkey.vni = vxlan-\u003edefault_dst.remote_vni;\n+\tif (!(cfg-\u003eflags \u0026 VXLAN_F_COLLECT_METADATA))\n+\t\tkey.vni = cfg-\u003evni;\n \telse\n \t\tkey.vni = vni;\n \n@@ -393,11 +398,12 @@ static struct vxlan_fdb *vxlan_find_mac_rcu(struct vxlan_dev *vxlan,\n }\n \n static struct vxlan_fdb *vxlan_find_mac_tx(struct vxlan_dev *vxlan,\n+\t\t\t\t\t   const struct vxlan_config *cfg,\n \t\t\t\t\t   const u8 *mac, __be32 vni)\n {\n \tstruct vxlan_fdb *f;\n \n-\tf = vxlan_find_mac_rcu(vxlan, mac, vni);\n+\tf = vxlan_find_mac_rcu(vxlan, cfg, mac, vni);\n \tif (f) {\n \t\tunsigned long now = jiffies;\n \n@@ -416,7 +422,7 @@ static struct vxlan_fdb *vxlan_find_mac(struct vxlan_dev *vxlan,\n \tlockdep_assert_held_once(\u0026vxlan-\u003ehash_lock);\n \n \trcu_read_lock();\n-\tf = vxlan_find_mac_rcu(vxlan, mac, vni);\n+\tf = vxlan_find_mac_rcu(vxlan, rcu_dereference(vxlan-\u003ecfg), mac, vni);\n \trcu_read_unlock();\n \n \treturn f;\n@@ -457,7 +463,7 @@ int vxlan_fdb_find_uc(struct net_device *dev, const u8 *mac, __be32 vni,\n \n \trcu_read_lock();\n \n-\tf = vxlan_find_mac_rcu(vxlan, eth_addr, vni);\n+\tf = vxlan_find_mac_rcu(vxlan, rcu_dereference(vxlan-\u003ecfg), eth_addr, vni);\n \tif (f)\n \t\trdst = first_remote_rcu(f);\n \tif (!rdst) {\n@@ -798,6 +804,7 @@ static int vxlan_fdb_nh_update(struct vxlan_dev *vxlan, struct vxlan_fdb *fdb,\n \t\t\t       u32 nhid, struct netlink_ext_ack *extack)\n {\n \tstruct nexthop *old_nh = rtnl_dereference(fdb-\u003enh);\n+\tconst struct vxlan_config *cfg;\n \tstruct nexthop *nh;\n \tint err = -EINVAL;\n \n@@ -826,7 +833,8 @@ static int vxlan_fdb_nh_update(struct vxlan_dev *vxlan, struct vxlan_fdb *fdb,\n \t}\n \n \t/* check nexthop group family */\n-\tswitch (vxlan-\u003edefault_dst.remote_ip.sa.sa_family) {\n+\tcfg = rtnl_dereference(vxlan-\u003ecfg);\n+\tswitch (cfg-\u003eremote_ip.sa.sa_family) {\n \tcase AF_INET:\n \t\tif (!nexthop_has_v4(nh)) {\n \t\t\terr = -EAFNOSUPPORT;\n@@ -863,12 +871,13 @@ int vxlan_fdb_create(struct vxlan_dev *vxlan,\n \t\t     u32 nhid, struct vxlan_fdb **fdb,\n \t\t     struct netlink_ext_ack *extack)\n {\n+\tconst struct vxlan_config *cfg = rcu_dereference_rtnl(vxlan-\u003ecfg);\n \tstruct vxlan_rdst *rd = NULL;\n \tstruct vxlan_fdb *f;\n \tint rc;\n \n-\tif (vxlan-\u003ecfg.addrmax \u0026\u0026\n-\t    vxlan-\u003eaddrcnt \u003e= vxlan-\u003ecfg.addrmax)\n+\tif (cfg-\u003eaddrmax \u0026\u0026\n+\t    vxlan-\u003eaddrcnt \u003e= cfg-\u003eaddrmax)\n \t\treturn -ENOSPC;\n \n \tnetdev_dbg(vxlan-\u003edev, \"add %pM -\u003e %pIS\\n\", mac, ip);\n@@ -1148,6 +1157,7 @@ static int vxlan_fdb_parse(struct nlattr *tb[], struct vxlan_dev *vxlan,\n \t\t\t   __be32 *vni, u32 *ifindex, u32 *nhid,\n \t\t\t   struct netlink_ext_ack *extack)\n {\n+\tconst struct vxlan_config *cfg = rtnl_dereference(vxlan-\u003ecfg);\n \tstruct net *net = dev_net(vxlan-\u003edev);\n \tint err;\n \n@@ -1164,7 +1174,7 @@ static int vxlan_fdb_parse(struct nlattr *tb[], struct vxlan_dev *vxlan,\n \t\t\treturn err;\n \t\t}\n \t} else {\n-\t\tunion vxlan_addr *remote = \u0026vxlan-\u003edefault_dst.remote_ip;\n+\t\tconst union vxlan_addr *remote = \u0026cfg-\u003eremote_ip;\n \n \t\tif (remote-\u003esa.sa_family == AF_INET) {\n \t\t\tip-\u003esin.sin_addr.s_addr = htonl(INADDR_ANY);\n@@ -1184,7 +1194,7 @@ static int vxlan_fdb_parse(struct nlattr *tb[], struct vxlan_dev *vxlan,\n \t\t}\n \t\t*port = nla_get_be16(tb[NDA_PORT]);\n \t} else {\n-\t\t*port = vxlan-\u003ecfg.dst_port;\n+\t\t*port = cfg-\u003edst_port;\n \t}\n \n \tif (tb[NDA_VNI]) {\n@@ -1194,7 +1204,7 @@ static int vxlan_fdb_parse(struct nlattr *tb[], struct vxlan_dev *vxlan,\n \t\t}\n \t\t*vni = cpu_to_be32(nla_get_u32(tb[NDA_VNI]));\n \t} else {\n-\t\t*vni = vxlan-\u003edefault_dst.remote_vni;\n+\t\t*vni = cfg-\u003evni;\n \t}\n \n \tif (tb[NDA_SRC_VNI]) {\n@@ -1204,7 +1214,7 @@ static int vxlan_fdb_parse(struct nlattr *tb[], struct vxlan_dev *vxlan,\n \t\t}\n \t\t*src_vni = cpu_to_be32(nla_get_u32(tb[NDA_SRC_VNI]));\n \t} else {\n-\t\t*src_vni = vxlan-\u003edefault_dst.remote_vni;\n+\t\t*src_vni = cfg-\u003evni;\n \t}\n \n \tif (tb[NDA_IFINDEX]) {\n@@ -1235,6 +1245,7 @@ static int vxlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],\n \t\t\t const unsigned char *addr, u16 vid, u16 flags,\n \t\t\t bool *notified, struct netlink_ext_ack *extack)\n {\n+\tconst struct vxlan_config *cfg;\n \tstruct vxlan_dev *vxlan = netdev_priv(dev);\n \t/* struct net *net = dev_net(vxlan-\u003edev); */\n \tunion vxlan_addr ip;\n@@ -1257,7 +1268,8 @@ static int vxlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],\n \tif (err)\n \t\treturn err;\n \n-\tif (vxlan-\u003edefault_dst.remote_ip.sa.sa_family != ip.sa.sa_family)\n+\tcfg = rtnl_dereference(vxlan-\u003ecfg);\n+\tif (cfg-\u003eremote_ip.sa.sa_family != ip.sa.sa_family)\n \t\treturn -EAFNOSUPPORT;\n \n \tspin_lock_bh(\u0026vxlan-\u003ehash_lock);\n@@ -1394,18 +1406,23 @@ static int vxlan_fdb_get(struct sk_buff *skb,\n \t\t\t struct netlink_ext_ack *extack)\n {\n \tstruct vxlan_dev *vxlan = netdev_priv(dev);\n+\tconst struct vxlan_config *cfg;\n \tstruct vxlan_fdb *f;\n \t__be32 vni;\n \tint err;\n \n+\tcfg = rcu_dereference_rtnl(vxlan-\u003ecfg);\n+\n \tif (tb[NDA_VNI])\n \t\tvni = cpu_to_be32(nla_get_u32(tb[NDA_VNI]));\n+\telse if (cfg)\n+\t\tvni = cfg-\u003evni;\n \telse\n-\t\tvni = vxlan-\u003edefault_dst.remote_vni;\n+\t\treturn -ENODEV;\n \n \trcu_read_lock();\n \n-\tf = vxlan_find_mac_rcu(vxlan, addr, vni);\n+\tf = vxlan_find_mac_rcu(vxlan, cfg, addr, vni);\n \tif (!f) {\n \t\tNL_SET_ERR_MSG(extack, \"Fdb entry not found\");\n \t\terr = -ENOENT;\n@@ -1423,6 +1440,7 @@ static int vxlan_fdb_get(struct sk_buff *skb,\n  * and Tunnel endpoint.\n  */\n static enum skb_drop_reason vxlan_snoop(struct net_device *dev,\n+\t\t\t\t\tconst struct vxlan_config *cfg,\n \t\t\t\t\tunion vxlan_addr *src_ip,\n \t\t\t\t\tconst u8 *src_mac, u32 src_ifindex,\n \t\t\t\t\t__be32 vni)\n@@ -1441,7 +1459,7 @@ static enum skb_drop_reason vxlan_snoop(struct net_device *dev,\n \t\tifindex = src_ifindex;\n #endif\n \n-\tf = vxlan_find_mac_rcu(vxlan, src_mac, vni);\n+\tf = vxlan_find_mac_rcu(vxlan, cfg, src_mac, vni);\n \tif (likely(f)) {\n \t\tstruct vxlan_rdst *rdst = first_remote_rcu(f);\n \t\tunsigned long now = jiffies;\n@@ -1477,9 +1495,9 @@ static enum skb_drop_reason vxlan_snoop(struct net_device *dev,\n \t\t\tvxlan_fdb_update(vxlan, src_mac, src_ip,\n \t\t\t\t\t NUD_REACHABLE,\n \t\t\t\t\t NLM_F_EXCL|NLM_F_CREATE,\n-\t\t\t\t\t vxlan-\u003ecfg.dst_port,\n+\t\t\t\t\t cfg-\u003edst_port,\n \t\t\t\t\t vni,\n-\t\t\t\t\t vxlan-\u003edefault_dst.remote_vni,\n+\t\t\t\t\t cfg-\u003evni,\n \t\t\t\t\t ifindex, NTF_SELF, 0, true, NULL);\n \t\tspin_unlock(\u0026vxlan-\u003ehash_lock);\n \t}\n@@ -1507,6 +1525,7 @@ static bool __vxlan_sock_release_prep(struct vxlan_sock *vs)\n \n static void vxlan_sock_release(struct vxlan_dev *vxlan)\n {\n+\tconst struct vxlan_config *cfg = rtnl_dereference(vxlan-\u003ecfg);\n \tstruct vxlan_sock *sock4 = rtnl_dereference(vxlan-\u003evn4_sock);\n #if IS_ENABLED(CONFIG_IPV6)\n \tstruct vxlan_sock *sock6 = rtnl_dereference(vxlan-\u003evn6_sock);\n@@ -1516,7 +1535,7 @@ static void vxlan_sock_release(struct vxlan_dev *vxlan)\n \n \tRCU_INIT_POINTER(vxlan-\u003evn4_sock, NULL);\n \n-\tif (vxlan-\u003ecfg.flags \u0026 VXLAN_F_VNIFILTER)\n+\tif (cfg \u0026\u0026 (cfg-\u003eflags \u0026 VXLAN_F_VNIFILTER))\n \t\tvxlan_vs_del_vnigrp(vxlan);\n \telse\n \t\tvxlan_vs_del_dev(vxlan);\n@@ -1587,6 +1606,7 @@ static void vxlan_parse_gbp_hdr(struct sk_buff *skb, u32 vxflags,\n }\n \n static enum skb_drop_reason vxlan_set_mac(struct vxlan_dev *vxlan,\n+\t\t\t\t\t  const struct vxlan_config *cfg,\n \t\t\t\t\t  struct vxlan_sock *vs,\n \t\t\t\t\t  struct sk_buff *skb, __be32 vni)\n {\n@@ -1612,10 +1632,10 @@ static enum skb_drop_reason vxlan_set_mac(struct vxlan_dev *vxlan,\n #endif\n \t}\n \n-\tif (!(vxlan-\u003ecfg.flags \u0026 VXLAN_F_LEARN))\n+\tif (!(cfg-\u003eflags \u0026 VXLAN_F_LEARN))\n \t\treturn SKB_NOT_DROPPED_YET;\n \n-\treturn vxlan_snoop(skb-\u003edev, \u0026saddr, eth_hdr(skb)-\u003eh_source,\n+\treturn vxlan_snoop(skb-\u003edev, cfg, \u0026saddr, eth_hdr(skb)-\u003eh_source,\n \t\t\t   ifindex, vni);\n }\n \n@@ -1646,18 +1666,21 @@ static bool vxlan_ecn_decapsulate(struct vxlan_sock *vs, void *oiph,\n static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)\n {\n \tstruct vxlan_vni_node *vninode = NULL;\n-\tconst struct vxlanhdr *vh;\n-\tstruct vxlan_dev *vxlan;\n-\tstruct vxlan_sock *vs;\n-\tstruct vxlan_metadata _md;\n-\tstruct vxlan_metadata *md = \u0026_md;\n \t__be16 protocol = htons(ETH_P_TEB);\n+\tconst struct vxlan_config *cfg;\n \tenum skb_drop_reason reason;\n+\tconst struct vxlanhdr *vh;\n+\tstruct vxlan_metadata *md;\n+\tstruct vxlan_metadata _md;\n+\tstruct vxlan_dev *vxlan;\n \tbool raw_proto = false;\n-\tvoid *oiph;\n+\tstruct vxlan_sock *vs;\n \t__be32 vni = 0;\n+\tvoid *oiph;\n \tint nh;\n \n+\tmd = \u0026_md;\n+\n \t/* Need UDP and VXLAN header to be present */\n \treason = pskb_may_pull_reason(skb, VXLAN_HLEN);\n \tif (reason)\n@@ -1685,8 +1708,10 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)\n \t\tgoto drop;\n \t}\n \n-\tif (vh-\u003evx_flags \u0026 vxlan-\u003ecfg.reserved_bits.vx_flags ||\n-\t    vh-\u003evx_vni \u0026 vxlan-\u003ecfg.reserved_bits.vx_vni) {\n+\tcfg = rcu_dereference(vxlan-\u003ecfg);\n+\n+\tif (vh-\u003evx_flags \u0026 cfg-\u003ereserved_bits.vx_flags ||\n+\t    vh-\u003evx_vni \u0026 cfg-\u003ereserved_bits.vx_vni) {\n \t\t/* If the header uses bits besides those enabled by the\n \t\t * netdevice configuration, treat this as a malformed packet.\n \t\t * This behavior diverges from VXLAN RFC (RFC7348) which\n@@ -1698,12 +1723,12 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)\n \t\treason = SKB_DROP_REASON_VXLAN_INVALID_HDR;\n \t\tDEV_STATS_INC(vxlan-\u003edev, rx_frame_errors);\n \t\tDEV_STATS_INC(vxlan-\u003edev, rx_errors);\n-\t\tvxlan_vnifilter_count(vxlan, vni, vninode,\n+\t\tvxlan_vnifilter_count(vxlan, cfg, vni, vninode,\n \t\t\t\t      VXLAN_VNI_STATS_RX_ERRORS, 0);\n \t\tgoto drop;\n \t}\n \n-\tif (vxlan-\u003ecfg.flags \u0026 VXLAN_F_GPE) {\n+\tif (cfg-\u003eflags \u0026 VXLAN_F_GPE) {\n \t\tif (!vxlan_parse_gpe_proto(vh, \u0026protocol))\n \t\t\tgoto drop;\n \t\traw_proto = true;\n@@ -1715,8 +1740,8 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)\n \t\tgoto drop;\n \t}\n \n-\tif (vxlan-\u003ecfg.flags \u0026 VXLAN_F_REMCSUM_RX) {\n-\t\treason = vxlan_remcsum(skb, vxlan-\u003ecfg.flags);\n+\tif (cfg-\u003eflags \u0026 VXLAN_F_REMCSUM_RX) {\n+\t\treason = vxlan_remcsum(skb, cfg-\u003eflags);\n \t\tif (unlikely(reason))\n \t\t\tgoto drop;\n \t}\n@@ -1741,14 +1766,14 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)\n \t\tmemset(md, 0, sizeof(*md));\n \t}\n \n-\tif (vxlan-\u003ecfg.flags \u0026 VXLAN_F_GBP)\n-\t\tvxlan_parse_gbp_hdr(skb, vxlan-\u003ecfg.flags, md);\n+\tif (cfg-\u003eflags \u0026 VXLAN_F_GBP)\n+\t\tvxlan_parse_gbp_hdr(skb, cfg-\u003eflags, md);\n \t/* Note that GBP and GPE can never be active together. This is\n \t * ensured in vxlan_dev_configure.\n \t */\n \n \tif (!raw_proto) {\n-\t\treason = vxlan_set_mac(vxlan, vs, skb, vni);\n+\t\treason = vxlan_set_mac(vxlan, cfg, vs, skb, vni);\n \t\tif (reason)\n \t\t\tgoto drop;\n \t} else {\n@@ -1769,7 +1794,7 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)\n \tif (reason) {\n \t\tDEV_STATS_INC(vxlan-\u003edev, rx_length_errors);\n \t\tDEV_STATS_INC(vxlan-\u003edev, rx_errors);\n-\t\tvxlan_vnifilter_count(vxlan, vni, vninode,\n+\t\tvxlan_vnifilter_count(vxlan, cfg, vni, vninode,\n \t\t\t\t      VXLAN_VNI_STATS_RX_ERRORS, 0);\n \t\tgoto drop;\n \t}\n@@ -1781,7 +1806,7 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)\n \t\treason = SKB_DROP_REASON_IP_TUNNEL_ECN;\n \t\tDEV_STATS_INC(vxlan-\u003edev, rx_frame_errors);\n \t\tDEV_STATS_INC(vxlan-\u003edev, rx_errors);\n-\t\tvxlan_vnifilter_count(vxlan, vni, vninode,\n+\t\tvxlan_vnifilter_count(vxlan, cfg, vni, vninode,\n \t\t\t\t      VXLAN_VNI_STATS_RX_ERRORS, 0);\n \t\tgoto drop;\n \t}\n@@ -1791,14 +1816,15 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)\n \tif (unlikely(!(vxlan-\u003edev-\u003eflags \u0026 IFF_UP))) {\n \t\trcu_read_unlock();\n \t\tdev_dstats_rx_dropped(vxlan-\u003edev);\n-\t\tvxlan_vnifilter_count(vxlan, vni, vninode,\n+\t\tvxlan_vnifilter_count(vxlan, cfg, vni, vninode,\n \t\t\t\t      VXLAN_VNI_STATS_RX_DROPS, 0);\n \t\treason = SKB_DROP_REASON_DEV_READY;\n \t\tgoto drop;\n \t}\n \n \tdev_dstats_rx_add(vxlan-\u003edev, skb-\u003elen);\n-\tvxlan_vnifilter_count(vxlan, vni, vninode, VXLAN_VNI_STATS_RX, skb-\u003elen);\n+\tvxlan_vnifilter_count(vxlan, cfg, vni, vninode, VXLAN_VNI_STATS_RX,\n+\t\t\t      skb-\u003elen);\n \tgro_cells_receive(\u0026vxlan-\u003egro_cells, skb);\n \n \trcu_read_unlock();\n@@ -1839,7 +1865,8 @@ static int vxlan_err_lookup(struct sock *sk, struct sk_buff *skb)\n \treturn 0;\n }\n \n-static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)\n+static int arp_reduce(struct net_device *dev, struct sk_buff *skb,\n+\t\t      const struct vxlan_config *cfg, __be32 vni)\n {\n \tstruct neigh_table *tbl = arp_table(dev_net(dev));\n \tstruct vxlan_dev *vxlan = netdev_priv(dev);\n@@ -1853,7 +1880,7 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)\n \n \tif (!pskb_network_may_pull(skb, arp_hdr_len(dev))) {\n \t\tdev_dstats_tx_dropped(dev);\n-\t\tvxlan_vnifilter_count(vxlan, vni, NULL,\n+\t\tvxlan_vnifilter_count(vxlan, cfg, vni, NULL,\n \t\t\t\t      VXLAN_VNI_STATS_TX_DROPS, 0);\n \t\tgoto out;\n \t}\n@@ -1894,7 +1921,7 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)\n \t\tneigh_ha_snapshot(ha, n, n-\u003edev);\n \n \t\trcu_read_lock();\n-\t\tf = vxlan_find_mac_tx(vxlan, ha, vni);\n+\t\tf = vxlan_find_mac_tx(vxlan, cfg, ha, vni);\n \t\tif (f)\n \t\t\trdst = first_remote_rcu(f);\n \t\tif (rdst \u0026\u0026 vxlan_addr_any(\u0026rdst-\u003eremote_ip)) {\n@@ -1920,11 +1947,11 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)\n \n \t\tif (netif_rx(reply) == NET_RX_DROP) {\n \t\t\tdev_dstats_rx_dropped(dev);\n-\t\t\tvxlan_vnifilter_count(vxlan, vni, NULL,\n+\t\t\tvxlan_vnifilter_count(vxlan, cfg, vni, NULL,\n \t\t\t\t\t      VXLAN_VNI_STATS_RX_DROPS, 0);\n \t\t}\n \n-\t} else if (vxlan-\u003ecfg.flags \u0026 VXLAN_F_L3MISS) {\n+\t} else if (cfg-\u003eflags \u0026 VXLAN_F_L3MISS) {\n \t\tunion vxlan_addr ipa = {\n \t\t\t.sin.sin_addr.s_addr = tip,\n \t\t\t.sin.sin_family = AF_INET,\n@@ -2032,7 +2059,8 @@ static struct sk_buff *vxlan_na_create(struct sk_buff *request,\n \treturn reply;\n }\n \n-static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)\n+static int neigh_reduce(struct net_device *dev, struct sk_buff *skb,\n+\t\t\tconst struct vxlan_config *cfg, __be32 vni)\n {\n \tstruct vxlan_dev *vxlan = netdev_priv(dev);\n \tconst struct in6_addr *daddr;\n@@ -2066,7 +2094,7 @@ static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)\n \t\t}\n \n \t\tneigh_ha_snapshot(ha, n, n-\u003edev);\n-\t\tf = vxlan_find_mac_tx(vxlan, ha, vni);\n+\t\tf = vxlan_find_mac_tx(vxlan, cfg, ha, vni);\n \t\tif (f)\n \t\t\trdst = first_remote_rcu(f);\n \t\tif (rdst \u0026\u0026 vxlan_addr_any(\u0026rdst-\u003eremote_ip)) {\n@@ -2085,10 +2113,10 @@ static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)\n \n \t\tif (netif_rx(reply) == NET_RX_DROP) {\n \t\t\tdev_dstats_rx_dropped(dev);\n-\t\t\tvxlan_vnifilter_count(vxlan, vni, NULL,\n+\t\t\tvxlan_vnifilter_count(vxlan, cfg, vni, NULL,\n \t\t\t\t\t      VXLAN_VNI_STATS_RX_DROPS, 0);\n \t\t}\n-\t} else if (vxlan-\u003ecfg.flags \u0026 VXLAN_F_L3MISS) {\n+\t} else if (cfg-\u003eflags \u0026 VXLAN_F_L3MISS) {\n \t\tunion vxlan_addr ipa = {\n \t\t\t.sin6.sin6_addr = msg-\u003etarget,\n \t\t\t.sin6.sin6_family = AF_INET6,\n@@ -2104,9 +2132,9 @@ static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)\n }\n #endif\n \n-static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb)\n+static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb,\n+\t\t\t       const struct vxlan_config *cfg)\n {\n-\tstruct vxlan_dev *vxlan = netdev_priv(dev);\n \tstruct neigh_table *tbl;\n \tstruct neighbour *n;\n \n@@ -2125,7 +2153,7 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb)\n \t\ttbl = arp_table(dev_net(dev));\n \t\tpip = ip_hdr(skb);\n \t\tn = neigh_lookup(tbl, \u0026pip-\u003edaddr, dev);\n-\t\tif (!n \u0026\u0026 (vxlan-\u003ecfg.flags \u0026 VXLAN_F_L3MISS)) {\n+\t\tif (!n \u0026\u0026 (cfg-\u003eflags \u0026 VXLAN_F_L3MISS)) {\n \t\t\tunion vxlan_addr ipa = {\n \t\t\t\t.sin.sin_addr.s_addr = pip-\u003edaddr,\n \t\t\t\t.sin.sin_family = AF_INET,\n@@ -2153,7 +2181,7 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb)\n \t\ttbl = nd_table(dev_net(dev));\n \t\tpip6 = ipv6_hdr(skb);\n \t\tn = neigh_lookup(tbl, \u0026pip6-\u003edaddr, dev);\n-\t\tif (!n \u0026\u0026 (vxlan-\u003ecfg.flags \u0026 VXLAN_F_L3MISS)) {\n+\t\tif (!n \u0026\u0026 (cfg-\u003eflags \u0026 VXLAN_F_L3MISS)) {\n \t\t\tunion vxlan_addr ipa = {\n \t\t\t\t.sin6.sin6_addr = pip6-\u003edaddr,\n \t\t\t\t.sin6.sin6_family = AF_INET6,\n@@ -2271,20 +2299,28 @@ static int vxlan_build_skb(struct sk_buff *skb, struct dst_entry *dst,\n \n /* Bypass encapsulation if the destination is local */\n static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan,\n-\t\t\t       struct vxlan_dev *dst_vxlan, __be32 vni,\n-\t\t\t       bool snoop)\n+\t\t\t       struct vxlan_dev *dst_vxlan,\n+\t\t\t       const struct vxlan_config *src_cfg,\n+\t\t\t       __be32 vni, bool snoop)\n {\n+\tconst struct vxlan_config *dst_cfg;\n \tunion vxlan_addr loopback;\n-\tunion vxlan_addr *remote_ip = \u0026dst_vxlan-\u003edefault_dst.remote_ip;\n \tunsigned int len = skb-\u003elen;\n-\tstruct net_device *dev;\n+\tstruct net_device *dev = dst_vxlan-\u003edev;\n \n \tskb-\u003epkt_type = PACKET_HOST;\n \tskb-\u003eencapsulation = 0;\n-\tskb-\u003edev = dst_vxlan-\u003edev;\n+\tskb-\u003edev = dev;\n \t__skb_pull(skb, skb_network_offset(skb));\n \n-\tif (remote_ip-\u003esa.sa_family == AF_INET) {\n+\trcu_read_lock();\n+\tdst_cfg = rcu_dereference(dst_vxlan-\u003ecfg);\n+\tif (unlikely(!dst_cfg || !(dev-\u003eflags \u0026 IFF_UP))) {\n+\t\tkfree_skb_reason(skb, SKB_DROP_REASON_DEV_READY);\n+\t\tgoto drop;\n+\t}\n+\n+\tif (dst_cfg-\u003eremote_ip.sa.sa_family == AF_INET) {\n \t\tloopback.sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);\n \t\tloopback.sa.sa_family =  AF_INET;\n #if IS_ENABLED(CONFIG_IPV6)\n@@ -2294,34 +2330,29 @@ static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan,\n #endif\n \t}\n \n-\trcu_read_lock();\n-\tdev = skb-\u003edev;\n-\tif (unlikely(!(dev-\u003eflags \u0026 IFF_UP))) {\n-\t\tkfree_skb_reason(skb, SKB_DROP_REASON_DEV_READY);\n-\t\tgoto drop;\n-\t}\n-\n-\tif ((dst_vxlan-\u003ecfg.flags \u0026 VXLAN_F_LEARN) \u0026\u0026 snoop)\n-\t\tvxlan_snoop(dev, \u0026loopback, eth_hdr(skb)-\u003eh_source, 0, vni);\n+\tif ((dst_cfg-\u003eflags \u0026 VXLAN_F_LEARN) \u0026\u0026 snoop)\n+\t\tvxlan_snoop(dev, dst_cfg, \u0026loopback, eth_hdr(skb)-\u003eh_source, 0, vni);\n \n \tdev_dstats_tx_add(src_vxlan-\u003edev, len);\n-\tvxlan_vnifilter_count(src_vxlan, vni, NULL, VXLAN_VNI_STATS_TX, len);\n+\tvxlan_vnifilter_count(src_vxlan, src_cfg, vni, NULL, VXLAN_VNI_STATS_TX, len);\n \n \tif (__netif_rx(skb) == NET_RX_SUCCESS) {\n \t\tdev_dstats_rx_add(dst_vxlan-\u003edev, len);\n-\t\tvxlan_vnifilter_count(dst_vxlan, vni, NULL, VXLAN_VNI_STATS_RX,\n+\t\tvxlan_vnifilter_count(dst_vxlan, dst_cfg, vni, NULL, VXLAN_VNI_STATS_RX,\n \t\t\t\t      len);\n \t} else {\n drop:\n \t\tdev_dstats_rx_dropped(dev);\n-\t\tvxlan_vnifilter_count(dst_vxlan, vni, NULL,\n-\t\t\t\t      VXLAN_VNI_STATS_RX_DROPS, 0);\n+\t\tif (dst_cfg)\n+\t\t\tvxlan_vnifilter_count(dst_vxlan, dst_cfg, vni, NULL,\n+\t\t\t\t\t      VXLAN_VNI_STATS_RX_DROPS, 0);\n \t}\n \trcu_read_unlock();\n }\n \n static int encap_bypass_if_local(struct sk_buff *skb, struct net_device *dev,\n \t\t\t\t struct vxlan_dev *vxlan,\n+\t\t\t\t const struct vxlan_config *cfg,\n \t\t\t\t int addr_family,\n \t\t\t\t __be16 dst_port, int dst_ifindex, __be32 vni,\n \t\t\t\t struct dst_entry *dst,\n@@ -2337,22 +2368,22 @@ static int encap_bypass_if_local(struct sk_buff *skb, struct net_device *dev,\n \t/* Bypass encapsulation if the destination is local */\n \tif (rt_flags \u0026 RTCF_LOCAL \u0026\u0026\n \t    !(rt_flags \u0026 (RTCF_BROADCAST | RTCF_MULTICAST)) \u0026\u0026\n-\t    vxlan-\u003ecfg.flags \u0026 VXLAN_F_LOCALBYPASS) {\n+\t    cfg-\u003eflags \u0026 VXLAN_F_LOCALBYPASS) {\n \t\tstruct vxlan_dev *dst_vxlan;\n \n \t\tdst_release(dst);\n \t\tdst_vxlan = vxlan_find_vni(vxlan-\u003enet, dst_ifindex, vni,\n \t\t\t\t\t   addr_family, dst_port,\n-\t\t\t\t\t   vxlan-\u003ecfg.flags);\n+\t\t\t\t\t   cfg-\u003eflags);\n \t\tif (!dst_vxlan) {\n \t\t\tDEV_STATS_INC(dev, tx_errors);\n-\t\t\tvxlan_vnifilter_count(vxlan, vni, NULL,\n+\t\t\tvxlan_vnifilter_count(vxlan, cfg, vni, NULL,\n \t\t\t\t\t      VXLAN_VNI_STATS_TX_ERRORS, 0);\n \t\t\tkfree_skb_reason(skb, SKB_DROP_REASON_VXLAN_VNI_NOT_FOUND);\n \n \t\t\treturn -ENOENT;\n \t\t}\n-\t\tvxlan_encap_bypass(skb, vxlan, dst_vxlan, vni, true);\n+\t\tvxlan_encap_bypass(skb, vxlan, dst_vxlan, cfg, vni, true);\n \t\treturn 1;\n \t}\n \n@@ -2360,30 +2391,35 @@ static int encap_bypass_if_local(struct sk_buff *skb, struct net_device *dev,\n }\n \n void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,\n+\t\t    const struct vxlan_config *cfg,\n \t\t    __be32 default_vni, struct vxlan_rdst *rdst, bool did_rsc)\n {\n+\tunsigned int pkt_len = skb-\u003elen;\n+\tstruct vxlan_metadata _md = {};\n+\t__be16 src_port = 0, dst_port;\n+\tstruct dst_entry *ndst = NULL;\n+\tenum skb_drop_reason reason;\n \tstruct dst_cache *dst_cache;\n+\tconst struct iphdr *old_iph;\n \tstruct ip_tunnel_info *info;\n \tstruct ip_tunnel_key *pkey;\n+\tstruct vxlan_metadata *md;\n \tstruct ip_tunnel_key key;\n-\tstruct vxlan_dev *vxlan = netdev_priv(dev);\n-\tconst struct iphdr *old_iph;\n-\tstruct vxlan_metadata _md;\n-\tstruct vxlan_metadata *md = \u0026_md;\n-\tunsigned int pkt_len = skb-\u003elen;\n-\t__be16 src_port = 0, dst_port;\n-\tstruct dst_entry *ndst = NULL;\n+\tstruct vxlan_dev *vxlan;\n+\tu32 flags = cfg-\u003eflags;\n+\tbool udp_sum = false;\n+\tbool no_eth_encap;\n \tint addr_family;\n+\tbool use_cache;\n+\t__be32 vni = 0;\n \t__u8 tos, ttl;\n \tint ifindex;\n \tint err = 0;\n-\tu32 flags = vxlan-\u003ecfg.flags;\n-\tbool use_cache;\n-\tbool udp_sum = false;\n-\tbool xnet = !net_eq(vxlan-\u003enet, dev_net(vxlan-\u003edev));\n-\tenum skb_drop_reason reason;\n-\tbool no_eth_encap;\n-\t__be32 vni = 0;\n+\tbool xnet;\n+\n+\tvxlan = netdev_priv(dev);\n+\txnet = !net_eq(vxlan-\u003enet, dev_net(vxlan-\u003edev));\n+\tmd = \u0026_md;\n \n \tno_eth_encap = flags \u0026 VXLAN_F_GPE \u0026\u0026 skb-\u003eprotocol != htons(ETH_P_TEB);\n \treason = skb_vlan_inet_prepare(skb, no_eth_encap);\n@@ -2403,23 +2439,23 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,\n \t\tif (vxlan_addr_any(\u0026rdst-\u003eremote_ip)) {\n \t\t\tif (did_rsc) {\n \t\t\t\t/* short-circuited back to local bridge */\n-\t\t\t\tvxlan_encap_bypass(skb, vxlan, vxlan,\n+\t\t\t\tvxlan_encap_bypass(skb, vxlan, vxlan, cfg,\n \t\t\t\t\t\t   default_vni, true);\n \t\t\t\treturn;\n \t\t\t}\n \t\t\tgoto drop;\n \t\t}\n \n-\t\taddr_family = vxlan-\u003ecfg.saddr.sa.sa_family;\n-\t\tdst_port = rdst-\u003eremote_port ? rdst-\u003eremote_port : vxlan-\u003ecfg.dst_port;\n+\t\taddr_family = cfg-\u003esaddr.sa.sa_family;\n+\t\tdst_port = rdst-\u003eremote_port ? rdst-\u003eremote_port : cfg-\u003edst_port;\n \t\tvni = (rdst-\u003eremote_vni) ? : default_vni;\n \t\tifindex = rdst-\u003eremote_ifindex;\n \n \t\tif (addr_family == AF_INET) {\n-\t\t\tkey.u.ipv4.src = vxlan-\u003ecfg.saddr.sin.sin_addr.s_addr;\n+\t\t\tkey.u.ipv4.src = cfg-\u003esaddr.sin.sin_addr.s_addr;\n \t\t\tkey.u.ipv4.dst = rdst-\u003eremote_ip.sin.sin_addr.s_addr;\n \t\t} else {\n-\t\t\tkey.u.ipv6.src = vxlan-\u003ecfg.saddr.sin6.sin6_addr;\n+\t\t\tkey.u.ipv6.src = cfg-\u003esaddr.sin6.sin6_addr;\n \t\t\tkey.u.ipv6.dst = rdst-\u003eremote_ip.sin6.sin6_addr;\n \t\t}\n \n@@ -2428,11 +2464,11 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,\n \t\tif (flags \u0026 VXLAN_F_TTL_INHERIT) {\n \t\t\tttl = ip_tunnel_get_ttl(old_iph, skb);\n \t\t} else {\n-\t\t\tttl = vxlan-\u003ecfg.ttl;\n+\t\t\tttl = cfg-\u003ettl;\n \t\t\tif (!ttl \u0026\u0026 vxlan_addr_multicast(\u0026rdst-\u003eremote_ip))\n \t\t\t\tttl = 1;\n \t\t}\n-\t\ttos = vxlan-\u003ecfg.tos;\n+\t\ttos = cfg-\u003etos;\n \t\tif (tos == 1)\n \t\t\ttos = ip_tunnel_get_dsfield(old_iph, skb);\n \t\tif (tos \u0026\u0026 !info)\n@@ -2443,9 +2479,9 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,\n \t\telse\n \t\t\tudp_sum = !(flags \u0026 VXLAN_F_UDP_ZERO_CSUM6_TX);\n #if IS_ENABLED(CONFIG_IPV6)\n-\t\tswitch (vxlan-\u003ecfg.label_policy) {\n+\t\tswitch (cfg-\u003elabel_policy) {\n \t\tcase VXLAN_LABEL_FIXED:\n-\t\t\tkey.label = vxlan-\u003ecfg.label;\n+\t\t\tkey.label = cfg-\u003elabel;\n \t\t\tbreak;\n \t\tcase VXLAN_LABEL_INHERIT:\n \t\t\tkey.label = ip_tunnel_get_flowlabel(old_iph, skb);\n@@ -2463,7 +2499,7 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,\n \t\t}\n \t\tpkey = \u0026info-\u003ekey;\n \t\taddr_family = ip_tunnel_info_af(info);\n-\t\tdst_port = info-\u003ekey.tp_dst ? : vxlan-\u003ecfg.dst_port;\n+\t\tdst_port = info-\u003ekey.tp_dst ? : cfg-\u003edst_port;\n \t\tvni = tunnel_id_to_key32(info-\u003ekey.tun_id);\n \t\tifindex = 0;\n \t\tdst_cache = \u0026info-\u003edst_cache;\n@@ -2476,8 +2512,8 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,\n \t\ttos = info-\u003ekey.tos;\n \t\tudp_sum = test_bit(IP_TUNNEL_CSUM_BIT, info-\u003ekey.tun_flags);\n \t}\n-\tsrc_port = udp_flow_src_port(dev_net(dev), skb, vxlan-\u003ecfg.port_min,\n-\t\t\t\t     vxlan-\u003ecfg.port_max, true);\n+\tsrc_port = udp_flow_src_port(dev_net(dev), skb, cfg-\u003eport_min,\n+\t\t\t\t     cfg-\u003eport_max, true);\n \n \trcu_read_lock();\n \tif (addr_family == AF_INET) {\n@@ -2510,15 +2546,15 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,\n \n \t\tif (!info) {\n \t\t\t/* Bypass encapsulation if the destination is local */\n-\t\t\terr = encap_bypass_if_local(skb, dev, vxlan, AF_INET,\n+\t\t\terr = encap_bypass_if_local(skb, dev, vxlan, cfg, AF_INET,\n \t\t\t\t\t\t    dst_port, ifindex, vni,\n \t\t\t\t\t\t    \u0026rt-\u003edst, rt-\u003ert_flags);\n \t\t\tif (err)\n \t\t\t\tgoto out_unlock;\n \n-\t\t\tif (vxlan-\u003ecfg.df == VXLAN_DF_SET) {\n+\t\t\tif (cfg-\u003edf == VXLAN_DF_SET) {\n \t\t\t\tdf = htons(IP_DF);\n-\t\t\t} else if (vxlan-\u003ecfg.df == VXLAN_DF_INHERIT) {\n+\t\t\t} else if (cfg-\u003edf == VXLAN_DF_INHERIT) {\n \t\t\t\tstruct ethhdr *eth = eth_hdr(skb);\n \n \t\t\t\tif (ntohs(eth-\u003eh_proto) == ETH_P_IPV6 ||\n@@ -2547,7 +2583,7 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,\n \t\t\t\tunclone-\u003ekey.u.ipv4.src = pkey-\u003eu.ipv4.dst;\n \t\t\t\tunclone-\u003ekey.u.ipv4.dst = saddr;\n \t\t\t}\n-\t\t\tvxlan_encap_bypass(skb, vxlan, vxlan, vni, false);\n+\t\t\tvxlan_encap_bypass(skb, vxlan, vxlan, cfg, vni, false);\n \t\t\tdst_release(ndst);\n \t\t\tgoto out_unlock;\n \t\t}\n@@ -2597,7 +2633,7 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,\n \t\tif (!info) {\n \t\t\tu32 rt6i_flags = dst_rt6_info(ndst)-\u003ert6i_flags;\n \n-\t\t\terr = encap_bypass_if_local(skb, dev, vxlan, AF_INET6,\n+\t\t\terr = encap_bypass_if_local(skb, dev, vxlan, cfg, AF_INET6,\n \t\t\t\t\t\t    dst_port, ifindex, vni,\n \t\t\t\t\t\t    ndst, rt6i_flags);\n \t\t\tif (err)\n@@ -2621,7 +2657,7 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,\n \t\t\t\tunclone-\u003ekey.u.ipv6.dst = saddr;\n \t\t\t}\n \n-\t\t\tvxlan_encap_bypass(skb, vxlan, vxlan, vni, false);\n+\t\t\tvxlan_encap_bypass(skb, vxlan, vxlan, cfg, vni, false);\n \t\t\tdst_release(ndst);\n \t\t\tgoto out_unlock;\n \t\t}\n@@ -2642,14 +2678,14 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,\n \t\t\t\t     ip6cb_flags);\n #endif\n \t}\n-\tvxlan_vnifilter_count(vxlan, vni, NULL, VXLAN_VNI_STATS_TX, pkt_len);\n+\tvxlan_vnifilter_count(vxlan, cfg, vni, NULL, VXLAN_VNI_STATS_TX, pkt_len);\n out_unlock:\n \trcu_read_unlock();\n \treturn;\n \n drop:\n \tdev_dstats_tx_dropped(dev);\n-\tvxlan_vnifilter_count(vxlan, vni, NULL, VXLAN_VNI_STATS_TX_DROPS, 0);\n+\tvxlan_vnifilter_count(vxlan, cfg, vni, NULL, VXLAN_VNI_STATS_TX_DROPS, 0);\n \tkfree_skb_reason(skb, reason);\n \treturn;\n \n@@ -2661,11 +2697,12 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,\n \t\tDEV_STATS_INC(dev, tx_carrier_errors);\n \tdst_release(ndst);\n \tDEV_STATS_INC(dev, tx_errors);\n-\tvxlan_vnifilter_count(vxlan, vni, NULL, VXLAN_VNI_STATS_TX_ERRORS, 0);\n+\tvxlan_vnifilter_count(vxlan, cfg, vni, NULL, VXLAN_VNI_STATS_TX_ERRORS, 0);\n \tkfree_skb_reason(skb, reason);\n }\n \n static void vxlan_xmit_nh(struct sk_buff *skb, struct net_device *dev,\n+\t\t\t  const struct vxlan_config *cfg,\n \t\t\t  struct vxlan_fdb *f, __be32 vni, bool did_rsc)\n {\n \tstruct vxlan_rdst nh_rdst;\n@@ -2682,7 +2719,7 @@ static void vxlan_xmit_nh(struct sk_buff *skb, struct net_device *dev,\n \tdo_xmit = vxlan_fdb_nh_path_select(nh, hash, \u0026nh_rdst);\n \n \tif (likely(do_xmit))\n-\t\tvxlan_xmit_one(skb, dev, vni, \u0026nh_rdst, did_rsc);\n+\t\tvxlan_xmit_one(skb, dev, cfg, vni, \u0026nh_rdst, did_rsc);\n \telse\n \t\tgoto drop;\n \n@@ -2690,15 +2727,15 @@ static void vxlan_xmit_nh(struct sk_buff *skb, struct net_device *dev,\n \n drop:\n \tdev_dstats_tx_dropped(dev);\n-\tvxlan_vnifilter_count(netdev_priv(dev), vni, NULL,\n+\tvxlan_vnifilter_count(netdev_priv(dev), cfg, vni, NULL,\n \t\t\t      VXLAN_VNI_STATS_TX_DROPS, 0);\n \tdev_kfree_skb(skb);\n }\n \n static netdev_tx_t vxlan_xmit_nhid(struct sk_buff *skb, struct net_device *dev,\n-\t\t\t\t   u32 nhid, __be32 vni)\n+\t\t\t\t   u32 nhid, __be32 vni, int saddr_family,\n+\t\t\t\t   const struct vxlan_config *cfg)\n {\n-\tstruct vxlan_dev *vxlan = netdev_priv(dev);\n \tstruct vxlan_rdst nh_rdst;\n \tstruct nexthop *nh;\n \tbool do_xmit;\n@@ -2716,11 +2753,11 @@ static netdev_tx_t vxlan_xmit_nhid(struct sk_buff *skb, struct net_device *dev,\n \tdo_xmit = vxlan_fdb_nh_path_select(nh, hash, \u0026nh_rdst);\n \trcu_read_unlock();\n \n-\tif (vxlan-\u003ecfg.saddr.sa.sa_family != nh_rdst.remote_ip.sa.sa_family)\n+\tif (saddr_family != nh_rdst.remote_ip.sa.sa_family)\n \t\tgoto drop;\n \n \tif (likely(do_xmit))\n-\t\tvxlan_xmit_one(skb, dev, vni, \u0026nh_rdst, false);\n+\t\tvxlan_xmit_one(skb, dev, cfg, vni, \u0026nh_rdst, false);\n \telse\n \t\tgoto drop;\n \n@@ -2728,7 +2765,7 @@ static netdev_tx_t vxlan_xmit_nhid(struct sk_buff *skb, struct net_device *dev,\n \n drop:\n \tdev_dstats_tx_dropped(dev);\n-\tvxlan_vnifilter_count(netdev_priv(dev), vni, NULL,\n+\tvxlan_vnifilter_count(netdev_priv(dev), cfg, vni, NULL,\n \t\t\t      VXLAN_VNI_STATS_TX_DROPS, 0);\n \tdev_kfree_skb(skb);\n \treturn NETDEV_TX_OK;\n@@ -2745,34 +2782,49 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)\n \tstruct vxlan_dev *vxlan = netdev_priv(dev);\n \tstruct vxlan_rdst *rdst, *fdst = NULL;\n \tconst struct ip_tunnel_info *info;\n+\tconst struct vxlan_config *cfg;\n+\t__be32 default_vni;\n \tstruct vxlan_fdb *f;\n \tstruct ethhdr *eth;\n+\tint saddr_family;\n \t__be32 vni = 0;\n-\tu32 nhid = 0;\n \tbool did_rsc;\n+\tu32 nhid = 0;\n+\tu32 flags;\n+\n+\trcu_read_lock();\n+\tcfg = rcu_dereference(vxlan-\u003ecfg);\n+\tflags = cfg-\u003eflags;\n+\tdefault_vni = cfg-\u003evni;\n+\tsaddr_family = cfg-\u003esaddr.sa.sa_family;\n \n \tinfo = skb_tunnel_info(skb);\n \n \tskb_reset_mac_header(skb);\n \n-\tif (vxlan-\u003ecfg.flags \u0026 VXLAN_F_COLLECT_METADATA) {\n+\tif (flags \u0026 VXLAN_F_COLLECT_METADATA) {\n \t\tif (info \u0026\u0026 info-\u003emode \u0026 IP_TUNNEL_INFO_BRIDGE \u0026\u0026\n \t\t    info-\u003emode \u0026 IP_TUNNEL_INFO_TX) {\n \t\t\tvni = tunnel_id_to_key32(info-\u003ekey.tun_id);\n \t\t\tnhid = info-\u003ekey.nhid;\n \t\t} else {\n \t\t\tif (info \u0026\u0026 info-\u003emode \u0026 IP_TUNNEL_INFO_TX)\n-\t\t\t\tvxlan_xmit_one(skb, dev, vni, NULL, false);\n+\t\t\t\tvxlan_xmit_one(skb, dev, cfg, vni, NULL, false);\n \t\t\telse\n \t\t\t\tkfree_skb_reason(skb, SKB_DROP_REASON_TUNNEL_TXINFO);\n+\t\t\trcu_read_unlock();\n \t\t\treturn NETDEV_TX_OK;\n \t\t}\n \t}\n \n-\tif (vxlan-\u003ecfg.flags \u0026 VXLAN_F_PROXY) {\n+\tif (flags \u0026 VXLAN_F_PROXY) {\n \t\teth = eth_hdr(skb);\n-\t\tif (ntohs(eth-\u003eh_proto) == ETH_P_ARP)\n-\t\t\treturn arp_reduce(dev, skb, vni);\n+\t\tif (ntohs(eth-\u003eh_proto) == ETH_P_ARP) {\n+\t\t\tnetdev_tx_t res = arp_reduce(dev, skb, cfg, vni);\n+\n+\t\t\trcu_read_unlock();\n+\t\t\treturn res;\n+\t\t}\n #if IS_ENABLED(CONFIG_IPV6)\n \t\telse if (ntohs(eth-\u003eh_proto) == ETH_P_IPV6 \u0026\u0026\n \t\t\t pskb_network_may_pull(skb, sizeof(struct ipv6hdr) +\n@@ -2781,53 +2833,57 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)\n \t\t\tstruct nd_msg *m = (struct nd_msg *)(ipv6_hdr(skb) + 1);\n \n \t\t\tif (m-\u003eicmph.icmp6_code == 0 \u0026\u0026\n-\t\t\t    m-\u003eicmph.icmp6_type == NDISC_NEIGHBOUR_SOLICITATION)\n-\t\t\t\treturn neigh_reduce(dev, skb, vni);\n+\t\t\t    m-\u003eicmph.icmp6_type == NDISC_NEIGHBOUR_SOLICITATION) {\n+\t\t\t\tnetdev_tx_t res = neigh_reduce(dev, skb, cfg, vni);\n+\n+\t\t\t\trcu_read_unlock();\n+\t\t\t\treturn res;\n+\t\t\t}\n \t\t}\n #endif\n \t}\n \n-\tif (nhid)\n-\t\treturn vxlan_xmit_nhid(skb, dev, nhid, vni);\n+\tif (nhid) {\n+\t\tnetdev_tx_t res = vxlan_xmit_nhid(skb, dev, nhid, vni, saddr_family, cfg);\n+\n+\t\trcu_read_unlock();\n+\t\treturn res;\n+\t}\n \n-\tif (vxlan-\u003ecfg.flags \u0026 VXLAN_F_MDB) {\n+\tif (test_bit(VXLAN_DEV_F_MDB, \u0026vxlan-\u003eflags)) {\n \t\tstruct vxlan_mdb_entry *mdb_entry;\n \n-\t\trcu_read_lock();\n-\t\tmdb_entry = vxlan_mdb_entry_skb_get(vxlan, skb, vni);\n+\t\tmdb_entry = vxlan_mdb_entry_skb_get(vxlan, cfg, skb, vni);\n \t\tif (mdb_entry) {\n-\t\t\tnetdev_tx_t ret;\n+\t\t\tnetdev_tx_t ret = vxlan_mdb_xmit(vxlan, cfg, mdb_entry, skb);\n \n-\t\t\tret = vxlan_mdb_xmit(vxlan, mdb_entry, skb);\n \t\t\trcu_read_unlock();\n \t\t\treturn ret;\n \t\t}\n-\t\trcu_read_unlock();\n \t}\n \n \teth = eth_hdr(skb);\n-\trcu_read_lock();\n-\tf = vxlan_find_mac_tx(vxlan, eth-\u003eh_dest, vni);\n+\tf = vxlan_find_mac_tx(vxlan, cfg, eth-\u003eh_dest, vni);\n \tdid_rsc = false;\n \n-\tif (f \u0026\u0026 (f-\u003eflags \u0026 NTF_ROUTER) \u0026\u0026 (vxlan-\u003ecfg.flags \u0026 VXLAN_F_RSC) \u0026\u0026\n+\tif (f \u0026\u0026 (f-\u003eflags \u0026 NTF_ROUTER) \u0026\u0026 (flags \u0026 VXLAN_F_RSC) \u0026\u0026\n \t    (ntohs(eth-\u003eh_proto) == ETH_P_IP ||\n \t     ntohs(eth-\u003eh_proto) == ETH_P_IPV6)) {\n-\t\tdid_rsc = route_shortcircuit(dev, skb);\n+\t\tdid_rsc = route_shortcircuit(dev, skb, cfg);\n \t\teth = eth_hdr(skb);\n \t\tif (did_rsc)\n-\t\t\tf = vxlan_find_mac_tx(vxlan, eth-\u003eh_dest, vni);\n+\t\t\tf = vxlan_find_mac_tx(vxlan, cfg, eth-\u003eh_dest, vni);\n \t}\n \n \tif (f == NULL) {\n-\t\tf = vxlan_find_mac_tx(vxlan, all_zeros_mac, vni);\n+\t\tf = vxlan_find_mac_tx(vxlan, cfg, all_zeros_mac, vni);\n \t\tif (f == NULL) {\n-\t\t\tif ((vxlan-\u003ecfg.flags \u0026 VXLAN_F_L2MISS) \u0026\u0026\n+\t\t\tif ((flags \u0026 VXLAN_F_L2MISS) \u0026\u0026\n \t\t\t    !is_multicast_ether_addr(eth-\u003eh_dest))\n \t\t\t\tvxlan_fdb_miss(vxlan, eth-\u003eh_dest);\n \n \t\t\tdev_dstats_tx_dropped(dev);\n-\t\t\tvxlan_vnifilter_count(vxlan, vni, NULL,\n+\t\t\tvxlan_vnifilter_count(vxlan, cfg, vni, NULL,\n \t\t\t\t\t      VXLAN_VNI_STATS_TX_DROPS, 0);\n \t\t\tkfree_skb_reason(skb, SKB_DROP_REASON_NO_TX_TARGET);\n \t\t\tgoto out;\n@@ -2835,8 +2891,8 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)\n \t}\n \n \tif (rcu_access_pointer(f-\u003enh)) {\n-\t\tvxlan_xmit_nh(skb, dev, f,\n-\t\t\t      (vni ? : vxlan-\u003edefault_dst.remote_vni), did_rsc);\n+\t\tvxlan_xmit_nh(skb, dev, cfg, f,\n+\t\t\t      (vni ? : default_vni), did_rsc);\n \t} else {\n \t\tlist_for_each_entry_rcu(rdst, \u0026f-\u003eremotes, list) {\n \t\t\tstruct sk_buff *skb1;\n@@ -2847,10 +2903,10 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)\n \t\t\t}\n \t\t\tskb1 = skb_clone(skb, GFP_ATOMIC);\n \t\t\tif (skb1)\n-\t\t\t\tvxlan_xmit_one(skb1, dev, vni, rdst, did_rsc);\n+\t\t\t\tvxlan_xmit_one(skb1, dev, cfg, vni, rdst, did_rsc);\n \t\t}\n \t\tif (fdst)\n-\t\t\tvxlan_xmit_one(skb, dev, vni, fdst, did_rsc);\n+\t\t\tvxlan_xmit_one(skb, dev, cfg, vni, fdst, did_rsc);\n \t\telse\n \t\t\tkfree_skb_reason(skb, SKB_DROP_REASON_NO_TX_TARGET);\n \t}\n@@ -2865,12 +2921,15 @@ static void vxlan_cleanup(struct timer_list *t)\n {\n \tstruct vxlan_dev *vxlan = timer_container_of(vxlan, t, age_timer);\n \tunsigned long next_timer = jiffies + FDB_AGE_INTERVAL;\n+\tconst struct vxlan_config *cfg;\n \tstruct vxlan_fdb *f;\n \n \tif (!netif_running(vxlan-\u003edev))\n \t\treturn;\n \n \trcu_read_lock();\n+\tcfg = rcu_dereference(vxlan-\u003ecfg);\n+\n \thlist_for_each_entry_rcu(f, \u0026vxlan-\u003efdb_list, fdb_node) {\n \t\tunsigned long timeout;\n \n@@ -2880,7 +2939,7 @@ static void vxlan_cleanup(struct timer_list *t)\n \t\tif (f-\u003eflags \u0026 NTF_EXT_LEARNED)\n \t\t\tcontinue;\n \n-\t\ttimeout = READ_ONCE(f-\u003eupdated) + vxlan-\u003ecfg.age_interval * HZ;\n+\t\ttimeout = READ_ONCE(f-\u003eupdated) + cfg-\u003eage_interval * HZ;\n \t\tif (time_before_eq(timeout, jiffies)) {\n \t\t\tspin_lock(\u0026vxlan-\u003ehash_lock);\n \t\t\tif (!hlist_unhashed(\u0026f-\u003efdb_node)) {\n@@ -2912,10 +2971,14 @@ static void vxlan_vs_del_dev(struct vxlan_dev *vxlan)\n static void vxlan_vs_add_dev(struct vxlan_sock *vs, struct vxlan_dev *vxlan,\n \t\t\t     struct vxlan_dev_node *node)\n {\n-\t__be32 vni = vxlan-\u003edefault_dst.remote_vni;\n+\tconst struct vxlan_config *cfg;\n+\t__be32 vni;\n \n \tASSERT_RTNL();\n \n+\tcfg = rtnl_dereference(vxlan-\u003ecfg);\n+\tvni = cfg-\u003evni;\n+\n \tnode-\u003evxlan = vxlan;\n \thlist_add_head_rcu(\u0026node-\u003ehlist, vni_head(vs, vni));\n }\n@@ -2924,13 +2987,16 @@ static void vxlan_vs_add_dev(struct vxlan_sock *vs, struct vxlan_dev *vxlan,\n static int vxlan_init(struct net_device *dev)\n {\n \tstruct vxlan_dev *vxlan = netdev_priv(dev);\n+\tconst struct vxlan_config *cfg;\n \tint err;\n \n+\tcfg = rtnl_dereference(vxlan-\u003ecfg);\n+\n \terr = rhashtable_init(\u0026vxlan-\u003efdb_hash_tbl, \u0026vxlan_fdb_rht_params);\n \tif (err)\n \t\treturn err;\n \n-\tif (vxlan-\u003ecfg.flags \u0026 VXLAN_F_VNIFILTER) {\n+\tif (cfg-\u003eflags \u0026 VXLAN_F_VNIFILTER) {\n \t\terr = vxlan_vnigroup_init(vxlan);\n \t\tif (err)\n \t\t\tgoto err_rhashtable_destroy;\n@@ -2950,7 +3016,7 @@ static int vxlan_init(struct net_device *dev)\n err_gro_cells_destroy:\n \tgro_cells_destroy(\u0026vxlan-\u003egro_cells);\n err_vnigroup_uninit:\n-\tif (vxlan-\u003ecfg.flags \u0026 VXLAN_F_VNIFILTER)\n+\tif (cfg-\u003eflags \u0026 VXLAN_F_VNIFILTER)\n \t\tvxlan_vnigroup_uninit(vxlan);\n err_rhashtable_destroy:\n \trhashtable_destroy(\u0026vxlan-\u003efdb_hash_tbl);\n@@ -2960,10 +3026,13 @@ static int vxlan_init(struct net_device *dev)\n static void vxlan_uninit(struct net_device *dev)\n {\n \tstruct vxlan_dev *vxlan = netdev_priv(dev);\n+\tconst struct vxlan_config *cfg;\n+\n+\tcfg = rtnl_dereference(vxlan-\u003ecfg);\n \n \tvxlan_mdb_fini(vxlan);\n \n-\tif (vxlan-\u003ecfg.flags \u0026 VXLAN_F_VNIFILTER)\n+\tif (cfg \u0026\u0026 (cfg-\u003eflags \u0026 VXLAN_F_VNIFILTER))\n \t\tvxlan_vnigroup_uninit(vxlan);\n \n \tgro_cells_destroy(\u0026vxlan-\u003egro_cells);\n@@ -2975,6 +3044,7 @@ static void vxlan_uninit(struct net_device *dev)\n static int vxlan_open(struct net_device *dev)\n {\n \tstruct vxlan_dev *vxlan = netdev_priv(dev);\n+\tconst struct vxlan_config *cfg;\n \tint ret;\n \n \tret = vxlan_sock_add(vxlan);\n@@ -2987,7 +3057,8 @@ static int vxlan_open(struct net_device *dev)\n \t\treturn ret;\n \t}\n \n-\tif (vxlan-\u003ecfg.age_interval)\n+\tcfg = rtnl_dereference(vxlan-\u003ecfg);\n+\tif (cfg \u0026\u0026 cfg-\u003eage_interval)\n \t\tmod_timer(\u0026vxlan-\u003eage_timer, jiffies + FDB_AGE_INTERVAL);\n \n \treturn ret;\n@@ -3009,8 +3080,10 @@ struct vxlan_fdb_flush_desc {\n static bool vxlan_fdb_is_default_entry(const struct vxlan_fdb *f,\n \t\t\t\t       const struct vxlan_dev *vxlan)\n {\n+\tconst struct vxlan_config *cfg = rcu_dereference_rtnl(vxlan-\u003ecfg);\n+\n \treturn is_zero_ether_addr(f-\u003ekey.eth_addr) \u0026\u0026\n-\t       f-\u003ekey.vni == vxlan-\u003ecfg.vni;\n+\t       f-\u003ekey.vni == cfg-\u003evni;\n }\n \n static bool vxlan_fdb_nhid_matches(const struct vxlan_fdb *f, u32 nhid)\n@@ -3227,15 +3300,18 @@ static void vxlan_set_multicast_list(struct net_device *dev)\n static int vxlan_change_mtu(struct net_device *dev, int new_mtu)\n {\n \tstruct vxlan_dev *vxlan = netdev_priv(dev);\n-\tstruct vxlan_rdst *dst = \u0026vxlan-\u003edefault_dst;\n-\tstruct net_device *lowerdev = __dev_get_by_index(vxlan-\u003enet,\n-\t\t\t\t\t\t\t dst-\u003eremote_ifindex);\n+\tconst struct vxlan_config *cfg;\n+\tstruct net_device *lowerdev;\n+\n+\tcfg = rtnl_dereference(vxlan-\u003ecfg);\n+\n+\tlowerdev = __dev_get_by_index(vxlan-\u003enet, cfg-\u003eremote_ifindex);\n \n \t/* This check is different than dev-\u003emax_mtu, because it looks at\n \t * the lowerdev-\u003emtu, rather than the static dev-\u003emax_mtu\n \t */\n \tif (lowerdev) {\n-\t\tint max_mtu = lowerdev-\u003emtu - vxlan_headroom(vxlan-\u003ecfg.flags);\n+\t\tint max_mtu = lowerdev-\u003emtu - vxlan_headroom(cfg-\u003eflags);\n \t\tif (new_mtu \u003e max_mtu)\n \t\t\treturn -EINVAL;\n \t}\n@@ -3248,11 +3324,14 @@ static int vxlan_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb)\n {\n \tstruct vxlan_dev *vxlan = netdev_priv(dev);\n \tstruct ip_tunnel_info *info = skb_tunnel_info(skb);\n+\tconst struct vxlan_config *cfg;\n \t__be16 sport, dport;\n \n-\tsport = udp_flow_src_port(dev_net(dev), skb, vxlan-\u003ecfg.port_min,\n-\t\t\t\t  vxlan-\u003ecfg.port_max, true);\n-\tdport = info-\u003ekey.tp_dst ? : vxlan-\u003ecfg.dst_port;\n+\tcfg = rcu_dereference(vxlan-\u003ecfg);\n+\n+\tsport = udp_flow_src_port(dev_net(dev), skb, cfg-\u003eport_min,\n+\t\t\t\t  cfg-\u003eport_max, true);\n+\tdport = info-\u003ekey.tp_dst ? : cfg-\u003edst_port;\n \n \tif (ip_tunnel_info_af(info) == AF_INET) {\n \t\tstruct vxlan_sock *sock4 = rcu_dereference(vxlan-\u003evn4_sock);\n@@ -3362,6 +3441,15 @@ static void vxlan_offload_rx_ports(struct net_device *dev, bool push)\n \t}\n }\n \n+static void vxlan_free_dev(struct net_device *dev)\n+{\n+\tstruct vxlan_dev *vxlan = netdev_priv(dev);\n+\tstruct vxlan_config *cfg = rcu_dereference_protected(vxlan-\u003ecfg, 1);\n+\n+\tRCU_INIT_POINTER(vxlan-\u003ecfg, NULL);\n+\tkfree(cfg);\n+}\n+\n /* Initialize the device structure. */\n static void vxlan_setup(struct net_device *dev)\n {\n@@ -3370,6 +3458,8 @@ static void vxlan_setup(struct net_device *dev)\n \teth_hw_addr_random(dev);\n \tether_setup(dev);\n \n+\tdev-\u003epriv_destructor = vxlan_free_dev;\n+\n \tdev-\u003eneeds_free_netdev = true;\n \tSET_NETDEV_DEVTYPE(dev, \u0026vxlan_type);\n \n@@ -3544,9 +3634,11 @@ static int vxlan_get_link_ksettings(struct net_device *dev,\n \t\t\t\t    struct ethtool_link_ksettings *cmd)\n {\n \tstruct vxlan_dev *vxlan = netdev_priv(dev);\n-\tstruct vxlan_rdst *dst = \u0026vxlan-\u003edefault_dst;\n-\tstruct net_device *lowerdev = __dev_get_by_index(vxlan-\u003enet,\n-\t\t\t\t\t\t\t dst-\u003eremote_ifindex);\n+\tconst struct vxlan_config *cfg;\n+\tstruct net_device *lowerdev;\n+\n+\tcfg = rtnl_dereference(vxlan-\u003ecfg);\n+\tlowerdev = __dev_get_by_index(vxlan-\u003enet, cfg-\u003eremote_ifindex);\n \n \tif (!lowerdev) {\n \t\tcmd-\u003ebase.duplex = DUPLEX_UNKNOWN;\n@@ -3652,21 +3744,22 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, bool ipv6,\n \n static int __vxlan_sock_add(struct vxlan_dev *vxlan, bool ipv6)\n {\n-\tbool metadata = vxlan-\u003ecfg.flags \u0026 VXLAN_F_COLLECT_METADATA;\n+\tconst struct vxlan_config *cfg = rtnl_dereference(vxlan-\u003ecfg);\n+\tbool metadata = cfg-\u003eflags \u0026 VXLAN_F_COLLECT_METADATA;\n \tstruct vxlan_sock *vs = NULL;\n \tstruct vxlan_dev_node *node;\n \tint l3mdev_index = 0;\n \n \tASSERT_RTNL();\n \n-\tif (vxlan-\u003ecfg.remote_ifindex)\n+\tif (cfg-\u003eremote_ifindex)\n \t\tl3mdev_index = l3mdev_master_upper_ifindex_by_index(\n-\t\t\tvxlan-\u003enet, vxlan-\u003ecfg.remote_ifindex);\n+\t\t\tvxlan-\u003enet, cfg-\u003eremote_ifindex);\n \n-\tif (!vxlan-\u003ecfg.no_share) {\n+\tif (!cfg-\u003eno_share) {\n \t\trcu_read_lock();\n \t\tvs = vxlan_find_sock(vxlan-\u003enet, ipv6 ? AF_INET6 : AF_INET,\n-\t\t\t\t     vxlan-\u003ecfg.dst_port, vxlan-\u003ecfg.flags,\n+\t\t\t\t     cfg-\u003edst_port, cfg-\u003eflags,\n \t\t\t\t     l3mdev_index);\n \t\tif (vs \u0026\u0026 !refcount_inc_not_zero(\u0026vs-\u003erefcnt)) {\n \t\t\trcu_read_unlock();\n@@ -3676,7 +3769,7 @@ static int __vxlan_sock_add(struct vxlan_dev *vxlan, bool ipv6)\n \t}\n \tif (!vs)\n \t\tvs = vxlan_socket_create(vxlan-\u003enet, ipv6,\n-\t\t\t\t\t vxlan-\u003ecfg.dst_port, vxlan-\u003ecfg.flags,\n+\t\t\t\t\t cfg-\u003edst_port, cfg-\u003eflags,\n \t\t\t\t\t l3mdev_index);\n \tif (IS_ERR(vs))\n \t\treturn PTR_ERR(vs);\n@@ -3691,7 +3784,7 @@ static int __vxlan_sock_add(struct vxlan_dev *vxlan, bool ipv6)\n \t\tnode = \u0026vxlan-\u003ehlist4;\n \t}\n \n-\tif (metadata \u0026\u0026 (vxlan-\u003ecfg.flags \u0026 VXLAN_F_VNIFILTER))\n+\tif (metadata \u0026\u0026 (cfg-\u003eflags \u0026 VXLAN_F_VNIFILTER))\n \t\tvxlan_vs_add_vnigrp(vxlan, vs, ipv6);\n \telse\n \t\tvxlan_vs_add_dev(vs, vxlan, node);\n@@ -3701,11 +3794,14 @@ static int __vxlan_sock_add(struct vxlan_dev *vxlan, bool ipv6)\n \n static int vxlan_sock_add(struct vxlan_dev *vxlan)\n {\n-\tbool metadata = vxlan-\u003ecfg.flags \u0026 VXLAN_F_COLLECT_METADATA;\n-\tbool ipv6 = vxlan-\u003ecfg.flags \u0026 VXLAN_F_IPV6 || metadata;\n-\tbool ipv4 = !ipv6 || metadata;\n+\tconst struct vxlan_config *cfg = rtnl_dereference(vxlan-\u003ecfg);\n+\tbool metadata, ipv6, ipv4;\n \tint ret = 0;\n \n+\tmetadata = cfg-\u003eflags \u0026 VXLAN_F_COLLECT_METADATA;\n+\tipv6 = (cfg-\u003eflags \u0026 VXLAN_F_IPV6) || metadata;\n+\tipv4 = !ipv6 || metadata;\n+\n \tRCU_INIT_POINTER(vxlan-\u003evn4_sock, NULL);\n #if IS_ENABLED(CONFIG_IPV6)\n \tRCU_INIT_POINTER(vxlan-\u003evn6_sock, NULL);\n@@ -3723,28 +3819,33 @@ static int vxlan_sock_add(struct vxlan_dev *vxlan)\n }\n \n int vxlan_vni_in_use(struct net *src_net, struct vxlan_dev *vxlan,\n-\t\t     struct vxlan_config *conf, __be32 vni)\n+\t\t     const struct vxlan_config *conf, __be32 vni)\n {\n \tstruct vxlan_net *vn = net_generic(src_net, vxlan_net_id);\n \tstruct vxlan_dev *tmp;\n \n \tlist_for_each_entry(tmp, \u0026vn-\u003evxlan_list, next) {\n+\t\tconst struct vxlan_config *tmp_cfg;\n+\n \t\tif (tmp == vxlan)\n \t\t\tcontinue;\n-\t\tif (tmp-\u003ecfg.flags \u0026 VXLAN_F_VNIFILTER) {\n+\n+\t\ttmp_cfg = rtnl_dereference(tmp-\u003ecfg);\n+\n+\t\tif (tmp_cfg-\u003eflags \u0026 VXLAN_F_VNIFILTER) {\n \t\t\tif (!vxlan_vnifilter_lookup(tmp, vni))\n \t\t\t\tcontinue;\n-\t\t} else if (tmp-\u003ecfg.vni != vni) {\n+\t\t} else if (tmp_cfg-\u003evni != vni) {\n \t\t\tcontinue;\n \t\t}\n-\t\tif (tmp-\u003ecfg.dst_port != conf-\u003edst_port)\n+\t\tif (tmp_cfg-\u003edst_port != conf-\u003edst_port)\n \t\t\tcontinue;\n-\t\tif ((tmp-\u003ecfg.flags \u0026 (VXLAN_F_RCV_FLAGS | VXLAN_F_IPV6)) !=\n+\t\tif ((tmp_cfg-\u003eflags \u0026 (VXLAN_F_RCV_FLAGS | VXLAN_F_IPV6)) !=\n \t\t    (conf-\u003eflags \u0026 (VXLAN_F_RCV_FLAGS | VXLAN_F_IPV6)))\n \t\t\tcontinue;\n \n \t\tif ((conf-\u003eflags \u0026 VXLAN_F_IPV6_LINKLOCAL) \u0026\u0026\n-\t\t    tmp-\u003ecfg.remote_ifindex != conf-\u003eremote_ifindex)\n+\t\t    tmp_cfg-\u003eremote_ifindex != conf-\u003eremote_ifindex)\n \t\t\tcontinue;\n \n \t\treturn -EEXIST;\n@@ -3906,16 +4007,16 @@ static int vxlan_config_validate(struct net *src_net, struct vxlan_config *conf,\n }\n \n static void vxlan_config_apply(struct net_device *dev,\n-\t\t\t       struct vxlan_config *conf,\n+\t\t\t       struct vxlan_config *new_cfg,\n \t\t\t       struct net_device *lowerdev,\n \t\t\t       struct net *src_net,\n \t\t\t       bool changelink)\n {\n \tstruct vxlan_dev *vxlan = netdev_priv(dev);\n-\tstruct vxlan_rdst *dst = \u0026vxlan-\u003edefault_dst;\n \tunsigned short needed_headroom = ETH_HLEN;\n+\tstruct vxlan_config *old_cfg;\n \tint max_mtu = ETH_MAX_MTU;\n-\tu32 flags = conf-\u003eflags;\n+\tu32 flags = new_cfg-\u003eflags;\n \n \tif (!changelink) {\n \t\tif (flags \u0026 VXLAN_F_GPE)\n@@ -3923,19 +4024,13 @@ static void vxlan_config_apply(struct net_device *dev,\n \t\telse\n \t\t\tvxlan_ether_setup(dev);\n \n-\t\tif (conf-\u003emtu)\n-\t\t\tdev-\u003emtu = conf-\u003emtu;\n+\t\tif (new_cfg-\u003emtu)\n+\t\t\tdev-\u003emtu = new_cfg-\u003emtu;\n \n \t\tvxlan-\u003enet = src_net;\n \t}\n \n-\tdst-\u003eremote_vni = conf-\u003evni;\n-\n-\tmemcpy(\u0026dst-\u003eremote_ip, \u0026conf-\u003eremote_ip, sizeof(conf-\u003eremote_ip));\n-\n \tif (lowerdev) {\n-\t\tdst-\u003eremote_ifindex = conf-\u003eremote_ifindex;\n-\n \t\tnetif_inherit_tso_max(dev, lowerdev);\n \n \t\tneeded_headroom = lowerdev-\u003ehard_header_len;\n@@ -3947,7 +4042,7 @@ static void vxlan_config_apply(struct net_device *dev,\n \t\tif (max_mtu \u003c ETH_MIN_MTU)\n \t\t\tmax_mtu = ETH_MIN_MTU;\n \n-\t\tif (!changelink \u0026\u0026 !conf-\u003emtu)\n+\t\tif (!changelink \u0026\u0026 !new_cfg-\u003emtu)\n \t\t\tdev-\u003emtu = max_mtu;\n \t}\n \n@@ -3959,7 +4054,10 @@ static void vxlan_config_apply(struct net_device *dev,\n \tneeded_headroom += vxlan_headroom(flags);\n \tdev-\u003eneeded_headroom = needed_headroom;\n \n-\tmemcpy(\u0026vxlan-\u003ecfg, conf, sizeof(*conf));\n+\told_cfg = rtnl_dereference(vxlan-\u003ecfg);\n+\trcu_assign_pointer(vxlan-\u003ecfg, new_cfg);\n+\tif (old_cfg)\n+\t\tkfree_rcu(old_cfg, rcu);\n }\n \n static int vxlan_dev_configure(struct net *src_net, struct net_device *dev,\n@@ -3968,13 +4066,18 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev,\n {\n \tstruct vxlan_dev *vxlan = netdev_priv(dev);\n \tstruct net_device *lowerdev;\n+\tstruct vxlan_config *new_cfg;\n \tint ret;\n \n \tret = vxlan_config_validate(src_net, conf, \u0026lowerdev, vxlan, extack);\n \tif (ret)\n \t\treturn ret;\n \n-\tvxlan_config_apply(dev, conf, lowerdev, src_net, false);\n+\tnew_cfg = kmemdup(conf, sizeof(*conf), GFP_KERNEL);\n+\tif (!new_cfg)\n+\t\treturn -ENOMEM;\n+\n+\tvxlan_config_apply(dev, new_cfg, lowerdev, src_net, false);\n \n \treturn 0;\n }\n@@ -3986,22 +4089,25 @@ static int vxlan_dev_create(struct net *net, struct net_device *dev,\n \tstruct vxlan_net *vn = net_generic(net, vxlan_net_id);\n \tstruct vxlan_dev *vxlan = netdev_priv(dev);\n \tstruct net_device *remote_dev = NULL;\n-\tstruct vxlan_rdst *dst;\n+\tconst struct vxlan_config *cfg;\n \tint err;\n \n-\tdst = \u0026vxlan-\u003edefault_dst;\n \terr = vxlan_dev_configure(net, dev, conf, extack);\n \tif (err)\n \t\treturn err;\n \n+\tcfg = rtnl_dereference(vxlan-\u003ecfg);\n+\n \tdev-\u003eethtool_ops = \u0026vxlan_ethtool_ops;\n \n \terr = register_netdevice(dev);\n-\tif (err)\n+\tif (err) {\n+\t\tvxlan_free_dev(dev);\n \t\treturn err;\n+\t}\n \n-\tif (dst-\u003eremote_ifindex) {\n-\t\tremote_dev = __dev_get_by_index(net, dst-\u003eremote_ifindex);\n+\tif (cfg-\u003eremote_ifindex) {\n+\t\tremote_dev = __dev_get_by_index(net, cfg-\u003eremote_ifindex);\n \t\tif (!remote_dev) {\n \t\t\terr = -ENODEV;\n \t\t\tgoto unregister;\n@@ -4011,7 +4117,7 @@ static int vxlan_dev_create(struct net *net, struct net_device *dev,\n \t\tif (err)\n \t\t\tgoto unregister;\n \n-\t\tdst-\u003eremote_dev = remote_dev;\n+\t\tvxlan-\u003elowerdev = remote_dev;\n \t}\n \n \terr = rtnl_configure_link(dev, NULL, 0, NULL);\n@@ -4019,16 +4125,18 @@ static int vxlan_dev_create(struct net *net, struct net_device *dev,\n \t\tgoto unlink;\n \n \t/* create an fdb entry for a valid default destination */\n-\tif (!vxlan_addr_any(\u0026dst-\u003eremote_ip)) {\n+\tif (!vxlan_addr_any(\u0026cfg-\u003eremote_ip)) {\n+\t\tunion vxlan_addr rip = cfg-\u003eremote_ip;\n+\n \t\tspin_lock_bh(\u0026vxlan-\u003ehash_lock);\n \t\terr = vxlan_fdb_update(vxlan, all_zeros_mac,\n-\t\t\t\t       \u0026dst-\u003eremote_ip,\n+\t\t\t\t       \u0026rip,\n \t\t\t\t       NUD_REACHABLE | NUD_PERMANENT,\n \t\t\t\t       NLM_F_EXCL | NLM_F_CREATE,\n-\t\t\t\t       vxlan-\u003ecfg.dst_port,\n-\t\t\t\t       dst-\u003eremote_vni,\n-\t\t\t\t       dst-\u003eremote_vni,\n-\t\t\t\t       dst-\u003eremote_ifindex,\n+\t\t\t\t       cfg-\u003edst_port,\n+\t\t\t\t       cfg-\u003evni,\n+\t\t\t\t       cfg-\u003evni,\n+\t\t\t\t       cfg-\u003eremote_ifindex,\n \t\t\t\t       NTF_SELF, 0, true, extack);\n \t\tspin_unlock_bh(\u0026vxlan-\u003ehash_lock);\n \t\tif (err)\n@@ -4089,8 +4197,12 @@ static int vxlan_nl2conf(struct nlattr *tb[], struct nlattr *data[],\n \tmemset(conf, 0, sizeof(*conf));\n \n \t/* if changelink operation, start with old existing cfg */\n-\tif (changelink)\n-\t\tmemcpy(conf, \u0026vxlan-\u003ecfg, sizeof(*conf));\n+\tif (changelink) {\n+\t\tconst struct vxlan_config *cfg = rtnl_dereference(vxlan-\u003ecfg);\n+\n+\t\tif (cfg)\n+\t\t\tmemcpy(conf, cfg, sizeof(*conf));\n+\t}\n \n \tif (data[IFLA_VXLAN_ID]) {\n \t\t__be32 vni = cpu_to_be32(nla_get_u32(data[IFLA_VXLAN_ID]));\n@@ -4437,16 +4549,16 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[],\n \t\t\t    struct netlink_ext_ack *extack)\n {\n \tstruct vxlan_dev *vxlan = netdev_priv(dev);\n+\tconst struct vxlan_config *cfg = rtnl_dereference(vxlan-\u003ecfg);\n \tbool rem_ip_changed, change_igmp;\n \tstruct net_device *lowerdev;\n \tstruct vxlan_config conf;\n-\tstruct vxlan_rdst *dst;\n+\tstruct vxlan_config *new_cfg;\n \tint err;\n \n \tif (!rtnl_dev_link_net_capable(dev, vxlan-\u003enet))\n \t\treturn -EPERM;\n \n-\tdst = \u0026vxlan-\u003edefault_dst;\n \terr = vxlan_nl2conf(tb, data, dev, \u0026conf, true, extack);\n \tif (err)\n \t\treturn err;\n@@ -4456,18 +4568,23 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[],\n \tif (err)\n \t\treturn err;\n \n-\tif (dst-\u003eremote_dev == lowerdev)\n-\t\tlowerdev = NULL;\n+\tnew_cfg = kmemdup(\u0026conf, sizeof(conf), GFP_KERNEL);\n+\tif (!new_cfg)\n+\t\treturn -ENOMEM;\n \n-\terr = netdev_adjacent_change_prepare(dst-\u003eremote_dev, lowerdev, dev,\n-\t\t\t\t\t     extack);\n-\tif (err)\n-\t\treturn err;\n+\tif (vxlan-\u003elowerdev != lowerdev) {\n+\t\terr = netdev_adjacent_change_prepare(vxlan-\u003elowerdev, lowerdev,\n+\t\t\t\t\t\t     dev, extack);\n+\t\tif (err) {\n+\t\t\tkfree(new_cfg);\n+\t\t\treturn err;\n+\t\t}\n+\t}\n \n-\trem_ip_changed = !vxlan_addr_equal(\u0026conf.remote_ip, \u0026dst-\u003eremote_ip);\n+\trem_ip_changed = !vxlan_addr_equal(\u0026conf.remote_ip, \u0026cfg-\u003eremote_ip);\n \tchange_igmp = vxlan-\u003edev-\u003eflags \u0026 IFF_UP \u0026\u0026\n \t\t      (rem_ip_changed ||\n-\t\t       dst-\u003eremote_ifindex != conf.remote_ifindex);\n+\t\t       cfg-\u003eremote_ifindex != conf.remote_ifindex);\n \n \t/* handle default dst entry */\n \tif (rem_ip_changed) {\n@@ -4477,54 +4594,63 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[],\n \t\t\t\t\t       \u0026conf.remote_ip,\n \t\t\t\t\t       NUD_REACHABLE | NUD_PERMANENT,\n \t\t\t\t\t       NLM_F_APPEND | NLM_F_CREATE,\n-\t\t\t\t\t       vxlan-\u003ecfg.dst_port,\n+\t\t\t\t\t       cfg-\u003edst_port,\n \t\t\t\t\t       conf.vni, conf.vni,\n \t\t\t\t\t       conf.remote_ifindex,\n \t\t\t\t\t       NTF_SELF, 0, true, extack);\n \t\t\tif (err) {\n \t\t\t\tspin_unlock_bh(\u0026vxlan-\u003ehash_lock);\n-\t\t\t\tnetdev_adjacent_change_abort(dst-\u003eremote_dev,\n-\t\t\t\t\t\t\t     lowerdev, dev);\n+\t\t\t\tif (vxlan-\u003elowerdev != lowerdev)\n+\t\t\t\t\tnetdev_adjacent_change_abort(vxlan-\u003elowerdev,\n+\t\t\t\t\t\t\t\t     lowerdev, dev);\n+\t\t\t\tkfree(new_cfg);\n \t\t\t\treturn err;\n \t\t\t}\n \t\t}\n-\t\tif (!vxlan_addr_any(\u0026dst-\u003eremote_ip))\n+\t\tif (!vxlan_addr_any(\u0026cfg-\u003eremote_ip))\n \t\t\t__vxlan_fdb_delete(vxlan, all_zeros_mac,\n-\t\t\t\t\t   dst-\u003eremote_ip,\n-\t\t\t\t\t   vxlan-\u003ecfg.dst_port,\n-\t\t\t\t\t   dst-\u003eremote_vni,\n-\t\t\t\t\t   dst-\u003eremote_vni,\n-\t\t\t\t\t   dst-\u003eremote_ifindex,\n+\t\t\t\t\t   cfg-\u003eremote_ip,\n+\t\t\t\t\t   cfg-\u003edst_port,\n+\t\t\t\t\t   cfg-\u003evni,\n+\t\t\t\t\t   cfg-\u003evni,\n+\t\t\t\t\t   cfg-\u003eremote_ifindex,\n \t\t\t\t\t   true);\n \t\tspin_unlock_bh(\u0026vxlan-\u003ehash_lock);\n \n \t\t/* If vni filtering device, also update fdb entries of\n \t\t * all vnis that were using default remote ip\n \t\t */\n-\t\tif (vxlan-\u003ecfg.flags \u0026 VXLAN_F_VNIFILTER) {\n-\t\t\terr = vxlan_vnilist_update_group(vxlan, \u0026dst-\u003eremote_ip,\n+\t\tif (cfg-\u003eflags \u0026 VXLAN_F_VNIFILTER) {\n+\t\t\terr = vxlan_vnilist_update_group(vxlan, \u0026cfg-\u003eremote_ip,\n \t\t\t\t\t\t\t \u0026conf.remote_ip, extack);\n \t\t\tif (err) {\n-\t\t\t\tnetdev_adjacent_change_abort(dst-\u003eremote_dev,\n-\t\t\t\t\t\t\t     lowerdev, dev);\n+\t\t\t\tif (vxlan-\u003elowerdev != lowerdev)\n+\t\t\t\t\tnetdev_adjacent_change_abort(vxlan-\u003elowerdev,\n+\t\t\t\t\t\t\t\t     lowerdev, dev);\n+\t\t\t\tkfree(new_cfg);\n \t\t\t\treturn err;\n \t\t\t}\n \t\t}\n \t}\n \n-\tif (change_igmp \u0026\u0026 vxlan_addr_multicast(\u0026dst-\u003eremote_ip))\n+\tif (change_igmp \u0026\u0026 vxlan_addr_multicast(\u0026cfg-\u003eremote_ip))\n \t\terr = vxlan_multicast_leave(vxlan);\n \n-\tif (netif_running(dev) \u0026\u0026 conf.age_interval != vxlan-\u003ecfg.age_interval)\n+\tif (netif_running(dev) \u0026\u0026 conf.age_interval != cfg-\u003eage_interval)\n \t\tmod_timer(\u0026vxlan-\u003eage_timer, jiffies);\n \n-\tnetdev_adjacent_change_commit(dst-\u003eremote_dev, lowerdev, dev);\n-\tif (lowerdev \u0026\u0026 lowerdev != dst-\u003eremote_dev)\n-\t\tdst-\u003eremote_dev = lowerdev;\n-\tvxlan_config_apply(dev, \u0026conf, lowerdev, vxlan-\u003enet, true);\n+\tif (vxlan-\u003elowerdev != lowerdev) {\n+\t\tif (lowerdev)\n+\t\t\tnetdev_adjacent_change_commit(vxlan-\u003elowerdev, lowerdev,\n+\t\t\t\t\t\t      dev);\n+\t\telse\n+\t\t\tnetdev_upper_dev_unlink(vxlan-\u003elowerdev, dev);\n+\t\tvxlan-\u003elowerdev = lowerdev;\n+\t}\n+\tvxlan_config_apply(dev, new_cfg, lowerdev, vxlan-\u003enet, true);\n \n \tif (!err \u0026\u0026 change_igmp \u0026\u0026\n-\t    vxlan_addr_multicast(\u0026dst-\u003eremote_ip))\n+\t    vxlan_addr_multicast(\u0026new_cfg-\u003eremote_ip))\n \t\terr = vxlan_multicast_join(vxlan);\n \n \treturn err;\n@@ -4539,8 +4665,8 @@ static void vxlan_dellink(struct net_device *dev, struct list_head *head)\n \n \tlist_del(\u0026vxlan-\u003enext);\n \tunregister_netdevice_queue(dev, head);\n-\tif (vxlan-\u003edefault_dst.remote_dev)\n-\t\tnetdev_upper_dev_unlink(vxlan-\u003edefault_dst.remote_dev, dev);\n+\tif (vxlan-\u003elowerdev)\n+\t\tnetdev_upper_dev_unlink(vxlan-\u003elowerdev, dev);\n }\n \n static size_t vxlan_get_size(const struct net_device *dev)\n@@ -4584,110 +4710,120 @@ static size_t vxlan_get_size(const struct net_device *dev)\n static int vxlan_fill_info(struct sk_buff *skb, const struct net_device *dev)\n {\n \tconst struct vxlan_dev *vxlan = netdev_priv(dev);\n-\tconst struct vxlan_rdst *dst = \u0026vxlan-\u003edefault_dst;\n-\tstruct ifla_vxlan_port_range ports = {\n-\t\t.low =  htons(vxlan-\u003ecfg.port_min),\n-\t\t.high = htons(vxlan-\u003ecfg.port_max),\n-\t};\n+\tstruct ifla_vxlan_port_range ports;\n+\tconst struct vxlan_config *cfg;\n+\tint err = 0;\n \n-\tif (nla_put_u32(skb, IFLA_VXLAN_ID, be32_to_cpu(dst-\u003eremote_vni)))\n+\trcu_read_lock();\n+\tcfg = rcu_dereference(vxlan-\u003ecfg);\n+\tif (!cfg) {\n+\t\terr = -ENODEV;\n+\t\tgoto out;\n+\t}\n+\n+\tif (nla_put_u32(skb, IFLA_VXLAN_ID, be32_to_cpu(cfg-\u003evni)))\n \t\tgoto nla_put_failure;\n \n-\tif (!vxlan_addr_any(\u0026dst-\u003eremote_ip)) {\n-\t\tif (dst-\u003eremote_ip.sa.sa_family == AF_INET) {\n+\tif (!vxlan_addr_any(\u0026cfg-\u003eremote_ip)) {\n+\t\tif (cfg-\u003eremote_ip.sa.sa_family == AF_INET) {\n \t\t\tif (nla_put_in_addr(skb, IFLA_VXLAN_GROUP,\n-\t\t\t\t\t    dst-\u003eremote_ip.sin.sin_addr.s_addr))\n+\t\t\t\t\t    cfg-\u003eremote_ip.sin.sin_addr.s_addr))\n \t\t\t\tgoto nla_put_failure;\n #if IS_ENABLED(CONFIG_IPV6)\n \t\t} else {\n \t\t\tif (nla_put_in6_addr(skb, IFLA_VXLAN_GROUP6,\n-\t\t\t\t\t     \u0026dst-\u003eremote_ip.sin6.sin6_addr))\n+\t\t\t\t\t     \u0026cfg-\u003eremote_ip.sin6.sin6_addr))\n \t\t\t\tgoto nla_put_failure;\n #endif\n \t\t}\n \t}\n \n-\tif (dst-\u003eremote_ifindex \u0026\u0026 nla_put_u32(skb, IFLA_VXLAN_LINK, dst-\u003eremote_ifindex))\n+\tif (cfg-\u003eremote_ifindex \u0026\u0026 nla_put_u32(skb, IFLA_VXLAN_LINK, cfg-\u003eremote_ifindex))\n \t\tgoto nla_put_failure;\n \n-\tif (!vxlan_addr_any(\u0026vxlan-\u003ecfg.saddr)) {\n-\t\tif (vxlan-\u003ecfg.saddr.sa.sa_family == AF_INET) {\n+\tif (!vxlan_addr_any(\u0026cfg-\u003esaddr)) {\n+\t\tif (cfg-\u003esaddr.sa.sa_family == AF_INET) {\n \t\t\tif (nla_put_in_addr(skb, IFLA_VXLAN_LOCAL,\n-\t\t\t\t\t    vxlan-\u003ecfg.saddr.sin.sin_addr.s_addr))\n+\t\t\t\t\t    cfg-\u003esaddr.sin.sin_addr.s_addr))\n \t\t\t\tgoto nla_put_failure;\n #if IS_ENABLED(CONFIG_IPV6)\n \t\t} else {\n \t\t\tif (nla_put_in6_addr(skb, IFLA_VXLAN_LOCAL6,\n-\t\t\t\t\t     \u0026vxlan-\u003ecfg.saddr.sin6.sin6_addr))\n+\t\t\t\t\t     \u0026cfg-\u003esaddr.sin6.sin6_addr))\n \t\t\t\tgoto nla_put_failure;\n #endif\n \t\t}\n \t}\n \n-\tif (nla_put_u8(skb, IFLA_VXLAN_TTL, vxlan-\u003ecfg.ttl) ||\n+\tif (nla_put_u8(skb, IFLA_VXLAN_TTL, cfg-\u003ettl) ||\n \t    nla_put_u8(skb, IFLA_VXLAN_TTL_INHERIT,\n-\t\t       !!(vxlan-\u003ecfg.flags \u0026 VXLAN_F_TTL_INHERIT)) ||\n-\t    nla_put_u8(skb, IFLA_VXLAN_TOS, vxlan-\u003ecfg.tos) ||\n-\t    nla_put_u8(skb, IFLA_VXLAN_DF, vxlan-\u003ecfg.df) ||\n-\t    nla_put_be32(skb, IFLA_VXLAN_LABEL, vxlan-\u003ecfg.label) ||\n-\t    nla_put_u32(skb, IFLA_VXLAN_LABEL_POLICY, vxlan-\u003ecfg.label_policy) ||\n+\t\t       !!(cfg-\u003eflags \u0026 VXLAN_F_TTL_INHERIT)) ||\n+\t    nla_put_u8(skb, IFLA_VXLAN_TOS, cfg-\u003etos) ||\n+\t    nla_put_u8(skb, IFLA_VXLAN_DF, cfg-\u003edf) ||\n+\t    nla_put_be32(skb, IFLA_VXLAN_LABEL, cfg-\u003elabel) ||\n+\t    nla_put_u32(skb, IFLA_VXLAN_LABEL_POLICY, cfg-\u003elabel_policy) ||\n \t    nla_put_u8(skb, IFLA_VXLAN_LEARNING,\n-\t\t       !!(vxlan-\u003ecfg.flags \u0026 VXLAN_F_LEARN)) ||\n+\t\t       !!(cfg-\u003eflags \u0026 VXLAN_F_LEARN)) ||\n \t    nla_put_u8(skb, IFLA_VXLAN_PROXY,\n-\t\t       !!(vxlan-\u003ecfg.flags \u0026 VXLAN_F_PROXY)) ||\n+\t\t       !!(cfg-\u003eflags \u0026 VXLAN_F_PROXY)) ||\n \t    nla_put_u8(skb, IFLA_VXLAN_RSC,\n-\t\t       !!(vxlan-\u003ecfg.flags \u0026 VXLAN_F_RSC)) ||\n+\t\t       !!(cfg-\u003eflags \u0026 VXLAN_F_RSC)) ||\n \t    nla_put_u8(skb, IFLA_VXLAN_L2MISS,\n-\t\t       !!(vxlan-\u003ecfg.flags \u0026 VXLAN_F_L2MISS)) ||\n+\t\t       !!(cfg-\u003eflags \u0026 VXLAN_F_L2MISS)) ||\n \t    nla_put_u8(skb, IFLA_VXLAN_L3MISS,\n-\t\t       !!(vxlan-\u003ecfg.flags \u0026 VXLAN_F_L3MISS)) ||\n+\t\t       !!(cfg-\u003eflags \u0026 VXLAN_F_L3MISS)) ||\n \t    nla_put_u8(skb, IFLA_VXLAN_COLLECT_METADATA,\n-\t\t       !!(vxlan-\u003ecfg.flags \u0026 VXLAN_F_COLLECT_METADATA)) ||\n-\t    nla_put_u32(skb, IFLA_VXLAN_AGEING, vxlan-\u003ecfg.age_interval) ||\n-\t    nla_put_u32(skb, IFLA_VXLAN_LIMIT, vxlan-\u003ecfg.addrmax) ||\n-\t    nla_put_be16(skb, IFLA_VXLAN_PORT, vxlan-\u003ecfg.dst_port) ||\n+\t\t       !!(cfg-\u003eflags \u0026 VXLAN_F_COLLECT_METADATA)) ||\n+\t    nla_put_u32(skb, IFLA_VXLAN_AGEING, cfg-\u003eage_interval) ||\n+\t    nla_put_u32(skb, IFLA_VXLAN_LIMIT, cfg-\u003eaddrmax) ||\n+\t    nla_put_be16(skb, IFLA_VXLAN_PORT, cfg-\u003edst_port) ||\n \t    nla_put_u8(skb, IFLA_VXLAN_UDP_CSUM,\n-\t\t       !(vxlan-\u003ecfg.flags \u0026 VXLAN_F_UDP_ZERO_CSUM_TX)) ||\n+\t\t       !(cfg-\u003eflags \u0026 VXLAN_F_UDP_ZERO_CSUM_TX)) ||\n \t    nla_put_u8(skb, IFLA_VXLAN_UDP_ZERO_CSUM6_TX,\n-\t\t       !!(vxlan-\u003ecfg.flags \u0026 VXLAN_F_UDP_ZERO_CSUM6_TX)) ||\n+\t\t       !!(cfg-\u003eflags \u0026 VXLAN_F_UDP_ZERO_CSUM6_TX)) ||\n \t    nla_put_u8(skb, IFLA_VXLAN_UDP_ZERO_CSUM6_RX,\n-\t\t       !!(vxlan-\u003ecfg.flags \u0026 VXLAN_F_UDP_ZERO_CSUM6_RX)) ||\n+\t\t       !!(cfg-\u003eflags \u0026 VXLAN_F_UDP_ZERO_CSUM6_RX)) ||\n \t    nla_put_u8(skb, IFLA_VXLAN_REMCSUM_TX,\n-\t\t       !!(vxlan-\u003ecfg.flags \u0026 VXLAN_F_REMCSUM_TX)) ||\n+\t\t       !!(cfg-\u003eflags \u0026 VXLAN_F_REMCSUM_TX)) ||\n \t    nla_put_u8(skb, IFLA_VXLAN_REMCSUM_RX,\n-\t\t       !!(vxlan-\u003ecfg.flags \u0026 VXLAN_F_REMCSUM_RX)) ||\n+\t\t       !!(cfg-\u003eflags \u0026 VXLAN_F_REMCSUM_RX)) ||\n \t    nla_put_u8(skb, IFLA_VXLAN_LOCALBYPASS,\n-\t\t       !!(vxlan-\u003ecfg.flags \u0026 VXLAN_F_LOCALBYPASS)))\n+\t\t       !!(cfg-\u003eflags \u0026 VXLAN_F_LOCALBYPASS)))\n \t\tgoto nla_put_failure;\n \n+\tports.low = htons(cfg-\u003eport_min);\n+\tports.high = htons(cfg-\u003eport_max);\n \tif (nla_put(skb, IFLA_VXLAN_PORT_RANGE, sizeof(ports), \u0026ports))\n \t\tgoto nla_put_failure;\n \n-\tif (vxlan-\u003ecfg.flags \u0026 VXLAN_F_GBP \u0026\u0026\n+\tif (cfg-\u003eflags \u0026 VXLAN_F_GBP \u0026\u0026\n \t    nla_put_flag(skb, IFLA_VXLAN_GBP))\n \t\tgoto nla_put_failure;\n \n-\tif (vxlan-\u003ecfg.flags \u0026 VXLAN_F_GPE \u0026\u0026\n+\tif (cfg-\u003eflags \u0026 VXLAN_F_GPE \u0026\u0026\n \t    nla_put_flag(skb, IFLA_VXLAN_GPE))\n \t\tgoto nla_put_failure;\n \n-\tif (vxlan-\u003ecfg.flags \u0026 VXLAN_F_REMCSUM_NOPARTIAL \u0026\u0026\n+\tif (cfg-\u003eflags \u0026 VXLAN_F_REMCSUM_NOPARTIAL \u0026\u0026\n \t    nla_put_flag(skb, IFLA_VXLAN_REMCSUM_NOPARTIAL))\n \t\tgoto nla_put_failure;\n \n-\tif (vxlan-\u003ecfg.flags \u0026 VXLAN_F_VNIFILTER \u0026\u0026\n+\tif (cfg-\u003eflags \u0026 VXLAN_F_VNIFILTER \u0026\u0026\n \t    nla_put_u8(skb, IFLA_VXLAN_VNIFILTER,\n-\t\t       !!(vxlan-\u003ecfg.flags \u0026 VXLAN_F_VNIFILTER)))\n+\t\t       !!(cfg-\u003eflags \u0026 VXLAN_F_VNIFILTER)))\n \t\tgoto nla_put_failure;\n \n \tif (nla_put(skb, IFLA_VXLAN_RESERVED_BITS,\n-\t\t    sizeof(vxlan-\u003ecfg.reserved_bits),\n-\t\t    \u0026vxlan-\u003ecfg.reserved_bits))\n+\t\t    sizeof(cfg-\u003ereserved_bits),\n+\t\t    \u0026cfg-\u003ereserved_bits))\n \t\tgoto nla_put_failure;\n \n-\treturn 0;\n+out:\n+\trcu_read_unlock();\n+\treturn err;\n \n nla_put_failure:\n+\trcu_read_unlock();\n \treturn -EMSGSIZE;\n }\n \n@@ -4720,7 +4856,7 @@ static void vxlan_handle_lowerdev_unregister(struct vxlan_net *vn,\n \tLIST_HEAD(list_kill);\n \n \tlist_for_each_entry_safe(vxlan, next, \u0026vn-\u003evxlan_list, next) {\n-\t\tstruct vxlan_rdst *dst = \u0026vxlan-\u003edefault_dst;\n+\t\tconst struct vxlan_config *cfg = rtnl_dereference(vxlan-\u003ecfg);\n \n \t\t/* In case we created vxlan device with carrier\n \t\t * and we loose the carrier due to module unload\n@@ -4728,7 +4864,7 @@ static void vxlan_handle_lowerdev_unregister(struct vxlan_net *vn,\n \t\t * cases, it's not necessary and remote_ifindex\n \t\t * is 0 here, so no matches.\n \t\t */\n-\t\tif (dst-\u003eremote_ifindex == dev-\u003eifindex)\n+\t\tif (cfg-\u003eremote_ifindex == dev-\u003eifindex)\n \t\t\tvxlan_dellink(vxlan-\u003edev, \u0026list_kill);\n \t}\n \ndiff --git a/drivers/net/vxlan/vxlan_mdb.c b/drivers/net/vxlan/vxlan_mdb.c\nindex 841f42ffecb9a..c1a0551990555 100644\n--- a/drivers/net/vxlan/vxlan_mdb.c\n+++ b/drivers/net/vxlan/vxlan_mdb.c\n@@ -165,6 +165,7 @@ static int vxlan_mdb_entry_info_fill(const struct vxlan_dev *vxlan,\n \t\t\t\t     const struct vxlan_mdb_entry *mdb_entry,\n \t\t\t\t     const struct vxlan_mdb_remote *remote)\n {\n+\tconst struct vxlan_config *cfg = rcu_dereference_rtnl(vxlan-\u003ecfg);\n \tstruct vxlan_rdst *rd = rtnl_dereference(remote-\u003erd);\n \tstruct br_mdb_entry e;\n \tstruct nlattr *nest;\n@@ -189,12 +190,12 @@ static int vxlan_mdb_entry_info_fill(const struct vxlan_dev *vxlan,\n \t    vxlan_nla_put_addr(skb, MDBA_MDB_EATTR_DST, \u0026rd-\u003eremote_ip))\n \t\tgoto nest_err;\n \n-\tif (rd-\u003eremote_port \u0026\u0026 rd-\u003eremote_port != vxlan-\u003ecfg.dst_port \u0026\u0026\n+\tif (rd-\u003eremote_port \u0026\u0026 rd-\u003eremote_port != cfg-\u003edst_port \u0026\u0026\n \t    nla_put_u16(skb, MDBA_MDB_EATTR_DST_PORT,\n \t\t\tbe16_to_cpu(rd-\u003eremote_port)))\n \t\tgoto nest_err;\n \n-\tif (rd-\u003eremote_vni != vxlan-\u003edefault_dst.remote_vni \u0026\u0026\n+\tif (rd-\u003eremote_vni != cfg-\u003evni \u0026\u0026\n \t    nla_put_u32(skb, MDBA_MDB_EATTR_VNI, be32_to_cpu(rd-\u003eremote_vni)))\n \t\tgoto nest_err;\n \n@@ -202,7 +203,7 @@ static int vxlan_mdb_entry_info_fill(const struct vxlan_dev *vxlan,\n \t    nla_put_u32(skb, MDBA_MDB_EATTR_IFINDEX, rd-\u003eremote_ifindex))\n \t\tgoto nest_err;\n \n-\tif ((vxlan-\u003ecfg.flags \u0026 VXLAN_F_COLLECT_METADATA) \u0026\u0026\n+\tif ((cfg-\u003eflags \u0026 VXLAN_F_COLLECT_METADATA) \u0026\u0026\n \t    mdb_entry-\u003ekey.vni \u0026\u0026 nla_put_u32(skb, MDBA_MDB_EATTR_SRC_VNI,\n \t\t\t\t\t      be32_to_cpu(mdb_entry-\u003ekey.vni)))\n \t\tgoto nest_err;\n@@ -613,16 +614,19 @@ static int vxlan_mdb_config_init(struct vxlan_mdb_config *cfg,\n {\n \tstruct br_mdb_entry *entry = nla_data(tb[MDBA_SET_ENTRY]);\n \tstruct vxlan_dev *vxlan = netdev_priv(dev);\n+\tconst struct vxlan_config *vcfg;\n+\n+\tvcfg = rtnl_dereference(vxlan-\u003ecfg);\n \n \tmemset(cfg, 0, sizeof(*cfg));\n \tcfg-\u003evxlan = vxlan;\n-\tcfg-\u003egroup.vni = vxlan-\u003edefault_dst.remote_vni;\n+\tcfg-\u003egroup.vni = vcfg-\u003evni;\n \tINIT_LIST_HEAD(\u0026cfg-\u003esrc_list);\n \tcfg-\u003enlflags = nlmsg_flags;\n \tcfg-\u003efilter_mode = MCAST_EXCLUDE;\n \tcfg-\u003ert_protocol = RTPROT_STATIC;\n-\tcfg-\u003eremote_vni = vxlan-\u003edefault_dst.remote_vni;\n-\tcfg-\u003eremote_port = vxlan-\u003ecfg.dst_port;\n+\tcfg-\u003eremote_vni = vcfg-\u003evni;\n+\tcfg-\u003eremote_port = vcfg-\u003edst_port;\n \n \tif (entry-\u003eifindex != dev-\u003eifindex) {\n \t\tNL_SET_ERR_MSG_MOD(extack, \"Port net device must be the VXLAN net device\");\n@@ -957,11 +961,12 @@ vxlan_mdb_nlmsg_remote_size(const struct vxlan_dev *vxlan,\n \t\t\t    const struct vxlan_mdb_entry *mdb_entry,\n \t\t\t    const struct vxlan_mdb_remote *remote)\n {\n+\tconst struct vxlan_config *cfg = rcu_dereference_rtnl(vxlan-\u003ecfg);\n \tconst struct vxlan_mdb_entry_key *group = \u0026mdb_entry-\u003ekey;\n \tstruct vxlan_rdst *rd = rtnl_dereference(remote-\u003erd);\n \tsize_t nlmsg_size;\n \n-\t\t     /* MDBA_MDB_ENTRY_INFO */\n+\t/* MDBA_MDB_ENTRY_INFO */\n \tnlmsg_size = nla_total_size(sizeof(struct br_mdb_entry)) +\n \t\t     /* MDBA_MDB_EATTR_TIMER */\n \t\t     nla_total_size(sizeof(u32));\n@@ -978,16 +983,16 @@ vxlan_mdb_nlmsg_remote_size(const struct vxlan_dev *vxlan,\n \t/* MDBA_MDB_EATTR_DST */\n \tnlmsg_size += nla_total_size(vxlan_addr_size(\u0026rd-\u003eremote_ip));\n \t/* MDBA_MDB_EATTR_DST_PORT */\n-\tif (rd-\u003eremote_port \u0026\u0026 rd-\u003eremote_port != vxlan-\u003ecfg.dst_port)\n+\tif (rd-\u003eremote_port \u0026\u0026 rd-\u003eremote_port != cfg-\u003edst_port)\n \t\tnlmsg_size += nla_total_size(sizeof(u16));\n \t/* MDBA_MDB_EATTR_VNI */\n-\tif (rd-\u003eremote_vni != vxlan-\u003edefault_dst.remote_vni)\n+\tif (rd-\u003eremote_vni != cfg-\u003evni)\n \t\tnlmsg_size += nla_total_size(sizeof(u32));\n \t/* MDBA_MDB_EATTR_IFINDEX */\n \tif (rd-\u003eremote_ifindex)\n \t\tnlmsg_size += nla_total_size(sizeof(u32));\n \t/* MDBA_MDB_EATTR_SRC_VNI */\n-\tif ((vxlan-\u003ecfg.flags \u0026 VXLAN_F_COLLECT_METADATA) \u0026\u0026 group-\u003evni)\n+\tif ((cfg-\u003eflags \u0026 VXLAN_F_COLLECT_METADATA) \u0026\u0026 group-\u003evni)\n \t\tnlmsg_size += nla_total_size(sizeof(u32));\n \n \treturn nlmsg_size;\n@@ -1216,7 +1221,7 @@ vxlan_mdb_entry_get(struct vxlan_dev *vxlan,\n \t\tgoto err_free_entry;\n \n \tif (hlist_is_singular_node(\u0026mdb_entry-\u003emdb_node, \u0026vxlan-\u003emdb_list))\n-\t\tvxlan-\u003ecfg.flags |= VXLAN_F_MDB;\n+\t\tset_bit(VXLAN_DEV_F_MDB, \u0026vxlan-\u003eflags);\n \n \treturn mdb_entry;\n \n@@ -1233,7 +1238,7 @@ static void vxlan_mdb_entry_put(struct vxlan_dev *vxlan,\n \t\treturn;\n \n \tif (hlist_is_singular_node(\u0026mdb_entry-\u003emdb_node, \u0026vxlan-\u003emdb_list))\n-\t\tvxlan-\u003ecfg.flags \u0026= ~VXLAN_F_MDB;\n+\t\tclear_bit(VXLAN_DEV_F_MDB, \u0026vxlan-\u003eflags);\n \n \trhashtable_remove_fast(\u0026vxlan-\u003emdb_tbl, \u0026mdb_entry-\u003erhnode,\n \t\t\t       vxlan_mdb_rht_params);\n@@ -1483,11 +1488,13 @@ static int vxlan_mdb_get_parse(struct net_device *dev, struct nlattr *tb[],\n {\n \tstruct br_mdb_entry *entry = nla_data(tb[MDBA_GET_ENTRY]);\n \tstruct nlattr *mdbe_attrs[MDBE_ATTR_MAX + 1];\n+\tconst struct vxlan_config *cfg;\n \tstruct vxlan_dev *vxlan = netdev_priv(dev);\n \tint err;\n \n+\tcfg = rtnl_dereference(vxlan-\u003ecfg);\n \tmemset(group, 0, sizeof(*group));\n-\tgroup-\u003evni = vxlan-\u003edefault_dst.remote_vni;\n+\tgroup-\u003evni = cfg-\u003evni;\n \n \tif (!tb[MDBA_GET_ENTRY_ATTRS]) {\n \t\tvxlan_mdb_group_set(group, entry, NULL);\n@@ -1621,6 +1628,7 @@ int vxlan_mdb_get(struct net_device *dev, struct nlattr *tb[], u32 portid,\n }\n \n struct vxlan_mdb_entry *vxlan_mdb_entry_skb_get(struct vxlan_dev *vxlan,\n+\t\t\t\t\t\tconst struct vxlan_config *cfg,\n \t\t\t\t\t\tstruct sk_buff *skb,\n \t\t\t\t\t\t__be32 src_vni)\n {\n@@ -1634,8 +1642,8 @@ struct vxlan_mdb_entry *vxlan_mdb_entry_skb_get(struct vxlan_dev *vxlan,\n \t/* When not in collect metadata mode, 'src_vni' is zero, but MDB\n \t * entries are stored with the VNI of the VXLAN device.\n \t */\n-\tif (!(vxlan-\u003ecfg.flags \u0026 VXLAN_F_COLLECT_METADATA))\n-\t\tsrc_vni = vxlan-\u003edefault_dst.remote_vni;\n+\tif (!(cfg-\u003eflags \u0026 VXLAN_F_COLLECT_METADATA))\n+\t\tsrc_vni = cfg-\u003evni;\n \n \tmemset(\u0026group, 0, sizeof(group));\n \tgroup.vni = src_vni;\n@@ -1700,6 +1708,7 @@ struct vxlan_mdb_entry *vxlan_mdb_entry_skb_get(struct vxlan_dev *vxlan,\n }\n \n netdev_tx_t vxlan_mdb_xmit(struct vxlan_dev *vxlan,\n+\t\t\t   const struct vxlan_config *cfg,\n \t\t\t   const struct vxlan_mdb_entry *mdb_entry,\n \t\t\t   struct sk_buff *skb)\n {\n@@ -1721,12 +1730,12 @@ netdev_tx_t vxlan_mdb_xmit(struct vxlan_dev *vxlan,\n \n \t\tskb1 = skb_clone(skb, GFP_ATOMIC);\n \t\tif (skb1)\n-\t\t\tvxlan_xmit_one(skb1, vxlan-\u003edev, src_vni,\n+\t\t\tvxlan_xmit_one(skb1, vxlan-\u003edev, cfg, src_vni,\n \t\t\t\t       rcu_dereference(remote-\u003erd), false);\n \t}\n \n \tif (fremote)\n-\t\tvxlan_xmit_one(skb, vxlan-\u003edev, src_vni,\n+\t\tvxlan_xmit_one(skb, vxlan-\u003edev, cfg, src_vni,\n \t\t\t       rcu_dereference(fremote-\u003erd), false);\n \telse\n \t\tkfree_skb_reason(skb, SKB_DROP_REASON_NO_TX_TARGET);\n@@ -1757,7 +1766,7 @@ void vxlan_mdb_fini(struct vxlan_dev *vxlan)\n \tstruct vxlan_mdb_flush_desc desc = {};\n \n \tvxlan_mdb_flush(vxlan, \u0026desc);\n-\tWARN_ON_ONCE(vxlan-\u003ecfg.flags \u0026 VXLAN_F_MDB);\n+\tWARN_ON_ONCE(test_bit(VXLAN_DEV_F_MDB, \u0026vxlan-\u003eflags));\n \trhashtable_free_and_destroy(\u0026vxlan-\u003emdb_tbl, vxlan_mdb_check_empty,\n \t\t\t\t    NULL);\n }\ndiff --git a/drivers/net/vxlan/vxlan_multicast.c b/drivers/net/vxlan/vxlan_multicast.c\nindex 3b75b48dc726d..ae0320921c8ca 100644\n--- a/drivers/net/vxlan/vxlan_multicast.c\n+++ b/drivers/net/vxlan/vxlan_multicast.c\n@@ -14,11 +14,12 @@\n /* Update multicast group membership when first VNI on\n  * multicast address is brought up\n  */\n-int vxlan_igmp_join(struct vxlan_dev *vxlan, union vxlan_addr *rip,\n+int vxlan_igmp_join(struct vxlan_dev *vxlan, const union vxlan_addr *rip,\n \t\t    int rifindex)\n {\n-\tunion vxlan_addr *ip = (rip ? : \u0026vxlan-\u003edefault_dst.remote_ip);\n-\tint ifindex = (rifindex ? : vxlan-\u003edefault_dst.remote_ifindex);\n+\tconst struct vxlan_config *cfg = rtnl_dereference(vxlan-\u003ecfg);\n+\tconst union vxlan_addr *ip = (rip ? : \u0026cfg-\u003eremote_ip);\n+\tint ifindex = (rifindex ? : cfg-\u003eremote_ifindex);\n \tint ret = -EINVAL;\n \tstruct sock *sk;\n \n@@ -47,11 +48,12 @@ int vxlan_igmp_join(struct vxlan_dev *vxlan, union vxlan_addr *rip,\n \treturn ret;\n }\n \n-int vxlan_igmp_leave(struct vxlan_dev *vxlan, union vxlan_addr *rip,\n+int vxlan_igmp_leave(struct vxlan_dev *vxlan, const union vxlan_addr *rip,\n \t\t     int rifindex)\n {\n-\tunion vxlan_addr *ip = (rip ? : \u0026vxlan-\u003edefault_dst.remote_ip);\n-\tint ifindex = (rifindex ? : vxlan-\u003edefault_dst.remote_ifindex);\n+\tconst struct vxlan_config *cfg = rtnl_dereference(vxlan-\u003ecfg);\n+\tconst union vxlan_addr *ip = (rip ? : \u0026cfg-\u003eremote_ip);\n+\tint ifindex = (rifindex ? : cfg-\u003eremote_ifindex);\n \tint ret = -EINVAL;\n \tstruct sock *sk;\n \n@@ -80,8 +82,8 @@ int vxlan_igmp_leave(struct vxlan_dev *vxlan, union vxlan_addr *rip,\n \treturn ret;\n }\n \n-static bool vxlan_group_used_match(union vxlan_addr *ip, int ifindex,\n-\t\t\t\t   union vxlan_addr *rip, int rifindex)\n+static bool vxlan_group_used_match(const union vxlan_addr *ip, int ifindex,\n+\t\t\t\t   const union vxlan_addr *rip, int rifindex)\n {\n \tif (!vxlan_addr_multicast(rip))\n \t\treturn false;\n@@ -96,14 +98,16 @@ static bool vxlan_group_used_match(union vxlan_addr *ip, int ifindex,\n }\n \n static bool vxlan_group_used_by_vnifilter(struct vxlan_dev *vxlan,\n-\t\t\t\t\t  union vxlan_addr *ip, int ifindex)\n+\t\t\t\t\t  const struct vxlan_config *cfg,\n+\t\t\t\t\t  const union vxlan_addr *ip,\n+\t\t\t\t\t  int ifindex)\n {\n \tstruct vxlan_vni_group *vg = rtnl_dereference(vxlan-\u003evnigrp);\n \tstruct vxlan_vni_node *v, *tmp;\n \n \tif (vxlan_group_used_match(ip, ifindex,\n-\t\t\t\t   \u0026vxlan-\u003edefault_dst.remote_ip,\n-\t\t\t\t   vxlan-\u003edefault_dst.remote_ifindex))\n+\t\t\t\t   \u0026cfg-\u003eremote_ip,\n+\t\t\t\t   cfg-\u003eremote_ifindex))\n \t\treturn true;\n \n \tlist_for_each_entry_safe(v, tmp, \u0026vg-\u003evni_list, vlist) {\n@@ -112,7 +116,7 @@ static bool vxlan_group_used_by_vnifilter(struct vxlan_dev *vxlan,\n \n \t\tif (vxlan_group_used_match(ip, ifindex,\n \t\t\t\t\t   \u0026v-\u003eremote_ip,\n-\t\t\t\t\t   vxlan-\u003edefault_dst.remote_ifindex))\n+\t\t\t\t\t   cfg-\u003eremote_ifindex))\n \t\t\treturn true;\n \t}\n \n@@ -121,16 +125,17 @@ static bool vxlan_group_used_by_vnifilter(struct vxlan_dev *vxlan,\n \n /* See if multicast group is already in use by other ID */\n bool vxlan_group_used(struct vxlan_net *vn, struct vxlan_dev *dev,\n-\t\t      __be32 vni, union vxlan_addr *rip, int rifindex)\n+\t\t      __be32 vni, const union vxlan_addr *rip, int rifindex)\n {\n-\tunion vxlan_addr *ip = (rip ? : \u0026dev-\u003edefault_dst.remote_ip);\n-\tint ifindex = (rifindex ? : dev-\u003edefault_dst.remote_ifindex);\n+\tconst struct vxlan_config *dev_cfg = rtnl_dereference(dev-\u003ecfg);\n+\tconst union vxlan_addr *ip = (rip ? : \u0026dev_cfg-\u003eremote_ip);\n+\tint ifindex = (rifindex ? : dev_cfg-\u003eremote_ifindex);\n \tstruct vxlan_dev *vxlan;\n \tstruct vxlan_sock *sock4;\n #if IS_ENABLED(CONFIG_IPV6)\n \tstruct vxlan_sock *sock6;\n #endif\n-\tunsigned short family = dev-\u003edefault_dst.remote_ip.sa.sa_family;\n+\tunsigned short family = dev_cfg-\u003eremote_ip.sa.sa_family;\n \n \tsock4 = rtnl_dereference(dev-\u003evn4_sock);\n \n@@ -147,24 +152,28 @@ bool vxlan_group_used(struct vxlan_net *vn, struct vxlan_dev *dev,\n #endif\n \n \tlist_for_each_entry(vxlan, \u0026vn-\u003evxlan_list, next) {\n+\t\tconst struct vxlan_config *cfg;\n+\n \t\tif (!netif_running(vxlan-\u003edev) || vxlan == dev)\n \t\t\tcontinue;\n \n \t\tif (family == AF_INET \u0026\u0026\n-\t\t    rtnl_dereference(vxlan-\u003evn4_sock) != sock4)\n+\t\t\trtnl_dereference(vxlan-\u003evn4_sock) != sock4)\n \t\t\tcontinue;\n #if IS_ENABLED(CONFIG_IPV6)\n \t\tif (family == AF_INET6 \u0026\u0026\n-\t\t    rtnl_dereference(vxlan-\u003evn6_sock) != sock6)\n+\t\t\trtnl_dereference(vxlan-\u003evn6_sock) != sock6)\n \t\t\tcontinue;\n #endif\n-\t\tif (vxlan-\u003ecfg.flags \u0026 VXLAN_F_VNIFILTER) {\n-\t\t\tif (!vxlan_group_used_by_vnifilter(vxlan, ip, ifindex))\n+\t\tcfg = rtnl_dereference(vxlan-\u003ecfg);\n+\n+\t\tif (cfg-\u003eflags \u0026 VXLAN_F_VNIFILTER) {\n+\t\t\tif (!vxlan_group_used_by_vnifilter(vxlan, cfg, ip, ifindex))\n \t\t\t\tcontinue;\n \t\t} else {\n \t\t\tif (!vxlan_group_used_match(ip, ifindex,\n-\t\t\t\t\t\t    \u0026vxlan-\u003edefault_dst.remote_ip,\n-\t\t\t\t\t\t    vxlan-\u003edefault_dst.remote_ifindex))\n+\t\t\t\t\t\t    \u0026cfg-\u003eremote_ip,\n+\t\t\t\t\t\t    cfg-\u003eremote_ifindex))\n \t\t\t\tcontinue;\n \t\t}\n \n@@ -174,7 +183,8 @@ bool vxlan_group_used(struct vxlan_net *vn, struct vxlan_dev *dev,\n \treturn false;\n }\n \n-static int vxlan_multicast_join_vnigrp(struct vxlan_dev *vxlan)\n+static int vxlan_multicast_join_vnigrp(struct vxlan_dev *vxlan,\n+\t\t\t\t       const struct vxlan_config *cfg)\n {\n \tstruct vxlan_vni_group *vg = rtnl_dereference(vxlan-\u003evnigrp);\n \tstruct vxlan_vni_node *v, *tmp, *vgood = NULL;\n@@ -185,7 +195,7 @@ static int vxlan_multicast_join_vnigrp(struct vxlan_dev *vxlan)\n \t\t\tcontinue;\n \t\t/* skip if address is same as default address */\n \t\tif (vxlan_addr_equal(\u0026v-\u003eremote_ip,\n-\t\t\t\t     \u0026vxlan-\u003edefault_dst.remote_ip))\n+\t\t\t\t     \u0026cfg-\u003eremote_ip))\n \t\t\tcontinue;\n \t\tret = vxlan_igmp_join(vxlan, \u0026v-\u003eremote_ip, 0);\n \t\tif (ret == -EADDRINUSE)\n@@ -200,7 +210,7 @@ static int vxlan_multicast_join_vnigrp(struct vxlan_dev *vxlan)\n \t\t\tif (!vxlan_addr_multicast(\u0026v-\u003eremote_ip))\n \t\t\t\tcontinue;\n \t\t\tif (vxlan_addr_equal(\u0026v-\u003eremote_ip,\n-\t\t\t\t\t     \u0026vxlan-\u003edefault_dst.remote_ip))\n+\t\t\t\t\t     \u0026cfg-\u003eremote_ip))\n \t\t\t\tcontinue;\n \t\t\tvxlan_igmp_leave(vxlan, \u0026v-\u003eremote_ip, 0);\n \t\t\tif (v == vgood)\n@@ -233,37 +243,39 @@ static int vxlan_multicast_leave_vnigrp(struct vxlan_dev *vxlan)\n \n int vxlan_multicast_join(struct vxlan_dev *vxlan)\n {\n+\tconst struct vxlan_config *cfg = rtnl_dereference(vxlan-\u003ecfg);\n \tint ret = 0;\n \n-\tif (vxlan_addr_multicast(\u0026vxlan-\u003edefault_dst.remote_ip)) {\n-\t\tret = vxlan_igmp_join(vxlan, \u0026vxlan-\u003edefault_dst.remote_ip,\n-\t\t\t\t      vxlan-\u003edefault_dst.remote_ifindex);\n+\tif (vxlan_addr_multicast(\u0026cfg-\u003eremote_ip)) {\n+\t\tret = vxlan_igmp_join(vxlan, \u0026cfg-\u003eremote_ip,\n+\t\t\t\t      cfg-\u003eremote_ifindex);\n \t\tif (ret == -EADDRINUSE)\n \t\t\tret = 0;\n \t\tif (ret)\n \t\t\treturn ret;\n \t}\n \n-\tif (vxlan-\u003ecfg.flags \u0026 VXLAN_F_VNIFILTER)\n-\t\treturn vxlan_multicast_join_vnigrp(vxlan);\n+\tif (cfg-\u003eflags \u0026 VXLAN_F_VNIFILTER)\n+\t\treturn vxlan_multicast_join_vnigrp(vxlan, cfg);\n \n \treturn 0;\n }\n \n int vxlan_multicast_leave(struct vxlan_dev *vxlan)\n {\n+\tconst struct vxlan_config *cfg = rtnl_dereference(vxlan-\u003ecfg);\n \tstruct vxlan_net *vn = net_generic(vxlan-\u003enet, vxlan_net_id);\n \tint ret = 0;\n \n-\tif (vxlan_addr_multicast(\u0026vxlan-\u003edefault_dst.remote_ip) \u0026\u0026\n+\tif (vxlan_addr_multicast(\u0026cfg-\u003eremote_ip) \u0026\u0026\n \t    !vxlan_group_used(vn, vxlan, 0, NULL, 0)) {\n-\t\tret = vxlan_igmp_leave(vxlan, \u0026vxlan-\u003edefault_dst.remote_ip,\n-\t\t\t\t       vxlan-\u003edefault_dst.remote_ifindex);\n+\t\tret = vxlan_igmp_leave(vxlan, \u0026cfg-\u003eremote_ip,\n+\t\t\t\t       cfg-\u003eremote_ifindex);\n \t\tif (ret)\n \t\t\treturn ret;\n \t}\n \n-\tif (vxlan-\u003ecfg.flags \u0026 VXLAN_F_VNIFILTER)\n+\tif (cfg-\u003eflags \u0026 VXLAN_F_VNIFILTER)\n \t\treturn vxlan_multicast_leave_vnigrp(vxlan);\n \n \treturn 0;\ndiff --git a/drivers/net/vxlan/vxlan_private.h b/drivers/net/vxlan/vxlan_private.h\nindex b1eec22163608..4f70afc502c90 100644\n--- a/drivers/net/vxlan/vxlan_private.h\n+++ b/drivers/net/vxlan/vxlan_private.h\n@@ -195,9 +195,10 @@ int vxlan_fdb_update(struct vxlan_dev *vxlan,\n \t\t     __u32 ifindex, __u16 ndm_flags, u32 nhid,\n \t\t     bool swdev_notify, struct netlink_ext_ack *extack);\n void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,\n+\t\t    const struct vxlan_config *cfg,\n \t\t    __be32 default_vni, struct vxlan_rdst *rdst, bool did_rsc);\n int vxlan_vni_in_use(struct net *src_net, struct vxlan_dev *vxlan,\n-\t\t     struct vxlan_config *conf, __be32 vni);\n+\t\t     const struct vxlan_config *conf, __be32 vni);\n \n /* vxlan_vnifilter.c */\n int vxlan_vnigroup_init(struct vxlan_dev *vxlan);\n@@ -205,7 +206,8 @@ void vxlan_vnigroup_uninit(struct vxlan_dev *vxlan);\n \n int vxlan_vnifilter_init(void);\n void vxlan_vnifilter_uninit(void);\n-void vxlan_vnifilter_count(struct vxlan_dev *vxlan, __be32 vni,\n+void vxlan_vnifilter_count(struct vxlan_dev *vxlan,\n+\t\t\t   const struct vxlan_config *cfg, __be32 vni,\n \t\t\t   struct vxlan_vni_node *vninode,\n \t\t\t   int type, unsigned int len);\n \n@@ -214,8 +216,8 @@ void vxlan_vs_add_vnigrp(struct vxlan_dev *vxlan,\n \t\t\t bool ipv6);\n void vxlan_vs_del_vnigrp(struct vxlan_dev *vxlan);\n int vxlan_vnilist_update_group(struct vxlan_dev *vxlan,\n-\t\t\t       union vxlan_addr *old_remote_ip,\n-\t\t\t       union vxlan_addr *new_remote_ip,\n+\t\t\t       const union vxlan_addr *old_remote_ip,\n+\t\t\t       const union vxlan_addr *new_remote_ip,\n \t\t\t       struct netlink_ext_ack *extack);\n \n \n@@ -223,10 +225,10 @@ int vxlan_vnilist_update_group(struct vxlan_dev *vxlan,\n int vxlan_multicast_join(struct vxlan_dev *vxlan);\n int vxlan_multicast_leave(struct vxlan_dev *vxlan);\n bool vxlan_group_used(struct vxlan_net *vn, struct vxlan_dev *dev,\n-\t\t      __be32 vni, union vxlan_addr *rip, int rifindex);\n-int vxlan_igmp_join(struct vxlan_dev *vxlan, union vxlan_addr *rip,\n+\t\t      __be32 vni, const union vxlan_addr *rip, int rifindex);\n+int vxlan_igmp_join(struct vxlan_dev *vxlan, const union vxlan_addr *rip,\n \t\t    int rifindex);\n-int vxlan_igmp_leave(struct vxlan_dev *vxlan, union vxlan_addr *rip,\n+int vxlan_igmp_leave(struct vxlan_dev *vxlan, const union vxlan_addr *rip,\n \t\t     int rifindex);\n \n /* vxlan_mdb.c */\n@@ -241,9 +243,11 @@ int vxlan_mdb_del_bulk(struct net_device *dev, struct nlattr *tb[],\n int vxlan_mdb_get(struct net_device *dev, struct nlattr *tb[], u32 portid,\n \t\t  u32 seq, struct netlink_ext_ack *extack);\n struct vxlan_mdb_entry *vxlan_mdb_entry_skb_get(struct vxlan_dev *vxlan,\n+\t\t\t\t\t\tconst struct vxlan_config *cfg,\n \t\t\t\t\t\tstruct sk_buff *skb,\n \t\t\t\t\t\t__be32 src_vni);\n netdev_tx_t vxlan_mdb_xmit(struct vxlan_dev *vxlan,\n+\t\t\t   const struct vxlan_config *cfg,\n \t\t\t   const struct vxlan_mdb_entry *mdb_entry,\n \t\t\t   struct sk_buff *skb);\n int vxlan_mdb_init(struct vxlan_dev *vxlan);\ndiff --git a/drivers/net/vxlan/vxlan_vnifilter.c b/drivers/net/vxlan/vxlan_vnifilter.c\nindex dd94085e08865..e6c5fa2301347 100644\n--- a/drivers/net/vxlan/vxlan_vnifilter.c\n+++ b/drivers/net/vxlan/vxlan_vnifilter.c\n@@ -171,13 +171,14 @@ static void vxlan_vnifilter_stats_add(struct vxlan_vni_node *vninode,\n \tu64_stats_update_end(\u0026pstats-\u003esyncp);\n }\n \n-void vxlan_vnifilter_count(struct vxlan_dev *vxlan, __be32 vni,\n+void vxlan_vnifilter_count(struct vxlan_dev *vxlan,\n+\t\t\t   const struct vxlan_config *cfg, __be32 vni,\n \t\t\t   struct vxlan_vni_node *vninode,\n \t\t\t   int type, unsigned int len)\n {\n \tstruct vxlan_vni_node *vnode;\n \n-\tif (!(vxlan-\u003ecfg.flags \u0026 VXLAN_F_VNIFILTER))\n+\tif (!(cfg-\u003eflags \u0026 VXLAN_F_VNIFILTER))\n \t\treturn;\n \n \tif (vninode) {\n@@ -333,22 +334,28 @@ static int vxlan_vnifilter_dump_dev(const struct net_device *dev,\n \t\t\t\t    struct sk_buff *skb,\n \t\t\t\t    struct netlink_callback *cb)\n {\n-\tstruct vxlan_vni_node *tmp, *v, *vbegin = NULL, *vend = NULL;\n+\tstruct vxlan_vni_node *v, *vbegin = NULL, *vend = NULL;\n \tstruct vxlan_dev *vxlan = netdev_priv(dev);\n \tstruct tunnel_msg *new_tmsg, *tmsg;\n+\tconst struct vxlan_config *cfg;\n \tint idx = 0, s_idx = cb-\u003eargs[1];\n \tstruct vxlan_vni_group *vg;\n \tstruct nlmsghdr *nlh;\n \tbool dump_stats;\n \tint err = 0;\n \n-\tif (!(vxlan-\u003ecfg.flags \u0026 VXLAN_F_VNIFILTER))\n+\tcfg = rcu_dereference(vxlan-\u003ecfg);\n+\tif (!(cfg-\u003eflags \u0026 VXLAN_F_VNIFILTER)) {\n+\t\tcb-\u003eargs[1] = 0;\n \t\treturn -EINVAL;\n+\t}\n \n \t/* RCU needed because of the vni locking rules (rcu || rtnl) */\n \tvg = rcu_dereference(vxlan-\u003evnigrp);\n-\tif (!vg || !vg-\u003enum_vnis)\n+\tif (!vg || !vg-\u003enum_vnis) {\n+\t\tcb-\u003eargs[1] = 0;\n \t\treturn 0;\n+\t}\n \n \ttmsg = nlmsg_data(cb-\u003enlh);\n \tdump_stats = !!(tmsg-\u003eflags \u0026 TUNNEL_MSG_FLAG_STATS);\n@@ -362,7 +369,7 @@ static int vxlan_vnifilter_dump_dev(const struct net_device *dev,\n \tnew_tmsg-\u003efamily = PF_BRIDGE;\n \tnew_tmsg-\u003eifindex = dev-\u003eifindex;\n \n-\tlist_for_each_entry_safe(v, tmp, \u0026vg-\u003evni_list, vlist) {\n+\tlist_for_each_entry_rcu(v, \u0026vg-\u003evni_list, vlist) {\n \t\tif (idx \u003c s_idx) {\n \t\t\tidx++;\n \t\t\tcontinue;\n@@ -471,23 +478,25 @@ static const struct nla_policy vni_filter_policy[VXLAN_VNIFILTER_MAX + 1] = {\n };\n \n static int vxlan_update_default_fdb_entry(struct vxlan_dev *vxlan, __be32 vni,\n-\t\t\t\t\t  union vxlan_addr *old_remote_ip,\n-\t\t\t\t\t  union vxlan_addr *remote_ip,\n+\t\t\t\t\t  const union vxlan_addr *old_remote_ip,\n+\t\t\t\t\t  const union vxlan_addr *remote_ip,\n \t\t\t\t\t  struct netlink_ext_ack *extack)\n {\n-\tstruct vxlan_rdst *dst = \u0026vxlan-\u003edefault_dst;\n+\tconst struct vxlan_config *cfg = rtnl_dereference(vxlan-\u003ecfg);\n \tint err = 0;\n \n \tspin_lock_bh(\u0026vxlan-\u003ehash_lock);\n \tif (remote_ip \u0026\u0026 !vxlan_addr_any(remote_ip)) {\n+\t\tunion vxlan_addr rip = *remote_ip;\n+\n \t\terr = vxlan_fdb_update(vxlan, all_zeros_mac,\n-\t\t\t\t       remote_ip,\n+\t\t\t\t       \u0026rip,\n \t\t\t\t       NUD_REACHABLE | NUD_PERMANENT,\n \t\t\t\t       NLM_F_APPEND | NLM_F_CREATE,\n-\t\t\t\t       vxlan-\u003ecfg.dst_port,\n+\t\t\t\t       cfg-\u003edst_port,\n \t\t\t\t       vni,\n \t\t\t\t       vni,\n-\t\t\t\t       dst-\u003eremote_ifindex,\n+\t\t\t\t       cfg-\u003eremote_ifindex,\n \t\t\t\t       NTF_SELF, 0, true, extack);\n \t\tif (err) {\n \t\t\tspin_unlock_bh(\u0026vxlan-\u003ehash_lock);\n@@ -498,9 +507,9 @@ static int vxlan_update_default_fdb_entry(struct vxlan_dev *vxlan, __be32 vni,\n \tif (old_remote_ip \u0026\u0026 !vxlan_addr_any(old_remote_ip)) {\n \t\t__vxlan_fdb_delete(vxlan, all_zeros_mac,\n \t\t\t\t   *old_remote_ip,\n-\t\t\t\t   vxlan-\u003ecfg.dst_port,\n+\t\t\t\t   cfg-\u003edst_port,\n \t\t\t\t   vni, vni,\n-\t\t\t\t   dst-\u003eremote_ifindex,\n+\t\t\t\t   cfg-\u003eremote_ifindex,\n \t\t\t\t   true);\n \t}\n \tspin_unlock_bh(\u0026vxlan-\u003ehash_lock);\n@@ -515,8 +524,8 @@ static int vxlan_vni_update_group(struct vxlan_dev *vxlan,\n \t\t\t\t  struct netlink_ext_ack *extack)\n {\n \tstruct vxlan_net *vn = net_generic(vxlan-\u003enet, vxlan_net_id);\n-\tstruct vxlan_rdst *dst = \u0026vxlan-\u003edefault_dst;\n-\tunion vxlan_addr *newrip = NULL, *oldrip = NULL;\n+\tconst struct vxlan_config *cfg = rtnl_dereference(vxlan-\u003ecfg);\n+\tconst union vxlan_addr *newrip = NULL, *oldrip = NULL;\n \tunion vxlan_addr old_remote_ip;\n \tint ret = 0;\n \n@@ -528,8 +537,8 @@ static int vxlan_vni_update_group(struct vxlan_dev *vxlan,\n \tif (group \u0026\u0026 !vxlan_addr_any(group)) {\n \t\tnewrip = group;\n \t} else {\n-\t\tif (!vxlan_addr_any(\u0026dst-\u003eremote_ip))\n-\t\t\tnewrip = \u0026dst-\u003eremote_ip;\n+\t\tif (!vxlan_addr_any(\u0026cfg-\u003eremote_ip))\n+\t\t\tnewrip = \u0026cfg-\u003eremote_ip;\n \t}\n \n \t/* if old rip exists, and no newrip,\n@@ -557,7 +566,7 @@ static int vxlan_vni_update_group(struct vxlan_dev *vxlan,\n \t\tif (vxlan_addr_multicast(\u0026old_remote_ip) \u0026\u0026\n \t\t    !vxlan_group_used(vn, vxlan, vninode-\u003evni,\n \t\t\t\t      \u0026old_remote_ip,\n-\t\t\t\t      vxlan-\u003edefault_dst.remote_ifindex)) {\n+\t\t\t\t      cfg-\u003eremote_ifindex)) {\n \t\t\tret = vxlan_igmp_leave(vxlan, \u0026old_remote_ip,\n \t\t\t\t\t       0);\n \t\t\tif (ret)\n@@ -581,8 +590,8 @@ static int vxlan_vni_update_group(struct vxlan_dev *vxlan,\n }\n \n int vxlan_vnilist_update_group(struct vxlan_dev *vxlan,\n-\t\t\t       union vxlan_addr *old_remote_ip,\n-\t\t\t       union vxlan_addr *new_remote_ip,\n+\t\t\t       const union vxlan_addr *old_remote_ip,\n+\t\t\t       const union vxlan_addr *new_remote_ip,\n \t\t\t       struct netlink_ext_ack *extack)\n {\n \tstruct list_head *headp, *hpos;\n@@ -612,20 +621,20 @@ static void vxlan_vni_delete_group(struct vxlan_dev *vxlan,\n \t\t\t\t   struct vxlan_vni_node *vninode)\n {\n \tstruct vxlan_net *vn = net_generic(vxlan-\u003enet, vxlan_net_id);\n-\tstruct vxlan_rdst *dst = \u0026vxlan-\u003edefault_dst;\n+\tconst struct vxlan_config *cfg = rtnl_dereference(vxlan-\u003ecfg);\n \n \t/* if per vni remote_ip not present, delete the\n \t * default dst remote_ip previously added for this vni\n \t */\n \tif (!vxlan_addr_any(\u0026vninode-\u003eremote_ip) ||\n-\t    !vxlan_addr_any(\u0026dst-\u003eremote_ip)) {\n+\t    !vxlan_addr_any(\u0026cfg-\u003eremote_ip)) {\n \t\tspin_lock_bh(\u0026vxlan-\u003ehash_lock);\n \t\t__vxlan_fdb_delete(vxlan, all_zeros_mac,\n \t\t\t\t   (vxlan_addr_any(\u0026vninode-\u003eremote_ip) ?\n-\t\t\t\t   dst-\u003eremote_ip : vninode-\u003eremote_ip),\n-\t\t\t\t   vxlan-\u003ecfg.dst_port,\n+\t\t\t\t   cfg-\u003eremote_ip : vninode-\u003eremote_ip),\n+\t\t\t\t   cfg-\u003edst_port,\n \t\t\t\t   vninode-\u003evni, vninode-\u003evni,\n-\t\t\t\t   dst-\u003eremote_ifindex,\n+\t\t\t\t   cfg-\u003eremote_ifindex,\n \t\t\t\t   true);\n \t\tspin_unlock_bh(\u0026vxlan-\u003ehash_lock);\n \t}\n@@ -634,7 +643,7 @@ static void vxlan_vni_delete_group(struct vxlan_dev *vxlan,\n \t\tif (vxlan_addr_multicast(\u0026vninode-\u003eremote_ip) \u0026\u0026\n \t\t    !vxlan_group_used(vn, vxlan, vninode-\u003evni,\n \t\t\t\t      \u0026vninode-\u003eremote_ip,\n-\t\t\t\t      dst-\u003eremote_ifindex)) {\n+\t\t\t\t      cfg-\u003eremote_ifindex)) {\n \t\t\tvxlan_igmp_leave(vxlan, \u0026vninode-\u003eremote_ip, 0);\n \t\t}\n \t}\n@@ -723,6 +732,7 @@ static int vxlan_vni_add(struct vxlan_dev *vxlan,\n \t\t\t u32 vni, union vxlan_addr *group,\n \t\t\t struct netlink_ext_ack *extack)\n {\n+\tconst struct vxlan_config *cfg = rtnl_dereference(vxlan-\u003ecfg);\n \tstruct vxlan_vni_node *vninode;\n \t__be32 v = cpu_to_be32(vni);\n \tbool changed = false;\n@@ -731,7 +741,7 @@ static int vxlan_vni_add(struct vxlan_dev *vxlan,\n \tif (vxlan_vnifilter_lookup(vxlan, v))\n \t\treturn vxlan_vni_update(vxlan, vg, v, group, \u0026changed, extack);\n \n-\terr = vxlan_vni_in_use(vxlan-\u003enet, vxlan, \u0026vxlan-\u003ecfg, v);\n+\terr = vxlan_vni_in_use(vxlan-\u003enet, vxlan, cfg, v);\n \tif (err) {\n \t\tNL_SET_ERR_MSG(extack, \"VNI in use\");\n \t\treturn err;\n@@ -844,6 +854,7 @@ static int vxlan_process_vni_filter(struct vxlan_dev *vxlan,\n \t\t\t\t    int cmd, struct netlink_ext_ack *extack)\n {\n \tstruct nlattr *vattrs[VXLAN_VNIFILTER_ENTRY_MAX + 1];\n+\tconst struct vxlan_config *cfg;\n \tu32 vni_start = 0, vni_end = 0;\n \tunion vxlan_addr group;\n \tint err;\n@@ -881,7 +892,8 @@ static int vxlan_process_vni_filter(struct vxlan_dev *vxlan,\n \t\tmemset(\u0026group, 0, sizeof(group));\n \t}\n \n-\tif (vxlan_addr_multicast(\u0026group) \u0026\u0026 !vxlan-\u003edefault_dst.remote_ifindex) {\n+\tcfg = rtnl_dereference(vxlan-\u003ecfg);\n+\tif (vxlan_addr_multicast(\u0026group) \u0026\u0026 !cfg-\u003eremote_ifindex) {\n \t\tNL_SET_ERR_MSG(extack,\n \t\t\t       \"Local interface required for multicast remote group\");\n \n@@ -940,6 +952,7 @@ static int vxlan_vnifilter_process(struct sk_buff *skb, struct nlmsghdr *nlh,\n \t\t\t\t   struct netlink_ext_ack *extack)\n {\n \tstruct net *net = sock_net(skb-\u003esk);\n+\tconst struct vxlan_config *cfg;\n \tstruct tunnel_msg *tmsg;\n \tstruct vxlan_dev *vxlan;\n \tstruct net_device *dev;\n@@ -964,8 +977,9 @@ static int vxlan_vnifilter_process(struct sk_buff *skb, struct nlmsghdr *nlh,\n \t}\n \n \tvxlan = netdev_priv(dev);\n+\tcfg = rtnl_dereference(vxlan-\u003ecfg);\n \n-\tif (!(vxlan-\u003ecfg.flags \u0026 VXLAN_F_VNIFILTER))\n+\tif (!(cfg-\u003eflags \u0026 VXLAN_F_VNIFILTER))\n \t\treturn -EOPNOTSUPP;\n \n \tnlmsg_for_each_attr_type(attr, VXLAN_VNIFILTER_ENTRY, nlh,\ndiff --git a/include/net/vxlan.h b/include/net/vxlan.h\nindex 7b82075055237..f90d43a9284c1 100644\n--- a/include/net/vxlan.h\n+++ b/include/net/vxlan.h\n@@ -229,6 +229,7 @@ struct vxlan_config {\n \tbool\t\t\t\tno_share;\n \tenum ifla_vxlan_df\t\tdf;\n \tstruct vxlanhdr\t\t\treserved_bits;\n+\tstruct rcu_head\t\t\trcu;\n };\n \n enum {\n@@ -294,14 +295,15 @@ struct vxlan_dev {\n #endif\n \tstruct net_device *dev;\n \tstruct net\t  *net;\t\t/* netns for packet i/o */\n-\tstruct vxlan_rdst default_dst;\t/* default destination */\n+\tstruct net_device *lowerdev;\n \n \tstruct timer_list age_timer;\n \tspinlock_t\t  hash_lock;\n \tunsigned int\t  addrcnt;\n \tstruct gro_cells  gro_cells;\n+\tunsigned long\t  flags;\n \n-\tstruct vxlan_config\tcfg;\n+\tstruct vxlan_config __rcu\t*cfg;\n \n \tstruct vxlan_vni_group  __rcu *vnigrp;\n \n@@ -313,6 +315,10 @@ struct vxlan_dev {\n \tunsigned int mdb_seq;\n };\n \n+enum vxlan_dev_flags {\n+\tVXLAN_DEV_F_MDB,\n+};\n+\n #define VXLAN_F_LEARN\t\t\t0x01\n #define VXLAN_F_PROXY\t\t\t0x02\n #define VXLAN_F_RSC\t\t\t0x04\n@@ -331,7 +337,6 @@ struct vxlan_dev {\n #define VXLAN_F_IPV6_LINKLOCAL\t\t0x8000\n #define VXLAN_F_TTL_INHERIT\t\t0x10000\n #define VXLAN_F_VNIFILTER               0x20000\n-#define VXLAN_F_MDB\t\t\t0x40000\n #define VXLAN_F_LOCALBYPASS\t\t0x80000\n #define VXLAN_F_MC_ROUTE\t\t0x100000\n \n"
}

2/1 2026/09/07 02:23 agent patch-evaluator
0m Model:
core

Args:
null

Results:
{
  "EnableConfigs": [
    "VXLAN",
    "INET"
  ],
  "FocusSymbols": [
    "vxlan_xmit",
    "vxlan_rcv",
    "vxlan_changelink",
    "vxlan_dev_configure",
    "vxlan_fdb_add",
    "vxlan_fdb_get",
    "vxlan_fill_info",
    "vxlan_mdb_xmit",
    "vxlan_mdb_get",
    "vxlan_vnifilter_process",
    "vxlan_multicast_join",
    "vxlan_multicast_leave"
  ],
  "Reasoning": "The patch refactors the VXLAN driver to manage struct vxlan_config dynamically via RCU pointers (rcu_assign_pointer / kfree_rcu) and updates RCU/RTNL dereferencing across both control path (changelink, FDB, MDB, vnifilter) and data path (xmit, rcv, encap bypass). VXLAN devices are virtual network interfaces fully reachable without dedicated hardware in standard QEMU/syzkaller environments. Fuzzing is warranted to test for potential NULL dereferences, RCU race conditions, and use-after-free bugs.",
  "WorthFuzzing": true
}

Instruction:
You are an expert Linux kernel maintainer and security engineer.
Your job is to review a provided patch series and evaluate whether it warrants fuzzing with syzkaller.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on internal assumptions. You must actively use your code access
tools to inspect the actual source code, callers, and surrounding context.

================================================================================
1. CORE TRIAGE PHILOSOPHY
================================================================================
The goal of patch fuzzing is to discover crashes, regressions, exposed latent bugs,
and newly triggered assertions introduced by the patch series.

- REACHABILITY IS THE PRIMARY GATE:
  Fuzzing can only discover bugs in code that can actually execute in standard virtualized
  environments (GCE or QEMU, utilizing software-emulated devices like USB gadgets, netdev, tun/tap).
  If the modified code is structurally unreachable (see Section 2), it MUST NOT be fuzzed,
  regardless of whether it adds assertions or complex logic.

- DO NOT BLINDLY TRUST "NO FUNCTIONAL CHANGE" (NFCI) OR "REFACTORING" CLAIMS:
  Patch authors routinely label changes as "cleanups", "refactorings", or state
  "No functional change intended". Do NOT take these claims at face value.
  Code refactorings that rearrange logic, introduce helper functions, or alter state management
  in core subsystems frequently introduce subtle semantic shifts or uncover latent kernel bugs.
  If reachable executable code is modified or refactored, it MUST be fuzzed.

- NEW OR MODIFIED ASSERTIONS IN REACHABLE CODE MUST BE FUZZED:
  When a patch introduces or modifies runtime checks or assertions (e.g., WARN_ON*, VM_WARN_ON*,
  BUG_ON*, lockdep_assert*) in reachable code paths, it enforces new or stricter invariants.
  Even if the author believes the invariant always holds, fuzzing is essential to verify whether
  an unusual sequence of operations can violate it.

================================================================================
2. WHEN TO RETURN WorthFuzzing=false (NEGATIVE CRITERIA)
================================================================================
Return WorthFuzzing=false ONLY IF all modified code falls strictly into one or more of these categories:

- Non-kernel and non-executable changes:
  * Modifications to Documentation/, comments, or spelling fixes.
  * User-space directories, self-tests, samples, or scripts (e.g., tools/, samples/, scripts/, usr/)
    that do not affect the compiled kernel image (vmlinux) or kernel modules.
  * Purely decorative logging (e.g., message strings in pr_err, printk, dev_info) or tracepoints
    that do not alter control flow or data structures.
  * Build system or Kconfig changes that do not alter compiled C logic.
- Structurally unreachable hardware:
  * Vendor-specific PCIe switches, SmartNICs, or GPU drivers (e.g., mlxsw, pds_core, qed,
    ionic, amdgpu) requiring physical ASIC/PCIe cards not emulated in standard QEMU.
- Unreachable execution paths:
  * Driver teardown callbacks (.remove, .shutdown, pci_unregister_driver) executed only during
    physical PCI hot-unplug or manual sysfs driver unbinding.
  * Code paths exclusive to architectures other than the target architecture.

================================================================================
3. WHEN TO RETURN WorthFuzzing=true (POSITIVE CRITERIA)
================================================================================
Return WorthFuzzing=true whenever the patch touches reachable executable code, including:
- Core Subsystems:
  * Any logic modifications in memory management (mm/), synchronization/locking (kernel/locking/),
    BPF, scheduler, core networking, VFS, or syscall handling.
- Refactorings and Code Cleanups:
  * Any restructuring of reachable data structures, helper abstractions, or algorithm flows.
- Runtime Assertions and Defensive Checks:
  * Any introduction or alteration of assertions (WARN_ON*, VM_WARN_ON*, BUG_ON*, etc.) in reachable paths.
- Reachable Drivers and Protocols:
  * Drivers accessible via virtual buses (virtio, USB gadget, loopback, netlink, binder, sockets, etc.).

================================================================================
4. EXTRACTING FocusSymbols (PREVENTING DILUTION)
================================================================================
When WorthFuzzing=true, you must extract specific kernel functions into FocusSymbols to guide the fuzzer:

- AVOID UBIQUITOUS LIFECYCLE HOT-PATHS:
  Do NOT list generic, ubiquitous functions called by almost every program in the corpus
  (including, but not limited to: general memory allocators and deallocators, page fault
  and trap handlers, or core synchronization primitives; this is not an exhaustive list).
  Listing ubiquitous functions causes the fuzzer to classify thousands of unrelated tests as "focused",
  which severely dilutes fuzzing effort away from the actual changes.

- TARGET SPECIFIC FEATURE LOGIC AND ENTRYPOINTS:
  List functions that specifically implement the logic being added or altered, or direct API entrypoints
  for the subsystem feature under review.

- HANDLING STATIC INLINE FUNCTIONS IN HEADERS (.h):
  Compiler-inlined static functions (such as static inlines in mm/*.h or include/linux/*.h) lack
  distinct symbol addresses in vmlinux and cannot be targeted directly by symbol coverage filters.
  If the changes are primarily in static inline helpers, identify non-static, feature-specific caller
  functions in .c files that exercise them (avoiding ubiquitous lifecycle wrappers).

================================================================================
5. IDENTIFYING EnableConfigs
================================================================================
Identify any specific CONFIG_ options required to properly compile and reach the modified code:
- Inspect Kconfig files and #ifdef guards; do not make assumptions.
- Check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed.
- Strip any 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
Target architecture: amd64

For your convenience, here is the diff of the changes:
commit d979d7f8eafee3d72098e74d55fabd826fb15eb1
Author: syz-cluster <triage@syzkaller.com>
Date:   Mon Sep 7 02:23:27 2026 +0000

    syz-cluster: applied patch under review

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 7a18a469961db..467fbe43b89e9 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/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c
index 52c2fe3644d4b..50cea39323f57 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 fe45e533a4b2e..de60b698bea98 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 0f0dfb4e176a9..fd993a95501b0 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 = rcu_dereference(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 = rcu_dereference_rtnl(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;
@@ -377,14 +381,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 +398,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 +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, mac, vni);
+	f = vxlan_find_mac_rcu(vxlan, rcu_dereference(vxlan->cfg), mac, vni);
 	rcu_read_unlock();
 
 	return f;
@@ -457,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, 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) {
@@ -798,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;
 
@@ -826,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;
@@ -863,12 +871,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 = rcu_dereference_rtnl(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);
@@ -1148,6 +1157,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 = rtnl_dereference(vxlan->cfg);
 	struct net *net = dev_net(vxlan->dev);
 	int err;
 
@@ -1164,7 +1174,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);
@@ -1184,7 +1194,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]) {
@@ -1194,7 +1204,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]) {
@@ -1204,7 +1214,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]) {
@@ -1235,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;
@@ -1257,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);
@@ -1394,18 +1406,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 = rcu_dereference_rtnl(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, addr, vni);
+	f = vxlan_find_mac_rcu(vxlan, cfg, addr, vni);
 	if (!f) {
 		NL_SET_ERR_MSG(extack, "Fdb entry not found");
 		err = -ENOENT;
@@ -1423,6 +1440,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 +1459,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 +1495,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);
 	}
@@ -1507,6 +1525,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 = 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);
@@ -1516,7 +1535,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);
@@ -1587,6 +1606,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 +1632,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 +1666,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 +1708,10 @@ 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 = rcu_dereference(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 +1723,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 +1740,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 +1766,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 +1794,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 +1806,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 +1816,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 +1865,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)
+static int arp_reduce(struct net_device *dev, struct sk_buff *skb,
+		      const struct vxlan_config *cfg, __be32 vni)
 {
 	struct neigh_table *tbl = arp_table(dev_net(dev));
 	struct vxlan_dev *vxlan = netdev_priv(dev);
@@ -1853,7 +1880,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, cfg, vni, NULL,
 				      VXLAN_VNI_STATS_TX_DROPS, 0);
 		goto out;
 	}
@@ -1894,7 +1921,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, cfg, ha, vni);
 		if (f)
 			rdst = first_remote_rcu(f);
 		if (rdst && vxlan_addr_any(&rdst->remote_ip)) {
@@ -1920,11 +1947,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, cfg, vni, NULL,
 					      VXLAN_VNI_STATS_RX_DROPS, 0);
 		}
 
-	} else if (vxlan->cfg.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,
@@ -2032,7 +2059,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)
+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;
@@ -2066,7 +2094,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, cfg, ha, vni);
 		if (f)
 			rdst = first_remote_rcu(f);
 		if (rdst && vxlan_addr_any(&rdst->remote_ip)) {
@@ -2085,10 +2113,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, cfg, vni, NULL,
 					      VXLAN_VNI_STATS_RX_DROPS, 0);
 		}
-	} else if (vxlan->cfg.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,
@@ -2104,9 +2132,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 neigh_table *tbl;
 	struct neighbour *n;
 
@@ -2125,7 +2153,7 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb)
 		tbl = arp_table(dev_net(dev));
 		pip = ip_hdr(skb);
 		n = neigh_lookup(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,
@@ -2153,7 +2181,7 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb)
 		tbl = nd_table(dev_net(dev));
 		pip6 = ipv6_hdr(skb);
 		n = neigh_lookup(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,
@@ -2271,20 +2299,28 @@ 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;
 	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) {
+	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)
@@ -2294,34 +2330,29 @@ static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan,
 #endif
 	}
 
-	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_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();
 }
 
 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,
@@ -2337,22 +2368,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;
 	}
 
@@ -2360,30 +2391,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);
@@ -2403,23 +2439,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;
 		}
 
@@ -2428,11 +2464,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)
@@ -2443,9 +2479,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);
@@ -2463,7 +2499,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;
@@ -2476,8 +2512,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) {
@@ -2510,15 +2546,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 ||
@@ -2547,7 +2583,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;
 		}
@@ -2597,7 +2633,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)
@@ -2621,7 +2657,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;
 		}
@@ -2642,14 +2678,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;
 
@@ -2661,11 +2697,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;
@@ -2682,7 +2719,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;
 
@@ -2690,15 +2727,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;
@@ -2716,11 +2753,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;
 
@@ -2728,7 +2765,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;
@@ -2745,34 +2782,49 @@ 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;
+
+	rcu_read_lock();
+	cfg = rcu_dereference(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);
+			rcu_read_unlock();
 			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);
+		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) +
@@ -2781,53 +2833,57 @@ 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, 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);
+	if (nhid) {
+		netdev_tx_t res = vxlan_xmit_nhid(skb, dev, nhid, vni, saddr_family, cfg);
+
+		rcu_read_unlock();
+		return res;
+	}
 
-	if (vxlan->cfg.flags & VXLAN_F_MDB) {
+	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, skb, vni);
+		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, mdb_entry, skb);
 			rcu_read_unlock();
 			return ret;
 		}
-		rcu_read_unlock();
 	}
 
 	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;
@@ -2835,8 +2891,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;
@@ -2847,10 +2903,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);
 	}
@@ -2865,12 +2921,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 = rcu_dereference(vxlan->cfg);
+
 	hlist_for_each_entry_rcu(f, &vxlan->fdb_list, fdb_node) {
 		unsigned long timeout;
 
@@ -2880,7 +2939,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)) {
@@ -2912,10 +2971,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));
 }
@@ -2924,13 +2987,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 = rtnl_dereference(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;
@@ -2950,7 +3016,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);
@@ -2960,10 +3026,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);
 
 	gro_cells_destroy(&vxlan->gro_cells);
@@ -2975,6 +3044,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);
@@ -2987,7 +3057,8 @@ static int vxlan_open(struct net_device *dev)
 		return ret;
 	}
 
-	if (vxlan->cfg.age_interval)
+	cfg = rtnl_dereference(vxlan->cfg);
+	if (cfg && cfg->age_interval)
 		mod_timer(&vxlan->age_timer, jiffies + FDB_AGE_INTERVAL);
 
 	return ret;
@@ -3009,8 +3080,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 = rcu_dereference_rtnl(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)
@@ -3227,15 +3300,18 @@ 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;
-	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);
 
 	/* 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;
 	}
@@ -3248,11 +3324,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 = rcu_dereference(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);
@@ -3362,6 +3441,15 @@ 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);
+
+	RCU_INIT_POINTER(vxlan->cfg, NULL);
+	kfree(cfg);
+}
+
 /* Initialize the device structure. */
 static void vxlan_setup(struct net_device *dev)
 {
@@ -3370,6 +3458,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);
 
@@ -3544,9 +3634,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;
@@ -3652,21 +3744,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 = rtnl_dereference(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();
@@ -3676,7 +3769,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);
@@ -3691,7 +3784,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);
@@ -3701,11 +3794,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 = rtnl_dereference(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);
@@ -3723,28 +3819,33 @@ 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;
 
 	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 = rtnl_dereference(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;
@@ -3906,16 +4007,16 @@ 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)
 {
 	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)
@@ -3923,19 +4024,13 @@ 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;
-
-	memcpy(&dst->remote_ip, &conf->remote_ip, sizeof(conf->remote_ip));
-
 	if (lowerdev) {
-		dst->remote_ifindex = conf->remote_ifindex;
-
 		netif_inherit_tso_max(dev, lowerdev);
 
 		needed_headroom = lowerdev->hard_header_len;
@@ -3947,7 +4042,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;
 	}
 
@@ -3959,7 +4054,10 @@ 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 = 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,
@@ -3968,13 +4066,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;
 }
@@ -3986,22 +4089,25 @@ 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;
-	struct vxlan_rdst *dst;
+	const struct vxlan_config *cfg;
 	int err;
 
-	dst = &vxlan->default_dst;
 	err = vxlan_dev_configure(net, dev, conf, extack);
 	if (err)
 		return err;
 
+	cfg = rtnl_dereference(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);
+	if (cfg->remote_ifindex) {
+		remote_dev = __dev_get_by_index(net, cfg->remote_ifindex);
 		if (!remote_dev) {
 			err = -ENODEV;
 			goto unregister;
@@ -4011,7 +4117,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);
@@ -4019,16 +4125,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,
-				       vxlan->cfg.dst_port,
-				       dst->remote_vni,
-				       dst->remote_vni,
-				       dst->remote_ifindex,
+				       cfg->dst_port,
+				       cfg->vni,
+				       cfg->vni,
+				       cfg->remote_ifindex,
 				       NTF_SELF, 0, true, extack);
 		spin_unlock_bh(&vxlan->hash_lock);
 		if (err)
@@ -4089,8 +4197,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 = rtnl_dereference(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]));
@@ -4437,16 +4549,16 @@ 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 = rtnl_dereference(vxlan->cfg);
 	bool rem_ip_changed, change_igmp;
 	struct net_device *lowerdev;
 	struct vxlan_config conf;
-	struct vxlan_rdst *dst;
+	struct vxlan_config *new_cfg;
 	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;
@@ -4456,18 +4568,23 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[],
 	if (err)
 		return err;
 
-	if (dst->remote_dev == lowerdev)
-		lowerdev = NULL;
+	new_cfg = kmemdup(&conf, sizeof(conf), GFP_KERNEL);
+	if (!new_cfg)
+		return -ENOMEM;
 
-	err = netdev_adjacent_change_prepare(dst->remote_dev, lowerdev, dev,
-					     extack);
-	if (err)
-		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) {
@@ -4477,54 +4594,63 @@ 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);
 			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,
-					   vxlan->cfg.dst_port,
-					   dst->remote_vni,
-					   dst->remote_vni,
-					   dst->remote_ifindex,
+					   cfg->remote_ip,
+					   cfg->dst_port,
+					   cfg->vni,
+					   cfg->vni,
+					   cfg->remote_ifindex,
 					   true);
 		spin_unlock_bh(&vxlan->hash_lock);
 
 		/* If vni filtering device, also update fdb entries of
 		 * all vnis that were using default remote ip
 		 */
-		if (vxlan->cfg.flags & VXLAN_F_VNIFILTER) {
-			err = vxlan_vnilist_update_group(vxlan, &dst->remote_ip,
+		if (cfg->flags & VXLAN_F_VNIFILTER) {
+			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 != 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);
+	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;
@@ -4539,8 +4665,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)
@@ -4584,110 +4710,120 @@ 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 = {
-		.low =  htons(vxlan->cfg.port_min),
-		.high = htons(vxlan->cfg.port_max),
-	};
+	struct ifla_vxlan_port_range ports;
+	const struct vxlan_config *cfg;
+	int err = 0;
 
-	if (nla_put_u32(skb, IFLA_VXLAN_ID, be32_to_cpu(dst->remote_vni)))
+	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;
 
-	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(&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;
+out:
+	rcu_read_unlock();
+	return err;
 
 nla_put_failure:
+	rcu_read_unlock();
 	return -EMSGSIZE;
 }
 
@@ -4720,7 +4856,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
@@ -4728,7 +4864,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 841f42ffecb9a..c1a0551990555 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 = rcu_dereference_rtnl(vxlan->cfg);
 	struct vxlan_rdst *rd = rtnl_dereference(remote->rd);
 	struct br_mdb_entry e;
 	struct nlattr *nest;
@@ -189,12 +190,12 @@ 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;
 
-	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;
 
@@ -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;
@@ -613,16 +614,19 @@ 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;
+
+	vcfg = rtnl_dereference(vxlan->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_port = vxlan->cfg.dst_port;
+	cfg->remote_vni = vcfg->vni;
+	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");
@@ -957,11 +961,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 = 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));
@@ -978,16 +983,16 @@ 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)
+	if (rd->remote_vni != cfg->vni)
 		nlmsg_size += nla_total_size(sizeof(u32));
 	/* MDBA_MDB_EATTR_IFINDEX */
 	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;
@@ -1216,7 +1221,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;
 
@@ -1233,7 +1238,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);
@@ -1483,11 +1488,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);
@@ -1621,6 +1628,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)
 {
@@ -1634,8 +1642,8 @@ 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))
-		src_vni = vxlan->default_dst.remote_vni;
+	if (!(cfg->flags & VXLAN_F_COLLECT_METADATA))
+		src_vni = cfg->vni;
 
 	memset(&group, 0, sizeof(group));
 	group.vni = src_vni;
@@ -1700,6 +1708,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)
 {
@@ -1721,12 +1730,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);
@@ -1757,7 +1766,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/drivers/net/vxlan/vxlan_multicast.c b/drivers/net/vxlan/vxlan_multicast.c
index 3b75b48dc726d..ae0320921c8ca 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);
 
@@ -147,24 +152,28 @@ 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;
 
 		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
-		if (vxlan->cfg.flags & VXLAN_F_VNIFILTER) {
-			if (!vxlan_group_used_by_vnifilter(vxlan, ip, ifindex))
+		cfg = rtnl_dereference(vxlan->cfg);
+
+		if (cfg->flags & VXLAN_F_VNIFILTER) {
+			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;
 		}
 
@@ -174,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;
@@ -185,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)
@@ -200,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)
@@ -233,37 +243,39 @@ 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)) {
-		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)
 			return ret;
 	}
 
-	if (vxlan->cfg.flags & VXLAN_F_VNIFILTER)
-		return vxlan_multicast_join_vnigrp(vxlan);
+	if (cfg->flags & VXLAN_F_VNIFILTER)
+		return vxlan_multicast_join_vnigrp(vxlan, cfg);
 
 	return 0;
 }
 
 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;
 
-	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;
 	}
 
-	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_private.h b/drivers/net/vxlan/vxlan_private.h
index b1eec22163608..4f70afc502c90 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);
 
@@ -214,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);
 
 
@@ -223,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 */
@@ -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 dd94085e08865..e6c5fa2301347 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->flags & VXLAN_F_VNIFILTER))
 		return;
 
 	if (vninode) {
@@ -333,22 +334,28 @@ 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;
+	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;
+	}
 
 	/* 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 +369,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;
@@ -471,23 +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)
 {
-	struct vxlan_rdst *dst = &vxlan->default_dst;
+	const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg);
 	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,
-				       vxlan->cfg.dst_port,
+				       cfg->dst_port,
 				       vni,
 				       vni,
-				       dst->remote_ifindex,
+				       cfg->remote_ifindex,
 				       NTF_SELF, 0, true, extack);
 		if (err) {
 			spin_unlock_bh(&vxlan->hash_lock);
@@ -498,9 +507,9 @@ 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,
+				   cfg->remote_ifindex,
 				   true);
 	}
 	spin_unlock_bh(&vxlan->hash_lock);
@@ -515,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;
 
@@ -528,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,
@@ -557,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)
@@ -581,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;
@@ -612,20 +621,20 @@ 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);
-	struct vxlan_rdst *dst = &vxlan->default_dst;
+	const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg);
 
 	/* 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),
-				   vxlan->cfg.dst_port,
+				   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);
 	}
@@ -634,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);
 		}
 	}
@@ -723,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;
@@ -731,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;
@@ -844,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;
@@ -881,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");
 
@@ -940,6 +952,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;
@@ -964,8 +977,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 7b82075055237..f90d43a9284c1 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 {
@@ -294,14 +295,15 @@ 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;
 	unsigned int	  addrcnt;
 	struct gro_cells  gro_cells;
+	unsigned long	  flags;
 
-	struct vxlan_config	cfg;
+	struct vxlan_config __rcu	*cfg;
 
 	struct vxlan_vni_group  __rcu *vnigrp;
 
@@ -313,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
@@ -331,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
 

3/2 2026/09/07 02:23 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 45568
							output: 26
							thoughts: 566
						

Thoughts:
**Analyzing the VXLAN Patch for Fuzzing Potential**

Okay, let's break down this patch and see if it's worth our time fuzzing with syzkaller. First, I need to get a handle on what's actually changed. From a quick scan, it looks like this patch is all about reworking the VXLAN driver, specifically touching `drivers/net/vxlan/*`, some Mellanox drivers (`mlx5`, `mlxsw`), and the ubiquitous `include/net/vxlan.h`.

The core of the change seems to be centered around the `struct vxlan_dev`. It looks like they've done a major overhaul, particularly around the `cfg` and `default_dst` members. Instead of having a static `struct vxlan_config cfg;`, they're now dynamically allocating it via an RCU pointer (`struct vxlan_config __rcu *cfg;`).  `default_dst` is gone, with its data now integrated into `cfg`, and a `lowerdev` member has been added to `struct vxlan_dev`.

This means a bunch of functions are likely affected.  We're talking about changes to `vxlan_free_dev`, `vxlan_config_apply`, `vxlan_dev_configure`, `vxlan_changelink`, and a whole slew of other routines. They're all being modified to deal with the RCU-based allocation, update, and freeing of the `cfg` structure. I'm seeing `kfree_rcu`, `kmemdup`, `rtnl_dereference`, `rcu_dereference`, etc. all over the place, which is a good indicator of significant RCU usage and concurrent access changes. We also see modifications to locking and RCU dereferencing throughout the VXLAN datapath and control path.

Now the million-dollar question: is this reachable code?  Absolutely! VXLAN is a well-established and standard virtual network interface in Linux (`CONFIG_VXLAN`), and syzkaller *loves* messing with networking. Syzkaller actively creates and configures VXLAN devices via netlink (rtnetlink), sends and receives packets, manipulates FDBs, MDBs, changelink, and all that good stuff. This means we should be able to get syzkaller to exercise all these changed code paths.

Next step: I need to quickly determine the required configurations to enable VXLAN and ensure that it is enabled. I can check `drivers/net/vxlan/Kconfig` or `drivers/net/Kconfig` to do this, using `grepper` or `read-file`. This is important, as it helps me identify the relevant symbols that syzkaller needs to activate, so I can then verify that those settings are appropriately included in the syzkaller configuration.



4/2 2026/09/07 02:23 tool grepper
0m Args:
{
  "Expression": "config VXLAN",
  "PathPrefix": "drivers/net"
}

Results:
{
  "Output": "drivers/net/Kconfig=216=config IPVTAP\n--\ndrivers/net/Kconfig-229-\ndrivers/net/Kconfig:230:config VXLAN\ndrivers/net/Kconfig-231-\ttristate \"Virtual eXtensible Local Area Network (VXLAN)\"\n"
}

5/2 2026/09/07 02:23 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 46842
							output: 34
							thoughts: 22
						

Thoughts:
Here's my summary, as if I were thinking these thoughts:

**Examining Network Driver Dependencies**

Okay, let's dive into the `drivers/net/Kconfig` file. I need to understand the dependencies around line 230. My goal here is to quickly grasp the relevant configuration options and how they relate to each other, especially as they pertain to network drivers. I'm looking for a specific dependency chain or perhaps a conditional statement that's causing an issue I'm trying to resolve. This Kconfig file holds the key to which features and drivers are enabled based on various configuration choices, so getting a quick handle on that section will be useful.



6/2 2026/09/07 02:23 tool read-file
0m Args:
{
  "File": "drivers/net/Kconfig",
  "FirstLine": 228,
  "LineCount": 25
}

Results:
{
  "Contents": " 228:\t\t  will be called ipvtap.\n 229:\t\n 230:\tconfig VXLAN\n 231:\t\ttristate \"Virtual eXtensible Local Area Network (VXLAN)\"\n 232:\t\tdepends on INET\n 233:\t\tselect NET_UDP_TUNNEL\n 234:\t\tselect GRO_CELLS\n 235:\t\thelp\n 236:\t\t  This allows one to create vxlan virtual interfaces that provide\n 237:\t\t  Layer 2 Networks over Layer 3 Networks. VXLAN is often used\n 238:\t\t  to tunnel virtual network infrastructure in virtualized environments.\n 239:\t\t  For more information see:\n 240:\t\t    http://tools.ietf.org/html/draft-mahalingam-dutt-dcops-vxlan-02\n 241:\t\n 242:\t\t  To compile this driver as a module, choose M here: the module\n 243:\t\t  will be called vxlan.\n 244:\t\n 245:\tconfig GENEVE\n 246:\t\ttristate \"Generic Network Virtualization Encapsulation\"\n 247:\t\tdepends on INET\n 248:\t\tselect NET_UDP_TUNNEL\n 249:\t\tselect GRO_CELLS\n 250:\t\thelp\n 251:\t\t  This allows one to create geneve virtual interfaces that provide\n 252:\t\t  Layer 2 Networks over Layer 3 Networks. GENEVE is often used\n"
}

7/2 2026/09/07 02:23 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 47428
							output: 277
							thoughts: 993
						

Thoughts:
**Fuzzing Candidate Assessment: VXLAN Driver**

Okay, let's break this down. First, I need to confirm if there are any related configurations I should keep an eye out for. I see mentions of `CONFIG_INET` and `CONFIG_VXLAN`. A quick check reveals that `VXLAN` depends on `INET`, so let's keep both in mind.

Next, I'll need to identify the key functions that were modified or are central to the changes in this patch. Based on the diff, these are the functions that immediately jump out:

*   `vxlan_dev_configure`, `vxlan_config_apply`, `vxlan_dev_create`, `vxlan_changelink`
*   `vxlan_free_dev`
*   `vxlan_rcv`
*   `vxlan_xmit`
*   `vxlan_fdb_info`, `vxlan_fdb_get`, `vxlan_fdb_parse`, `vxlan_fdb_add`, `vxlan_fdb_create`
*   `vxlan_encap_bypass`, `vxlan_xmit_one`, `vxlan_xmit_nh`, `vxlan_xmit_nhid`
*   `vxlan_vnifilter_count`, `vxlan_vnifilter_dump_dev`, `vxlan_process_vni_filter`, `vxlan_vnifilter_process`
*   `vxlan_mdb_entry_skb_get`, `vxlan_mdb_xmit`, `vxlan_mdb_get`
*   `vxlan_multicast_join`, `vxlan_multicast_leave`, `vxlan_group_used`, `vxlan_igmp_join`, `vxlan_igmp_leave`

I'll then review these functions in the context of their source files. I'll need to figure out which of them are non-static and serve as entry points or if they are central in core `vxlan_core.c`, `vxlan_mdb.c`, `vxlan_vnifilter.c`, and `vxlan_multicast.c` files. I will focus on the most important ones: `vxlan_xmit`, `vxlan_rcv`, `vxlan_changelink`, `vxlan_fdb_add`, `vxlan_fdb_get`, `vxlan_fill_info`, `vxlan_mdb_get`, `vxlan_process_vni_filter`.

I'll check the visibility of `vxlan_free_dev` - and also consider static functions, because coverage-guided fuzzers can hit these if `kcov` or other tools are used. `vxlan_changelink` is static but called via `rtnl_link_ops`. My focus is to create a list of key modified functions:

*   `vxlan_xmit`
*   `vxlan_rcv`
*   `vxlan_changelink`
*   `vxlan_dev_configure`
*   `vxlan_fdb_add`
*   `vxlan_fdb_get`
*   `vxlan_fill_info`
*   `vxlan_mdb_xmit`
*   `vxlan_mdb_get`
*   `vxlan_vnifilter_process`
*   `vxlan_multicast_join`
*   `vxlan_multicast_leave`

Now, let's assess whether this patch warrants fuzzing. The subsystem is VXLAN, a virtual networking device. The nature of the change is significant: a major architectural shift. The old `vxlan->cfg` is being converted to an RCU-managed dynamic structure (`struct vxlan_config __rcu *cfg;`), and it introduces `vxlan_free_dev`, removes `default_dst`, and modifies locking and dereferencing throughout the RX/TX and rtnl control paths.

The reachability is complete via standard netlink socket interfaces and packet transmission/reception. This isn't a niche change. And that RCU refactoring, specifically the dereferences in the network datapath versus rtnl updates (`vxlan_changelink`, `vxlan_dev_configure`, `vxlan_free_dev`), screams potential for UAF, NULL dereferences, and race conditions. This has extremely high value for fuzzing.

Let's quickly double-check the configs: `VXLAN` is essential, and `INET` is beneficial, although `VXLAN` is the primary driver here.

Now, based on this evaluation, here are the parameters I'd use for `set-results


8/2 2026/09/07 02:23 tool set-results
0m Args:
{
  "EnableConfigs": [
    "VXLAN",
    "INET"
  ],
  "FocusSymbols": [
    "vxlan_xmit",
    "vxlan_rcv",
    "vxlan_changelink",
    "vxlan_dev_configure",
    "vxlan_fdb_add",
    "vxlan_fdb_get",
    "vxlan_fill_info",
    "vxlan_mdb_xmit",
    "vxlan_mdb_get",
    "vxlan_vnifilter_process",
    "vxlan_multicast_join",
    "vxlan_multicast_leave"
  ],
  "Reasoning": "The patch refactors the VXLAN driver to manage struct vxlan_config dynamically via RCU pointers (rcu_assign_pointer / kfree_rcu) and updates RCU/RTNL dereferencing across both control path (changelink, FDB, MDB, vnifilter) and data path (xmit, rcv, encap bypass). VXLAN devices are virtual network interfaces fully reachable without dedicated hardware in standard QEMU/syzkaller environments. Fuzzing is warranted to test for potential NULL dereferences, RCU race conditions, and use-after-free bugs.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": [
    "VXLAN",
    "INET"
  ],
  "FocusSymbols": [
    "vxlan_xmit",
    "vxlan_rcv",
    "vxlan_changelink",
    "vxlan_dev_configure",
    "vxlan_fdb_add",
    "vxlan_fdb_get",
    "vxlan_fill_info",
    "vxlan_mdb_xmit",
    "vxlan_mdb_get",
    "vxlan_vnifilter_process",
    "vxlan_multicast_join",
    "vxlan_multicast_leave"
  ],
  "Reasoning": "The patch refactors the VXLAN driver to manage struct vxlan_config dynamically via RCU pointers (rcu_assign_pointer / kfree_rcu) and updates RCU/RTNL dereferencing across both control path (changelink, FDB, MDB, vnifilter) and data path (xmit, rcv, encap bypass). VXLAN devices are virtual network interfaces fully reachable without dedicated hardware in standard QEMU/syzkaller environments. Fuzzing is warranted to test for potential NULL dereferences, RCU race conditions, and use-after-free bugs.",
  "WorthFuzzing": true
}

9/1 2026/09/07 02:23 action If
0m Args:
{
  "WorthFuzzing": true
}

Results:
null

10/2 2026/09/07 02:23 agent kmsan-evaluator
0m Model:
core

Args:
null

Results:
{
  "KMSANReasoning": "The patch refactors VXLAN configuration management by converting `vxlan-\u003ecfg` from an embedded struct to a dynamically-allocated, RCU-protected pointer (`struct vxlan_config __rcu *cfg`), removing duplicate fields previously mirrored in `vxlan-\u003edefault_dst`, and moving the MDB flag to `vxlan-\u003eflags`.\n\nAll new allocations (`kmemdup`) operate on fully initialized structures (cleared with `memset` in `vxlan_nl2conf`), and local stack structures (`struct ifla_vxlan_port_range ports`, `struct mlxsw_sp_nve_params params`, `struct vxlan_metadata _md`) have all their fields fully initialized before use. No uninitialized struct padding or uninitialized memory is read or exposed across security boundaries (e.g. copied to user space via netlink).\n\nAny potential bugs in this patch pertain to pointer lifetime, concurrency, RCU synchronization, or use-after-free, which are effectively detected by KASAN and LOCKDEP rather than KMSAN. Dedicated KMSAN fuzzing is not required.",
  "NeedsKMSAN": false
}

Instruction:
You are an expert Linux kernel security engineer specializing in kernel memory
error detectors (KASAN and KMSAN). Your job is to review the provided patch series and
determine if the code changes justify spawning a dedicated KMSAN (KernelMemorySanitizer)
fuzzing session in addition to standard KASAN fuzzing.

CRITICAL DISTINCTION BETWEEN KASAN AND KMSAN:
- Standard KASAN kernel builds (upstream-apparmor-kasan.config) already enable
  a comprehensive suite of debugging tools and sanitizers, including KASAN
  (out-of-bounds accesses, use-after-free, double free, invalid free), LOCKDEP
  (locking bugs and deadlocks), UB-sanitizers, and memory corruption checks.
- KMSAN (KernelMemorySanitizer) detects reads of UNINITIALIZED memory (stack, heap,
  or page allocations) and kernel-to-user memory info-leaks.

Rule: THERE IS NO SENSE IN RUNNING A KMSAN SESSION IF A BUG CAN BE CAUGHT BY KASAN,
LOCKDEP, OR OTHER STANDARD BUG DETECTORS.
A dedicated KMSAN fuzzing session incurs significant resource costs. You must ONLY
set NeedsKMSAN=true if the code changes introduce or expose UNINITIALIZED MEMORY risks
that are detected ONLY by KMSAN.

Look holistically at the patch series and surrounding code. Even if no direct
uninitialized field accesses or new buffer allocations are added in the diff itself,
a patch may alter control flow, bounds checking, or data length calculations in ways
that change how the rest of the code operates on existing buffers (e.g. allowing
uninitialized stack/heap memory to be read, copied to user space, or used in control
flow). Do not hesitate to use your code access tools to inspect the surrounding code,
called functions, and callers.

Set NeedsKMSAN=true ONLY IF the patch introduces or modifies:
1. Kernel structures sent to user space (via copy_to_user, put_user, netlink skb
   attributes, ioctl output arguments, socket options, or BPF buffers) where fields
   or structure padding might not be fully initialized/zeroed.
2. Conditional logic or branching that depends on potentially uninitialized variables
   or struct fields.
3. Allocation or initialization of complex data structures where uninitialized fields
   could be read later in reachable code paths.
4. Bounds checks, lengths, or logic in a way that allows surrounding code to access
   uninitialized bytes of existing buffers.

Set NeedsKMSAN=false IF:
- The code changes primarily risk out-of-bounds access, array overflows, NULL pointer
  dereferences, locking deadlocks, or use-after-free bugs (these are already caught
  by KASAN, LOCKDEP, or standard bug detectors).
- All stack/heap structures touched or introduced by the patch are fully zeroed
  or initialized (e.g. using = {0}, memset, kzalloc) before being read or copied.
- The patch does not introduce any risk of uninitialized memory usage or info-leaks.

Use your code access tools to inspect the surrounding code if necessary, then provide
detailed KMSANReasoning contrasting KASAN vs KMSAN applicability for this patch.
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
Target architecture: amd64

For your convenience, here is the diff of the changes:
commit d979d7f8eafee3d72098e74d55fabd826fb15eb1
Author: syz-cluster <triage@syzkaller.com>
Date:   Mon Sep 7 02:23:27 2026 +0000

    syz-cluster: applied patch under review

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 7a18a469961db..467fbe43b89e9 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/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c
index 52c2fe3644d4b..50cea39323f57 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 fe45e533a4b2e..de60b698bea98 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 0f0dfb4e176a9..fd993a95501b0 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 = rcu_dereference(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 = rcu_dereference_rtnl(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;
@@ -377,14 +381,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 +398,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 +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, mac, vni);
+	f = vxlan_find_mac_rcu(vxlan, rcu_dereference(vxlan->cfg), mac, vni);
 	rcu_read_unlock();
 
 	return f;
@@ -457,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, 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) {
@@ -798,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;
 
@@ -826,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;
@@ -863,12 +871,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 = rcu_dereference_rtnl(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);
@@ -1148,6 +1157,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 = rtnl_dereference(vxlan->cfg);
 	struct net *net = dev_net(vxlan->dev);
 	int err;
 
@@ -1164,7 +1174,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);
@@ -1184,7 +1194,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]) {
@@ -1194,7 +1204,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]) {
@@ -1204,7 +1214,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]) {
@@ -1235,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;
@@ -1257,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);
@@ -1394,18 +1406,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 = rcu_dereference_rtnl(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, addr, vni);
+	f = vxlan_find_mac_rcu(vxlan, cfg, addr, vni);
 	if (!f) {
 		NL_SET_ERR_MSG(extack, "Fdb entry not found");
 		err = -ENOENT;
@@ -1423,6 +1440,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 +1459,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 +1495,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);
 	}
@@ -1507,6 +1525,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 = 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);
@@ -1516,7 +1535,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);
@@ -1587,6 +1606,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 +1632,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 +1666,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 +1708,10 @@ 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 = rcu_dereference(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 +1723,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 +1740,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 +1766,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 +1794,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 +1806,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 +1816,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 +1865,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)
+static int arp_reduce(struct net_device *dev, struct sk_buff *skb,
+		      const struct vxlan_config *cfg, __be32 vni)
 {
 	struct neigh_table *tbl = arp_table(dev_net(dev));
 	struct vxlan_dev *vxlan = netdev_priv(dev);
@@ -1853,7 +1880,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, cfg, vni, NULL,
 				      VXLAN_VNI_STATS_TX_DROPS, 0);
 		goto out;
 	}
@@ -1894,7 +1921,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, cfg, ha, vni);
 		if (f)
 			rdst = first_remote_rcu(f);
 		if (rdst && vxlan_addr_any(&rdst->remote_ip)) {
@@ -1920,11 +1947,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, cfg, vni, NULL,
 					      VXLAN_VNI_STATS_RX_DROPS, 0);
 		}
 
-	} else if (vxlan->cfg.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,
@@ -2032,7 +2059,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)
+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;
@@ -2066,7 +2094,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, cfg, ha, vni);
 		if (f)
 			rdst = first_remote_rcu(f);
 		if (rdst && vxlan_addr_any(&rdst->remote_ip)) {
@@ -2085,10 +2113,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, cfg, vni, NULL,
 					      VXLAN_VNI_STATS_RX_DROPS, 0);
 		}
-	} else if (vxlan->cfg.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,
@@ -2104,9 +2132,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 neigh_table *tbl;
 	struct neighbour *n;
 
@@ -2125,7 +2153,7 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb)
 		tbl = arp_table(dev_net(dev));
 		pip = ip_hdr(skb);
 		n = neigh_lookup(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,
@@ -2153,7 +2181,7 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb)
 		tbl = nd_table(dev_net(dev));
 		pip6 = ipv6_hdr(skb);
 		n = neigh_lookup(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,
@@ -2271,20 +2299,28 @@ 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;
 	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) {
+	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)
@@ -2294,34 +2330,29 @@ static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan,
 #endif
 	}
 
-	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_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();
 }
 
 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,
@@ -2337,22 +2368,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;
 	}
 
@@ -2360,30 +2391,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);
@@ -2403,23 +2439,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;
 		}
 
@@ -2428,11 +2464,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)
@@ -2443,9 +2479,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);
@@ -2463,7 +2499,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;
@@ -2476,8 +2512,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) {
@@ -2510,15 +2546,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 ||
@@ -2547,7 +2583,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;
 		}
@@ -2597,7 +2633,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)
@@ -2621,7 +2657,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;
 		}
@@ -2642,14 +2678,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;
 
@@ -2661,11 +2697,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;
@@ -2682,7 +2719,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;
 
@@ -2690,15 +2727,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;
@@ -2716,11 +2753,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;
 
@@ -2728,7 +2765,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;
@@ -2745,34 +2782,49 @@ 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;
+
+	rcu_read_lock();
+	cfg = rcu_dereference(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);
+			rcu_read_unlock();
 			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);
+		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) +
@@ -2781,53 +2833,57 @@ 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, 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);
+	if (nhid) {
+		netdev_tx_t res = vxlan_xmit_nhid(skb, dev, nhid, vni, saddr_family, cfg);
+
+		rcu_read_unlock();
+		return res;
+	}
 
-	if (vxlan->cfg.flags & VXLAN_F_MDB) {
+	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, skb, vni);
+		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, mdb_entry, skb);
 			rcu_read_unlock();
 			return ret;
 		}
-		rcu_read_unlock();
 	}
 
 	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;
@@ -2835,8 +2891,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;
@@ -2847,10 +2903,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);
 	}
@@ -2865,12 +2921,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 = rcu_dereference(vxlan->cfg);
+
 	hlist_for_each_entry_rcu(f, &vxlan->fdb_list, fdb_node) {
 		unsigned long timeout;
 
@@ -2880,7 +2939,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)) {
@@ -2912,10 +2971,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));
 }
@@ -2924,13 +2987,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 = rtnl_dereference(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;
@@ -2950,7 +3016,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);
@@ -2960,10 +3026,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);
 
 	gro_cells_destroy(&vxlan->gro_cells);
@@ -2975,6 +3044,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);
@@ -2987,7 +3057,8 @@ static int vxlan_open(struct net_device *dev)
 		return ret;
 	}
 
-	if (vxlan->cfg.age_interval)
+	cfg = rtnl_dereference(vxlan->cfg);
+	if (cfg && cfg->age_interval)
 		mod_timer(&vxlan->age_timer, jiffies + FDB_AGE_INTERVAL);
 
 	return ret;
@@ -3009,8 +3080,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 = rcu_dereference_rtnl(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)
@@ -3227,15 +3300,18 @@ 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;
-	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);
 
 	/* 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;
 	}
@@ -3248,11 +3324,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 = rcu_dereference(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);
@@ -3362,6 +3441,15 @@ 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);
+
+	RCU_INIT_POINTER(vxlan->cfg, NULL);
+	kfree(cfg);
+}
+
 /* Initialize the device structure. */
 static void vxlan_setup(struct net_device *dev)
 {
@@ -3370,6 +3458,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);
 
@@ -3544,9 +3634,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;
@@ -3652,21 +3744,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 = rtnl_dereference(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();
@@ -3676,7 +3769,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);
@@ -3691,7 +3784,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);
@@ -3701,11 +3794,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 = rtnl_dereference(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);
@@ -3723,28 +3819,33 @@ 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;
 
 	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 = rtnl_dereference(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;
@@ -3906,16 +4007,16 @@ 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)
 {
 	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)
@@ -3923,19 +4024,13 @@ 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;
-
-	memcpy(&dst->remote_ip, &conf->remote_ip, sizeof(conf->remote_ip));
-
 	if (lowerdev) {
-		dst->remote_ifindex = conf->remote_ifindex;
-
 		netif_inherit_tso_max(dev, lowerdev);
 
 		needed_headroom = lowerdev->hard_header_len;
@@ -3947,7 +4042,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;
 	}
 
@@ -3959,7 +4054,10 @@ 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 = 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,
@@ -3968,13 +4066,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;
 }
@@ -3986,22 +4089,25 @@ 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;
-	struct vxlan_rdst *dst;
+	const struct vxlan_config *cfg;
 	int err;
 
-	dst = &vxlan->default_dst;
 	err = vxlan_dev_configure(net, dev, conf, extack);
 	if (err)
 		return err;
 
+	cfg = rtnl_dereference(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);
+	if (cfg->remote_ifindex) {
+		remote_dev = __dev_get_by_index(net, cfg->remote_ifindex);
 		if (!remote_dev) {
 			err = -ENODEV;
 			goto unregister;
@@ -4011,7 +4117,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);
@@ -4019,16 +4125,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,
-				       vxlan->cfg.dst_port,
-				       dst->remote_vni,
-				       dst->remote_vni,
-				       dst->remote_ifindex,
+				       cfg->dst_port,
+				       cfg->vni,
+				       cfg->vni,
+				       cfg->remote_ifindex,
 				       NTF_SELF, 0, true, extack);
 		spin_unlock_bh(&vxlan->hash_lock);
 		if (err)
@@ -4089,8 +4197,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 = rtnl_dereference(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]));
@@ -4437,16 +4549,16 @@ 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 = rtnl_dereference(vxlan->cfg);
 	bool rem_ip_changed, change_igmp;
 	struct net_device *lowerdev;
 	struct vxlan_config conf;
-	struct vxlan_rdst *dst;
+	struct vxlan_config *new_cfg;
 	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;
@@ -4456,18 +4568,23 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[],
 	if (err)
 		return err;
 
-	if (dst->remote_dev == lowerdev)
-		lowerdev = NULL;
+	new_cfg = kmemdup(&conf, sizeof(conf), GFP_KERNEL);
+	if (!new_cfg)
+		return -ENOMEM;
 
-	err = netdev_adjacent_change_prepare(dst->remote_dev, lowerdev, dev,
-					     extack);
-	if (err)
-		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) {
@@ -4477,54 +4594,63 @@ 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);
 			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,
-					   vxlan->cfg.dst_port,
-					   dst->remote_vni,
-					   dst->remote_vni,
-					   dst->remote_ifindex,
+					   cfg->remote_ip,
+					   cfg->dst_port,
+					   cfg->vni,
+					   cfg->vni,
+					   cfg->remote_ifindex,
 					   true);
 		spin_unlock_bh(&vxlan->hash_lock);
 
 		/* If vni filtering device, also update fdb entries of
 		 * all vnis that were using default remote ip
 		 */
-		if (vxlan->cfg.flags & VXLAN_F_VNIFILTER) {
-			err = vxlan_vnilist_update_group(vxlan, &dst->remote_ip,
+		if (cfg->flags & VXLAN_F_VNIFILTER) {
+			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 != 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);
+	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;
@@ -4539,8 +4665,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)
@@ -4584,110 +4710,120 @@ 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 = {
-		.low =  htons(vxlan->cfg.port_min),
-		.high = htons(vxlan->cfg.port_max),
-	};
+	struct ifla_vxlan_port_range ports;
+	const struct vxlan_config *cfg;
+	int err = 0;
 
-	if (nla_put_u32(skb, IFLA_VXLAN_ID, be32_to_cpu(dst->remote_vni)))
+	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;
 
-	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(&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;
+out:
+	rcu_read_unlock();
+	return err;
 
 nla_put_failure:
+	rcu_read_unlock();
 	return -EMSGSIZE;
 }
 
@@ -4720,7 +4856,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
@@ -4728,7 +4864,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 841f42ffecb9a..c1a0551990555 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 = rcu_dereference_rtnl(vxlan->cfg);
 	struct vxlan_rdst *rd = rtnl_dereference(remote->rd);
 	struct br_mdb_entry e;
 	struct nlattr *nest;
@@ -189,12 +190,12 @@ 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;
 
-	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;
 
@@ -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;
@@ -613,16 +614,19 @@ 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;
+
+	vcfg = rtnl_dereference(vxlan->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_port = vxlan->cfg.dst_port;
+	cfg->remote_vni = vcfg->vni;
+	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");
@@ -957,11 +961,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 = 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));
@@ -978,16 +983,16 @@ 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)
+	if (rd->remote_vni != cfg->vni)
 		nlmsg_size += nla_total_size(sizeof(u32));
 	/* MDBA_MDB_EATTR_IFINDEX */
 	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;
@@ -1216,7 +1221,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;
 
@@ -1233,7 +1238,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);
@@ -1483,11 +1488,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);
@@ -1621,6 +1628,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)
 {
@@ -1634,8 +1642,8 @@ 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))
-		src_vni = vxlan->default_dst.remote_vni;
+	if (!(cfg->flags & VXLAN_F_COLLECT_METADATA))
+		src_vni = cfg->vni;
 
 	memset(&group, 0, sizeof(group));
 	group.vni = src_vni;
@@ -1700,6 +1708,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)
 {
@@ -1721,12 +1730,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);
@@ -1757,7 +1766,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/drivers/net/vxlan/vxlan_multicast.c b/drivers/net/vxlan/vxlan_multicast.c
index 3b75b48dc726d..ae0320921c8ca 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);
 
@@ -147,24 +152,28 @@ 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;
 
 		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
-		if (vxlan->cfg.flags & VXLAN_F_VNIFILTER) {
-			if (!vxlan_group_used_by_vnifilter(vxlan, ip, ifindex))
+		cfg = rtnl_dereference(vxlan->cfg);
+
+		if (cfg->flags & VXLAN_F_VNIFILTER) {
+			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;
 		}
 
@@ -174,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;
@@ -185,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)
@@ -200,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)
@@ -233,37 +243,39 @@ 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)) {
-		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)
 			return ret;
 	}
 
-	if (vxlan->cfg.flags & VXLAN_F_VNIFILTER)
-		return vxlan_multicast_join_vnigrp(vxlan);
+	if (cfg->flags & VXLAN_F_VNIFILTER)
+		return vxlan_multicast_join_vnigrp(vxlan, cfg);
 
 	return 0;
 }
 
 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;
 
-	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;
 	}
 
-	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_private.h b/drivers/net/vxlan/vxlan_private.h
index b1eec22163608..4f70afc502c90 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);
 
@@ -214,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);
 
 
@@ -223,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 */
@@ -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 dd94085e08865..e6c5fa2301347 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->flags & VXLAN_F_VNIFILTER))
 		return;
 
 	if (vninode) {
@@ -333,22 +334,28 @@ 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;
+	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;
+	}
 
 	/* 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 +369,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;
@@ -471,23 +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)
 {
-	struct vxlan_rdst *dst = &vxlan->default_dst;
+	const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg);
 	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,
-				       vxlan->cfg.dst_port,
+				       cfg->dst_port,
 				       vni,
 				       vni,
-				       dst->remote_ifindex,
+				       cfg->remote_ifindex,
 				       NTF_SELF, 0, true, extack);
 		if (err) {
 			spin_unlock_bh(&vxlan->hash_lock);
@@ -498,9 +507,9 @@ 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,
+				   cfg->remote_ifindex,
 				   true);
 	}
 	spin_unlock_bh(&vxlan->hash_lock);
@@ -515,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;
 
@@ -528,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,
@@ -557,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)
@@ -581,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;
@@ -612,20 +621,20 @@ 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);
-	struct vxlan_rdst *dst = &vxlan->default_dst;
+	const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg);
 
 	/* 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),
-				   vxlan->cfg.dst_port,
+				   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);
 	}
@@ -634,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);
 		}
 	}
@@ -723,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;
@@ -731,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;
@@ -844,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;
@@ -881,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");
 
@@ -940,6 +952,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;
@@ -964,8 +977,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 7b82075055237..f90d43a9284c1 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 {
@@ -294,14 +295,15 @@ 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;
 	unsigned int	  addrcnt;
 	struct gro_cells  gro_cells;
+	unsigned long	  flags;
 
-	struct vxlan_config	cfg;
+	struct vxlan_config __rcu	*cfg;
 
 	struct vxlan_vni_group  __rcu *vnigrp;
 
@@ -313,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
@@ -331,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
 

11/3 2026/09/07 02:23 llm kmsan-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 44780
							output: 24
							thoughts: 629
						

Thoughts:
**Analyzing the VXLAN Patch: Uninitialized Memory Risks?**

Okay, let's break this down. My initial assessment is that this patch seems focused on refactoring the `vxlan_dev` structure, likely to improve concurrency and simplify configuration. It looks like they're transitioning from embedded `struct vxlan_config` and `default_dst` to a dynamically allocated, RCU-protected `cfg` pointer. This immediately signals a need to check for potential race conditions and memory safety issues. We need to be especially vigilant about uninitialized memory being exposed. The patch is definitely cleaning things up and adding a `lowerdev`, and seems to have moved some flags around.

First, I need to confirm the change itself. They've made `vxlan->cfg` dynamic and RCU-protected, so the old direct access is gone. `vxlan->default_dst` is also deprecated. Now, accesses use `rcu_dereference` and `rtnl_dereference`. In `vxlan_dev_configure` and `vxlan_changelink`, the allocation is `kmemdup` and uses `rcu_assign_pointer`. It cleans up the old allocation with `kfree_rcu` and also frees the `vxlan->cfg` in `vxlan_free_dev`. Okay, the mechanics of it seem reasonable, assuming they've got their RCU usage correct.

The crux of my concern: Can any of these changes introduce uninitialized memory leaks? Is there user-space copying? Uninitialized padding? Let's dig in. I see `vxlan_fill_info` looks like a key place to start. In the section I'm looking at, `cfg` is dereferenced and used to fill a `ports` struct, which is then put into the skb with `nla_put`. This is where I want to pay attention! Wait, the code uses `cfg->port_min` and `cfg->port_max` after declaring `const struct vxlan_config *cfg;`. *Where is this initialized*? The use of `htons` is a good sign, but doesn't erase any initialization issues. Wait... what's `struct ifla_vxlan_port_range`? Let me see what that data structure looks like to make sure there's no subtle issue there. I need to make sure the members I'm seeing being copied are initialized. Let's see...



12/3 2026/09/07 02:23 tool grepper
0m Args:
{
  "Expression": "struct ifla_vxlan_port_range"
}

Results:
{
  "Output": "drivers/net/vxlan/vxlan_core.c=3521=static const struct nla_policy vxlan_policy[IFLA_VXLAN_MAX + 1] = {\n--\ndrivers/net/vxlan/vxlan_core.c-3534-\t[IFLA_VXLAN_LIMIT]\t= { .type = NLA_U32 },\ndrivers/net/vxlan/vxlan_core.c:3535:\t[IFLA_VXLAN_PORT_RANGE] = { .len  = sizeof(struct ifla_vxlan_port_range) },\ndrivers/net/vxlan/vxlan_core.c-3536-\t[IFLA_VXLAN_PROXY]\t= { .type = NLA_U8 },\n--\ndrivers/net/vxlan/vxlan_core.c=3559=static int vxlan_validate(struct nlattr *tb[], struct nlattr *data[],\n--\ndrivers/net/vxlan/vxlan_core.c-3602-\tif (data[IFLA_VXLAN_PORT_RANGE]) {\ndrivers/net/vxlan/vxlan_core.c:3603:\t\tconst struct ifla_vxlan_port_range *p\ndrivers/net/vxlan/vxlan_core.c-3604-\t\t\t= nla_data(data[IFLA_VXLAN_PORT_RANGE]);\n--\ndrivers/net/vxlan/vxlan_core.c=4186=static int vxlan_nl2conf(struct nlattr *tb[], struct nlattr *data[],\n--\ndrivers/net/vxlan/vxlan_core.c-4352-\t\tif (!changelink) {\ndrivers/net/vxlan/vxlan_core.c:4353:\t\t\tconst struct ifla_vxlan_port_range *p\ndrivers/net/vxlan/vxlan_core.c-4354-\t\t\t\t= nla_data(data[IFLA_VXLAN_PORT_RANGE]);\n--\ndrivers/net/vxlan/vxlan_core.c=4672=static size_t vxlan_get_size(const struct net_device *dev)\n--\ndrivers/net/vxlan/vxlan_core.c-4699-\t\t/* IFLA_VXLAN_PORT_RANGE */\ndrivers/net/vxlan/vxlan_core.c:4700:\t\tnla_total_size(sizeof(struct ifla_vxlan_port_range)) +\ndrivers/net/vxlan/vxlan_core.c-4701-\t\tnla_total_size(0) + /* IFLA_VXLAN_GBP */\n--\ndrivers/net/vxlan/vxlan_core.c=4710=static int vxlan_fill_info(struct sk_buff *skb, const struct net_device *dev)\n--\ndrivers/net/vxlan/vxlan_core.c-4712-\tconst struct vxlan_dev *vxlan = netdev_priv(dev);\ndrivers/net/vxlan/vxlan_core.c:4713:\tstruct ifla_vxlan_port_range ports;\ndrivers/net/vxlan/vxlan_core.c-4714-\tconst struct vxlan_config *cfg;\n--\ninclude/uapi/linux/if_link.h=1430=enum {\n--\ninclude/uapi/linux/if_link.h-1469-\ninclude/uapi/linux/if_link.h:1470:struct ifla_vxlan_port_range {\ninclude/uapi/linux/if_link.h-1471-\t__be16\tlow;\n--\ntools/include/uapi/linux/if_link.h=1365=enum {\n--\ntools/include/uapi/linux/if_link.h-1402-\ntools/include/uapi/linux/if_link.h:1403:struct ifla_vxlan_port_range {\ntools/include/uapi/linux/if_link.h-1404-\t__be16\tlow;\n"
}

13/3 2026/09/07 02:23 llm kmsan-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 46964
							output: 41
							thoughts: 31
						

Thoughts:
Here's my attempt at summarizing the thought process, tailored for an expert audience:

**Examining the VXLAN Port Range Structure**

Okay, let's dive right in. My first thought is to verify the definition of `struct ifla_vxlan_port_range`. This struct, residing in `include/uapi/linux/if_link.h`, is critical for specifying the port ranges used by VXLAN interfaces. I need to refresh my memory on its exact members and their implications. I suspect I might be dealing with a change in the kernel or perhaps a subtle bug related to port range handling. The `uapi` directory suggests we're talking about a user-space interface, so the goal is to see how the port range information is formatted and what implications it has. This information is key for debugging or extending VXLAN functionality.



14/3 2026/09/07 02:23 tool read-file
0m Args:
{
  "File": "include/uapi/linux/if_link.h",
  "FirstLine": 1468,
  "LineCount": 10
}

Results:
{
  "Contents": "1468:\t#define IFLA_VXLAN_MAX\t(__IFLA_VXLAN_MAX - 1)\n1469:\t\n1470:\tstruct ifla_vxlan_port_range {\n1471:\t\t__be16\tlow;\n1472:\t\t__be16\thigh;\n1473:\t};\n1474:\t\n1475:\tenum ifla_vxlan_df {\n1476:\t\tVXLAN_DF_UNSET = 0,\n1477:\t\tVXLAN_DF_SET,\n"
}

15/3 2026/09/07 02:23 llm kmsan-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 47373
							output: 25
							thoughts: 440
						

Thoughts:
**Analyzing VXLAN Port Range and mlxsw Code**

Okay, so I'm looking at this `struct ifla_vxlan_port_range`. It's pretty straightforward, just two `__be16` fields, `low` and `high`, totaling four bytes with no padding, which makes sense. I see both `ports.low` and `ports.high` are initialized before the `nla_put` call, which is as expected.

Now, let's dive into the `mlxsw_sp` code, specifically `mlxsw_sp_bridge_vlan_aware_vxlan_join` and `mlxsw_sp_bridge_8021d_vxlan_join`. I notice a diff here. It looks like the `struct mlxsw_sp_nve_params` is initialized differently. In the older version it's initialized on declaration:

```c
struct mlxsw_sp_nve_params params = {
    .type = MLXSW_SP_NVE_TYPE_VXLAN,
    .vni = vxlan->cfg.vni,
    .dev = vxlan_dev,
    .ethertype = ethertype,
};
```
But in the newer version, it's declared and then initialized separately:

```c
struct mlxsw_sp_nve_params params;
const struct vxlan_config *cfg;

cfg = rtnl_dereference(vxlan->cfg);
params.type = MLXSW_SP_NVE_TYPE_VXLAN;
params.vni = cfg->vni;
params.dev = vxlan_dev;
params.ethertype = ethertype;
```

This change is interesting, particularly the introduction of `rtnl_dereference(vxlan->cfg)`. I should really check the definition of `struct mlxsw_sp_nve_params` to understand this better, to see if anything else is going on under the hood. It seems to be related to safe access to the `vxlan->cfg` pointer, potentially due to locking or memory management considerations. Need to investigate further.



16/3 2026/09/07 02:23 tool grepper
0m Args:
{
  "Expression": "struct mlxsw_sp_nve_params"
}

Results:
{
  "Output": "drivers/net/ethernet/mellanox/mlxsw/spectrum.h=1352=extern const struct mlxsw_sp_mr_tcam_ops mlxsw_sp2_mr_tcam_ops;\n--\ndrivers/net/ethernet/mellanox/mlxsw/spectrum.h-1354-/* spectrum_nve.c */\ndrivers/net/ethernet/mellanox/mlxsw/spectrum.h:1355:struct mlxsw_sp_nve_params {\ndrivers/net/ethernet/mellanox/mlxsw/spectrum.h-1356-\tenum mlxsw_sp_nve_type type;\n--\ndrivers/net/ethernet/mellanox/mlxsw/spectrum.h=1387=int mlxsw_sp_nve_fid_enable(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fid *fid,\ndrivers/net/ethernet/mellanox/mlxsw/spectrum.h:1388:\t\t\t    struct mlxsw_sp_nve_params *params,\ndrivers/net/ethernet/mellanox/mlxsw/spectrum.h-1389-\t\t\t    struct netlink_ext_ack *extack);\n--\ndrivers/net/ethernet/mellanox/mlxsw/spectrum_nve.c=935=int mlxsw_sp_nve_fid_enable(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fid *fid,\ndrivers/net/ethernet/mellanox/mlxsw/spectrum_nve.c:936:\t\t\t    struct mlxsw_sp_nve_params *params,\ndrivers/net/ethernet/mellanox/mlxsw/spectrum_nve.c-937-\t\t\t    struct netlink_ext_ack *extack)\n--\ndrivers/net/ethernet/mellanox/mlxsw/spectrum_nve.h=36=struct mlxsw_sp_nve_ops {\n--\ndrivers/net/ethernet/mellanox/mlxsw/spectrum_nve.h-38-\tbool (*can_offload)(const struct mlxsw_sp_nve *nve,\ndrivers/net/ethernet/mellanox/mlxsw/spectrum_nve.h:39:\t\t\t    const struct mlxsw_sp_nve_params *params,\ndrivers/net/ethernet/mellanox/mlxsw/spectrum_nve.h-40-\t\t\t    struct netlink_ext_ack *extack);\ndrivers/net/ethernet/mellanox/mlxsw/spectrum_nve.h-41-\tvoid (*nve_config)(const struct mlxsw_sp_nve *nve,\ndrivers/net/ethernet/mellanox/mlxsw/spectrum_nve.h:42:\t\t\t   const struct mlxsw_sp_nve_params *params,\ndrivers/net/ethernet/mellanox/mlxsw/spectrum_nve.h-43-\t\t\t   struct mlxsw_sp_nve_config *config);\n--\ndrivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c=58=static bool mlxsw_sp_nve_vxlan_can_offload(const struct mlxsw_sp_nve *nve,\ndrivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c:59:\t\t\t\t\t   const struct mlxsw_sp_nve_params *params,\ndrivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c-60-\t\t\t\t\t   struct netlink_ext_ack *extack)\n--\ndrivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c=122=static bool mlxsw_sp1_nve_vxlan_can_offload(const struct mlxsw_sp_nve *nve,\ndrivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c:123:\t\t\t\t\t    const struct mlxsw_sp_nve_params *params,\ndrivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c-124-\t\t\t\t\t    struct netlink_ext_ack *extack)\n--\ndrivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c=150=static void mlxsw_sp_nve_vxlan_config(const struct mlxsw_sp_nve *nve,\ndrivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c:151:\t\t\t\t      const struct mlxsw_sp_nve_params *params,\ndrivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c-152-\t\t\t\t      struct mlxsw_sp_nve_config *config)\n--\ndrivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c=2509=mlxsw_sp_bridge_vlan_aware_vxlan_join(struct mlxsw_sp_bridge_device *bridge_device,\n--\ndrivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c-2515-\tstruct vxlan_dev *vxlan = netdev_priv(vxlan_dev);\ndrivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c:2516:\tstruct mlxsw_sp_nve_params params;\ndrivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c-2517-\tconst struct vxlan_config *cfg;\n--\ndrivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c=2703=mlxsw_sp_bridge_8021d_vxlan_join(struct mlxsw_sp_bridge_device *bridge_device,\n--\ndrivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c-2708-\tstruct vxlan_dev *vxlan = netdev_priv(vxlan_dev);\ndrivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c:2709:\tstruct mlxsw_sp_nve_params params;\ndrivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c-2710-\tconst struct vxlan_config *cfg;\n"
}

17/3 2026/09/07 02:23 llm kmsan-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 49894
							output: 46
							thoughts: 40
						

Thoughts:
Okay, let's dive into this. Here's my thought process on dissecting the `mlxsw_sp_nve_params` struct:

**Examining the mlxsw_sp_nve_params Struct**

Right, so I'm thinking, let's get a handle on the specifics of this `mlxsw_sp_nve_params` struct, specifically where it's defined. My first step is to locate it within the Mellanox driver's code base, and it looks like it's defined in `drivers/net/ethernet/mellanox/mlxsw/spectrum.h`. Now, I need to get my head around what each field within the struct represents in terms of its role in the system. I need to understand how it's used to configure or control the Network Virtualization Edge (NVE) functionality in the Mellanox Spectrum switch. I'll be looking for things like encapsulation types, VLAN IDs, VXLAN network identifiers (VNIs), MAC address settings, and any parameters related to tunnel endpoints. I'm especially interested in how these parameters map to the hardware's internal registers or data structures. I'll need to correlate these with the various NVE-related functions in the driver to get a good understanding of how it's all used. This is going to be my key to understanding the NVE implementation.



18/3 2026/09/07 02:24 tool read-file
0m Args:
{
  "File": "drivers/net/ethernet/mellanox/mlxsw/spectrum.h",
  "FirstLine": 1354,
  "LineCount": 15
}

Results:
{
  "Contents": "1354:\t/* spectrum_nve.c */\n1355:\tstruct mlxsw_sp_nve_params {\n1356:\t\tenum mlxsw_sp_nve_type type;\n1357:\t\t__be32 vni;\n1358:\t\tconst struct net_device *dev;\n1359:\t\tu16 ethertype;\n1360:\t};\n1361:\t\n1362:\textern const struct mlxsw_sp_nve_ops *mlxsw_sp1_nve_ops_arr[];\n1363:\textern const struct mlxsw_sp_nve_ops *mlxsw_sp2_nve_ops_arr[];\n1364:\t\n1365:\tint mlxsw_sp_nve_learned_ip_resolve(struct mlxsw_sp *mlxsw_sp, u32 uip,\n1366:\t\t\t\t\t    enum mlxsw_sp_l3proto proto,\n1367:\t\t\t\t\t    union mlxsw_sp_l3addr *addr);\n1368:\tint mlxsw_sp_nve_flood_ip_add(struct mlxsw_sp *mlxsw_sp,\n"
}

19/3 2026/09/07 02:24 llm kmsan-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 50588
							output: 42
							thoughts: 235
						

Thoughts:
**Analyzing the mlxsw_sp VXLAN Configuration**

Okay, let's break this down. My initial observation is that the `struct mlxsw_sp_nve_params` is rather sparse. It only contains `type`, `vni`, `dev`, and `ethertype`. That's it. Interesting. Now, looking at how it's used within `mlxsw_sp_bridge_vlan_aware_vxlan_join` and `mlxsw_sp_bridge_8021d_vxlan_join`, I can see that *all* of those fields are explicitly set. That's good, confirms my understanding. And, more importantly, this struct is purely internal; it's passed around internally through functions like `mlxsw_sp_nve_fid_enable`. No user-space interaction, which is a key point to note.

Now, I need to broaden the scope. Let's look at the broader VXLAN picture. Let's shift our focus to the standard VXLAN operations, so I need to check out what happens in `vxlan_dev_create` and `vxlan_dev_configure`. Aha! `new_cfg = kmemdup(conf, sizeof(*conf), GFP_KERNEL);` is what's done here. And `conf` is initialized using `memset` via `vxlan_nl2conf`. This indicates a standard kernel configuration approach.

Time to dive deeper into the standard VXLAN code. Let's locate `vxlan_nl2conf` in `drivers/net/vxlan/vxlan_core.c` and confirm my expectations.



20/3 2026/09/07 02:24 tool read-file
0m Args:
{
  "File": "drivers/net/vxlan/vxlan_core.c",
  "FirstLine": 4186,
  "LineCount": 40
}

Results:
{
  "Contents": "4186:\tstatic int vxlan_nl2conf(struct nlattr *tb[], struct nlattr *data[],\n4187:\t\t\t\t struct net_device *dev, struct vxlan_config *conf,\n4188:\t\t\t\t bool changelink, struct netlink_ext_ack *extack)\n4189:\t{\n4190:\t\tstruct vxlanhdr used_bits = {\n4191:\t\t\t.vx_flags = VXLAN_HF_VNI,\n4192:\t\t\t.vx_vni = VXLAN_VNI_MASK,\n4193:\t\t};\n4194:\t\tstruct vxlan_dev *vxlan = netdev_priv(dev);\n4195:\t\tint err = 0;\n4196:\t\n4197:\t\tmemset(conf, 0, sizeof(*conf));\n4198:\t\n4199:\t\t/* if changelink operation, start with old existing cfg */\n4200:\t\tif (changelink) {\n4201:\t\t\tconst struct vxlan_config *cfg = rtnl_dereference(vxlan-\u003ecfg);\n4202:\t\n4203:\t\t\tif (cfg)\n4204:\t\t\t\tmemcpy(conf, cfg, sizeof(*conf));\n4205:\t\t}\n4206:\t\n4207:\t\tif (data[IFLA_VXLAN_ID]) {\n4208:\t\t\t__be32 vni = cpu_to_be32(nla_get_u32(data[IFLA_VXLAN_ID]));\n4209:\t\n4210:\t\t\tif (changelink \u0026\u0026 (vni != conf-\u003evni)) {\n4211:\t\t\t\tNL_SET_ERR_MSG_ATTR(extack, tb[IFLA_VXLAN_ID], \"Cannot change VNI\");\n4212:\t\t\t\treturn -EOPNOTSUPP;\n4213:\t\t\t}\n4214:\t\t\tconf-\u003evni = vni;\n4215:\t\t}\n4216:\t\n4217:\t\tif (data[IFLA_VXLAN_GROUP]) {\n4218:\t\t\tif (changelink \u0026\u0026 (conf-\u003eremote_ip.sa.sa_family != AF_INET)) {\n4219:\t\t\t\tNL_SET_ERR_MSG_ATTR(extack, tb[IFLA_VXLAN_GROUP], \"New group address family does not match old group\");\n4220:\t\t\t\treturn -EOPNOTSUPP;\n4221:\t\t\t}\n4222:\t\n4223:\t\t\tconf-\u003eremote_ip.sin.sin_addr.s_addr = nla_get_in_addr(data[IFLA_VXLAN_GROUP]);\n4224:\t\t\tconf-\u003eremote_ip.sa.sa_family = AF_INET;\n4225:\t\t} else if (data[IFLA_VXLAN_GROUP6]) {\n"
}

21/3 2026/09/07 02:24 llm kmsan-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 51938
							output: 254
							thoughts: 1357
						

Thoughts:
**Analysis of VXLAN Configuration Refactoring for KMSAN Considerations**

Okay, so I've been digging into this VXLAN driver refactoring. The primary goal seems to be making `vxlan->cfg` dynamic and RCU-managed, which is a good move for concurrent access.  The current approach has multiple initialization patterns.  `memset(conf, 0, sizeof(*conf))` zeros out `conf`, and then some of the functions initialize local data structures directly using `= {}`, and other functions initialize variables such as `ifindex` to `0` explicitly.

Now, my main concern, as always, is whether these changes introduce any new uninitialized memory reads or leaks that KMSAN would catch. This seems unlikely. I checked several key areas: `vxlan_xmit_one`'s metadata struct `_md`, `mlx5e_tc_tun_get_remote_ifindex`, and `vxlan_fdb_get` for any potential issues in how configuration data is accessed. I also scanned a list of all affected files, just to get a good overview.

The core refactoring uses RCU reads of the config which is a point of attention, though `cfg` is initialized to prevent any read of uninitialized data.  It cleans up redundant data (like `default_dst`) and moves the `VXLAN_F_MDB` flag.  No immediate red flags there.

I've carefully examined various data structures to see if anything new might be uninitialized. For example, `ports` in `vxlan_fill_info`, `params` in `mlxsw_sp_bridge_*`, and `new_cfg`, which is created via `kmemdup` from a zeroed `conf` struct.  I am satisfied that all relevant structures are initialized before use, and no new uninitialized stack buffers, heap allocations, or struct padding leaks are introduced, and no conditional jumps rely on uninitialized variables.

Therefore, since the patch *avoids* the introduction of uninitialized memory risks, and any potential memory safety issues are likely caught by existing mechanisms such as KASAN and lockdep, I'm confident in setting `NeedsKMSAN` to false. This refactoring is primarily about synchronization and configuration management, and the existing kernel tools should be sufficient to catch any remaining memory errors. It's time to set the results!



22/3 2026/09/07 02:24 tool set-results
0m Args:
{
  "KMSANReasoning": "The patch refactors VXLAN configuration management by converting `vxlan-\u003ecfg` from an embedded struct to a dynamically-allocated, RCU-protected pointer (`struct vxlan_config __rcu *cfg`), removing duplicate fields previously mirrored in `vxlan-\u003edefault_dst`, and moving the MDB flag to `vxlan-\u003eflags`.\n\nAll new allocations (`kmemdup`) operate on fully initialized structures (cleared with `memset` in `vxlan_nl2conf`), and local stack structures (`struct ifla_vxlan_port_range ports`, `struct mlxsw_sp_nve_params params`, `struct vxlan_metadata _md`) have all their fields fully initialized before use. No uninitialized struct padding or uninitialized memory is read or exposed across security boundaries (e.g. copied to user space via netlink).\n\nAny potential bugs in this patch pertain to pointer lifetime, concurrency, RCU synchronization, or use-after-free, which are effectively detected by KASAN and LOCKDEP rather than KMSAN. Dedicated KMSAN fuzzing is not required.",
  "NeedsKMSAN": false
}

Results:
{
  "KMSANReasoning": "The patch refactors VXLAN configuration management by converting `vxlan-\u003ecfg` from an embedded struct to a dynamically-allocated, RCU-protected pointer (`struct vxlan_config __rcu *cfg`), removing duplicate fields previously mirrored in `vxlan-\u003edefault_dst`, and moving the MDB flag to `vxlan-\u003eflags`.\n\nAll new allocations (`kmemdup`) operate on fully initialized structures (cleared with `memset` in `vxlan_nl2conf`), and local stack structures (`struct ifla_vxlan_port_range ports`, `struct mlxsw_sp_nve_params params`, `struct vxlan_metadata _md`) have all their fields fully initialized before use. No uninitialized struct padding or uninitialized memory is read or exposed across security boundaries (e.g. copied to user space via netlink).\n\nAny potential bugs in this patch pertain to pointer lifetime, concurrency, RCU synchronization, or use-after-free, which are effectively detected by KASAN and LOCKDEP rather than KMSAN. Dedicated KMSAN fuzzing is not required.",
  "NeedsKMSAN": false
}

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)