root/drivers/net/vxlan/vxlan_private.h
/* SPDX-License-Identifier: GPL-2.0 */
/*
 *      Vxlan private header file
 *
 */

#ifndef _VXLAN_PRIVATE_H
#define _VXLAN_PRIVATE_H

#include <linux/rhashtable.h>

extern unsigned int vxlan_net_id;
extern const u8 all_zeros_mac[ETH_ALEN + 2];
extern const struct rhashtable_params vxlan_vni_rht_params;

#define PORT_HASH_BITS  8
#define PORT_HASH_SIZE  (1 << PORT_HASH_BITS)

/* per-network namespace private data for this module */
struct vxlan_net {
        struct list_head  vxlan_list;
        /* sock_list is protected by rtnl lock */
        struct hlist_head sock_list[PORT_HASH_SIZE];
        struct notifier_block nexthop_notifier_block;
};

struct vxlan_fdb_key {
        u8 eth_addr[ETH_ALEN];
        __be32 vni;
};

/* Forwarding table entry */
struct vxlan_fdb {
        struct rhash_head rhnode;
        struct rcu_head   rcu;
        unsigned long     updated;      /* jiffies */
        unsigned long     used;
        struct list_head  remotes;
        struct vxlan_fdb_key key;
        u16               state;        /* see ndm_state */
        u16               flags;        /* see ndm_flags and below */
        struct list_head  nh_list;
        struct hlist_node fdb_node;
        struct nexthop __rcu *nh;
        struct vxlan_dev  __rcu *vdev;
};

#define NTF_VXLAN_ADDED_BY_USER 0x100

/* Virtual Network hash table head */
static inline struct hlist_head *vni_head(struct vxlan_sock *vs, __be32 vni)
{
        return &vs->vni_list[hash_32((__force u32)vni, VNI_HASH_BITS)];
}

/* Socket hash table head */
static inline struct hlist_head *vs_head(struct net *net, __be16 port)
{
        struct vxlan_net *vn = net_generic(net, vxlan_net_id);

        return &vn->sock_list[hash_32(ntohs(port), PORT_HASH_BITS)];
}

/* First remote destination for a forwarding entry. */
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);
}

#if IS_ENABLED(CONFIG_IPV6)
static inline
bool vxlan_addr_equal(const union vxlan_addr *a, const union vxlan_addr *b)
{
        if (a->sa.sa_family != b->sa.sa_family)
                return false;
        if (a->sa.sa_family == AF_INET6)
                return ipv6_addr_equal(&a->sin6.sin6_addr, &b->sin6.sin6_addr);
        else
                return a->sin.sin_addr.s_addr == b->sin.sin_addr.s_addr;
}

static inline int vxlan_nla_get_addr(union vxlan_addr *ip,
                                     const struct nlattr *nla)
{
        if (nla_len(nla) >= sizeof(struct in6_addr)) {
                ip->sin6.sin6_addr = nla_get_in6_addr(nla);
                ip->sa.sa_family = AF_INET6;
                return 0;
        } else if (nla_len(nla) >= sizeof(__be32)) {
                ip->sin.sin_addr.s_addr = nla_get_in_addr(nla);
                ip->sa.sa_family = AF_INET;
                return 0;
        } else {
                return -EAFNOSUPPORT;
        }
}

static inline int vxlan_nla_put_addr(struct sk_buff *skb, int attr,
                                     const union vxlan_addr *ip)
{
        if (ip->sa.sa_family == AF_INET6)
                return nla_put_in6_addr(skb, attr, &ip->sin6.sin6_addr);
        else
                return nla_put_in_addr(skb, attr, ip->sin.sin_addr.s_addr);
}

static inline bool vxlan_addr_is_multicast(const union vxlan_addr *ip)
{
        if (ip->sa.sa_family == AF_INET6)
                return ipv6_addr_is_multicast(&ip->sin6.sin6_addr);
        else
                return ipv4_is_multicast(ip->sin.sin_addr.s_addr);
}

