vxlan: support fdb and learning in COLLECT_METADATA mode

Vxlan COLLECT_METADATA mode today solves the per-vni netdev
scalability problem in l3 networks. It expects all forwarding
information to be present in dst_metadata. This patch series
enhances collect metadata mode to include the case where only
vni is present in dst_metadata, and the vxlan driver can then use
the rest of the forwarding information datbase to make forwarding
decisions. There is no change to default COLLECT_METADATA
behaviour. These changes only apply to COLLECT_METADATA when
used with the bridging use-case with a special dst_metadata
tunnel info flag (eg: where vxlan device is part of a bridge).
For all this to work, the vxlan driver will need to now support a
single fdb table hashed by mac + vni. This series essentially makes
this happen.

use-case and workflow:
vxlan collect metadata device participates in bridging vlan
to vn-segments. Bridge driver above the vxlan device,
sends the vni corresponding to the vlan in the dst_metadata.
vxlan driver will lookup forwarding database with (mac + vni)
for the required remote destination information to forward the
packet.

Changes introduced by this patch:
    - allow learning and forwarding database state in vxlan netdev in
      COLLECT_METADATA mode. Current behaviour is not changed
      by default. tunnel info flag IP_TUNNEL_INFO_BRIDGE is used
      to support the new bridge friendly mode.
    - A single fdb table hashed by (mac, vni) to allow fdb entries with
      multiple vnis in the same fdb table
    - rx path already has the vni
    - tx path expects a vni in the packet with dst_metadata
    - prior to this series, fdb remote_dsts carried remote vni and
      the vxlan device carrying the fdb table represented the
      source vni. With the vxlan device now representing multiple vnis,
      this patch adds a src vni attribute to the fdb entry. The remote
      vni already uses NDA_VNI attribute. This patch introduces
      NDA_SRC_VNI netlink attribute to represent the src vni in a multi
      vni fdb table.

iproute2 example (patched and pruned iproute2 output to just show
relevant fdb entries):
example shows same host mac learnt on two vni's.

before (netdev per vni):
$bridge fdb show | grep "00:02:00:00:00:03"
00:02:00:00:00:03 dev vxlan1001 dst 12.0.0.8 self
00:02:00:00:00:03 dev vxlan1000 dst 12.0.0.8 self

after this patch with collect metadata in bridged mode (single netdev):
$bridge fdb show | grep "00:02:00:00:00:03"
00:02:00:00:00:03 dev vxlan0 src_vni 1001 dst 12.0.0.8 self
00:02:00:00:00:03 dev vxlan0 src_vni 1000 dst 12.0.0.8 self

Signed-off-by: Roopa Prabhu <roopa@cumulusnetworks.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Roopa Prabhu 2017-01-31 22:59:52 -08:00 committed by David S. Miller
parent f35581d64e
commit 3ad7a4b141
2 changed files with 126 additions and 71 deletions

View File

@ -75,6 +75,7 @@ struct vxlan_fdb {
struct list_head remotes; struct list_head remotes;
u8 eth_addr[ETH_ALEN]; u8 eth_addr[ETH_ALEN];
u16 state; /* see ndm_state */ u16 state; /* see ndm_state */
__be32 vni;
u8 flags; /* see ndm_flags */ u8 flags; /* see ndm_flags */
}; };
@ -302,6 +303,10 @@ static int vxlan_fdb_info(struct sk_buff *skb, struct vxlan_dev *vxlan,
if (rdst->remote_vni != vxlan->default_dst.remote_vni && if (rdst->remote_vni != vxlan->default_dst.remote_vni &&
nla_put_u32(skb, NDA_VNI, be32_to_cpu(rdst->remote_vni))) nla_put_u32(skb, NDA_VNI, be32_to_cpu(rdst->remote_vni)))
goto nla_put_failure; goto nla_put_failure;
if ((vxlan->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 && if (rdst->remote_ifindex &&
nla_put_u32(skb, NDA_IFINDEX, rdst->remote_ifindex)) nla_put_u32(skb, NDA_IFINDEX, rdst->remote_ifindex))
goto nla_put_failure; goto nla_put_failure;
@ -400,34 +405,51 @@ static u32 eth_hash(const unsigned char *addr)
return hash_64(value, FDB_HASH_BITS); return hash_64(value, FDB_HASH_BITS);
} }
static u32 eth_vni_hash(const unsigned char *addr, __be32 vni)
{
/* use 1 byte of OUI and 3 bytes of NIC */
u32 key = get_unaligned((u32 *)(addr + 2));
return jhash_2words(key, vni, vxlan_salt) & (FDB_HASH_SIZE - 1);
}
/* Hash chain to use given mac address */ /* Hash chain to use given mac address */
static inline struct hlist_head *vxlan_fdb_head(struct vxlan_dev *vxlan, static inline struct hlist_head *vxlan_fdb_head(struct vxlan_dev *vxlan,
const u8 *mac) const u8 *mac, __be32 vni)
{ {
return &vxlan->fdb_head[eth_hash(mac)]; if (vxlan->flags & VXLAN_F_COLLECT_METADATA)
return &vxlan->fdb_head[eth_vni_hash(mac, vni)];
else
return &vxlan->fdb_head[eth_hash(mac)];
} }
/* Look up Ethernet address in forwarding table */ /* Look up Ethernet address in forwarding table */
static struct vxlan_fdb *__vxlan_find_mac(struct vxlan_dev *vxlan, static struct vxlan_fdb *__vxlan_find_mac(struct vxlan_dev *vxlan,
const u8 *mac) const u8 *mac, __be32 vni)
{ {
struct hlist_head *head = vxlan_fdb_head(vxlan, mac); struct hlist_head *head = vxlan_fdb_head(vxlan, mac, vni);
struct vxlan_fdb *f; struct vxlan_fdb *f;
hlist_for_each_entry_rcu(f, head, hlist) { hlist_for_each_entry_rcu(f, head, hlist) {
if (ether_addr_equal(mac, f->eth_addr)) if (ether_addr_equal(mac, f->eth_addr)) {
return f; if (vxlan->flags & VXLAN_F_COLLECT_METADATA) {
if (vni == f->vni)
return f;
} else {
return f;
}
}
} }
return NULL; return NULL;
} }
static struct vxlan_fdb *vxlan_find_mac(struct vxlan_dev *vxlan, static struct vxlan_fdb *vxlan_find_mac(struct vxlan_dev *vxlan,
const u8 *mac) const u8 *mac, __be32 vni)
{ {
struct vxlan_fdb *f; struct vxlan_fdb *f;
f = __vxlan_find_mac(vxlan, mac); f = __vxlan_find_mac(vxlan, mac, vni);
if (f) if (f)
f->used = jiffies; f->used = jiffies;
@ -605,15 +627,15 @@ static int vxlan_gro_complete(struct sock *sk, struct sk_buff *skb, int nhoff)
static int vxlan_fdb_create(struct vxlan_dev *vxlan, static int vxlan_fdb_create(struct vxlan_dev *vxlan,
const u8 *mac, union vxlan_addr *ip, const u8 *mac, union vxlan_addr *ip,
__u16 state, __u16 flags, __u16 state, __u16 flags,
__be16 port, __be32 vni, __u32 ifindex, __be16 port, __be32 src_vni, __be32 vni,
__u8 ndm_flags) __u32 ifindex, __u8 ndm_flags)
{ {
struct vxlan_rdst *rd = NULL; struct vxlan_rdst *rd = NULL;
struct vxlan_fdb *f; struct vxlan_fdb *f;
int notify = 0; int notify = 0;
int rc; int rc;
f = __vxlan_find_mac(vxlan, mac); f = __vxlan_find_mac(vxlan, mac, src_vni);
if (f) { if (f) {
if (flags & NLM_F_EXCL) { if (flags & NLM_F_EXCL) {
netdev_dbg(vxlan->dev, netdev_dbg(vxlan->dev,
@ -670,6 +692,7 @@ static int vxlan_fdb_create(struct vxlan_dev *vxlan,
f->state = state; f->state = state;
f->flags = ndm_flags; f->flags = ndm_flags;
f->updated = f->used = jiffies; f->updated = f->used = jiffies;
f->vni = src_vni;
INIT_LIST_HEAD(&f->remotes); INIT_LIST_HEAD(&f->remotes);
memcpy(f->eth_addr, mac, ETH_ALEN); memcpy(f->eth_addr, mac, ETH_ALEN);
@ -681,7 +704,7 @@ static int vxlan_fdb_create(struct vxlan_dev *vxlan,
++vxlan->addrcnt; ++vxlan->addrcnt;
hlist_add_head_rcu(&f->hlist, hlist_add_head_rcu(&f->hlist,
vxlan_fdb_head(vxlan, mac)); vxlan_fdb_head(vxlan, mac, src_vni));
} }
if (notify) { if (notify) {
@ -718,8 +741,8 @@ static void vxlan_fdb_destroy(struct vxlan_dev *vxlan, struct vxlan_fdb *f)
} }
static int vxlan_fdb_parse(struct nlattr *tb[], struct vxlan_dev *vxlan, static int vxlan_fdb_parse(struct nlattr *tb[], struct vxlan_dev *vxlan,
union vxlan_addr *ip, __be16 *port, __be32 *vni, union vxlan_addr *ip, __be16 *port, __be32 *src_vni,
u32 *ifindex) __be32 *vni, u32 *ifindex)
{ {
struct net *net = dev_net(vxlan->dev); struct net *net = dev_net(vxlan->dev);
int err; int err;
@ -757,6 +780,14 @@ static int vxlan_fdb_parse(struct nlattr *tb[], struct vxlan_dev *vxlan,
*vni = vxlan->default_dst.remote_vni; *vni = vxlan->default_dst.remote_vni;
} }
if (tb[NDA_SRC_VNI]) {
if (nla_len(tb[NDA_SRC_VNI]) != sizeof(u32))
return -EINVAL;
*src_vni = cpu_to_be32(nla_get_u32(tb[NDA_SRC_VNI]));
} else {
*src_vni = vxlan->default_dst.remote_vni;
}
if (tb[NDA_IFINDEX]) { if (tb[NDA_IFINDEX]) {
struct net_device *tdev; struct net_device *tdev;
@ -782,7 +813,7 @@ static int vxlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
/* struct net *net = dev_net(vxlan->dev); */ /* struct net *net = dev_net(vxlan->dev); */
union vxlan_addr ip; union vxlan_addr ip;
__be16 port; __be16 port;
__be32 vni; __be32 src_vni, vni;
u32 ifindex; u32 ifindex;
int err; int err;
@ -795,7 +826,7 @@ static int vxlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
if (tb[NDA_DST] == NULL) if (tb[NDA_DST] == NULL)
return -EINVAL; return -EINVAL;
err = vxlan_fdb_parse(tb, vxlan, &ip, &port, &vni, &ifindex); err = vxlan_fdb_parse(tb, vxlan, &ip, &port, &src_vni, &vni, &ifindex);
if (err) if (err)
return err; return err;
@ -804,36 +835,24 @@ static int vxlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
spin_lock_bh(&vxlan->hash_lock); spin_lock_bh(&vxlan->hash_lock);
err = vxlan_fdb_create(vxlan, addr, &ip, ndm->ndm_state, flags, err = vxlan_fdb_create(vxlan, addr, &ip, ndm->ndm_state, flags,
port, vni, ifindex, ndm->ndm_flags); port, src_vni, vni, ifindex, ndm->ndm_flags);
spin_unlock_bh(&vxlan->hash_lock); spin_unlock_bh(&vxlan->hash_lock);
return err; return err;
} }
/* Delete entry (via netlink) */ static int __vxlan_fdb_delete(struct vxlan_dev *vxlan,
static int vxlan_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[], const unsigned char *addr, union vxlan_addr ip,
struct net_device *dev, __be16 port, __be32 src_vni, u32 vni, u32 ifindex,
const unsigned char *addr, u16 vid) u16 vid)
{ {
struct vxlan_dev *vxlan = netdev_priv(dev);
struct vxlan_fdb *f; struct vxlan_fdb *f;
struct vxlan_rdst *rd = NULL; struct vxlan_rdst *rd = NULL;
union vxlan_addr ip; int err = -ENOENT;
__be16 port;
__be32 vni;
u32 ifindex;
int err;
err = vxlan_fdb_parse(tb, vxlan, &ip, &port, &vni, &ifindex); f = vxlan_find_mac(vxlan, addr, src_vni);
if (err)
return err;
err = -ENOENT;
spin_lock_bh(&vxlan->hash_lock);
f = vxlan_find_mac(vxlan, addr);
if (!f) if (!f)
goto out; return err;
if (!vxlan_addr_any(&ip)) { if (!vxlan_addr_any(&ip)) {
rd = vxlan_fdb_find_rdst(f, &ip, port, vni, ifindex); rd = vxlan_fdb_find_rdst(f, &ip, port, vni, ifindex);
@ -841,8 +860,6 @@ static int vxlan_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
goto out; goto out;
} }
err = 0;
/* remove a destination if it's not the only one on the list, /* remove a destination if it's not the only one on the list,
* otherwise destroy the fdb entry * otherwise destroy the fdb entry
*/ */
@ -856,6 +873,28 @@ static int vxlan_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
vxlan_fdb_destroy(vxlan, f); vxlan_fdb_destroy(vxlan, f);
out: out:
return 0;
}
/* Delete entry (via netlink) */
static int vxlan_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev,
const unsigned char *addr, u16 vid)
{
struct vxlan_dev *vxlan = netdev_priv(dev);
union vxlan_addr ip;
__be32 src_vni, vni;
__be16 port;
u32 ifindex;
int err;
err = vxlan_fdb_parse(tb, vxlan, &ip, &port, &src_vni, &vni, &ifindex);
if (err)
return err;
spin_lock_bh(&vxlan->hash_lock);
err = __vxlan_fdb_delete(vxlan, addr, ip, port, src_vni, vni, ifindex,
vid);
spin_unlock_bh(&vxlan->hash_lock); spin_unlock_bh(&vxlan->hash_lock);
return err; return err;
@ -901,12 +940,13 @@ out:
* Return true if packet is bogus and should be dropped. * Return true if packet is bogus and should be dropped.
*/ */
static bool vxlan_snoop(struct net_device *dev, static bool vxlan_snoop(struct net_device *dev,
union vxlan_addr *src_ip, const u8 *src_mac) union vxlan_addr *src_ip, const u8 *src_mac,
__be32 vni)
{ {
struct vxlan_dev *vxlan = netdev_priv(dev); struct vxlan_dev *vxlan = netdev_priv(dev);
struct vxlan_fdb *f; struct vxlan_fdb *f;
f = vxlan_find_mac(vxlan, src_mac); f = vxlan_find_mac(vxlan, src_mac, vni);
if (likely(f)) { if (likely(f)) {
struct vxlan_rdst *rdst = first_remote_rcu(f); struct vxlan_rdst *rdst = first_remote_rcu(f);
@ -935,6 +975,7 @@ static bool vxlan_snoop(struct net_device *dev,
NUD_REACHABLE, NUD_REACHABLE,
NLM_F_EXCL|NLM_F_CREATE, NLM_F_EXCL|NLM_F_CREATE,
vxlan->cfg.dst_port, vxlan->cfg.dst_port,
vni,
vxlan->default_dst.remote_vni, vxlan->default_dst.remote_vni,
0, NTF_SELF); 0, NTF_SELF);
spin_unlock(&vxlan->hash_lock); spin_unlock(&vxlan->hash_lock);
@ -1202,7 +1243,7 @@ static bool vxlan_parse_gpe_hdr(struct vxlanhdr *unparsed,
static bool vxlan_set_mac(struct vxlan_dev *vxlan, static bool vxlan_set_mac(struct vxlan_dev *vxlan,
struct vxlan_sock *vs, struct vxlan_sock *vs,
struct sk_buff *skb) struct sk_buff *skb, __be32 vni)
{ {
union vxlan_addr saddr; union vxlan_addr saddr;
@ -1226,7 +1267,7 @@ static bool vxlan_set_mac(struct vxlan_dev *vxlan,
} }
if ((vxlan->flags & VXLAN_F_LEARN) && if ((vxlan->flags & VXLAN_F_LEARN) &&
vxlan_snoop(skb->dev, &saddr, eth_hdr(skb)->h_source)) vxlan_snoop(skb->dev, &saddr, eth_hdr(skb)->h_source, vni))
return false; return false;
return true; return true;
@ -1268,6 +1309,7 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
__be16 protocol = htons(ETH_P_TEB); __be16 protocol = htons(ETH_P_TEB);
bool raw_proto = false; bool raw_proto = false;
void *oiph; void *oiph;
__be32 vni = 0;
/* Need UDP and VXLAN header to be present */ /* Need UDP and VXLAN header to be present */
if (!pskb_may_pull(skb, VXLAN_HLEN)) if (!pskb_may_pull(skb, VXLAN_HLEN))
@ -1289,7 +1331,12 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
if (!vs) if (!vs)
goto drop; goto drop;
vxlan = vxlan_vs_find_vni(vs, vxlan_vni(vxlan_hdr(skb)->vx_vni)); vni = vxlan_vni(vxlan_hdr(skb)->vx_vni);
if ((vs->flags & VXLAN_F_COLLECT_METADATA) && !vni)
goto drop;
vxlan = vxlan_vs_find_vni(vs, vni);
if (!vxlan) if (!vxlan)
goto drop; goto drop;
@ -1307,7 +1354,6 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
goto drop; goto drop;
if (vxlan_collect_metadata(vs)) { if (vxlan_collect_metadata(vs)) {
__be32 vni = vxlan_vni(vxlan_hdr(skb)->vx_vni);
struct metadata_dst *tun_dst; struct metadata_dst *tun_dst;
tun_dst = udp_tun_rx_dst(skb, vxlan_get_sk_family(vs), TUNNEL_KEY, tun_dst = udp_tun_rx_dst(skb, vxlan_get_sk_family(vs), TUNNEL_KEY,
@ -1345,7 +1391,7 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
} }
if (!raw_proto) { if (!raw_proto) {
if (!vxlan_set_mac(vxlan, vs, skb)) if (!vxlan_set_mac(vxlan, vs, skb, vni))
goto drop; goto drop;
} else { } else {
skb_reset_mac_header(skb); skb_reset_mac_header(skb);
@ -1377,7 +1423,7 @@ drop:
return 0; return 0;
} }
static int arp_reduce(struct net_device *dev, struct sk_buff *skb) static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)
{ {
struct vxlan_dev *vxlan = netdev_priv(dev); struct vxlan_dev *vxlan = netdev_priv(dev);
struct arphdr *parp; struct arphdr *parp;
@ -1424,7 +1470,7 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb)
goto out; goto out;
} }
f = vxlan_find_mac(vxlan, n->ha); f = vxlan_find_mac(vxlan, n->ha, vni);
if (f && vxlan_addr_any(&(first_remote_rcu(f)->remote_ip))) { if (f && vxlan_addr_any(&(first_remote_rcu(f)->remote_ip))) {
/* bridge-local neighbor */ /* bridge-local neighbor */
neigh_release(n); neigh_release(n);
@ -1548,7 +1594,7 @@ static struct sk_buff *vxlan_na_create(struct sk_buff *request,
return reply; return reply;
} }
static int neigh_reduce(struct net_device *dev, struct sk_buff *skb) static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)
{ {
struct vxlan_dev *vxlan = netdev_priv(dev); struct vxlan_dev *vxlan = netdev_priv(dev);
struct nd_msg *msg; struct nd_msg *msg;
@ -1585,7 +1631,7 @@ static int neigh_reduce(struct net_device *dev, struct sk_buff *skb)
goto out; goto out;
} }
f = vxlan_find_mac(vxlan, n->ha); f = vxlan_find_mac(vxlan, n->ha, vni);
if (f && vxlan_addr_any(&(first_remote_rcu(f)->remote_ip))) { if (f && vxlan_addr_any(&(first_remote_rcu(f)->remote_ip))) {
/* bridge-local neighbor */ /* bridge-local neighbor */
neigh_release(n); neigh_release(n);
@ -1906,7 +1952,7 @@ static struct dst_entry *vxlan6_get_route(struct vxlan_dev *vxlan,
/* Bypass encapsulation if the destination is local */ /* Bypass encapsulation if the destination is local */
static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan, static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan,
struct vxlan_dev *dst_vxlan) struct vxlan_dev *dst_vxlan, __be32 vni)
{ {
struct pcpu_sw_netstats *tx_stats, *rx_stats; struct pcpu_sw_netstats *tx_stats, *rx_stats;
union vxlan_addr loopback; union vxlan_addr loopback;
@ -1932,7 +1978,7 @@ static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan,
} }
if (dst_vxlan->flags & VXLAN_F_LEARN) if (dst_vxlan->flags & VXLAN_F_LEARN)
vxlan_snoop(skb->dev, &loopback, eth_hdr(skb)->h_source); vxlan_snoop(skb->dev, &loopback, eth_hdr(skb)->h_source, vni);
u64_stats_update_begin(&tx_stats->syncp); u64_stats_update_begin(&tx_stats->syncp);
tx_stats->tx_packets++; tx_stats->tx_packets++;
@ -1976,7 +2022,7 @@ static int encap_bypass_if_local(struct sk_buff *skb, struct net_device *dev,
return -ENOENT; return -ENOENT;
} }
vxlan_encap_bypass(skb, vxlan, dst_vxlan); vxlan_encap_bypass(skb, vxlan, dst_vxlan, vni);
return 1; return 1;
} }
@ -1984,7 +2030,8 @@ static int encap_bypass_if_local(struct sk_buff *skb, struct net_device *dev,
} }
static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
struct vxlan_rdst *rdst, bool did_rsc) __be32 default_vni, struct vxlan_rdst *rdst,
bool did_rsc)
{ {
struct dst_cache *dst_cache; struct dst_cache *dst_cache;
struct ip_tunnel_info *info; struct ip_tunnel_info *info;
@ -2011,14 +2058,14 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
if (vxlan_addr_any(dst)) { if (vxlan_addr_any(dst)) {
if (did_rsc) { if (did_rsc) {
/* short-circuited back to local bridge */ /* short-circuited back to local bridge */
vxlan_encap_bypass(skb, vxlan, vxlan); vxlan_encap_bypass(skb, vxlan, vxlan, default_vni);
return; return;
} }
goto drop; goto drop;
} }
dst_port = rdst->remote_port ? rdst->remote_port : vxlan->cfg.dst_port; dst_port = rdst->remote_port ? rdst->remote_port : vxlan->cfg.dst_port;
vni = rdst->remote_vni; vni = (rdst->remote_vni) ? : default_vni;
src = &vxlan->cfg.saddr; src = &vxlan->cfg.saddr;
dst_cache = &rdst->dst_cache; dst_cache = &rdst->dst_cache;
md->gbp = skb->mark; md->gbp = skb->mark;
@ -2173,23 +2220,29 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
bool did_rsc = false; bool did_rsc = false;
struct vxlan_rdst *rdst, *fdst = NULL; struct vxlan_rdst *rdst, *fdst = NULL;
struct vxlan_fdb *f; struct vxlan_fdb *f;
__be32 vni = 0;
info = skb_tunnel_info(skb); info = skb_tunnel_info(skb);
skb_reset_mac_header(skb); skb_reset_mac_header(skb);
if (vxlan->flags & VXLAN_F_COLLECT_METADATA) { if (vxlan->flags & VXLAN_F_COLLECT_METADATA) {
if (info && info->mode & IP_TUNNEL_INFO_TX) if (info && info->mode & IP_TUNNEL_INFO_BRIDGE &&
vxlan_xmit_one(skb, dev, NULL, false); info->mode & IP_TUNNEL_INFO_TX) {
else vni = tunnel_id_to_key32(info->key.tun_id);
kfree_skb(skb); } else {
return NETDEV_TX_OK; if (info && info->mode & IP_TUNNEL_INFO_TX)
vxlan_xmit_one(skb, dev, vni, NULL, false);
else
kfree_skb(skb);
return NETDEV_TX_OK;
}
} }
if (vxlan->flags & VXLAN_F_PROXY) { if (vxlan->flags & VXLAN_F_PROXY) {
eth = eth_hdr(skb); eth = eth_hdr(skb);
if (ntohs(eth->h_proto) == ETH_P_ARP) if (ntohs(eth->h_proto) == ETH_P_ARP)
return arp_reduce(dev, skb); return arp_reduce(dev, skb, vni);
#if IS_ENABLED(CONFIG_IPV6) #if IS_ENABLED(CONFIG_IPV6)
else if (ntohs(eth->h_proto) == ETH_P_IPV6 && else if (ntohs(eth->h_proto) == ETH_P_IPV6 &&
pskb_may_pull(skb, sizeof(struct ipv6hdr) pskb_may_pull(skb, sizeof(struct ipv6hdr)
@ -2200,13 +2253,13 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
msg = (struct nd_msg *)skb_transport_header(skb); msg = (struct nd_msg *)skb_transport_header(skb);
if (msg->icmph.icmp6_code == 0 && if (msg->icmph.icmp6_code == 0 &&
msg->icmph.icmp6_type == NDISC_NEIGHBOUR_SOLICITATION) msg->icmph.icmp6_type == NDISC_NEIGHBOUR_SOLICITATION)
return neigh_reduce(dev, skb); return neigh_reduce(dev, skb, vni);
} }
#endif #endif
} }
eth = eth_hdr(skb); eth = eth_hdr(skb);
f = vxlan_find_mac(vxlan, eth->h_dest); f = vxlan_find_mac(vxlan, eth->h_dest, vni);
did_rsc = false; did_rsc = false;
if (f && (f->flags & NTF_ROUTER) && (vxlan->flags & VXLAN_F_RSC) && if (f && (f->flags & NTF_ROUTER) && (vxlan->flags & VXLAN_F_RSC) &&
@ -2214,11 +2267,11 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
ntohs(eth->h_proto) == ETH_P_IPV6)) { ntohs(eth->h_proto) == ETH_P_IPV6)) {
did_rsc = route_shortcircuit(dev, skb); did_rsc = route_shortcircuit(dev, skb);
if (did_rsc) if (did_rsc)
f = vxlan_find_mac(vxlan, eth->h_dest); f = vxlan_find_mac(vxlan, eth->h_dest, vni);
} }
if (f == NULL) { if (f == NULL) {
f = vxlan_find_mac(vxlan, all_zeros_mac); f = vxlan_find_mac(vxlan, all_zeros_mac, vni);
if (f == NULL) { if (f == NULL) {
if ((vxlan->flags & VXLAN_F_L2MISS) && if ((vxlan->flags & VXLAN_F_L2MISS) &&
!is_multicast_ether_addr(eth->h_dest)) !is_multicast_ether_addr(eth->h_dest))
@ -2239,11 +2292,11 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
} }
skb1 = skb_clone(skb, GFP_ATOMIC); skb1 = skb_clone(skb, GFP_ATOMIC);
if (skb1) if (skb1)
vxlan_xmit_one(skb1, dev, rdst, did_rsc); vxlan_xmit_one(skb1, dev, vni, rdst, did_rsc);
} }
if (fdst) if (fdst)
vxlan_xmit_one(skb, dev, fdst, did_rsc); vxlan_xmit_one(skb, dev, vni, fdst, did_rsc);
else else
kfree_skb(skb); kfree_skb(skb);
return NETDEV_TX_OK; return NETDEV_TX_OK;
@ -2307,12 +2360,12 @@ static int vxlan_init(struct net_device *dev)
return 0; return 0;
} }
static void vxlan_fdb_delete_default(struct vxlan_dev *vxlan) static void vxlan_fdb_delete_default(struct vxlan_dev *vxlan, __be32 vni)
{ {
struct vxlan_fdb *f; struct vxlan_fdb *f;
spin_lock_bh(&vxlan->hash_lock); spin_lock_bh(&vxlan->hash_lock);
f = __vxlan_find_mac(vxlan, all_zeros_mac); f = __vxlan_find_mac(vxlan, all_zeros_mac, vni);
if (f) if (f)
vxlan_fdb_destroy(vxlan, f); vxlan_fdb_destroy(vxlan, f);
spin_unlock_bh(&vxlan->hash_lock); spin_unlock_bh(&vxlan->hash_lock);
@ -2322,7 +2375,7 @@ static void vxlan_uninit(struct net_device *dev)
{ {
struct vxlan_dev *vxlan = netdev_priv(dev); struct vxlan_dev *vxlan = netdev_priv(dev);
vxlan_fdb_delete_default(vxlan); vxlan_fdb_delete_default(vxlan, vxlan->cfg.vni);
free_percpu(dev->tstats); free_percpu(dev->tstats);
} }
@ -2923,6 +2976,7 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev,
NLM_F_EXCL|NLM_F_CREATE, NLM_F_EXCL|NLM_F_CREATE,
vxlan->cfg.dst_port, vxlan->cfg.dst_port,
vxlan->default_dst.remote_vni, vxlan->default_dst.remote_vni,
vxlan->default_dst.remote_vni,
vxlan->default_dst.remote_ifindex, vxlan->default_dst.remote_ifindex,
NTF_SELF); NTF_SELF);
if (err) if (err)
@ -2931,7 +2985,7 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev,
err = register_netdevice(dev); err = register_netdevice(dev);
if (err) { if (err) {
vxlan_fdb_delete_default(vxlan); vxlan_fdb_delete_default(vxlan, vxlan->cfg.vni);
return err; return err;
} }

View File

@ -26,6 +26,7 @@ enum {
NDA_IFINDEX, NDA_IFINDEX,
NDA_MASTER, NDA_MASTER,
NDA_LINK_NETNSID, NDA_LINK_NETNSID,
NDA_SRC_VNI,
__NDA_MAX __NDA_MAX
}; };