Currently arp/neigh_reduce read neigh ha directly which can lead to
partial reads while the neigh is being updated. Use neigh_ha_snapshot to
take a stable snapshot of the address similar to route_shortcircuit which
already does the right thing.

Fixes: e4f67addf158 ("add DOVE extensions for VXLAN")
Fixes: f564f45c4518 ("vxlan: add ipv6 proxy support")
Signed-off-by: Nikolay Aleksandrov <[email protected]>
---
v2: - use ETH_ALEN instead of MAX_ADDR_LEN, the bridge devices all use
      ETH_ALEN and vxlan allows arp/nd reduce only when not in raw/gpe
      so it also always uses ETH_ALEN
    - align ha to 2 bytes because ether_addr_copy() expects it (Sashiko)

 drivers/net/vxlan/vxlan_core.c | 20 +++++++++++++-------
 1 file changed, 13 insertions(+), 7 deletions(-)

diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c
index 824144bb7774..a94168f7a18e 100644
--- a/drivers/net/vxlan/vxlan_core.c
+++ b/drivers/net/vxlan/vxlan_core.c
@@ -1881,6 +1881,7 @@ static int arp_reduce(struct net_device *dev, struct 
sk_buff *skb, __be32 vni)
 
        if (n) {
                struct vxlan_rdst *rdst = NULL;
+               u8 ha[ETH_ALEN] __aligned(2);
                struct vxlan_fdb *f;
                struct sk_buff  *reply;
 
@@ -1889,8 +1890,10 @@ static int arp_reduce(struct net_device *dev, struct 
sk_buff *skb, __be32 vni)
                        goto out;
                }
 
+               neigh_ha_snapshot(ha, n, n->dev);
+
                rcu_read_lock();
-               f = vxlan_find_mac_tx(vxlan, n->ha, vni);
+               f = vxlan_find_mac_tx(vxlan, ha, vni);
                if (f)
                        rdst = first_remote_rcu(f);
                if (rdst && vxlan_addr_any(&rdst->remote_ip)) {
@@ -1902,7 +1905,7 @@ static int arp_reduce(struct net_device *dev, struct 
sk_buff *skb, __be32 vni)
                rcu_read_unlock();
 
                reply = arp_create(ARPOP_REPLY, ETH_P_ARP, sip, dev, tip, sha,
-                               n->ha, sha);
+                                  ha, sha);
 
                neigh_release(n);
 
@@ -1935,7 +1938,8 @@ static int arp_reduce(struct net_device *dev, struct 
sk_buff *skb, __be32 vni)
 
 #if IS_ENABLED(CONFIG_IPV6)
 static struct sk_buff *vxlan_na_create(struct sk_buff *request,
-       struct neighbour *n, bool isrouter)
+                                      struct neighbour *n, u8 *ha,
+                                      bool isrouter)
 {
        struct net_device *dev = request->dev;
        struct sk_buff *reply;
@@ -1981,7 +1985,7 @@ static struct sk_buff *vxlan_na_create(struct sk_buff 
*request,
 
        /* Ethernet header */
        ether_addr_copy(eth_hdr(reply)->h_dest, daddr);
-       ether_addr_copy(eth_hdr(reply)->h_source, n->ha);
+       ether_addr_copy(eth_hdr(reply)->h_source, ha);
        eth_hdr(reply)->h_proto = htons(ETH_P_IPV6);
        reply->protocol = htons(ETH_P_IPV6);
 
@@ -2010,7 +2014,7 @@ static struct sk_buff *vxlan_na_create(struct sk_buff 
*request,
        na->icmph.icmp6_override = 1;
        na->icmph.icmp6_solicited = 1;
        na->target = ns->target;
-       ether_addr_copy(&na->opt[2], n->ha);
+       ether_addr_copy(&na->opt[2], ha);
        na->opt[0] = ND_OPT_TARGET_LL_ADDR;
        na->opt[1] = na_olen >> 3;
 
@@ -2051,6 +2055,7 @@ static int neigh_reduce(struct net_device *dev, struct 
sk_buff *skb, __be32 vni)
 
        if (n) {
                struct vxlan_rdst *rdst = NULL;
+               u8 ha[ETH_ALEN] __aligned(2);
                struct vxlan_fdb *f;
                struct sk_buff *reply;
 
@@ -2059,7 +2064,8 @@ static int neigh_reduce(struct net_device *dev, struct 
sk_buff *skb, __be32 vni)
                        goto out;
                }
 
-               f = vxlan_find_mac_tx(vxlan, n->ha, vni);
+               neigh_ha_snapshot(ha, n, n->dev);
+               f = vxlan_find_mac_tx(vxlan, ha, vni);
                if (f)
                        rdst = first_remote_rcu(f);
                if (rdst && vxlan_addr_any(&rdst->remote_ip)) {
@@ -2068,7 +2074,7 @@ static int neigh_reduce(struct net_device *dev, struct 
sk_buff *skb, __be32 vni)
                        goto out;
                }
 
-               reply = vxlan_na_create(skb, n,
+               reply = vxlan_na_create(skb, n, ha,
                                        !!(f ? f->flags & NTF_ROUTER : 0));
 
                neigh_release(n);
-- 
2.47.3


Reply via email to