#else /* !CONFIG_IPV6 */

static inline
bool vxlan_addr_equal(const union vxlan_addr *a, const union vxlan_addr *b)
{
        return a->sin.sin_addr.s_addr == b->sin.sin_addr.s_addr;
}

static inline int vxlan_nla_get_addr(union vxlan_addr *ip,
                                     const struct nlattr *nla)
{
        if (nla_len(nla) >= sizeof(struct in6_addr)) {
                return -EAFNOSUPPORT;
        } else if (nla_len(nla) >= sizeof(__be32)) {
                ip->sin.sin_addr.s_addr = nla_get_in_addr(nla);
                ip->sa.sa_family = AF_INET;
                return 0;
        } else {
                return -EAFNOSUPPORT;
        }
}

static inline int vxlan_nla_put_addr(struct sk_buff *skb, int attr,
                                     const union vxlan_addr *ip)
{
        return nla_put_in_addr(skb, attr, ip->sin.sin_addr.s_addr);
}

static inline bool vxlan_addr_is_multicast(const union vxlan_addr *ip)
{
        return ipv4_is_multicast(ip->sin.sin_addr.s_addr);
}

#endif

static inline size_t vxlan_addr_size(const union vxlan_addr *ip)
{
        if (ip->sa.sa_family == AF_INET6)
                return sizeof(struct in6_addr);
        else
                return sizeof(__be32);
}

static inline struct vxlan_vni_node *
vxlan_vnifilter_lookup(struct vxlan_dev *vxlan, __be32 vni)
{
        struct vxlan_vni_group *vg;

        vg = rcu_dereference_rtnl(vxlan->vnigrp);
        if (!vg)
                return NULL;

        return rhashtable_lookup_fast(&vg->vni_hash, &vni,
                                      vxlan_vni_rht_params);
}

/* vxlan_core.c */
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,
                     u32 nhid, struct vxlan_fdb **fdb,
                     struct netlink_ext_ack *extack);
int __vxlan_fdb_delete(struct vxlan_dev *vxlan,
                       const unsigned char *addr, union vxlan_addr ip,
                       __be16 port, __be32 src_vni, __be32 vni,
                       u32 ifindex, bool swdev_notify);
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 nhid,
                     bool swdev_notify, struct netlink_ext_ack *extack);
void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
                    __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);

/* vxlan_vnifilter.c */
int vxlan_vnigroup_init(struct vxlan_dev *vxlan);
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,
                           struct vxlan_vni_node *vninode,
                           int type, unsigned int len);

void vxlan_vs_add_vnigrp(struct vxlan_dev *vxlan,
                         struct vxlan_sock *vs,
                         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,
                               struct netlink_ext_ack *extack);


/* vxlan_multicast.c */
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,
                    int rifindex);
int vxlan_igmp_leave(struct vxlan_dev *vxlan, union vxlan_addr *rip,
                     int rifindex);

/* vxlan_mdb.c */
int vxlan_mdb_dump(struct net_device *dev, struct sk_buff *skb,
                   struct netlink_callback *cb);
int vxlan_mdb_add(struct net_device *dev, struct nlattr *tb[], u16 nlmsg_flags,
                  struct netlink_ext_ack *extack);
int vxlan_mdb_del(struct net_device *dev, struct nlattr *tb[],
                  struct netlink_ext_ack *extack);
int vxlan_mdb_del_bulk(struct net_device *dev, struct nlattr *tb[],
                       struct netlink_ext_ack *extack);
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,
                                                struct sk_buff *skb,
                                                __be32 src_vni);
netdev_tx_t vxlan_mdb_xmit(struct vxlan_dev *vxlan,
                           const struct vxlan_mdb_entry *mdb_entry,
                           struct sk_buff *skb);
int vxlan_mdb_init(struct vxlan_dev *vxlan);
void vxlan_mdb_fini(struct vxlan_dev *vxlan);
#endif