From: Matthias Schiffer 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 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: +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.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: +References: +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.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: +References: +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.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: +References: +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.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 +