On 20/05/2020 07:33, Roopa Prabhu wrote:
> From: Roopa Prabhu <ro...@cumulusnetworks.com>
> 
> Todays vxlan mac fdb entries can point to multiple remote
> ips (rdsts) with the sole purpose of replicating
> broadcast-multicast and unknown unicast packets to those remote ips.
> 
> E-VPN multihoming [1,2,3] requires bridged vxlan traffic to be
> load balanced to remote switches (vteps) belonging to the
> same multi-homed ethernet segment (E-VPN multihoming is analogous
> to multi-homed LAG implementations, but with the inter-switch
> peerlink replaced with a vxlan tunnel). In other words it needs
> support for mac ecmp. Furthermore, for faster convergence, E-VPN
> multihoming needs the ability to update fdb ecmp nexthops independent
> of the fdb entries.
> 
> New route nexthop API is perfect for this usecase.
> This patch extends the vxlan fdb code to take a nexthop id
> pointing to an ecmp nexthop group.
> 
> Changes include:
> - New NDA_NH_ID attribute for fdbs
> - Use the newly added fdb nexthop groups
> - makes vxlan rdsts and nexthop handling code mutually
>   exclusive
> - since this is a new use-case and the requirement is for ecmp
> nexthop groups, the fdb add and update path checks that the
> nexthop is really an ecmp nexthop group. This check can be relaxed
> in the future, if we want to introduce replication fdb nexthop groups
> and allow its use in lieu of current rdst lists.
> - fdb update requests with nexthop id's only allowed for existing
> fdb's that have nexthop id's
> - learning will not override an existing fdb entry with nexthop
> group
> - I have wrapped the switchdev offload code around the presence of
> rdst
> - I think there is scope for simplyfing vxlan_xmit_one: Will see
> what I can do before the non-RFC version
> 
> [1] E-VPN RFC https://tools.ietf.org/html/rfc7432
> [2] E-VPN with vxlan https://tools.ietf.org/html/rfc8365
> [3] 
> http://vger.kernel.org/lpc_net2018_talks/scaling_bridge_fdb_database_slidesV3.pdf
> 
> Includes a null check fix in vxlan_xmit from Nikolay
> 
> v2 - Fixed build issue:
> Reported-by: kbuild test robot <l...@intel.com>
> Signed-off-by: Roopa Prabhu <ro...@cumulusnetworks.com>
> ---
>  drivers/net/vxlan.c            | 301 
> +++++++++++++++++++++++++++++++++--------
>  include/net/vxlan.h            |  25 ++++
>  include/uapi/linux/neighbour.h |   1 +
>  net/core/neighbour.c           |   2 +
>  4 files changed, 270 insertions(+), 59 deletions(-)
> 

Overall looks good, just one more potential issue with the xmit below.

> diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
> index a5b415f..380d887 100644
> --- a/drivers/net/vxlan.c
> +++ b/drivers/net/vxlan.c
> @@ -26,6 +26,7 @@
>  #include <net/netns/generic.h>
>  #include <net/tun_proto.h>
>  #include <net/vxlan.h>
> +#include <net/nexthop.h>
>  
>  #if IS_ENABLED(CONFIG_IPV6)
>  #include <net/ip6_tunnel.h>
> @@ -78,6 +79,8 @@ struct vxlan_fdb {
>       u16               state;        /* see ndm_state */
>       __be32            vni;
>       u16               flags;        /* see ndm_flags and below */
> +     struct list_head  nh_list;
> +     struct nexthop __rcu *nh;
>  };
>  
>  #define NTF_VXLAN_ADDED_BY_USER 0x100
> @@ -174,11 +177,15 @@ static inline struct hlist_head *vs_head(struct net 
> *net, __be16 port)
>   */
>  static inline struct vxlan_rdst *first_remote_rcu(struct vxlan_fdb *fdb)
>  {
> +     if (rcu_access_pointer(fdb->nh))
> +             return NULL;
>       return list_entry_rcu(fdb->remotes.next, struct vxlan_rdst, list);
>  }
>  
>  static inline struct vxlan_rdst *first_remote_rtnl(struct vxlan_fdb *fdb)
>  {
> +     if (rcu_access_pointer(fdb->nh))
> +             return NULL;
>       return list_first_entry(&fdb->remotes, struct vxlan_rdst, list);
>  }
>  
> @@ -251,9 +258,10 @@ static int vxlan_fdb_info(struct sk_buff *skb, struct 
> vxlan_dev *vxlan,
>  {
>       unsigned long now = jiffies;
>       struct nda_cacheinfo ci;
> +     bool send_ip, send_eth;
>       struct nlmsghdr *nlh;
> +     struct nexthop *nh;
>       struct ndmsg *ndm;
> -     bool send_ip, send_eth;
>  
>       nlh = nlmsg_put(skb, portid, seq, type, sizeof(*ndm), flags);
>       if (nlh == NULL)
> @@ -264,16 +272,21 @@ static int vxlan_fdb_info(struct sk_buff *skb, struct 
> vxlan_dev *vxlan,
>  
>       send_eth = send_ip = true;
>  
> +     nh = rcu_dereference_rtnl(fdb->nh);
>       if (type == RTM_GETNEIGH) {
> -             send_ip = !vxlan_addr_any(&rdst->remote_ip);
> +             if (rdst) {
> +                     send_ip = !vxlan_addr_any(&rdst->remote_ip);
> +                     ndm->ndm_family = send_ip ? 
> rdst->remote_ip.sa.sa_family : AF_INET;
> +             } else if (nh) {
> +                     ndm->ndm_family = nexthop_get_family(nh);
> +             }
>               send_eth = !is_zero_ether_addr(fdb->eth_addr);
> -             ndm->ndm_family = send_ip ? rdst->remote_ip.sa.sa_family : 
> AF_INET;
>       } else
>               ndm->ndm_family = AF_BRIDGE;
>       ndm->ndm_state = fdb->state;
>       ndm->ndm_ifindex = vxlan->dev->ifindex;
>       ndm->ndm_flags = fdb->flags;
> -     if (rdst->offloaded)
> +     if (rdst && rdst->offloaded)
>               ndm->ndm_flags |= NTF_OFFLOADED;
>       ndm->ndm_type = RTN_UNICAST;
>  
> @@ -284,23 +297,30 @@ static int vxlan_fdb_info(struct sk_buff *skb, struct 
> vxlan_dev *vxlan,
>  
>       if (send_eth && nla_put(skb, NDA_LLADDR, ETH_ALEN, &fdb->eth_addr))
>               goto nla_put_failure;
> +     if (nh) {
> +             if (nla_put_u32(skb, NDA_NH_ID, nh->id))
> +                     goto nla_put_failure;
> +     } else if (rdst) {
> +             if (send_ip && vxlan_nla_put_addr(skb, NDA_DST,
> +                                               &rdst->remote_ip))
> +                     goto nla_put_failure;
> +
> +             if (rdst->remote_port &&
> +                 rdst->remote_port != vxlan->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 &&
> +                 nla_put_u32(skb, NDA_VNI, be32_to_cpu(rdst->remote_vni)))
> +                     goto nla_put_failure;
> +             if (rdst->remote_ifindex &&
> +                 nla_put_u32(skb, NDA_IFINDEX, rdst->remote_ifindex))
> +                     goto nla_put_failure;
> +     }
>  
> -     if (send_ip && vxlan_nla_put_addr(skb, NDA_DST, &rdst->remote_ip))
> -             goto nla_put_failure;
> -
> -     if (rdst->remote_port && rdst->remote_port != vxlan->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 &&
> -         nla_put_u32(skb, NDA_VNI, be32_to_cpu(rdst->remote_vni)))
> -             goto nla_put_failure;
>       if ((vxlan->cfg.flags & VXLAN_F_COLLECT_METADATA) && fdb->vni &&
>           nla_put_u32(skb, NDA_SRC_VNI,
>                       be32_to_cpu(fdb->vni)))
>               goto nla_put_failure;
> -     if (rdst->remote_ifindex &&
> -         nla_put_u32(skb, NDA_IFINDEX, rdst->remote_ifindex))
> -             goto nla_put_failure;
>  
>       ci.ndm_used      = jiffies_to_clock_t(now - fdb->used);
>       ci.ndm_confirmed = 0;
> @@ -401,7 +421,7 @@ static int vxlan_fdb_notify(struct vxlan_dev *vxlan, 
> struct vxlan_fdb *fdb,
>  {
>       int err;
>  
> -     if (swdev_notify) {
> +     if (swdev_notify && rd) {
>               switch (type) {
>               case RTM_NEWNEIGH:
>                       err = vxlan_fdb_switchdev_call_notifiers(vxlan, fdb, rd,
> @@ -805,6 +825,8 @@ static struct vxlan_fdb *vxlan_fdb_alloc(const u8 *mac, 
> __u16 state,
>       f->flags = ndm_flags;
>       f->updated = f->used = jiffies;
>       f->vni = src_vni;
> +     f->nh = NULL;
> +     INIT_LIST_HEAD(&f->nh_list);
>       INIT_LIST_HEAD(&f->remotes);
>       memcpy(f->eth_addr, mac, ETH_ALEN);
>  
> @@ -819,11 +841,78 @@ static void vxlan_fdb_insert(struct vxlan_dev *vxlan, 
> const u8 *mac,
>                          vxlan_fdb_head(vxlan, mac, src_vni));
>  }
>  
> +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);
> +     struct nh_group *nhg;
> +     struct nexthop *nh;
> +     int err = -EINVAL;
> +
> +     if (old_nh && old_nh->id == nhid)
> +             return 0;
> +
> +     nh = nexthop_find_by_id(vxlan->net, nhid);
> +     if (!nh) {
> +             NL_SET_ERR_MSG(extack, "Nexthop id does not exist");
> +             goto err_inval;
> +     }
> +
> +     if (nh) {
> +             if (!nexthop_get(nh)) {
> +                     NL_SET_ERR_MSG(extack, "Nexthop has been deleted");
> +                     nh = NULL;
> +                     goto err_inval;
> +             }
> +             if (!nh->is_fdb_nh) {
> +                     NL_SET_ERR_MSG(extack, "Nexthop is not a fdb nexthop");
> +                     goto err_inval;
> +             }
> +
> +             if (!nh->is_group || !nh->nh_grp->mpath) {
> +                     NL_SET_ERR_MSG(extack, "Nexthop is not a multipath 
> group");
> +                     goto err_inval;
> +             }
> +
> +             /* check nexthop group family */
> +             nhg = rtnl_dereference(nh->nh_grp);
> +             switch (vxlan->default_dst.remote_ip.sa.sa_family) {
> +             case AF_INET:
> +                     if (!nhg->has_v4) {
> +                             err = -EAFNOSUPPORT;
> +                             NL_SET_ERR_MSG(extack, "Nexthop group family 
> not supported");
> +                             goto err_inval;
> +                     }
> +                     break;
> +             case AF_INET6:
> +                     if (nhg->has_v4) {
> +                             err = -EAFNOSUPPORT;
> +                             NL_SET_ERR_MSG(extack, "Nexthop group family 
> not supported");
> +                             goto err_inval;
> +                     }
> +             }
> +     }
> +
> +     if (old_nh) {
> +             list_del_rcu(&fdb->nh_list);
> +             nexthop_put(old_nh);
> +     }
> +     rcu_assign_pointer(fdb->nh, nh);
> +     list_add_tail_rcu(&fdb->nh_list, &nh->fdb_list);
> +     return 1;
> +
> +err_inval:
> +     if (nh)
> +             nexthop_put(nh);
> +     return err;
> +}
> +
>  static int vxlan_fdb_create(struct vxlan_dev *vxlan,
>                           const u8 *mac, union vxlan_addr *ip,
>                           __u16 state, __be16 port, __be32 src_vni,
>                           __be32 vni, __u32 ifindex, __u16 ndm_flags,
> -                         struct vxlan_fdb **fdb)
> +                         u32 nhid, struct vxlan_fdb **fdb,
> +                         struct netlink_ext_ack *extack)
>  {
>       struct vxlan_rdst *rd = NULL;
>       struct vxlan_fdb *f;
> @@ -838,20 +927,33 @@ static int vxlan_fdb_create(struct vxlan_dev *vxlan,
>       if (!f)
>               return -ENOMEM;
>  
> -     rc = vxlan_fdb_append(f, ip, port, vni, ifindex, &rd);
> -     if (rc < 0) {
> -             kfree(f);
> -             return rc;
> -     }
> +     if (nhid)
> +             rc = vxlan_fdb_nh_update(vxlan, f, nhid, extack);
> +     else
> +             rc = vxlan_fdb_append(f, ip, port, vni, ifindex, &rd);
> +     if (rc < 0)
> +             goto errout;
>  
>       *fdb = f;
>  
>       return 0;
> +
> +errout:
> +     kfree(f);
> +     return rc;
>  }
>  
>  static void __vxlan_fdb_free(struct vxlan_fdb *f)
>  {
>       struct vxlan_rdst *rd, *nd;
> +     struct nexthop *nh;
> +
> +     nh = rcu_dereference_raw(f->nh);
> +     if (nh) {
> +             rcu_assign_pointer(f->nh, NULL);
> +             list_del_rcu(&f->nh_list);
> +             nexthop_put(nh);
> +     }
>  
>       list_for_each_entry_safe(rd, nd, &f->remotes, list) {
>               dst_cache_destroy(&rd->dst_cache);
> @@ -875,10 +977,15 @@ static void vxlan_fdb_destroy(struct vxlan_dev *vxlan, 
> struct vxlan_fdb *f,
>       netdev_dbg(vxlan->dev, "delete %pM\n", f->eth_addr);
>  
>       --vxlan->addrcnt;
> -     if (do_notify)
> -             list_for_each_entry(rd, &f->remotes, list)
> -                     vxlan_fdb_notify(vxlan, f, rd, RTM_DELNEIGH,
> +     if (do_notify) {
> +             if (rcu_access_pointer(f->nh))
> +                     vxlan_fdb_notify(vxlan, f, NULL, RTM_DELNEIGH,
>                                        swdev_notify, NULL);
> +             else
> +                     list_for_each_entry(rd, &f->remotes, list)
> +                             vxlan_fdb_notify(vxlan, f, rd, RTM_DELNEIGH,
> +                                              swdev_notify, NULL);
> +     }
>  
>       hlist_del_rcu(&f->hlist);
>       call_rcu(&f->rcu, vxlan_fdb_free);
> @@ -897,7 +1004,7 @@ static int vxlan_fdb_update_existing(struct vxlan_dev 
> *vxlan,
>                                    __u16 state, __u16 flags,
>                                    __be16 port, __be32 vni,
>                                    __u32 ifindex, __u16 ndm_flags,
> -                                  struct vxlan_fdb *f,
> +                                  struct vxlan_fdb *f, u32 nhid,
>                                    bool swdev_notify,
>                                    struct netlink_ext_ack *extack)
>  {
> @@ -908,6 +1015,18 @@ static int vxlan_fdb_update_existing(struct vxlan_dev 
> *vxlan,
>       int rc = 0;
>       int err;
>  
> +     if (nhid && !rcu_access_pointer(f->nh)) {
> +             NL_SET_ERR_MSG(extack,
> +                            "Cannot replace an existing non nexthop fdb with 
> a nexthop");
> +             return -EOPNOTSUPP;
> +     }
> +
> +     if (nhid && (flags & NLM_F_APPEND)) {
> +             NL_SET_ERR_MSG(extack,
> +                            "Cannot append to a nexthop fdb");
> +             return -EOPNOTSUPP;
> +     }
> +
>       /* Do not allow an externally learned entry to take over an entry added
>        * by the user.
>        */
> @@ -929,10 +1048,18 @@ static int vxlan_fdb_update_existing(struct vxlan_dev 
> *vxlan,
>               /* Only change unicasts */
>               if (!(is_multicast_ether_addr(f->eth_addr) ||
>                     is_zero_ether_addr(f->eth_addr))) {
> -                     rc = vxlan_fdb_replace(f, ip, port, vni,
> -                                            ifindex, &oldrd);
> +                     if (nhid) {
> +                             rc = vxlan_fdb_nh_update(vxlan, f, nhid, 
> extack);
> +                             if (rc < 0)
> +                                     return rc;
> +                     } else {
> +                             rc = vxlan_fdb_replace(f, ip, port, vni,
> +                                                    ifindex, &oldrd);
> +                     }
>                       notify |= rc;
>               } else {
> +                     NL_SET_ERR_MSG(extack,
> +                            "Cannot replace non-unicast fdb entries");
>                       return -EOPNOTSUPP;
>               }
>       }
> @@ -962,6 +1089,8 @@ static int vxlan_fdb_update_existing(struct vxlan_dev 
> *vxlan,
>       return 0;
>  
>  err_notify:
> +     if (nhid)
> +             return err;
>       if ((flags & NLM_F_REPLACE) && rc)
>               *rd = oldrd;
>       else if ((flags & NLM_F_APPEND) && rc) {
> @@ -975,7 +1104,7 @@ static int vxlan_fdb_update_create(struct vxlan_dev 
> *vxlan,
>                                  const u8 *mac, union vxlan_addr *ip,
>                                  __u16 state, __u16 flags,
>                                  __be16 port, __be32 src_vni, __be32 vni,
> -                                __u32 ifindex, __u16 ndm_flags,
> +                                __u32 ifindex, __u16 ndm_flags, u32 nhid,
>                                  bool swdev_notify,
>                                  struct netlink_ext_ack *extack)
>  {
> @@ -990,7 +1119,7 @@ static int vxlan_fdb_update_create(struct vxlan_dev 
> *vxlan,
>  
>       netdev_dbg(vxlan->dev, "add %pM -> %pIS\n", mac, ip);
>       rc = vxlan_fdb_create(vxlan, mac, ip, state, port, src_vni,
> -                           vni, ifindex, fdb_flags, &f);
> +                           vni, ifindex, fdb_flags, nhid, &f, extack);
>       if (rc < 0)
>               return rc;
>  
> @@ -1012,7 +1141,7 @@ static int vxlan_fdb_update(struct vxlan_dev *vxlan,
>                           const u8 *mac, union vxlan_addr *ip,
>                           __u16 state, __u16 flags,
>                           __be16 port, __be32 src_vni, __be32 vni,
> -                         __u32 ifindex, __u16 ndm_flags,
> +                         __u32 ifindex, __u16 ndm_flags, u32 nhid,
>                           bool swdev_notify,
>                           struct netlink_ext_ack *extack)
>  {
> @@ -1028,14 +1157,15 @@ static int vxlan_fdb_update(struct vxlan_dev *vxlan,
>  
>               return vxlan_fdb_update_existing(vxlan, ip, state, flags, port,
>                                                vni, ifindex, ndm_flags, f,
> -                                              swdev_notify, extack);
> +                                              nhid, swdev_notify, extack);
>       } else {
>               if (!(flags & NLM_F_CREATE))
>                       return -ENOENT;
>  
>               return vxlan_fdb_update_create(vxlan, mac, ip, state, flags,
>                                              port, src_vni, vni, ifindex,
> -                                            ndm_flags, swdev_notify, extack);
> +                                            ndm_flags, nhid, swdev_notify,
> +                                            extack);
>       }
>  }
>  
> @@ -1049,7 +1179,7 @@ static void vxlan_fdb_dst_destroy(struct vxlan_dev 
> *vxlan, struct vxlan_fdb *f,
>  
>  static int vxlan_fdb_parse(struct nlattr *tb[], struct vxlan_dev *vxlan,
>                          union vxlan_addr *ip, __be16 *port, __be32 *src_vni,
> -                        __be32 *vni, u32 *ifindex)
> +                        __be32 *vni, u32 *ifindex, u32 *nhid)
>  {
>       struct net *net = dev_net(vxlan->dev);
>       int err;
> @@ -1109,6 +1239,11 @@ static int vxlan_fdb_parse(struct nlattr *tb[], struct 
> vxlan_dev *vxlan,
>               *ifindex = 0;
>       }
>  
> +     if (tb[NDA_NH_ID])
> +             *nhid = nla_get_u32(tb[NDA_NH_ID]);
> +     else
> +             *nhid = 0;
> +
>       return 0;
>  }
>  
> @@ -1123,7 +1258,7 @@ static int vxlan_fdb_add(struct ndmsg *ndm, struct 
> nlattr *tb[],
>       union vxlan_addr ip;
>       __be16 port;
>       __be32 src_vni, vni;
> -     u32 ifindex;
> +     u32 ifindex, nhid;
>       u32 hash_index;
>       int err;
>  
> @@ -1133,10 +1268,11 @@ static int vxlan_fdb_add(struct ndmsg *ndm, struct 
> nlattr *tb[],
>               return -EINVAL;
>       }
>  
> -     if (tb[NDA_DST] == NULL)
> +     if (!tb || (!tb[NDA_DST] && !tb[NDA_NH_ID]))
>               return -EINVAL;
>  
> -     err = vxlan_fdb_parse(tb, vxlan, &ip, &port, &src_vni, &vni, &ifindex);
> +     err = vxlan_fdb_parse(tb, vxlan, &ip, &port, &src_vni, &vni, &ifindex,
> +                           &nhid);
>       if (err)
>               return err;
>  
> @@ -1148,7 +1284,7 @@ static int vxlan_fdb_add(struct ndmsg *ndm, struct 
> nlattr *tb[],
>       err = vxlan_fdb_update(vxlan, addr, &ip, ndm->ndm_state, flags,
>                              port, src_vni, vni, ifindex,
>                              ndm->ndm_flags | NTF_VXLAN_ADDED_BY_USER,
> -                            true, extack);
> +                            nhid, true, extack);
>       spin_unlock_bh(&vxlan->hash_lock[hash_index]);
>  
>       return err;
> @@ -1159,8 +1295,8 @@ static int __vxlan_fdb_delete(struct vxlan_dev *vxlan,
>                             __be16 port, __be32 src_vni, __be32 vni,
>                             u32 ifindex, bool swdev_notify)
>  {
> -     struct vxlan_fdb *f;
>       struct vxlan_rdst *rd = NULL;
> +     struct vxlan_fdb *f;
>       int err = -ENOENT;
>  
>       f = vxlan_find_mac(vxlan, addr, src_vni);
> @@ -1195,12 +1331,13 @@ static int vxlan_fdb_delete(struct ndmsg *ndm, struct 
> nlattr *tb[],
>       struct vxlan_dev *vxlan = netdev_priv(dev);
>       union vxlan_addr ip;
>       __be32 src_vni, vni;
> -     __be16 port;
> -     u32 ifindex;
> +     u32 ifindex, nhid;
>       u32 hash_index;
> +     __be16 port;
>       int err;
>  
> -     err = vxlan_fdb_parse(tb, vxlan, &ip, &port, &src_vni, &vni, &ifindex);
> +     err = vxlan_fdb_parse(tb, vxlan, &ip, &port, &src_vni, &vni, &ifindex,
> +                           &nhid);
>       if (err)
>               return err;
>  
> @@ -1228,6 +1365,17 @@ static int vxlan_fdb_dump(struct sk_buff *skb, struct 
> netlink_callback *cb,
>               hlist_for_each_entry_rcu(f, &vxlan->fdb_head[h], hlist) {
>                       struct vxlan_rdst *rd;
>  
> +                     if (rcu_access_pointer(f->nh)) {
> +                             err = vxlan_fdb_info(skb, vxlan, f,
> +                                                  NETLINK_CB(cb->skb).portid,
> +                                                  cb->nlh->nlmsg_seq,
> +                                                  RTM_NEWNEIGH,
> +                                                  NLM_F_MULTI, NULL);
> +                             if (err < 0)
> +                                     goto out;
> +                             continue;
> +                     }
> +
>                       list_for_each_entry_rcu(rd, &f->remotes, list) {
>                               if (*idx < cb->args[2])
>                                       goto skip;
> @@ -1311,6 +1459,10 @@ static bool vxlan_snoop(struct net_device *dev,
>               if (f->state & (NUD_PERMANENT | NUD_NOARP))
>                       return true;
>  
> +             /* Don't override an fdb with nexthop with a learnt entry */
> +             if (rcu_access_pointer(f->nh))
> +                     return true;
> +
>               if (net_ratelimit())
>                       netdev_info(dev,
>                                   "%pM migrated from %pIS to %pIS\n",
> @@ -1333,7 +1485,7 @@ static bool vxlan_snoop(struct net_device *dev,
>                                        vxlan->cfg.dst_port,
>                                        vni,
>                                        vxlan->default_dst.remote_vni,
> -                                      ifindex, NTF_SELF, true, NULL);
> +                                      ifindex, NTF_SELF, 0, true, NULL);
>               spin_unlock(&vxlan->hash_lock[hash_index]);
>       }
>  
> @@ -2616,6 +2768,32 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct 
> net_device *dev,
>       kfree_skb(skb);
>  }
>  
> +static void vxlan_xmit_nh(struct sk_buff *skb, struct net_device *dev,
> +                       struct vxlan_fdb *f, __be32 vni, bool did_rsc)
> +{
> +     struct vxlan_rdst nh_rdst;
> +     struct nexthop *nh;
> +     bool do_xmit;
> +     u32 hash;
> +
> +     memset(&nh_rdst, 0, sizeof(struct vxlan_rdst));
> +     hash = skb_get_hash(skb);
> +
> +     rcu_read_lock();
> +     nh = rcu_dereference(f->nh);
> +     if (!nh) {

Shouldn't you also kfree_skb() in this error case?
Below it seems we don't do anything else with the skb.

> +             rcu_read_unlock();
> +             return;
> +     }
> +     do_xmit = vxlan_fdb_nh_path_select(nh, hash, &nh_rdst);
> +     rcu_read_unlock();
> +
> +     if (likely(do_xmit))
> +             vxlan_xmit_one(skb, dev, vni, &nh_rdst, did_rsc);
> +     else
> +             kfree_skb(skb);
> +}
> +
>  /* Transmit local packets over Vxlan
>   *
>   * Outer IP header inherits ECN and DF from inner header.
> @@ -2692,22 +2870,27 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, 
> struct net_device *dev)
>               }
>       }
>  
> -     list_for_each_entry_rcu(rdst, &f->remotes, list) {
> -             struct sk_buff *skb1;
> +     if (rcu_access_pointer(f->nh)) {
> +             vxlan_xmit_nh(skb, dev, f,
> +                           (vni ? : vxlan->default_dst.remote_vni), did_rsc);
> +     } else {
> +             list_for_each_entry_rcu(rdst, &f->remotes, list) {
> +                     struct sk_buff *skb1;
>  
> -             if (!fdst) {
> -                     fdst = rdst;
> -                     continue;
> +                     if (!fdst) {
> +                             fdst = rdst;
> +                             continue;
> +                     }
> +                     skb1 = skb_clone(skb, GFP_ATOMIC);
> +                     if (skb1)
> +                             vxlan_xmit_one(skb1, dev, vni, rdst, did_rsc);
>               }
> -             skb1 = skb_clone(skb, GFP_ATOMIC);
> -             if (skb1)
> -                     vxlan_xmit_one(skb1, dev, vni, rdst, did_rsc);
> +             if (fdst)
> +                     vxlan_xmit_one(skb, dev, vni, fdst, did_rsc);
> +             else
> +                     kfree_skb(skb);
>       }
>  
> -     if (fdst)
> -             vxlan_xmit_one(skb, dev, vni, fdst, did_rsc);
> -     else
> -             kfree_skb(skb);
>       return NETDEV_TX_OK;
>  }
>  
> @@ -3615,7 +3798,7 @@ static int __vxlan_dev_create(struct net *net, struct 
> net_device *dev,
>                                      dst->remote_vni,
>                                      dst->remote_vni,
>                                      dst->remote_ifindex,
> -                                    NTF_SELF, &f);
> +                                    NTF_SELF, 0, &f, extack);
>               if (err)
>                       return err;
>       }
> @@ -4013,7 +4196,7 @@ static int vxlan_changelink(struct net_device *dev, 
> struct nlattr *tb[],
>                                              vxlan->cfg.dst_port,
>                                              conf.vni, conf.vni,
>                                              conf.remote_ifindex,
> -                                            NTF_SELF, true, extack);
> +                                            NTF_SELF, 0, true, extack);
>                       if (err) {
>                               spin_unlock_bh(&vxlan->hash_lock[hash_index]);
>                               netdev_adjacent_change_abort(dst->remote_dev,
> @@ -4335,7 +4518,7 @@ vxlan_fdb_external_learn_add(struct net_device *dev,
>                              fdb_info->remote_vni,
>                              fdb_info->remote_ifindex,
>                              NTF_USE | NTF_SELF | NTF_EXT_LEARNED,
> -                            false, extack);
> +                            0, false, extack);
>       spin_unlock_bh(&vxlan->hash_lock[hash_index]);
>  
>       return err;
> diff --git a/include/net/vxlan.h b/include/net/vxlan.h
> index 373aadc..3a41627c 100644
> --- a/include/net/vxlan.h
> +++ b/include/net/vxlan.h
> @@ -7,6 +7,7 @@
>  #include <net/dst_metadata.h>
>  #include <net/rtnetlink.h>
>  #include <net/switchdev.h>
> +#include <net/nexthop.h>
>  
>  #define IANA_VXLAN_UDP_PORT     4789
>  
> @@ -487,4 +488,28 @@ static inline void vxlan_flag_attr_error(int attrtype,
>  #undef VXLAN_FLAG
>  }
>  
> +static inline bool vxlan_fdb_nh_path_select(struct nexthop *nh,
> +                                         int hash,
> +                                         struct vxlan_rdst *rdst)
> +{
> +     struct fib_nh_common *nhc;
> +
> +     nhc = nexthop_path_fdb_result(nh, hash);
> +     if (unlikely(!nhc))
> +             return false;
> +
> +     switch (nhc->nhc_gw_family) {
> +     case AF_INET:
> +             rdst->remote_ip.sin.sin_addr.s_addr = nhc->nhc_gw.ipv4;
> +             rdst->remote_ip.sa.sa_family = AF_INET;
> +             break;
> +     case AF_INET6:
> +             rdst->remote_ip.sin6.sin6_addr = nhc->nhc_gw.ipv6;
> +             rdst->remote_ip.sa.sa_family = AF_INET6;
> +             break;
> +     }
> +
> +     return true;
> +}
> +
>  #endif
> diff --git a/include/uapi/linux/neighbour.h b/include/uapi/linux/neighbour.h
> index cd144e3..eefcda8 100644
> --- a/include/uapi/linux/neighbour.h
> +++ b/include/uapi/linux/neighbour.h
> @@ -29,6 +29,7 @@ enum {
>       NDA_LINK_NETNSID,
>       NDA_SRC_VNI,
>       NDA_PROTOCOL,  /* Originator of entry */
> +     NDA_NH_ID,
>       __NDA_MAX
>  };
>  
> diff --git a/net/core/neighbour.c b/net/core/neighbour.c
> index b607ea6..37e4dba 100644
> --- a/net/core/neighbour.c
> +++ b/net/core/neighbour.c
> @@ -1771,6 +1771,7 @@ static struct neigh_table *neigh_find_table(int family)
>  }
>  
>  const struct nla_policy nda_policy[NDA_MAX+1] = {
> +     [NDA_UNSPEC]            = { .strict_start_type = NDA_NH_ID },
>       [NDA_DST]               = { .type = NLA_BINARY, .len = MAX_ADDR_LEN },
>       [NDA_LLADDR]            = { .type = NLA_BINARY, .len = MAX_ADDR_LEN },
>       [NDA_CACHEINFO]         = { .len = sizeof(struct nda_cacheinfo) },
> @@ -1781,6 +1782,7 @@ const struct nla_policy nda_policy[NDA_MAX+1] = {
>       [NDA_IFINDEX]           = { .type = NLA_U32 },
>       [NDA_MASTER]            = { .type = NLA_U32 },
>       [NDA_PROTOCOL]          = { .type = NLA_U8 },
> +     [NDA_NH_ID]             = { .type = NLA_U32 },
>  };
>  
>  static int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh,
> 

Reply via email to