From: Matthias Schiffer <mschiffer@universe-factory.net>
Date: Tue, 9 Jan 2018 22:38:19 +0100
Subject: generic: vxlan: backport support for VXLAN over link-local IPv6 to 4.9

Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net>

diff --git a/target/linux/generic/backport-4.9/095-0001-vxlan-improve-validation-of-address-family-configura.patch b/target/linux/generic/backport-4.9/095-0001-vxlan-improve-validation-of-address-family-configura.patch
new file mode 100644
index 0000000000000000000000000000000000000000..2114562536675cd59450928f591e70d66f8fc7b9
--- /dev/null
+++ b/target/linux/generic/backport-4.9/095-0001-vxlan-improve-validation-of-address-family-configura.patch
@@ -0,0 +1,73 @@
+From f45ba82cd83d27b5d44d3dc417e0e480ba0d3703 Mon Sep 17 00:00:00 2001
+Message-Id: <f45ba82cd83d27b5d44d3dc417e0e480ba0d3703.1515533863.git.mschiffer@universe-factory.net>
+From: Matthias Schiffer <mschiffer@universe-factory.net>
+Date: Mon, 19 Jun 2017 10:03:57 +0200
+Subject: [PATCH 1/4] vxlan: improve validation of address family configuration
+
+Address families of source and destination addresses must match, and
+changelink operations can't change the address family.
+
+In addition, always use the VXLAN_F_IPV6 to check if a VXLAN device uses
+IPv4 or IPv6.
+
+Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+[Matthias Schiffer: rebase to v4.9.y]
+---
+ drivers/net/vxlan.c | 23 +++++++++++++++--------
+ 1 file changed, 15 insertions(+), 8 deletions(-)
+
+diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
+index 983e941bdf29..fbe8da7fa296 100644
+--- a/drivers/net/vxlan.c
++++ b/drivers/net/vxlan.c
+@@ -2867,12 +2867,20 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev,
+ 
+ 	memcpy(&dst->remote_ip, &conf->remote_ip, sizeof(conf->remote_ip));
+ 
+-	/* Unless IPv6 is explicitly requested, assume IPv4 */
+-	if (!dst->remote_ip.sa.sa_family)
++	if (!dst->remote_ip.sa.sa_family && !conf->saddr.sa.sa_family) {
++		/* Unless IPv6 is explicitly requested, assume IPv4 */
+ 		dst->remote_ip.sa.sa_family = AF_INET;
++		conf->saddr.sa.sa_family = AF_INET;
++	} else if (!dst->remote_ip.sa.sa_family) {
++		dst->remote_ip.sa.sa_family = conf->saddr.sa.sa_family;
++	} else if (!conf->saddr.sa.sa_family) {
++		conf->saddr.sa.sa_family = dst->remote_ip.sa.sa_family;
++	}
++
++	if (conf->saddr.sa.sa_family != dst->remote_ip.sa.sa_family)
++		return -EINVAL;
+ 
+-	if (dst->remote_ip.sa.sa_family == AF_INET6 ||
+-	    vxlan->cfg.saddr.sa.sa_family == AF_INET6) {
++	if (conf->saddr.sa.sa_family == AF_INET6) {
+ 		if (!IS_ENABLED(CONFIG_IPV6))
+ 			return -EPFNOSUPPORT;
+ 		use_ipv6 = true;
+@@ -2938,11 +2946,9 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev,
+ 
+ 	list_for_each_entry(tmp, &vn->vxlan_list, next) {
+ 		if (tmp->cfg.vni == conf->vni &&
+-		    (tmp->default_dst.remote_ip.sa.sa_family == AF_INET6 ||
+-		     tmp->cfg.saddr.sa.sa_family == AF_INET6) == use_ipv6 &&
+ 		    tmp->cfg.dst_port == vxlan->cfg.dst_port &&
+-		    (tmp->flags & VXLAN_F_RCV_FLAGS) ==
+-		    (vxlan->flags & VXLAN_F_RCV_FLAGS)) {
++		    (tmp->flags & (VXLAN_F_RCV_FLAGS | VXLAN_F_IPV6)) ==
++		    (vxlan->flags & (VXLAN_F_RCV_FLAGS | VXLAN_F_IPV6))) {
+ 			pr_info("duplicate VNI %u\n", be32_to_cpu(conf->vni));
+ 			return -EEXIST;
+ 		}
+@@ -2987,6 +2993,7 @@ static int vxlan_newlink(struct net *src_net, struct net_device *dev,
+ 
+ 	if (data[IFLA_VXLAN_GROUP]) {
+ 		conf.remote_ip.sin.sin_addr.s_addr = nla_get_in_addr(data[IFLA_VXLAN_GROUP]);
++		conf.remote_ip.sa.sa_family = AF_INET;
+ 	} else if (data[IFLA_VXLAN_GROUP6]) {
+ 		if (!IS_ENABLED(CONFIG_IPV6))
+ 			return -EPFNOSUPPORT;
+-- 
+2.15.1
+
diff --git a/target/linux/generic/backport-4.9/095-0002-vxlan-check-valid-combinations-of-address-scopes.patch b/target/linux/generic/backport-4.9/095-0002-vxlan-check-valid-combinations-of-address-scopes.patch
new file mode 100644
index 0000000000000000000000000000000000000000..b38b9977bca192eafe9a0d9b8c36a120b3a2c590
--- /dev/null
+++ b/target/linux/generic/backport-4.9/095-0002-vxlan-check-valid-combinations-of-address-scopes.patch
@@ -0,0 +1,91 @@
+From 0eb4ccfc77e07fb4bfc7b1778a7ecb136b47aba4 Mon Sep 17 00:00:00 2001
+Message-Id: <0eb4ccfc77e07fb4bfc7b1778a7ecb136b47aba4.1515533863.git.mschiffer@universe-factory.net>
+In-Reply-To: <f45ba82cd83d27b5d44d3dc417e0e480ba0d3703.1515533863.git.mschiffer@universe-factory.net>
+References: <f45ba82cd83d27b5d44d3dc417e0e480ba0d3703.1515533863.git.mschiffer@universe-factory.net>
+From: Matthias Schiffer <mschiffer@universe-factory.net>
+Date: Mon, 19 Jun 2017 10:03:58 +0200
+Subject: [PATCH 2/4] vxlan: check valid combinations of address scopes
+
+* Multicast addresses are never valid as local address
+* Link-local IPv6 unicast addresses may only be used as remote when the
+  local address is link-local as well
+* Don't allow link-local IPv6 local/remote addresses without interface
+
+We also store in the flags field if link-local addresses are used for the
+follow-up patches that actually make VXLAN over link-local IPv6 work.
+
+Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+[Matthias Schiffer: rebase to v4.9.y]
+---
+ drivers/net/vxlan.c | 29 +++++++++++++++++++++++++++++
+ include/net/vxlan.h |  1 +
+ 2 files changed, 30 insertions(+)
+
+diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
+index fbe8da7fa296..863d9528b900 100644
+--- a/drivers/net/vxlan.c
++++ b/drivers/net/vxlan.c
+@@ -2880,11 +2880,35 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev,
+ 	if (conf->saddr.sa.sa_family != dst->remote_ip.sa.sa_family)
+ 		return -EINVAL;
+ 
++	if (vxlan_addr_multicast(&conf->saddr))
++		return -EINVAL;
++
+ 	if (conf->saddr.sa.sa_family == AF_INET6) {
+ 		if (!IS_ENABLED(CONFIG_IPV6))
+ 			return -EPFNOSUPPORT;
+ 		use_ipv6 = true;
+ 		vxlan->flags |= VXLAN_F_IPV6;
++
++		if (!(conf->flags & VXLAN_F_COLLECT_METADATA)) {
++			int local_type =
++				ipv6_addr_type(&conf->saddr.sin6.sin6_addr);
++			int remote_type =
++				ipv6_addr_type(&dst->remote_ip.sin6.sin6_addr);
++
++			if (local_type & IPV6_ADDR_LINKLOCAL) {
++				if (!(remote_type & IPV6_ADDR_LINKLOCAL) &&
++				    (remote_type != IPV6_ADDR_ANY))
++					return -EINVAL;
++
++				vxlan->flags |= VXLAN_F_IPV6_LINKLOCAL;
++			} else {
++				if (remote_type ==
++				    (IPV6_ADDR_UNICAST | IPV6_ADDR_LINKLOCAL))
++					return -EINVAL;
++
++				vxlan->flags &= ~VXLAN_F_IPV6_LINKLOCAL;
++			}
++		}
+ 	}
+ 
+ 	if (conf->label && !use_ipv6) {
+@@ -2918,6 +2942,11 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev,
+ 	} else if (vxlan_addr_multicast(&dst->remote_ip)) {
+ 		pr_info("multicast destination requires interface to be specified\n");
+ 		return -EINVAL;
++	} else {
++#if IS_ENABLED(CONFIG_IPV6)
++		if (vxlan->flags & VXLAN_F_IPV6_LINKLOCAL)
++			return -EINVAL;
++#endif
+ 	}
+ 
+ 	if (conf->mtu) {
+diff --git a/include/net/vxlan.h b/include/net/vxlan.h
+index 9fce47e3e13e..c1c0d03e3456 100644
+--- a/include/net/vxlan.h
++++ b/include/net/vxlan.h
+@@ -267,6 +267,7 @@ struct vxlan_dev {
+ #define VXLAN_F_REMCSUM_NOPARTIAL	0x1000
+ #define VXLAN_F_COLLECT_METADATA	0x2000
+ #define VXLAN_F_GPE			0x4000
++#define VXLAN_F_IPV6_LINKLOCAL		0x8000
+ 
+ /* Flags that are used in the receive path. These flags must match in
+  * order for a socket to be shareable
+-- 
+2.15.1
+
diff --git a/target/linux/generic/backport-4.9/095-0003-vxlan-fix-snooping-for-link-local-IPv6-addresses.patch b/target/linux/generic/backport-4.9/095-0003-vxlan-fix-snooping-for-link-local-IPv6-addresses.patch
new file mode 100644
index 0000000000000000000000000000000000000000..89523ac027b227a9f84b1130db06a7fc67ff68ce
--- /dev/null
+++ b/target/linux/generic/backport-4.9/095-0003-vxlan-fix-snooping-for-link-local-IPv6-addresses.patch
@@ -0,0 +1,88 @@
+From 010b2b541d958e12d78ba1c79734c700f169610b Mon Sep 17 00:00:00 2001
+Message-Id: <010b2b541d958e12d78ba1c79734c700f169610b.1515533863.git.mschiffer@universe-factory.net>
+In-Reply-To: <f45ba82cd83d27b5d44d3dc417e0e480ba0d3703.1515533863.git.mschiffer@universe-factory.net>
+References: <f45ba82cd83d27b5d44d3dc417e0e480ba0d3703.1515533863.git.mschiffer@universe-factory.net>
+From: Matthias Schiffer <mschiffer@universe-factory.net>
+Date: Mon, 19 Jun 2017 10:03:59 +0200
+Subject: [PATCH 3/4] vxlan: fix snooping for link-local IPv6 addresses
+
+If VXLAN is run over link-local IPv6 addresses, it is necessary to store
+the ifindex in the FDB entries. Otherwise, the used interface is undefined
+and unicast communication will most likely fail.
+
+Support for link-local IPv4 addresses should be possible as well, but as
+the semantics aren't as well defined as for IPv6, and there doesn't seem to
+be much interest in having the support, it's not implemented for now.
+
+Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+[Matthias Schiffer: rebase to v4.9.y]
+---
+ drivers/net/vxlan.c | 20 +++++++++++++++-----
+ 1 file changed, 15 insertions(+), 5 deletions(-)
+
+--- a/drivers/net/vxlan.c
++++ b/drivers/net/vxlan.c
+@@ -917,16 +917,25 @@ out:
+  * Return true if packet is bogus and should be dropped.
+  */
+ 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,
++			u32 src_ifindex)
+ {
+ 	struct vxlan_dev *vxlan = netdev_priv(dev);
+ 	struct vxlan_fdb *f;
++	u32 ifindex = 0;
++
++#if IS_ENABLED(CONFIG_IPV6)
++	if (src_ip->sa.sa_family == AF_INET6 &&
++	    (ipv6_addr_type(&src_ip->sin6.sin6_addr) & IPV6_ADDR_LINKLOCAL))
++		ifindex = src_ifindex;
++#endif
+ 
+ 	f = vxlan_find_mac(vxlan, src_mac);
+ 	if (likely(f)) {
+ 		struct vxlan_rdst *rdst = first_remote_rcu(f);
+ 
+-		if (likely(vxlan_addr_equal(&rdst->remote_ip, src_ip)))
++		if (likely(vxlan_addr_equal(&rdst->remote_ip, src_ip) &&
++			   rdst->remote_ifindex == ifindex))
+ 			return false;
+ 
+ 		/* Don't migrate static entries, drop packets */
+@@ -952,7 +961,7 @@ static bool vxlan_snoop(struct net_devic
+ 					 NLM_F_EXCL|NLM_F_CREATE,
+ 					 vxlan->cfg.dst_port,
+ 					 vxlan->default_dst.remote_vni,
+-					 0, NTF_SELF);
++					 ifindex, NTF_SELF);
+ 		spin_unlock(&vxlan->hash_lock);
+ 	}
+ 
+@@ -1223,6 +1232,7 @@ static bool vxlan_set_mac(struct vxlan_d
+ 			  struct sk_buff *skb)
+ {
+ 	union vxlan_addr saddr;
++	u32 ifindex = skb->dev->ifindex;
+ 
+ 	skb_reset_mac_header(skb);
+ 	skb->protocol = eth_type_trans(skb, vxlan->dev);
+@@ -1244,7 +1254,7 @@ static bool vxlan_set_mac(struct vxlan_d
+ 	}
+ 
+ 	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, ifindex))
+ 		return false;
+ 
+ 	return true;
+@@ -1939,7 +1949,7 @@ static void vxlan_encap_bypass(struct sk
+ 	}
+ 
+ 	if (dst_vxlan->flags & VXLAN_F_LEARN)
+-		vxlan_snoop(dev, &loopback, eth_hdr(skb)->h_source);
++		vxlan_snoop(dev, &loopback, eth_hdr(skb)->h_source, 0);
+ 
+ 	u64_stats_update_begin(&tx_stats->syncp);
+ 	tx_stats->tx_packets++;
diff --git a/target/linux/generic/backport-4.9/095-0004-vxlan-allow-multiple-VXLANs-with-same-VNI-for-IPv6-l.patch b/target/linux/generic/backport-4.9/095-0004-vxlan-allow-multiple-VXLANs-with-same-VNI-for-IPv6-l.patch
new file mode 100644
index 0000000000000000000000000000000000000000..18ae230a3b04d2b57184109fa14f9533f0fb7192
--- /dev/null
+++ b/target/linux/generic/backport-4.9/095-0004-vxlan-allow-multiple-VXLANs-with-same-VNI-for-IPv6-l.patch
@@ -0,0 +1,182 @@
+From 95583c75a95449dade713e1dad4ed0a8dcc1b391 Mon Sep 17 00:00:00 2001
+Message-Id: <95583c75a95449dade713e1dad4ed0a8dcc1b391.1515533863.git.mschiffer@universe-factory.net>
+In-Reply-To: <f45ba82cd83d27b5d44d3dc417e0e480ba0d3703.1515533863.git.mschiffer@universe-factory.net>
+References: <f45ba82cd83d27b5d44d3dc417e0e480ba0d3703.1515533863.git.mschiffer@universe-factory.net>
+From: Matthias Schiffer <mschiffer@universe-factory.net>
+Date: Mon, 19 Jun 2017 10:04:00 +0200
+Subject: [PATCH 4/4] 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: Matthias Schiffer <mschiffer@universe-factory.net>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+[Matthias Schiffer: rebase to v4.9.y]
+---
+ drivers/net/vxlan.c | 60 ++++++++++++++++++++++++++++++++++++-----------------
+ 1 file changed, 41 insertions(+), 19 deletions(-)
+
+diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
+index c28c6f34b3b3..9208e3d9ec43 100644
+--- a/drivers/net/vxlan.c
++++ b/drivers/net/vxlan.c
+@@ -225,7 +225,8 @@ static struct vxlan_sock *vxlan_find_sock(struct net *net, sa_family_t family,
+ 	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_node *node;
+ 
+@@ -234,17 +235,27 @@ static struct vxlan_dev *vxlan_vs_find_vni(struct vxlan_sock *vs, __be32 vni)
+ 		vni = 0;
+ 
+ 	hlist_for_each_entry_rcu(node, vni_head(vs, vni), hlist) {
+-		if (node->vxlan->default_dst.remote_vni == vni)
+-			return node->vxlan;
++		if (node->vxlan->default_dst.remote_vni != vni)
++			continue;
++
++		if (IS_ENABLED(CONFIG_IPV6)) {
++			const struct vxlan_config *cfg = &node->vxlan->cfg;
++
++			if ((node->vxlan->flags & VXLAN_F_IPV6_LINKLOCAL) &&
++			    cfg->remote_ifindex != ifindex)
++				continue;
++		}
++
++		return node->vxlan;
+ 	}
+ 
+ 	return NULL;
+ }
+ 
+ /* Look up VNI in a per net namespace table */
+-static struct vxlan_dev *vxlan_find_vni(struct net *net, __be32 vni,
+-					sa_family_t family, __be16 port,
+-					u32 flags)
++static struct vxlan_dev *vxlan_find_vni(struct net *net, int ifindex,
++				        __be32 vni, sa_family_t family,
++					__be16 port, u32 flags)
+ {
+ 	struct vxlan_sock *vs;
+ 
+@@ -252,7 +263,7 @@ static struct vxlan_dev *vxlan_find_vni(struct net *net, __be32 vni,
+ 	if (!vs)
+ 		return NULL;
+ 
+-	return vxlan_vs_find_vni(vs, vni);
++	return vxlan_vs_find_vni(vs, ifindex, vni);
+ }
+ 
+ /* Fill in neighbour message in skbuff. */
+@@ -1317,7 +1328,8 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
+ 	if (!vs)
+ 		goto drop;
+ 
+-	vxlan = vxlan_vs_find_vni(vs, vxlan_vni(vxlan_hdr(skb)->vx_vni));
++	vxlan = vxlan_vs_find_vni(vs, skb->dev->ifindex,
++                                  vxlan_vni(vxlan_hdr(skb)->vx_vni));
+ 	if (!vxlan)
+ 		goto drop;
+ 
+@@ -1976,6 +1988,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
+ 	__be32 vni, label;
+ 	__be16 df = 0;
+ 	__u8 tos, ttl;
++	int ifindex;
+ 	int err;
+ 	u32 flags = vxlan->flags;
+ 	bool udp_sum = false;
+@@ -1987,6 +2000,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
+ 	if (rdst) {
+ 		dst_port = rdst->remote_port ? rdst->remote_port : vxlan->cfg.dst_port;
+ 		vni = rdst->remote_vni;
++		ifindex = rdst->remote_ifindex;
+ 		dst = &rdst->remote_ip;
+ 		local_ip = vxlan->cfg.saddr;
+ 		dst_cache = &rdst->dst_cache;
+@@ -1998,6 +2012,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
+ 		}
+ 		dst_port = info->key.tp_dst ? : vxlan->cfg.dst_port;
+ 		vni = tunnel_id_to_key32(info->key.tun_id);
++		ifindex = 0;
+ 		remote_ip.sa.sa_family = ip_tunnel_info_af(info);
+ 		if (remote_ip.sa.sa_family == AF_INET) {
+ 			remote_ip.sin.sin_addr.s_addr = info->key.u.ipv4.dst;
+@@ -2053,7 +2068,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
+ 		sk = sock4->sock->sk;
+ 
+ 		rt = vxlan_get_route(vxlan, skb,
+-				     rdst ? rdst->remote_ifindex : 0, tos,
++				     ifindex, tos,
+ 				     dst->sin.sin_addr.s_addr,
+ 				     &local_ip.sin.sin_addr.s_addr,
+ 				     dst_cache, info);
+@@ -2077,7 +2092,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
+ 			struct vxlan_dev *dst_vxlan;
+ 
+ 			ip_rt_put(rt);
+-			dst_vxlan = vxlan_find_vni(vxlan->net, vni,
++			dst_vxlan = vxlan_find_vni(vxlan->net, ifindex, vni,
+ 						   dst->sa.sa_family, dst_port,
+ 						   vxlan->flags);
+ 			if (!dst_vxlan)
+@@ -2112,7 +2127,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
+ 		sk = sock6->sock->sk;
+ 
+ 		ndst = vxlan6_get_route(vxlan, skb,
+-					rdst ? rdst->remote_ifindex : 0, tos,
++					ifindex, tos,
+ 					label, &dst->sin6.sin6_addr,
+ 					&local_ip.sin6.sin6_addr,
+ 					dst_cache, info);
+@@ -2138,7 +2153,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
+ 			struct vxlan_dev *dst_vxlan;
+ 
+ 			dst_release(ndst);
+-			dst_vxlan = vxlan_find_vni(vxlan->net, vni,
++			dst_vxlan = vxlan_find_vni(vxlan->net, ifindex, vni,
+ 						   dst->sa.sa_family, dst_port,
+ 						   vxlan->flags);
+ 			if (!dst_vxlan)
+@@ -2984,13 +2999,20 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev,
+ 		vxlan->cfg.age_interval = FDB_AGE_DEFAULT;
+ 
+ 	list_for_each_entry(tmp, &vn->vxlan_list, next) {
+-		if (tmp->cfg.vni == conf->vni &&
+-		    tmp->cfg.dst_port == vxlan->cfg.dst_port &&
+-		    (tmp->flags & (VXLAN_F_RCV_FLAGS | VXLAN_F_IPV6)) ==
+-		    (vxlan->flags & (VXLAN_F_RCV_FLAGS | VXLAN_F_IPV6))) {
+-			pr_info("duplicate VNI %u\n", be32_to_cpu(conf->vni));
+-			return -EEXIST;
+-		}
++		if (tmp->cfg.vni != conf->vni)
++			continue;
++		if (tmp->cfg.dst_port != vxlan->cfg.dst_port)
++			continue;
++		if ((tmp->flags & (VXLAN_F_RCV_FLAGS | VXLAN_F_IPV6)) !=
++		    (vxlan->flags & (VXLAN_F_RCV_FLAGS | VXLAN_F_IPV6)))
++			continue;
++
++		if ((vxlan->flags & VXLAN_F_IPV6_LINKLOCAL) &&
++		    tmp->cfg.remote_ifindex != vxlan->cfg.remote_ifindex)
++			continue;
++
++		pr_info("duplicate VNI %u\n", be32_to_cpu(conf->vni));
++		return -EEXIST;
+ 	}
+ 
+ 	dev->ethtool_ops = &vxlan_ethtool_ops;
+-- 
+2.15.1
+
