Commit 49f810f0 authored by Matthias Schiffer's avatar Matthias Schiffer Committed by David S. Miller

vxlan: allow multiple VXLANs with same VNI for IPv6 link-local addresses

As link-local addresses are only valid for a single interface, we can allow
to use the same VNI for multiple independent VXLANs, as long as the used
interfaces are distinct. This way, VXLANs can always be used as a drop-in
replacement for VLANs with greater ID space.

This also extends VNI lookup to respect the ifindex when link-local IPv6
addresses are used, so using the same VNI on multiple interfaces can
actually work.
Signed-off-by: default avatarMatthias Schiffer <mschiffer@universe-factory.net>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 87613de9
...@@ -226,7 +226,8 @@ static struct vxlan_sock *vxlan_find_sock(struct net *net, sa_family_t family, ...@@ -226,7 +226,8 @@ static struct vxlan_sock *vxlan_find_sock(struct net *net, sa_family_t family,
return NULL; return NULL;
} }
static struct vxlan_dev *vxlan_vs_find_vni(struct vxlan_sock *vs, __be32 vni) static struct vxlan_dev *vxlan_vs_find_vni(struct vxlan_sock *vs, int ifindex,
__be32 vni)
{ {
struct vxlan_dev *vxlan; struct vxlan_dev *vxlan;
...@@ -235,7 +236,17 @@ static struct vxlan_dev *vxlan_vs_find_vni(struct vxlan_sock *vs, __be32 vni) ...@@ -235,7 +236,17 @@ static struct vxlan_dev *vxlan_vs_find_vni(struct vxlan_sock *vs, __be32 vni)
vni = 0; vni = 0;
hlist_for_each_entry_rcu(vxlan, vni_head(vs, vni), hlist) { hlist_for_each_entry_rcu(vxlan, vni_head(vs, vni), hlist) {
if (vxlan->default_dst.remote_vni == vni) if (vxlan->default_dst.remote_vni != vni)
continue;
if (IS_ENABLED(CONFIG_IPV6)) {
const struct vxlan_config *cfg = &vxlan->cfg;
if ((cfg->flags & VXLAN_F_IPV6_LINKLOCAL) &&
cfg->remote_ifindex != ifindex)
continue;
}
return vxlan; return vxlan;
} }
...@@ -243,9 +254,9 @@ static struct vxlan_dev *vxlan_vs_find_vni(struct vxlan_sock *vs, __be32 vni) ...@@ -243,9 +254,9 @@ static struct vxlan_dev *vxlan_vs_find_vni(struct vxlan_sock *vs, __be32 vni)
} }
/* Look up VNI in a per net namespace table */ /* Look up VNI in a per net namespace table */
static struct vxlan_dev *vxlan_find_vni(struct net *net, __be32 vni, static struct vxlan_dev *vxlan_find_vni(struct net *net, int ifindex,
sa_family_t family, __be16 port, __be32 vni, sa_family_t family,
u32 flags) __be16 port, u32 flags)
{ {
struct vxlan_sock *vs; struct vxlan_sock *vs;
...@@ -253,7 +264,7 @@ static struct vxlan_dev *vxlan_find_vni(struct net *net, __be32 vni, ...@@ -253,7 +264,7 @@ static struct vxlan_dev *vxlan_find_vni(struct net *net, __be32 vni,
if (!vs) if (!vs)
return NULL; return NULL;
return vxlan_vs_find_vni(vs, vni); return vxlan_vs_find_vni(vs, ifindex, vni);
} }
/* Fill in neighbour message in skbuff. */ /* Fill in neighbour message in skbuff. */
...@@ -1360,7 +1371,7 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb) ...@@ -1360,7 +1371,7 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
vni = vxlan_vni(vxlan_hdr(skb)->vx_vni); vni = vxlan_vni(vxlan_hdr(skb)->vx_vni);
vxlan = vxlan_vs_find_vni(vs, vni); vxlan = vxlan_vs_find_vni(vs, skb->dev->ifindex, vni);
if (!vxlan) if (!vxlan)
goto drop; goto drop;
...@@ -2022,8 +2033,10 @@ static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan, ...@@ -2022,8 +2033,10 @@ static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan,
} }
static int encap_bypass_if_local(struct sk_buff *skb, struct net_device *dev, static int encap_bypass_if_local(struct sk_buff *skb, struct net_device *dev,
struct vxlan_dev *vxlan, union vxlan_addr *daddr, struct vxlan_dev *vxlan,
__be16 dst_port, __be32 vni, struct dst_entry *dst, union vxlan_addr *daddr,
__be16 dst_port, int dst_ifindex, __be32 vni,
struct dst_entry *dst,
u32 rt_flags) u32 rt_flags)
{ {
#if IS_ENABLED(CONFIG_IPV6) #if IS_ENABLED(CONFIG_IPV6)
...@@ -2039,7 +2052,7 @@ static int encap_bypass_if_local(struct sk_buff *skb, struct net_device *dev, ...@@ -2039,7 +2052,7 @@ static int encap_bypass_if_local(struct sk_buff *skb, struct net_device *dev,
struct vxlan_dev *dst_vxlan; struct vxlan_dev *dst_vxlan;
dst_release(dst); dst_release(dst);
dst_vxlan = vxlan_find_vni(vxlan->net, vni, dst_vxlan = vxlan_find_vni(vxlan->net, dst_ifindex, vni,
daddr->sa.sa_family, dst_port, daddr->sa.sa_family, dst_port,
vxlan->cfg.flags); vxlan->cfg.flags);
if (!dst_vxlan) { if (!dst_vxlan) {
...@@ -2071,6 +2084,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, ...@@ -2071,6 +2084,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
struct dst_entry *ndst = NULL; struct dst_entry *ndst = NULL;
__be32 vni, label; __be32 vni, label;
__u8 tos, ttl; __u8 tos, ttl;
int ifindex;
int err; int err;
u32 flags = vxlan->cfg.flags; u32 flags = vxlan->cfg.flags;
bool udp_sum = false; bool udp_sum = false;
...@@ -2091,6 +2105,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, ...@@ -2091,6 +2105,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
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) ? : default_vni; vni = (rdst->remote_vni) ? : default_vni;
ifindex = rdst->remote_ifindex;
local_ip = vxlan->cfg.saddr; local_ip = vxlan->cfg.saddr;
dst_cache = &rdst->dst_cache; dst_cache = &rdst->dst_cache;
md->gbp = skb->mark; md->gbp = skb->mark;
...@@ -2124,6 +2139,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, ...@@ -2124,6 +2139,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
dst = &remote_ip; dst = &remote_ip;
dst_port = info->key.tp_dst ? : vxlan->cfg.dst_port; dst_port = info->key.tp_dst ? : vxlan->cfg.dst_port;
vni = tunnel_id_to_key32(info->key.tun_id); vni = tunnel_id_to_key32(info->key.tun_id);
ifindex = 0;
dst_cache = &info->dst_cache; dst_cache = &info->dst_cache;
if (info->options_len) if (info->options_len)
md = ip_tunnel_info_opts(info); md = ip_tunnel_info_opts(info);
...@@ -2141,8 +2157,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, ...@@ -2141,8 +2157,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
struct rtable *rt; struct rtable *rt;
__be16 df = 0; __be16 df = 0;
rt = vxlan_get_route(vxlan, dev, sock4, skb, rt = vxlan_get_route(vxlan, dev, sock4, skb, ifindex, tos,
rdst ? rdst->remote_ifindex : 0, tos,
dst->sin.sin_addr.s_addr, dst->sin.sin_addr.s_addr,
&local_ip.sin.sin_addr.s_addr, &local_ip.sin.sin_addr.s_addr,
dst_port, src_port, dst_port, src_port,
...@@ -2155,8 +2170,8 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, ...@@ -2155,8 +2170,8 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
/* Bypass encapsulation if the destination is local */ /* Bypass encapsulation if the destination is local */
if (!info) { if (!info) {
err = encap_bypass_if_local(skb, dev, vxlan, dst, err = encap_bypass_if_local(skb, dev, vxlan, dst,
dst_port, vni, &rt->dst, dst_port, ifindex, vni,
rt->rt_flags); &rt->dst, rt->rt_flags);
if (err) if (err)
goto out_unlock; goto out_unlock;
} else if (info->key.tun_flags & TUNNEL_DONT_FRAGMENT) { } else if (info->key.tun_flags & TUNNEL_DONT_FRAGMENT) {
...@@ -2178,8 +2193,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, ...@@ -2178,8 +2193,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
} else { } else {
struct vxlan_sock *sock6 = rcu_dereference(vxlan->vn6_sock); struct vxlan_sock *sock6 = rcu_dereference(vxlan->vn6_sock);
ndst = vxlan6_get_route(vxlan, dev, sock6, skb, ndst = vxlan6_get_route(vxlan, dev, sock6, skb, ifindex, tos,
rdst ? rdst->remote_ifindex : 0, tos,
label, &dst->sin6.sin6_addr, label, &dst->sin6.sin6_addr,
&local_ip.sin6.sin6_addr, &local_ip.sin6.sin6_addr,
dst_port, src_port, dst_port, src_port,
...@@ -2194,8 +2208,8 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, ...@@ -2194,8 +2208,8 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
u32 rt6i_flags = ((struct rt6_info *)ndst)->rt6i_flags; u32 rt6i_flags = ((struct rt6_info *)ndst)->rt6i_flags;
err = encap_bypass_if_local(skb, dev, vxlan, dst, err = encap_bypass_if_local(skb, dev, vxlan, dst,
dst_port, vni, ndst, dst_port, ifindex, vni,
rt6i_flags); ndst, rt6i_flags);
if (err) if (err)
goto out_unlock; goto out_unlock;
} }
...@@ -2993,10 +3007,18 @@ static int vxlan_config_validate(struct net *src_net, struct vxlan_config *conf, ...@@ -2993,10 +3007,18 @@ static int vxlan_config_validate(struct net *src_net, struct vxlan_config *conf,
if (tmp == old) if (tmp == old)
continue; continue;
if (tmp->cfg.vni == conf->vni && if (tmp->cfg.vni != conf->vni)
tmp->cfg.dst_port == conf->dst_port && continue;
(tmp->cfg.flags & (VXLAN_F_RCV_FLAGS | VXLAN_F_IPV6)) == if (tmp->cfg.dst_port != conf->dst_port)
continue;
if ((tmp->cfg.flags & (VXLAN_F_RCV_FLAGS | VXLAN_F_IPV6)) !=
(conf->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)
continue;
return -EEXIST; return -EEXIST;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment