[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1369628193-14925-8-git-send-email-amwang@redhat.com>
Date: Mon, 27 May 2013 12:16:29 +0800
From: Cong Wang <amwang@...hat.com>
To: netdev@...r.kernel.org
Cc: David Stevens <dlstevens@...ibm.com>,
Stephen Hemminger <stephen@...workplumber.org>,
"David S. Miller" <davem@...emloft.net>,
Cong Wang <amwang@...hat.com>
Subject: [Patch net-next v9 07/11] vxlan: add ipv6 support
From: Cong Wang <amwang@...hat.com>
This patch adds IPv6 support to vxlan device, as the new version
RFC already mentions it:
http://tools.ietf.org/html/draft-mahalingam-dutt-dcops-vxlan-03
Cc: David Stevens <dlstevens@...ibm.com>
Cc: Stephen Hemminger <stephen@...workplumber.org>
Cc: David S. Miller <davem@...emloft.net>
Signed-off-by: Cong Wang <amwang@...hat.com>
---
drivers/net/vxlan.c | 731 +++++++++++++++++++++++++++++++++---------
include/uapi/linux/if_link.h | 2 +
2 files changed, 587 insertions(+), 146 deletions(-)
diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
index 5b37f76..8ebb670 100644
--- a/drivers/net/vxlan.c
+++ b/drivers/net/vxlan.c
@@ -6,9 +6,6 @@
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
- *
- * TODO
- * - IPv6 (not in RFC)
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -41,6 +38,11 @@
#include <net/inet_ecn.h>
#include <net/net_namespace.h>
#include <net/netns/generic.h>
+#if IS_ENABLED(CONFIG_IPV6)
+#include <net/addrconf.h>
+#include <net/ip6_route.h>
+#include <net/ip6_tunnel.h>
+#endif
#define VXLAN_VERSION "0.1"
@@ -57,6 +59,8 @@
#define VXLAN_VID_MASK (VXLAN_N_VID - 1)
/* IP header + UDP + VXLAN + Ethernet header */
#define VXLAN_HEADROOM (20 + 8 + 8 + 14)
+/* IPv6 header + UDP + VXLAN + Ethernet header */
+#define VXLAN6_HEADROOM (40 + 8 + 8 + 14)
#define VXLAN_FLAGS 0x08000000 /* struct vxlanhdr.vx_flags required value. */
@@ -79,6 +83,9 @@ module_param(log_ecn_error, bool, 0644);
MODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN");
static unsigned int vxlan_net_id;
+#if IS_ENABLED(CONFIG_IPV6)
+static bool ipv6_disabled = false;
+#endif
/* per UDP socket information */
struct vxlan_sock {
@@ -96,9 +103,15 @@ struct vxlan_net {
struct hlist_head sock_list[PORT_HASH_SIZE];
};
+union vxlan_addr {
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ struct sockaddr sa;
+};
+
struct vxlan_rdst {
struct rcu_head rcu;
- __be32 remote_ip;
+ union vxlan_addr remote_ip;
__be16 remote_port;
u32 remote_vni;
u32 remote_ifindex;
@@ -124,7 +137,7 @@ struct vxlan_dev {
struct vxlan_sock *vn_sock; /* listening socket */
struct net_device *dev;
struct vxlan_rdst default_dst; /* default destination */
- __be32 saddr; /* source address */
+ union vxlan_addr saddr; /* source address */
__be16 dst_port;
__u16 port_min; /* source port range */
__u16 port_max;
@@ -147,6 +160,97 @@ struct vxlan_dev {
#define VXLAN_F_L2MISS 0x08
#define VXLAN_F_L3MISS 0x10
+#if IS_ENABLED(CONFIG_IPV6)
+static inline
+bool vxlan_addr_equal(const union vxlan_addr *a, const union vxlan_addr *b)
+{
+ if (a->sa.sa_family != b->sa.sa_family)
+ return false;
+ if (a->sa.sa_family == AF_INET6)
+ return ipv6_addr_equal(&a->sin6.sin6_addr, &b->sin6.sin6_addr);
+ else
+ return a->sin.sin_addr.s_addr == b->sin.sin_addr.s_addr;
+}
+
+static inline bool vxlan_addr_any(const union vxlan_addr *ipa)
+{
+ if (ipa->sa.sa_family == AF_INET6)
+ return ipv6_addr_any(&ipa->sin6.sin6_addr);
+ else
+ return ipa->sin.sin_addr.s_addr == htonl(INADDR_ANY);
+}
+
+static inline bool vxlan_addr_multicast(const union vxlan_addr *ipa)
+{
+ if (ipa->sa.sa_family == AF_INET6)
+ return ipv6_addr_is_multicast(&ipa->sin6.sin6_addr);
+ else
+ return IN_MULTICAST(ntohl(ipa->sin.sin_addr.s_addr));
+}
+
+static int vxlan_nla_get_addr(union vxlan_addr *ip, struct nlattr *nla)
+{
+ if (nla_len(nla) >= sizeof(struct in6_addr)) {
+ nla_memcpy(&ip->sin6.sin6_addr, nla, sizeof(struct in6_addr));
+ ip->sa.sa_family = AF_INET6;
+ return 0;
+ } else if (nla_len(nla) >= sizeof(__be32)) {
+ ip->sin.sin_addr.s_addr = nla_get_be32(nla);
+ ip->sa.sa_family = AF_INET;
+ return 0;
+ } else {
+ return -EAFNOSUPPORT;
+ }
+}
+
+static int vxlan_nla_put_addr(struct sk_buff *skb, int attr,
+ const union vxlan_addr *ip)
+{
+ if (ip->sa.sa_family == AF_INET6)
+ return nla_put(skb, attr, sizeof(struct in6_addr), &ip->sin6.sin6_addr);
+ else
+ return nla_put_be32(skb, attr, ip->sin.sin_addr.s_addr);
+}
+
+#else /* !CONFIG_IPV6 */
+
+static inline
+bool vxlan_addr_equal(const union vxlan_addr *a, const union vxlan_addr *b)
+{
+ return a->sin.sin_addr.s_addr == b->sin.sin_addr.s_addr;
+}
+
+static inline bool vxlan_addr_any(const union vxlan_addr *ipa)
+{
+ return ipa->sin.sin_addr.s_addr == htonl(INADDR_ANY);
+}
+
+static inline bool vxlan_addr_multicast(const union vxlan_addr *ipa)
+{
+ return IN_MULTICAST(ntohl(ipa->sin.sin_addr.s_addr));
+}
+
+static int vxlan_nla_get_addr(union vxlan_addr *ip, struct nlattr *nla)
+{
+ if (nla_len(nla) >= sizeof(struct in6_addr)) {
+ return -EAFNOSUPPORT;
+ } else if (nla_len(nla) >= sizeof(__be32)) {
+ ip->sin.sin_addr.s_addr = nla_get_be32(nla);
+ ip->sa.sa_family = AF_INET;
+ return 0;
+ } else {
+ return -EAFNOSUPPORT;
+ }
+}
+
+static int vxlan_nla_put_addr(struct sk_buff *skb, int attr,
+ const union vxlan_addr *ip)
+{
+ return nla_put_be32(skb, attr, ip->sin.sin_addr.s_addr);
+}
+
+#endif
+
/* salt for hash table */
static u32 vxlan_salt __read_mostly;
@@ -217,7 +321,7 @@ static int vxlan_fdb_info(struct sk_buff *skb, struct vxlan_dev *vxlan,
if (type == RTM_GETNEIGH) {
ndm->ndm_family = AF_INET;
- send_ip = rdst->remote_ip != htonl(INADDR_ANY);
+ send_ip = !vxlan_addr_any(&rdst->remote_ip);
send_eth = !is_zero_ether_addr(fdb->eth_addr);
} else
ndm->ndm_family = AF_BRIDGE;
@@ -229,7 +333,7 @@ static int vxlan_fdb_info(struct sk_buff *skb, struct vxlan_dev *vxlan,
if (send_eth && nla_put(skb, NDA_LLADDR, ETH_ALEN, &fdb->eth_addr))
goto nla_put_failure;
- if (send_ip && nla_put_be32(skb, NDA_DST, rdst->remote_ip))
+ if (send_ip && vxlan_nla_put_addr(skb, NDA_DST, &rdst->remote_ip))
goto nla_put_failure;
if (rdst->remote_port && rdst->remote_port != vxlan->dst_port &&
@@ -261,7 +365,7 @@ static inline size_t vxlan_nlmsg_size(void)
{
return NLMSG_ALIGN(sizeof(struct ndmsg))
+ nla_total_size(ETH_ALEN) /* NDA_LLADDR */
- + nla_total_size(sizeof(__be32)) /* NDA_DST */
+ + nla_total_size(sizeof(struct in6_addr)) /* NDA_DST */
+ nla_total_size(sizeof(__be16)) /* NDA_PORT */
+ nla_total_size(sizeof(__be32)) /* NDA_VNI */
+ nla_total_size(sizeof(__u32)) /* NDA_IFINDEX */
@@ -294,14 +398,14 @@ errout:
rtnl_set_sk_err(net, RTNLGRP_NEIGH, err);
}
-static void vxlan_ip_miss(struct net_device *dev, __be32 ipa)
+static void vxlan_ip_miss(struct net_device *dev, union vxlan_addr *ipa)
{
struct vxlan_dev *vxlan = netdev_priv(dev);
struct vxlan_fdb f;
memset(&f, 0, sizeof f);
f.state = NUD_STALE;
- f.remote.remote_ip = ipa; /* goes to NDA_DST */
+ f.remote.remote_ip = *ipa; /* goes to NDA_DST */
f.remote.remote_vni = VXLAN_N_VID;
vxlan_fdb_notify(vxlan, &f, RTM_GETNEIGH);
@@ -368,14 +472,14 @@ static struct vxlan_fdb *vxlan_find_mac(struct vxlan_dev *vxlan,
}
/* Add/update destinations for multicast */
-static int vxlan_fdb_append(struct vxlan_fdb *f,
- __be32 ip, __be16 port, __u32 vni, __u32 ifindex)
+static int vxlan_fdb_append(struct vxlan_fdb *f, union vxlan_addr *ip,
+ __be16 port, __u32 vni, __u32 ifindex)
{
struct vxlan_rdst *rd_prev, *rd;
rd_prev = NULL;
for (rd = &f->remote; rd; rd = rd->remote_next) {
- if (rd->remote_ip == ip &&
+ if (vxlan_addr_equal(&rd->remote_ip, ip) &&
rd->remote_port == port &&
rd->remote_vni == vni &&
rd->remote_ifindex == ifindex)
@@ -385,7 +489,7 @@ static int vxlan_fdb_append(struct vxlan_fdb *f,
rd = kmalloc(sizeof(*rd), GFP_ATOMIC);
if (rd == NULL)
return -ENOBUFS;
- rd->remote_ip = ip;
+ rd->remote_ip = *ip;
rd->remote_port = port;
rd->remote_vni = vni;
rd->remote_ifindex = ifindex;
@@ -396,7 +500,7 @@ static int vxlan_fdb_append(struct vxlan_fdb *f,
/* Add new entry to forwarding table -- assumes lock held */
static int vxlan_fdb_create(struct vxlan_dev *vxlan,
- const u8 *mac, __be32 ip,
+ const u8 *mac, union vxlan_addr *ip,
__u16 state, __u16 flags,
__be16 port, __u32 vni, __u32 ifindex,
__u8 ndm_flags)
@@ -436,13 +540,20 @@ static int vxlan_fdb_create(struct vxlan_dev *vxlan,
if (vxlan->addrmax && vxlan->addrcnt >= vxlan->addrmax)
return -ENOSPC;
- netdev_dbg(vxlan->dev, "add %pM -> %pI4\n", mac, &ip);
+#if IS_ENABLED(CONFIG_IPV6)
+ if (ip->sa.sa_family == AF_INET6)
+ netdev_dbg(vxlan->dev, "add %pM -> %pI6\n", mac,
+ &ip->sin6.sin6_addr);
+ else
+#endif
+ netdev_dbg(vxlan->dev, "add %pM -> %pI4\n", mac,
+ &ip->sin.sin_addr.s_addr);
f = kmalloc(sizeof(*f), GFP_ATOMIC);
if (!f)
return -ENOMEM;
notify = 1;
- f->remote.remote_ip = ip;
+ f->remote.remote_ip = *ip;
f->remote.remote_port = port;
f->remote.remote_vni = vni;
f->remote.remote_ifindex = ifindex;
@@ -495,7 +606,7 @@ static int vxlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
{
struct vxlan_dev *vxlan = netdev_priv(dev);
struct net *net = dev_net(vxlan->dev);
- __be32 ip;
+ union vxlan_addr ip;
__be16 port;
u32 vni, ifindex;
int err;
@@ -509,10 +620,9 @@ static int vxlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
if (tb[NDA_DST] == NULL)
return -EINVAL;
- if (nla_len(tb[NDA_DST]) != sizeof(__be32))
- return -EAFNOSUPPORT;
-
- ip = nla_get_be32(tb[NDA_DST]);
+ err = vxlan_nla_get_addr(&ip, tb[NDA_DST]);
+ if (err)
+ return err;
if (tb[NDA_PORT]) {
if (nla_len(tb[NDA_PORT]) != sizeof(__be16))
@@ -542,7 +652,7 @@ static int vxlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
ifindex = 0;
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);
spin_unlock_bh(&vxlan->hash_lock);
@@ -606,7 +716,7 @@ skip:
* and Tunnel endpoint.
*/
static void vxlan_snoop(struct net_device *dev,
- __be32 src_ip, const u8 *src_mac)
+ union vxlan_addr *src_ip, const u8 *src_mac)
{
struct vxlan_dev *vxlan = netdev_priv(dev);
struct vxlan_fdb *f;
@@ -614,15 +724,25 @@ static void vxlan_snoop(struct net_device *dev,
f = vxlan_find_mac(vxlan, src_mac);
if (likely(f)) {
- if (likely(f->remote.remote_ip == src_ip))
+ if (likely(vxlan_addr_equal(&f->remote.remote_ip, src_ip)))
return;
- if (net_ratelimit())
- netdev_info(dev,
- "%pM migrated from %pI4 to %pI4\n",
- src_mac, &f->remote.remote_ip, &src_ip);
+ if (net_ratelimit()) {
+#if IS_ENABLED(CONFIG_IPV6)
+ if (src_ip->sa.sa_family == AF_INET6)
+ netdev_info(dev,
+ "%pM migrated from %pI6 to %pI6\n",
+ src_mac, &f->remote.remote_ip.sin6.sin6_addr,
+ &src_ip->sin6.sin6_addr);
+ else
+#endif
+ netdev_info(dev,
+ "%pM migrated from %pI4 to %pI4\n",
+ src_mac, &f->remote.remote_ip.sin.sin_addr.s_addr,
+ &src_ip->sin.sin_addr.s_addr);
+ }
- f->remote.remote_ip = src_ip;
+ f->remote.remote_ip = *src_ip;
f->updated = jiffies;
} else {
/* learned new entry */
@@ -651,7 +771,8 @@ static bool vxlan_group_used(struct vxlan_net *vn,
if (!netif_running(vxlan->dev))
continue;
- if (vxlan->default_dst.remote_ip == this->default_dst.remote_ip)
+ if (vxlan_addr_equal(&vxlan->default_dst.remote_ip,
+ &this->default_dst.remote_ip))
return true;
}
@@ -664,11 +785,12 @@ static int vxlan_join_group(struct net_device *dev)
struct vxlan_dev *vxlan = netdev_priv(dev);
struct vxlan_net *vn = net_generic(dev_net(dev), vxlan_net_id);
struct sock *sk = vxlan->vn_sock->sock->sk;
+ union vxlan_addr *ip = &vxlan->default_dst.remote_ip;
struct ip_mreqn mreq = {
- .imr_multiaddr.s_addr = vxlan->default_dst.remote_ip,
+ .imr_multiaddr.s_addr = ip->sin.sin_addr.s_addr,
.imr_ifindex = vxlan->default_dst.remote_ifindex,
};
- int err;
+ int err = 0;
/* Already a member of group */
if (vxlan_group_used(vn, vxlan))
@@ -677,7 +799,13 @@ static int vxlan_join_group(struct net_device *dev)
/* Need to drop RTNL to call multicast join */
rtnl_unlock();
lock_sock(sk);
- err = ip_mc_join_group(sk, &mreq);
+ if (ip->sa.sa_family == AF_INET)
+ err = ip_mc_join_group(sk, &mreq);
+#if IS_ENABLED(CONFIG_IPV6)
+ else
+ err = ipv6_stub->ipv6_sock_mc_join(sk, vxlan->default_dst.remote_ifindex,
+ &ip->sin6.sin6_addr);
+#endif
release_sock(sk);
rtnl_lock();
@@ -692,8 +820,9 @@ static int vxlan_leave_group(struct net_device *dev)
struct vxlan_net *vn = net_generic(dev_net(dev), vxlan_net_id);
int err = 0;
struct sock *sk = vxlan->vn_sock->sock->sk;
+ union vxlan_addr *ip = &vxlan->default_dst.remote_ip;
struct ip_mreqn mreq = {
- .imr_multiaddr.s_addr = vxlan->default_dst.remote_ip,
+ .imr_multiaddr.s_addr = ip->sin.sin_addr.s_addr,
.imr_ifindex = vxlan->default_dst.remote_ifindex,
};
@@ -704,7 +833,13 @@ static int vxlan_leave_group(struct net_device *dev)
/* Need to drop RTNL to call multicast leave */
rtnl_unlock();
lock_sock(sk);
- err = ip_mc_leave_group(sk, &mreq);
+ if (ip->sa.sa_family == AF_INET)
+ err = ip_mc_leave_group(sk, &mreq);
+#if IS_ENABLED(CONFIG_IPV6)
+ else
+ err = ipv6_stub->ipv6_sock_mc_drop(sk, vxlan->default_dst.remote_ifindex,
+ &ip->sin6.sin6_addr);
+#endif
release_sock(sk);
rtnl_lock();
@@ -714,13 +849,17 @@ static int vxlan_leave_group(struct net_device *dev)
/* Callback from net/ipv4/udp.c to receive packets */
static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
{
- struct iphdr *oip;
+ struct iphdr *oip = NULL;
+#if IS_ENABLED(CONFIG_IPV6)
+ struct ipv6hdr *oip6 = NULL;
+#endif
struct vxlanhdr *vxh;
struct vxlan_dev *vxlan;
struct pcpu_tstats *stats;
__be16 port;
+ union vxlan_addr src_ip;
__u32 vni;
- int err;
+ int err = 0;
/* pop off outer UDP header */
__skb_pull(skb, sizeof(struct udphdr));
@@ -759,7 +898,13 @@ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
skb_reset_mac_header(skb);
/* Re-examine inner Ethernet packet */
- oip = ip_hdr(skb);
+ if (skb->protocol == htons(ETH_P_IP))
+ oip = ip_hdr(skb);
+#if IS_ENABLED(CONFIG_IPV6)
+ if (skb->protocol == htons(ETH_P_IPV6))
+ oip6 = ipv6_hdr(skb);
+#endif
+
skb->protocol = eth_type_trans(skb, vxlan->dev);
/* Ignore packet loops (and multicast echo) */
@@ -767,8 +912,19 @@ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
vxlan->dev->dev_addr) == 0)
goto drop;
- if (vxlan->flags & VXLAN_F_LEARN)
- vxlan_snoop(skb->dev, oip->saddr, eth_hdr(skb)->h_source);
+ if (vxlan->flags & VXLAN_F_LEARN) {
+ if (oip) {
+ src_ip.sin.sin_addr.s_addr = oip->saddr;
+ src_ip.sa.sa_family = AF_INET;
+ }
+#if IS_ENABLED(CONFIG_IPV6)
+ if (oip6) {
+ src_ip.sin6.sin6_addr = oip6->saddr;
+ src_ip.sa.sa_family = AF_INET6;
+ }
+#endif
+ vxlan_snoop(skb->dev, &src_ip, eth_hdr(skb)->h_source);
+ }
__skb_tunnel_rx(skb, vxlan->dev);
skb_reset_network_header(skb);
@@ -784,11 +940,24 @@ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
skb->encapsulation = 0;
- err = IP_ECN_decapsulate(oip, skb);
+#if IS_ENABLED(CONFIG_IPV6)
+ if (oip6)
+ err = IP6_ECN_decapsulate(oip6, skb);
+#endif
+ if (oip)
+ err = IP_ECN_decapsulate(oip, skb);
+
if (unlikely(err)) {
- if (log_ecn_error)
- net_info_ratelimited("non-ECT from %pI4 with TOS=%#x\n",
- &oip->saddr, oip->tos);
+ if (log_ecn_error) {
+#if IS_ENABLED(CONFIG_IPV6)
+ if (oip6)
+ net_info_ratelimited("non-ECT from %pI6\n",
+ &oip6->saddr);
+#endif
+ if (oip)
+ net_info_ratelimited("non-ECT from %pI4 with TOS=%#x\n",
+ &oip->saddr, oip->tos);
+ }
if (err > 1) {
++vxlan->dev->stats.rx_frame_errors;
++vxlan->dev->stats.rx_errors;
@@ -823,6 +992,7 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb)
u8 *arpptr, *sha;
__be32 sip, tip;
struct neighbour *n;
+ union vxlan_addr ipa;
if (dev->flags & IFF_NOARP)
goto out;
@@ -864,7 +1034,7 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb)
}
f = vxlan_find_mac(vxlan, n->ha);
- if (f && f->remote.remote_ip == htonl(INADDR_ANY)) {
+ if (f && vxlan_addr_any(&f->remote.remote_ip)) {
/* bridge-local neighbor */
neigh_release(n);
goto out;
@@ -882,8 +1052,11 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb)
if (netif_rx_ni(reply) == NET_RX_DROP)
dev->stats.rx_dropped++;
- } else if (vxlan->flags & VXLAN_F_L3MISS)
- vxlan_ip_miss(dev, tip);
+ } else if (vxlan->flags & VXLAN_F_L3MISS) {
+ ipa.sin.sin_addr.s_addr = tip;
+ ipa.sa.sa_family = AF_INET;
+ vxlan_ip_miss(dev, &ipa);
+ }
out:
consume_skb(skb);
return NETDEV_TX_OK;
@@ -905,6 +1078,14 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb)
return false;
pip = ip_hdr(skb);
n = neigh_lookup(&arp_tbl, &pip->daddr, dev);
+ if (!n && vxlan->flags & VXLAN_F_L3MISS) {
+ union vxlan_addr ipa;
+ ipa.sin.sin_addr.s_addr = pip->daddr;
+ ipa.sa.sa_family = AF_INET;
+ vxlan_ip_miss(dev, &ipa);
+ return false;
+ }
+
break;
default:
return false;
@@ -921,8 +1102,8 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb)
}
neigh_release(n);
return diff;
- } else if (vxlan->flags & VXLAN_F_L3MISS)
- vxlan_ip_miss(dev, pip->daddr);
+ }
+
return false;
}
@@ -981,15 +1162,26 @@ static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan,
{
struct pcpu_tstats *tx_stats = this_cpu_ptr(src_vxlan->dev->tstats);
struct pcpu_tstats *rx_stats = this_cpu_ptr(dst_vxlan->dev->tstats);
+ union vxlan_addr loopback;
skb->pkt_type = PACKET_HOST;
skb->encapsulation = 0;
skb->dev = dst_vxlan->dev;
__skb_pull(skb, skb_network_offset(skb));
+ if (dst_vxlan->default_dst.remote_ip.sa.sa_family == AF_INET) {
+ loopback.sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ loopback.sa.sa_family = AF_INET;
+ }
+#if IS_ENABLED(CONFIG_IPV6)
+ else {
+ loopback.sin6.sin6_addr = in6addr_loopback;
+ loopback.sa.sa_family = AF_INET6;
+ }
+#endif
+
if (dst_vxlan->flags & VXLAN_F_LEARN)
- vxlan_snoop(skb->dev, htonl(INADDR_LOOPBACK),
- eth_hdr(skb)->h_source);
+ vxlan_snoop(skb->dev, &loopback, eth_hdr(skb)->h_source);
u64_stats_update_begin(&tx_stats->syncp);
tx_stats->tx_packets++;
@@ -1011,22 +1203,28 @@ static netdev_tx_t vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
{
struct vxlan_dev *vxlan = netdev_priv(dev);
struct rtable *rt;
- const struct iphdr *old_iph;
+ const struct iphdr *old_iph = NULL;
struct iphdr *iph;
struct vxlanhdr *vxh;
struct udphdr *uh;
struct flowi4 fl4;
- __be32 dst;
- __be16 src_port, dst_port;
+#if IS_ENABLED(CONFIG_IPV6)
+ struct flowi6 fl6;
+ struct sock *sk = vxlan->vn_sock->sock->sk;
+ struct ipv6hdr *ip6h;
+#endif
+ const union vxlan_addr *dst;
+ struct dst_entry *ndst = NULL;
+ __be16 src_port = 0, dst_port;
u32 vni;
__be16 df = 0;
__u8 tos, ttl;
dst_port = rdst->remote_port ? rdst->remote_port : vxlan->dst_port;
vni = rdst->remote_vni;
- dst = rdst->remote_ip;
+ dst = &rdst->remote_ip;
- if (!dst) {
+ if (vxlan_addr_any(dst)) {
if (did_rsc) {
/* short-circuited back to local bridge */
vxlan_encap_bypass(skb, vxlan, vxlan);
@@ -1040,60 +1238,119 @@ static netdev_tx_t vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
skb->encapsulation = 1;
}
- /* Need space for new headers (invalidates iph ptr) */
- if (skb_cow_head(skb, VXLAN_HEADROOM))
- goto drop;
+ ttl = vxlan->ttl;
+ tos = vxlan->tos;
+ if (dst->sa.sa_family == AF_INET) {
+ /* Need space for new headers (invalidates iph ptr) */
+ if (skb_cow_head(skb, VXLAN_HEADROOM))
+ goto drop;
- old_iph = ip_hdr(skb);
+ old_iph = ip_hdr(skb);
+ if (!ttl && IN_MULTICAST(ntohl(dst->sin.sin_addr.s_addr)))
+ ttl = 1;
- ttl = vxlan->ttl;
- if (!ttl && IN_MULTICAST(ntohl(dst)))
- ttl = 1;
+ if (tos == 1)
+ tos = ip_tunnel_get_dsfield(old_iph, skb);
- tos = vxlan->tos;
- if (tos == 1)
- tos = ip_tunnel_get_dsfield(old_iph, skb);
-
- src_port = vxlan_src_port(vxlan, skb);
-
- memset(&fl4, 0, sizeof(fl4));
- fl4.flowi4_oif = rdst->remote_ifindex;
- fl4.flowi4_tos = RT_TOS(tos);
- fl4.daddr = dst;
- fl4.saddr = vxlan->saddr;
-
- rt = ip_route_output_key(dev_net(dev), &fl4);
- if (IS_ERR(rt)) {
- netdev_dbg(dev, "no route to %pI4\n", &dst);
- dev->stats.tx_carrier_errors++;
- goto tx_error;
- }
+ src_port = vxlan_src_port(vxlan, skb);
- if (rt->dst.dev == dev) {
- netdev_dbg(dev, "circular route to %pI4\n", &dst);
- ip_rt_put(rt);
- dev->stats.collisions++;
- goto tx_error;
- }
+ memset(&fl4, 0, sizeof(fl4));
+ fl4.flowi4_oif = rdst->remote_ifindex;
+ fl4.flowi4_tos = RT_TOS(tos);
+ fl4.daddr = dst->sin.sin_addr.s_addr;
+ fl4.saddr = vxlan->saddr.sin.sin_addr.s_addr;
- /* Bypass encapsulation if the destination is local */
- if (rt->rt_flags & RTCF_LOCAL &&
- !(rt->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST))) {
- struct vxlan_dev *dst_vxlan;
+ rt = ip_route_output_key(dev_net(dev), &fl4);
+ if (IS_ERR(rt)) {
+ netdev_dbg(dev, "no route to %pI4\n",
+ &dst->sin.sin_addr.s_addr);
+ dev->stats.tx_carrier_errors++;
+ goto tx_error;
+ }
- ip_rt_put(rt);
- dst_vxlan = vxlan_find_vni(dev_net(dev), vni, dst_port);
- if (!dst_vxlan)
+ if (rt->dst.dev == dev) {
+ netdev_dbg(dev, "circular route to %pI4\n",
+ &dst->sin.sin_addr.s_addr);
+ ip_rt_put(rt);
+ dev->stats.collisions++;
goto tx_error;
- vxlan_encap_bypass(skb, vxlan, dst_vxlan);
- return NETDEV_TX_OK;
+ }
+
+ /* Bypass encapsulation if the destination is local */
+ if (rt->rt_flags & RTCF_LOCAL &&
+ !(rt->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST))) {
+ struct vxlan_dev *dst_vxlan;
+
+ ip_rt_put(rt);
+ dst_vxlan = vxlan_find_vni(dev_net(dev), vni, dst_port);
+ if (!dst_vxlan)
+ goto tx_error;
+ vxlan_encap_bypass(skb, vxlan, dst_vxlan);
+ return NETDEV_TX_OK;
+ }
+
+ ndst = &rt->dst;
+#if IS_ENABLED(CONFIG_IPV6)
+ } else {
+ const struct ipv6hdr *old_iph6;
+ u32 flags;
+
+ /* Need space for new headers (invalidates ipv6h ptr) */
+ if (skb_cow_head(skb, VXLAN6_HEADROOM))
+ goto drop;
+
+ old_iph6 = ipv6_hdr(skb);
+ if (!ttl && ipv6_addr_is_multicast(&dst->sin6.sin6_addr))
+ ttl = 1;
+
+ if (tos == 1)
+ tos = ipv6_get_dsfield(old_iph6);
+
+ src_port = vxlan_src_port(vxlan, skb);
+
+ memset(&fl6, 0, sizeof(fl6));
+ fl6.flowi6_oif = rdst->remote_ifindex;
+ fl6.flowi6_tos = RT_TOS(tos);
+ fl6.daddr = dst->sin6.sin6_addr;
+ fl6.saddr = vxlan->saddr.sin6.sin6_addr;
+ fl6.flowi6_proto = skb->protocol;
+
+ if (ipv6_stub->ipv6_dst_lookup(sk, &ndst, &fl6)) {
+ netdev_dbg(dev, "no route to %pI6\n",
+ &dst->sin6.sin6_addr);
+ dev->stats.tx_carrier_errors++;
+ goto tx_error;
+ }
+
+ if (ndst->dev == dev) {
+ netdev_dbg(dev, "circular route to %pI6\n",
+ &dst->sin6.sin6_addr);
+ dst_release(ndst);
+ dev->stats.collisions++;
+ goto tx_error;
+ }
+
+ /* Bypass encapsulation if the destination is local */
+ flags = ((struct rt6_info *)ndst)->rt6i_flags;
+ if (flags & RTF_LOCAL &&
+ !(flags & (RTCF_BROADCAST | RTCF_MULTICAST))) {
+ struct vxlan_dev *dst_vxlan;
+
+ dst_release(ndst);
+ dst_vxlan = vxlan_find_vni(dev_net(dev), vni, dst_port);
+ if (!dst_vxlan)
+ goto tx_error;
+ vxlan_encap_bypass(skb, vxlan, dst_vxlan);
+ return NETDEV_TX_OK;
+ }
+#endif
}
memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED |
IPSKB_REROUTED);
skb_dst_drop(skb);
- skb_dst_set(skb, &rt->dst);
+ skb_dst_set(skb, ndst);
vxh = (struct vxlanhdr *) __skb_push(skb, sizeof(*vxh));
vxh->vx_flags = htonl(VXLAN_FLAGS);
@@ -1109,27 +1366,64 @@ static netdev_tx_t vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
uh->len = htons(skb->len);
uh->check = 0;
- __skb_push(skb, sizeof(*iph));
- skb_reset_network_header(skb);
- iph = ip_hdr(skb);
- iph->version = 4;
- iph->ihl = sizeof(struct iphdr) >> 2;
- iph->frag_off = df;
- iph->protocol = IPPROTO_UDP;
- iph->tos = ip_tunnel_ecn_encap(tos, old_iph, skb);
- iph->daddr = dst;
- iph->saddr = fl4.saddr;
- iph->ttl = ttl ? : ip4_dst_hoplimit(&rt->dst);
- tunnel_ip_select_ident(skb, old_iph, &rt->dst);
-
- nf_reset(skb);
+ if (dst->sa.sa_family == AF_INET) {
+ __skb_push(skb, sizeof(*iph));
+ skb_reset_network_header(skb);
+ iph = ip_hdr(skb);
+ iph->version = 4;
+ iph->ihl = sizeof(struct iphdr) >> 2;
+ iph->frag_off = df;
+ iph->protocol = IPPROTO_UDP;
+ iph->tos = ip_tunnel_ecn_encap(tos, old_iph, skb);
+ iph->daddr = dst->sin.sin_addr.s_addr;
+ iph->saddr = fl4.saddr;
+ iph->ttl = ttl ? : ip4_dst_hoplimit(ndst);
+ tunnel_ip_select_ident(skb, old_iph, ndst);
+#if IS_ENABLED(CONFIG_IPV6)
+ } else {
+ if (!skb_is_gso(skb) && !(ndst->dev->features & NETIF_F_IPV6_CSUM)) {
+ __wsum csum = skb_checksum(skb, 0, skb->len, 0);
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ uh->check = csum_ipv6_magic(&fl6.saddr, &fl6.daddr, skb->len,
+ IPPROTO_UDP, csum);
+ if (uh->check == 0)
+ uh->check = CSUM_MANGLED_0;
+ } else {
+ skb->ip_summed = CHECKSUM_PARTIAL;
+ skb->csum_start = skb_transport_header(skb) - skb->head;
+ skb->csum_offset = offsetof(struct udphdr, check);
+ uh->check = ~csum_ipv6_magic(&fl6.saddr, &fl6.daddr,
+ skb->len, IPPROTO_UDP, 0);
+ }
+
+ __skb_push(skb, sizeof(*ip6h));
+ skb_reset_network_header(skb);
+ ip6h = ipv6_hdr(skb);
+ ip6h->version = 6;
+ ip6h->priority = 0;
+ ip6h->flow_lbl[0] = 0;
+ ip6h->flow_lbl[1] = 0;
+ ip6h->flow_lbl[2] = 0;
+ ip6h->payload_len = htons(skb->len);
+ ip6h->nexthdr = IPPROTO_UDP;
+ ip6h->hop_limit = ttl ? : ip6_dst_hoplimit(ndst);
+ ip6h->daddr = fl6.daddr;
+ ip6h->saddr = fl6.saddr;
+#endif
+ }
vxlan_set_owner(dev, skb);
if (handle_offloads(skb))
goto drop;
- iptunnel_xmit(skb, dev);
+#if IS_ENABLED(CONFIG_IPV6)
+ if (dst->sa.sa_family == AF_INET6)
+ ip6tunnel_xmit(skb, dev);
+ else
+#endif
+ iptunnel_xmit(skb, dev);
+
return NETDEV_TX_OK;
drop:
@@ -1177,7 +1471,7 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
if (f == NULL) {
rdst0 = &vxlan->default_dst;
- if (rdst0->remote_ip == htonl(INADDR_ANY) &&
+ if (vxlan_addr_any(&rdst0->remote_ip) &&
(vxlan->flags & VXLAN_F_L2MISS) &&
!is_multicast_ether_addr(eth->h_dest))
vxlan_fdb_miss(vxlan, eth->h_dest);
@@ -1255,7 +1549,7 @@ static int vxlan_open(struct net_device *dev)
struct vxlan_dev *vxlan = netdev_priv(dev);
int err;
- if (IN_MULTICAST(ntohl(vxlan->default_dst.remote_ip))) {
+ if (vxlan_addr_multicast(&vxlan->default_dst.remote_ip)) {
err = vxlan_join_group(dev);
if (err)
return err;
@@ -1289,7 +1583,7 @@ static int vxlan_stop(struct net_device *dev)
{
struct vxlan_dev *vxlan = netdev_priv(dev);
- if (IN_MULTICAST(ntohl(vxlan->default_dst.remote_ip)))
+ if (vxlan_addr_multicast(&vxlan->default_dst.remote_ip))
vxlan_leave_group(dev);
del_timer_sync(&vxlan->age_timer);
@@ -1339,7 +1633,12 @@ static void vxlan_setup(struct net_device *dev)
eth_hw_addr_random(dev);
ether_setup(dev);
- dev->hard_header_len = ETH_HLEN + VXLAN_HEADROOM;
+#if IS_ENABLED(CONFIG_IPV6)
+ if (vxlan->default_dst.remote_ip.sa.sa_family == AF_INET6)
+ dev->hard_header_len = ETH_HLEN + VXLAN6_HEADROOM;
+ else
+#endif
+ dev->hard_header_len = ETH_HLEN + VXLAN_HEADROOM;
dev->netdev_ops = &vxlan_netdev_ops;
dev->destructor = vxlan_free;
@@ -1378,8 +1677,10 @@ static void vxlan_setup(struct net_device *dev)
static const struct nla_policy vxlan_policy[IFLA_VXLAN_MAX + 1] = {
[IFLA_VXLAN_ID] = { .type = NLA_U32 },
[IFLA_VXLAN_GROUP] = { .len = FIELD_SIZEOF(struct iphdr, daddr) },
+ [IFLA_VXLAN_GROUP6] = { .len = sizeof(struct in6_addr) },
[IFLA_VXLAN_LINK] = { .type = NLA_U32 },
[IFLA_VXLAN_LOCAL] = { .len = FIELD_SIZEOF(struct iphdr, saddr) },
+ [IFLA_VXLAN_LOCAL6] = { .len = sizeof(struct in6_addr) },
[IFLA_VXLAN_TOS] = { .type = NLA_U8 },
[IFLA_VXLAN_TTL] = { .type = NLA_U8 },
[IFLA_VXLAN_LEARNING] = { .type = NLA_U8 },
@@ -1446,37 +1747,85 @@ static void vxlan_del_work(struct work_struct *work)
{
struct vxlan_sock *vs = container_of(work, struct vxlan_sock, del_work);
- sk_release_kernel(vs->sock->sk);
+ if (vs->sock) {
+ sk_release_kernel(vs->sock->sk);
+ vs->sock = NULL;
+ }
kfree_rcu(vs, rcu);
}
-/* Create new listen socket if needed */
-static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port)
+/* Create UDP socket for encapsulation receive. AF_INET6 socket
+ * could be used for both IPv4 and IPv6 communications, but
+ * users may set bindv6only=1.
+ */
+#if IS_ENABLED(CONFIG_IPV6)
+static int create_v6_sock(struct net *net, struct vxlan_sock *vs, __be32 port)
+{
+ struct sock *sk;
+ struct sockaddr_in6 vxlan_addr = {
+ .sin6_family = AF_INET6,
+ .sin6_port = port,
+ };
+ int rc, val = 1;
+
+ rc = sock_create_kern(AF_INET6, SOCK_DGRAM, IPPROTO_UDP, &vs->sock);
+ if (rc < 0) {
+ pr_debug("UDP socket create failed\n");
+ return rc;
+ }
+
+ /* Put in proper namespace */
+ sk = vs->sock->sk;
+ sk_change_net(sk, net);
+
+ kernel_setsockopt(vs->sock, SOL_IPV6, IPV6_V6ONLY,
+ (char *)&val, sizeof(val));
+ rc = kernel_bind(vs->sock, (struct sockaddr *)&vxlan_addr,
+ sizeof(struct sockaddr_in6));
+ if (rc < 0) {
+ pr_debug("bind for UDP socket %pI6:%u (%d)\n",
+ &vxlan_addr.sin6_addr, ntohs(vxlan_addr.sin6_port), rc);
+ sk_release_kernel(sk);
+ vs->sock = NULL;
+ return rc;
+ }
+
+ /* At this point, IPv6 module should have been loaded in
+ * sock_create_kern().
+ */
+ BUG_ON(!ipv6_stub);
+
+ /* Disable multicast loopback */
+ inet_sk(sk)->mc_loop = 0;
+
+ /* Mark socket as an encapsulation socket. */
+ udp_sk(sk)->encap_type = 1;
+ udp_sk(sk)->encap_rcv = vxlan_udp_encap_recv;
+ ipv6_stub->udpv6_encap_enable();
+
+ return 0;
+}
+#else
+static int create_v6_sock(struct net *net, struct vxlan_sock *vs, __be32 port)
+{
+ return -EPFNOSUPPORT;
+}
+#endif
+
+static int create_v4_sock(struct net *net, struct vxlan_sock *vs, __be16 port)
{
- struct vxlan_sock *vs;
struct sock *sk;
struct sockaddr_in vxlan_addr = {
.sin_family = AF_INET,
.sin_addr.s_addr = htonl(INADDR_ANY),
};
int rc;
- unsigned int h;
-
- vs = kmalloc(sizeof(*vs), GFP_KERNEL);
- if (!vs)
- return ERR_PTR(-ENOMEM);
-
- for (h = 0; h < VNI_HASH_SIZE; ++h)
- INIT_HLIST_HEAD(&vs->vni_list[h]);
-
- INIT_WORK(&vs->del_work, vxlan_del_work);
/* Create UDP socket for encapsulation receive. */
rc = sock_create_kern(AF_INET, SOCK_DGRAM, IPPROTO_UDP, &vs->sock);
if (rc < 0) {
pr_debug("UDP socket create failed\n");
- kfree(vs);
- return ERR_PTR(rc);
+ return rc;
}
/* Put in proper namespace */
@@ -1491,8 +1840,8 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port)
pr_debug("bind for UDP socket %pI4:%u (%d)\n",
&vxlan_addr.sin_addr, ntohs(vxlan_addr.sin_port), rc);
sk_release_kernel(sk);
- kfree(vs);
- return ERR_PTR(rc);
+ vs->sock = NULL;
+ return rc;
}
/* Disable multicast loopback */
@@ -1503,6 +1852,34 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port)
udp_sk(sk)->encap_rcv = vxlan_udp_encap_recv;
udp_encap_enable();
+ return 0;
+}
+
+/* Create new listen socket if needed */
+static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port, bool ipv6)
+{
+ struct vxlan_sock *vs;
+ int rc = 0;
+ unsigned int h;
+
+ vs = kmalloc(sizeof(*vs), GFP_KERNEL);
+ if (!vs)
+ return ERR_PTR(-ENOMEM);
+
+ for (h = 0; h < VNI_HASH_SIZE; ++h)
+ INIT_HLIST_HEAD(&vs->vni_list[h]);
+
+ INIT_WORK(&vs->del_work, vxlan_del_work);
+
+ if (!ipv6)
+ rc = create_v4_sock(net, vs, port);
+ else
+ rc = create_v6_sock(net, vs, port);
+ if (rc < 0) {
+ kfree(vs);
+ return ERR_PTR(rc);
+ }
+
vs->refcnt = 1;
return vs;
}
@@ -1516,6 +1893,7 @@ static int vxlan_newlink(struct net *net, struct net_device *dev,
struct vxlan_sock *vs;
__u32 vni;
int err;
+ bool use_ipv6 = false;
if (!data[IFLA_VXLAN_ID])
return -EINVAL;
@@ -1523,11 +1901,39 @@ static int vxlan_newlink(struct net *net, struct net_device *dev,
vni = nla_get_u32(data[IFLA_VXLAN_ID]);
dst->remote_vni = vni;
- if (data[IFLA_VXLAN_GROUP])
- dst->remote_ip = nla_get_be32(data[IFLA_VXLAN_GROUP]);
+ if (data[IFLA_VXLAN_GROUP]) {
+ dst->remote_ip.sin.sin_addr.s_addr = nla_get_be32(data[IFLA_VXLAN_GROUP]);
+ dst->remote_ip.sa.sa_family = AF_INET;
+ } else if (data[IFLA_VXLAN_GROUP6]) {
+#if IS_ENABLED(CONFIG_IPV6)
+ if (ipv6_disabled)
+ return -EPFNOSUPPORT;
+
+ nla_memcpy(&dst->remote_ip.sin6.sin6_addr, data[IFLA_VXLAN_GROUP6],
+ sizeof(struct in6_addr));
+ dst->remote_ip.sa.sa_family = AF_INET6;
+ use_ipv6 = true;
+#else
+ return -EPFNOSUPPORT;
+#endif
+ }
- if (data[IFLA_VXLAN_LOCAL])
- vxlan->saddr = nla_get_be32(data[IFLA_VXLAN_LOCAL]);
+ if (data[IFLA_VXLAN_LOCAL]) {
+ vxlan->saddr.sin.sin_addr.s_addr = nla_get_be32(data[IFLA_VXLAN_LOCAL]);
+ vxlan->saddr.sa.sa_family = AF_INET;
+ } else if (data[IFLA_VXLAN_LOCAL6]) {
+#if IS_ENABLED(CONFIG_IPV6)
+ if (ipv6_disabled)
+ return -EPFNOSUPPORT;
+
+ nla_memcpy(&vxlan->saddr.sin6.sin6_addr, data[IFLA_VXLAN_LOCAL6],
+ sizeof(struct in6_addr));
+ vxlan->saddr.sa.sa_family = AF_INET6;
+ use_ipv6 = true;
+#else
+ return -EPFNOSUPPORT;
+#endif
+ }
if (data[IFLA_VXLAN_LINK] &&
(dst->remote_ifindex = nla_get_u32(data[IFLA_VXLAN_LINK]))) {
@@ -1539,6 +1945,17 @@ static int vxlan_newlink(struct net *net, struct net_device *dev,
return -ENODEV;
}
+#if IS_ENABLED(CONFIG_IPV6)
+ if (use_ipv6) {
+ struct inet6_dev *idev = in6_dev_get(lowerdev);
+ if (idev && idev->cnf.disable_ipv6) {
+ pr_info("IPv6 is disabled via sysctl\n");
+ return -EPERM;
+ }
+ }
+#else
+ BUG_ON(use_ipv6);
+#endif
if (!tb[IFLA_MTU])
dev->mtu = lowerdev->mtu - VXLAN_HEADROOM;
@@ -1597,7 +2014,7 @@ static int vxlan_newlink(struct net *net, struct net_device *dev,
else {
/* Drop lock because socket create acquires RTNL lock */
rtnl_unlock();
- vs = vxlan_socket_create(net, vxlan->dst_port);
+ vs = vxlan_socket_create(net, vxlan->dst_port, use_ipv6);
rtnl_lock();
if (IS_ERR(vs))
return PTR_ERR(vs);
@@ -1644,9 +2061,9 @@ static size_t vxlan_get_size(const struct net_device *dev)
{
return nla_total_size(sizeof(__u32)) + /* IFLA_VXLAN_ID */
- nla_total_size(sizeof(__be32)) +/* IFLA_VXLAN_GROUP */
+ nla_total_size(sizeof(struct in6_addr)) + /* IFLA_VXLAN_GROUP{6} */
nla_total_size(sizeof(__u32)) + /* IFLA_VXLAN_LINK */
- nla_total_size(sizeof(__be32))+ /* IFLA_VXLAN_LOCAL */
+ nla_total_size(sizeof(struct in6_addr)) + /* IFLA_VXLAN_LOCAL{6} */
nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_TTL */
nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_TOS */
nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_LEARNING */
@@ -1673,14 +2090,36 @@ static int vxlan_fill_info(struct sk_buff *skb, const struct net_device *dev)
if (nla_put_u32(skb, IFLA_VXLAN_ID, dst->remote_vni))
goto nla_put_failure;
- if (dst->remote_ip && nla_put_be32(skb, IFLA_VXLAN_GROUP, dst->remote_ip))
- goto nla_put_failure;
+ if (!vxlan_addr_any(&dst->remote_ip)) {
+ if (dst->remote_ip.sa.sa_family == AF_INET) {
+ if (nla_put_be32(skb, IFLA_VXLAN_GROUP,
+ dst->remote_ip.sin.sin_addr.s_addr))
+ goto nla_put_failure;
+ } else {
+#if IS_ENABLED(CONFIG_IPV6)
+ if (nla_put(skb, IFLA_VXLAN_GROUP6, sizeof(struct in6_addr),
+ &dst->remote_ip.sin6.sin6_addr))
+ goto nla_put_failure;
+#endif
+ }
+ }
if (dst->remote_ifindex && nla_put_u32(skb, IFLA_VXLAN_LINK, dst->remote_ifindex))
goto nla_put_failure;
- if (vxlan->saddr && nla_put_be32(skb, IFLA_VXLAN_LOCAL, vxlan->saddr))
- goto nla_put_failure;
+ if (!vxlan_addr_any(&vxlan->saddr)) {
+ if (vxlan->saddr.sa.sa_family == AF_INET) {
+ if (nla_put_be32(skb, IFLA_VXLAN_LOCAL,
+ vxlan->saddr.sin.sin_addr.s_addr))
+ goto nla_put_failure;
+ } else {
+#if IS_ENABLED(CONFIG_IPV6)
+ if (nla_put(skb, IFLA_VXLAN_LOCAL6, sizeof(struct in6_addr),
+ &vxlan->saddr.sin6.sin6_addr))
+ goto nla_put_failure;
+#endif
+ }
+ }
if (nla_put_u8(skb, IFLA_VXLAN_TTL, vxlan->ttl) ||
nla_put_u8(skb, IFLA_VXLAN_TOS, vxlan->tos) ||
diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
index b05823c..f7bed18 100644
--- a/include/uapi/linux/if_link.h
+++ b/include/uapi/linux/if_link.h
@@ -311,6 +311,8 @@ enum {
IFLA_VXLAN_L2MISS,
IFLA_VXLAN_L3MISS,
IFLA_VXLAN_PORT, /* destination port */
+ IFLA_VXLAN_GROUP6,
+ IFLA_VXLAN_LOCAL6,
__IFLA_VXLAN_MAX
};
#define IFLA_VXLAN_MAX (__IFLA_VXLAN_MAX - 1)
--
1.7.7.6
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Powered by blists - more mailing lists