From: Matthias Schiffer Date: Wed, 21 Jun 2017 00:54:57 +0200 Subject: generic: vxlan: backport support for VXLAN over link-local IPv6 diff --git a/target/linux/generic/patches-4.4/075-0001-vxlan-improve-validation-of-address-family-configura.patch b/target/linux/generic/patches-4.4/075-0001-vxlan-improve-validation-of-address-family-configura.patch new file mode 100644 index 0000000000000000000000000000000000000000..439eb5d0f769f1cde23ff4deacc80728e82605f2 --- /dev/null +++ b/target/linux/generic/patches-4.4/075-0001-vxlan-improve-validation-of-address-family-configura.patch @@ -0,0 +1,68 @@ +From 434a1bb54b24b538f81d7945128b7ca25976d19b Mon Sep 17 00:00:00 2001 +Message-Id: <434a1bb54b24b538f81d7945128b7ca25976d19b.1498005061.git.mschiffer@universe-factory.net> +From: Matthias Schiffer +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 +Signed-off-by: David S. Miller +[Matthias Schiffer: rebase to v4.4.y] +--- + drivers/net/vxlan.c | 23 +++++++++++++++-------- + 1 file changed, 15 insertions(+), 8 deletions(-) + +--- a/drivers/net/vxlan.c ++++ b/drivers/net/vxlan.c +@@ -2784,12 +2784,20 @@ static int vxlan_dev_configure(struct ne + + 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; +@@ -2843,11 +2851,9 @@ static int vxlan_dev_configure(struct ne + + 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))) + return -EEXIST; + } + +@@ -2915,6 +2921,7 @@ static int vxlan_newlink(struct net *src + + 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; diff --git a/target/linux/generic/patches-4.4/075-0002-vxlan-check-valid-combinations-of-address-scopes.patch b/target/linux/generic/patches-4.4/075-0002-vxlan-check-valid-combinations-of-address-scopes.patch new file mode 100644 index 0000000000000000000000000000000000000000..abf9b8a0eae88aa5a64be5cc82a6827d4d562c55 --- /dev/null +++ b/target/linux/generic/patches-4.4/075-0002-vxlan-check-valid-combinations-of-address-scopes.patch @@ -0,0 +1,84 @@ +From 8956b9db43347a51e88dddc3c08fb88ff60dea54 Mon Sep 17 00:00:00 2001 +Message-Id: <8956b9db43347a51e88dddc3c08fb88ff60dea54.1498005061.git.mschiffer@universe-factory.net> +In-Reply-To: <434a1bb54b24b538f81d7945128b7ca25976d19b.1498005061.git.mschiffer@universe-factory.net> +References: <434a1bb54b24b538f81d7945128b7ca25976d19b.1498005061.git.mschiffer@universe-factory.net> +From: Matthias Schiffer +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 +Signed-off-by: David S. Miller +[Matthias Schiffer: rebase to v4.4.y] +--- + drivers/net/vxlan.c | 29 +++++++++++++++++++++++++++++ + include/net/vxlan.h | 1 + + 2 files changed, 30 insertions(+) + +--- a/drivers/net/vxlan.c ++++ b/drivers/net/vxlan.c +@@ -2797,11 +2797,35 @@ static int vxlan_dev_configure(struct ne + 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->remote_ifindex) { +@@ -2827,6 +2851,11 @@ static int vxlan_dev_configure(struct ne + dev->mtu = lowerdev->mtu - (use_ipv6 ? VXLAN6_HEADROOM : VXLAN_HEADROOM); + + needed_headroom = lowerdev->hard_header_len; ++ } else { ++#if IS_ENABLED(CONFIG_IPV6) ++ if (vxlan->flags & VXLAN_F_IPV6_LINKLOCAL) ++ return -EINVAL; ++#endif + } + + if (conf->mtu) { +--- a/include/net/vxlan.h ++++ b/include/net/vxlan.h +@@ -185,6 +185,7 @@ struct vxlan_dev { + #define VXLAN_F_GBP 0x800 + #define VXLAN_F_REMCSUM_NOPARTIAL 0x1000 + #define VXLAN_F_COLLECT_METADATA 0x2000 ++#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 diff --git a/target/linux/generic/patches-4.4/075-0003-vxlan-fix-snooping-for-link-local-IPv6-addresses.patch b/target/linux/generic/patches-4.4/075-0003-vxlan-fix-snooping-for-link-local-IPv6-addresses.patch new file mode 100644 index 0000000000000000000000000000000000000000..5ebbce6e0682fff11b0d86c8df781a5c2fab5125 --- /dev/null +++ b/target/linux/generic/patches-4.4/075-0003-vxlan-fix-snooping-for-link-local-IPv6-addresses.patch @@ -0,0 +1,88 @@ +From e3bdb4bc6c4020e90c1bbafd91645ff3ae8966b9 Mon Sep 17 00:00:00 2001 +Message-Id: +In-Reply-To: <434a1bb54b24b538f81d7945128b7ca25976d19b.1498005061.git.mschiffer@universe-factory.net> +References: <434a1bb54b24b538f81d7945128b7ca25976d19b.1498005061.git.mschiffer@universe-factory.net> +From: Matthias Schiffer +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 +Signed-off-by: David S. Miller +[Matthias Schiffer: rebase to v4.4.y] +--- + drivers/net/vxlan.c | 20 +++++++++++++++----- + 1 file changed, 15 insertions(+), 5 deletions(-) + +--- a/drivers/net/vxlan.c ++++ b/drivers/net/vxlan.c +@@ -947,16 +947,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 */ +@@ -982,7 +991,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); + } + +@@ -1157,6 +1166,7 @@ static void vxlan_rcv(struct vxlan_sock + struct vxlan_dev *vxlan; + struct pcpu_sw_netstats *stats; + union vxlan_addr saddr; ++ u32 ifindex = skb->dev->ifindex; + int err = 0; + + /* For flow based devices, map all packets to VNI 0 */ +@@ -1196,7 +1206,7 @@ static void vxlan_rcv(struct vxlan_sock + } + + 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)) + goto drop; + + skb_reset_network_header(skb); +@@ -1898,7 +1908,7 @@ static void vxlan_encap_bypass(struct sk + } + + 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, 0); + + u64_stats_update_begin(&tx_stats->syncp); + tx_stats->tx_packets++; diff --git a/target/linux/generic/patches-4.4/075-0004-vxlan-allow-multiple-VXLANs-with-same-VNI-for-IPv6-l.patch b/target/linux/generic/patches-4.4/075-0004-vxlan-allow-multiple-VXLANs-with-same-VNI-for-IPv6-l.patch new file mode 100644 index 0000000000000000000000000000000000000000..c184c32385e6c802f1bed7647ce720f0e429f4a5 --- /dev/null +++ b/target/linux/generic/patches-4.4/075-0004-vxlan-allow-multiple-VXLANs-with-same-VNI-for-IPv6-l.patch @@ -0,0 +1,168 @@ +From 7a1fa05f8d460e2a81cb724f441f7346f950680a Mon Sep 17 00:00:00 2001 +Message-Id: <7a1fa05f8d460e2a81cb724f441f7346f950680a.1498005061.git.mschiffer@universe-factory.net> +In-Reply-To: <434a1bb54b24b538f81d7945128b7ca25976d19b.1498005061.git.mschiffer@universe-factory.net> +References: <434a1bb54b24b538f81d7945128b7ca25976d19b.1498005061.git.mschiffer@universe-factory.net> +From: Matthias Schiffer +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 +Signed-off-by: David S. Miller +[Matthias Schiffer: rebase to v4.4.y] +--- + drivers/net/vxlan.c | 53 +++++++++++++++++++++++++++++++++++++---------------- + 1 file changed, 37 insertions(+), 16 deletions(-) + +--- a/drivers/net/vxlan.c ++++ b/drivers/net/vxlan.c +@@ -242,22 +242,33 @@ static struct vxlan_sock *vxlan_find_soc + return NULL; + } + +-static struct vxlan_dev *vxlan_vs_find_vni(struct vxlan_sock *vs, u32 id) ++static struct vxlan_dev *vxlan_vs_find_vni(struct vxlan_sock *vs, int ifindex, ++ u32 id) + { + struct vxlan_dev *vxlan; + + hlist_for_each_entry_rcu(vxlan, vni_head(vs, id), hlist) { +- if (vxlan->default_dst.remote_vni == id) +- return vxlan; ++ if (vxlan->default_dst.remote_vni != id) ++ continue; ++ ++ if (IS_ENABLED(CONFIG_IPV6)) { ++ const struct vxlan_config *cfg = &vxlan->cfg; ++ ++ if ((vxlan->flags & VXLAN_F_IPV6_LINKLOCAL) && ++ cfg->remote_ifindex != ifindex) ++ continue; ++ } ++ ++ return vxlan; + } + + return NULL; + } + + /* Look up VNI in a per net namespace table */ +-static struct vxlan_dev *vxlan_find_vni(struct net *net, u32 id, +- sa_family_t family, __be16 port, +- u32 flags) ++static struct vxlan_dev *vxlan_find_vni(struct net *net, int ifindex, ++ u32 id, sa_family_t family, ++ __be16 port, u32 flags) + { + struct vxlan_sock *vs; + +@@ -265,7 +276,7 @@ static struct vxlan_dev *vxlan_find_vni( + if (!vs) + return NULL; + +- return vxlan_vs_find_vni(vs, id); ++ return vxlan_vs_find_vni(vs, ifindex, id); + } + + /* Fill in neighbour message in skbuff. */ +@@ -1174,7 +1185,7 @@ static void vxlan_rcv(struct vxlan_sock + vni = 0; + + /* Is this VNI defined? */ +- vxlan = vxlan_vs_find_vni(vs, vni); ++ vxlan = vxlan_vs_find_vni(vs, skb->dev->ifindex, vni); + if (!vxlan) + goto drop; + +@@ -1942,6 +1953,7 @@ static void vxlan_xmit_one(struct sk_buf + u32 vni; + __be16 df = 0; + __u8 tos, ttl; ++ int ifindex; + int err; + u32 flags = vxlan->flags; + +@@ -1950,6 +1962,7 @@ static void vxlan_xmit_one(struct sk_buf + 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; + } else { + if (!info) { +@@ -1959,6 +1972,7 @@ static void vxlan_xmit_one(struct sk_buf + } + dst_port = info->key.tp_dst ? : vxlan->cfg.dst_port; + vni = be64_to_cpu(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; +@@ -2015,7 +2029,7 @@ static void vxlan_xmit_one(struct sk_buf + } + + memset(&fl4, 0, sizeof(fl4)); +- fl4.flowi4_oif = rdst ? rdst->remote_ifindex : 0; ++ fl4.flowi4_oif = ifindex; + fl4.flowi4_tos = RT_TOS(tos); + fl4.flowi4_mark = skb->mark; + fl4.flowi4_proto = IPPROTO_UDP; +@@ -2043,7 +2057,7 @@ static void vxlan_xmit_one(struct sk_buf + 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) +@@ -2076,8 +2090,7 @@ static void vxlan_xmit_one(struct sk_buf + goto drop; + sk = vxlan->vn6_sock->sock->sk; + +- ndst = vxlan6_get_route(vxlan, skb, +- rdst ? rdst->remote_ifindex : 0, ++ ndst = vxlan6_get_route(vxlan, skb, ifindex, + &dst->sin6.sin6_addr, &saddr); + if (IS_ERR(ndst)) { + netdev_dbg(dev, "no route to %pI6\n", +@@ -2101,7 +2114,7 @@ static void vxlan_xmit_one(struct sk_buf + 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) +@@ -2889,10 +2902,18 @@ static int vxlan_dev_configure(struct ne + 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)) == ++ 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; ++ + return -EEXIST; + } +