netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFT] geneve: implement support for IPv6-based tunnels
@ 2015-09-24 18:34 John W. Linville
  2015-09-24 18:39 ` [PATCH iproute2] geneve: add support for IPv6 link partners John W. Linville
                   ` (2 more replies)
  0 siblings, 3 replies; 37+ messages in thread
From: John W. Linville @ 2015-09-24 18:34 UTC (permalink / raw)
  To: netdev; +Cc: davem, Pravin B Shelar, Jesse Gross, John W. Linville

Signed-off-by: John W. Linville <linville@tuxdriver.com>
---
The IPv6 tunnel_info/metadata paths are untested due to lack of OVS
infrastructure for testing.  The traditional netdev paths have had
reasonable testing by me with some local virt guests over the past
few weeks.

NOTE -- this patch requires the earlier geneve patches merged in the
net tree:

7bbe33ff1896 geneve: use network byte order for destination port config parameter
08399efc6319 geneve: ensure ECN info is handled properly in all tx/rx paths
5eb8f289ac30 geneve: remove vlan-related feature assignment

 drivers/net/geneve.c         | 368 +++++++++++++++++++++++++++++++++++++------
 include/uapi/linux/if_link.h |   1 +
 2 files changed, 318 insertions(+), 51 deletions(-)

diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c
index 8f5c02eed47d..9fa0580b1f7c 100644
--- a/drivers/net/geneve.c
+++ b/drivers/net/geneve.c
@@ -46,6 +46,16 @@ struct geneve_net {
 
 static int geneve_net_id;
 
+union geneve_addr {
+	struct sockaddr_in sin;
+	struct sockaddr_in6 sin6;
+	struct sockaddr sa;
+};
+
+union geneve_addr geneve_remote_unspec = { .sa.sa_family = AF_UNSPEC, };
+
+#define GENEVE_F_IPV6		0x00000001
+
 /* Pseudo network device */
 struct geneve_dev {
 	struct hlist_node  hlist;	/* vni hash table */
@@ -55,7 +65,8 @@ struct geneve_dev {
 	u8                 vni[3];	/* virtual network ID for tunnel */
 	u8                 ttl;		/* TTL override */
 	u8                 tos;		/* TOS override */
-	struct sockaddr_in remote;	/* IPv4 address for link partner */
+	u32                flags;	/* GENEVE_F_* above */
+	union geneve_addr  remote;	/* IP address for link partner */
 	struct list_head   next;	/* geneve's per namespace list */
 	__be16		   dst_port;
 	bool		   collect_md;
@@ -103,11 +114,32 @@ static struct geneve_dev *geneve_lookup(struct geneve_sock *gs,
 	vni_list_head = &gs->vni_list[hash];
 	hlist_for_each_entry_rcu(geneve, vni_list_head, hlist) {
 		if (!memcmp(vni, geneve->vni, sizeof(geneve->vni)) &&
-		    addr == geneve->remote.sin_addr.s_addr)
+		    addr == geneve->remote.sin.sin_addr.s_addr)
+			return geneve;
+	}
+	return NULL;
+}
+
+#if IS_ENABLED(CONFIG_IPV6)
+static struct geneve_dev *geneve6_lookup(struct geneve_sock *gs,
+					 struct in6_addr addr6, u8 vni[])
+{
+	struct hlist_head *vni_list_head;
+	struct geneve_dev *geneve;
+	__u32 hash;
+
+	/* Find the device for this VNI */
+	hash = geneve_net_vni_hash(vni);
+	vni_list_head = &gs->vni_list[hash];
+	hlist_for_each_entry_rcu(geneve, vni_list_head, hlist) {
+		if (!memcmp(vni, geneve->vni, sizeof(geneve->vni)) &&
+		    !memcmp(&addr6, &geneve->remote.sin6.sin6_addr,
+			    sizeof(addr6)))
 			return geneve;
 	}
 	return NULL;
 }
+#endif
 
 static inline struct genevehdr *geneve_hdr(const struct sk_buff *skb)
 {
@@ -121,24 +153,44 @@ static void geneve_rx(struct geneve_sock *gs, struct sk_buff *skb)
 	struct metadata_dst *tun_dst = NULL;
 	struct geneve_dev *geneve = NULL;
 	struct pcpu_sw_netstats *stats;
-	struct iphdr *iph;
-	u8 *vni;
+	struct iphdr *iph = NULL;
 	__be32 addr;
-	int err;
+	static u8 zero_vni[3];
+	u8 *vni;
+	int err = 0;
+#if IS_ENABLED(CONFIG_IPV6)
+	struct ipv6hdr *ip6h = NULL;
+	struct in6_addr addr6;
+	static struct in6_addr zero_addr6;
+#endif
 
-	iph = ip_hdr(skb); /* outer IP header... */
+	if (gs->sock->sk->sk_family == AF_INET) {
+		iph = ip_hdr(skb); /* outer IP header... */
 
-	if (gs->collect_md) {
-		static u8 zero_vni[3];
+		if (gs->collect_md) {
+			vni = zero_vni;
+			addr = 0;
+		} else {
+			vni = gnvh->vni;
 
-		vni = zero_vni;
-		addr = 0;
-	} else {
-		vni = gnvh->vni;
-		addr = iph->saddr;
-	}
+			addr = iph->saddr;
+		}
+
+		geneve = geneve_lookup(gs, addr, vni);
+	} else if (gs->sock->sk->sk_family == AF_INET6) {
+		ip6h = ipv6_hdr(skb); /* outer IPv6 header... */
+
+		if (gs->collect_md) {
+			vni = zero_vni;
+			addr6 = zero_addr6;
+		} else {
+			vni = gnvh->vni;
 
-	geneve = geneve_lookup(gs, addr, vni);
+			addr6 = ip6h->saddr;
+		}
+
+		geneve = geneve6_lookup(gs, addr6, vni);
+	}
 	if (!geneve)
 		goto drop;
 
@@ -179,12 +231,21 @@ static void geneve_rx(struct geneve_sock *gs, struct sk_buff *skb)
 
 	skb_reset_network_header(skb);
 
-	err = IP_ECN_decapsulate(iph, skb);
+	if (iph)
+		err = IP_ECN_decapsulate(iph, skb);
+	if (ip6h)
+		err = IP6_ECN_decapsulate(ip6h, skb);
 
 	if (unlikely(err)) {
-		if (log_ecn_error)
-			net_info_ratelimited("non-ECT from %pI4 with TOS=%#x\n",
-					     &iph->saddr, iph->tos);
+		if (log_ecn_error) {
+			if (iph)
+				net_info_ratelimited("non-ECT from %pI4 "
+						     "with TOS=%#x\n",
+						     &iph->saddr, iph->tos);
+			if (ip6h)
+				net_info_ratelimited("non-ECT from %pI6\n",
+						     &ip6h->saddr);
+		}
 		if (err > 1) {
 			++geneve->dev->stats.rx_frame_errors;
 			++geneve->dev->stats.rx_errors;
@@ -284,6 +345,7 @@ static struct socket *geneve_create_sock(struct net *net, bool ipv6,
 
 	if (ipv6) {
 		udp_conf.family = AF_INET6;
+		udp_conf.ipv6_v6only = 1;
 	} else {
 		udp_conf.family = AF_INET;
 		udp_conf.local_ip.s_addr = htonl(INADDR_ANY);
@@ -470,13 +532,14 @@ static void geneve_sock_release(struct geneve_sock *gs)
 }
 
 static struct geneve_sock *geneve_find_sock(struct geneve_net *gn,
+					    sa_family_t family,
 					    __be16 dst_port)
 {
 	struct geneve_sock *gs;
 
 	list_for_each_entry(gs, &gn->sock_list, list) {
 		if (inet_sk(gs->sock->sk)->inet_sport == dst_port &&
-		    inet_sk(gs->sock->sk)->sk.sk_family == AF_INET) {
+		    inet_sk(gs->sock->sk)->sk.sk_family == family) {
 			return gs;
 		}
 	}
@@ -490,14 +553,15 @@ static int geneve_open(struct net_device *dev)
 	struct geneve_net *gn = net_generic(net, geneve_net_id);
 	struct geneve_sock *gs;
 	__u32 hash;
+	bool ipv6 = !!(geneve->flags & GENEVE_F_IPV6);
 
-	gs = geneve_find_sock(gn, geneve->dst_port);
+	gs = geneve_find_sock(gn, ipv6 ? AF_INET6 : AF_INET, geneve->dst_port);
 	if (gs) {
 		gs->refcnt++;
 		goto out;
 	}
 
-	gs = geneve_socket_create(net, geneve->dst_port, false);
+	gs = geneve_socket_create(net, geneve->dst_port, ipv6);
 	if (IS_ERR(gs))
 		return PTR_ERR(gs);
 
@@ -521,6 +585,22 @@ static int geneve_stop(struct net_device *dev)
 	return 0;
 }
 
+static void geneve_build_header(struct genevehdr *geneveh,
+				__be16 tun_flags, u8 vni[3],
+				u8 options_len, u8 *options)
+{
+	geneveh->ver = GENEVE_VER;
+	geneveh->opt_len = options_len / 4;
+	geneveh->oam = !!(tun_flags & TUNNEL_OAM);
+	geneveh->critical = !!(tun_flags & TUNNEL_CRIT_OPT);
+	geneveh->rsvd1 = 0;
+	memcpy(geneveh->vni, vni, 3);
+	geneveh->proto_type = htons(ETH_P_TEB);
+	geneveh->rsvd2 = 0;
+
+	memcpy(geneveh->options, options, options_len);
+}
+
 static int geneve_build_skb(struct rtable *rt, struct sk_buff *skb,
 			    __be16 tun_flags, u8 vni[3], u8 opt_len, u8 *opt,
 			    bool csum)
@@ -544,15 +624,7 @@ static int geneve_build_skb(struct rtable *rt, struct sk_buff *skb,
 	}
 
 	gnvh = (struct genevehdr *)__skb_push(skb, sizeof(*gnvh) + opt_len);
-	gnvh->ver = GENEVE_VER;
-	gnvh->opt_len = opt_len / 4;
-	gnvh->oam = !!(tun_flags & TUNNEL_OAM);
-	gnvh->critical = !!(tun_flags & TUNNEL_CRIT_OPT);
-	gnvh->rsvd1 = 0;
-	memcpy(gnvh->vni, vni, 3);
-	gnvh->proto_type = htons(ETH_P_TEB);
-	gnvh->rsvd2 = 0;
-	memcpy(gnvh->options, opt, opt_len);
+	geneve_build_header(gnvh, tun_flags, vni, opt_len, opt);
 
 	skb_set_inner_protocol(skb, htons(ETH_P_TEB));
 	return 0;
@@ -562,6 +634,79 @@ free_rt:
 	return err;
 }
 
+#if IS_ENABLED(CONFIG_IPV6)
+static int geneve6_build_skb(struct dst_entry *dst, struct sk_buff *skb,
+			     __be16 tun_flags, u8 vni[3], u8 opt_len, u8 *opt,
+			     bool csum, bool xnet)
+{
+	struct genevehdr *gnvh;
+	int min_headroom;
+	int err;
+
+	skb_scrub_packet(skb, xnet);
+
+	min_headroom = LL_RESERVED_SPACE(dst->dev) + dst->header_len
+			+ GENEVE_BASE_HLEN + opt_len + sizeof(struct ipv6hdr);
+	err = skb_cow_head(skb, min_headroom);
+	if (unlikely(err)) {
+		kfree_skb(skb);
+		goto free_dst;
+	}
+
+	skb = udp_tunnel_handle_offloads(skb, csum);
+	if (IS_ERR(skb)) {
+		err = PTR_ERR(skb);
+		goto free_dst;
+	}
+
+	gnvh = (struct genevehdr *)__skb_push(skb, sizeof(*gnvh) + opt_len);
+	geneve_build_header(gnvh, tun_flags, vni, opt_len, opt);
+
+	skb_set_inner_protocol(skb, htons(ETH_P_TEB));
+	return 0;
+
+free_dst:
+	dst_release(dst);
+	return err;
+}
+#endif
+
+#if IS_ENABLED(CONFIG_IPV6)
+static struct dst_entry *geneve_get_dst(struct sk_buff *skb,
+					struct net_device *dev,
+					struct flowi6 *fl6,
+					struct ip_tunnel_info *info)
+{
+	struct geneve_dev *geneve = netdev_priv(dev);
+	struct geneve_sock *gs = geneve->sock;
+	struct dst_entry *dst = NULL;
+
+	memset(fl6, 0, sizeof(*fl6));
+	fl6->flowi6_mark = skb->mark;
+	fl6->flowi6_proto = IPPROTO_UDP;
+
+	if (info) {
+		fl6->daddr = info->key.u.ipv6.dst;
+		fl6->saddr = info->key.u.ipv6.src;
+	} else {
+		fl6->daddr = geneve->remote.sin6.sin6_addr;
+	}
+
+	if (ipv6_stub->ipv6_dst_lookup(geneve->net, gs->sock->sk, &dst, fl6)) {
+		netdev_dbg(dev, "no route to %pI6\n", &fl6->daddr);
+		dev->stats.tx_carrier_errors++;
+		return ERR_PTR(-EHOSTUNREACH);
+	}
+	if (dst->dev == dev) { /* is this necessary? */
+		netdev_dbg(dev, "circular route to %pI6\n", &fl6->daddr);
+		dev->stats.collisions++;
+		dst_release(dst);
+		return ERR_PTR(-EINVAL);
+	}
+	return dst;
+}
+#endif
+
 static struct rtable *geneve_get_rt(struct sk_buff *skb,
 				    struct net_device *dev,
 				    struct flowi4 *fl4,
@@ -588,7 +733,7 @@ static struct rtable *geneve_get_rt(struct sk_buff *skb,
 		}
 
 		fl4->flowi4_tos = RT_TOS(tos);
-		fl4->daddr = geneve->remote.sin_addr.s_addr;
+		fl4->daddr = geneve->remote.sin.sin_addr.s_addr;
 	}
 
 	rt = ip_route_output_key(geneve->net, fl4);
@@ -620,7 +765,7 @@ static void tunnel_id_to_vni(__be64 tun_id, __u8 *vni)
 #endif
 }
 
-static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev)
 {
 	struct geneve_dev *geneve = netdev_priv(dev);
 	struct geneve_sock *gs = geneve->sock;
@@ -703,6 +848,93 @@ err:
 	return NETDEV_TX_OK;
 }
 
+#if IS_ENABLED(CONFIG_IPV6)
+static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev)
+{
+	struct geneve_dev *geneve = netdev_priv(dev);
+	struct geneve_sock *gs = geneve->sock;
+	struct ip_tunnel_info *info = NULL;
+	struct dst_entry *dst = NULL;
+	struct flowi6 fl6;
+	__u8 ttl;
+	__be16 sport;
+	bool udp_csum;
+	int err;
+	bool xnet = !net_eq(geneve->net, dev_net(geneve->dev));
+
+	if (geneve->collect_md) {
+		info = skb_tunnel_info(skb);
+		if (unlikely(info && info->mode != IP_TUNNEL_INFO_TX)) {
+			netdev_dbg(dev, "no tunnel metadata\n");
+			goto tx_error;
+		}
+	}
+
+	dst = geneve_get_dst(skb, dev, &fl6, info);
+	if (IS_ERR(dst)) {
+		netdev_dbg(dev, "no route to %pI6\n", &fl6.daddr);
+		dev->stats.tx_carrier_errors++;
+		goto tx_error;
+	}
+
+	sport = udp_flow_src_port(geneve->net, skb, 1, USHRT_MAX, true);
+	skb_reset_mac_header(skb);
+
+	if (info) {
+		const struct ip_tunnel_key *key = &info->key;
+		u8 *opts = NULL;
+		u8 vni[3];
+
+		tunnel_id_to_vni(key->tun_id, vni);
+		if (key->tun_flags & TUNNEL_GENEVE_OPT)
+			opts = ip_tunnel_info_opts(info);
+
+		udp_csum = !!(key->tun_flags & TUNNEL_CSUM);
+		err = geneve6_build_skb(dst, skb, key->tun_flags, vni,
+					info->options_len, opts,
+					udp_csum, xnet);
+		if (unlikely(err))
+			goto err;
+
+		ttl = key->ttl;
+	} else {
+		udp_csum = false;
+		err = geneve6_build_skb(dst, skb, 0, geneve->vni,
+					0, NULL, udp_csum, xnet);
+		if (unlikely(err))
+			goto err;
+
+		ttl = geneve->ttl;
+		if (!ttl && ipv6_addr_is_multicast(&fl6.daddr))
+			ttl = 1;
+		ttl = ttl ? : ip6_dst_hoplimit(dst);
+	}
+	err = udp_tunnel6_xmit_skb(dst, gs->sock->sk, skb, dev,
+				   &fl6.saddr, &fl6.daddr, 0, ttl,
+				   sport, geneve->dst_port, !udp_csum);
+
+	iptunnel_xmit_stats(err, &dev->stats, dev->tstats);
+	return NETDEV_TX_OK;
+
+tx_error:
+	dev_kfree_skb(skb);
+err:
+	dev->stats.tx_errors++;
+	return NETDEV_TX_OK;
+}
+#endif
+
+static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+#if IS_ENABLED(CONFIG_IPV6)
+	struct geneve_dev *geneve = netdev_priv(dev);
+
+	if (geneve->remote.sa.sa_family == AF_INET6)
+		return geneve6_xmit_skb(skb, dev);
+#endif
+	return geneve_xmit_skb(skb, dev);
+}
+
 static const struct net_device_ops geneve_netdev_ops = {
 	.ndo_init		= geneve_init,
 	.ndo_uninit		= geneve_uninit,
@@ -759,6 +991,7 @@ static void geneve_setup(struct net_device *dev)
 static const struct nla_policy geneve_policy[IFLA_GENEVE_MAX + 1] = {
 	[IFLA_GENEVE_ID]		= { .type = NLA_U32 },
 	[IFLA_GENEVE_REMOTE]		= { .len = FIELD_SIZEOF(struct iphdr, daddr) },
+	[IFLA_GENEVE_REMOTE6]		= { .len = sizeof(struct in6_addr) },
 	[IFLA_GENEVE_TTL]		= { .type = NLA_U8 },
 	[IFLA_GENEVE_TOS]		= { .type = NLA_U8 },
 	[IFLA_GENEVE_PORT]		= { .type = NLA_U16 },
@@ -790,7 +1023,7 @@ static int geneve_validate(struct nlattr *tb[], struct nlattr *data[])
 
 static struct geneve_dev *geneve_find_dev(struct geneve_net *gn,
 					  __be16 dst_port,
-					  __be32 rem_addr,
+					  union geneve_addr *remote,
 					  u8 vni[],
 					  bool *tun_on_same_port,
 					  bool *tun_collect_md)
@@ -806,7 +1039,7 @@ static struct geneve_dev *geneve_find_dev(struct geneve_net *gn,
 			*tun_on_same_port = true;
 		}
 		if (!memcmp(vni, geneve->vni, sizeof(geneve->vni)) &&
-		    rem_addr == geneve->remote.sin_addr.s_addr &&
+		    !memcmp(remote, &geneve->remote, sizeof(geneve->remote)) &&
 		    dst_port == geneve->dst_port)
 			t = geneve;
 	}
@@ -814,8 +1047,9 @@ static struct geneve_dev *geneve_find_dev(struct geneve_net *gn,
 }
 
 static int geneve_configure(struct net *net, struct net_device *dev,
-			    __be32 rem_addr, __u32 vni, __u8 ttl, __u8 tos,
-			    __be16 dst_port, bool metadata)
+			    union geneve_addr *remote,
+			    __u32 vni, __u8 ttl, __u8 tos, __be16 dst_port,
+			    bool metadata)
 {
 	struct geneve_net *gn = net_generic(net, geneve_net_id);
 	struct geneve_dev *t, *geneve = netdev_priv(dev);
@@ -823,9 +1057,11 @@ static int geneve_configure(struct net *net, struct net_device *dev,
 	int err;
 
 	if (metadata) {
-		if (rem_addr || vni || tos || ttl)
+		if (remote != &geneve_remote_unspec || vni || tos || ttl)
 			return -EINVAL;
 	}
+	if (!remote)
+		return -EINVAL;
 
 	geneve->net = net;
 	geneve->dev = dev;
@@ -834,16 +1070,22 @@ static int geneve_configure(struct net *net, struct net_device *dev,
 	geneve->vni[1] = (vni & 0x0000ff00) >> 8;
 	geneve->vni[2] =  vni & 0x000000ff;
 
-	geneve->remote.sin_addr.s_addr = rem_addr;
-	if (IN_MULTICAST(ntohl(geneve->remote.sin_addr.s_addr)))
+	if ((remote->sa.sa_family == AF_INET &&
+	     IN_MULTICAST(ntohl(remote->sin.sin_addr.s_addr))) ||
+	    (remote->sa.sa_family == AF_INET6 &&
+	     ipv6_addr_is_multicast(&remote->sin6.sin6_addr)))
 		return -EINVAL;
+	geneve->remote = *remote;
+
+	if (geneve->remote.sa.sa_family == AF_INET6)
+		geneve->flags |= GENEVE_F_IPV6;
 
 	geneve->ttl = ttl;
 	geneve->tos = tos;
 	geneve->dst_port = dst_port;
 	geneve->collect_md = metadata;
 
-	t = geneve_find_dev(gn, dst_port, rem_addr, geneve->vni,
+	t = geneve_find_dev(gn, dst_port, remote, geneve->vni,
 			    &tun_on_same_port, &tun_collect_md);
 	if (t)
 		return -EBUSY;
@@ -870,14 +1112,29 @@ static int geneve_newlink(struct net *net, struct net_device *dev,
 	__be16 dst_port = htons(GENEVE_UDP_PORT);
 	__u8 ttl = 0, tos = 0;
 	bool metadata = false;
-	__be32 rem_addr;
+	union geneve_addr remote;
 	__u32 vni;
 
-	if (!data[IFLA_GENEVE_ID] || !data[IFLA_GENEVE_REMOTE])
+	if (!data[IFLA_GENEVE_ID] ||
+	    (data[IFLA_GENEVE_REMOTE] && data[IFLA_GENEVE_REMOTE6]) ||
+	    (!data[IFLA_GENEVE_REMOTE] && !data[IFLA_GENEVE_REMOTE6]))
 		return -EINVAL;
 
 	vni = nla_get_u32(data[IFLA_GENEVE_ID]);
-	rem_addr = nla_get_in_addr(data[IFLA_GENEVE_REMOTE]);
+
+	memset(&remote, 0, sizeof(remote));
+	if (data[IFLA_GENEVE_REMOTE]) {
+		remote.sa.sa_family = AF_INET;
+		remote.sin.sin_addr.s_addr =
+			nla_get_in_addr(data[IFLA_GENEVE_REMOTE]);
+	} else if (data[IFLA_GENEVE_REMOTE6]) {
+		if (!IS_ENABLED(CONFIG_IPV6))
+			return -EPFNOSUPPORT;
+
+		remote.sa.sa_family = AF_INET6;
+		remote.sin6.sin6_addr =
+			nla_get_in6_addr(data[IFLA_GENEVE_REMOTE6]);
+	}
 
 	if (data[IFLA_GENEVE_TTL])
 		ttl = nla_get_u8(data[IFLA_GENEVE_TTL]);
@@ -891,8 +1148,8 @@ static int geneve_newlink(struct net *net, struct net_device *dev,
 	if (data[IFLA_GENEVE_COLLECT_METADATA])
 		metadata = true;
 
-	return geneve_configure(net, dev, rem_addr, vni,
-				ttl, tos, dst_port, metadata);
+	return geneve_configure(net, dev, &remote, vni, ttl, tos, dst_port,
+				metadata);
 }
 
 static void geneve_dellink(struct net_device *dev, struct list_head *head)
@@ -906,7 +1163,7 @@ static void geneve_dellink(struct net_device *dev, struct list_head *head)
 static size_t geneve_get_size(const struct net_device *dev)
 {
 	return nla_total_size(sizeof(__u32)) +	/* IFLA_GENEVE_ID */
-		nla_total_size(sizeof(struct in_addr)) + /* IFLA_GENEVE_REMOTE */
+		nla_total_size(sizeof(struct in6_addr)) + /* IFLA_GENEVE_REMOTE{6} */
 		nla_total_size(sizeof(__u8)) +  /* IFLA_GENEVE_TTL */
 		nla_total_size(sizeof(__u8)) +  /* IFLA_GENEVE_TOS */
 		nla_total_size(sizeof(__be16)) +  /* IFLA_GENEVE_PORT */
@@ -923,9 +1180,17 @@ static int geneve_fill_info(struct sk_buff *skb, const struct net_device *dev)
 	if (nla_put_u32(skb, IFLA_GENEVE_ID, vni))
 		goto nla_put_failure;
 
-	if (nla_put_in_addr(skb, IFLA_GENEVE_REMOTE,
-			    geneve->remote.sin_addr.s_addr))
-		goto nla_put_failure;
+	if (geneve->remote.sa.sa_family == AF_INET) {
+		if (nla_put_in_addr(skb, IFLA_GENEVE_REMOTE,
+				    geneve->remote.sin.sin_addr.s_addr))
+			goto nla_put_failure;
+#if IS_ENABLED(CONFIG_IPV6)
+	} else {
+		if (nla_put_in6_addr(skb, IFLA_GENEVE_REMOTE6,
+				     &geneve->remote.sin6.sin6_addr))
+			goto nla_put_failure;
+#endif
+	}
 
 	if (nla_put_u8(skb, IFLA_GENEVE_TTL, geneve->ttl) ||
 	    nla_put_u8(skb, IFLA_GENEVE_TOS, geneve->tos))
@@ -971,7 +1236,8 @@ struct net_device *geneve_dev_create_fb(struct net *net, const char *name,
 	if (IS_ERR(dev))
 		return dev;
 
-	err = geneve_configure(net, dev, 0, 0, 0, 0, htons(dst_port), true);
+	err = geneve_configure(net, dev, &geneve_remote_unspec,
+			       0, 0, 0, htons(dst_port), true);
 	if (err) {
 		free_netdev(dev);
 		return ERR_PTR(err);
diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
index 3a5f263cfc2f..b0cd0498ce5a 100644
--- a/include/uapi/linux/if_link.h
+++ b/include/uapi/linux/if_link.h
@@ -412,6 +412,7 @@ enum {
 	IFLA_GENEVE_TOS,
 	IFLA_GENEVE_PORT,	/* destination port */
 	IFLA_GENEVE_COLLECT_METADATA,
+	IFLA_GENEVE_REMOTE6,
 	__IFLA_GENEVE_MAX
 };
 #define IFLA_GENEVE_MAX	(__IFLA_GENEVE_MAX - 1)
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH iproute2] geneve: add support for IPv6 link partners
  2015-09-24 18:34 [RFT] geneve: implement support for IPv6-based tunnels John W. Linville
@ 2015-09-24 18:39 ` John W. Linville
  2015-11-24  0:23   ` Stephen Hemminger
  2015-09-25 12:08 ` [RFT] geneve: implement support for IPv6-based tunnels Jiri Benc
  2015-09-30 17:04 ` [RFT v2] " John W. Linville
  2 siblings, 1 reply; 37+ messages in thread
From: John W. Linville @ 2015-09-24 18:39 UTC (permalink / raw)
  To: netdev
  Cc: Stephen Hemminger, Pravin B Shelar, Jesse Gross, davem, John W. Linville

Signed-off-by: John W. Linville <linville@tuxdriver.com>
---
 include/linux/if_link.h |  1 +
 ip/iplink_geneve.c      | 20 +++++++++++++-------
 2 files changed, 14 insertions(+), 7 deletions(-)

diff --git a/include/linux/if_link.h b/include/linux/if_link.h
index 193456660ee8..d0f385579c83 100644
--- a/include/linux/if_link.h
+++ b/include/linux/if_link.h
@@ -410,6 +410,7 @@ enum {
 	IFLA_GENEVE_TOS,
 	IFLA_GENEVE_PORT,	/* destination port */
 	IFLA_GENEVE_COLLECT_METADATA,
+	IFLA_GENEVE_REMOTE6,
 	__IFLA_GENEVE_MAX
 };
 #define IFLA_GENEVE_MAX	(__IFLA_GENEVE_MAX - 1)
diff --git a/ip/iplink_geneve.c b/ip/iplink_geneve.c
index 0a45647844f5..92fae46ed9e4 100644
--- a/ip/iplink_geneve.c
+++ b/ip/iplink_geneve.c
@@ -57,7 +57,7 @@ static int geneve_parse_opt(struct link_util *lu, int argc, char **argv,
 				fprintf(stderr, "Invalid address \"%s\"\n", *argv);
 				return -1;
 			}
-			if (IN_MULTICAST(ntohl(daddr)))
+			if (IN6_IS_ADDR_MULTICAST(&daddr6) || IN_MULTICAST(ntohl(daddr)))
 				invarg("invalid remote address", *argv);
 		} else if (!matches(*argv, "ttl") ||
 			   !matches(*argv, "hoplimit")) {
@@ -102,18 +102,16 @@ static int geneve_parse_opt(struct link_util *lu, int argc, char **argv,
 		return -1;
 	}
 
-	if (!daddr) {
-		fprintf(stderr, "geneve: remove link partner not specified\n");
-		return -1;
-	}
-	if (memcmp(&daddr6, &in6addr_any, sizeof(daddr6)) != 0) {
-		fprintf(stderr, "geneve: remove link over IPv6 not supported\n");
+	if (!daddr && memcmp(&daddr6, &in6addr_any, sizeof(daddr6)) == 0) {
+		fprintf(stderr, "geneve: remote link partner not specified\n");
 		return -1;
 	}
 
 	addattr32(n, 1024, IFLA_GENEVE_ID, vni);
 	if (daddr)
 		addattr_l(n, 1024, IFLA_GENEVE_REMOTE, &daddr, 4);
+	if (memcmp(&daddr6, &in6addr_any, sizeof(daddr6)) != 0)
+		addattr_l(n, 1024, IFLA_GENEVE_REMOTE6, &daddr6, sizeof(struct in6_addr));
 	addattr8(n, 1024, IFLA_GENEVE_TTL, ttl);
 	addattr8(n, 1024, IFLA_GENEVE_TOS, tos);
 
@@ -144,6 +142,14 @@ static void geneve_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 		if (addr)
 			fprintf(f, "remote %s ",
 				format_host(AF_INET, 4, &addr, s1, sizeof(s1)));
+	} else if (tb[IFLA_GENEVE_REMOTE6]) {
+		struct in6_addr addr;
+		memcpy(&addr, RTA_DATA(tb[IFLA_GENEVE_REMOTE6]), sizeof(struct in6_addr));
+		if (memcmp(&addr, &in6addr_any, sizeof(addr)) != 0) {
+			if (IN6_IS_ADDR_MULTICAST(&addr))
+				fprintf(f, "remote %s ",
+					format_host(AF_INET6, sizeof(struct in6_addr), &addr, s1, sizeof(s1)));
+		}
 	}
 
 	if (tb[IFLA_GENEVE_TTL]) {
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* Re: [RFT] geneve: implement support for IPv6-based tunnels
  2015-09-24 18:34 [RFT] geneve: implement support for IPv6-based tunnels John W. Linville
  2015-09-24 18:39 ` [PATCH iproute2] geneve: add support for IPv6 link partners John W. Linville
@ 2015-09-25 12:08 ` Jiri Benc
  2015-09-28 19:20   ` John W. Linville
  2015-09-30 17:04 ` [RFT v2] " John W. Linville
  2 siblings, 1 reply; 37+ messages in thread
From: Jiri Benc @ 2015-09-25 12:08 UTC (permalink / raw)
  To: John W. Linville; +Cc: netdev, davem, Pravin B Shelar, Jesse Gross

On Thu, 24 Sep 2015 14:34:42 -0400, John W. Linville wrote:
> +#if IS_ENABLED(CONFIG_IPV6)
> +static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev)
> +{
> +	struct geneve_dev *geneve = netdev_priv(dev);
> +	struct geneve_sock *gs = geneve->sock;
> +	struct ip_tunnel_info *info = NULL;
> +	struct dst_entry *dst = NULL;
> +	struct flowi6 fl6;
> +	__u8 ttl;
> +	__be16 sport;
> +	bool udp_csum;
> +	int err;
> +	bool xnet = !net_eq(geneve->net, dev_net(geneve->dev));
> +
> +	if (geneve->collect_md) {
> +		info = skb_tunnel_info(skb);
> +		if (unlikely(info && info->mode != IP_TUNNEL_INFO_TX)) {
> +			netdev_dbg(dev, "no tunnel metadata\n");
> +			goto tx_error;
> +		}
> +	}

You may get IPv4 tunnel info here. Either a check whether it's really
IPv6 is needed or, better, decide whether to use IPv4 or IPv6 for xmit
based on the tunnel info. See below.

> +static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
> +{
> +#if IS_ENABLED(CONFIG_IPV6)
> +	struct geneve_dev *geneve = netdev_priv(dev);
> +
> +	if (geneve->remote.sa.sa_family == AF_INET6)
> +		return geneve6_xmit_skb(skb, dev);
> +#endif
> +	return geneve_xmit_skb(skb, dev);
> +}

For metadata based tunnels, there should be no requirement for the
remote to be specified. As the consequence, you cannot decide based on
the remote type.

To be really useful, geneve should open both IPv4 and IPv6 socket when
it's metadata based. Take a look at my recent patchset that does this
for vxlan: http://thread.gmane.org/gmane.linux.network/379282

 Jiri

-- 
Jiri Benc

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [RFT] geneve: implement support for IPv6-based tunnels
  2015-09-25 12:08 ` [RFT] geneve: implement support for IPv6-based tunnels Jiri Benc
@ 2015-09-28 19:20   ` John W. Linville
  2015-09-29 16:10     ` Jiri Benc
  0 siblings, 1 reply; 37+ messages in thread
From: John W. Linville @ 2015-09-28 19:20 UTC (permalink / raw)
  To: Jiri Benc; +Cc: netdev, davem, Pravin B Shelar, Jesse Gross

On Fri, Sep 25, 2015 at 02:08:44PM +0200, Jiri Benc wrote:
> On Thu, 24 Sep 2015 14:34:42 -0400, John W. Linville wrote:
> > +#if IS_ENABLED(CONFIG_IPV6)
> > +static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev)
> > +{
> > +	struct geneve_dev *geneve = netdev_priv(dev);
> > +	struct geneve_sock *gs = geneve->sock;
> > +	struct ip_tunnel_info *info = NULL;
> > +	struct dst_entry *dst = NULL;
> > +	struct flowi6 fl6;
> > +	__u8 ttl;
> > +	__be16 sport;
> > +	bool udp_csum;
> > +	int err;
> > +	bool xnet = !net_eq(geneve->net, dev_net(geneve->dev));
> > +
> > +	if (geneve->collect_md) {
> > +		info = skb_tunnel_info(skb);
> > +		if (unlikely(info && info->mode != IP_TUNNEL_INFO_TX)) {
> > +			netdev_dbg(dev, "no tunnel metadata\n");
> > +			goto tx_error;
> > +		}
> > +	}
> 
> You may get IPv4 tunnel info here. Either a check whether it's really
> IPv6 is needed or, better, decide whether to use IPv4 or IPv6 for xmit
> based on the tunnel info. See below.
> 
> > +static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
> > +{
> > +#if IS_ENABLED(CONFIG_IPV6)
> > +	struct geneve_dev *geneve = netdev_priv(dev);
> > +
> > +	if (geneve->remote.sa.sa_family == AF_INET6)
> > +		return geneve6_xmit_skb(skb, dev);
> > +#endif
> > +	return geneve_xmit_skb(skb, dev);
> > +}
> 
> For metadata based tunnels, there should be no requirement for the
> remote to be specified. As the consequence, you cannot decide based on
> the remote type.

Sure, that makes sense.  I'm testing something now...

> To be really useful, geneve should open both IPv4 and IPv6 socket when
> it's metadata based. Take a look at my recent patchset that does this
> for vxlan: http://thread.gmane.org/gmane.linux.network/379282

OK, that seems simple enough.  So we should just assume that a metadata
tunnel could do either protocol at any time?  Or are there more rules
than that?

John
-- 
John W. Linville		Someday the world will need a hero, and you
linville@tuxdriver.com			might be all we have.  Be ready.

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [RFT] geneve: implement support for IPv6-based tunnels
  2015-09-28 19:20   ` John W. Linville
@ 2015-09-29 16:10     ` Jiri Benc
  0 siblings, 0 replies; 37+ messages in thread
From: Jiri Benc @ 2015-09-29 16:10 UTC (permalink / raw)
  To: John W. Linville; +Cc: netdev, davem, Pravin B Shelar, Jesse Gross

On Mon, 28 Sep 2015 15:20:33 -0400, John W. Linville wrote:
> > To be really useful, geneve should open both IPv4 and IPv6 socket when
> > it's metadata based. Take a look at my recent patchset that does this
> > for vxlan: http://thread.gmane.org/gmane.linux.network/379282
> 
> OK, that seems simple enough.  So we should just assume that a metadata
> tunnel could do either protocol at any time?  Or are there more rules
> than that?

That should be it, on egress. On ingress, udp_tun_rx_dst needs to be
called with the appropriate family which seems to be missing from your
patch, too (there's AF_INET unconditionally, currently).

 Jiri

-- 
Jiri Benc

^ permalink raw reply	[flat|nested] 37+ messages in thread

* [RFT v2] geneve: implement support for IPv6-based tunnels
  2015-09-24 18:34 [RFT] geneve: implement support for IPv6-based tunnels John W. Linville
  2015-09-24 18:39 ` [PATCH iproute2] geneve: add support for IPv6 link partners John W. Linville
  2015-09-25 12:08 ` [RFT] geneve: implement support for IPv6-based tunnels Jiri Benc
@ 2015-09-30 17:04 ` John W. Linville
  2015-09-30 18:07   ` kbuild test robot
  2015-09-30 18:34   ` [RFT v3] " John W. Linville
  2 siblings, 2 replies; 37+ messages in thread
From: John W. Linville @ 2015-09-30 17:04 UTC (permalink / raw)
  To: netdev; +Cc: Pravin B Shelar, Jesse Gross, Jiri Benc, davem, John W. Linville

Signed-off-by: John W. Linville <linville@tuxdriver.com>
---
v2:
- do not require remote address for tx on metadata tunnels
- pass correct sockaddr family to udp_tun_rx_dst in geneve_rx
- accommodate both ipv4 and ipv6 sockets open on same tunnel
- move declaration of geneve_get_dst for aesthetic purposes

 drivers/net/geneve.c         | 430 ++++++++++++++++++++++++++++++++++++-------
 include/uapi/linux/if_link.h |   1 +
 2 files changed, 368 insertions(+), 63 deletions(-)

diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c
index 8f5c02eed47d..546b5350a008 100644
--- a/drivers/net/geneve.c
+++ b/drivers/net/geneve.c
@@ -46,16 +46,28 @@ struct geneve_net {
 
 static int geneve_net_id;
 
+union geneve_addr {
+	struct sockaddr_in sin;
+	struct sockaddr_in6 sin6;
+	struct sockaddr sa;
+};
+
+union geneve_addr geneve_remote_unspec = { .sa.sa_family = AF_UNSPEC, };
+
+#define GENEVE_F_IPV6		0x00000001
+
 /* Pseudo network device */
 struct geneve_dev {
 	struct hlist_node  hlist;	/* vni hash table */
 	struct net	   *net;	/* netns for packet i/o */
 	struct net_device  *dev;	/* netdev for geneve tunnel */
-	struct geneve_sock *sock;	/* socket used for geneve tunnel */
+	struct geneve_sock *sock4;	/* IPv4 socket used for geneve tunnel */
+	struct geneve_sock *sock6;	/* IPv6 socket used for geneve tunnel */
 	u8                 vni[3];	/* virtual network ID for tunnel */
 	u8                 ttl;		/* TTL override */
 	u8                 tos;		/* TOS override */
-	struct sockaddr_in remote;	/* IPv4 address for link partner */
+	u32                flags;	/* GENEVE_F_* above */
+	union geneve_addr  remote;	/* IP address for link partner */
 	struct list_head   next;	/* geneve's per namespace list */
 	__be16		   dst_port;
 	bool		   collect_md;
@@ -103,11 +115,32 @@ static struct geneve_dev *geneve_lookup(struct geneve_sock *gs,
 	vni_list_head = &gs->vni_list[hash];
 	hlist_for_each_entry_rcu(geneve, vni_list_head, hlist) {
 		if (!memcmp(vni, geneve->vni, sizeof(geneve->vni)) &&
-		    addr == geneve->remote.sin_addr.s_addr)
+		    addr == geneve->remote.sin.sin_addr.s_addr)
+			return geneve;
+	}
+	return NULL;
+}
+
+#if IS_ENABLED(CONFIG_IPV6)
+static struct geneve_dev *geneve6_lookup(struct geneve_sock *gs,
+					 struct in6_addr addr6, u8 vni[])
+{
+	struct hlist_head *vni_list_head;
+	struct geneve_dev *geneve;
+	__u32 hash;
+
+	/* Find the device for this VNI */
+	hash = geneve_net_vni_hash(vni);
+	vni_list_head = &gs->vni_list[hash];
+	hlist_for_each_entry_rcu(geneve, vni_list_head, hlist) {
+		if (!memcmp(vni, geneve->vni, sizeof(geneve->vni)) &&
+		    !memcmp(&addr6, &geneve->remote.sin6.sin6_addr,
+			    sizeof(addr6)))
 			return geneve;
 	}
 	return NULL;
 }
+#endif
 
 static inline struct genevehdr *geneve_hdr(const struct sk_buff *skb)
 {
@@ -121,24 +154,47 @@ static void geneve_rx(struct geneve_sock *gs, struct sk_buff *skb)
 	struct metadata_dst *tun_dst = NULL;
 	struct geneve_dev *geneve = NULL;
 	struct pcpu_sw_netstats *stats;
-	struct iphdr *iph;
-	u8 *vni;
+	struct iphdr *iph = NULL;
 	__be32 addr;
-	int err;
+	static u8 zero_vni[3];
+	u8 *vni;
+	int err = 0;
+	sa_family_t sa_family;
+#if IS_ENABLED(CONFIG_IPV6)
+	struct ipv6hdr *ip6h = NULL;
+	struct in6_addr addr6;
+	static struct in6_addr zero_addr6;
+#endif
+
+	sa_family = gs->sock->sk->sk_family;
 
-	iph = ip_hdr(skb); /* outer IP header... */
+	if (sa_family == AF_INET) {
+		iph = ip_hdr(skb); /* outer IP header... */
 
-	if (gs->collect_md) {
-		static u8 zero_vni[3];
+		if (gs->collect_md) {
+			vni = zero_vni;
+			addr = 0;
+		} else {
+			vni = gnvh->vni;
 
-		vni = zero_vni;
-		addr = 0;
-	} else {
-		vni = gnvh->vni;
-		addr = iph->saddr;
-	}
+			addr = iph->saddr;
+		}
+
+		geneve = geneve_lookup(gs, addr, vni);
+	} else if (sa_family == AF_INET6) {
+		ip6h = ipv6_hdr(skb); /* outer IPv6 header... */
+
+		if (gs->collect_md) {
+			vni = zero_vni;
+			addr6 = zero_addr6;
+		} else {
+			vni = gnvh->vni;
+
+			addr6 = ip6h->saddr;
+		}
 
-	geneve = geneve_lookup(gs, addr, vni);
+		geneve = geneve6_lookup(gs, addr6, vni);
+	}
 	if (!geneve)
 		goto drop;
 
@@ -149,7 +205,7 @@ static void geneve_rx(struct geneve_sock *gs, struct sk_buff *skb)
 			(gnvh->oam ? TUNNEL_OAM : 0) |
 			(gnvh->critical ? TUNNEL_CRIT_OPT : 0);
 
-		tun_dst = udp_tun_rx_dst(skb, AF_INET, flags,
+		tun_dst = udp_tun_rx_dst(skb, sa_family, flags,
 					 vni_to_tunnel_id(gnvh->vni),
 					 gnvh->opt_len * 4);
 		if (!tun_dst)
@@ -179,12 +235,21 @@ static void geneve_rx(struct geneve_sock *gs, struct sk_buff *skb)
 
 	skb_reset_network_header(skb);
 
-	err = IP_ECN_decapsulate(iph, skb);
+	if (iph)
+		err = IP_ECN_decapsulate(iph, skb);
+	if (ip6h)
+		err = IP6_ECN_decapsulate(ip6h, skb);
 
 	if (unlikely(err)) {
-		if (log_ecn_error)
-			net_info_ratelimited("non-ECT from %pI4 with TOS=%#x\n",
-					     &iph->saddr, iph->tos);
+		if (log_ecn_error) {
+			if (iph)
+				net_info_ratelimited("non-ECT from %pI4 "
+						     "with TOS=%#x\n",
+						     &iph->saddr, iph->tos);
+			if (ip6h)
+				net_info_ratelimited("non-ECT from %pI6\n",
+						     &ip6h->saddr);
+		}
 		if (err > 1) {
 			++geneve->dev->stats.rx_frame_errors;
 			++geneve->dev->stats.rx_errors;
@@ -284,6 +349,7 @@ static struct socket *geneve_create_sock(struct net *net, bool ipv6,
 
 	if (ipv6) {
 		udp_conf.family = AF_INET6;
+		udp_conf.ipv6_v6only = 1;
 	} else {
 		udp_conf.family = AF_INET;
 		udp_conf.local_ip.s_addr = htonl(INADDR_ANY);
@@ -458,7 +524,7 @@ static void geneve_notify_del_rx_port(struct geneve_sock *gs)
 		udp_del_offload(&gs->udp_offloads);
 }
 
-static void geneve_sock_release(struct geneve_sock *gs)
+static void __geneve_sock_release(struct geneve_sock *gs)
 {
 	if (--gs->refcnt)
 		return;
@@ -469,58 +535,107 @@ static void geneve_sock_release(struct geneve_sock *gs)
 	kfree_rcu(gs, rcu);
 }
 
+static void geneve_sock_release(struct geneve_dev *geneve)
+{
+	__geneve_sock_release(geneve->sock4);
+#if IS_ENABLED(CONFIG_IPV6)
+	__geneve_sock_release(geneve->sock6);
+#endif
+}
+
 static struct geneve_sock *geneve_find_sock(struct geneve_net *gn,
+					    sa_family_t family,
 					    __be16 dst_port)
 {
 	struct geneve_sock *gs;
 
 	list_for_each_entry(gs, &gn->sock_list, list) {
 		if (inet_sk(gs->sock->sk)->inet_sport == dst_port &&
-		    inet_sk(gs->sock->sk)->sk.sk_family == AF_INET) {
+		    inet_sk(gs->sock->sk)->sk.sk_family == family) {
 			return gs;
 		}
 	}
 	return NULL;
 }
 
-static int geneve_open(struct net_device *dev)
+static int geneve_sock_add(struct geneve_dev *geneve, bool ipv6)
 {
-	struct geneve_dev *geneve = netdev_priv(dev);
 	struct net *net = geneve->net;
 	struct geneve_net *gn = net_generic(net, geneve_net_id);
 	struct geneve_sock *gs;
 	__u32 hash;
 
-	gs = geneve_find_sock(gn, geneve->dst_port);
+	gs = geneve_find_sock(gn, ipv6 ? AF_INET6 : AF_INET, geneve->dst_port);
 	if (gs) {
 		gs->refcnt++;
 		goto out;
 	}
 
-	gs = geneve_socket_create(net, geneve->dst_port, false);
+	gs = geneve_socket_create(net, geneve->dst_port, ipv6);
 	if (IS_ERR(gs))
 		return PTR_ERR(gs);
 
 out:
 	gs->collect_md = geneve->collect_md;
-	geneve->sock = gs;
+#if IS_ENABLED(CONFIG_IPV6)
+	if (ipv6)
+		geneve->sock6 = gs;
+	else
+#endif
+		geneve->sock4 = gs;
 
 	hash = geneve_net_vni_hash(geneve->vni);
 	hlist_add_head_rcu(&geneve->hlist, &gs->vni_list[hash]);
 	return 0;
 }
 
+static int geneve_open(struct net_device *dev)
+{
+	struct geneve_dev *geneve = netdev_priv(dev);
+	bool ipv6 = !!(geneve->flags & GENEVE_F_IPV6);
+	bool metadata = !!geneve->collect_md;
+	int ret = 0;
+
+	geneve->sock4 = NULL;
+#if IS_ENABLED(CONFIG_IPV6)
+	geneve->sock6 = NULL;
+	if (ipv6 || metadata)
+		ret = geneve_sock_add(geneve, true);
+#endif
+	if (!ret && (!ipv6 || metadata))
+		ret = geneve_sock_add(geneve, false);
+	if (ret < 0)
+		geneve_sock_release(geneve);
+
+	return ret;
+}
+
 static int geneve_stop(struct net_device *dev)
 {
 	struct geneve_dev *geneve = netdev_priv(dev);
-	struct geneve_sock *gs = geneve->sock;
 
 	if (!hlist_unhashed(&geneve->hlist))
 		hlist_del_rcu(&geneve->hlist);
-	geneve_sock_release(gs);
+	geneve_sock_release(geneve);
 	return 0;
 }
 
+static void geneve_build_header(struct genevehdr *geneveh,
+				__be16 tun_flags, u8 vni[3],
+				u8 options_len, u8 *options)
+{
+	geneveh->ver = GENEVE_VER;
+	geneveh->opt_len = options_len / 4;
+	geneveh->oam = !!(tun_flags & TUNNEL_OAM);
+	geneveh->critical = !!(tun_flags & TUNNEL_CRIT_OPT);
+	geneveh->rsvd1 = 0;
+	memcpy(geneveh->vni, vni, 3);
+	geneveh->proto_type = htons(ETH_P_TEB);
+	geneveh->rsvd2 = 0;
+
+	memcpy(geneveh->options, options, options_len);
+}
+
 static int geneve_build_skb(struct rtable *rt, struct sk_buff *skb,
 			    __be16 tun_flags, u8 vni[3], u8 opt_len, u8 *opt,
 			    bool csum)
@@ -544,15 +659,7 @@ static int geneve_build_skb(struct rtable *rt, struct sk_buff *skb,
 	}
 
 	gnvh = (struct genevehdr *)__skb_push(skb, sizeof(*gnvh) + opt_len);
-	gnvh->ver = GENEVE_VER;
-	gnvh->opt_len = opt_len / 4;
-	gnvh->oam = !!(tun_flags & TUNNEL_OAM);
-	gnvh->critical = !!(tun_flags & TUNNEL_CRIT_OPT);
-	gnvh->rsvd1 = 0;
-	memcpy(gnvh->vni, vni, 3);
-	gnvh->proto_type = htons(ETH_P_TEB);
-	gnvh->rsvd2 = 0;
-	memcpy(gnvh->options, opt, opt_len);
+	geneve_build_header(gnvh, tun_flags, vni, opt_len, opt);
 
 	skb_set_inner_protocol(skb, htons(ETH_P_TEB));
 	return 0;
@@ -562,6 +669,43 @@ free_rt:
 	return err;
 }
 
+#if IS_ENABLED(CONFIG_IPV6)
+static int geneve6_build_skb(struct dst_entry *dst, struct sk_buff *skb,
+			     __be16 tun_flags, u8 vni[3], u8 opt_len, u8 *opt,
+			     bool csum, bool xnet)
+{
+	struct genevehdr *gnvh;
+	int min_headroom;
+	int err;
+
+	skb_scrub_packet(skb, xnet);
+
+	min_headroom = LL_RESERVED_SPACE(dst->dev) + dst->header_len
+			+ GENEVE_BASE_HLEN + opt_len + sizeof(struct ipv6hdr);
+	err = skb_cow_head(skb, min_headroom);
+	if (unlikely(err)) {
+		kfree_skb(skb);
+		goto free_dst;
+	}
+
+	skb = udp_tunnel_handle_offloads(skb, csum);
+	if (IS_ERR(skb)) {
+		err = PTR_ERR(skb);
+		goto free_dst;
+	}
+
+	gnvh = (struct genevehdr *)__skb_push(skb, sizeof(*gnvh) + opt_len);
+	geneve_build_header(gnvh, tun_flags, vni, opt_len, opt);
+
+	skb_set_inner_protocol(skb, htons(ETH_P_TEB));
+	return 0;
+
+free_dst:
+	dst_release(dst);
+	return err;
+}
+#endif
+
 static struct rtable *geneve_get_rt(struct sk_buff *skb,
 				    struct net_device *dev,
 				    struct flowi4 *fl4,
@@ -588,7 +732,7 @@ static struct rtable *geneve_get_rt(struct sk_buff *skb,
 		}
 
 		fl4->flowi4_tos = RT_TOS(tos);
-		fl4->daddr = geneve->remote.sin_addr.s_addr;
+		fl4->daddr = geneve->remote.sin.sin_addr.s_addr;
 	}
 
 	rt = ip_route_output_key(geneve->net, fl4);
@@ -606,6 +750,42 @@ static struct rtable *geneve_get_rt(struct sk_buff *skb,
 	return rt;
 }
 
+#if IS_ENABLED(CONFIG_IPV6)
+static struct dst_entry *geneve_get_dst(struct sk_buff *skb,
+					struct net_device *dev,
+					struct flowi6 *fl6,
+					struct ip_tunnel_info *info)
+{
+	struct geneve_dev *geneve = netdev_priv(dev);
+	struct geneve_sock *gs6 = geneve->sock6;
+	struct dst_entry *dst = NULL;
+
+	memset(fl6, 0, sizeof(*fl6));
+	fl6->flowi6_mark = skb->mark;
+	fl6->flowi6_proto = IPPROTO_UDP;
+
+	if (info) {
+		fl6->daddr = info->key.u.ipv6.dst;
+		fl6->saddr = info->key.u.ipv6.src;
+	} else {
+		fl6->daddr = geneve->remote.sin6.sin6_addr;
+	}
+
+	if (ipv6_stub->ipv6_dst_lookup(geneve->net, gs6->sock->sk, &dst, fl6)) {
+		netdev_dbg(dev, "no route to %pI6\n", &fl6->daddr);
+		dev->stats.tx_carrier_errors++;
+		return ERR_PTR(-EHOSTUNREACH);
+	}
+	if (dst->dev == dev) { /* is this necessary? */
+		netdev_dbg(dev, "circular route to %pI6\n", &fl6->daddr);
+		dev->stats.collisions++;
+		dst_release(dst);
+		return ERR_PTR(-EINVAL);
+	}
+	return dst;
+}
+#endif
+
 /* Convert 64 bit tunnel ID to 24 bit VNI. */
 static void tunnel_id_to_vni(__be64 tun_id, __u8 *vni)
 {
@@ -620,11 +800,11 @@ static void tunnel_id_to_vni(__be64 tun_id, __u8 *vni)
 #endif
 }
 
-static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev,
+				   struct ip_tunnel_info *info)
 {
 	struct geneve_dev *geneve = netdev_priv(dev);
-	struct geneve_sock *gs = geneve->sock;
-	struct ip_tunnel_info *info = NULL;
+	struct geneve_sock *gs4 = geneve->sock4;
 	struct rtable *rt = NULL;
 	const struct iphdr *iip; /* interior IP header */
 	struct flowi4 fl4;
@@ -635,7 +815,6 @@ static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
 	int err;
 
 	if (geneve->collect_md) {
-		info = skb_tunnel_info(skb);
 		if (unlikely(info && !(info->mode & IP_TUNNEL_INFO_TX))) {
 			netdev_dbg(dev, "no tunnel metadata\n");
 			goto tx_error;
@@ -688,7 +867,7 @@ static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
 		ttl = ttl ? : ip4_dst_hoplimit(&rt->dst);
 		df = 0;
 	}
-	err = udp_tunnel_xmit_skb(rt, gs->sock->sk, skb, fl4.saddr, fl4.daddr,
+	err = udp_tunnel_xmit_skb(rt, gs4->sock->sk, skb, fl4.saddr, fl4.daddr,
 				  tos, ttl, df, sport, geneve->dst_port,
 				  !net_eq(geneve->net, dev_net(geneve->dev)),
 				  !udp_csum);
@@ -703,6 +882,97 @@ err:
 	return NETDEV_TX_OK;
 }
 
+#if IS_ENABLED(CONFIG_IPV6)
+static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
+				    struct ip_tunnel_info *info)
+{
+	struct geneve_dev *geneve = netdev_priv(dev);
+	struct geneve_sock *gs6 = geneve->sock6;
+	struct dst_entry *dst = NULL;
+	struct flowi6 fl6;
+	__u8 ttl;
+	__be16 sport;
+	bool udp_csum;
+	int err;
+	bool xnet = !net_eq(geneve->net, dev_net(geneve->dev));
+
+	if (geneve->collect_md) {
+		if (unlikely(info && info->mode != IP_TUNNEL_INFO_TX)) {
+			netdev_dbg(dev, "no tunnel metadata\n");
+			goto tx_error;
+		}
+	}
+
+	dst = geneve_get_dst(skb, dev, &fl6, info);
+	if (IS_ERR(dst)) {
+		netdev_dbg(dev, "no route to %pI6\n", &fl6.daddr);
+		dev->stats.tx_carrier_errors++;
+		goto tx_error;
+	}
+
+	sport = udp_flow_src_port(geneve->net, skb, 1, USHRT_MAX, true);
+	skb_reset_mac_header(skb);
+
+	if (info) {
+		const struct ip_tunnel_key *key = &info->key;
+		u8 *opts = NULL;
+		u8 vni[3];
+
+		tunnel_id_to_vni(key->tun_id, vni);
+		if (key->tun_flags & TUNNEL_GENEVE_OPT)
+			opts = ip_tunnel_info_opts(info);
+
+		udp_csum = !!(key->tun_flags & TUNNEL_CSUM);
+		err = geneve6_build_skb(dst, skb, key->tun_flags, vni,
+					info->options_len, opts,
+					udp_csum, xnet);
+		if (unlikely(err))
+			goto err;
+
+		ttl = key->ttl;
+	} else {
+		udp_csum = false;
+		err = geneve6_build_skb(dst, skb, 0, geneve->vni,
+					0, NULL, udp_csum, xnet);
+		if (unlikely(err))
+			goto err;
+
+		ttl = geneve->ttl;
+		if (!ttl && ipv6_addr_is_multicast(&fl6.daddr))
+			ttl = 1;
+		ttl = ttl ? : ip6_dst_hoplimit(dst);
+	}
+	err = udp_tunnel6_xmit_skb(dst, gs6->sock->sk, skb, dev,
+				   &fl6.saddr, &fl6.daddr, 0, ttl,
+				   sport, geneve->dst_port, !udp_csum);
+
+	iptunnel_xmit_stats(err, &dev->stats, dev->tstats);
+	return NETDEV_TX_OK;
+
+tx_error:
+	dev_kfree_skb(skb);
+err:
+	dev->stats.tx_errors++;
+	return NETDEV_TX_OK;
+}
+#endif
+
+static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct geneve_dev *geneve = netdev_priv(dev);
+	struct ip_tunnel_info *info = NULL;
+
+	if (geneve->collect_md)
+		info = skb_tunnel_info(skb);
+
+#if IS_ENABLED(CONFIG_IPV6)
+	if ((info && ip_tunnel_info_af(info) == AF_INET6) ||
+	    (!info && geneve->remote.sa.sa_family == AF_INET6))
+		return geneve6_xmit_skb(skb, dev, info);
+#endif
+	return geneve_xmit_skb(skb, dev, info);
+}
+
 static const struct net_device_ops geneve_netdev_ops = {
 	.ndo_init		= geneve_init,
 	.ndo_uninit		= geneve_uninit,
@@ -759,6 +1029,7 @@ static void geneve_setup(struct net_device *dev)
 static const struct nla_policy geneve_policy[IFLA_GENEVE_MAX + 1] = {
 	[IFLA_GENEVE_ID]		= { .type = NLA_U32 },
 	[IFLA_GENEVE_REMOTE]		= { .len = FIELD_SIZEOF(struct iphdr, daddr) },
+	[IFLA_GENEVE_REMOTE6]		= { .len = sizeof(struct in6_addr) },
 	[IFLA_GENEVE_TTL]		= { .type = NLA_U8 },
 	[IFLA_GENEVE_TOS]		= { .type = NLA_U8 },
 	[IFLA_GENEVE_PORT]		= { .type = NLA_U16 },
@@ -790,7 +1061,7 @@ static int geneve_validate(struct nlattr *tb[], struct nlattr *data[])
 
 static struct geneve_dev *geneve_find_dev(struct geneve_net *gn,
 					  __be16 dst_port,
-					  __be32 rem_addr,
+					  union geneve_addr *remote,
 					  u8 vni[],
 					  bool *tun_on_same_port,
 					  bool *tun_collect_md)
@@ -806,7 +1077,7 @@ static struct geneve_dev *geneve_find_dev(struct geneve_net *gn,
 			*tun_on_same_port = true;
 		}
 		if (!memcmp(vni, geneve->vni, sizeof(geneve->vni)) &&
-		    rem_addr == geneve->remote.sin_addr.s_addr &&
+		    !memcmp(remote, &geneve->remote, sizeof(geneve->remote)) &&
 		    dst_port == geneve->dst_port)
 			t = geneve;
 	}
@@ -814,8 +1085,9 @@ static struct geneve_dev *geneve_find_dev(struct geneve_net *gn,
 }
 
 static int geneve_configure(struct net *net, struct net_device *dev,
-			    __be32 rem_addr, __u32 vni, __u8 ttl, __u8 tos,
-			    __be16 dst_port, bool metadata)
+			    union geneve_addr *remote,
+			    __u32 vni, __u8 ttl, __u8 tos, __be16 dst_port,
+			    bool metadata)
 {
 	struct geneve_net *gn = net_generic(net, geneve_net_id);
 	struct geneve_dev *t, *geneve = netdev_priv(dev);
@@ -823,9 +1095,11 @@ static int geneve_configure(struct net *net, struct net_device *dev,
 	int err;
 
 	if (metadata) {
-		if (rem_addr || vni || tos || ttl)
+		if (remote != &geneve_remote_unspec || vni || tos || ttl)
 			return -EINVAL;
 	}
+	if (!remote)
+		return -EINVAL;
 
 	geneve->net = net;
 	geneve->dev = dev;
@@ -834,16 +1108,22 @@ static int geneve_configure(struct net *net, struct net_device *dev,
 	geneve->vni[1] = (vni & 0x0000ff00) >> 8;
 	geneve->vni[2] =  vni & 0x000000ff;
 
-	geneve->remote.sin_addr.s_addr = rem_addr;
-	if (IN_MULTICAST(ntohl(geneve->remote.sin_addr.s_addr)))
+	if ((remote->sa.sa_family == AF_INET &&
+	     IN_MULTICAST(ntohl(remote->sin.sin_addr.s_addr))) ||
+	    (remote->sa.sa_family == AF_INET6 &&
+	     ipv6_addr_is_multicast(&remote->sin6.sin6_addr)))
 		return -EINVAL;
+	geneve->remote = *remote;
+
+	if (geneve->remote.sa.sa_family == AF_INET6)
+		geneve->flags |= GENEVE_F_IPV6;
 
 	geneve->ttl = ttl;
 	geneve->tos = tos;
 	geneve->dst_port = dst_port;
 	geneve->collect_md = metadata;
 
-	t = geneve_find_dev(gn, dst_port, rem_addr, geneve->vni,
+	t = geneve_find_dev(gn, dst_port, remote, geneve->vni,
 			    &tun_on_same_port, &tun_collect_md);
 	if (t)
 		return -EBUSY;
@@ -870,14 +1150,29 @@ static int geneve_newlink(struct net *net, struct net_device *dev,
 	__be16 dst_port = htons(GENEVE_UDP_PORT);
 	__u8 ttl = 0, tos = 0;
 	bool metadata = false;
-	__be32 rem_addr;
+	union geneve_addr remote;
 	__u32 vni;
 
-	if (!data[IFLA_GENEVE_ID] || !data[IFLA_GENEVE_REMOTE])
+	if (!data[IFLA_GENEVE_ID] ||
+	    (data[IFLA_GENEVE_REMOTE] && data[IFLA_GENEVE_REMOTE6]) ||
+	    (!data[IFLA_GENEVE_REMOTE] && !data[IFLA_GENEVE_REMOTE6]))
 		return -EINVAL;
 
 	vni = nla_get_u32(data[IFLA_GENEVE_ID]);
-	rem_addr = nla_get_in_addr(data[IFLA_GENEVE_REMOTE]);
+
+	memset(&remote, 0, sizeof(remote));
+	if (data[IFLA_GENEVE_REMOTE]) {
+		remote.sa.sa_family = AF_INET;
+		remote.sin.sin_addr.s_addr =
+			nla_get_in_addr(data[IFLA_GENEVE_REMOTE]);
+	} else if (data[IFLA_GENEVE_REMOTE6]) {
+		if (!IS_ENABLED(CONFIG_IPV6))
+			return -EPFNOSUPPORT;
+
+		remote.sa.sa_family = AF_INET6;
+		remote.sin6.sin6_addr =
+			nla_get_in6_addr(data[IFLA_GENEVE_REMOTE6]);
+	}
 
 	if (data[IFLA_GENEVE_TTL])
 		ttl = nla_get_u8(data[IFLA_GENEVE_TTL]);
@@ -891,8 +1186,8 @@ static int geneve_newlink(struct net *net, struct net_device *dev,
 	if (data[IFLA_GENEVE_COLLECT_METADATA])
 		metadata = true;
 
-	return geneve_configure(net, dev, rem_addr, vni,
-				ttl, tos, dst_port, metadata);
+	return geneve_configure(net, dev, &remote, vni, ttl, tos, dst_port,
+				metadata);
 }
 
 static void geneve_dellink(struct net_device *dev, struct list_head *head)
@@ -906,7 +1201,7 @@ static void geneve_dellink(struct net_device *dev, struct list_head *head)
 static size_t geneve_get_size(const struct net_device *dev)
 {
 	return nla_total_size(sizeof(__u32)) +	/* IFLA_GENEVE_ID */
-		nla_total_size(sizeof(struct in_addr)) + /* IFLA_GENEVE_REMOTE */
+		nla_total_size(sizeof(struct in6_addr)) + /* IFLA_GENEVE_REMOTE{6} */
 		nla_total_size(sizeof(__u8)) +  /* IFLA_GENEVE_TTL */
 		nla_total_size(sizeof(__u8)) +  /* IFLA_GENEVE_TOS */
 		nla_total_size(sizeof(__be16)) +  /* IFLA_GENEVE_PORT */
@@ -923,9 +1218,17 @@ static int geneve_fill_info(struct sk_buff *skb, const struct net_device *dev)
 	if (nla_put_u32(skb, IFLA_GENEVE_ID, vni))
 		goto nla_put_failure;
 
-	if (nla_put_in_addr(skb, IFLA_GENEVE_REMOTE,
-			    geneve->remote.sin_addr.s_addr))
-		goto nla_put_failure;
+	if (geneve->remote.sa.sa_family == AF_INET) {
+		if (nla_put_in_addr(skb, IFLA_GENEVE_REMOTE,
+				    geneve->remote.sin.sin_addr.s_addr))
+			goto nla_put_failure;
+#if IS_ENABLED(CONFIG_IPV6)
+	} else {
+		if (nla_put_in6_addr(skb, IFLA_GENEVE_REMOTE6,
+				     &geneve->remote.sin6.sin6_addr))
+			goto nla_put_failure;
+#endif
+	}
 
 	if (nla_put_u8(skb, IFLA_GENEVE_TTL, geneve->ttl) ||
 	    nla_put_u8(skb, IFLA_GENEVE_TOS, geneve->tos))
@@ -971,7 +1274,8 @@ struct net_device *geneve_dev_create_fb(struct net *net, const char *name,
 	if (IS_ERR(dev))
 		return dev;
 
-	err = geneve_configure(net, dev, 0, 0, 0, 0, htons(dst_port), true);
+	err = geneve_configure(net, dev, &geneve_remote_unspec,
+			       0, 0, 0, htons(dst_port), true);
 	if (err) {
 		free_netdev(dev);
 		return ERR_PTR(err);
diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
index 3a5f263cfc2f..b0cd0498ce5a 100644
--- a/include/uapi/linux/if_link.h
+++ b/include/uapi/linux/if_link.h
@@ -412,6 +412,7 @@ enum {
 	IFLA_GENEVE_TOS,
 	IFLA_GENEVE_PORT,	/* destination port */
 	IFLA_GENEVE_COLLECT_METADATA,
+	IFLA_GENEVE_REMOTE6,
 	__IFLA_GENEVE_MAX
 };
 #define IFLA_GENEVE_MAX	(__IFLA_GENEVE_MAX - 1)
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* Re: [RFT v2] geneve: implement support for IPv6-based tunnels
  2015-09-30 17:04 ` [RFT v2] " John W. Linville
@ 2015-09-30 18:07   ` kbuild test robot
  2015-09-30 18:34   ` [RFT v3] " John W. Linville
  1 sibling, 0 replies; 37+ messages in thread
From: kbuild test robot @ 2015-09-30 18:07 UTC (permalink / raw)
  To: John W. Linville
  Cc: kbuild-all, netdev, Pravin B Shelar, Jesse Gross, Jiri Benc,
	davem, John W. Linville

Hi John,

[auto build test results on v4.3-rc3 -- if it's inappropriate base, please ignore]

reproduce:
  # apt-get install sparse
  make ARCH=x86_64 allmodconfig
  make C=1 CF=-D__CHECK_ENDIAN__


sparse warnings: (new ones prefixed by >>)

>> drivers/net/geneve.c:55:19: sparse: symbol 'geneve_remote_unspec' was not declared. Should it be static?

Please review and possibly fold the followup patch.

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

^ permalink raw reply	[flat|nested] 37+ messages in thread

* [RFT v3] geneve: implement support for IPv6-based tunnels
  2015-09-30 17:04 ` [RFT v2] " John W. Linville
  2015-09-30 18:07   ` kbuild test robot
@ 2015-09-30 18:34   ` John W. Linville
  2015-10-01  1:55     ` kbuild test robot
                       ` (3 more replies)
  1 sibling, 4 replies; 37+ messages in thread
From: John W. Linville @ 2015-09-30 18:34 UTC (permalink / raw)
  To: netdev; +Cc: Pravin B Shelar, Jesse Gross, Jiri Benc, davem, John W. Linville

Signed-off-by: John W. Linville <linville@tuxdriver.com>
---
v3: 
- declare geneve_remote_unspec as static

v2:
- do not require remote address for tx on metadata tunnels
- pass correct sockaddr family to udp_tun_rx_dst in geneve_rx
- accommodate both ipv4 and ipv6 sockets open on same tunnel
- move declaration of geneve_get_dst for aesthetic purposes

 drivers/net/geneve.c         | 430 ++++++++++++++++++++++++++++++++++++-------
 include/uapi/linux/if_link.h |   1 +
 2 files changed, 368 insertions(+), 63 deletions(-)

diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c
index 8f5c02eed47d..291d3d7754a8 100644
--- a/drivers/net/geneve.c
+++ b/drivers/net/geneve.c
@@ -46,16 +46,28 @@ struct geneve_net {
 
 static int geneve_net_id;
 
+union geneve_addr {
+	struct sockaddr_in sin;
+	struct sockaddr_in6 sin6;
+	struct sockaddr sa;
+};
+
+static union geneve_addr geneve_remote_unspec = { .sa.sa_family = AF_UNSPEC, };
+
+#define GENEVE_F_IPV6		0x00000001
+
 /* Pseudo network device */
 struct geneve_dev {
 	struct hlist_node  hlist;	/* vni hash table */
 	struct net	   *net;	/* netns for packet i/o */
 	struct net_device  *dev;	/* netdev for geneve tunnel */
-	struct geneve_sock *sock;	/* socket used for geneve tunnel */
+	struct geneve_sock *sock4;	/* IPv4 socket used for geneve tunnel */
+	struct geneve_sock *sock6;	/* IPv6 socket used for geneve tunnel */
 	u8                 vni[3];	/* virtual network ID for tunnel */
 	u8                 ttl;		/* TTL override */
 	u8                 tos;		/* TOS override */
-	struct sockaddr_in remote;	/* IPv4 address for link partner */
+	u32                flags;	/* GENEVE_F_* above */
+	union geneve_addr  remote;	/* IP address for link partner */
 	struct list_head   next;	/* geneve's per namespace list */
 	__be16		   dst_port;
 	bool		   collect_md;
@@ -103,11 +115,32 @@ static struct geneve_dev *geneve_lookup(struct geneve_sock *gs,
 	vni_list_head = &gs->vni_list[hash];
 	hlist_for_each_entry_rcu(geneve, vni_list_head, hlist) {
 		if (!memcmp(vni, geneve->vni, sizeof(geneve->vni)) &&
-		    addr == geneve->remote.sin_addr.s_addr)
+		    addr == geneve->remote.sin.sin_addr.s_addr)
+			return geneve;
+	}
+	return NULL;
+}
+
+#if IS_ENABLED(CONFIG_IPV6)
+static struct geneve_dev *geneve6_lookup(struct geneve_sock *gs,
+					 struct in6_addr addr6, u8 vni[])
+{
+	struct hlist_head *vni_list_head;
+	struct geneve_dev *geneve;
+	__u32 hash;
+
+	/* Find the device for this VNI */
+	hash = geneve_net_vni_hash(vni);
+	vni_list_head = &gs->vni_list[hash];
+	hlist_for_each_entry_rcu(geneve, vni_list_head, hlist) {
+		if (!memcmp(vni, geneve->vni, sizeof(geneve->vni)) &&
+		    !memcmp(&addr6, &geneve->remote.sin6.sin6_addr,
+			    sizeof(addr6)))
 			return geneve;
 	}
 	return NULL;
 }
+#endif
 
 static inline struct genevehdr *geneve_hdr(const struct sk_buff *skb)
 {
@@ -121,24 +154,47 @@ static void geneve_rx(struct geneve_sock *gs, struct sk_buff *skb)
 	struct metadata_dst *tun_dst = NULL;
 	struct geneve_dev *geneve = NULL;
 	struct pcpu_sw_netstats *stats;
-	struct iphdr *iph;
-	u8 *vni;
+	struct iphdr *iph = NULL;
 	__be32 addr;
-	int err;
+	static u8 zero_vni[3];
+	u8 *vni;
+	int err = 0;
+	sa_family_t sa_family;
+#if IS_ENABLED(CONFIG_IPV6)
+	struct ipv6hdr *ip6h = NULL;
+	struct in6_addr addr6;
+	static struct in6_addr zero_addr6;
+#endif
+
+	sa_family = gs->sock->sk->sk_family;
 
-	iph = ip_hdr(skb); /* outer IP header... */
+	if (sa_family == AF_INET) {
+		iph = ip_hdr(skb); /* outer IP header... */
 
-	if (gs->collect_md) {
-		static u8 zero_vni[3];
+		if (gs->collect_md) {
+			vni = zero_vni;
+			addr = 0;
+		} else {
+			vni = gnvh->vni;
 
-		vni = zero_vni;
-		addr = 0;
-	} else {
-		vni = gnvh->vni;
-		addr = iph->saddr;
-	}
+			addr = iph->saddr;
+		}
+
+		geneve = geneve_lookup(gs, addr, vni);
+	} else if (sa_family == AF_INET6) {
+		ip6h = ipv6_hdr(skb); /* outer IPv6 header... */
+
+		if (gs->collect_md) {
+			vni = zero_vni;
+			addr6 = zero_addr6;
+		} else {
+			vni = gnvh->vni;
+
+			addr6 = ip6h->saddr;
+		}
 
-	geneve = geneve_lookup(gs, addr, vni);
+		geneve = geneve6_lookup(gs, addr6, vni);
+	}
 	if (!geneve)
 		goto drop;
 
@@ -149,7 +205,7 @@ static void geneve_rx(struct geneve_sock *gs, struct sk_buff *skb)
 			(gnvh->oam ? TUNNEL_OAM : 0) |
 			(gnvh->critical ? TUNNEL_CRIT_OPT : 0);
 
-		tun_dst = udp_tun_rx_dst(skb, AF_INET, flags,
+		tun_dst = udp_tun_rx_dst(skb, sa_family, flags,
 					 vni_to_tunnel_id(gnvh->vni),
 					 gnvh->opt_len * 4);
 		if (!tun_dst)
@@ -179,12 +235,21 @@ static void geneve_rx(struct geneve_sock *gs, struct sk_buff *skb)
 
 	skb_reset_network_header(skb);
 
-	err = IP_ECN_decapsulate(iph, skb);
+	if (iph)
+		err = IP_ECN_decapsulate(iph, skb);
+	if (ip6h)
+		err = IP6_ECN_decapsulate(ip6h, skb);
 
 	if (unlikely(err)) {
-		if (log_ecn_error)
-			net_info_ratelimited("non-ECT from %pI4 with TOS=%#x\n",
-					     &iph->saddr, iph->tos);
+		if (log_ecn_error) {
+			if (iph)
+				net_info_ratelimited("non-ECT from %pI4 "
+						     "with TOS=%#x\n",
+						     &iph->saddr, iph->tos);
+			if (ip6h)
+				net_info_ratelimited("non-ECT from %pI6\n",
+						     &ip6h->saddr);
+		}
 		if (err > 1) {
 			++geneve->dev->stats.rx_frame_errors;
 			++geneve->dev->stats.rx_errors;
@@ -284,6 +349,7 @@ static struct socket *geneve_create_sock(struct net *net, bool ipv6,
 
 	if (ipv6) {
 		udp_conf.family = AF_INET6;
+		udp_conf.ipv6_v6only = 1;
 	} else {
 		udp_conf.family = AF_INET;
 		udp_conf.local_ip.s_addr = htonl(INADDR_ANY);
@@ -458,7 +524,7 @@ static void geneve_notify_del_rx_port(struct geneve_sock *gs)
 		udp_del_offload(&gs->udp_offloads);
 }
 
-static void geneve_sock_release(struct geneve_sock *gs)
+static void __geneve_sock_release(struct geneve_sock *gs)
 {
 	if (--gs->refcnt)
 		return;
@@ -469,58 +535,107 @@ static void geneve_sock_release(struct geneve_sock *gs)
 	kfree_rcu(gs, rcu);
 }
 
+static void geneve_sock_release(struct geneve_dev *geneve)
+{
+	__geneve_sock_release(geneve->sock4);
+#if IS_ENABLED(CONFIG_IPV6)
+	__geneve_sock_release(geneve->sock6);
+#endif
+}
+
 static struct geneve_sock *geneve_find_sock(struct geneve_net *gn,
+					    sa_family_t family,
 					    __be16 dst_port)
 {
 	struct geneve_sock *gs;
 
 	list_for_each_entry(gs, &gn->sock_list, list) {
 		if (inet_sk(gs->sock->sk)->inet_sport == dst_port &&
-		    inet_sk(gs->sock->sk)->sk.sk_family == AF_INET) {
+		    inet_sk(gs->sock->sk)->sk.sk_family == family) {
 			return gs;
 		}
 	}
 	return NULL;
 }
 
-static int geneve_open(struct net_device *dev)
+static int geneve_sock_add(struct geneve_dev *geneve, bool ipv6)
 {
-	struct geneve_dev *geneve = netdev_priv(dev);
 	struct net *net = geneve->net;
 	struct geneve_net *gn = net_generic(net, geneve_net_id);
 	struct geneve_sock *gs;
 	__u32 hash;
 
-	gs = geneve_find_sock(gn, geneve->dst_port);
+	gs = geneve_find_sock(gn, ipv6 ? AF_INET6 : AF_INET, geneve->dst_port);
 	if (gs) {
 		gs->refcnt++;
 		goto out;
 	}
 
-	gs = geneve_socket_create(net, geneve->dst_port, false);
+	gs = geneve_socket_create(net, geneve->dst_port, ipv6);
 	if (IS_ERR(gs))
 		return PTR_ERR(gs);
 
 out:
 	gs->collect_md = geneve->collect_md;
-	geneve->sock = gs;
+#if IS_ENABLED(CONFIG_IPV6)
+	if (ipv6)
+		geneve->sock6 = gs;
+	else
+#endif
+		geneve->sock4 = gs;
 
 	hash = geneve_net_vni_hash(geneve->vni);
 	hlist_add_head_rcu(&geneve->hlist, &gs->vni_list[hash]);
 	return 0;
 }
 
+static int geneve_open(struct net_device *dev)
+{
+	struct geneve_dev *geneve = netdev_priv(dev);
+	bool ipv6 = !!(geneve->flags & GENEVE_F_IPV6);
+	bool metadata = !!geneve->collect_md;
+	int ret = 0;
+
+	geneve->sock4 = NULL;
+#if IS_ENABLED(CONFIG_IPV6)
+	geneve->sock6 = NULL;
+	if (ipv6 || metadata)
+		ret = geneve_sock_add(geneve, true);
+#endif
+	if (!ret && (!ipv6 || metadata))
+		ret = geneve_sock_add(geneve, false);
+	if (ret < 0)
+		geneve_sock_release(geneve);
+
+	return ret;
+}
+
 static int geneve_stop(struct net_device *dev)
 {
 	struct geneve_dev *geneve = netdev_priv(dev);
-	struct geneve_sock *gs = geneve->sock;
 
 	if (!hlist_unhashed(&geneve->hlist))
 		hlist_del_rcu(&geneve->hlist);
-	geneve_sock_release(gs);
+	geneve_sock_release(geneve);
 	return 0;
 }
 
+static void geneve_build_header(struct genevehdr *geneveh,
+				__be16 tun_flags, u8 vni[3],
+				u8 options_len, u8 *options)
+{
+	geneveh->ver = GENEVE_VER;
+	geneveh->opt_len = options_len / 4;
+	geneveh->oam = !!(tun_flags & TUNNEL_OAM);
+	geneveh->critical = !!(tun_flags & TUNNEL_CRIT_OPT);
+	geneveh->rsvd1 = 0;
+	memcpy(geneveh->vni, vni, 3);
+	geneveh->proto_type = htons(ETH_P_TEB);
+	geneveh->rsvd2 = 0;
+
+	memcpy(geneveh->options, options, options_len);
+}
+
 static int geneve_build_skb(struct rtable *rt, struct sk_buff *skb,
 			    __be16 tun_flags, u8 vni[3], u8 opt_len, u8 *opt,
 			    bool csum)
@@ -544,15 +659,7 @@ static int geneve_build_skb(struct rtable *rt, struct sk_buff *skb,
 	}
 
 	gnvh = (struct genevehdr *)__skb_push(skb, sizeof(*gnvh) + opt_len);
-	gnvh->ver = GENEVE_VER;
-	gnvh->opt_len = opt_len / 4;
-	gnvh->oam = !!(tun_flags & TUNNEL_OAM);
-	gnvh->critical = !!(tun_flags & TUNNEL_CRIT_OPT);
-	gnvh->rsvd1 = 0;
-	memcpy(gnvh->vni, vni, 3);
-	gnvh->proto_type = htons(ETH_P_TEB);
-	gnvh->rsvd2 = 0;
-	memcpy(gnvh->options, opt, opt_len);
+	geneve_build_header(gnvh, tun_flags, vni, opt_len, opt);
 
 	skb_set_inner_protocol(skb, htons(ETH_P_TEB));
 	return 0;
@@ -562,6 +669,43 @@ free_rt:
 	return err;
 }
 
+#if IS_ENABLED(CONFIG_IPV6)
+static int geneve6_build_skb(struct dst_entry *dst, struct sk_buff *skb,
+			     __be16 tun_flags, u8 vni[3], u8 opt_len, u8 *opt,
+			     bool csum, bool xnet)
+{
+	struct genevehdr *gnvh;
+	int min_headroom;
+	int err;
+
+	skb_scrub_packet(skb, xnet);
+
+	min_headroom = LL_RESERVED_SPACE(dst->dev) + dst->header_len
+			+ GENEVE_BASE_HLEN + opt_len + sizeof(struct ipv6hdr);
+	err = skb_cow_head(skb, min_headroom);
+	if (unlikely(err)) {
+		kfree_skb(skb);
+		goto free_dst;
+	}
+
+	skb = udp_tunnel_handle_offloads(skb, csum);
+	if (IS_ERR(skb)) {
+		err = PTR_ERR(skb);
+		goto free_dst;
+	}
+
+	gnvh = (struct genevehdr *)__skb_push(skb, sizeof(*gnvh) + opt_len);
+	geneve_build_header(gnvh, tun_flags, vni, opt_len, opt);
+
+	skb_set_inner_protocol(skb, htons(ETH_P_TEB));
+	return 0;
+
+free_dst:
+	dst_release(dst);
+	return err;
+}
+#endif
+
 static struct rtable *geneve_get_rt(struct sk_buff *skb,
 				    struct net_device *dev,
 				    struct flowi4 *fl4,
@@ -588,7 +732,7 @@ static struct rtable *geneve_get_rt(struct sk_buff *skb,
 		}
 
 		fl4->flowi4_tos = RT_TOS(tos);
-		fl4->daddr = geneve->remote.sin_addr.s_addr;
+		fl4->daddr = geneve->remote.sin.sin_addr.s_addr;
 	}
 
 	rt = ip_route_output_key(geneve->net, fl4);
@@ -606,6 +750,42 @@ static struct rtable *geneve_get_rt(struct sk_buff *skb,
 	return rt;
 }
 
+#if IS_ENABLED(CONFIG_IPV6)
+static struct dst_entry *geneve_get_dst(struct sk_buff *skb,
+					struct net_device *dev,
+					struct flowi6 *fl6,
+					struct ip_tunnel_info *info)
+{
+	struct geneve_dev *geneve = netdev_priv(dev);
+	struct geneve_sock *gs6 = geneve->sock6;
+	struct dst_entry *dst = NULL;
+
+	memset(fl6, 0, sizeof(*fl6));
+	fl6->flowi6_mark = skb->mark;
+	fl6->flowi6_proto = IPPROTO_UDP;
+
+	if (info) {
+		fl6->daddr = info->key.u.ipv6.dst;
+		fl6->saddr = info->key.u.ipv6.src;
+	} else {
+		fl6->daddr = geneve->remote.sin6.sin6_addr;
+	}
+
+	if (ipv6_stub->ipv6_dst_lookup(geneve->net, gs6->sock->sk, &dst, fl6)) {
+		netdev_dbg(dev, "no route to %pI6\n", &fl6->daddr);
+		dev->stats.tx_carrier_errors++;
+		return ERR_PTR(-EHOSTUNREACH);
+	}
+	if (dst->dev == dev) { /* is this necessary? */
+		netdev_dbg(dev, "circular route to %pI6\n", &fl6->daddr);
+		dev->stats.collisions++;
+		dst_release(dst);
+		return ERR_PTR(-EINVAL);
+	}
+	return dst;
+}
+#endif
+
 /* Convert 64 bit tunnel ID to 24 bit VNI. */
 static void tunnel_id_to_vni(__be64 tun_id, __u8 *vni)
 {
@@ -620,11 +800,11 @@ static void tunnel_id_to_vni(__be64 tun_id, __u8 *vni)
 #endif
 }
 
-static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev,
+				   struct ip_tunnel_info *info)
 {
 	struct geneve_dev *geneve = netdev_priv(dev);
-	struct geneve_sock *gs = geneve->sock;
-	struct ip_tunnel_info *info = NULL;
+	struct geneve_sock *gs4 = geneve->sock4;
 	struct rtable *rt = NULL;
 	const struct iphdr *iip; /* interior IP header */
 	struct flowi4 fl4;
@@ -635,7 +815,6 @@ static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
 	int err;
 
 	if (geneve->collect_md) {
-		info = skb_tunnel_info(skb);
 		if (unlikely(info && !(info->mode & IP_TUNNEL_INFO_TX))) {
 			netdev_dbg(dev, "no tunnel metadata\n");
 			goto tx_error;
@@ -688,7 +867,7 @@ static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
 		ttl = ttl ? : ip4_dst_hoplimit(&rt->dst);
 		df = 0;
 	}
-	err = udp_tunnel_xmit_skb(rt, gs->sock->sk, skb, fl4.saddr, fl4.daddr,
+	err = udp_tunnel_xmit_skb(rt, gs4->sock->sk, skb, fl4.saddr, fl4.daddr,
 				  tos, ttl, df, sport, geneve->dst_port,
 				  !net_eq(geneve->net, dev_net(geneve->dev)),
 				  !udp_csum);
@@ -703,6 +882,97 @@ err:
 	return NETDEV_TX_OK;
 }
 
+#if IS_ENABLED(CONFIG_IPV6)
+static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
+				    struct ip_tunnel_info *info)
+{
+	struct geneve_dev *geneve = netdev_priv(dev);
+	struct geneve_sock *gs6 = geneve->sock6;
+	struct dst_entry *dst = NULL;
+	struct flowi6 fl6;
+	__u8 ttl;
+	__be16 sport;
+	bool udp_csum;
+	int err;
+	bool xnet = !net_eq(geneve->net, dev_net(geneve->dev));
+
+	if (geneve->collect_md) {
+		if (unlikely(info && info->mode != IP_TUNNEL_INFO_TX)) {
+			netdev_dbg(dev, "no tunnel metadata\n");
+			goto tx_error;
+		}
+	}
+
+	dst = geneve_get_dst(skb, dev, &fl6, info);
+	if (IS_ERR(dst)) {
+		netdev_dbg(dev, "no route to %pI6\n", &fl6.daddr);
+		dev->stats.tx_carrier_errors++;
+		goto tx_error;
+	}
+
+	sport = udp_flow_src_port(geneve->net, skb, 1, USHRT_MAX, true);
+	skb_reset_mac_header(skb);
+
+	if (info) {
+		const struct ip_tunnel_key *key = &info->key;
+		u8 *opts = NULL;
+		u8 vni[3];
+
+		tunnel_id_to_vni(key->tun_id, vni);
+		if (key->tun_flags & TUNNEL_GENEVE_OPT)
+			opts = ip_tunnel_info_opts(info);
+
+		udp_csum = !!(key->tun_flags & TUNNEL_CSUM);
+		err = geneve6_build_skb(dst, skb, key->tun_flags, vni,
+					info->options_len, opts,
+					udp_csum, xnet);
+		if (unlikely(err))
+			goto err;
+
+		ttl = key->ttl;
+	} else {
+		udp_csum = false;
+		err = geneve6_build_skb(dst, skb, 0, geneve->vni,
+					0, NULL, udp_csum, xnet);
+		if (unlikely(err))
+			goto err;
+
+		ttl = geneve->ttl;
+		if (!ttl && ipv6_addr_is_multicast(&fl6.daddr))
+			ttl = 1;
+		ttl = ttl ? : ip6_dst_hoplimit(dst);
+	}
+	err = udp_tunnel6_xmit_skb(dst, gs6->sock->sk, skb, dev,
+				   &fl6.saddr, &fl6.daddr, 0, ttl,
+				   sport, geneve->dst_port, !udp_csum);
+
+	iptunnel_xmit_stats(err, &dev->stats, dev->tstats);
+	return NETDEV_TX_OK;
+
+tx_error:
+	dev_kfree_skb(skb);
+err:
+	dev->stats.tx_errors++;
+	return NETDEV_TX_OK;
+}
+#endif
+
+static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct geneve_dev *geneve = netdev_priv(dev);
+	struct ip_tunnel_info *info = NULL;
+
+	if (geneve->collect_md)
+		info = skb_tunnel_info(skb);
+
+#if IS_ENABLED(CONFIG_IPV6)
+	if ((info && ip_tunnel_info_af(info) == AF_INET6) ||
+	    (!info && geneve->remote.sa.sa_family == AF_INET6))
+		return geneve6_xmit_skb(skb, dev, info);
+#endif
+	return geneve_xmit_skb(skb, dev, info);
+}
+
 static const struct net_device_ops geneve_netdev_ops = {
 	.ndo_init		= geneve_init,
 	.ndo_uninit		= geneve_uninit,
@@ -759,6 +1029,7 @@ static void geneve_setup(struct net_device *dev)
 static const struct nla_policy geneve_policy[IFLA_GENEVE_MAX + 1] = {
 	[IFLA_GENEVE_ID]		= { .type = NLA_U32 },
 	[IFLA_GENEVE_REMOTE]		= { .len = FIELD_SIZEOF(struct iphdr, daddr) },
+	[IFLA_GENEVE_REMOTE6]		= { .len = sizeof(struct in6_addr) },
 	[IFLA_GENEVE_TTL]		= { .type = NLA_U8 },
 	[IFLA_GENEVE_TOS]		= { .type = NLA_U8 },
 	[IFLA_GENEVE_PORT]		= { .type = NLA_U16 },
@@ -790,7 +1061,7 @@ static int geneve_validate(struct nlattr *tb[], struct nlattr *data[])
 
 static struct geneve_dev *geneve_find_dev(struct geneve_net *gn,
 					  __be16 dst_port,
-					  __be32 rem_addr,
+					  union geneve_addr *remote,
 					  u8 vni[],
 					  bool *tun_on_same_port,
 					  bool *tun_collect_md)
@@ -806,7 +1077,7 @@ static struct geneve_dev *geneve_find_dev(struct geneve_net *gn,
 			*tun_on_same_port = true;
 		}
 		if (!memcmp(vni, geneve->vni, sizeof(geneve->vni)) &&
-		    rem_addr == geneve->remote.sin_addr.s_addr &&
+		    !memcmp(remote, &geneve->remote, sizeof(geneve->remote)) &&
 		    dst_port == geneve->dst_port)
 			t = geneve;
 	}
@@ -814,8 +1085,9 @@ static struct geneve_dev *geneve_find_dev(struct geneve_net *gn,
 }
 
 static int geneve_configure(struct net *net, struct net_device *dev,
-			    __be32 rem_addr, __u32 vni, __u8 ttl, __u8 tos,
-			    __be16 dst_port, bool metadata)
+			    union geneve_addr *remote,
+			    __u32 vni, __u8 ttl, __u8 tos, __be16 dst_port,
+			    bool metadata)
 {
 	struct geneve_net *gn = net_generic(net, geneve_net_id);
 	struct geneve_dev *t, *geneve = netdev_priv(dev);
@@ -823,9 +1095,11 @@ static int geneve_configure(struct net *net, struct net_device *dev,
 	int err;
 
 	if (metadata) {
-		if (rem_addr || vni || tos || ttl)
+		if (remote != &geneve_remote_unspec || vni || tos || ttl)
 			return -EINVAL;
 	}
+	if (!remote)
+		return -EINVAL;
 
 	geneve->net = net;
 	geneve->dev = dev;
@@ -834,16 +1108,22 @@ static int geneve_configure(struct net *net, struct net_device *dev,
 	geneve->vni[1] = (vni & 0x0000ff00) >> 8;
 	geneve->vni[2] =  vni & 0x000000ff;
 
-	geneve->remote.sin_addr.s_addr = rem_addr;
-	if (IN_MULTICAST(ntohl(geneve->remote.sin_addr.s_addr)))
+	if ((remote->sa.sa_family == AF_INET &&
+	     IN_MULTICAST(ntohl(remote->sin.sin_addr.s_addr))) ||
+	    (remote->sa.sa_family == AF_INET6 &&
+	     ipv6_addr_is_multicast(&remote->sin6.sin6_addr)))
 		return -EINVAL;
+	geneve->remote = *remote;
+
+	if (geneve->remote.sa.sa_family == AF_INET6)
+		geneve->flags |= GENEVE_F_IPV6;
 
 	geneve->ttl = ttl;
 	geneve->tos = tos;
 	geneve->dst_port = dst_port;
 	geneve->collect_md = metadata;
 
-	t = geneve_find_dev(gn, dst_port, rem_addr, geneve->vni,
+	t = geneve_find_dev(gn, dst_port, remote, geneve->vni,
 			    &tun_on_same_port, &tun_collect_md);
 	if (t)
 		return -EBUSY;
@@ -870,14 +1150,29 @@ static int geneve_newlink(struct net *net, struct net_device *dev,
 	__be16 dst_port = htons(GENEVE_UDP_PORT);
 	__u8 ttl = 0, tos = 0;
 	bool metadata = false;
-	__be32 rem_addr;
+	union geneve_addr remote;
 	__u32 vni;
 
-	if (!data[IFLA_GENEVE_ID] || !data[IFLA_GENEVE_REMOTE])
+	if (!data[IFLA_GENEVE_ID] ||
+	    (data[IFLA_GENEVE_REMOTE] && data[IFLA_GENEVE_REMOTE6]) ||
+	    (!data[IFLA_GENEVE_REMOTE] && !data[IFLA_GENEVE_REMOTE6]))
 		return -EINVAL;
 
 	vni = nla_get_u32(data[IFLA_GENEVE_ID]);
-	rem_addr = nla_get_in_addr(data[IFLA_GENEVE_REMOTE]);
+
+	memset(&remote, 0, sizeof(remote));
+	if (data[IFLA_GENEVE_REMOTE]) {
+		remote.sa.sa_family = AF_INET;
+		remote.sin.sin_addr.s_addr =
+			nla_get_in_addr(data[IFLA_GENEVE_REMOTE]);
+	} else if (data[IFLA_GENEVE_REMOTE6]) {
+		if (!IS_ENABLED(CONFIG_IPV6))
+			return -EPFNOSUPPORT;
+
+		remote.sa.sa_family = AF_INET6;
+		remote.sin6.sin6_addr =
+			nla_get_in6_addr(data[IFLA_GENEVE_REMOTE6]);
+	}
 
 	if (data[IFLA_GENEVE_TTL])
 		ttl = nla_get_u8(data[IFLA_GENEVE_TTL]);
@@ -891,8 +1186,8 @@ static int geneve_newlink(struct net *net, struct net_device *dev,
 	if (data[IFLA_GENEVE_COLLECT_METADATA])
 		metadata = true;
 
-	return geneve_configure(net, dev, rem_addr, vni,
-				ttl, tos, dst_port, metadata);
+	return geneve_configure(net, dev, &remote, vni, ttl, tos, dst_port,
+				metadata);
 }
 
 static void geneve_dellink(struct net_device *dev, struct list_head *head)
@@ -906,7 +1201,7 @@ static void geneve_dellink(struct net_device *dev, struct list_head *head)
 static size_t geneve_get_size(const struct net_device *dev)
 {
 	return nla_total_size(sizeof(__u32)) +	/* IFLA_GENEVE_ID */
-		nla_total_size(sizeof(struct in_addr)) + /* IFLA_GENEVE_REMOTE */
+		nla_total_size(sizeof(struct in6_addr)) + /* IFLA_GENEVE_REMOTE{6} */
 		nla_total_size(sizeof(__u8)) +  /* IFLA_GENEVE_TTL */
 		nla_total_size(sizeof(__u8)) +  /* IFLA_GENEVE_TOS */
 		nla_total_size(sizeof(__be16)) +  /* IFLA_GENEVE_PORT */
@@ -923,9 +1218,17 @@ static int geneve_fill_info(struct sk_buff *skb, const struct net_device *dev)
 	if (nla_put_u32(skb, IFLA_GENEVE_ID, vni))
 		goto nla_put_failure;
 
-	if (nla_put_in_addr(skb, IFLA_GENEVE_REMOTE,
-			    geneve->remote.sin_addr.s_addr))
-		goto nla_put_failure;
+	if (geneve->remote.sa.sa_family == AF_INET) {
+		if (nla_put_in_addr(skb, IFLA_GENEVE_REMOTE,
+				    geneve->remote.sin.sin_addr.s_addr))
+			goto nla_put_failure;
+#if IS_ENABLED(CONFIG_IPV6)
+	} else {
+		if (nla_put_in6_addr(skb, IFLA_GENEVE_REMOTE6,
+				     &geneve->remote.sin6.sin6_addr))
+			goto nla_put_failure;
+#endif
+	}
 
 	if (nla_put_u8(skb, IFLA_GENEVE_TTL, geneve->ttl) ||
 	    nla_put_u8(skb, IFLA_GENEVE_TOS, geneve->tos))
@@ -971,7 +1274,8 @@ struct net_device *geneve_dev_create_fb(struct net *net, const char *name,
 	if (IS_ERR(dev))
 		return dev;
 
-	err = geneve_configure(net, dev, 0, 0, 0, 0, htons(dst_port), true);
+	err = geneve_configure(net, dev, &geneve_remote_unspec,
+			       0, 0, 0, htons(dst_port), true);
 	if (err) {
 		free_netdev(dev);
 		return ERR_PTR(err);
diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
index 3a5f263cfc2f..b0cd0498ce5a 100644
--- a/include/uapi/linux/if_link.h
+++ b/include/uapi/linux/if_link.h
@@ -412,6 +412,7 @@ enum {
 	IFLA_GENEVE_TOS,
 	IFLA_GENEVE_PORT,	/* destination port */
 	IFLA_GENEVE_COLLECT_METADATA,
+	IFLA_GENEVE_REMOTE6,
 	__IFLA_GENEVE_MAX
 };
 #define IFLA_GENEVE_MAX	(__IFLA_GENEVE_MAX - 1)
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* Re: [RFT v3] geneve: implement support for IPv6-based tunnels
  2015-09-30 18:34   ` [RFT v3] " John W. Linville
@ 2015-10-01  1:55     ` kbuild test robot
  2015-10-01 15:38     ` Jiri Benc
                       ` (2 subsequent siblings)
  3 siblings, 0 replies; 37+ messages in thread
From: kbuild test robot @ 2015-10-01  1:55 UTC (permalink / raw)
  To: John W. Linville
  Cc: kbuild-all, netdev, Pravin B Shelar, Jesse Gross, Jiri Benc,
	davem, John W. Linville

[-- Attachment #1: Type: text/plain, Size: 1875 bytes --]

Hi John,

[auto build test results on v4.3-rc3 -- if it's inappropriate base, please ignore]

config: x86_64-randconfig-s2-10010927 (attached as .config)
reproduce:
        git checkout c1211d3ada9fc6f89f657c1d9c870de3baff506e
        # save the attached .config to linux build tree
        make ARCH=x86_64 

All error/warnings (new ones prefixed by >>):

   drivers/net/geneve.c: In function 'geneve_rx':
>> drivers/net/geneve.c:185:3: error: 'ip6h' undeclared (first use in this function)
      ip6h = ipv6_hdr(skb); /* outer IPv6 header... */
      ^
   drivers/net/geneve.c:185:3: note: each undeclared identifier is reported only once for each function it appears in
>> drivers/net/geneve.c:189:4: error: 'addr6' undeclared (first use in this function)
       addr6 = zero_addr6;
       ^
>> drivers/net/geneve.c:189:12: error: 'zero_addr6' undeclared (first use in this function)
       addr6 = zero_addr6;
               ^
>> drivers/net/geneve.c:196:12: error: implicit declaration of function 'geneve6_lookup' [-Werror=implicit-function-declaration]
      geneve = geneve6_lookup(gs, addr6, vni);
               ^
   cc1: some warnings being treated as errors

vim +/ip6h +185 drivers/net/geneve.c

   179	
   180				addr = iph->saddr;
   181			}
   182	
   183			geneve = geneve_lookup(gs, addr, vni);
   184		} else if (sa_family == AF_INET6) {
 > 185			ip6h = ipv6_hdr(skb); /* outer IPv6 header... */
   186	
   187			if (gs->collect_md) {
   188				vni = zero_vni;
 > 189				addr6 = zero_addr6;
   190			} else {
   191				vni = gnvh->vni;
   192	
   193				addr6 = ip6h->saddr;
   194			}
   195	
 > 196			geneve = geneve6_lookup(gs, addr6, vni);
   197		}
   198		if (!geneve)
   199			goto drop;

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/octet-stream, Size: 26170 bytes --]

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [RFT v3] geneve: implement support for IPv6-based tunnels
  2015-09-30 18:34   ` [RFT v3] " John W. Linville
  2015-10-01  1:55     ` kbuild test robot
@ 2015-10-01 15:38     ` Jiri Benc
  2015-10-01 16:26     ` Jesse Gross
  2015-10-20 15:11     ` [PATCH v4 1/2] " John W. Linville
  3 siblings, 0 replies; 37+ messages in thread
From: Jiri Benc @ 2015-10-01 15:38 UTC (permalink / raw)
  To: John W. Linville; +Cc: netdev, Pravin B Shelar, Jesse Gross, davem

On Wed, 30 Sep 2015 14:34:05 -0400, John W. Linville wrote:
> +#if IS_ENABLED(CONFIG_IPV6)
> +static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
> +				    struct ip_tunnel_info *info)
> +{
> +	struct geneve_dev *geneve = netdev_priv(dev);
> +	struct geneve_sock *gs6 = geneve->sock6;
> +	struct dst_entry *dst = NULL;
> +	struct flowi6 fl6;
> +	__u8 ttl;
> +	__be16 sport;
> +	bool udp_csum;
> +	int err;
> +	bool xnet = !net_eq(geneve->net, dev_net(geneve->dev));
> +
> +	if (geneve->collect_md) {
> +		if (unlikely(info && info->mode != IP_TUNNEL_INFO_TX)) {

Should be !(info->mode & IP_TUNNEL_INFO_TX).

The rest looks good to me.

 Jiri

-- 
Jiri Benc

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [RFT v3] geneve: implement support for IPv6-based tunnels
  2015-09-30 18:34   ` [RFT v3] " John W. Linville
  2015-10-01  1:55     ` kbuild test robot
  2015-10-01 15:38     ` Jiri Benc
@ 2015-10-01 16:26     ` Jesse Gross
  2015-10-01 20:03       ` John W. Linville
  2015-10-20 15:11     ` [PATCH v4 1/2] " John W. Linville
  3 siblings, 1 reply; 37+ messages in thread
From: Jesse Gross @ 2015-10-01 16:26 UTC (permalink / raw)
  To: John W. Linville; +Cc: netdev, Pravin B Shelar, Jiri Benc, David Miller

On Wed, Sep 30, 2015 at 11:34 AM, John W. Linville
<linville@tuxdriver.com> wrote:
> diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c
> index 8f5c02eed47d..291d3d7754a8 100644
> --- a/drivers/net/geneve.c
> +++ b/drivers/net/geneve.c
> +#define GENEVE_F_IPV6          0x00000001

I wasn't sure why we needed this flag. Can't we just look at the
remote address family?

> -static void geneve_sock_release(struct geneve_sock *gs)
> +static void __geneve_sock_release(struct geneve_sock *gs)
>  {
>         if (--gs->refcnt)
>                 return;

Do we need a check for NULL first here?

> +#if IS_ENABLED(CONFIG_IPV6)
> +static int geneve6_build_skb(struct dst_entry *dst, struct sk_buff *skb,
> +                            __be16 tun_flags, u8 vni[3], u8 opt_len, u8 *opt,
> +                            bool csum, bool xnet)
> +{
> +       struct genevehdr *gnvh;
> +       int min_headroom;
> +       int err;
> +
> +       skb_scrub_packet(skb, xnet);

Is there a reason why this applies to only IPv6? It seems like it
would be common

> +static struct dst_entry *geneve_get_dst(struct sk_buff *skb,

It might be worth clarifying this name - it wasn't immediately obvious
to me the difference between geneve_get_rt() and geneve_get_dst() is
IPv4 vs. IPv6.

> +#if IS_ENABLED(CONFIG_IPV6)
> +static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
> +                                   struct ip_tunnel_info *info)
[...]
> +       dst = geneve_get_dst(skb, dev, &fl6, info);
> +       if (IS_ERR(dst)) {
> +               netdev_dbg(dev, "no route to %pI6\n", &fl6.daddr);
> +               dev->stats.tx_carrier_errors++;
> +               goto tx_error;
> +       }

It looks like we double log/count this error (although this also
appears to be a problem for IPv4).

> +       err = udp_tunnel6_xmit_skb(dst, gs6->sock->sk, skb, dev,
> +                                  &fl6.saddr, &fl6.daddr, 0, ttl,
> +                                  sport, geneve->dst_port, !udp_csum);

It seems like TOS is not handled here?

> @@ -823,9 +1095,11 @@ static int geneve_configure(struct net *net, struct net_device *dev,
>         int err;
>
>         if (metadata) {
> -               if (rem_addr || vni || tos || ttl)
> +               if (remote != &geneve_remote_unspec || vni || tos || ttl)
>                         return -EINVAL;

I think this will fail in the non-compat metadata case. The remote
that is passed in will be a zeroed copy on the stack, so the address
won't match the static version. I believe the check should be for
AF_UNSPEC instead.

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [RFT v3] geneve: implement support for IPv6-based tunnels
  2015-10-01 16:26     ` Jesse Gross
@ 2015-10-01 20:03       ` John W. Linville
  2015-10-01 21:07         ` Jesse Gross
  0 siblings, 1 reply; 37+ messages in thread
From: John W. Linville @ 2015-10-01 20:03 UTC (permalink / raw)
  To: Jesse Gross; +Cc: netdev, Pravin B Shelar, Jiri Benc, David Miller

On Thu, Oct 01, 2015 at 09:26:59AM -0700, Jesse Gross wrote:
> On Wed, Sep 30, 2015 at 11:34 AM, John W. Linville
> <linville@tuxdriver.com> wrote:
> > diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c
> > index 8f5c02eed47d..291d3d7754a8 100644
> > --- a/drivers/net/geneve.c
> > +++ b/drivers/net/geneve.c
> > +#define GENEVE_F_IPV6          0x00000001
> 
> I wasn't sure why we needed this flag. Can't we just look at the
> remote address family?

Yeah, I had grander plans... :-)  I think it can be removed.

> > -static void geneve_sock_release(struct geneve_sock *gs)
> > +static void __geneve_sock_release(struct geneve_sock *gs)
> >  {
> >         if (--gs->refcnt)
> >                 return;
> 
> Do we need a check for NULL first here?

Sure.

> > +#if IS_ENABLED(CONFIG_IPV6)
> > +static int geneve6_build_skb(struct dst_entry *dst, struct sk_buff *skb,
> > +                            __be16 tun_flags, u8 vni[3], u8 opt_len, u8 *opt,
> > +                            bool csum, bool xnet)
> > +{
> > +       struct genevehdr *gnvh;
> > +       int min_headroom;
> > +       int err;
> > +
> > +       skb_scrub_packet(skb, xnet);
> 
> Is there a reason why this applies to only IPv6? It seems like it
> would be common

The dst vs rt thing was the motivator.  It probably could be refactored
to share some code between geneve_build_skb and geneve6_build_skb.

> > +static struct dst_entry *geneve_get_dst(struct sk_buff *skb,
> 
> It might be worth clarifying this name - it wasn't immediately obvious
> to me the difference between geneve_get_rt() and geneve_get_dst() is
> IPv4 vs. IPv6.

geneve_get_v4_rt and geneve_get_v6_dst?

> > +#if IS_ENABLED(CONFIG_IPV6)
> > +static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
> > +                                   struct ip_tunnel_info *info)
> [...]
> > +       dst = geneve_get_dst(skb, dev, &fl6, info);
> > +       if (IS_ERR(dst)) {
> > +               netdev_dbg(dev, "no route to %pI6\n", &fl6.daddr);
> > +               dev->stats.tx_carrier_errors++;
> > +               goto tx_error;
> > +       }
> 
> It looks like we double log/count this error (although this also
> appears to be a problem for IPv4).

Indeed.  I'll try to fix/refactor that a bit...

> > +       err = udp_tunnel6_xmit_skb(dst, gs6->sock->sk, skb, dev,
> > +                                  &fl6.saddr, &fl6.daddr, 0, ttl,
> > +                                  sport, geneve->dst_port, !udp_csum);
> 
> It seems like TOS is not handled here?

There is no tos parameter for udp_tunnel6_xmit_skb.  Is there some
other way to inject it?  Is there a mapping to priority (i.e. the
0 parameter)?

> > @@ -823,9 +1095,11 @@ static int geneve_configure(struct net *net, struct net_device *dev,
> >         int err;
> >
> >         if (metadata) {
> > -               if (rem_addr || vni || tos || ttl)
> > +               if (remote != &geneve_remote_unspec || vni || tos || ttl)
> >                         return -EINVAL;
> 
> I think this will fail in the non-compat metadata case. The remote
> that is passed in will be a zeroed copy on the stack, so the address
> won't match the static version. I believe the check should be for
> AF_UNSPEC instead.

It is actually checking the pointer value against the address of
that static data structure, which is only reference through the
geneve_dev_create_fb path to calling geneve_configure.  Knowing that
are you still troubled by it?

John

P.S. I may not respond/repost for a while due to some travel during
the next week...
-- 
John W. Linville		Someday the world will need a hero, and you
linville@tuxdriver.com			might be all we have.  Be ready.

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [RFT v3] geneve: implement support for IPv6-based tunnels
  2015-10-01 20:03       ` John W. Linville
@ 2015-10-01 21:07         ` Jesse Gross
  0 siblings, 0 replies; 37+ messages in thread
From: Jesse Gross @ 2015-10-01 21:07 UTC (permalink / raw)
  To: John W. Linville; +Cc: netdev, Pravin B Shelar, Jiri Benc, David Miller

On Thu, Oct 1, 2015 at 1:03 PM, John W. Linville <linville@tuxdriver.com> wrote:
> On Thu, Oct 01, 2015 at 09:26:59AM -0700, Jesse Gross wrote:
>> On Wed, Sep 30, 2015 at 11:34 AM, John W. Linville
>> <linville@tuxdriver.com> wrote:
>> > +static struct dst_entry *geneve_get_dst(struct sk_buff *skb,
>>
>> It might be worth clarifying this name - it wasn't immediately obvious
>> to me the difference between geneve_get_rt() and geneve_get_dst() is
>> IPv4 vs. IPv6.
>
> geneve_get_v4_rt and geneve_get_v6_dst?

Sure.

>> > +       err = udp_tunnel6_xmit_skb(dst, gs6->sock->sk, skb, dev,
>> > +                                  &fl6.saddr, &fl6.daddr, 0, ttl,
>> > +                                  sport, geneve->dst_port, !udp_csum);
>>
>> It seems like TOS is not handled here?
>
> There is no tos parameter for udp_tunnel6_xmit_skb.  Is there some
> other way to inject it?  Is there a mapping to priority (i.e. the
> 0 parameter)?

I think the TOS field and priority are essentially the same thing - no
mapping required, just a different name in IPv6. And then once we do
that, I guess we should bring over the ECN logic from IPv4.

>> > @@ -823,9 +1095,11 @@ static int geneve_configure(struct net *net, struct net_device *dev,
>> >         int err;
>> >
>> >         if (metadata) {
>> > -               if (rem_addr || vni || tos || ttl)
>> > +               if (remote != &geneve_remote_unspec || vni || tos || ttl)
>> >                         return -EINVAL;
>>
>> I think this will fail in the non-compat metadata case. The remote
>> that is passed in will be a zeroed copy on the stack, so the address
>> won't match the static version. I believe the check should be for
>> AF_UNSPEC instead.
>
> It is actually checking the pointer value against the address of
> that static data structure, which is only reference through the
> geneve_dev_create_fb path to calling geneve_configure.  Knowing that
> are you still troubled by it?

Yeah, I understand how it is working for the geneve_dev_create_fb()
path. However, I think that we should also be able to hit this using
the path from geneve_newlink() - this is basically the
COLLECT_METADATA case.

I now see that this isn't possible because we require a remote address
in geneve_newlink (including with the existing code). However, that
seems wrong now that we have lightweight tunnels.

^ permalink raw reply	[flat|nested] 37+ messages in thread

* [PATCH v4 1/2] geneve: implement support for IPv6-based tunnels
  2015-09-30 18:34   ` [RFT v3] " John W. Linville
                       ` (2 preceding siblings ...)
  2015-10-01 16:26     ` Jesse Gross
@ 2015-10-20 15:11     ` John W. Linville
  2015-10-20 15:11       ` [PATCH 2/2] geneve: handle ipv6 priority like ipv4 tos John W. Linville
                         ` (4 more replies)
  3 siblings, 5 replies; 37+ messages in thread
From: John W. Linville @ 2015-10-20 15:11 UTC (permalink / raw)
  To: netdev
  Cc: Dave Miller, Pravin B Shelar, Jesse Gross, Jiri Benc, John W. Linville

Signed-off-by: John W. Linville <linville@tuxdriver.com>
---
v4:
- treat mode field of ip_tunnel_info as flags
- add a missing IS_ENABLED(CONFIG_IPV6) to geneve_rx
- remove unneeded flags field in geneve_dev
- NULL-check parameter for __geneve_sock_release
- check remote socket family for AF_UNSPEC in geneve_configure
- rename geneve_get_{rt,dst} as geneve_get_{v4_rt,v6_dst}
- refactor some error handling in the xmit paths

v3:
- declare geneve_remote_unspec as static

v2:
- do not require remote address for tx on metadata tunnels
- pass correct sockaddr family to udp_tun_rx_dst in geneve_rx
- accommodate both ipv4 and ipv6 sockets open on same tunnel
- move declaration of geneve_get_dst for aesthetic purposes

 drivers/net/geneve.c         | 459 +++++++++++++++++++++++++++++++++++--------
 include/uapi/linux/if_link.h |   1 +
 2 files changed, 377 insertions(+), 83 deletions(-)

diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c
index 8f5c02eed47d..217b472ab9e7 100644
--- a/drivers/net/geneve.c
+++ b/drivers/net/geneve.c
@@ -46,16 +46,25 @@ struct geneve_net {
 
 static int geneve_net_id;
 
+union geneve_addr {
+	struct sockaddr_in sin;
+	struct sockaddr_in6 sin6;
+	struct sockaddr sa;
+};
+
+static union geneve_addr geneve_remote_unspec = { .sa.sa_family = AF_UNSPEC, };
+
 /* Pseudo network device */
 struct geneve_dev {
 	struct hlist_node  hlist;	/* vni hash table */
 	struct net	   *net;	/* netns for packet i/o */
 	struct net_device  *dev;	/* netdev for geneve tunnel */
-	struct geneve_sock *sock;	/* socket used for geneve tunnel */
+	struct geneve_sock *sock4;	/* IPv4 socket used for geneve tunnel */
+	struct geneve_sock *sock6;	/* IPv6 socket used for geneve tunnel */
 	u8                 vni[3];	/* virtual network ID for tunnel */
 	u8                 ttl;		/* TTL override */
 	u8                 tos;		/* TOS override */
-	struct sockaddr_in remote;	/* IPv4 address for link partner */
+	union geneve_addr  remote;	/* IP address for link partner */
 	struct list_head   next;	/* geneve's per namespace list */
 	__be16		   dst_port;
 	bool		   collect_md;
@@ -103,11 +112,32 @@ static struct geneve_dev *geneve_lookup(struct geneve_sock *gs,
 	vni_list_head = &gs->vni_list[hash];
 	hlist_for_each_entry_rcu(geneve, vni_list_head, hlist) {
 		if (!memcmp(vni, geneve->vni, sizeof(geneve->vni)) &&
-		    addr == geneve->remote.sin_addr.s_addr)
+		    addr == geneve->remote.sin.sin_addr.s_addr)
+			return geneve;
+	}
+	return NULL;
+}
+
+#if IS_ENABLED(CONFIG_IPV6)
+static struct geneve_dev *geneve6_lookup(struct geneve_sock *gs,
+					 struct in6_addr addr6, u8 vni[])
+{
+	struct hlist_head *vni_list_head;
+	struct geneve_dev *geneve;
+	__u32 hash;
+
+	/* Find the device for this VNI */
+	hash = geneve_net_vni_hash(vni);
+	vni_list_head = &gs->vni_list[hash];
+	hlist_for_each_entry_rcu(geneve, vni_list_head, hlist) {
+		if (!memcmp(vni, geneve->vni, sizeof(geneve->vni)) &&
+		    !memcmp(&addr6, &geneve->remote.sin6.sin6_addr,
+			    sizeof(addr6)))
 			return geneve;
 	}
 	return NULL;
 }
+#endif
 
 static inline struct genevehdr *geneve_hdr(const struct sk_buff *skb)
 {
@@ -121,24 +151,49 @@ static void geneve_rx(struct geneve_sock *gs, struct sk_buff *skb)
 	struct metadata_dst *tun_dst = NULL;
 	struct geneve_dev *geneve = NULL;
 	struct pcpu_sw_netstats *stats;
-	struct iphdr *iph;
-	u8 *vni;
+	struct iphdr *iph = NULL;
 	__be32 addr;
-	int err;
+	static u8 zero_vni[3];
+	u8 *vni;
+	int err = 0;
+	sa_family_t sa_family;
+#if IS_ENABLED(CONFIG_IPV6)
+	struct ipv6hdr *ip6h = NULL;
+	struct in6_addr addr6;
+	static struct in6_addr zero_addr6;
+#endif
 
-	iph = ip_hdr(skb); /* outer IP header... */
+	sa_family = gs->sock->sk->sk_family;
 
-	if (gs->collect_md) {
-		static u8 zero_vni[3];
+	if (sa_family == AF_INET) {
+		iph = ip_hdr(skb); /* outer IP header... */
 
-		vni = zero_vni;
-		addr = 0;
-	} else {
-		vni = gnvh->vni;
-		addr = iph->saddr;
-	}
+		if (gs->collect_md) {
+			vni = zero_vni;
+			addr = 0;
+		} else {
+			vni = gnvh->vni;
+
+			addr = iph->saddr;
+		}
+
+		geneve = geneve_lookup(gs, addr, vni);
+#if IS_ENABLED(CONFIG_IPV6)
+	} else if (sa_family == AF_INET6) {
+		ip6h = ipv6_hdr(skb); /* outer IPv6 header... */
+
+		if (gs->collect_md) {
+			vni = zero_vni;
+			addr6 = zero_addr6;
+		} else {
+			vni = gnvh->vni;
 
-	geneve = geneve_lookup(gs, addr, vni);
+			addr6 = ip6h->saddr;
+		}
+
+		geneve = geneve6_lookup(gs, addr6, vni);
+#endif
+	}
 	if (!geneve)
 		goto drop;
 
@@ -149,7 +204,7 @@ static void geneve_rx(struct geneve_sock *gs, struct sk_buff *skb)
 			(gnvh->oam ? TUNNEL_OAM : 0) |
 			(gnvh->critical ? TUNNEL_CRIT_OPT : 0);
 
-		tun_dst = udp_tun_rx_dst(skb, AF_INET, flags,
+		tun_dst = udp_tun_rx_dst(skb, sa_family, flags,
 					 vni_to_tunnel_id(gnvh->vni),
 					 gnvh->opt_len * 4);
 		if (!tun_dst)
@@ -179,12 +234,21 @@ static void geneve_rx(struct geneve_sock *gs, struct sk_buff *skb)
 
 	skb_reset_network_header(skb);
 
-	err = IP_ECN_decapsulate(iph, skb);
+	if (iph)
+		err = IP_ECN_decapsulate(iph, skb);
+	if (ip6h)
+		err = IP6_ECN_decapsulate(ip6h, skb);
 
 	if (unlikely(err)) {
-		if (log_ecn_error)
-			net_info_ratelimited("non-ECT from %pI4 with TOS=%#x\n",
-					     &iph->saddr, iph->tos);
+		if (log_ecn_error) {
+			if (iph)
+				net_info_ratelimited("non-ECT from %pI4 "
+						     "with TOS=%#x\n",
+						     &iph->saddr, iph->tos);
+			if (ip6h)
+				net_info_ratelimited("non-ECT from %pI6\n",
+						     &ip6h->saddr);
+		}
 		if (err > 1) {
 			++geneve->dev->stats.rx_frame_errors;
 			++geneve->dev->stats.rx_errors;
@@ -284,6 +348,7 @@ static struct socket *geneve_create_sock(struct net *net, bool ipv6,
 
 	if (ipv6) {
 		udp_conf.family = AF_INET6;
+		udp_conf.ipv6_v6only = 1;
 	} else {
 		udp_conf.family = AF_INET;
 		udp_conf.local_ip.s_addr = htonl(INADDR_ANY);
@@ -458,9 +523,9 @@ static void geneve_notify_del_rx_port(struct geneve_sock *gs)
 		udp_del_offload(&gs->udp_offloads);
 }
 
-static void geneve_sock_release(struct geneve_sock *gs)
+static void __geneve_sock_release(struct geneve_sock *gs)
 {
-	if (--gs->refcnt)
+	if (!gs || --gs->refcnt)
 		return;
 
 	list_del(&gs->list);
@@ -469,58 +534,107 @@ static void geneve_sock_release(struct geneve_sock *gs)
 	kfree_rcu(gs, rcu);
 }
 
+static void geneve_sock_release(struct geneve_dev *geneve)
+{
+	__geneve_sock_release(geneve->sock4);
+#if IS_ENABLED(CONFIG_IPV6)
+	__geneve_sock_release(geneve->sock6);
+#endif
+}
+
 static struct geneve_sock *geneve_find_sock(struct geneve_net *gn,
+					    sa_family_t family,
 					    __be16 dst_port)
 {
 	struct geneve_sock *gs;
 
 	list_for_each_entry(gs, &gn->sock_list, list) {
 		if (inet_sk(gs->sock->sk)->inet_sport == dst_port &&
-		    inet_sk(gs->sock->sk)->sk.sk_family == AF_INET) {
+		    inet_sk(gs->sock->sk)->sk.sk_family == family) {
 			return gs;
 		}
 	}
 	return NULL;
 }
 
-static int geneve_open(struct net_device *dev)
+static int geneve_sock_add(struct geneve_dev *geneve, bool ipv6)
 {
-	struct geneve_dev *geneve = netdev_priv(dev);
 	struct net *net = geneve->net;
 	struct geneve_net *gn = net_generic(net, geneve_net_id);
 	struct geneve_sock *gs;
 	__u32 hash;
 
-	gs = geneve_find_sock(gn, geneve->dst_port);
+	gs = geneve_find_sock(gn, ipv6 ? AF_INET6 : AF_INET, geneve->dst_port);
 	if (gs) {
 		gs->refcnt++;
 		goto out;
 	}
 
-	gs = geneve_socket_create(net, geneve->dst_port, false);
+	gs = geneve_socket_create(net, geneve->dst_port, ipv6);
 	if (IS_ERR(gs))
 		return PTR_ERR(gs);
 
 out:
 	gs->collect_md = geneve->collect_md;
-	geneve->sock = gs;
+#if IS_ENABLED(CONFIG_IPV6)
+	if (ipv6)
+		geneve->sock6 = gs;
+	else
+#endif
+		geneve->sock4 = gs;
 
 	hash = geneve_net_vni_hash(geneve->vni);
 	hlist_add_head_rcu(&geneve->hlist, &gs->vni_list[hash]);
 	return 0;
 }
 
+static int geneve_open(struct net_device *dev)
+{
+	struct geneve_dev *geneve = netdev_priv(dev);
+	bool ipv6 = geneve->remote.sa.sa_family == AF_INET6;
+	bool metadata = !!geneve->collect_md;
+	int ret = 0;
+
+	geneve->sock4 = NULL;
+#if IS_ENABLED(CONFIG_IPV6)
+	geneve->sock6 = NULL;
+	if (ipv6 || metadata)
+		ret = geneve_sock_add(geneve, true);
+#endif
+	if (!ret && (!ipv6 || metadata))
+		ret = geneve_sock_add(geneve, false);
+	if (ret < 0)
+		geneve_sock_release(geneve);
+
+	return ret;
+}
+
 static int geneve_stop(struct net_device *dev)
 {
 	struct geneve_dev *geneve = netdev_priv(dev);
-	struct geneve_sock *gs = geneve->sock;
 
 	if (!hlist_unhashed(&geneve->hlist))
 		hlist_del_rcu(&geneve->hlist);
-	geneve_sock_release(gs);
+	geneve_sock_release(geneve);
 	return 0;
 }
 
+static void geneve_build_header(struct genevehdr *geneveh,
+				__be16 tun_flags, u8 vni[3],
+				u8 options_len, u8 *options)
+{
+	geneveh->ver = GENEVE_VER;
+	geneveh->opt_len = options_len / 4;
+	geneveh->oam = !!(tun_flags & TUNNEL_OAM);
+	geneveh->critical = !!(tun_flags & TUNNEL_CRIT_OPT);
+	geneveh->rsvd1 = 0;
+	memcpy(geneveh->vni, vni, 3);
+	geneveh->proto_type = htons(ETH_P_TEB);
+	geneveh->rsvd2 = 0;
+
+	memcpy(geneveh->options, options, options_len);
+}
+
 static int geneve_build_skb(struct rtable *rt, struct sk_buff *skb,
 			    __be16 tun_flags, u8 vni[3], u8 opt_len, u8 *opt,
 			    bool csum)
@@ -544,15 +658,7 @@ static int geneve_build_skb(struct rtable *rt, struct sk_buff *skb,
 	}
 
 	gnvh = (struct genevehdr *)__skb_push(skb, sizeof(*gnvh) + opt_len);
-	gnvh->ver = GENEVE_VER;
-	gnvh->opt_len = opt_len / 4;
-	gnvh->oam = !!(tun_flags & TUNNEL_OAM);
-	gnvh->critical = !!(tun_flags & TUNNEL_CRIT_OPT);
-	gnvh->rsvd1 = 0;
-	memcpy(gnvh->vni, vni, 3);
-	gnvh->proto_type = htons(ETH_P_TEB);
-	gnvh->rsvd2 = 0;
-	memcpy(gnvh->options, opt, opt_len);
+	geneve_build_header(gnvh, tun_flags, vni, opt_len, opt);
 
 	skb_set_inner_protocol(skb, htons(ETH_P_TEB));
 	return 0;
@@ -562,10 +668,47 @@ free_rt:
 	return err;
 }
 
-static struct rtable *geneve_get_rt(struct sk_buff *skb,
-				    struct net_device *dev,
-				    struct flowi4 *fl4,
-				    struct ip_tunnel_info *info)
+#if IS_ENABLED(CONFIG_IPV6)
+static int geneve6_build_skb(struct dst_entry *dst, struct sk_buff *skb,
+			     __be16 tun_flags, u8 vni[3], u8 opt_len, u8 *opt,
+			     bool csum, bool xnet)
+{
+	struct genevehdr *gnvh;
+	int min_headroom;
+	int err;
+
+	skb_scrub_packet(skb, xnet);
+
+	min_headroom = LL_RESERVED_SPACE(dst->dev) + dst->header_len
+			+ GENEVE_BASE_HLEN + opt_len + sizeof(struct ipv6hdr);
+	err = skb_cow_head(skb, min_headroom);
+	if (unlikely(err)) {
+		kfree_skb(skb);
+		goto free_dst;
+	}
+
+	skb = udp_tunnel_handle_offloads(skb, csum);
+	if (IS_ERR(skb)) {
+		err = PTR_ERR(skb);
+		goto free_dst;
+	}
+
+	gnvh = (struct genevehdr *)__skb_push(skb, sizeof(*gnvh) + opt_len);
+	geneve_build_header(gnvh, tun_flags, vni, opt_len, opt);
+
+	skb_set_inner_protocol(skb, htons(ETH_P_TEB));
+	return 0;
+
+free_dst:
+	dst_release(dst);
+	return err;
+}
+#endif
+
+static struct rtable *geneve_get_v4_rt(struct sk_buff *skb,
+				       struct net_device *dev,
+				       struct flowi4 *fl4,
+				       struct ip_tunnel_info *info)
 {
 	struct geneve_dev *geneve = netdev_priv(dev);
 	struct rtable *rt = NULL;
@@ -588,24 +731,42 @@ static struct rtable *geneve_get_rt(struct sk_buff *skb,
 		}
 
 		fl4->flowi4_tos = RT_TOS(tos);
-		fl4->daddr = geneve->remote.sin_addr.s_addr;
+		fl4->daddr = geneve->remote.sin.sin_addr.s_addr;
 	}
 
 	rt = ip_route_output_key(geneve->net, fl4);
-	if (IS_ERR(rt)) {
-		netdev_dbg(dev, "no route to %pI4\n", &fl4->daddr);
-		dev->stats.tx_carrier_errors++;
-		return rt;
-	}
-	if (rt->dst.dev == dev) { /* is this necessary? */
-		netdev_dbg(dev, "circular route to %pI4\n", &fl4->daddr);
-		dev->stats.collisions++;
-		ip_rt_put(rt);
-		return ERR_PTR(-EINVAL);
-	}
+
 	return rt;
 }
 
+#if IS_ENABLED(CONFIG_IPV6)
+static struct dst_entry *geneve_get_v6_dst(struct sk_buff *skb,
+					   struct net_device *dev,
+					   struct flowi6 *fl6,
+					   struct ip_tunnel_info *info)
+{
+	struct geneve_dev *geneve = netdev_priv(dev);
+	struct geneve_sock *gs6 = geneve->sock6;
+	struct dst_entry *dst = NULL;
+
+	memset(fl6, 0, sizeof(*fl6));
+	fl6->flowi6_mark = skb->mark;
+	fl6->flowi6_proto = IPPROTO_UDP;
+
+	if (info) {
+		fl6->daddr = info->key.u.ipv6.dst;
+		fl6->saddr = info->key.u.ipv6.src;
+	} else {
+		fl6->daddr = geneve->remote.sin6.sin6_addr;
+	}
+
+	if (ipv6_stub->ipv6_dst_lookup(geneve->net, gs6->sock->sk, &dst, fl6))
+		return ERR_PTR(-EHOSTUNREACH);
+
+	return dst;
+}
+#endif
+
 /* Convert 64 bit tunnel ID to 24 bit VNI. */
 static void tunnel_id_to_vni(__be64 tun_id, __u8 *vni)
 {
@@ -620,11 +781,11 @@ static void tunnel_id_to_vni(__be64 tun_id, __u8 *vni)
 #endif
 }
 
-static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev,
+				   struct ip_tunnel_info *info)
 {
 	struct geneve_dev *geneve = netdev_priv(dev);
-	struct geneve_sock *gs = geneve->sock;
-	struct ip_tunnel_info *info = NULL;
+	struct geneve_sock *gs4 = geneve->sock4;
 	struct rtable *rt = NULL;
 	const struct iphdr *iip; /* interior IP header */
 	struct flowi4 fl4;
@@ -635,7 +796,6 @@ static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
 	int err;
 
 	if (geneve->collect_md) {
-		info = skb_tunnel_info(skb);
 		if (unlikely(info && !(info->mode & IP_TUNNEL_INFO_TX))) {
 			netdev_dbg(dev, "no tunnel metadata\n");
 			goto tx_error;
@@ -644,12 +804,18 @@ static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
 			goto tx_error;
 	}
 
-	rt = geneve_get_rt(skb, dev, &fl4, info);
+	rt = geneve_get_v4_rt(skb, dev, &fl4, info);
 	if (IS_ERR(rt)) {
 		netdev_dbg(dev, "no route to %pI4\n", &fl4.daddr);
 		dev->stats.tx_carrier_errors++;
 		goto tx_error;
 	}
+	if (rt->dst.dev == dev) { /* is this necessary? */
+		netdev_dbg(dev, "circular route to %pI4\n", &fl4.daddr);
+		dev->stats.collisions++;
+		ip_rt_put(rt);
+		goto tx_error;
+	}
 
 	sport = udp_flow_src_port(geneve->net, skb, 1, USHRT_MAX, true);
 	skb_reset_mac_header(skb);
@@ -688,7 +854,7 @@ static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
 		ttl = ttl ? : ip4_dst_hoplimit(&rt->dst);
 		df = 0;
 	}
-	err = udp_tunnel_xmit_skb(rt, gs->sock->sk, skb, fl4.saddr, fl4.daddr,
+	err = udp_tunnel_xmit_skb(rt, gs4->sock->sk, skb, fl4.saddr, fl4.daddr,
 				  tos, ttl, df, sport, geneve->dst_port,
 				  !net_eq(geneve->net, dev_net(geneve->dev)),
 				  !udp_csum);
@@ -703,6 +869,103 @@ err:
 	return NETDEV_TX_OK;
 }
 
+#if IS_ENABLED(CONFIG_IPV6)
+static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
+				    struct ip_tunnel_info *info)
+{
+	struct geneve_dev *geneve = netdev_priv(dev);
+	struct geneve_sock *gs6 = geneve->sock6;
+	struct dst_entry *dst = NULL;
+	struct flowi6 fl6;
+	__u8 ttl;
+	__be16 sport;
+	bool udp_csum;
+	int err;
+	bool xnet = !net_eq(geneve->net, dev_net(geneve->dev));
+
+	if (geneve->collect_md) {
+		if (unlikely(info && !(info->mode & IP_TUNNEL_INFO_TX))) {
+			netdev_dbg(dev, "no tunnel metadata\n");
+			goto tx_error;
+		}
+	}
+
+	dst = geneve_get_v6_dst(skb, dev, &fl6, info);
+	if (IS_ERR(dst)) {
+		netdev_dbg(dev, "no route to %pI6\n", &fl6.daddr);
+		dev->stats.tx_carrier_errors++;
+		goto tx_error;
+	}
+	if (dst->dev == dev) { /* is this necessary? */
+		netdev_dbg(dev, "circular route to %pI6\n", &fl6.daddr);
+		dev->stats.collisions++;
+		dst_release(dst);
+		goto tx_error;
+	}
+
+	sport = udp_flow_src_port(geneve->net, skb, 1, USHRT_MAX, true);
+	skb_reset_mac_header(skb);
+
+	if (info) {
+		const struct ip_tunnel_key *key = &info->key;
+		u8 *opts = NULL;
+		u8 vni[3];
+
+		tunnel_id_to_vni(key->tun_id, vni);
+		if (key->tun_flags & TUNNEL_GENEVE_OPT)
+			opts = ip_tunnel_info_opts(info);
+
+		udp_csum = !!(key->tun_flags & TUNNEL_CSUM);
+		err = geneve6_build_skb(dst, skb, key->tun_flags, vni,
+					info->options_len, opts,
+					udp_csum, xnet);
+		if (unlikely(err))
+			goto err;
+
+		ttl = key->ttl;
+	} else {
+		udp_csum = false;
+		err = geneve6_build_skb(dst, skb, 0, geneve->vni,
+					0, NULL, udp_csum, xnet);
+		if (unlikely(err))
+			goto err;
+
+		ttl = geneve->ttl;
+		if (!ttl && ipv6_addr_is_multicast(&fl6.daddr))
+			ttl = 1;
+		ttl = ttl ? : ip6_dst_hoplimit(dst);
+	}
+	err = udp_tunnel6_xmit_skb(dst, gs6->sock->sk, skb, dev,
+				   &fl6.saddr, &fl6.daddr, 0, ttl,
+				   sport, geneve->dst_port, !udp_csum);
+
+	iptunnel_xmit_stats(err, &dev->stats, dev->tstats);
+	return NETDEV_TX_OK;
+
+tx_error:
+	dev_kfree_skb(skb);
+err:
+	dev->stats.tx_errors++;
+	return NETDEV_TX_OK;
+}
+#endif
+
+static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct geneve_dev *geneve = netdev_priv(dev);
+	struct ip_tunnel_info *info = NULL;
+
+	if (geneve->collect_md)
+		info = skb_tunnel_info(skb);
+
+#if IS_ENABLED(CONFIG_IPV6)
+	if ((info && ip_tunnel_info_af(info) == AF_INET6) ||
+	    (!info && geneve->remote.sa.sa_family == AF_INET6))
+		return geneve6_xmit_skb(skb, dev, info);
+#endif
+	return geneve_xmit_skb(skb, dev, info);
+}
+
 static const struct net_device_ops geneve_netdev_ops = {
 	.ndo_init		= geneve_init,
 	.ndo_uninit		= geneve_uninit,
@@ -759,6 +1022,7 @@ static void geneve_setup(struct net_device *dev)
 static const struct nla_policy geneve_policy[IFLA_GENEVE_MAX + 1] = {
 	[IFLA_GENEVE_ID]		= { .type = NLA_U32 },
 	[IFLA_GENEVE_REMOTE]		= { .len = FIELD_SIZEOF(struct iphdr, daddr) },
+	[IFLA_GENEVE_REMOTE6]		= { .len = sizeof(struct in6_addr) },
 	[IFLA_GENEVE_TTL]		= { .type = NLA_U8 },
 	[IFLA_GENEVE_TOS]		= { .type = NLA_U8 },
 	[IFLA_GENEVE_PORT]		= { .type = NLA_U16 },
@@ -790,7 +1054,7 @@ static int geneve_validate(struct nlattr *tb[], struct nlattr *data[])
 
 static struct geneve_dev *geneve_find_dev(struct geneve_net *gn,
 					  __be16 dst_port,
-					  __be32 rem_addr,
+					  union geneve_addr *remote,
 					  u8 vni[],
 					  bool *tun_on_same_port,
 					  bool *tun_collect_md)
@@ -806,7 +1070,7 @@ static struct geneve_dev *geneve_find_dev(struct geneve_net *gn,
 			*tun_on_same_port = true;
 		}
 		if (!memcmp(vni, geneve->vni, sizeof(geneve->vni)) &&
-		    rem_addr == geneve->remote.sin_addr.s_addr &&
+		    !memcmp(remote, &geneve->remote, sizeof(geneve->remote)) &&
 		    dst_port == geneve->dst_port)
 			t = geneve;
 	}
@@ -814,18 +1078,20 @@ static struct geneve_dev *geneve_find_dev(struct geneve_net *gn,
 }
 
 static int geneve_configure(struct net *net, struct net_device *dev,
-			    __be32 rem_addr, __u32 vni, __u8 ttl, __u8 tos,
-			    __be16 dst_port, bool metadata)
+			    union geneve_addr *remote,
+			    __u32 vni, __u8 ttl, __u8 tos, __be16 dst_port,
+			    bool metadata)
 {
 	struct geneve_net *gn = net_generic(net, geneve_net_id);
 	struct geneve_dev *t, *geneve = netdev_priv(dev);
 	bool tun_collect_md, tun_on_same_port;
 	int err;
 
-	if (metadata) {
-		if (rem_addr || vni || tos || ttl)
-			return -EINVAL;
-	}
+	if (!remote)
+		return -EINVAL;
+	if (metadata &&
+	    (remote->sa.sa_family != AF_UNSPEC || vni || tos || ttl))
+		return -EINVAL;
 
 	geneve->net = net;
 	geneve->dev = dev;
@@ -834,16 +1100,19 @@ static int geneve_configure(struct net *net, struct net_device *dev,
 	geneve->vni[1] = (vni & 0x0000ff00) >> 8;
 	geneve->vni[2] =  vni & 0x000000ff;
 
-	geneve->remote.sin_addr.s_addr = rem_addr;
-	if (IN_MULTICAST(ntohl(geneve->remote.sin_addr.s_addr)))
+	if ((remote->sa.sa_family == AF_INET &&
+	     IN_MULTICAST(ntohl(remote->sin.sin_addr.s_addr))) ||
+	    (remote->sa.sa_family == AF_INET6 &&
+	     ipv6_addr_is_multicast(&remote->sin6.sin6_addr)))
 		return -EINVAL;
+	geneve->remote = *remote;
 
 	geneve->ttl = ttl;
 	geneve->tos = tos;
 	geneve->dst_port = dst_port;
 	geneve->collect_md = metadata;
 
-	t = geneve_find_dev(gn, dst_port, rem_addr, geneve->vni,
+	t = geneve_find_dev(gn, dst_port, remote, geneve->vni,
 			    &tun_on_same_port, &tun_collect_md);
 	if (t)
 		return -EBUSY;
@@ -870,14 +1139,29 @@ static int geneve_newlink(struct net *net, struct net_device *dev,
 	__be16 dst_port = htons(GENEVE_UDP_PORT);
 	__u8 ttl = 0, tos = 0;
 	bool metadata = false;
-	__be32 rem_addr;
+	union geneve_addr remote;
 	__u32 vni;
 
-	if (!data[IFLA_GENEVE_ID] || !data[IFLA_GENEVE_REMOTE])
+	if (!data[IFLA_GENEVE_ID] ||
+	    (data[IFLA_GENEVE_REMOTE] && data[IFLA_GENEVE_REMOTE6]) ||
+	    (!data[IFLA_GENEVE_REMOTE] && !data[IFLA_GENEVE_REMOTE6]))
 		return -EINVAL;
 
 	vni = nla_get_u32(data[IFLA_GENEVE_ID]);
-	rem_addr = nla_get_in_addr(data[IFLA_GENEVE_REMOTE]);
+
+	memset(&remote, 0, sizeof(remote));
+	if (data[IFLA_GENEVE_REMOTE]) {
+		remote.sa.sa_family = AF_INET;
+		remote.sin.sin_addr.s_addr =
+			nla_get_in_addr(data[IFLA_GENEVE_REMOTE]);
+	} else if (data[IFLA_GENEVE_REMOTE6]) {
+		if (!IS_ENABLED(CONFIG_IPV6))
+			return -EPFNOSUPPORT;
+
+		remote.sa.sa_family = AF_INET6;
+		remote.sin6.sin6_addr =
+			nla_get_in6_addr(data[IFLA_GENEVE_REMOTE6]);
+	}
 
 	if (data[IFLA_GENEVE_TTL])
 		ttl = nla_get_u8(data[IFLA_GENEVE_TTL]);
@@ -891,8 +1175,8 @@ static int geneve_newlink(struct net *net, struct net_device *dev,
 	if (data[IFLA_GENEVE_COLLECT_METADATA])
 		metadata = true;
 
-	return geneve_configure(net, dev, rem_addr, vni,
-				ttl, tos, dst_port, metadata);
+	return geneve_configure(net, dev, &remote, vni, ttl, tos, dst_port,
+				metadata);
 }
 
 static void geneve_dellink(struct net_device *dev, struct list_head *head)
@@ -906,7 +1190,7 @@ static void geneve_dellink(struct net_device *dev, struct list_head *head)
 static size_t geneve_get_size(const struct net_device *dev)
 {
 	return nla_total_size(sizeof(__u32)) +	/* IFLA_GENEVE_ID */
-		nla_total_size(sizeof(struct in_addr)) + /* IFLA_GENEVE_REMOTE */
+		nla_total_size(sizeof(struct in6_addr)) + /* IFLA_GENEVE_REMOTE{6} */
 		nla_total_size(sizeof(__u8)) +  /* IFLA_GENEVE_TTL */
 		nla_total_size(sizeof(__u8)) +  /* IFLA_GENEVE_TOS */
 		nla_total_size(sizeof(__be16)) +  /* IFLA_GENEVE_PORT */
@@ -923,9 +1207,17 @@ static int geneve_fill_info(struct sk_buff *skb, const struct net_device *dev)
 	if (nla_put_u32(skb, IFLA_GENEVE_ID, vni))
 		goto nla_put_failure;
 
-	if (nla_put_in_addr(skb, IFLA_GENEVE_REMOTE,
-			    geneve->remote.sin_addr.s_addr))
-		goto nla_put_failure;
+	if (geneve->remote.sa.sa_family == AF_INET) {
+		if (nla_put_in_addr(skb, IFLA_GENEVE_REMOTE,
+				    geneve->remote.sin.sin_addr.s_addr))
+			goto nla_put_failure;
+#if IS_ENABLED(CONFIG_IPV6)
+	} else {
+		if (nla_put_in6_addr(skb, IFLA_GENEVE_REMOTE6,
+				     &geneve->remote.sin6.sin6_addr))
+			goto nla_put_failure;
+#endif
+	}
 
 	if (nla_put_u8(skb, IFLA_GENEVE_TTL, geneve->ttl) ||
 	    nla_put_u8(skb, IFLA_GENEVE_TOS, geneve->tos))
@@ -971,7 +1263,8 @@ struct net_device *geneve_dev_create_fb(struct net *net, const char *name,
 	if (IS_ERR(dev))
 		return dev;
 
-	err = geneve_configure(net, dev, 0, 0, 0, 0, htons(dst_port), true);
+	err = geneve_configure(net, dev, &geneve_remote_unspec,
+			       0, 0, 0, htons(dst_port), true);
 	if (err) {
 		free_netdev(dev);
 		return ERR_PTR(err);
diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
index e3b6217f34f1..45e3a48550f9 100644
--- a/include/uapi/linux/if_link.h
+++ b/include/uapi/linux/if_link.h
@@ -461,6 +461,7 @@ enum {
 	IFLA_GENEVE_TOS,
 	IFLA_GENEVE_PORT,	/* destination port */
 	IFLA_GENEVE_COLLECT_METADATA,
+	IFLA_GENEVE_REMOTE6,
 	__IFLA_GENEVE_MAX
 };
 #define IFLA_GENEVE_MAX	(__IFLA_GENEVE_MAX - 1)
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH 2/2] geneve: handle ipv6 priority like ipv4 tos
  2015-10-20 15:11     ` [PATCH v4 1/2] " John W. Linville
@ 2015-10-20 15:11       ` John W. Linville
  2015-10-21  5:13         ` Jesse Gross
  2015-10-20 22:55       ` [PATCH v4 1/2] geneve: implement support for IPv6-based tunnels kbuild test robot
                         ` (3 subsequent siblings)
  4 siblings, 1 reply; 37+ messages in thread
From: John W. Linville @ 2015-10-20 15:11 UTC (permalink / raw)
  To: netdev
  Cc: Dave Miller, Pravin B Shelar, Jesse Gross, Jiri Benc, John W. Linville

Other callers of udp_tunnel6_xmit_skb just pass 0 for the prio
argument.  Jesse Gross <jesse@nicira.com> suggested that prio is really
the same as IPv4's tos and should be handled the same, so this is my
interpretation of that suggestion.

Signed-off-by: John W. Linville <linville@tuxdriver.com>
Reported-by: Jesse Gross <jesse@nicira.com>
---
 drivers/net/geneve.c | 19 +++++++++++++++++--
 1 file changed, 17 insertions(+), 2 deletions(-)

diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c
index 217b472ab9e7..cef71bdda13d 100644
--- a/drivers/net/geneve.c
+++ b/drivers/net/geneve.c
@@ -748,6 +748,7 @@ static struct dst_entry *geneve_get_v6_dst(struct sk_buff *skb,
 	struct geneve_dev *geneve = netdev_priv(dev);
 	struct geneve_sock *gs6 = geneve->sock6;
 	struct dst_entry *dst = NULL;
+	__u8 prio;
 
 	memset(fl6, 0, sizeof(*fl6));
 	fl6->flowi6_mark = skb->mark;
@@ -756,7 +757,16 @@ static struct dst_entry *geneve_get_v6_dst(struct sk_buff *skb,
 	if (info) {
 		fl6->daddr = info->key.u.ipv6.dst;
 		fl6->saddr = info->key.u.ipv6.src;
+		fl6->flowi6_tos = RT_TOS(info->key.tos);
 	} else {
+		prio = geneve->tos;
+		if (prio == 1) {
+			const struct iphdr *iip = ip_hdr(skb);
+
+			prio = ip_tunnel_get_dsfield(iip, skb);
+		}
+
+		fl6->flowi6_tos = RT_TOS(prio);
 		fl6->daddr = geneve->remote.sin6.sin6_addr;
 	}
 
@@ -876,8 +886,9 @@ static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
 	struct geneve_dev *geneve = netdev_priv(dev);
 	struct geneve_sock *gs6 = geneve->sock6;
 	struct dst_entry *dst = NULL;
+	const struct iphdr *iip; /* interior IP header */
 	struct flowi6 fl6;
-	__u8 ttl;
+	__u8 prio, ttl;
 	__be16 sport;
 	bool udp_csum;
 	int err;
@@ -906,6 +917,8 @@ static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
 	sport = udp_flow_src_port(geneve->net, skb, 1, USHRT_MAX, true);
 	skb_reset_mac_header(skb);
 
+	iip = ip_hdr(skb);
+
 	if (info) {
 		const struct ip_tunnel_key *key = &info->key;
 		u8 *opts = NULL;
@@ -922,6 +935,7 @@ static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
 		if (unlikely(err))
 			goto err;
 
+		prio = ip_tunnel_ecn_encap(key->tos, iip, skb);
 		ttl = key->ttl;
 	} else {
 		udp_csum = false;
@@ -930,13 +944,14 @@ static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
 		if (unlikely(err))
 			goto err;
 
+		prio = ip_tunnel_ecn_encap(fl6.flowi6_tos, iip, skb);
 		ttl = geneve->ttl;
 		if (!ttl && ipv6_addr_is_multicast(&fl6.daddr))
 			ttl = 1;
 		ttl = ttl ? : ip6_dst_hoplimit(dst);
 	}
 	err = udp_tunnel6_xmit_skb(dst, gs6->sock->sk, skb, dev,
-				   &fl6.saddr, &fl6.daddr, 0, ttl,
+				   &fl6.saddr, &fl6.daddr, prio, ttl,
 				   sport, geneve->dst_port, !udp_csum);
 
 	iptunnel_xmit_stats(err, &dev->stats, dev->tstats);
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* Re: [PATCH v4 1/2] geneve: implement support for IPv6-based tunnels
  2015-10-20 15:11     ` [PATCH v4 1/2] " John W. Linville
  2015-10-20 15:11       ` [PATCH 2/2] geneve: handle ipv6 priority like ipv4 tos John W. Linville
@ 2015-10-20 22:55       ` kbuild test robot
  2015-10-21  1:52       ` YOSHIFUJI Hideaki/吉藤英明
                         ` (2 subsequent siblings)
  4 siblings, 0 replies; 37+ messages in thread
From: kbuild test robot @ 2015-10-20 22:55 UTC (permalink / raw)
  To: John W. Linville
  Cc: kbuild-all, netdev, Dave Miller, Pravin B Shelar, Jesse Gross,
	Jiri Benc, John W. Linville

[-- Attachment #1: Type: text/plain, Size: 2479 bytes --]

Hi John,

[auto build test WARNING on v4.3-rc6 -- if it's inappropriate base, please suggest rules for selecting the more suitable base]

url:    https://github.com/0day-ci/linux/commits/John-W-Linville/geneve-implement-support-for-IPv6-based-tunnels/20151020-232128
config: x86_64-randconfig-a0-10210538 (attached as .config)
reproduce:
        # save the attached .config to linux build tree
        make ARCH=x86_64 

All warnings (new ones prefixed by >>):

   In file included from include/linux/linkage.h:4:0,
                    from include/linux/kernel.h:6,
                    from drivers/net/geneve.c:13:
   drivers/net/geneve.c: In function 'geneve_rx':
   drivers/net/geneve.c:239:6: error: 'ip6h' undeclared (first use in this function)
     if (ip6h)
         ^
   include/linux/compiler.h:147:28: note: in definition of macro '__trace_if'
     if (__builtin_constant_p((cond)) ? !!(cond) :   \
                               ^
>> drivers/net/geneve.c:239:2: note: in expansion of macro 'if'
     if (ip6h)
     ^
   drivers/net/geneve.c:239:6: note: each undeclared identifier is reported only once for each function it appears in
     if (ip6h)
         ^
   include/linux/compiler.h:147:28: note: in definition of macro '__trace_if'
     if (__builtin_constant_p((cond)) ? !!(cond) :   \
                               ^
>> drivers/net/geneve.c:239:2: note: in expansion of macro 'if'
     if (ip6h)
     ^

vim +/if +239 drivers/net/geneve.c

   223		skb_reset_mac_header(skb);
   224		skb_scrub_packet(skb, !net_eq(geneve->net, dev_net(geneve->dev)));
   225		skb->protocol = eth_type_trans(skb, geneve->dev);
   226		skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN);
   227	
   228		if (tun_dst)
   229			skb_dst_set(skb, &tun_dst->dst);
   230	
   231		/* Ignore packet loops (and multicast echo) */
   232		if (ether_addr_equal(eth_hdr(skb)->h_source, geneve->dev->dev_addr))
   233			goto drop;
   234	
   235		skb_reset_network_header(skb);
   236	
   237		if (iph)
   238			err = IP_ECN_decapsulate(iph, skb);
 > 239		if (ip6h)
   240			err = IP6_ECN_decapsulate(ip6h, skb);
   241	
   242		if (unlikely(err)) {
   243			if (log_ecn_error) {
   244				if (iph)
   245					net_info_ratelimited("non-ECT from %pI4 "
   246							     "with TOS=%#x\n",
   247							     &iph->saddr, iph->tos);

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/octet-stream, Size: 24631 bytes --]

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v4 1/2] geneve: implement support for IPv6-based tunnels
  2015-10-20 15:11     ` [PATCH v4 1/2] " John W. Linville
  2015-10-20 15:11       ` [PATCH 2/2] geneve: handle ipv6 priority like ipv4 tos John W. Linville
  2015-10-20 22:55       ` [PATCH v4 1/2] geneve: implement support for IPv6-based tunnels kbuild test robot
@ 2015-10-21  1:52       ` YOSHIFUJI Hideaki/吉藤英明
  2015-10-21 18:58         ` John W. Linville
  2015-10-21  5:06       ` Jesse Gross
  2015-10-22 19:45       ` [PATCH v5 " John W. Linville
  4 siblings, 1 reply; 37+ messages in thread
From: YOSHIFUJI Hideaki/吉藤英明 @ 2015-10-21  1:52 UTC (permalink / raw)
  To: John W. Linville, netdev
  Cc: hideaki.yoshifuji, Dave Miller, Pravin B Shelar, Jesse Gross, Jiri Benc

Hi,

John W. Linville wrote:
> Signed-off-by: John W. Linville <linville@tuxdriver.com>
> ---
> v4:
> - treat mode field of ip_tunnel_info as flags
> - add a missing IS_ENABLED(CONFIG_IPV6) to geneve_rx
> - remove unneeded flags field in geneve_dev
> - NULL-check parameter for __geneve_sock_release
> - check remote socket family for AF_UNSPEC in geneve_configure
> - rename geneve_get_{rt,dst} as geneve_get_{v4_rt,v6_dst}
> - refactor some error handling in the xmit paths
> 
> v3:
> - declare geneve_remote_unspec as static
> 
> v2:
> - do not require remote address for tx on metadata tunnels
> - pass correct sockaddr family to udp_tun_rx_dst in geneve_rx
> - accommodate both ipv4 and ipv6 sockets open on same tunnel
> - move declaration of geneve_get_dst for aesthetic purposes
> 
>  drivers/net/geneve.c         | 459 +++++++++++++++++++++++++++++++++++--------
>  include/uapi/linux/if_link.h |   1 +
>  2 files changed, 377 insertions(+), 83 deletions(-)
> 
> diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c
> index 8f5c02eed47d..217b472ab9e7 100644
> --- a/drivers/net/geneve.c
> +++ b/drivers/net/geneve.c
> @@ -46,16 +46,25 @@ struct geneve_net {
>  
>  static int geneve_net_id;
>  
> +union geneve_addr {
> +	struct sockaddr_in sin;
> +	struct sockaddr_in6 sin6;
> +	struct sockaddr sa;
> +};
> +
> +static union geneve_addr geneve_remote_unspec = { .sa.sa_family = AF_UNSPEC, };
> +
>  /* Pseudo network device */
>  struct geneve_dev {
>  	struct hlist_node  hlist;	/* vni hash table */
>  	struct net	   *net;	/* netns for packet i/o */
>  	struct net_device  *dev;	/* netdev for geneve tunnel */
> -	struct geneve_sock *sock;	/* socket used for geneve tunnel */
> +	struct geneve_sock *sock4;	/* IPv4 socket used for geneve tunnel */
> +	struct geneve_sock *sock6;	/* IPv6 socket used for geneve tunnel */
>  	u8                 vni[3];	/* virtual network ID for tunnel */
>  	u8                 ttl;		/* TTL override */
>  	u8                 tos;		/* TOS override */
> -	struct sockaddr_in remote;	/* IPv4 address for link partner */
> +	union geneve_addr  remote;	/* IP address for link partner */
>  	struct list_head   next;	/* geneve's per namespace list */
>  	__be16		   dst_port;
>  	bool		   collect_md;
> @@ -103,11 +112,32 @@ static struct geneve_dev *geneve_lookup(struct geneve_sock *gs,
>  	vni_list_head = &gs->vni_list[hash];
>  	hlist_for_each_entry_rcu(geneve, vni_list_head, hlist) {
>  		if (!memcmp(vni, geneve->vni, sizeof(geneve->vni)) &&
> -		    addr == geneve->remote.sin_addr.s_addr)
> +		    addr == geneve->remote.sin.sin_addr.s_addr)
> +			return geneve;
> +	}
> +	return NULL;
> +}
> +
> +#if IS_ENABLED(CONFIG_IPV6)
> +static struct geneve_dev *geneve6_lookup(struct geneve_sock *gs,
> +					 struct in6_addr addr6, u8 vni[])
> +{
> +	struct hlist_head *vni_list_head;
> +	struct geneve_dev *geneve;
> +	__u32 hash;
> +
> +	/* Find the device for this VNI */
> +	hash = geneve_net_vni_hash(vni);
> +	vni_list_head = &gs->vni_list[hash];
> +	hlist_for_each_entry_rcu(geneve, vni_list_head, hlist) {
> +		if (!memcmp(vni, geneve->vni, sizeof(geneve->vni)) &&
> +		    !memcmp(&addr6, &geneve->remote.sin6.sin6_addr,
> +			    sizeof(addr6)))

Please use ipv6_addr_equal().
How do you handle link-local addresses here?

>  			return geneve;
>  	}
>  	return NULL;
>  }
> +#endif
>  
>  static inline struct genevehdr *geneve_hdr(const struct sk_buff *skb)
>  {
> @@ -121,24 +151,49 @@ static void geneve_rx(struct geneve_sock *gs, struct sk_buff *skb)
>  	struct metadata_dst *tun_dst = NULL;
>  	struct geneve_dev *geneve = NULL;
>  	struct pcpu_sw_netstats *stats;
> -	struct iphdr *iph;
> -	u8 *vni;
> +	struct iphdr *iph = NULL;
>  	__be32 addr;
> -	int err;
> +	static u8 zero_vni[3];
> +	u8 *vni;
> +	int err = 0;
> +	sa_family_t sa_family;
> +#if IS_ENABLED(CONFIG_IPV6)
> +	struct ipv6hdr *ip6h = NULL;
> +	struct in6_addr addr6;
> +	static struct in6_addr zero_addr6;
> +#endif
>  
> -	iph = ip_hdr(skb); /* outer IP header... */
> +	sa_family = gs->sock->sk->sk_family;
>  
> -	if (gs->collect_md) {
> -		static u8 zero_vni[3];
> +	if (sa_family == AF_INET) {
> +		iph = ip_hdr(skb); /* outer IP header... */
>  
> -		vni = zero_vni;
> -		addr = 0;
> -	} else {
> -		vni = gnvh->vni;
> -		addr = iph->saddr;
> -	}
> +		if (gs->collect_md) {
> +			vni = zero_vni;
> +			addr = 0;
> +		} else {
> +			vni = gnvh->vni;
> +
> +			addr = iph->saddr;
> +		}
> +
> +		geneve = geneve_lookup(gs, addr, vni);
> +#if IS_ENABLED(CONFIG_IPV6)
> +	} else if (sa_family == AF_INET6) {
> +		ip6h = ipv6_hdr(skb); /* outer IPv6 header... */
> +
> +		if (gs->collect_md) {
> +			vni = zero_vni;
> +			addr6 = zero_addr6;
> +		} else {
> +			vni = gnvh->vni;
>  
> -	geneve = geneve_lookup(gs, addr, vni);
> +			addr6 = ip6h->saddr;
> +		}
> +
> +		geneve = geneve6_lookup(gs, addr6, vni);
> +#endif
> +	}
>  	if (!geneve)
>  		goto drop;
>  
> @@ -149,7 +204,7 @@ static void geneve_rx(struct geneve_sock *gs, struct sk_buff *skb)
>  			(gnvh->oam ? TUNNEL_OAM : 0) |
>  			(gnvh->critical ? TUNNEL_CRIT_OPT : 0);
>  
> -		tun_dst = udp_tun_rx_dst(skb, AF_INET, flags,
> +		tun_dst = udp_tun_rx_dst(skb, sa_family, flags,
>  					 vni_to_tunnel_id(gnvh->vni),
>  					 gnvh->opt_len * 4);
>  		if (!tun_dst)
> @@ -179,12 +234,21 @@ static void geneve_rx(struct geneve_sock *gs, struct sk_buff *skb)
>  
>  	skb_reset_network_header(skb);
>  
> -	err = IP_ECN_decapsulate(iph, skb);
> +	if (iph)
> +		err = IP_ECN_decapsulate(iph, skb);
> +	if (ip6h)
> +		err = IP6_ECN_decapsulate(ip6h, skb);
>  
>  	if (unlikely(err)) {
> -		if (log_ecn_error)
> -			net_info_ratelimited("non-ECT from %pI4 with TOS=%#x\n",
> -					     &iph->saddr, iph->tos);
> +		if (log_ecn_error) {
> +			if (iph)
> +				net_info_ratelimited("non-ECT from %pI4 "
> +						     "with TOS=%#x\n",
> +						     &iph->saddr, iph->tos);
> +			if (ip6h)
> +				net_info_ratelimited("non-ECT from %pI6\n",
> +						     &ip6h->saddr);
> +		}
>  		if (err > 1) {
>  			++geneve->dev->stats.rx_frame_errors;
>  			++geneve->dev->stats.rx_errors;
> @@ -284,6 +348,7 @@ static struct socket *geneve_create_sock(struct net *net, bool ipv6,
>  
>  	if (ipv6) {
>  		udp_conf.family = AF_INET6;
> +		udp_conf.ipv6_v6only = 1;
>  	} else {
>  		udp_conf.family = AF_INET;
>  		udp_conf.local_ip.s_addr = htonl(INADDR_ANY);
> @@ -458,9 +523,9 @@ static void geneve_notify_del_rx_port(struct geneve_sock *gs)
>  		udp_del_offload(&gs->udp_offloads);
>  }
>  
> -static void geneve_sock_release(struct geneve_sock *gs)
> +static void __geneve_sock_release(struct geneve_sock *gs)
>  {
> -	if (--gs->refcnt)
> +	if (!gs || --gs->refcnt)
>  		return;
>  
>  	list_del(&gs->list);
> @@ -469,58 +534,107 @@ static void geneve_sock_release(struct geneve_sock *gs)
>  	kfree_rcu(gs, rcu);
>  }
>  
> +static void geneve_sock_release(struct geneve_dev *geneve)
> +{
> +	__geneve_sock_release(geneve->sock4);
> +#if IS_ENABLED(CONFIG_IPV6)
> +	__geneve_sock_release(geneve->sock6);
> +#endif
> +}
> +
>  static struct geneve_sock *geneve_find_sock(struct geneve_net *gn,
> +					    sa_family_t family,
>  					    __be16 dst_port)
>  {
>  	struct geneve_sock *gs;
>  
>  	list_for_each_entry(gs, &gn->sock_list, list) {
>  		if (inet_sk(gs->sock->sk)->inet_sport == dst_port &&
> -		    inet_sk(gs->sock->sk)->sk.sk_family == AF_INET) {
> +		    inet_sk(gs->sock->sk)->sk.sk_family == family) {
>  			return gs;
>  		}
>  	}
>  	return NULL;
>  }
>  
> -static int geneve_open(struct net_device *dev)
> +static int geneve_sock_add(struct geneve_dev *geneve, bool ipv6)
>  {
> -	struct geneve_dev *geneve = netdev_priv(dev);
>  	struct net *net = geneve->net;
>  	struct geneve_net *gn = net_generic(net, geneve_net_id);
>  	struct geneve_sock *gs;
>  	__u32 hash;
>  
> -	gs = geneve_find_sock(gn, geneve->dst_port);
> +	gs = geneve_find_sock(gn, ipv6 ? AF_INET6 : AF_INET, geneve->dst_port);
>  	if (gs) {
>  		gs->refcnt++;
>  		goto out;
>  	}
>  
> -	gs = geneve_socket_create(net, geneve->dst_port, false);
> +	gs = geneve_socket_create(net, geneve->dst_port, ipv6);
>  	if (IS_ERR(gs))
>  		return PTR_ERR(gs);
>  
>  out:
>  	gs->collect_md = geneve->collect_md;
> -	geneve->sock = gs;
> +#if IS_ENABLED(CONFIG_IPV6)
> +	if (ipv6)
> +		geneve->sock6 = gs;
> +	else
> +#endif
> +		geneve->sock4 = gs;
>  
>  	hash = geneve_net_vni_hash(geneve->vni);
>  	hlist_add_head_rcu(&geneve->hlist, &gs->vni_list[hash]);
>  	return 0;
>  }
>  
> +static int geneve_open(struct net_device *dev)
> +{
> +	struct geneve_dev *geneve = netdev_priv(dev);
> +	bool ipv6 = geneve->remote.sa.sa_family == AF_INET6;
> +	bool metadata = !!geneve->collect_md;
> +	int ret = 0;
> +
> +	geneve->sock4 = NULL;
> +#if IS_ENABLED(CONFIG_IPV6)
> +	geneve->sock6 = NULL;
> +	if (ipv6 || metadata)
> +		ret = geneve_sock_add(geneve, true);
> +#endif
> +	if (!ret && (!ipv6 || metadata))
> +		ret = geneve_sock_add(geneve, false);
> +	if (ret < 0)
> +		geneve_sock_release(geneve);
> +
> +	return ret;
> +}
> +
>  static int geneve_stop(struct net_device *dev)
>  {
>  	struct geneve_dev *geneve = netdev_priv(dev);
> -	struct geneve_sock *gs = geneve->sock;
>  
>  	if (!hlist_unhashed(&geneve->hlist))
>  		hlist_del_rcu(&geneve->hlist);
> -	geneve_sock_release(gs);
> +	geneve_sock_release(geneve);
>  	return 0;
>  }
>  
> +static void geneve_build_header(struct genevehdr *geneveh,
> +				__be16 tun_flags, u8 vni[3],
> +				u8 options_len, u8 *options)
> +{
> +	geneveh->ver = GENEVE_VER;
> +	geneveh->opt_len = options_len / 4;
> +	geneveh->oam = !!(tun_flags & TUNNEL_OAM);
> +	geneveh->critical = !!(tun_flags & TUNNEL_CRIT_OPT);
> +	geneveh->rsvd1 = 0;
> +	memcpy(geneveh->vni, vni, 3);
> +	geneveh->proto_type = htons(ETH_P_TEB);
> +	geneveh->rsvd2 = 0;
> +
> +	memcpy(geneveh->options, options, options_len);
> +}
> +
>  static int geneve_build_skb(struct rtable *rt, struct sk_buff *skb,
>  			    __be16 tun_flags, u8 vni[3], u8 opt_len, u8 *opt,
>  			    bool csum)
> @@ -544,15 +658,7 @@ static int geneve_build_skb(struct rtable *rt, struct sk_buff *skb,
>  	}
>  
>  	gnvh = (struct genevehdr *)__skb_push(skb, sizeof(*gnvh) + opt_len);
> -	gnvh->ver = GENEVE_VER;
> -	gnvh->opt_len = opt_len / 4;
> -	gnvh->oam = !!(tun_flags & TUNNEL_OAM);
> -	gnvh->critical = !!(tun_flags & TUNNEL_CRIT_OPT);
> -	gnvh->rsvd1 = 0;
> -	memcpy(gnvh->vni, vni, 3);
> -	gnvh->proto_type = htons(ETH_P_TEB);
> -	gnvh->rsvd2 = 0;
> -	memcpy(gnvh->options, opt, opt_len);
> +	geneve_build_header(gnvh, tun_flags, vni, opt_len, opt);
>  
>  	skb_set_inner_protocol(skb, htons(ETH_P_TEB));
>  	return 0;
> @@ -562,10 +668,47 @@ free_rt:
>  	return err;
>  }
>  
> -static struct rtable *geneve_get_rt(struct sk_buff *skb,
> -				    struct net_device *dev,
> -				    struct flowi4 *fl4,
> -				    struct ip_tunnel_info *info)
> +#if IS_ENABLED(CONFIG_IPV6)
> +static int geneve6_build_skb(struct dst_entry *dst, struct sk_buff *skb,
> +			     __be16 tun_flags, u8 vni[3], u8 opt_len, u8 *opt,
> +			     bool csum, bool xnet)
> +{
> +	struct genevehdr *gnvh;
> +	int min_headroom;
> +	int err;
> +
> +	skb_scrub_packet(skb, xnet);
> +
> +	min_headroom = LL_RESERVED_SPACE(dst->dev) + dst->header_len
> +			+ GENEVE_BASE_HLEN + opt_len + sizeof(struct ipv6hdr);
> +	err = skb_cow_head(skb, min_headroom);
> +	if (unlikely(err)) {
> +		kfree_skb(skb);
> +		goto free_dst;
> +	}
> +
> +	skb = udp_tunnel_handle_offloads(skb, csum);
> +	if (IS_ERR(skb)) {
> +		err = PTR_ERR(skb);
> +		goto free_dst;
> +	}
> +
> +	gnvh = (struct genevehdr *)__skb_push(skb, sizeof(*gnvh) + opt_len);
> +	geneve_build_header(gnvh, tun_flags, vni, opt_len, opt);
> +
> +	skb_set_inner_protocol(skb, htons(ETH_P_TEB));
> +	return 0;
> +
> +free_dst:
> +	dst_release(dst);
> +	return err;
> +}
> +#endif
> +
> +static struct rtable *geneve_get_v4_rt(struct sk_buff *skb,
> +				       struct net_device *dev,
> +				       struct flowi4 *fl4,
> +				       struct ip_tunnel_info *info)
>  {
>  	struct geneve_dev *geneve = netdev_priv(dev);
>  	struct rtable *rt = NULL;
> @@ -588,24 +731,42 @@ static struct rtable *geneve_get_rt(struct sk_buff *skb,
>  		}
>  
>  		fl4->flowi4_tos = RT_TOS(tos);
> -		fl4->daddr = geneve->remote.sin_addr.s_addr;
> +		fl4->daddr = geneve->remote.sin.sin_addr.s_addr;
>  	}
>  
>  	rt = ip_route_output_key(geneve->net, fl4);
> -	if (IS_ERR(rt)) {
> -		netdev_dbg(dev, "no route to %pI4\n", &fl4->daddr);
> -		dev->stats.tx_carrier_errors++;
> -		return rt;
> -	}
> -	if (rt->dst.dev == dev) { /* is this necessary? */
> -		netdev_dbg(dev, "circular route to %pI4\n", &fl4->daddr);
> -		dev->stats.collisions++;
> -		ip_rt_put(rt);
> -		return ERR_PTR(-EINVAL);
> -	}
> +
>  	return rt;
>  }
>  
> +#if IS_ENABLED(CONFIG_IPV6)
> +static struct dst_entry *geneve_get_v6_dst(struct sk_buff *skb,
> +					   struct net_device *dev,
> +					   struct flowi6 *fl6,
> +					   struct ip_tunnel_info *info)
> +{
> +	struct geneve_dev *geneve = netdev_priv(dev);
> +	struct geneve_sock *gs6 = geneve->sock6;
> +	struct dst_entry *dst = NULL;
> +
> +	memset(fl6, 0, sizeof(*fl6));
> +	fl6->flowi6_mark = skb->mark;
> +	fl6->flowi6_proto = IPPROTO_UDP;
> +
> +	if (info) {
> +		fl6->daddr = info->key.u.ipv6.dst;
> +		fl6->saddr = info->key.u.ipv6.src;
> +	} else {
> +		fl6->daddr = geneve->remote.sin6.sin6_addr;
> +	}
> +
> +	if (ipv6_stub->ipv6_dst_lookup(geneve->net, gs6->sock->sk, &dst, fl6))
> +		return ERR_PTR(-EHOSTUNREACH);
> +
> +	return dst;
> +}
> +#endif
> +
>  /* Convert 64 bit tunnel ID to 24 bit VNI. */
>  static void tunnel_id_to_vni(__be64 tun_id, __u8 *vni)
>  {
> @@ -620,11 +781,11 @@ static void tunnel_id_to_vni(__be64 tun_id, __u8 *vni)
>  #endif
>  }
>  
> -static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
> +static netdev_tx_t geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev,
> +				   struct ip_tunnel_info *info)
>  {
>  	struct geneve_dev *geneve = netdev_priv(dev);
> -	struct geneve_sock *gs = geneve->sock;
> -	struct ip_tunnel_info *info = NULL;
> +	struct geneve_sock *gs4 = geneve->sock4;
>  	struct rtable *rt = NULL;
>  	const struct iphdr *iip; /* interior IP header */
>  	struct flowi4 fl4;
> @@ -635,7 +796,6 @@ static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
>  	int err;
>  
>  	if (geneve->collect_md) {
> -		info = skb_tunnel_info(skb);
>  		if (unlikely(info && !(info->mode & IP_TUNNEL_INFO_TX))) {
>  			netdev_dbg(dev, "no tunnel metadata\n");
>  			goto tx_error;
> @@ -644,12 +804,18 @@ static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
>  			goto tx_error;
>  	}
>  
> -	rt = geneve_get_rt(skb, dev, &fl4, info);
> +	rt = geneve_get_v4_rt(skb, dev, &fl4, info);
>  	if (IS_ERR(rt)) {
>  		netdev_dbg(dev, "no route to %pI4\n", &fl4.daddr);
>  		dev->stats.tx_carrier_errors++;
>  		goto tx_error;
>  	}
> +	if (rt->dst.dev == dev) { /* is this necessary? */
> +		netdev_dbg(dev, "circular route to %pI4\n", &fl4.daddr);
> +		dev->stats.collisions++;
> +		ip_rt_put(rt);
> +		goto tx_error;
> +	}
>  
>  	sport = udp_flow_src_port(geneve->net, skb, 1, USHRT_MAX, true);
>  	skb_reset_mac_header(skb);
> @@ -688,7 +854,7 @@ static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
>  		ttl = ttl ? : ip4_dst_hoplimit(&rt->dst);
>  		df = 0;
>  	}
> -	err = udp_tunnel_xmit_skb(rt, gs->sock->sk, skb, fl4.saddr, fl4.daddr,
> +	err = udp_tunnel_xmit_skb(rt, gs4->sock->sk, skb, fl4.saddr, fl4.daddr,
>  				  tos, ttl, df, sport, geneve->dst_port,
>  				  !net_eq(geneve->net, dev_net(geneve->dev)),
>  				  !udp_csum);
> @@ -703,6 +869,103 @@ err:
>  	return NETDEV_TX_OK;
>  }
>  
> +#if IS_ENABLED(CONFIG_IPV6)
> +static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
> +				    struct ip_tunnel_info *info)
> +{
> +	struct geneve_dev *geneve = netdev_priv(dev);
> +	struct geneve_sock *gs6 = geneve->sock6;
> +	struct dst_entry *dst = NULL;
> +	struct flowi6 fl6;
> +	__u8 ttl;
> +	__be16 sport;
> +	bool udp_csum;
> +	int err;
> +	bool xnet = !net_eq(geneve->net, dev_net(geneve->dev));
> +
> +	if (geneve->collect_md) {
> +		if (unlikely(info && !(info->mode & IP_TUNNEL_INFO_TX))) {
> +			netdev_dbg(dev, "no tunnel metadata\n");
> +			goto tx_error;
> +		}
> +	}
> +
> +	dst = geneve_get_v6_dst(skb, dev, &fl6, info);
> +	if (IS_ERR(dst)) {
> +		netdev_dbg(dev, "no route to %pI6\n", &fl6.daddr);
> +		dev->stats.tx_carrier_errors++;
> +		goto tx_error;
> +	}
> +	if (dst->dev == dev) { /* is this necessary? */
> +		netdev_dbg(dev, "circular route to %pI6\n", &fl6.daddr);
> +		dev->stats.collisions++;
> +		dst_release(dst);
> +		goto tx_error;
> +	}
> +
> +	sport = udp_flow_src_port(geneve->net, skb, 1, USHRT_MAX, true);
> +	skb_reset_mac_header(skb);
> +
> +	if (info) {
> +		const struct ip_tunnel_key *key = &info->key;
> +		u8 *opts = NULL;
> +		u8 vni[3];
> +
> +		tunnel_id_to_vni(key->tun_id, vni);
> +		if (key->tun_flags & TUNNEL_GENEVE_OPT)
> +			opts = ip_tunnel_info_opts(info);
> +
> +		udp_csum = !!(key->tun_flags & TUNNEL_CSUM);
> +		err = geneve6_build_skb(dst, skb, key->tun_flags, vni,
> +					info->options_len, opts,
> +					udp_csum, xnet);
> +		if (unlikely(err))
> +			goto err;
> +
> +		ttl = key->ttl;
> +	} else {
> +		udp_csum = false;
> +		err = geneve6_build_skb(dst, skb, 0, geneve->vni,
> +					0, NULL, udp_csum, xnet);
> +		if (unlikely(err))
> +			goto err;
> +
> +		ttl = geneve->ttl;
> +		if (!ttl && ipv6_addr_is_multicast(&fl6.daddr))
> +			ttl = 1;
> +		ttl = ttl ? : ip6_dst_hoplimit(dst);
> +	}
> +	err = udp_tunnel6_xmit_skb(dst, gs6->sock->sk, skb, dev,
> +				   &fl6.saddr, &fl6.daddr, 0, ttl,
> +				   sport, geneve->dst_port, !udp_csum);
> +
> +	iptunnel_xmit_stats(err, &dev->stats, dev->tstats);
> +	return NETDEV_TX_OK;
> +
> +tx_error:
> +	dev_kfree_skb(skb);
> +err:
> +	dev->stats.tx_errors++;
> +	return NETDEV_TX_OK;
> +}
> +#endif
> +
> +static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
> +{
> +	struct geneve_dev *geneve = netdev_priv(dev);
> +	struct ip_tunnel_info *info = NULL;
> +
> +	if (geneve->collect_md)
> +		info = skb_tunnel_info(skb);
> +
> +#if IS_ENABLED(CONFIG_IPV6)
> +	if ((info && ip_tunnel_info_af(info) == AF_INET6) ||
> +	    (!info && geneve->remote.sa.sa_family == AF_INET6))
> +		return geneve6_xmit_skb(skb, dev, info);
> +#endif
> +	return geneve_xmit_skb(skb, dev, info);
> +}
> +
>  static const struct net_device_ops geneve_netdev_ops = {
>  	.ndo_init		= geneve_init,
>  	.ndo_uninit		= geneve_uninit,
> @@ -759,6 +1022,7 @@ static void geneve_setup(struct net_device *dev)
>  static const struct nla_policy geneve_policy[IFLA_GENEVE_MAX + 1] = {
>  	[IFLA_GENEVE_ID]		= { .type = NLA_U32 },
>  	[IFLA_GENEVE_REMOTE]		= { .len = FIELD_SIZEOF(struct iphdr, daddr) },
> +	[IFLA_GENEVE_REMOTE6]		= { .len = sizeof(struct in6_addr) },
>  	[IFLA_GENEVE_TTL]		= { .type = NLA_U8 },
>  	[IFLA_GENEVE_TOS]		= { .type = NLA_U8 },
>  	[IFLA_GENEVE_PORT]		= { .type = NLA_U16 },
> @@ -790,7 +1054,7 @@ static int geneve_validate(struct nlattr *tb[], struct nlattr *data[])
>  
>  static struct geneve_dev *geneve_find_dev(struct geneve_net *gn,
>  					  __be16 dst_port,
> -					  __be32 rem_addr,
> +					  union geneve_addr *remote,
>  					  u8 vni[],
>  					  bool *tun_on_same_port,
>  					  bool *tun_collect_md)
> @@ -806,7 +1070,7 @@ static struct geneve_dev *geneve_find_dev(struct geneve_net *gn,
>  			*tun_on_same_port = true;
>  		}
>  		if (!memcmp(vni, geneve->vni, sizeof(geneve->vni)) &&
> -		    rem_addr == geneve->remote.sin_addr.s_addr &&
> +		    !memcmp(remote, &geneve->remote, sizeof(geneve->remote)) &&
>  		    dst_port == geneve->dst_port)
>  			t = geneve;
>  	}
> @@ -814,18 +1078,20 @@ static struct geneve_dev *geneve_find_dev(struct geneve_net *gn,
>  }
>  
>  static int geneve_configure(struct net *net, struct net_device *dev,
> -			    __be32 rem_addr, __u32 vni, __u8 ttl, __u8 tos,
> -			    __be16 dst_port, bool metadata)
> +			    union geneve_addr *remote,
> +			    __u32 vni, __u8 ttl, __u8 tos, __be16 dst_port,
> +			    bool metadata)
>  {
>  	struct geneve_net *gn = net_generic(net, geneve_net_id);
>  	struct geneve_dev *t, *geneve = netdev_priv(dev);
>  	bool tun_collect_md, tun_on_same_port;
>  	int err;
>  
> -	if (metadata) {
> -		if (rem_addr || vni || tos || ttl)
> -			return -EINVAL;
> -	}
> +	if (!remote)
> +		return -EINVAL;
> +	if (metadata &&
> +	    (remote->sa.sa_family != AF_UNSPEC || vni || tos || ttl))
> +		return -EINVAL;
>  
>  	geneve->net = net;
>  	geneve->dev = dev;
> @@ -834,16 +1100,19 @@ static int geneve_configure(struct net *net, struct net_device *dev,
>  	geneve->vni[1] = (vni & 0x0000ff00) >> 8;
>  	geneve->vni[2] =  vni & 0x000000ff;
>  
> -	geneve->remote.sin_addr.s_addr = rem_addr;
> -	if (IN_MULTICAST(ntohl(geneve->remote.sin_addr.s_addr)))
> +	if ((remote->sa.sa_family == AF_INET &&
> +	     IN_MULTICAST(ntohl(remote->sin.sin_addr.s_addr))) ||
> +	    (remote->sa.sa_family == AF_INET6 &&
> +	     ipv6_addr_is_multicast(&remote->sin6.sin6_addr)))
>  		return -EINVAL;
> +	geneve->remote = *remote;
>  
>  	geneve->ttl = ttl;
>  	geneve->tos = tos;
>  	geneve->dst_port = dst_port;
>  	geneve->collect_md = metadata;
>  
> -	t = geneve_find_dev(gn, dst_port, rem_addr, geneve->vni,
> +	t = geneve_find_dev(gn, dst_port, remote, geneve->vni,
>  			    &tun_on_same_port, &tun_collect_md);
>  	if (t)
>  		return -EBUSY;
> @@ -870,14 +1139,29 @@ static int geneve_newlink(struct net *net, struct net_device *dev,
>  	__be16 dst_port = htons(GENEVE_UDP_PORT);
>  	__u8 ttl = 0, tos = 0;
>  	bool metadata = false;
> -	__be32 rem_addr;
> +	union geneve_addr remote;
>  	__u32 vni;
>  
> -	if (!data[IFLA_GENEVE_ID] || !data[IFLA_GENEVE_REMOTE])
> +	if (!data[IFLA_GENEVE_ID] ||
> +	    (data[IFLA_GENEVE_REMOTE] && data[IFLA_GENEVE_REMOTE6]) ||
> +	    (!data[IFLA_GENEVE_REMOTE] && !data[IFLA_GENEVE_REMOTE6]))
>  		return -EINVAL;
>  
>  	vni = nla_get_u32(data[IFLA_GENEVE_ID]);
> -	rem_addr = nla_get_in_addr(data[IFLA_GENEVE_REMOTE]);
> +
> +	memset(&remote, 0, sizeof(remote));
> +	if (data[IFLA_GENEVE_REMOTE]) {
> +		remote.sa.sa_family = AF_INET;
> +		remote.sin.sin_addr.s_addr =
> +			nla_get_in_addr(data[IFLA_GENEVE_REMOTE]);
> +	} else if (data[IFLA_GENEVE_REMOTE6]) {
> +		if (!IS_ENABLED(CONFIG_IPV6))
> +			return -EPFNOSUPPORT;
> +
> +		remote.sa.sa_family = AF_INET6;
> +		remote.sin6.sin6_addr =
> +			nla_get_in6_addr(data[IFLA_GENEVE_REMOTE6]);
> +	}
>  
>  	if (data[IFLA_GENEVE_TTL])
>  		ttl = nla_get_u8(data[IFLA_GENEVE_TTL]);
> @@ -891,8 +1175,8 @@ static int geneve_newlink(struct net *net, struct net_device *dev,
>  	if (data[IFLA_GENEVE_COLLECT_METADATA])
>  		metadata = true;
>  
> -	return geneve_configure(net, dev, rem_addr, vni,
> -				ttl, tos, dst_port, metadata);
> +	return geneve_configure(net, dev, &remote, vni, ttl, tos, dst_port,
> +				metadata);
>  }
>  
>  static void geneve_dellink(struct net_device *dev, struct list_head *head)
> @@ -906,7 +1190,7 @@ static void geneve_dellink(struct net_device *dev, struct list_head *head)
>  static size_t geneve_get_size(const struct net_device *dev)
>  {
>  	return nla_total_size(sizeof(__u32)) +	/* IFLA_GENEVE_ID */
> -		nla_total_size(sizeof(struct in_addr)) + /* IFLA_GENEVE_REMOTE */
> +		nla_total_size(sizeof(struct in6_addr)) + /* IFLA_GENEVE_REMOTE{6} */
>  		nla_total_size(sizeof(__u8)) +  /* IFLA_GENEVE_TTL */
>  		nla_total_size(sizeof(__u8)) +  /* IFLA_GENEVE_TOS */
>  		nla_total_size(sizeof(__be16)) +  /* IFLA_GENEVE_PORT */
> @@ -923,9 +1207,17 @@ static int geneve_fill_info(struct sk_buff *skb, const struct net_device *dev)
>  	if (nla_put_u32(skb, IFLA_GENEVE_ID, vni))
>  		goto nla_put_failure;
>  
> -	if (nla_put_in_addr(skb, IFLA_GENEVE_REMOTE,
> -			    geneve->remote.sin_addr.s_addr))
> -		goto nla_put_failure;
> +	if (geneve->remote.sa.sa_family == AF_INET) {
> +		if (nla_put_in_addr(skb, IFLA_GENEVE_REMOTE,
> +				    geneve->remote.sin.sin_addr.s_addr))
> +			goto nla_put_failure;
> +#if IS_ENABLED(CONFIG_IPV6)
> +	} else {
> +		if (nla_put_in6_addr(skb, IFLA_GENEVE_REMOTE6,
> +				     &geneve->remote.sin6.sin6_addr))
> +			goto nla_put_failure;
> +#endif
> +	}
>  
>  	if (nla_put_u8(skb, IFLA_GENEVE_TTL, geneve->ttl) ||
>  	    nla_put_u8(skb, IFLA_GENEVE_TOS, geneve->tos))
> @@ -971,7 +1263,8 @@ struct net_device *geneve_dev_create_fb(struct net *net, const char *name,
>  	if (IS_ERR(dev))
>  		return dev;
>  
> -	err = geneve_configure(net, dev, 0, 0, 0, 0, htons(dst_port), true);
> +	err = geneve_configure(net, dev, &geneve_remote_unspec,
> +			       0, 0, 0, htons(dst_port), true);
>  	if (err) {
>  		free_netdev(dev);
>  		return ERR_PTR(err);
> diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
> index e3b6217f34f1..45e3a48550f9 100644
> --- a/include/uapi/linux/if_link.h
> +++ b/include/uapi/linux/if_link.h
> @@ -461,6 +461,7 @@ enum {
>  	IFLA_GENEVE_TOS,
>  	IFLA_GENEVE_PORT,	/* destination port */
>  	IFLA_GENEVE_COLLECT_METADATA,
> +	IFLA_GENEVE_REMOTE6,
>  	__IFLA_GENEVE_MAX
>  };
>  #define IFLA_GENEVE_MAX	(__IFLA_GENEVE_MAX - 1)
> 

-- 
吉藤英明 <hideaki.yoshifuji@miraclelinux.com>
ミラクル・リナックス株式会社 技術本部 サポート部

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v4 1/2] geneve: implement support for IPv6-based tunnels
  2015-10-20 15:11     ` [PATCH v4 1/2] " John W. Linville
                         ` (2 preceding siblings ...)
  2015-10-21  1:52       ` YOSHIFUJI Hideaki/吉藤英明
@ 2015-10-21  5:06       ` Jesse Gross
  2015-10-22 19:45       ` [PATCH v5 " John W. Linville
  4 siblings, 0 replies; 37+ messages in thread
From: Jesse Gross @ 2015-10-21  5:06 UTC (permalink / raw)
  To: John W. Linville; +Cc: netdev, Dave Miller, Pravin B Shelar, Jiri Benc

All of this looks pretty good to me, I just have a few last things
that I noticed:

On Tue, Oct 20, 2015 at 11:11 PM, John W. Linville
<linville@tuxdriver.com> wrote:
> diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c
> index 8f5c02eed47d..217b472ab9e7 100644
> --- a/drivers/net/geneve.c
> +++ b/drivers/net/geneve.c
>  /* Pseudo network device */
>  struct geneve_dev {
>         struct hlist_node  hlist;       /* vni hash table */
>         struct net         *net;        /* netns for packet i/o */
>         struct net_device  *dev;        /* netdev for geneve tunnel */
> -       struct geneve_sock *sock;       /* socket used for geneve tunnel */
> +       struct geneve_sock *sock4;      /* IPv4 socket used for geneve tunnel */
> +       struct geneve_sock *sock6;      /* IPv6 socket used for geneve tunnel */

It might be worth wrapping sock6 in #if IS_ENABLED(CONFIG_IPV6). Not
because I'm trying to save space but because we make initializing it
conditional on this, so it would catch if somebody tries to access it
uninitialized.

> +static int geneve_open(struct net_device *dev)
> +{
> +       struct geneve_dev *geneve = netdev_priv(dev);
> +       bool ipv6 = geneve->remote.sa.sa_family == AF_INET6;
> +       bool metadata = !!geneve->collect_md;

A small thing but geneve->collect_md is also a bool, so I guess we
probably don't need the !!.

> +#if IS_ENABLED(CONFIG_IPV6)
> +static int geneve6_build_skb(struct dst_entry *dst, struct sk_buff *skb,
> +                            __be16 tun_flags, u8 vni[3], u8 opt_len, u8 *opt,
> +                            bool csum, bool xnet)
> +{
[...]
> +       skb_scrub_packet(skb, xnet);

I realized that I wasn't really all that clear with my previous
comment here. I was just asking if we should also use
skb_scrub_packet() in the IPv4 to keep things consistent. I agree that
the rt/dst differences makes sharing code a bit difficult in the
general sense.

> +static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
> +                                   struct ip_tunnel_info *info)
[...]
> +       if (geneve->collect_md) {
> +               if (unlikely(info && !(info->mode & IP_TUNNEL_INFO_TX))) {
> +                       netdev_dbg(dev, "no tunnel metadata\n");
> +                       goto tx_error;

It seems like this should also be checking for !info here - that's
perhaps the main cause of not having any tunnel metadata. This is the
same with IPv4 though too.

> @@ -870,14 +1139,29 @@ static int geneve_newlink(struct net *net, struct net_device *dev,
> -       if (!data[IFLA_GENEVE_ID] || !data[IFLA_GENEVE_REMOTE])
> +       if (!data[IFLA_GENEVE_ID] ||
> +           (data[IFLA_GENEVE_REMOTE] && data[IFLA_GENEVE_REMOTE6]) ||
> +           (!data[IFLA_GENEVE_REMOTE] && !data[IFLA_GENEVE_REMOTE6]))
>                 return -EINVAL;

I think this will conflict with/revert my change in -net. Obviously,
the conflict will need to be resolved at some point, but it's probably
just best to remove the whole block here so the resolution is obvious.

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH 2/2] geneve: handle ipv6 priority like ipv4 tos
  2015-10-20 15:11       ` [PATCH 2/2] geneve: handle ipv6 priority like ipv4 tos John W. Linville
@ 2015-10-21  5:13         ` Jesse Gross
  0 siblings, 0 replies; 37+ messages in thread
From: Jesse Gross @ 2015-10-21  5:13 UTC (permalink / raw)
  To: John W. Linville; +Cc: netdev, Dave Miller, Pravin B Shelar, Jiri Benc

On Tue, Oct 20, 2015 at 11:11 PM, John W. Linville
<linville@tuxdriver.com> wrote:
> Other callers of udp_tunnel6_xmit_skb just pass 0 for the prio
> argument.  Jesse Gross <jesse@nicira.com> suggested that prio is really
> the same as IPv4's tos and should be handled the same, so this is my
> interpretation of that suggestion.
>
> Signed-off-by: John W. Linville <linville@tuxdriver.com>
> Reported-by: Jesse Gross <jesse@nicira.com>

Reviewed-by: Jesse Gross <jesse@nicira.com>

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v4 1/2] geneve: implement support for IPv6-based tunnels
  2015-10-21  1:52       ` YOSHIFUJI Hideaki/吉藤英明
@ 2015-10-21 18:58         ` John W. Linville
  0 siblings, 0 replies; 37+ messages in thread
From: John W. Linville @ 2015-10-21 18:58 UTC (permalink / raw)
  To: YOSHIFUJI Hideaki/吉藤英明
  Cc: netdev, Dave Miller, Pravin B Shelar, Jesse Gross, Jiri Benc

On Wed, Oct 21, 2015 at 10:52:43AM +0900, YOSHIFUJI Hideaki/吉藤英明 wrote:
> Hi,

Yoshifuji-san -- thank you for taking a look at my proposed patch!

> John W. Linville wrote:
> > Signed-off-by: John W. Linville <linville@tuxdriver.com>
> > ---
> > v4:
> > - treat mode field of ip_tunnel_info as flags
> > - add a missing IS_ENABLED(CONFIG_IPV6) to geneve_rx
> > - remove unneeded flags field in geneve_dev
> > - NULL-check parameter for __geneve_sock_release
> > - check remote socket family for AF_UNSPEC in geneve_configure
> > - rename geneve_get_{rt,dst} as geneve_get_{v4_rt,v6_dst}
> > - refactor some error handling in the xmit paths
> > 
> > v3:
> > - declare geneve_remote_unspec as static
> > 
> > v2:
> > - do not require remote address for tx on metadata tunnels
> > - pass correct sockaddr family to udp_tun_rx_dst in geneve_rx
> > - accommodate both ipv4 and ipv6 sockets open on same tunnel
> > - move declaration of geneve_get_dst for aesthetic purposes
> > 
> >  drivers/net/geneve.c         | 459 +++++++++++++++++++++++++++++++++++--------
> >  include/uapi/linux/if_link.h |   1 +
> >  2 files changed, 377 insertions(+), 83 deletions(-)
> > 
> > diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c
> > index 8f5c02eed47d..217b472ab9e7 100644
> > --- a/drivers/net/geneve.c
> > +++ b/drivers/net/geneve.c
> > @@ -46,16 +46,25 @@ struct geneve_net {
> >  
> >  static int geneve_net_id;
> >  
> > +union geneve_addr {
> > +	struct sockaddr_in sin;
> > +	struct sockaddr_in6 sin6;
> > +	struct sockaddr sa;
> > +};
> > +
> > +static union geneve_addr geneve_remote_unspec = { .sa.sa_family = AF_UNSPEC, };
> > +
> >  /* Pseudo network device */
> >  struct geneve_dev {
> >  	struct hlist_node  hlist;	/* vni hash table */
> >  	struct net	   *net;	/* netns for packet i/o */
> >  	struct net_device  *dev;	/* netdev for geneve tunnel */
> > -	struct geneve_sock *sock;	/* socket used for geneve tunnel */
> > +	struct geneve_sock *sock4;	/* IPv4 socket used for geneve tunnel */
> > +	struct geneve_sock *sock6;	/* IPv6 socket used for geneve tunnel */
> >  	u8                 vni[3];	/* virtual network ID for tunnel */
> >  	u8                 ttl;		/* TTL override */
> >  	u8                 tos;		/* TOS override */
> > -	struct sockaddr_in remote;	/* IPv4 address for link partner */
> > +	union geneve_addr  remote;	/* IP address for link partner */
> >  	struct list_head   next;	/* geneve's per namespace list */
> >  	__be16		   dst_port;
> >  	bool		   collect_md;
> > @@ -103,11 +112,32 @@ static struct geneve_dev *geneve_lookup(struct geneve_sock *gs,
> >  	vni_list_head = &gs->vni_list[hash];
> >  	hlist_for_each_entry_rcu(geneve, vni_list_head, hlist) {
> >  		if (!memcmp(vni, geneve->vni, sizeof(geneve->vni)) &&
> > -		    addr == geneve->remote.sin_addr.s_addr)
> > +		    addr == geneve->remote.sin.sin_addr.s_addr)
> > +			return geneve;
> > +	}
> > +	return NULL;
> > +}
> > +
> > +#if IS_ENABLED(CONFIG_IPV6)
> > +static struct geneve_dev *geneve6_lookup(struct geneve_sock *gs,
> > +					 struct in6_addr addr6, u8 vni[])
> > +{
> > +	struct hlist_head *vni_list_head;
> > +	struct geneve_dev *geneve;
> > +	__u32 hash;
> > +
> > +	/* Find the device for this VNI */
> > +	hash = geneve_net_vni_hash(vni);
> > +	vni_list_head = &gs->vni_list[hash];
> > +	hlist_for_each_entry_rcu(geneve, vni_list_head, hlist) {
> > +		if (!memcmp(vni, geneve->vni, sizeof(geneve->vni)) &&
> > +		    !memcmp(&addr6, &geneve->remote.sin6.sin6_addr,
> > +			    sizeof(addr6)))
> 
> Please use ipv6_addr_equal().

Sure, no problem.

> How do you handle link-local addresses here?

Hmmmm...TBH, I had completely overlooked link-local addresses.
Do you have any suggestions for how to address this shortcoming?

Thanks,

John
-- 
John W. Linville		Someday the world will need a hero, and you
linville@tuxdriver.com			might be all we have.  Be ready.

^ permalink raw reply	[flat|nested] 37+ messages in thread

* [PATCH v5 1/2] geneve: implement support for IPv6-based tunnels
  2015-10-20 15:11     ` [PATCH v4 1/2] " John W. Linville
                         ` (3 preceding siblings ...)
  2015-10-21  5:06       ` Jesse Gross
@ 2015-10-22 19:45       ` John W. Linville
  2015-10-22 19:45         ` [PATCH v5 2/2] geneve: handle ipv6 priority like ipv4 tos John W. Linville
                           ` (2 more replies)
  4 siblings, 3 replies; 37+ messages in thread
From: John W. Linville @ 2015-10-22 19:45 UTC (permalink / raw)
  To: netdev
  Cc: Dave Miller, Pravin B Shelar, Jesse Gross, Jiri Benc,
	YOSHIFUJI Hideaki/吉藤英明,
	John W. Linville

NOTE: Link-local IPv6 addresses for remote endpoints are not supported,
since the driver currently has no capacity for binding a geneve
interface to a specific link.

Signed-off-by: John W. Linville <linville@tuxdriver.com>
---
v5:
- wrap declaration of sock6 in geneve_dev with IS_ENABLED(CONFIG_IPV6)
- remove superfluous '!!' when assigning geneve->collect_md to bool
- use skb_scrub_packet in IPv4 tx path as well 
- check for NULL ip_tunnel_info pointer in geneve[6]_xmit_skb
- use ipv6_addr_equal for comparing IPv6 addresses
- more use of IS_ENABLED(CONFIG_IPV6) for preserving build integrity
- reject link-local ipv6 address for remote tunnel endpoint

v4:
- treat mode field of ip_tunnel_info as flags
- add a missing IS_ENABLED(CONFIG_IPV6) to geneve_rx
- remove unneeded flags field in geneve_dev
- NULL-check parameter for __geneve_sock_release
- check remote socket family for AF_UNSPEC in geneve_configure
- rename geneve_get_{rt,dst} as geneve_get_{v4_rt,v6_dst}
- refactor some error handling in the xmit paths

v3:
- declare geneve_remote_unspec as static

v2:
- do not require remote address for tx on metadata tunnels
- pass correct sockaddr family to udp_tun_rx_dst in geneve_rx
- accommodate both ipv4 and ipv6 sockets open on same tunnel
- move declaration of geneve_get_dst for aesthetic purposes

 drivers/net/geneve.c         | 482 +++++++++++++++++++++++++++++++++++--------
 include/uapi/linux/if_link.h |   1 +
 2 files changed, 395 insertions(+), 88 deletions(-)

diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c
index cde29f8a37bf..47f7512f02cf 100644
--- a/drivers/net/geneve.c
+++ b/drivers/net/geneve.c
@@ -46,16 +46,27 @@ struct geneve_net {
 
 static int geneve_net_id;
 
+union geneve_addr {
+	struct sockaddr_in sin;
+	struct sockaddr_in6 sin6;
+	struct sockaddr sa;
+};
+
+static union geneve_addr geneve_remote_unspec = { .sa.sa_family = AF_UNSPEC, };
+
 /* Pseudo network device */
 struct geneve_dev {
 	struct hlist_node  hlist;	/* vni hash table */
 	struct net	   *net;	/* netns for packet i/o */
 	struct net_device  *dev;	/* netdev for geneve tunnel */
-	struct geneve_sock *sock;	/* socket used for geneve tunnel */
+	struct geneve_sock *sock4;	/* IPv4 socket used for geneve tunnel */
+#if IS_ENABLED(CONFIG_IPV6)
+	struct geneve_sock *sock6;	/* IPv6 socket used for geneve tunnel */
+#endif
 	u8                 vni[3];	/* virtual network ID for tunnel */
 	u8                 ttl;		/* TTL override */
 	u8                 tos;		/* TOS override */
-	struct sockaddr_in remote;	/* IPv4 address for link partner */
+	union geneve_addr  remote;	/* IP address for link partner */
 	struct list_head   next;	/* geneve's per namespace list */
 	__be16		   dst_port;
 	bool		   collect_md;
@@ -103,12 +114,32 @@ static struct geneve_dev *geneve_lookup(struct geneve_sock *gs,
 	vni_list_head = &gs->vni_list[hash];
 	hlist_for_each_entry_rcu(geneve, vni_list_head, hlist) {
 		if (!memcmp(vni, geneve->vni, sizeof(geneve->vni)) &&
-		    addr == geneve->remote.sin_addr.s_addr)
+		    addr == geneve->remote.sin.sin_addr.s_addr)
 			return geneve;
 	}
 	return NULL;
 }
 
+#if IS_ENABLED(CONFIG_IPV6)
+static struct geneve_dev *geneve6_lookup(struct geneve_sock *gs,
+					 struct in6_addr addr6, u8 vni[])
+{
+	struct hlist_head *vni_list_head;
+	struct geneve_dev *geneve;
+	__u32 hash;
+
+	/* Find the device for this VNI */
+	hash = geneve_net_vni_hash(vni);
+	vni_list_head = &gs->vni_list[hash];
+	hlist_for_each_entry_rcu(geneve, vni_list_head, hlist) {
+		if (!memcmp(vni, geneve->vni, sizeof(geneve->vni)) &&
+		    ipv6_addr_equal(&addr6, &geneve->remote.sin6.sin6_addr))
+			return geneve;
+	}
+	return NULL;
+}
+#endif
+
 static inline struct genevehdr *geneve_hdr(const struct sk_buff *skb)
 {
 	return (struct genevehdr *)(udp_hdr(skb) + 1);
@@ -121,24 +152,49 @@ static void geneve_rx(struct geneve_sock *gs, struct sk_buff *skb)
 	struct metadata_dst *tun_dst = NULL;
 	struct geneve_dev *geneve = NULL;
 	struct pcpu_sw_netstats *stats;
-	struct iphdr *iph;
-	u8 *vni;
+	struct iphdr *iph = NULL;
 	__be32 addr;
-	int err;
+	static u8 zero_vni[3];
+	u8 *vni;
+	int err = 0;
+	sa_family_t sa_family;
+#if IS_ENABLED(CONFIG_IPV6)
+	struct ipv6hdr *ip6h = NULL;
+	struct in6_addr addr6;
+	static struct in6_addr zero_addr6;
+#endif
+
+	sa_family = gs->sock->sk->sk_family;
 
-	iph = ip_hdr(skb); /* outer IP header... */
+	if (sa_family == AF_INET) {
+		iph = ip_hdr(skb); /* outer IP header... */
 
-	if (gs->collect_md) {
-		static u8 zero_vni[3];
+		if (gs->collect_md) {
+			vni = zero_vni;
+			addr = 0;
+		} else {
+			vni = gnvh->vni;
 
-		vni = zero_vni;
-		addr = 0;
-	} else {
-		vni = gnvh->vni;
-		addr = iph->saddr;
-	}
+			addr = iph->saddr;
+		}
 
-	geneve = geneve_lookup(gs, addr, vni);
+		geneve = geneve_lookup(gs, addr, vni);
+#if IS_ENABLED(CONFIG_IPV6)
+	} else if (sa_family == AF_INET6) {
+		ip6h = ipv6_hdr(skb); /* outer IPv6 header... */
+
+		if (gs->collect_md) {
+			vni = zero_vni;
+			addr6 = zero_addr6;
+		} else {
+			vni = gnvh->vni;
+
+			addr6 = ip6h->saddr;
+		}
+
+		geneve = geneve6_lookup(gs, addr6, vni);
+#endif
+	}
 	if (!geneve)
 		goto drop;
 
@@ -149,7 +205,7 @@ static void geneve_rx(struct geneve_sock *gs, struct sk_buff *skb)
 			(gnvh->oam ? TUNNEL_OAM : 0) |
 			(gnvh->critical ? TUNNEL_CRIT_OPT : 0);
 
-		tun_dst = udp_tun_rx_dst(skb, AF_INET, flags,
+		tun_dst = udp_tun_rx_dst(skb, sa_family, flags,
 					 vni_to_tunnel_id(gnvh->vni),
 					 gnvh->opt_len * 4);
 		if (!tun_dst)
@@ -179,12 +235,25 @@ static void geneve_rx(struct geneve_sock *gs, struct sk_buff *skb)
 
 	skb_reset_network_header(skb);
 
-	err = IP_ECN_decapsulate(iph, skb);
+	if (iph)
+		err = IP_ECN_decapsulate(iph, skb);
+#if IS_ENABLED(CONFIG_IPV6)
+	if (ip6h)
+		err = IP6_ECN_decapsulate(ip6h, skb);
+#endif
 
 	if (unlikely(err)) {
-		if (log_ecn_error)
-			net_info_ratelimited("non-ECT from %pI4 with TOS=%#x\n",
-					     &iph->saddr, iph->tos);
+		if (log_ecn_error) {
+			if (iph)
+				net_info_ratelimited("non-ECT from %pI4 "
+						     "with TOS=%#x\n",
+						     &iph->saddr, iph->tos);
+#if IS_ENABLED(CONFIG_IPV6)
+			if (ip6h)
+				net_info_ratelimited("non-ECT from %pI6\n",
+						     &ip6h->saddr);
+#endif
+		}
 		if (err > 1) {
 			++geneve->dev->stats.rx_frame_errors;
 			++geneve->dev->stats.rx_errors;
@@ -284,6 +353,7 @@ static struct socket *geneve_create_sock(struct net *net, bool ipv6,
 
 	if (ipv6) {
 		udp_conf.family = AF_INET6;
+		udp_conf.ipv6_v6only = 1;
 	} else {
 		udp_conf.family = AF_INET;
 		udp_conf.local_ip.s_addr = htonl(INADDR_ANY);
@@ -458,9 +528,9 @@ static void geneve_notify_del_rx_port(struct geneve_sock *gs)
 		udp_del_offload(&gs->udp_offloads);
 }
 
-static void geneve_sock_release(struct geneve_sock *gs)
+static void __geneve_sock_release(struct geneve_sock *gs)
 {
-	if (--gs->refcnt)
+	if (!gs || --gs->refcnt)
 		return;
 
 	list_del(&gs->list);
@@ -469,66 +539,117 @@ static void geneve_sock_release(struct geneve_sock *gs)
 	kfree_rcu(gs, rcu);
 }
 
+static void geneve_sock_release(struct geneve_dev *geneve)
+{
+	__geneve_sock_release(geneve->sock4);
+#if IS_ENABLED(CONFIG_IPV6)
+	__geneve_sock_release(geneve->sock6);
+#endif
+}
+
 static struct geneve_sock *geneve_find_sock(struct geneve_net *gn,
+					    sa_family_t family,
 					    __be16 dst_port)
 {
 	struct geneve_sock *gs;
 
 	list_for_each_entry(gs, &gn->sock_list, list) {
 		if (inet_sk(gs->sock->sk)->inet_sport == dst_port &&
-		    inet_sk(gs->sock->sk)->sk.sk_family == AF_INET) {
+		    inet_sk(gs->sock->sk)->sk.sk_family == family) {
 			return gs;
 		}
 	}
 	return NULL;
 }
 
-static int geneve_open(struct net_device *dev)
+static int geneve_sock_add(struct geneve_dev *geneve, bool ipv6)
 {
-	struct geneve_dev *geneve = netdev_priv(dev);
 	struct net *net = geneve->net;
 	struct geneve_net *gn = net_generic(net, geneve_net_id);
 	struct geneve_sock *gs;
 	__u32 hash;
 
-	gs = geneve_find_sock(gn, geneve->dst_port);
+	gs = geneve_find_sock(gn, ipv6 ? AF_INET6 : AF_INET, geneve->dst_port);
 	if (gs) {
 		gs->refcnt++;
 		goto out;
 	}
 
-	gs = geneve_socket_create(net, geneve->dst_port, false);
+	gs = geneve_socket_create(net, geneve->dst_port, ipv6);
 	if (IS_ERR(gs))
 		return PTR_ERR(gs);
 
 out:
 	gs->collect_md = geneve->collect_md;
-	geneve->sock = gs;
+#if IS_ENABLED(CONFIG_IPV6)
+	if (ipv6)
+		geneve->sock6 = gs;
+	else
+#endif
+		geneve->sock4 = gs;
 
 	hash = geneve_net_vni_hash(geneve->vni);
 	hlist_add_head_rcu(&geneve->hlist, &gs->vni_list[hash]);
 	return 0;
 }
 
+static int geneve_open(struct net_device *dev)
+{
+	struct geneve_dev *geneve = netdev_priv(dev);
+	bool ipv6 = geneve->remote.sa.sa_family == AF_INET6;
+	bool metadata = geneve->collect_md;
+	int ret = 0;
+
+	geneve->sock4 = NULL;
+#if IS_ENABLED(CONFIG_IPV6)
+	geneve->sock6 = NULL;
+	if (ipv6 || metadata)
+		ret = geneve_sock_add(geneve, true);
+#endif
+	if (!ret && (!ipv6 || metadata))
+		ret = geneve_sock_add(geneve, false);
+	if (ret < 0)
+		geneve_sock_release(geneve);
+
+	return ret;
+}
+
 static int geneve_stop(struct net_device *dev)
 {
 	struct geneve_dev *geneve = netdev_priv(dev);
-	struct geneve_sock *gs = geneve->sock;
 
 	if (!hlist_unhashed(&geneve->hlist))
 		hlist_del_rcu(&geneve->hlist);
-	geneve_sock_release(gs);
+	geneve_sock_release(geneve);
 	return 0;
 }
 
+static void geneve_build_header(struct genevehdr *geneveh,
+				__be16 tun_flags, u8 vni[3],
+				u8 options_len, u8 *options)
+{
+	geneveh->ver = GENEVE_VER;
+	geneveh->opt_len = options_len / 4;
+	geneveh->oam = !!(tun_flags & TUNNEL_OAM);
+	geneveh->critical = !!(tun_flags & TUNNEL_CRIT_OPT);
+	geneveh->rsvd1 = 0;
+	memcpy(geneveh->vni, vni, 3);
+	geneveh->proto_type = htons(ETH_P_TEB);
+	geneveh->rsvd2 = 0;
+
+	memcpy(geneveh->options, options, options_len);
+}
+
 static int geneve_build_skb(struct rtable *rt, struct sk_buff *skb,
 			    __be16 tun_flags, u8 vni[3], u8 opt_len, u8 *opt,
-			    bool csum)
+			    bool csum, bool xnet)
 {
 	struct genevehdr *gnvh;
 	int min_headroom;
 	int err;
 
+	skb_scrub_packet(skb, xnet);
+
 	min_headroom = LL_RESERVED_SPACE(rt->dst.dev) + rt->dst.header_len
 			+ GENEVE_BASE_HLEN + opt_len + sizeof(struct iphdr);
 	err = skb_cow_head(skb, min_headroom);
@@ -544,15 +665,7 @@ static int geneve_build_skb(struct rtable *rt, struct sk_buff *skb,
 	}
 
 	gnvh = (struct genevehdr *)__skb_push(skb, sizeof(*gnvh) + opt_len);
-	gnvh->ver = GENEVE_VER;
-	gnvh->opt_len = opt_len / 4;
-	gnvh->oam = !!(tun_flags & TUNNEL_OAM);
-	gnvh->critical = !!(tun_flags & TUNNEL_CRIT_OPT);
-	gnvh->rsvd1 = 0;
-	memcpy(gnvh->vni, vni, 3);
-	gnvh->proto_type = htons(ETH_P_TEB);
-	gnvh->rsvd2 = 0;
-	memcpy(gnvh->options, opt, opt_len);
+	geneve_build_header(gnvh, tun_flags, vni, opt_len, opt);
 
 	skb_set_inner_protocol(skb, htons(ETH_P_TEB));
 	return 0;
@@ -562,10 +675,47 @@ free_rt:
 	return err;
 }
 
-static struct rtable *geneve_get_rt(struct sk_buff *skb,
-				    struct net_device *dev,
-				    struct flowi4 *fl4,
-				    struct ip_tunnel_info *info)
+#if IS_ENABLED(CONFIG_IPV6)
+static int geneve6_build_skb(struct dst_entry *dst, struct sk_buff *skb,
+			     __be16 tun_flags, u8 vni[3], u8 opt_len, u8 *opt,
+			     bool csum, bool xnet)
+{
+	struct genevehdr *gnvh;
+	int min_headroom;
+	int err;
+
+	skb_scrub_packet(skb, xnet);
+
+	min_headroom = LL_RESERVED_SPACE(dst->dev) + dst->header_len
+			+ GENEVE_BASE_HLEN + opt_len + sizeof(struct ipv6hdr);
+	err = skb_cow_head(skb, min_headroom);
+	if (unlikely(err)) {
+		kfree_skb(skb);
+		goto free_dst;
+	}
+
+	skb = udp_tunnel_handle_offloads(skb, csum);
+	if (IS_ERR(skb)) {
+		err = PTR_ERR(skb);
+		goto free_dst;
+	}
+
+	gnvh = (struct genevehdr *)__skb_push(skb, sizeof(*gnvh) + opt_len);
+	geneve_build_header(gnvh, tun_flags, vni, opt_len, opt);
+
+	skb_set_inner_protocol(skb, htons(ETH_P_TEB));
+	return 0;
+
+free_dst:
+	dst_release(dst);
+	return err;
+}
+#endif
+
+static struct rtable *geneve_get_v4_rt(struct sk_buff *skb,
+				       struct net_device *dev,
+				       struct flowi4 *fl4,
+				       struct ip_tunnel_info *info)
 {
 	struct geneve_dev *geneve = netdev_priv(dev);
 	struct rtable *rt = NULL;
@@ -588,24 +738,42 @@ static struct rtable *geneve_get_rt(struct sk_buff *skb,
 		}
 
 		fl4->flowi4_tos = RT_TOS(tos);
-		fl4->daddr = geneve->remote.sin_addr.s_addr;
+		fl4->daddr = geneve->remote.sin.sin_addr.s_addr;
 	}
 
 	rt = ip_route_output_key(geneve->net, fl4);
-	if (IS_ERR(rt)) {
-		netdev_dbg(dev, "no route to %pI4\n", &fl4->daddr);
-		dev->stats.tx_carrier_errors++;
-		return rt;
-	}
-	if (rt->dst.dev == dev) { /* is this necessary? */
-		netdev_dbg(dev, "circular route to %pI4\n", &fl4->daddr);
-		dev->stats.collisions++;
-		ip_rt_put(rt);
-		return ERR_PTR(-EINVAL);
-	}
+
 	return rt;
 }
 
+#if IS_ENABLED(CONFIG_IPV6)
+static struct dst_entry *geneve_get_v6_dst(struct sk_buff *skb,
+					   struct net_device *dev,
+					   struct flowi6 *fl6,
+					   struct ip_tunnel_info *info)
+{
+	struct geneve_dev *geneve = netdev_priv(dev);
+	struct geneve_sock *gs6 = geneve->sock6;
+	struct dst_entry *dst = NULL;
+
+	memset(fl6, 0, sizeof(*fl6));
+	fl6->flowi6_mark = skb->mark;
+	fl6->flowi6_proto = IPPROTO_UDP;
+
+	if (info) {
+		fl6->daddr = info->key.u.ipv6.dst;
+		fl6->saddr = info->key.u.ipv6.src;
+	} else {
+		fl6->daddr = geneve->remote.sin6.sin6_addr;
+	}
+
+	if (ipv6_stub->ipv6_dst_lookup(geneve->net, gs6->sock->sk, &dst, fl6))
+		return ERR_PTR(-EHOSTUNREACH);
+
+	return dst;
+}
+#endif
+
 /* Convert 64 bit tunnel ID to 24 bit VNI. */
 static void tunnel_id_to_vni(__be64 tun_id, __u8 *vni)
 {
@@ -620,11 +788,11 @@ static void tunnel_id_to_vni(__be64 tun_id, __u8 *vni)
 #endif
 }
 
-static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev,
+				   struct ip_tunnel_info *info)
 {
 	struct geneve_dev *geneve = netdev_priv(dev);
-	struct geneve_sock *gs = geneve->sock;
-	struct ip_tunnel_info *info = NULL;
+	struct geneve_sock *gs4 = geneve->sock4;
 	struct rtable *rt = NULL;
 	const struct iphdr *iip; /* interior IP header */
 	struct flowi4 fl4;
@@ -633,10 +801,10 @@ static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
 	bool udp_csum;
 	__be16 df;
 	int err;
+	bool xnet = !net_eq(geneve->net, dev_net(geneve->dev));
 
 	if (geneve->collect_md) {
-		info = skb_tunnel_info(skb);
-		if (unlikely(info && !(info->mode & IP_TUNNEL_INFO_TX))) {
+		if (unlikely(!info || !(info->mode & IP_TUNNEL_INFO_TX))) {
 			netdev_dbg(dev, "no tunnel metadata\n");
 			goto tx_error;
 		}
@@ -644,12 +812,18 @@ static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
 			goto tx_error;
 	}
 
-	rt = geneve_get_rt(skb, dev, &fl4, info);
+	rt = geneve_get_v4_rt(skb, dev, &fl4, info);
 	if (IS_ERR(rt)) {
 		netdev_dbg(dev, "no route to %pI4\n", &fl4.daddr);
 		dev->stats.tx_carrier_errors++;
 		goto tx_error;
 	}
+	if (rt->dst.dev == dev) { /* is this necessary? */
+		netdev_dbg(dev, "circular route to %pI4\n", &fl4.daddr);
+		dev->stats.collisions++;
+		ip_rt_put(rt);
+		goto tx_error;
+	}
 
 	sport = udp_flow_src_port(geneve->net, skb, 1, USHRT_MAX, true);
 	skb_reset_mac_header(skb);
@@ -667,7 +841,7 @@ static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
 
 		udp_csum = !!(key->tun_flags & TUNNEL_CSUM);
 		err = geneve_build_skb(rt, skb, key->tun_flags, vni,
-				       info->options_len, opts, udp_csum);
+				       info->options_len, opts, udp_csum, xnet);
 		if (unlikely(err))
 			goto err;
 
@@ -677,7 +851,7 @@ static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
 	} else {
 		udp_csum = false;
 		err = geneve_build_skb(rt, skb, 0, geneve->vni,
-				       0, NULL, udp_csum);
+				       0, NULL, udp_csum, xnet);
 		if (unlikely(err))
 			goto err;
 
@@ -688,7 +862,7 @@ static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
 		ttl = ttl ? : ip4_dst_hoplimit(&rt->dst);
 		df = 0;
 	}
-	err = udp_tunnel_xmit_skb(rt, gs->sock->sk, skb, fl4.saddr, fl4.daddr,
+	err = udp_tunnel_xmit_skb(rt, gs4->sock->sk, skb, fl4.saddr, fl4.daddr,
 				  tos, ttl, df, sport, geneve->dst_port,
 				  !net_eq(geneve->net, dev_net(geneve->dev)),
 				  !udp_csum);
@@ -703,6 +877,103 @@ err:
 	return NETDEV_TX_OK;
 }
 
+#if IS_ENABLED(CONFIG_IPV6)
+static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
+				    struct ip_tunnel_info *info)
+{
+	struct geneve_dev *geneve = netdev_priv(dev);
+	struct geneve_sock *gs6 = geneve->sock6;
+	struct dst_entry *dst = NULL;
+	struct flowi6 fl6;
+	__u8 ttl;
+	__be16 sport;
+	bool udp_csum;
+	int err;
+	bool xnet = !net_eq(geneve->net, dev_net(geneve->dev));
+
+	if (geneve->collect_md) {
+		if (unlikely(!info || !(info->mode & IP_TUNNEL_INFO_TX))) {
+			netdev_dbg(dev, "no tunnel metadata\n");
+			goto tx_error;
+		}
+	}
+
+	dst = geneve_get_v6_dst(skb, dev, &fl6, info);
+	if (IS_ERR(dst)) {
+		netdev_dbg(dev, "no route to %pI6\n", &fl6.daddr);
+		dev->stats.tx_carrier_errors++;
+		goto tx_error;
+	}
+	if (dst->dev == dev) { /* is this necessary? */
+		netdev_dbg(dev, "circular route to %pI6\n", &fl6.daddr);
+		dev->stats.collisions++;
+		dst_release(dst);
+		goto tx_error;
+	}
+
+	sport = udp_flow_src_port(geneve->net, skb, 1, USHRT_MAX, true);
+	skb_reset_mac_header(skb);
+
+	if (info) {
+		const struct ip_tunnel_key *key = &info->key;
+		u8 *opts = NULL;
+		u8 vni[3];
+
+		tunnel_id_to_vni(key->tun_id, vni);
+		if (key->tun_flags & TUNNEL_GENEVE_OPT)
+			opts = ip_tunnel_info_opts(info);
+
+		udp_csum = !!(key->tun_flags & TUNNEL_CSUM);
+		err = geneve6_build_skb(dst, skb, key->tun_flags, vni,
+					info->options_len, opts,
+					udp_csum, xnet);
+		if (unlikely(err))
+			goto err;
+
+		ttl = key->ttl;
+	} else {
+		udp_csum = false;
+		err = geneve6_build_skb(dst, skb, 0, geneve->vni,
+					0, NULL, udp_csum, xnet);
+		if (unlikely(err))
+			goto err;
+
+		ttl = geneve->ttl;
+		if (!ttl && ipv6_addr_is_multicast(&fl6.daddr))
+			ttl = 1;
+		ttl = ttl ? : ip6_dst_hoplimit(dst);
+	}
+	err = udp_tunnel6_xmit_skb(dst, gs6->sock->sk, skb, dev,
+				   &fl6.saddr, &fl6.daddr, 0, ttl,
+				   sport, geneve->dst_port, !udp_csum);
+
+	iptunnel_xmit_stats(err, &dev->stats, dev->tstats);
+	return NETDEV_TX_OK;
+
+tx_error:
+	dev_kfree_skb(skb);
+err:
+	dev->stats.tx_errors++;
+	return NETDEV_TX_OK;
+}
+#endif
+
+static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct geneve_dev *geneve = netdev_priv(dev);
+	struct ip_tunnel_info *info = NULL;
+
+	if (geneve->collect_md)
+		info = skb_tunnel_info(skb);
+
+#if IS_ENABLED(CONFIG_IPV6)
+	if ((info && ip_tunnel_info_af(info) == AF_INET6) ||
+	    (!info && geneve->remote.sa.sa_family == AF_INET6))
+		return geneve6_xmit_skb(skb, dev, info);
+#endif
+	return geneve_xmit_skb(skb, dev, info);
+}
+
 static const struct net_device_ops geneve_netdev_ops = {
 	.ndo_init		= geneve_init,
 	.ndo_uninit		= geneve_uninit,
@@ -759,6 +1030,7 @@ static void geneve_setup(struct net_device *dev)
 static const struct nla_policy geneve_policy[IFLA_GENEVE_MAX + 1] = {
 	[IFLA_GENEVE_ID]		= { .type = NLA_U32 },
 	[IFLA_GENEVE_REMOTE]		= { .len = FIELD_SIZEOF(struct iphdr, daddr) },
+	[IFLA_GENEVE_REMOTE6]		= { .len = sizeof(struct in6_addr) },
 	[IFLA_GENEVE_TTL]		= { .type = NLA_U8 },
 	[IFLA_GENEVE_TOS]		= { .type = NLA_U8 },
 	[IFLA_GENEVE_PORT]		= { .type = NLA_U16 },
@@ -790,7 +1062,7 @@ static int geneve_validate(struct nlattr *tb[], struct nlattr *data[])
 
 static struct geneve_dev *geneve_find_dev(struct geneve_net *gn,
 					  __be16 dst_port,
-					  __be32 rem_addr,
+					  union geneve_addr *remote,
 					  u8 vni[],
 					  bool *tun_on_same_port,
 					  bool *tun_collect_md)
@@ -806,7 +1078,7 @@ static struct geneve_dev *geneve_find_dev(struct geneve_net *gn,
 			*tun_on_same_port = true;
 		}
 		if (!memcmp(vni, geneve->vni, sizeof(geneve->vni)) &&
-		    rem_addr == geneve->remote.sin_addr.s_addr &&
+		    !memcmp(remote, &geneve->remote, sizeof(geneve->remote)) &&
 		    dst_port == geneve->dst_port)
 			t = geneve;
 	}
@@ -814,18 +1086,20 @@ static struct geneve_dev *geneve_find_dev(struct geneve_net *gn,
 }
 
 static int geneve_configure(struct net *net, struct net_device *dev,
-			    __be32 rem_addr, __u32 vni, __u8 ttl, __u8 tos,
-			    __be16 dst_port, bool metadata)
+			    union geneve_addr *remote,
+			    __u32 vni, __u8 ttl, __u8 tos, __be16 dst_port,
+			    bool metadata)
 {
 	struct geneve_net *gn = net_generic(net, geneve_net_id);
 	struct geneve_dev *t, *geneve = netdev_priv(dev);
 	bool tun_collect_md, tun_on_same_port;
 	int err;
 
-	if (metadata) {
-		if (rem_addr || vni || tos || ttl)
-			return -EINVAL;
-	}
+	if (!remote)
+		return -EINVAL;
+	if (metadata &&
+	    (remote->sa.sa_family != AF_UNSPEC || vni || tos || ttl))
+		return -EINVAL;
 
 	geneve->net = net;
 	geneve->dev = dev;
@@ -834,16 +1108,19 @@ static int geneve_configure(struct net *net, struct net_device *dev,
 	geneve->vni[1] = (vni & 0x0000ff00) >> 8;
 	geneve->vni[2] =  vni & 0x000000ff;
 
-	geneve->remote.sin_addr.s_addr = rem_addr;
-	if (IN_MULTICAST(ntohl(geneve->remote.sin_addr.s_addr)))
+	if ((remote->sa.sa_family == AF_INET &&
+	     IN_MULTICAST(ntohl(remote->sin.sin_addr.s_addr))) ||
+	    (remote->sa.sa_family == AF_INET6 &&
+	     ipv6_addr_is_multicast(&remote->sin6.sin6_addr)))
 		return -EINVAL;
+	geneve->remote = *remote;
 
 	geneve->ttl = ttl;
 	geneve->tos = tos;
 	geneve->dst_port = dst_port;
 	geneve->collect_md = metadata;
 
-	t = geneve_find_dev(gn, dst_port, rem_addr, geneve->vni,
+	t = geneve_find_dev(gn, dst_port, remote, geneve->vni,
 			    &tun_on_same_port, &tun_collect_md);
 	if (t)
 		return -EBUSY;
@@ -870,15 +1147,35 @@ static int geneve_newlink(struct net *net, struct net_device *dev,
 	__be16 dst_port = htons(GENEVE_UDP_PORT);
 	__u8 ttl = 0, tos = 0;
 	bool metadata = false;
-	__be32 rem_addr = 0;
+	union geneve_addr remote = geneve_remote_unspec;
 	__u32 vni = 0;
 
+	if (data[IFLA_GENEVE_REMOTE] && data[IFLA_GENEVE_REMOTE6])
+		return -EINVAL;
+
+	if (data[IFLA_GENEVE_REMOTE]) {
+		remote.sa.sa_family = AF_INET;
+		remote.sin.sin_addr.s_addr =
+			nla_get_in_addr(data[IFLA_GENEVE_REMOTE]);
+	}
+
+	if (data[IFLA_GENEVE_REMOTE6]) {
+		if (!IS_ENABLED(CONFIG_IPV6))
+			return -EPFNOSUPPORT;
+
+		remote.sa.sa_family = AF_INET6;
+		remote.sin6.sin6_addr =
+			nla_get_in6_addr(data[IFLA_GENEVE_REMOTE6]);
+
+		if (ipv6_addr_type(&remote.sin6.sin6_addr) &
+		    IPV6_ADDR_LINKLOCAL)
+			netdev_dbg(dev, "link-local remote is unsupported\n");
+			return -EINVAL;
+	}
+
 	if (data[IFLA_GENEVE_ID])
 		vni = nla_get_u32(data[IFLA_GENEVE_ID]);
 
-	if (data[IFLA_GENEVE_REMOTE])
-		rem_addr = nla_get_in_addr(data[IFLA_GENEVE_REMOTE]);
-
 	if (data[IFLA_GENEVE_TTL])
 		ttl = nla_get_u8(data[IFLA_GENEVE_TTL]);
 
@@ -891,8 +1188,8 @@ static int geneve_newlink(struct net *net, struct net_device *dev,
 	if (data[IFLA_GENEVE_COLLECT_METADATA])
 		metadata = true;
 
-	return geneve_configure(net, dev, rem_addr, vni,
-				ttl, tos, dst_port, metadata);
+	return geneve_configure(net, dev, &remote, vni, ttl, tos, dst_port,
+				metadata);
 }
 
 static void geneve_dellink(struct net_device *dev, struct list_head *head)
@@ -906,7 +1203,7 @@ static void geneve_dellink(struct net_device *dev, struct list_head *head)
 static size_t geneve_get_size(const struct net_device *dev)
 {
 	return nla_total_size(sizeof(__u32)) +	/* IFLA_GENEVE_ID */
-		nla_total_size(sizeof(struct in_addr)) + /* IFLA_GENEVE_REMOTE */
+		nla_total_size(sizeof(struct in6_addr)) + /* IFLA_GENEVE_REMOTE{6} */
 		nla_total_size(sizeof(__u8)) +  /* IFLA_GENEVE_TTL */
 		nla_total_size(sizeof(__u8)) +  /* IFLA_GENEVE_TOS */
 		nla_total_size(sizeof(__be16)) +  /* IFLA_GENEVE_PORT */
@@ -923,9 +1220,17 @@ static int geneve_fill_info(struct sk_buff *skb, const struct net_device *dev)
 	if (nla_put_u32(skb, IFLA_GENEVE_ID, vni))
 		goto nla_put_failure;
 
-	if (nla_put_in_addr(skb, IFLA_GENEVE_REMOTE,
-			    geneve->remote.sin_addr.s_addr))
-		goto nla_put_failure;
+	if (geneve->remote.sa.sa_family == AF_INET) {
+		if (nla_put_in_addr(skb, IFLA_GENEVE_REMOTE,
+				    geneve->remote.sin.sin_addr.s_addr))
+			goto nla_put_failure;
+#if IS_ENABLED(CONFIG_IPV6)
+	} else {
+		if (nla_put_in6_addr(skb, IFLA_GENEVE_REMOTE6,
+				     &geneve->remote.sin6.sin6_addr))
+			goto nla_put_failure;
+#endif
+	}
 
 	if (nla_put_u8(skb, IFLA_GENEVE_TTL, geneve->ttl) ||
 	    nla_put_u8(skb, IFLA_GENEVE_TOS, geneve->tos))
@@ -971,7 +1276,8 @@ struct net_device *geneve_dev_create_fb(struct net *net, const char *name,
 	if (IS_ERR(dev))
 		return dev;
 
-	err = geneve_configure(net, dev, 0, 0, 0, 0, htons(dst_port), true);
+	err = geneve_configure(net, dev, &geneve_remote_unspec,
+			       0, 0, 0, htons(dst_port), true);
 	if (err) {
 		free_netdev(dev);
 		return ERR_PTR(err);
diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
index e3b6217f34f1..45e3a48550f9 100644
--- a/include/uapi/linux/if_link.h
+++ b/include/uapi/linux/if_link.h
@@ -461,6 +461,7 @@ enum {
 	IFLA_GENEVE_TOS,
 	IFLA_GENEVE_PORT,	/* destination port */
 	IFLA_GENEVE_COLLECT_METADATA,
+	IFLA_GENEVE_REMOTE6,
 	__IFLA_GENEVE_MAX
 };
 #define IFLA_GENEVE_MAX	(__IFLA_GENEVE_MAX - 1)
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v5 2/2] geneve: handle ipv6 priority like ipv4 tos
  2015-10-22 19:45       ` [PATCH v5 " John W. Linville
@ 2015-10-22 19:45         ` John W. Linville
  2015-10-23  4:48         ` [PATCH v5 1/2] geneve: implement support for IPv6-based tunnels YOSHIFUJI Hideaki
  2015-10-23 14:40         ` [PATCH v6 " John W. Linville
  2 siblings, 0 replies; 37+ messages in thread
From: John W. Linville @ 2015-10-22 19:45 UTC (permalink / raw)
  To: netdev
  Cc: Dave Miller, Pravin B Shelar, Jesse Gross, Jiri Benc,
	YOSHIFUJI Hideaki/吉藤英明,
	John W. Linville

Signed-off-by: John W. Linville <linville@tuxdriver.com>
Reported-by: Jesse Gross <jesse@nicira.com>
Reviewed-by: Jesse Gross <jesse@nicira.com>
---
v5 -- same as previous revision

 drivers/net/geneve.c | 19 +++++++++++++++++--
 1 file changed, 17 insertions(+), 2 deletions(-)

diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c
index 47f7512f02cf..4d4d8ca9eb7a 100644
--- a/drivers/net/geneve.c
+++ b/drivers/net/geneve.c
@@ -755,6 +755,7 @@ static struct dst_entry *geneve_get_v6_dst(struct sk_buff *skb,
 	struct geneve_dev *geneve = netdev_priv(dev);
 	struct geneve_sock *gs6 = geneve->sock6;
 	struct dst_entry *dst = NULL;
+	__u8 prio;
 
 	memset(fl6, 0, sizeof(*fl6));
 	fl6->flowi6_mark = skb->mark;
@@ -763,7 +764,16 @@ static struct dst_entry *geneve_get_v6_dst(struct sk_buff *skb,
 	if (info) {
 		fl6->daddr = info->key.u.ipv6.dst;
 		fl6->saddr = info->key.u.ipv6.src;
+		fl6->flowi6_tos = RT_TOS(info->key.tos);
 	} else {
+		prio = geneve->tos;
+		if (prio == 1) {
+			const struct iphdr *iip = ip_hdr(skb);
+
+			prio = ip_tunnel_get_dsfield(iip, skb);
+		}
+
+		fl6->flowi6_tos = RT_TOS(prio);
 		fl6->daddr = geneve->remote.sin6.sin6_addr;
 	}
 
@@ -884,8 +894,9 @@ static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
 	struct geneve_dev *geneve = netdev_priv(dev);
 	struct geneve_sock *gs6 = geneve->sock6;
 	struct dst_entry *dst = NULL;
+	const struct iphdr *iip; /* interior IP header */
 	struct flowi6 fl6;
-	__u8 ttl;
+	__u8 prio, ttl;
 	__be16 sport;
 	bool udp_csum;
 	int err;
@@ -914,6 +925,8 @@ static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
 	sport = udp_flow_src_port(geneve->net, skb, 1, USHRT_MAX, true);
 	skb_reset_mac_header(skb);
 
+	iip = ip_hdr(skb);
+
 	if (info) {
 		const struct ip_tunnel_key *key = &info->key;
 		u8 *opts = NULL;
@@ -930,6 +943,7 @@ static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
 		if (unlikely(err))
 			goto err;
 
+		prio = ip_tunnel_ecn_encap(key->tos, iip, skb);
 		ttl = key->ttl;
 	} else {
 		udp_csum = false;
@@ -938,13 +952,14 @@ static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
 		if (unlikely(err))
 			goto err;
 
+		prio = ip_tunnel_ecn_encap(fl6.flowi6_tos, iip, skb);
 		ttl = geneve->ttl;
 		if (!ttl && ipv6_addr_is_multicast(&fl6.daddr))
 			ttl = 1;
 		ttl = ttl ? : ip6_dst_hoplimit(dst);
 	}
 	err = udp_tunnel6_xmit_skb(dst, gs6->sock->sk, skb, dev,
-				   &fl6.saddr, &fl6.daddr, 0, ttl,
+				   &fl6.saddr, &fl6.daddr, prio, ttl,
 				   sport, geneve->dst_port, !udp_csum);
 
 	iptunnel_xmit_stats(err, &dev->stats, dev->tstats);
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* Re: [PATCH v5 1/2] geneve: implement support for IPv6-based tunnels
  2015-10-22 19:45       ` [PATCH v5 " John W. Linville
  2015-10-22 19:45         ` [PATCH v5 2/2] geneve: handle ipv6 priority like ipv4 tos John W. Linville
@ 2015-10-23  4:48         ` YOSHIFUJI Hideaki
  2015-10-23 13:38           ` John W. Linville
  2015-10-23 14:40         ` [PATCH v6 " John W. Linville
  2 siblings, 1 reply; 37+ messages in thread
From: YOSHIFUJI Hideaki @ 2015-10-23  4:48 UTC (permalink / raw)
  To: John W. Linville, netdev
  Cc: hideaki.yoshifuji, Dave Miller, Pravin B Shelar, Jesse Gross, Jiri Benc

Hi,

John W. Linville wrote:
> NOTE: Link-local IPv6 addresses for remote endpoints are not supported,
> since the driver currently has no capacity for binding a geneve
> interface to a specific link.
> 
> Signed-off-by: John W. Linville <linville@tuxdriver.com>
> ---
> v5:
> - wrap declaration of sock6 in geneve_dev with IS_ENABLED(CONFIG_IPV6)
> - remove superfluous '!!' when assigning geneve->collect_md to bool
> - use skb_scrub_packet in IPv4 tx path as well 
> - check for NULL ip_tunnel_info pointer in geneve[6]_xmit_skb
> - use ipv6_addr_equal for comparing IPv6 addresses
> - more use of IS_ENABLED(CONFIG_IPV6) for preserving build integrity
> - reject link-local ipv6 address for remote tunnel endpoint
:

> @@ -870,15 +1147,35 @@ static int geneve_newlink(struct net *net, struct net_device *dev,
:
> +
> +		if (ipv6_addr_type(&remote.sin6.sin6_addr) &
> +		    IPV6_ADDR_LINKLOCAL)
> +			netdev_dbg(dev, "link-local remote is unsupported\n");
> +			return -EINVAL;
> +	}
> +

This always returns -EINVAL; {} is missing.

-- 
Hideaki Yoshifuji <hideaki.yoshifuji@miraclelinux.com>
Technical Division, MIRACLE LINUX CORPORATION

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v5 1/2] geneve: implement support for IPv6-based tunnels
  2015-10-23  4:48         ` [PATCH v5 1/2] geneve: implement support for IPv6-based tunnels YOSHIFUJI Hideaki
@ 2015-10-23 13:38           ` John W. Linville
  0 siblings, 0 replies; 37+ messages in thread
From: John W. Linville @ 2015-10-23 13:38 UTC (permalink / raw)
  To: YOSHIFUJI Hideaki
  Cc: netdev, Dave Miller, Pravin B Shelar, Jesse Gross, Jiri Benc

On Fri, Oct 23, 2015 at 01:48:49PM +0900, YOSHIFUJI Hideaki wrote:
> Hi,
> 
> John W. Linville wrote:
> > NOTE: Link-local IPv6 addresses for remote endpoints are not supported,
> > since the driver currently has no capacity for binding a geneve
> > interface to a specific link.
> > 
> > Signed-off-by: John W. Linville <linville@tuxdriver.com>
> > ---
> > v5:
> > - wrap declaration of sock6 in geneve_dev with IS_ENABLED(CONFIG_IPV6)
> > - remove superfluous '!!' when assigning geneve->collect_md to bool
> > - use skb_scrub_packet in IPv4 tx path as well 
> > - check for NULL ip_tunnel_info pointer in geneve[6]_xmit_skb
> > - use ipv6_addr_equal for comparing IPv6 addresses
> > - more use of IS_ENABLED(CONFIG_IPV6) for preserving build integrity
> > - reject link-local ipv6 address for remote tunnel endpoint
> :
> 
> > @@ -870,15 +1147,35 @@ static int geneve_newlink(struct net *net, struct net_device *dev,
> :
> > +
> > +		if (ipv6_addr_type(&remote.sin6.sin6_addr) &
> > +		    IPV6_ADDR_LINKLOCAL)
> > +			netdev_dbg(dev, "link-local remote is unsupported\n");
> > +			return -EINVAL;
> > +	}
> > +
> 
> This always returns -EINVAL; {} is missing.

Yikes!  I posted the wrong patch...good eyes!

I will repost...

John
-- 
John W. Linville		Someday the world will need a hero, and you
linville@tuxdriver.com			might be all we have.  Be ready.

^ permalink raw reply	[flat|nested] 37+ messages in thread

* [PATCH v6 1/2] geneve: implement support for IPv6-based tunnels
  2015-10-22 19:45       ` [PATCH v5 " John W. Linville
  2015-10-22 19:45         ` [PATCH v5 2/2] geneve: handle ipv6 priority like ipv4 tos John W. Linville
  2015-10-23  4:48         ` [PATCH v5 1/2] geneve: implement support for IPv6-based tunnels YOSHIFUJI Hideaki
@ 2015-10-23 14:40         ` John W. Linville
  2015-10-23 14:40           ` [PATCH v6 2/2] geneve: handle ipv6 priority like ipv4 tos John W. Linville
                             ` (2 more replies)
  2 siblings, 3 replies; 37+ messages in thread
From: John W. Linville @ 2015-10-23 14:40 UTC (permalink / raw)
  To: netdev
  Cc: Dave Miller, Pravin B Shelar, Jesse Gross, Jiri Benc,
	YOSHIFUJI Hideaki/吉藤英明,
	John W. Linville

NOTE: Link-local IPv6 addresses for remote endpoints are not supported,
since the driver currently has no capacity for binding a geneve
interface to a specific link.

Signed-off-by: John W. Linville <linville@tuxdriver.com>
---
v6:
- fix a typo (missing {}'s)

v5:
- wrap declaration of sock6 in geneve_dev with IS_ENABLED(CONFIG_IPV6)
- remove superfluous '!!' when assigning geneve->collect_md to bool
- use skb_scrub_packet in IPv4 tx path as well 
- check for NULL ip_tunnel_info pointer in geneve[6]_xmit_skb
- use ipv6_addr_equal for comparing IPv6 addresses
- more use of IS_ENABLED(CONFIG_IPV6) for preserving build integrity
- reject link-local ipv6 address for remote tunnel endpoint

v4:
- treat mode field of ip_tunnel_info as flags
- add a missing IS_ENABLED(CONFIG_IPV6) to geneve_rx
- remove unneeded flags field in geneve_dev
- NULL-check parameter for __geneve_sock_release
- check remote socket family for AF_UNSPEC in geneve_configure
- rename geneve_get_{rt,dst} as geneve_get_{v4_rt,v6_dst}
- refactor some error handling in the xmit paths

v3:
- declare geneve_remote_unspec as static

v2:
- do not require remote address for tx on metadata tunnels
- pass correct sockaddr family to udp_tun_rx_dst in geneve_rx
- accommodate both ipv4 and ipv6 sockets open on same tunnel
- move declaration of geneve_get_dst for aesthetic purposes

 drivers/net/geneve.c         | 483 +++++++++++++++++++++++++++++++++++--------
 include/uapi/linux/if_link.h |   1 +
 2 files changed, 396 insertions(+), 88 deletions(-)

diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c
index cde29f8a37bf..46d4aa714893 100644
--- a/drivers/net/geneve.c
+++ b/drivers/net/geneve.c
@@ -46,16 +46,27 @@ struct geneve_net {
 
 static int geneve_net_id;
 
+union geneve_addr {
+	struct sockaddr_in sin;
+	struct sockaddr_in6 sin6;
+	struct sockaddr sa;
+};
+
+static union geneve_addr geneve_remote_unspec = { .sa.sa_family = AF_UNSPEC, };
+
 /* Pseudo network device */
 struct geneve_dev {
 	struct hlist_node  hlist;	/* vni hash table */
 	struct net	   *net;	/* netns for packet i/o */
 	struct net_device  *dev;	/* netdev for geneve tunnel */
-	struct geneve_sock *sock;	/* socket used for geneve tunnel */
+	struct geneve_sock *sock4;	/* IPv4 socket used for geneve tunnel */
+#if IS_ENABLED(CONFIG_IPV6)
+	struct geneve_sock *sock6;	/* IPv6 socket used for geneve tunnel */
+#endif
 	u8                 vni[3];	/* virtual network ID for tunnel */
 	u8                 ttl;		/* TTL override */
 	u8                 tos;		/* TOS override */
-	struct sockaddr_in remote;	/* IPv4 address for link partner */
+	union geneve_addr  remote;	/* IP address for link partner */
 	struct list_head   next;	/* geneve's per namespace list */
 	__be16		   dst_port;
 	bool		   collect_md;
@@ -103,12 +114,32 @@ static struct geneve_dev *geneve_lookup(struct geneve_sock *gs,
 	vni_list_head = &gs->vni_list[hash];
 	hlist_for_each_entry_rcu(geneve, vni_list_head, hlist) {
 		if (!memcmp(vni, geneve->vni, sizeof(geneve->vni)) &&
-		    addr == geneve->remote.sin_addr.s_addr)
+		    addr == geneve->remote.sin.sin_addr.s_addr)
 			return geneve;
 	}
 	return NULL;
 }
 
+#if IS_ENABLED(CONFIG_IPV6)
+static struct geneve_dev *geneve6_lookup(struct geneve_sock *gs,
+					 struct in6_addr addr6, u8 vni[])
+{
+	struct hlist_head *vni_list_head;
+	struct geneve_dev *geneve;
+	__u32 hash;
+
+	/* Find the device for this VNI */
+	hash = geneve_net_vni_hash(vni);
+	vni_list_head = &gs->vni_list[hash];
+	hlist_for_each_entry_rcu(geneve, vni_list_head, hlist) {
+		if (!memcmp(vni, geneve->vni, sizeof(geneve->vni)) &&
+		    ipv6_addr_equal(&addr6, &geneve->remote.sin6.sin6_addr))
+			return geneve;
+	}
+	return NULL;
+}
+#endif
+
 static inline struct genevehdr *geneve_hdr(const struct sk_buff *skb)
 {
 	return (struct genevehdr *)(udp_hdr(skb) + 1);
@@ -121,24 +152,49 @@ static void geneve_rx(struct geneve_sock *gs, struct sk_buff *skb)
 	struct metadata_dst *tun_dst = NULL;
 	struct geneve_dev *geneve = NULL;
 	struct pcpu_sw_netstats *stats;
-	struct iphdr *iph;
-	u8 *vni;
+	struct iphdr *iph = NULL;
 	__be32 addr;
-	int err;
+	static u8 zero_vni[3];
+	u8 *vni;
+	int err = 0;
+	sa_family_t sa_family;
+#if IS_ENABLED(CONFIG_IPV6)
+	struct ipv6hdr *ip6h = NULL;
+	struct in6_addr addr6;
+	static struct in6_addr zero_addr6;
+#endif
+
+	sa_family = gs->sock->sk->sk_family;
 
-	iph = ip_hdr(skb); /* outer IP header... */
+	if (sa_family == AF_INET) {
+		iph = ip_hdr(skb); /* outer IP header... */
 
-	if (gs->collect_md) {
-		static u8 zero_vni[3];
+		if (gs->collect_md) {
+			vni = zero_vni;
+			addr = 0;
+		} else {
+			vni = gnvh->vni;
 
-		vni = zero_vni;
-		addr = 0;
-	} else {
-		vni = gnvh->vni;
-		addr = iph->saddr;
-	}
+			addr = iph->saddr;
+		}
 
-	geneve = geneve_lookup(gs, addr, vni);
+		geneve = geneve_lookup(gs, addr, vni);
+#if IS_ENABLED(CONFIG_IPV6)
+	} else if (sa_family == AF_INET6) {
+		ip6h = ipv6_hdr(skb); /* outer IPv6 header... */
+
+		if (gs->collect_md) {
+			vni = zero_vni;
+			addr6 = zero_addr6;
+		} else {
+			vni = gnvh->vni;
+
+			addr6 = ip6h->saddr;
+		}
+
+		geneve = geneve6_lookup(gs, addr6, vni);
+#endif
+	}
 	if (!geneve)
 		goto drop;
 
@@ -149,7 +205,7 @@ static void geneve_rx(struct geneve_sock *gs, struct sk_buff *skb)
 			(gnvh->oam ? TUNNEL_OAM : 0) |
 			(gnvh->critical ? TUNNEL_CRIT_OPT : 0);
 
-		tun_dst = udp_tun_rx_dst(skb, AF_INET, flags,
+		tun_dst = udp_tun_rx_dst(skb, sa_family, flags,
 					 vni_to_tunnel_id(gnvh->vni),
 					 gnvh->opt_len * 4);
 		if (!tun_dst)
@@ -179,12 +235,25 @@ static void geneve_rx(struct geneve_sock *gs, struct sk_buff *skb)
 
 	skb_reset_network_header(skb);
 
-	err = IP_ECN_decapsulate(iph, skb);
+	if (iph)
+		err = IP_ECN_decapsulate(iph, skb);
+#if IS_ENABLED(CONFIG_IPV6)
+	if (ip6h)
+		err = IP6_ECN_decapsulate(ip6h, skb);
+#endif
 
 	if (unlikely(err)) {
-		if (log_ecn_error)
-			net_info_ratelimited("non-ECT from %pI4 with TOS=%#x\n",
-					     &iph->saddr, iph->tos);
+		if (log_ecn_error) {
+			if (iph)
+				net_info_ratelimited("non-ECT from %pI4 "
+						     "with TOS=%#x\n",
+						     &iph->saddr, iph->tos);
+#if IS_ENABLED(CONFIG_IPV6)
+			if (ip6h)
+				net_info_ratelimited("non-ECT from %pI6\n",
+						     &ip6h->saddr);
+#endif
+		}
 		if (err > 1) {
 			++geneve->dev->stats.rx_frame_errors;
 			++geneve->dev->stats.rx_errors;
@@ -284,6 +353,7 @@ static struct socket *geneve_create_sock(struct net *net, bool ipv6,
 
 	if (ipv6) {
 		udp_conf.family = AF_INET6;
+		udp_conf.ipv6_v6only = 1;
 	} else {
 		udp_conf.family = AF_INET;
 		udp_conf.local_ip.s_addr = htonl(INADDR_ANY);
@@ -458,9 +528,9 @@ static void geneve_notify_del_rx_port(struct geneve_sock *gs)
 		udp_del_offload(&gs->udp_offloads);
 }
 
-static void geneve_sock_release(struct geneve_sock *gs)
+static void __geneve_sock_release(struct geneve_sock *gs)
 {
-	if (--gs->refcnt)
+	if (!gs || --gs->refcnt)
 		return;
 
 	list_del(&gs->list);
@@ -469,66 +539,117 @@ static void geneve_sock_release(struct geneve_sock *gs)
 	kfree_rcu(gs, rcu);
 }
 
+static void geneve_sock_release(struct geneve_dev *geneve)
+{
+	__geneve_sock_release(geneve->sock4);
+#if IS_ENABLED(CONFIG_IPV6)
+	__geneve_sock_release(geneve->sock6);
+#endif
+}
+
 static struct geneve_sock *geneve_find_sock(struct geneve_net *gn,
+					    sa_family_t family,
 					    __be16 dst_port)
 {
 	struct geneve_sock *gs;
 
 	list_for_each_entry(gs, &gn->sock_list, list) {
 		if (inet_sk(gs->sock->sk)->inet_sport == dst_port &&
-		    inet_sk(gs->sock->sk)->sk.sk_family == AF_INET) {
+		    inet_sk(gs->sock->sk)->sk.sk_family == family) {
 			return gs;
 		}
 	}
 	return NULL;
 }
 
-static int geneve_open(struct net_device *dev)
+static int geneve_sock_add(struct geneve_dev *geneve, bool ipv6)
 {
-	struct geneve_dev *geneve = netdev_priv(dev);
 	struct net *net = geneve->net;
 	struct geneve_net *gn = net_generic(net, geneve_net_id);
 	struct geneve_sock *gs;
 	__u32 hash;
 
-	gs = geneve_find_sock(gn, geneve->dst_port);
+	gs = geneve_find_sock(gn, ipv6 ? AF_INET6 : AF_INET, geneve->dst_port);
 	if (gs) {
 		gs->refcnt++;
 		goto out;
 	}
 
-	gs = geneve_socket_create(net, geneve->dst_port, false);
+	gs = geneve_socket_create(net, geneve->dst_port, ipv6);
 	if (IS_ERR(gs))
 		return PTR_ERR(gs);
 
 out:
 	gs->collect_md = geneve->collect_md;
-	geneve->sock = gs;
+#if IS_ENABLED(CONFIG_IPV6)
+	if (ipv6)
+		geneve->sock6 = gs;
+	else
+#endif
+		geneve->sock4 = gs;
 
 	hash = geneve_net_vni_hash(geneve->vni);
 	hlist_add_head_rcu(&geneve->hlist, &gs->vni_list[hash]);
 	return 0;
 }
 
+static int geneve_open(struct net_device *dev)
+{
+	struct geneve_dev *geneve = netdev_priv(dev);
+	bool ipv6 = geneve->remote.sa.sa_family == AF_INET6;
+	bool metadata = geneve->collect_md;
+	int ret = 0;
+
+	geneve->sock4 = NULL;
+#if IS_ENABLED(CONFIG_IPV6)
+	geneve->sock6 = NULL;
+	if (ipv6 || metadata)
+		ret = geneve_sock_add(geneve, true);
+#endif
+	if (!ret && (!ipv6 || metadata))
+		ret = geneve_sock_add(geneve, false);
+	if (ret < 0)
+		geneve_sock_release(geneve);
+
+	return ret;
+}
+
 static int geneve_stop(struct net_device *dev)
 {
 	struct geneve_dev *geneve = netdev_priv(dev);
-	struct geneve_sock *gs = geneve->sock;
 
 	if (!hlist_unhashed(&geneve->hlist))
 		hlist_del_rcu(&geneve->hlist);
-	geneve_sock_release(gs);
+	geneve_sock_release(geneve);
 	return 0;
 }
 
+static void geneve_build_header(struct genevehdr *geneveh,
+				__be16 tun_flags, u8 vni[3],
+				u8 options_len, u8 *options)
+{
+	geneveh->ver = GENEVE_VER;
+	geneveh->opt_len = options_len / 4;
+	geneveh->oam = !!(tun_flags & TUNNEL_OAM);
+	geneveh->critical = !!(tun_flags & TUNNEL_CRIT_OPT);
+	geneveh->rsvd1 = 0;
+	memcpy(geneveh->vni, vni, 3);
+	geneveh->proto_type = htons(ETH_P_TEB);
+	geneveh->rsvd2 = 0;
+
+	memcpy(geneveh->options, options, options_len);
+}
+
 static int geneve_build_skb(struct rtable *rt, struct sk_buff *skb,
 			    __be16 tun_flags, u8 vni[3], u8 opt_len, u8 *opt,
-			    bool csum)
+			    bool csum, bool xnet)
 {
 	struct genevehdr *gnvh;
 	int min_headroom;
 	int err;
 
+	skb_scrub_packet(skb, xnet);
+
 	min_headroom = LL_RESERVED_SPACE(rt->dst.dev) + rt->dst.header_len
 			+ GENEVE_BASE_HLEN + opt_len + sizeof(struct iphdr);
 	err = skb_cow_head(skb, min_headroom);
@@ -544,15 +665,7 @@ static int geneve_build_skb(struct rtable *rt, struct sk_buff *skb,
 	}
 
 	gnvh = (struct genevehdr *)__skb_push(skb, sizeof(*gnvh) + opt_len);
-	gnvh->ver = GENEVE_VER;
-	gnvh->opt_len = opt_len / 4;
-	gnvh->oam = !!(tun_flags & TUNNEL_OAM);
-	gnvh->critical = !!(tun_flags & TUNNEL_CRIT_OPT);
-	gnvh->rsvd1 = 0;
-	memcpy(gnvh->vni, vni, 3);
-	gnvh->proto_type = htons(ETH_P_TEB);
-	gnvh->rsvd2 = 0;
-	memcpy(gnvh->options, opt, opt_len);
+	geneve_build_header(gnvh, tun_flags, vni, opt_len, opt);
 
 	skb_set_inner_protocol(skb, htons(ETH_P_TEB));
 	return 0;
@@ -562,10 +675,47 @@ free_rt:
 	return err;
 }
 
-static struct rtable *geneve_get_rt(struct sk_buff *skb,
-				    struct net_device *dev,
-				    struct flowi4 *fl4,
-				    struct ip_tunnel_info *info)
+#if IS_ENABLED(CONFIG_IPV6)
+static int geneve6_build_skb(struct dst_entry *dst, struct sk_buff *skb,
+			     __be16 tun_flags, u8 vni[3], u8 opt_len, u8 *opt,
+			     bool csum, bool xnet)
+{
+	struct genevehdr *gnvh;
+	int min_headroom;
+	int err;
+
+	skb_scrub_packet(skb, xnet);
+
+	min_headroom = LL_RESERVED_SPACE(dst->dev) + dst->header_len
+			+ GENEVE_BASE_HLEN + opt_len + sizeof(struct ipv6hdr);
+	err = skb_cow_head(skb, min_headroom);
+	if (unlikely(err)) {
+		kfree_skb(skb);
+		goto free_dst;
+	}
+
+	skb = udp_tunnel_handle_offloads(skb, csum);
+	if (IS_ERR(skb)) {
+		err = PTR_ERR(skb);
+		goto free_dst;
+	}
+
+	gnvh = (struct genevehdr *)__skb_push(skb, sizeof(*gnvh) + opt_len);
+	geneve_build_header(gnvh, tun_flags, vni, opt_len, opt);
+
+	skb_set_inner_protocol(skb, htons(ETH_P_TEB));
+	return 0;
+
+free_dst:
+	dst_release(dst);
+	return err;
+}
+#endif
+
+static struct rtable *geneve_get_v4_rt(struct sk_buff *skb,
+				       struct net_device *dev,
+				       struct flowi4 *fl4,
+				       struct ip_tunnel_info *info)
 {
 	struct geneve_dev *geneve = netdev_priv(dev);
 	struct rtable *rt = NULL;
@@ -588,24 +738,42 @@ static struct rtable *geneve_get_rt(struct sk_buff *skb,
 		}
 
 		fl4->flowi4_tos = RT_TOS(tos);
-		fl4->daddr = geneve->remote.sin_addr.s_addr;
+		fl4->daddr = geneve->remote.sin.sin_addr.s_addr;
 	}
 
 	rt = ip_route_output_key(geneve->net, fl4);
-	if (IS_ERR(rt)) {
-		netdev_dbg(dev, "no route to %pI4\n", &fl4->daddr);
-		dev->stats.tx_carrier_errors++;
-		return rt;
-	}
-	if (rt->dst.dev == dev) { /* is this necessary? */
-		netdev_dbg(dev, "circular route to %pI4\n", &fl4->daddr);
-		dev->stats.collisions++;
-		ip_rt_put(rt);
-		return ERR_PTR(-EINVAL);
-	}
+
 	return rt;
 }
 
+#if IS_ENABLED(CONFIG_IPV6)
+static struct dst_entry *geneve_get_v6_dst(struct sk_buff *skb,
+					   struct net_device *dev,
+					   struct flowi6 *fl6,
+					   struct ip_tunnel_info *info)
+{
+	struct geneve_dev *geneve = netdev_priv(dev);
+	struct geneve_sock *gs6 = geneve->sock6;
+	struct dst_entry *dst = NULL;
+
+	memset(fl6, 0, sizeof(*fl6));
+	fl6->flowi6_mark = skb->mark;
+	fl6->flowi6_proto = IPPROTO_UDP;
+
+	if (info) {
+		fl6->daddr = info->key.u.ipv6.dst;
+		fl6->saddr = info->key.u.ipv6.src;
+	} else {
+		fl6->daddr = geneve->remote.sin6.sin6_addr;
+	}
+
+	if (ipv6_stub->ipv6_dst_lookup(geneve->net, gs6->sock->sk, &dst, fl6))
+		return ERR_PTR(-EHOSTUNREACH);
+
+	return dst;
+}
+#endif
+
 /* Convert 64 bit tunnel ID to 24 bit VNI. */
 static void tunnel_id_to_vni(__be64 tun_id, __u8 *vni)
 {
@@ -620,11 +788,11 @@ static void tunnel_id_to_vni(__be64 tun_id, __u8 *vni)
 #endif
 }
 
-static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev,
+				   struct ip_tunnel_info *info)
 {
 	struct geneve_dev *geneve = netdev_priv(dev);
-	struct geneve_sock *gs = geneve->sock;
-	struct ip_tunnel_info *info = NULL;
+	struct geneve_sock *gs4 = geneve->sock4;
 	struct rtable *rt = NULL;
 	const struct iphdr *iip; /* interior IP header */
 	struct flowi4 fl4;
@@ -633,10 +801,10 @@ static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
 	bool udp_csum;
 	__be16 df;
 	int err;
+	bool xnet = !net_eq(geneve->net, dev_net(geneve->dev));
 
 	if (geneve->collect_md) {
-		info = skb_tunnel_info(skb);
-		if (unlikely(info && !(info->mode & IP_TUNNEL_INFO_TX))) {
+		if (unlikely(!info || !(info->mode & IP_TUNNEL_INFO_TX))) {
 			netdev_dbg(dev, "no tunnel metadata\n");
 			goto tx_error;
 		}
@@ -644,12 +812,18 @@ static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
 			goto tx_error;
 	}
 
-	rt = geneve_get_rt(skb, dev, &fl4, info);
+	rt = geneve_get_v4_rt(skb, dev, &fl4, info);
 	if (IS_ERR(rt)) {
 		netdev_dbg(dev, "no route to %pI4\n", &fl4.daddr);
 		dev->stats.tx_carrier_errors++;
 		goto tx_error;
 	}
+	if (rt->dst.dev == dev) { /* is this necessary? */
+		netdev_dbg(dev, "circular route to %pI4\n", &fl4.daddr);
+		dev->stats.collisions++;
+		ip_rt_put(rt);
+		goto tx_error;
+	}
 
 	sport = udp_flow_src_port(geneve->net, skb, 1, USHRT_MAX, true);
 	skb_reset_mac_header(skb);
@@ -667,7 +841,7 @@ static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
 
 		udp_csum = !!(key->tun_flags & TUNNEL_CSUM);
 		err = geneve_build_skb(rt, skb, key->tun_flags, vni,
-				       info->options_len, opts, udp_csum);
+				       info->options_len, opts, udp_csum, xnet);
 		if (unlikely(err))
 			goto err;
 
@@ -677,7 +851,7 @@ static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
 	} else {
 		udp_csum = false;
 		err = geneve_build_skb(rt, skb, 0, geneve->vni,
-				       0, NULL, udp_csum);
+				       0, NULL, udp_csum, xnet);
 		if (unlikely(err))
 			goto err;
 
@@ -688,7 +862,7 @@ static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
 		ttl = ttl ? : ip4_dst_hoplimit(&rt->dst);
 		df = 0;
 	}
-	err = udp_tunnel_xmit_skb(rt, gs->sock->sk, skb, fl4.saddr, fl4.daddr,
+	err = udp_tunnel_xmit_skb(rt, gs4->sock->sk, skb, fl4.saddr, fl4.daddr,
 				  tos, ttl, df, sport, geneve->dst_port,
 				  !net_eq(geneve->net, dev_net(geneve->dev)),
 				  !udp_csum);
@@ -703,6 +877,103 @@ err:
 	return NETDEV_TX_OK;
 }
 
+#if IS_ENABLED(CONFIG_IPV6)
+static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
+				    struct ip_tunnel_info *info)
+{
+	struct geneve_dev *geneve = netdev_priv(dev);
+	struct geneve_sock *gs6 = geneve->sock6;
+	struct dst_entry *dst = NULL;
+	struct flowi6 fl6;
+	__u8 ttl;
+	__be16 sport;
+	bool udp_csum;
+	int err;
+	bool xnet = !net_eq(geneve->net, dev_net(geneve->dev));
+
+	if (geneve->collect_md) {
+		if (unlikely(!info || !(info->mode & IP_TUNNEL_INFO_TX))) {
+			netdev_dbg(dev, "no tunnel metadata\n");
+			goto tx_error;
+		}
+	}
+
+	dst = geneve_get_v6_dst(skb, dev, &fl6, info);
+	if (IS_ERR(dst)) {
+		netdev_dbg(dev, "no route to %pI6\n", &fl6.daddr);
+		dev->stats.tx_carrier_errors++;
+		goto tx_error;
+	}
+	if (dst->dev == dev) { /* is this necessary? */
+		netdev_dbg(dev, "circular route to %pI6\n", &fl6.daddr);
+		dev->stats.collisions++;
+		dst_release(dst);
+		goto tx_error;
+	}
+
+	sport = udp_flow_src_port(geneve->net, skb, 1, USHRT_MAX, true);
+	skb_reset_mac_header(skb);
+
+	if (info) {
+		const struct ip_tunnel_key *key = &info->key;
+		u8 *opts = NULL;
+		u8 vni[3];
+
+		tunnel_id_to_vni(key->tun_id, vni);
+		if (key->tun_flags & TUNNEL_GENEVE_OPT)
+			opts = ip_tunnel_info_opts(info);
+
+		udp_csum = !!(key->tun_flags & TUNNEL_CSUM);
+		err = geneve6_build_skb(dst, skb, key->tun_flags, vni,
+					info->options_len, opts,
+					udp_csum, xnet);
+		if (unlikely(err))
+			goto err;
+
+		ttl = key->ttl;
+	} else {
+		udp_csum = false;
+		err = geneve6_build_skb(dst, skb, 0, geneve->vni,
+					0, NULL, udp_csum, xnet);
+		if (unlikely(err))
+			goto err;
+
+		ttl = geneve->ttl;
+		if (!ttl && ipv6_addr_is_multicast(&fl6.daddr))
+			ttl = 1;
+		ttl = ttl ? : ip6_dst_hoplimit(dst);
+	}
+	err = udp_tunnel6_xmit_skb(dst, gs6->sock->sk, skb, dev,
+				   &fl6.saddr, &fl6.daddr, 0, ttl,
+				   sport, geneve->dst_port, !udp_csum);
+
+	iptunnel_xmit_stats(err, &dev->stats, dev->tstats);
+	return NETDEV_TX_OK;
+
+tx_error:
+	dev_kfree_skb(skb);
+err:
+	dev->stats.tx_errors++;
+	return NETDEV_TX_OK;
+}
+#endif
+
+static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct geneve_dev *geneve = netdev_priv(dev);
+	struct ip_tunnel_info *info = NULL;
+
+	if (geneve->collect_md)
+		info = skb_tunnel_info(skb);
+
+#if IS_ENABLED(CONFIG_IPV6)
+	if ((info && ip_tunnel_info_af(info) == AF_INET6) ||
+	    (!info && geneve->remote.sa.sa_family == AF_INET6))
+		return geneve6_xmit_skb(skb, dev, info);
+#endif
+	return geneve_xmit_skb(skb, dev, info);
+}
+
 static const struct net_device_ops geneve_netdev_ops = {
 	.ndo_init		= geneve_init,
 	.ndo_uninit		= geneve_uninit,
@@ -759,6 +1030,7 @@ static void geneve_setup(struct net_device *dev)
 static const struct nla_policy geneve_policy[IFLA_GENEVE_MAX + 1] = {
 	[IFLA_GENEVE_ID]		= { .type = NLA_U32 },
 	[IFLA_GENEVE_REMOTE]		= { .len = FIELD_SIZEOF(struct iphdr, daddr) },
+	[IFLA_GENEVE_REMOTE6]		= { .len = sizeof(struct in6_addr) },
 	[IFLA_GENEVE_TTL]		= { .type = NLA_U8 },
 	[IFLA_GENEVE_TOS]		= { .type = NLA_U8 },
 	[IFLA_GENEVE_PORT]		= { .type = NLA_U16 },
@@ -790,7 +1062,7 @@ static int geneve_validate(struct nlattr *tb[], struct nlattr *data[])
 
 static struct geneve_dev *geneve_find_dev(struct geneve_net *gn,
 					  __be16 dst_port,
-					  __be32 rem_addr,
+					  union geneve_addr *remote,
 					  u8 vni[],
 					  bool *tun_on_same_port,
 					  bool *tun_collect_md)
@@ -806,7 +1078,7 @@ static struct geneve_dev *geneve_find_dev(struct geneve_net *gn,
 			*tun_on_same_port = true;
 		}
 		if (!memcmp(vni, geneve->vni, sizeof(geneve->vni)) &&
-		    rem_addr == geneve->remote.sin_addr.s_addr &&
+		    !memcmp(remote, &geneve->remote, sizeof(geneve->remote)) &&
 		    dst_port == geneve->dst_port)
 			t = geneve;
 	}
@@ -814,18 +1086,20 @@ static struct geneve_dev *geneve_find_dev(struct geneve_net *gn,
 }
 
 static int geneve_configure(struct net *net, struct net_device *dev,
-			    __be32 rem_addr, __u32 vni, __u8 ttl, __u8 tos,
-			    __be16 dst_port, bool metadata)
+			    union geneve_addr *remote,
+			    __u32 vni, __u8 ttl, __u8 tos, __be16 dst_port,
+			    bool metadata)
 {
 	struct geneve_net *gn = net_generic(net, geneve_net_id);
 	struct geneve_dev *t, *geneve = netdev_priv(dev);
 	bool tun_collect_md, tun_on_same_port;
 	int err;
 
-	if (metadata) {
-		if (rem_addr || vni || tos || ttl)
-			return -EINVAL;
-	}
+	if (!remote)
+		return -EINVAL;
+	if (metadata &&
+	    (remote->sa.sa_family != AF_UNSPEC || vni || tos || ttl))
+		return -EINVAL;
 
 	geneve->net = net;
 	geneve->dev = dev;
@@ -834,16 +1108,19 @@ static int geneve_configure(struct net *net, struct net_device *dev,
 	geneve->vni[1] = (vni & 0x0000ff00) >> 8;
 	geneve->vni[2] =  vni & 0x000000ff;
 
-	geneve->remote.sin_addr.s_addr = rem_addr;
-	if (IN_MULTICAST(ntohl(geneve->remote.sin_addr.s_addr)))
+	if ((remote->sa.sa_family == AF_INET &&
+	     IN_MULTICAST(ntohl(remote->sin.sin_addr.s_addr))) ||
+	    (remote->sa.sa_family == AF_INET6 &&
+	     ipv6_addr_is_multicast(&remote->sin6.sin6_addr)))
 		return -EINVAL;
+	geneve->remote = *remote;
 
 	geneve->ttl = ttl;
 	geneve->tos = tos;
 	geneve->dst_port = dst_port;
 	geneve->collect_md = metadata;
 
-	t = geneve_find_dev(gn, dst_port, rem_addr, geneve->vni,
+	t = geneve_find_dev(gn, dst_port, remote, geneve->vni,
 			    &tun_on_same_port, &tun_collect_md);
 	if (t)
 		return -EBUSY;
@@ -870,15 +1147,36 @@ static int geneve_newlink(struct net *net, struct net_device *dev,
 	__be16 dst_port = htons(GENEVE_UDP_PORT);
 	__u8 ttl = 0, tos = 0;
 	bool metadata = false;
-	__be32 rem_addr = 0;
+	union geneve_addr remote = geneve_remote_unspec;
 	__u32 vni = 0;
 
+	if (data[IFLA_GENEVE_REMOTE] && data[IFLA_GENEVE_REMOTE6])
+		return -EINVAL;
+
+	if (data[IFLA_GENEVE_REMOTE]) {
+		remote.sa.sa_family = AF_INET;
+		remote.sin.sin_addr.s_addr =
+			nla_get_in_addr(data[IFLA_GENEVE_REMOTE]);
+	}
+
+	if (data[IFLA_GENEVE_REMOTE6]) {
+		if (!IS_ENABLED(CONFIG_IPV6))
+			return -EPFNOSUPPORT;
+
+		remote.sa.sa_family = AF_INET6;
+		remote.sin6.sin6_addr =
+			nla_get_in6_addr(data[IFLA_GENEVE_REMOTE6]);
+
+		if (ipv6_addr_type(&remote.sin6.sin6_addr) &
+		    IPV6_ADDR_LINKLOCAL) {
+			netdev_dbg(dev, "link-local remote is unsupported\n");
+			return -EINVAL;
+		}
+	}
+
 	if (data[IFLA_GENEVE_ID])
 		vni = nla_get_u32(data[IFLA_GENEVE_ID]);
 
-	if (data[IFLA_GENEVE_REMOTE])
-		rem_addr = nla_get_in_addr(data[IFLA_GENEVE_REMOTE]);
-
 	if (data[IFLA_GENEVE_TTL])
 		ttl = nla_get_u8(data[IFLA_GENEVE_TTL]);
 
@@ -891,8 +1189,8 @@ static int geneve_newlink(struct net *net, struct net_device *dev,
 	if (data[IFLA_GENEVE_COLLECT_METADATA])
 		metadata = true;
 
-	return geneve_configure(net, dev, rem_addr, vni,
-				ttl, tos, dst_port, metadata);
+	return geneve_configure(net, dev, &remote, vni, ttl, tos, dst_port,
+				metadata);
 }
 
 static void geneve_dellink(struct net_device *dev, struct list_head *head)
@@ -906,7 +1204,7 @@ static void geneve_dellink(struct net_device *dev, struct list_head *head)
 static size_t geneve_get_size(const struct net_device *dev)
 {
 	return nla_total_size(sizeof(__u32)) +	/* IFLA_GENEVE_ID */
-		nla_total_size(sizeof(struct in_addr)) + /* IFLA_GENEVE_REMOTE */
+		nla_total_size(sizeof(struct in6_addr)) + /* IFLA_GENEVE_REMOTE{6} */
 		nla_total_size(sizeof(__u8)) +  /* IFLA_GENEVE_TTL */
 		nla_total_size(sizeof(__u8)) +  /* IFLA_GENEVE_TOS */
 		nla_total_size(sizeof(__be16)) +  /* IFLA_GENEVE_PORT */
@@ -923,9 +1221,17 @@ static int geneve_fill_info(struct sk_buff *skb, const struct net_device *dev)
 	if (nla_put_u32(skb, IFLA_GENEVE_ID, vni))
 		goto nla_put_failure;
 
-	if (nla_put_in_addr(skb, IFLA_GENEVE_REMOTE,
-			    geneve->remote.sin_addr.s_addr))
-		goto nla_put_failure;
+	if (geneve->remote.sa.sa_family == AF_INET) {
+		if (nla_put_in_addr(skb, IFLA_GENEVE_REMOTE,
+				    geneve->remote.sin.sin_addr.s_addr))
+			goto nla_put_failure;
+#if IS_ENABLED(CONFIG_IPV6)
+	} else {
+		if (nla_put_in6_addr(skb, IFLA_GENEVE_REMOTE6,
+				     &geneve->remote.sin6.sin6_addr))
+			goto nla_put_failure;
+#endif
+	}
 
 	if (nla_put_u8(skb, IFLA_GENEVE_TTL, geneve->ttl) ||
 	    nla_put_u8(skb, IFLA_GENEVE_TOS, geneve->tos))
@@ -971,7 +1277,8 @@ struct net_device *geneve_dev_create_fb(struct net *net, const char *name,
 	if (IS_ERR(dev))
 		return dev;
 
-	err = geneve_configure(net, dev, 0, 0, 0, 0, htons(dst_port), true);
+	err = geneve_configure(net, dev, &geneve_remote_unspec,
+			       0, 0, 0, htons(dst_port), true);
 	if (err) {
 		free_netdev(dev);
 		return ERR_PTR(err);
diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
index e3b6217f34f1..45e3a48550f9 100644
--- a/include/uapi/linux/if_link.h
+++ b/include/uapi/linux/if_link.h
@@ -461,6 +461,7 @@ enum {
 	IFLA_GENEVE_TOS,
 	IFLA_GENEVE_PORT,	/* destination port */
 	IFLA_GENEVE_COLLECT_METADATA,
+	IFLA_GENEVE_REMOTE6,
 	__IFLA_GENEVE_MAX
 };
 #define IFLA_GENEVE_MAX	(__IFLA_GENEVE_MAX - 1)
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v6 2/2] geneve: handle ipv6 priority like ipv4 tos
  2015-10-23 14:40         ` [PATCH v6 " John W. Linville
@ 2015-10-23 14:40           ` John W. Linville
  2015-10-26  4:08           ` [PATCH v6 1/2] geneve: implement support for IPv6-based tunnels Jesse Gross
  2015-10-26 21:01           ` [PATCH v7 1/3] " John W. Linville
  2 siblings, 0 replies; 37+ messages in thread
From: John W. Linville @ 2015-10-23 14:40 UTC (permalink / raw)
  To: netdev
  Cc: Dave Miller, Pravin B Shelar, Jesse Gross, Jiri Benc,
	YOSHIFUJI Hideaki/吉藤英明,
	John W. Linville

Other callers of udp_tunnel6_xmit_skb just pass 0 for the prio
argument.  Jesse Gross <jesse@nicira.com> suggested that prio is really
the same as IPv4's tos and should be handled the same, so this is my
interpretation of that suggestion.

Signed-off-by: John W. Linville <linville@tuxdriver.com>
Reported-by: Jesse Gross <jesse@nicira.com>
Reviewed-by: Jesse Gross <jesse@nicira.com>
---
v6 -- same as previous revision

 drivers/net/geneve.c | 19 +++++++++++++++++--
 1 file changed, 17 insertions(+), 2 deletions(-)

diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c
index 46d4aa714893..1327694c932e 100644
--- a/drivers/net/geneve.c
+++ b/drivers/net/geneve.c
@@ -755,6 +755,7 @@ static struct dst_entry *geneve_get_v6_dst(struct sk_buff *skb,
 	struct geneve_dev *geneve = netdev_priv(dev);
 	struct geneve_sock *gs6 = geneve->sock6;
 	struct dst_entry *dst = NULL;
+	__u8 prio;
 
 	memset(fl6, 0, sizeof(*fl6));
 	fl6->flowi6_mark = skb->mark;
@@ -763,7 +764,16 @@ static struct dst_entry *geneve_get_v6_dst(struct sk_buff *skb,
 	if (info) {
 		fl6->daddr = info->key.u.ipv6.dst;
 		fl6->saddr = info->key.u.ipv6.src;
+		fl6->flowi6_tos = RT_TOS(info->key.tos);
 	} else {
+		prio = geneve->tos;
+		if (prio == 1) {
+			const struct iphdr *iip = ip_hdr(skb);
+
+			prio = ip_tunnel_get_dsfield(iip, skb);
+		}
+
+		fl6->flowi6_tos = RT_TOS(prio);
 		fl6->daddr = geneve->remote.sin6.sin6_addr;
 	}
 
@@ -884,8 +894,9 @@ static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
 	struct geneve_dev *geneve = netdev_priv(dev);
 	struct geneve_sock *gs6 = geneve->sock6;
 	struct dst_entry *dst = NULL;
+	const struct iphdr *iip; /* interior IP header */
 	struct flowi6 fl6;
-	__u8 ttl;
+	__u8 prio, ttl;
 	__be16 sport;
 	bool udp_csum;
 	int err;
@@ -914,6 +925,8 @@ static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
 	sport = udp_flow_src_port(geneve->net, skb, 1, USHRT_MAX, true);
 	skb_reset_mac_header(skb);
 
+	iip = ip_hdr(skb);
+
 	if (info) {
 		const struct ip_tunnel_key *key = &info->key;
 		u8 *opts = NULL;
@@ -930,6 +943,7 @@ static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
 		if (unlikely(err))
 			goto err;
 
+		prio = ip_tunnel_ecn_encap(key->tos, iip, skb);
 		ttl = key->ttl;
 	} else {
 		udp_csum = false;
@@ -938,13 +952,14 @@ static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
 		if (unlikely(err))
 			goto err;
 
+		prio = ip_tunnel_ecn_encap(fl6.flowi6_tos, iip, skb);
 		ttl = geneve->ttl;
 		if (!ttl && ipv6_addr_is_multicast(&fl6.daddr))
 			ttl = 1;
 		ttl = ttl ? : ip6_dst_hoplimit(dst);
 	}
 	err = udp_tunnel6_xmit_skb(dst, gs6->sock->sk, skb, dev,
-				   &fl6.saddr, &fl6.daddr, 0, ttl,
+				   &fl6.saddr, &fl6.daddr, prio, ttl,
 				   sport, geneve->dst_port, !udp_csum);
 
 	iptunnel_xmit_stats(err, &dev->stats, dev->tstats);
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* Re: [PATCH v6 1/2] geneve: implement support for IPv6-based tunnels
  2015-10-23 14:40         ` [PATCH v6 " John W. Linville
  2015-10-23 14:40           ` [PATCH v6 2/2] geneve: handle ipv6 priority like ipv4 tos John W. Linville
@ 2015-10-26  4:08           ` Jesse Gross
  2015-10-26 21:01           ` [PATCH v7 1/3] " John W. Linville
  2 siblings, 0 replies; 37+ messages in thread
From: Jesse Gross @ 2015-10-26  4:08 UTC (permalink / raw)
  To: John W. Linville
  Cc: netdev, Dave Miller, Pravin B Shelar, Jiri Benc,
	YOSHIFUJI Hideaki/吉藤英明

On Fri, Oct 23, 2015 at 10:40 PM, John W. Linville
<linville@tuxdriver.com> wrote:
> NOTE: Link-local IPv6 addresses for remote endpoints are not supported,
> since the driver currently has no capacity for binding a geneve
> interface to a specific link.
>
> Signed-off-by: John W. Linville <linville@tuxdriver.com>

Reviewed-by: Jesse Gross <jesse@nicira.com>

Thanks!

By the way, there are now some merge conflicts against current
net-next due to the merge from net.

^ permalink raw reply	[flat|nested] 37+ messages in thread

* [PATCH v7 1/3] geneve: implement support for IPv6-based tunnels
  2015-10-23 14:40         ` [PATCH v6 " John W. Linville
  2015-10-23 14:40           ` [PATCH v6 2/2] geneve: handle ipv6 priority like ipv4 tos John W. Linville
  2015-10-26  4:08           ` [PATCH v6 1/2] geneve: implement support for IPv6-based tunnels Jesse Gross
@ 2015-10-26 21:01           ` John W. Linville
  2015-10-26 21:01             ` [PATCH v7 2/3] geneve: handle ipv6 priority like ipv4 tos John W. Linville
                               ` (2 more replies)
  2 siblings, 3 replies; 37+ messages in thread
From: John W. Linville @ 2015-10-26 21:01 UTC (permalink / raw)
  To: netdev
  Cc: Dave Miller, Pravin B Shelar, Jesse Gross, Jiri Benc,
	YOSHIFUJI Hideaki/吉藤英明,
	John W. Linville

NOTE: Link-local IPv6 addresses for remote endpoints are not supported,
since the driver currently has no capacity for binding a geneve
interface to a specific link.

Signed-off-by: John W. Linville <linville@tuxdriver.com>
Reviewed-by: Jesse Gross <jesse@nicira.com>
---
v7:
- rebase on top of commit fc4099f17240 ("openvswitch: Fix egress tunnel info.")
- revise error handling in ipv6 tx path to match ipv4 tx path (as above)
- Added Reviewed-by from Jesse based on v6 review -- changes above are minor

v6:
- fix a typo (missing {}'s)

v5:
- wrap declaration of sock6 in geneve_dev with IS_ENABLED(CONFIG_IPV6)
- remove superfluous '!!' when assigning geneve->collect_md to bool
- use skb_scrub_packet in IPv4 tx path as well 
- check for NULL ip_tunnel_info pointer in geneve[6]_xmit_skb
- use ipv6_addr_equal for comparing IPv6 addresses
- more use of IS_ENABLED(CONFIG_IPV6) for preserving build integrity
- reject link-local ipv6 address for remote tunnel endpoint

v4:
- treat mode field of ip_tunnel_info as flags
- add a missing IS_ENABLED(CONFIG_IPV6) to geneve_rx
- remove unneeded flags field in geneve_dev
- NULL-check parameter for __geneve_sock_release
- check remote socket family for AF_UNSPEC in geneve_configure
- rename geneve_get_{rt,dst} as geneve_get_{v4_rt,v6_dst}
- refactor some error handling in the xmit paths

v3:
- declare geneve_remote_unspec as static

v2:
- do not require remote address for tx on metadata tunnels
- pass correct sockaddr family to udp_tun_rx_dst in geneve_rx
- accommodate both ipv4 and ipv6 sockets open on same tunnel
- move declaration of geneve_get_dst for aesthetic purposes

 drivers/net/geneve.c         | 473 +++++++++++++++++++++++++++++++++++--------
 include/uapi/linux/if_link.h |   1 +
 2 files changed, 395 insertions(+), 79 deletions(-)

diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c
index 445071c163cb..393b0bddf7cf 100644
--- a/drivers/net/geneve.c
+++ b/drivers/net/geneve.c
@@ -46,16 +46,27 @@ struct geneve_net {
 
 static int geneve_net_id;
 
+union geneve_addr {
+	struct sockaddr_in sin;
+	struct sockaddr_in6 sin6;
+	struct sockaddr sa;
+};
+
+static union geneve_addr geneve_remote_unspec = { .sa.sa_family = AF_UNSPEC, };
+
 /* Pseudo network device */
 struct geneve_dev {
 	struct hlist_node  hlist;	/* vni hash table */
 	struct net	   *net;	/* netns for packet i/o */
 	struct net_device  *dev;	/* netdev for geneve tunnel */
-	struct geneve_sock *sock;	/* socket used for geneve tunnel */
+	struct geneve_sock *sock4;	/* IPv4 socket used for geneve tunnel */
+#if IS_ENABLED(CONFIG_IPV6)
+	struct geneve_sock *sock6;	/* IPv6 socket used for geneve tunnel */
+#endif
 	u8                 vni[3];	/* virtual network ID for tunnel */
 	u8                 ttl;		/* TTL override */
 	u8                 tos;		/* TOS override */
-	struct sockaddr_in remote;	/* IPv4 address for link partner */
+	union geneve_addr  remote;	/* IP address for link partner */
 	struct list_head   next;	/* geneve's per namespace list */
 	__be16		   dst_port;
 	bool		   collect_md;
@@ -103,11 +114,31 @@ static struct geneve_dev *geneve_lookup(struct geneve_sock *gs,
 	vni_list_head = &gs->vni_list[hash];
 	hlist_for_each_entry_rcu(geneve, vni_list_head, hlist) {
 		if (!memcmp(vni, geneve->vni, sizeof(geneve->vni)) &&
-		    addr == geneve->remote.sin_addr.s_addr)
+		    addr == geneve->remote.sin.sin_addr.s_addr)
+			return geneve;
+	}
+	return NULL;
+}
+
+#if IS_ENABLED(CONFIG_IPV6)
+static struct geneve_dev *geneve6_lookup(struct geneve_sock *gs,
+					 struct in6_addr addr6, u8 vni[])
+{
+	struct hlist_head *vni_list_head;
+	struct geneve_dev *geneve;
+	__u32 hash;
+
+	/* Find the device for this VNI */
+	hash = geneve_net_vni_hash(vni);
+	vni_list_head = &gs->vni_list[hash];
+	hlist_for_each_entry_rcu(geneve, vni_list_head, hlist) {
+		if (!memcmp(vni, geneve->vni, sizeof(geneve->vni)) &&
+		    ipv6_addr_equal(&addr6, &geneve->remote.sin6.sin6_addr))
 			return geneve;
 	}
 	return NULL;
 }
+#endif
 
 static inline struct genevehdr *geneve_hdr(const struct sk_buff *skb)
 {
@@ -121,24 +152,49 @@ static void geneve_rx(struct geneve_sock *gs, struct sk_buff *skb)
 	struct metadata_dst *tun_dst = NULL;
 	struct geneve_dev *geneve = NULL;
 	struct pcpu_sw_netstats *stats;
-	struct iphdr *iph;
-	u8 *vni;
+	struct iphdr *iph = NULL;
 	__be32 addr;
-	int err;
+	static u8 zero_vni[3];
+	u8 *vni;
+	int err = 0;
+	sa_family_t sa_family;
+#if IS_ENABLED(CONFIG_IPV6)
+	struct ipv6hdr *ip6h = NULL;
+	struct in6_addr addr6;
+	static struct in6_addr zero_addr6;
+#endif
 
-	iph = ip_hdr(skb); /* outer IP header... */
+	sa_family = gs->sock->sk->sk_family;
 
-	if (gs->collect_md) {
-		static u8 zero_vni[3];
+	if (sa_family == AF_INET) {
+		iph = ip_hdr(skb); /* outer IP header... */
 
-		vni = zero_vni;
-		addr = 0;
-	} else {
-		vni = gnvh->vni;
-		addr = iph->saddr;
-	}
+		if (gs->collect_md) {
+			vni = zero_vni;
+			addr = 0;
+		} else {
+			vni = gnvh->vni;
+
+			addr = iph->saddr;
+		}
+
+		geneve = geneve_lookup(gs, addr, vni);
+#if IS_ENABLED(CONFIG_IPV6)
+	} else if (sa_family == AF_INET6) {
+		ip6h = ipv6_hdr(skb); /* outer IPv6 header... */
 
-	geneve = geneve_lookup(gs, addr, vni);
+		if (gs->collect_md) {
+			vni = zero_vni;
+			addr6 = zero_addr6;
+		} else {
+			vni = gnvh->vni;
+
+			addr6 = ip6h->saddr;
+		}
+
+		geneve = geneve6_lookup(gs, addr6, vni);
+#endif
+	}
 	if (!geneve)
 		goto drop;
 
@@ -149,7 +205,7 @@ static void geneve_rx(struct geneve_sock *gs, struct sk_buff *skb)
 			(gnvh->oam ? TUNNEL_OAM : 0) |
 			(gnvh->critical ? TUNNEL_CRIT_OPT : 0);
 
-		tun_dst = udp_tun_rx_dst(skb, AF_INET, flags,
+		tun_dst = udp_tun_rx_dst(skb, sa_family, flags,
 					 vni_to_tunnel_id(gnvh->vni),
 					 gnvh->opt_len * 4);
 		if (!tun_dst)
@@ -179,12 +235,25 @@ static void geneve_rx(struct geneve_sock *gs, struct sk_buff *skb)
 
 	skb_reset_network_header(skb);
 
-	err = IP_ECN_decapsulate(iph, skb);
+	if (iph)
+		err = IP_ECN_decapsulate(iph, skb);
+#if IS_ENABLED(CONFIG_IPV6)
+	if (ip6h)
+		err = IP6_ECN_decapsulate(ip6h, skb);
+#endif
 
 	if (unlikely(err)) {
-		if (log_ecn_error)
-			net_info_ratelimited("non-ECT from %pI4 with TOS=%#x\n",
-					     &iph->saddr, iph->tos);
+		if (log_ecn_error) {
+			if (iph)
+				net_info_ratelimited("non-ECT from %pI4 "
+						     "with TOS=%#x\n",
+						     &iph->saddr, iph->tos);
+#if IS_ENABLED(CONFIG_IPV6)
+			if (ip6h)
+				net_info_ratelimited("non-ECT from %pI6\n",
+						     &ip6h->saddr);
+#endif
+		}
 		if (err > 1) {
 			++geneve->dev->stats.rx_frame_errors;
 			++geneve->dev->stats.rx_errors;
@@ -284,6 +353,7 @@ static struct socket *geneve_create_sock(struct net *net, bool ipv6,
 
 	if (ipv6) {
 		udp_conf.family = AF_INET6;
+		udp_conf.ipv6_v6only = 1;
 	} else {
 		udp_conf.family = AF_INET;
 		udp_conf.local_ip.s_addr = htonl(INADDR_ANY);
@@ -458,9 +528,9 @@ static void geneve_notify_del_rx_port(struct geneve_sock *gs)
 		udp_del_offload(&gs->udp_offloads);
 }
 
-static void geneve_sock_release(struct geneve_sock *gs)
+static void __geneve_sock_release(struct geneve_sock *gs)
 {
-	if (--gs->refcnt)
+	if (!gs || --gs->refcnt)
 		return;
 
 	list_del(&gs->list);
@@ -469,66 +539,117 @@ static void geneve_sock_release(struct geneve_sock *gs)
 	kfree_rcu(gs, rcu);
 }
 
+static void geneve_sock_release(struct geneve_dev *geneve)
+{
+	__geneve_sock_release(geneve->sock4);
+#if IS_ENABLED(CONFIG_IPV6)
+	__geneve_sock_release(geneve->sock6);
+#endif
+}
+
 static struct geneve_sock *geneve_find_sock(struct geneve_net *gn,
+					    sa_family_t family,
 					    __be16 dst_port)
 {
 	struct geneve_sock *gs;
 
 	list_for_each_entry(gs, &gn->sock_list, list) {
 		if (inet_sk(gs->sock->sk)->inet_sport == dst_port &&
-		    inet_sk(gs->sock->sk)->sk.sk_family == AF_INET) {
+		    inet_sk(gs->sock->sk)->sk.sk_family == family) {
 			return gs;
 		}
 	}
 	return NULL;
 }
 
-static int geneve_open(struct net_device *dev)
+static int geneve_sock_add(struct geneve_dev *geneve, bool ipv6)
 {
-	struct geneve_dev *geneve = netdev_priv(dev);
 	struct net *net = geneve->net;
 	struct geneve_net *gn = net_generic(net, geneve_net_id);
 	struct geneve_sock *gs;
 	__u32 hash;
 
-	gs = geneve_find_sock(gn, geneve->dst_port);
+	gs = geneve_find_sock(gn, ipv6 ? AF_INET6 : AF_INET, geneve->dst_port);
 	if (gs) {
 		gs->refcnt++;
 		goto out;
 	}
 
-	gs = geneve_socket_create(net, geneve->dst_port, false);
+	gs = geneve_socket_create(net, geneve->dst_port, ipv6);
 	if (IS_ERR(gs))
 		return PTR_ERR(gs);
 
 out:
 	gs->collect_md = geneve->collect_md;
-	geneve->sock = gs;
+#if IS_ENABLED(CONFIG_IPV6)
+	if (ipv6)
+		geneve->sock6 = gs;
+	else
+#endif
+		geneve->sock4 = gs;
 
 	hash = geneve_net_vni_hash(geneve->vni);
 	hlist_add_head_rcu(&geneve->hlist, &gs->vni_list[hash]);
 	return 0;
 }
 
+static int geneve_open(struct net_device *dev)
+{
+	struct geneve_dev *geneve = netdev_priv(dev);
+	bool ipv6 = geneve->remote.sa.sa_family == AF_INET6;
+	bool metadata = geneve->collect_md;
+	int ret = 0;
+
+	geneve->sock4 = NULL;
+#if IS_ENABLED(CONFIG_IPV6)
+	geneve->sock6 = NULL;
+	if (ipv6 || metadata)
+		ret = geneve_sock_add(geneve, true);
+#endif
+	if (!ret && (!ipv6 || metadata))
+		ret = geneve_sock_add(geneve, false);
+	if (ret < 0)
+		geneve_sock_release(geneve);
+
+	return ret;
+}
+
 static int geneve_stop(struct net_device *dev)
 {
 	struct geneve_dev *geneve = netdev_priv(dev);
-	struct geneve_sock *gs = geneve->sock;
 
 	if (!hlist_unhashed(&geneve->hlist))
 		hlist_del_rcu(&geneve->hlist);
-	geneve_sock_release(gs);
+	geneve_sock_release(geneve);
 	return 0;
 }
 
+static void geneve_build_header(struct genevehdr *geneveh,
+				__be16 tun_flags, u8 vni[3],
+				u8 options_len, u8 *options)
+{
+	geneveh->ver = GENEVE_VER;
+	geneveh->opt_len = options_len / 4;
+	geneveh->oam = !!(tun_flags & TUNNEL_OAM);
+	geneveh->critical = !!(tun_flags & TUNNEL_CRIT_OPT);
+	geneveh->rsvd1 = 0;
+	memcpy(geneveh->vni, vni, 3);
+	geneveh->proto_type = htons(ETH_P_TEB);
+	geneveh->rsvd2 = 0;
+
+	memcpy(geneveh->options, options, options_len);
+}
+
 static int geneve_build_skb(struct rtable *rt, struct sk_buff *skb,
 			    __be16 tun_flags, u8 vni[3], u8 opt_len, u8 *opt,
-			    bool csum)
+			    bool csum, bool xnet)
 {
 	struct genevehdr *gnvh;
 	int min_headroom;
 	int err;
 
+	skb_scrub_packet(skb, xnet);
+
 	min_headroom = LL_RESERVED_SPACE(rt->dst.dev) + rt->dst.header_len
 			+ GENEVE_BASE_HLEN + opt_len + sizeof(struct iphdr);
 	err = skb_cow_head(skb, min_headroom);
@@ -544,15 +665,7 @@ static int geneve_build_skb(struct rtable *rt, struct sk_buff *skb,
 	}
 
 	gnvh = (struct genevehdr *)__skb_push(skb, sizeof(*gnvh) + opt_len);
-	gnvh->ver = GENEVE_VER;
-	gnvh->opt_len = opt_len / 4;
-	gnvh->oam = !!(tun_flags & TUNNEL_OAM);
-	gnvh->critical = !!(tun_flags & TUNNEL_CRIT_OPT);
-	gnvh->rsvd1 = 0;
-	memcpy(gnvh->vni, vni, 3);
-	gnvh->proto_type = htons(ETH_P_TEB);
-	gnvh->rsvd2 = 0;
-	memcpy(gnvh->options, opt, opt_len);
+	geneve_build_header(gnvh, tun_flags, vni, opt_len, opt);
 
 	skb_set_inner_protocol(skb, htons(ETH_P_TEB));
 	return 0;
@@ -562,10 +675,47 @@ free_rt:
 	return err;
 }
 
-static struct rtable *geneve_get_rt(struct sk_buff *skb,
-				    struct net_device *dev,
-				    struct flowi4 *fl4,
-				    struct ip_tunnel_info *info)
+#if IS_ENABLED(CONFIG_IPV6)
+static int geneve6_build_skb(struct dst_entry *dst, struct sk_buff *skb,
+			     __be16 tun_flags, u8 vni[3], u8 opt_len, u8 *opt,
+			     bool csum, bool xnet)
+{
+	struct genevehdr *gnvh;
+	int min_headroom;
+	int err;
+
+	skb_scrub_packet(skb, xnet);
+
+	min_headroom = LL_RESERVED_SPACE(dst->dev) + dst->header_len
+			+ GENEVE_BASE_HLEN + opt_len + sizeof(struct ipv6hdr);
+	err = skb_cow_head(skb, min_headroom);
+	if (unlikely(err)) {
+		kfree_skb(skb);
+		goto free_dst;
+	}
+
+	skb = udp_tunnel_handle_offloads(skb, csum);
+	if (IS_ERR(skb)) {
+		err = PTR_ERR(skb);
+		goto free_dst;
+	}
+
+	gnvh = (struct genevehdr *)__skb_push(skb, sizeof(*gnvh) + opt_len);
+	geneve_build_header(gnvh, tun_flags, vni, opt_len, opt);
+
+	skb_set_inner_protocol(skb, htons(ETH_P_TEB));
+	return 0;
+
+free_dst:
+	dst_release(dst);
+	return err;
+}
+#endif
+
+static struct rtable *geneve_get_v4_rt(struct sk_buff *skb,
+				       struct net_device *dev,
+				       struct flowi4 *fl4,
+				       struct ip_tunnel_info *info)
 {
 	struct geneve_dev *geneve = netdev_priv(dev);
 	struct rtable *rt = NULL;
@@ -588,7 +738,7 @@ static struct rtable *geneve_get_rt(struct sk_buff *skb,
 		}
 
 		fl4->flowi4_tos = RT_TOS(tos);
-		fl4->daddr = geneve->remote.sin_addr.s_addr;
+		fl4->daddr = geneve->remote.sin.sin_addr.s_addr;
 	}
 
 	rt = ip_route_output_key(geneve->net, fl4);
@@ -604,6 +754,41 @@ static struct rtable *geneve_get_rt(struct sk_buff *skb,
 	return rt;
 }
 
+#if IS_ENABLED(CONFIG_IPV6)
+static struct dst_entry *geneve_get_v6_dst(struct sk_buff *skb,
+					   struct net_device *dev,
+					   struct flowi6 *fl6,
+					   struct ip_tunnel_info *info)
+{
+	struct geneve_dev *geneve = netdev_priv(dev);
+	struct geneve_sock *gs6 = geneve->sock6;
+	struct dst_entry *dst = NULL;
+
+	memset(fl6, 0, sizeof(*fl6));
+	fl6->flowi6_mark = skb->mark;
+	fl6->flowi6_proto = IPPROTO_UDP;
+
+	if (info) {
+		fl6->daddr = info->key.u.ipv6.dst;
+		fl6->saddr = info->key.u.ipv6.src;
+	} else {
+		fl6->daddr = geneve->remote.sin6.sin6_addr;
+	}
+
+	if (ipv6_stub->ipv6_dst_lookup(geneve->net, gs6->sock->sk, &dst, fl6)) {
+		netdev_dbg(dev, "no route to %pI6\n", &fl6->daddr);
+		return ERR_PTR(-ENETUNREACH);
+	}
+	if (dst->dev == dev) { /* is this necessary? */
+		netdev_dbg(dev, "circular route to %pI6\n", &fl6->daddr);
+		dst_release(dst);
+		return ERR_PTR(-ELOOP);
+	}
+
+	return dst;
+}
+#endif
+
 /* Convert 64 bit tunnel ID to 24 bit VNI. */
 static void tunnel_id_to_vni(__be64 tun_id, __u8 *vni)
 {
@@ -618,11 +803,11 @@ static void tunnel_id_to_vni(__be64 tun_id, __u8 *vni)
 #endif
 }
 
-static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev,
+				   struct ip_tunnel_info *info)
 {
 	struct geneve_dev *geneve = netdev_priv(dev);
-	struct geneve_sock *gs = geneve->sock;
-	struct ip_tunnel_info *info = NULL;
+	struct geneve_sock *gs4 = geneve->sock4;
 	struct rtable *rt = NULL;
 	const struct iphdr *iip; /* interior IP header */
 	int err = -EINVAL;
@@ -631,10 +816,10 @@ static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
 	__be16 sport;
 	bool udp_csum;
 	__be16 df;
+	bool xnet = !net_eq(geneve->net, dev_net(geneve->dev));
 
 	if (geneve->collect_md) {
-		info = skb_tunnel_info(skb);
-		if (unlikely(info && !(info->mode & IP_TUNNEL_INFO_TX))) {
+		if (unlikely(!info || !(info->mode & IP_TUNNEL_INFO_TX))) {
 			netdev_dbg(dev, "no tunnel metadata\n");
 			goto tx_error;
 		}
@@ -642,9 +827,8 @@ static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
 			goto tx_error;
 	}
 
-	rt = geneve_get_rt(skb, dev, &fl4, info);
+	rt = geneve_get_v4_rt(skb, dev, &fl4, info);
 	if (IS_ERR(rt)) {
-		netdev_dbg(dev, "no route to %pI4\n", &fl4.daddr);
 		err = PTR_ERR(rt);
 		goto tx_error;
 	}
@@ -665,7 +849,7 @@ static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
 
 		udp_csum = !!(key->tun_flags & TUNNEL_CSUM);
 		err = geneve_build_skb(rt, skb, key->tun_flags, vni,
-				       info->options_len, opts, udp_csum);
+				       info->options_len, opts, udp_csum, xnet);
 		if (unlikely(err))
 			goto err;
 
@@ -675,7 +859,7 @@ static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
 	} else {
 		udp_csum = false;
 		err = geneve_build_skb(rt, skb, 0, geneve->vni,
-				       0, NULL, udp_csum);
+				       0, NULL, udp_csum, xnet);
 		if (unlikely(err))
 			goto err;
 
@@ -686,7 +870,7 @@ static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
 		ttl = ttl ? : ip4_dst_hoplimit(&rt->dst);
 		df = 0;
 	}
-	err = udp_tunnel_xmit_skb(rt, gs->sock->sk, skb, fl4.saddr, fl4.daddr,
+	err = udp_tunnel_xmit_skb(rt, gs4->sock->sk, skb, fl4.saddr, fl4.daddr,
 				  tos, ttl, df, sport, geneve->dst_port,
 				  !net_eq(geneve->net, dev_net(geneve->dev)),
 				  !udp_csum);
@@ -706,6 +890,101 @@ err:
 	return NETDEV_TX_OK;
 }
 
+#if IS_ENABLED(CONFIG_IPV6)
+static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
+				    struct ip_tunnel_info *info)
+{
+	struct geneve_dev *geneve = netdev_priv(dev);
+	struct geneve_sock *gs6 = geneve->sock6;
+	struct dst_entry *dst = NULL;
+	int err = -EINVAL;
+	struct flowi6 fl6;
+	__u8 ttl;
+	__be16 sport;
+	bool udp_csum;
+	bool xnet = !net_eq(geneve->net, dev_net(geneve->dev));
+
+	if (geneve->collect_md) {
+		if (unlikely(!info || !(info->mode & IP_TUNNEL_INFO_TX))) {
+			netdev_dbg(dev, "no tunnel metadata\n");
+			goto tx_error;
+		}
+	}
+
+	dst = geneve_get_v6_dst(skb, dev, &fl6, info);
+	if (IS_ERR(dst)) {
+		err = PTR_ERR(dst);
+		goto tx_error;
+	}
+
+	sport = udp_flow_src_port(geneve->net, skb, 1, USHRT_MAX, true);
+	skb_reset_mac_header(skb);
+
+	if (info) {
+		const struct ip_tunnel_key *key = &info->key;
+		u8 *opts = NULL;
+		u8 vni[3];
+
+		tunnel_id_to_vni(key->tun_id, vni);
+		if (key->tun_flags & TUNNEL_GENEVE_OPT)
+			opts = ip_tunnel_info_opts(info);
+
+		udp_csum = !!(key->tun_flags & TUNNEL_CSUM);
+		err = geneve6_build_skb(dst, skb, key->tun_flags, vni,
+					info->options_len, opts,
+					udp_csum, xnet);
+		if (unlikely(err))
+			goto err;
+
+		ttl = key->ttl;
+	} else {
+		udp_csum = false;
+		err = geneve6_build_skb(dst, skb, 0, geneve->vni,
+					0, NULL, udp_csum, xnet);
+		if (unlikely(err))
+			goto err;
+
+		ttl = geneve->ttl;
+		if (!ttl && ipv6_addr_is_multicast(&fl6.daddr))
+			ttl = 1;
+		ttl = ttl ? : ip6_dst_hoplimit(dst);
+	}
+	err = udp_tunnel6_xmit_skb(dst, gs6->sock->sk, skb, dev,
+				   &fl6.saddr, &fl6.daddr, 0, ttl,
+				   sport, geneve->dst_port, !udp_csum);
+
+	iptunnel_xmit_stats(err, &dev->stats, dev->tstats);
+	return NETDEV_TX_OK;
+
+tx_error:
+	dev_kfree_skb(skb);
+err:
+	if (err == -ELOOP)
+		dev->stats.collisions++;
+	else if (err == -ENETUNREACH)
+		dev->stats.tx_carrier_errors++;
+	else
+		dev->stats.tx_errors++;
+	return NETDEV_TX_OK;
+}
+#endif
+
+static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct geneve_dev *geneve = netdev_priv(dev);
+	struct ip_tunnel_info *info = NULL;
+
+	if (geneve->collect_md)
+		info = skb_tunnel_info(skb);
+
+#if IS_ENABLED(CONFIG_IPV6)
+	if ((info && ip_tunnel_info_af(info) == AF_INET6) ||
+	    (!info && geneve->remote.sa.sa_family == AF_INET6))
+		return geneve6_xmit_skb(skb, dev, info);
+#endif
+	return geneve_xmit_skb(skb, dev, info);
+}
+
 static int geneve_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb)
 {
 	struct ip_tunnel_info *info = skb_tunnel_info(skb);
@@ -716,7 +995,7 @@ static int geneve_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb)
 	if (ip_tunnel_info_af(info) != AF_INET)
 		return -EINVAL;
 
-	rt = geneve_get_rt(skb, dev, &fl4, info);
+	rt = geneve_get_v4_rt(skb, dev, &fl4, info);
 	if (IS_ERR(rt))
 		return PTR_ERR(rt);
 
@@ -785,6 +1064,7 @@ static void geneve_setup(struct net_device *dev)
 static const struct nla_policy geneve_policy[IFLA_GENEVE_MAX + 1] = {
 	[IFLA_GENEVE_ID]		= { .type = NLA_U32 },
 	[IFLA_GENEVE_REMOTE]		= { .len = FIELD_SIZEOF(struct iphdr, daddr) },
+	[IFLA_GENEVE_REMOTE6]		= { .len = sizeof(struct in6_addr) },
 	[IFLA_GENEVE_TTL]		= { .type = NLA_U8 },
 	[IFLA_GENEVE_TOS]		= { .type = NLA_U8 },
 	[IFLA_GENEVE_PORT]		= { .type = NLA_U16 },
@@ -816,7 +1096,7 @@ static int geneve_validate(struct nlattr *tb[], struct nlattr *data[])
 
 static struct geneve_dev *geneve_find_dev(struct geneve_net *gn,
 					  __be16 dst_port,
-					  __be32 rem_addr,
+					  union geneve_addr *remote,
 					  u8 vni[],
 					  bool *tun_on_same_port,
 					  bool *tun_collect_md)
@@ -832,7 +1112,7 @@ static struct geneve_dev *geneve_find_dev(struct geneve_net *gn,
 			*tun_on_same_port = true;
 		}
 		if (!memcmp(vni, geneve->vni, sizeof(geneve->vni)) &&
-		    rem_addr == geneve->remote.sin_addr.s_addr &&
+		    !memcmp(remote, &geneve->remote, sizeof(geneve->remote)) &&
 		    dst_port == geneve->dst_port)
 			t = geneve;
 	}
@@ -840,18 +1120,20 @@ static struct geneve_dev *geneve_find_dev(struct geneve_net *gn,
 }
 
 static int geneve_configure(struct net *net, struct net_device *dev,
-			    __be32 rem_addr, __u32 vni, __u8 ttl, __u8 tos,
-			    __be16 dst_port, bool metadata)
+			    union geneve_addr *remote,
+			    __u32 vni, __u8 ttl, __u8 tos, __be16 dst_port,
+			    bool metadata)
 {
 	struct geneve_net *gn = net_generic(net, geneve_net_id);
 	struct geneve_dev *t, *geneve = netdev_priv(dev);
 	bool tun_collect_md, tun_on_same_port;
 	int err;
 
-	if (metadata) {
-		if (rem_addr || vni || tos || ttl)
-			return -EINVAL;
-	}
+	if (!remote)
+		return -EINVAL;
+	if (metadata &&
+	    (remote->sa.sa_family != AF_UNSPEC || vni || tos || ttl))
+		return -EINVAL;
 
 	geneve->net = net;
 	geneve->dev = dev;
@@ -860,16 +1142,19 @@ static int geneve_configure(struct net *net, struct net_device *dev,
 	geneve->vni[1] = (vni & 0x0000ff00) >> 8;
 	geneve->vni[2] =  vni & 0x000000ff;
 
-	geneve->remote.sin_addr.s_addr = rem_addr;
-	if (IN_MULTICAST(ntohl(geneve->remote.sin_addr.s_addr)))
+	if ((remote->sa.sa_family == AF_INET &&
+	     IN_MULTICAST(ntohl(remote->sin.sin_addr.s_addr))) ||
+	    (remote->sa.sa_family == AF_INET6 &&
+	     ipv6_addr_is_multicast(&remote->sin6.sin6_addr)))
 		return -EINVAL;
+	geneve->remote = *remote;
 
 	geneve->ttl = ttl;
 	geneve->tos = tos;
 	geneve->dst_port = dst_port;
 	geneve->collect_md = metadata;
 
-	t = geneve_find_dev(gn, dst_port, rem_addr, geneve->vni,
+	t = geneve_find_dev(gn, dst_port, remote, geneve->vni,
 			    &tun_on_same_port, &tun_collect_md);
 	if (t)
 		return -EBUSY;
@@ -896,15 +1181,36 @@ static int geneve_newlink(struct net *net, struct net_device *dev,
 	__be16 dst_port = htons(GENEVE_UDP_PORT);
 	__u8 ttl = 0, tos = 0;
 	bool metadata = false;
-	__be32 rem_addr = 0;
+	union geneve_addr remote = geneve_remote_unspec;
 	__u32 vni = 0;
 
+	if (data[IFLA_GENEVE_REMOTE] && data[IFLA_GENEVE_REMOTE6])
+		return -EINVAL;
+
+	if (data[IFLA_GENEVE_REMOTE]) {
+		remote.sa.sa_family = AF_INET;
+		remote.sin.sin_addr.s_addr =
+			nla_get_in_addr(data[IFLA_GENEVE_REMOTE]);
+	}
+
+	if (data[IFLA_GENEVE_REMOTE6]) {
+		if (!IS_ENABLED(CONFIG_IPV6))
+			return -EPFNOSUPPORT;
+
+		remote.sa.sa_family = AF_INET6;
+		remote.sin6.sin6_addr =
+			nla_get_in6_addr(data[IFLA_GENEVE_REMOTE6]);
+
+		if (ipv6_addr_type(&remote.sin6.sin6_addr) &
+		    IPV6_ADDR_LINKLOCAL) {
+			netdev_dbg(dev, "link-local remote is unsupported\n");
+			return -EINVAL;
+		}
+	}
+
 	if (data[IFLA_GENEVE_ID])
 		vni = nla_get_u32(data[IFLA_GENEVE_ID]);
 
-	if (data[IFLA_GENEVE_REMOTE])
-		rem_addr = nla_get_in_addr(data[IFLA_GENEVE_REMOTE]);
-
 	if (data[IFLA_GENEVE_TTL])
 		ttl = nla_get_u8(data[IFLA_GENEVE_TTL]);
 
@@ -917,8 +1223,8 @@ static int geneve_newlink(struct net *net, struct net_device *dev,
 	if (data[IFLA_GENEVE_COLLECT_METADATA])
 		metadata = true;
 
-	return geneve_configure(net, dev, rem_addr, vni,
-				ttl, tos, dst_port, metadata);
+	return geneve_configure(net, dev, &remote, vni, ttl, tos, dst_port,
+				metadata);
 }
 
 static void geneve_dellink(struct net_device *dev, struct list_head *head)
@@ -932,7 +1238,7 @@ static void geneve_dellink(struct net_device *dev, struct list_head *head)
 static size_t geneve_get_size(const struct net_device *dev)
 {
 	return nla_total_size(sizeof(__u32)) +	/* IFLA_GENEVE_ID */
-		nla_total_size(sizeof(struct in_addr)) + /* IFLA_GENEVE_REMOTE */
+		nla_total_size(sizeof(struct in6_addr)) + /* IFLA_GENEVE_REMOTE{6} */
 		nla_total_size(sizeof(__u8)) +  /* IFLA_GENEVE_TTL */
 		nla_total_size(sizeof(__u8)) +  /* IFLA_GENEVE_TOS */
 		nla_total_size(sizeof(__be16)) +  /* IFLA_GENEVE_PORT */
@@ -949,9 +1255,17 @@ static int geneve_fill_info(struct sk_buff *skb, const struct net_device *dev)
 	if (nla_put_u32(skb, IFLA_GENEVE_ID, vni))
 		goto nla_put_failure;
 
-	if (nla_put_in_addr(skb, IFLA_GENEVE_REMOTE,
-			    geneve->remote.sin_addr.s_addr))
-		goto nla_put_failure;
+	if (geneve->remote.sa.sa_family == AF_INET) {
+		if (nla_put_in_addr(skb, IFLA_GENEVE_REMOTE,
+				    geneve->remote.sin.sin_addr.s_addr))
+			goto nla_put_failure;
+#if IS_ENABLED(CONFIG_IPV6)
+	} else {
+		if (nla_put_in6_addr(skb, IFLA_GENEVE_REMOTE6,
+				     &geneve->remote.sin6.sin6_addr))
+			goto nla_put_failure;
+#endif
+	}
 
 	if (nla_put_u8(skb, IFLA_GENEVE_TTL, geneve->ttl) ||
 	    nla_put_u8(skb, IFLA_GENEVE_TOS, geneve->tos))
@@ -997,7 +1311,8 @@ struct net_device *geneve_dev_create_fb(struct net *net, const char *name,
 	if (IS_ERR(dev))
 		return dev;
 
-	err = geneve_configure(net, dev, 0, 0, 0, 0, htons(dst_port), true);
+	err = geneve_configure(net, dev, &geneve_remote_unspec,
+			       0, 0, 0, htons(dst_port), true);
 	if (err) {
 		free_netdev(dev);
 		return ERR_PTR(err);
diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
index a7aea8418abb..5ad57375a99f 100644
--- a/include/uapi/linux/if_link.h
+++ b/include/uapi/linux/if_link.h
@@ -461,6 +461,7 @@ enum {
 	IFLA_GENEVE_TOS,
 	IFLA_GENEVE_PORT,	/* destination port */
 	IFLA_GENEVE_COLLECT_METADATA,
+	IFLA_GENEVE_REMOTE6,
 	__IFLA_GENEVE_MAX
 };
 #define IFLA_GENEVE_MAX	(__IFLA_GENEVE_MAX - 1)
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v7 2/3] geneve: handle ipv6 priority like ipv4 tos
  2015-10-26 21:01           ` [PATCH v7 1/3] " John W. Linville
@ 2015-10-26 21:01             ` John W. Linville
  2015-10-30  3:11               ` David Miller
  2015-10-26 21:01             ` [PATCH v7 3/3] geneve: add IPv6 bits to geneve_fill_metadata_dst John W. Linville
  2015-10-30  3:11             ` [PATCH v7 1/3] geneve: implement support for IPv6-based tunnels David Miller
  2 siblings, 1 reply; 37+ messages in thread
From: John W. Linville @ 2015-10-26 21:01 UTC (permalink / raw)
  To: netdev
  Cc: Dave Miller, Pravin B Shelar, Jesse Gross, Jiri Benc,
	YOSHIFUJI Hideaki/吉藤英明,
	John W. Linville

Other callers of udp_tunnel6_xmit_skb just pass 0 for the prio
argument.  Jesse Gross <jesse@nicira.com> suggested that prio is really
the same as IPv4's tos and should be handled the same, so this is my
interpretation of that suggestion.

Signed-off-by: John W. Linville <linville@tuxdriver.com>
Reported-by: Jesse Gross <jesse@nicira.com>
Reviewed-by: Jesse Gross <jesse@nicira.com>
---
v7 -- same as previous revisions

 drivers/net/geneve.c | 19 +++++++++++++++++--
 1 file changed, 17 insertions(+), 2 deletions(-)

diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c
index 393b0bddf7cf..44e724508c55 100644
--- a/drivers/net/geneve.c
+++ b/drivers/net/geneve.c
@@ -763,6 +763,7 @@ static struct dst_entry *geneve_get_v6_dst(struct sk_buff *skb,
 	struct geneve_dev *geneve = netdev_priv(dev);
 	struct geneve_sock *gs6 = geneve->sock6;
 	struct dst_entry *dst = NULL;
+	__u8 prio;
 
 	memset(fl6, 0, sizeof(*fl6));
 	fl6->flowi6_mark = skb->mark;
@@ -771,7 +772,16 @@ static struct dst_entry *geneve_get_v6_dst(struct sk_buff *skb,
 	if (info) {
 		fl6->daddr = info->key.u.ipv6.dst;
 		fl6->saddr = info->key.u.ipv6.src;
+		fl6->flowi6_tos = RT_TOS(info->key.tos);
 	} else {
+		prio = geneve->tos;
+		if (prio == 1) {
+			const struct iphdr *iip = ip_hdr(skb);
+
+			prio = ip_tunnel_get_dsfield(iip, skb);
+		}
+
+		fl6->flowi6_tos = RT_TOS(prio);
 		fl6->daddr = geneve->remote.sin6.sin6_addr;
 	}
 
@@ -897,9 +907,10 @@ static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
 	struct geneve_dev *geneve = netdev_priv(dev);
 	struct geneve_sock *gs6 = geneve->sock6;
 	struct dst_entry *dst = NULL;
+	const struct iphdr *iip; /* interior IP header */
 	int err = -EINVAL;
 	struct flowi6 fl6;
-	__u8 ttl;
+	__u8 prio, ttl;
 	__be16 sport;
 	bool udp_csum;
 	bool xnet = !net_eq(geneve->net, dev_net(geneve->dev));
@@ -920,6 +931,8 @@ static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
 	sport = udp_flow_src_port(geneve->net, skb, 1, USHRT_MAX, true);
 	skb_reset_mac_header(skb);
 
+	iip = ip_hdr(skb);
+
 	if (info) {
 		const struct ip_tunnel_key *key = &info->key;
 		u8 *opts = NULL;
@@ -936,6 +949,7 @@ static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
 		if (unlikely(err))
 			goto err;
 
+		prio = ip_tunnel_ecn_encap(key->tos, iip, skb);
 		ttl = key->ttl;
 	} else {
 		udp_csum = false;
@@ -944,13 +958,14 @@ static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
 		if (unlikely(err))
 			goto err;
 
+		prio = ip_tunnel_ecn_encap(fl6.flowi6_tos, iip, skb);
 		ttl = geneve->ttl;
 		if (!ttl && ipv6_addr_is_multicast(&fl6.daddr))
 			ttl = 1;
 		ttl = ttl ? : ip6_dst_hoplimit(dst);
 	}
 	err = udp_tunnel6_xmit_skb(dst, gs6->sock->sk, skb, dev,
-				   &fl6.saddr, &fl6.daddr, 0, ttl,
+				   &fl6.saddr, &fl6.daddr, prio, ttl,
 				   sport, geneve->dst_port, !udp_csum);
 
 	iptunnel_xmit_stats(err, &dev->stats, dev->tstats);
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v7 3/3] geneve: add IPv6 bits to geneve_fill_metadata_dst
  2015-10-26 21:01           ` [PATCH v7 1/3] " John W. Linville
  2015-10-26 21:01             ` [PATCH v7 2/3] geneve: handle ipv6 priority like ipv4 tos John W. Linville
@ 2015-10-26 21:01             ` John W. Linville
  2015-10-27 12:48               ` Sergei Shtylyov
  2015-10-27 13:49               ` [PATCH v8 " John W. Linville
  2015-10-30  3:11             ` [PATCH v7 1/3] geneve: implement support for IPv6-based tunnels David Miller
  2 siblings, 2 replies; 37+ messages in thread
From: John W. Linville @ 2015-10-26 21:01 UTC (permalink / raw)
  To: netdev
  Cc: Dave Miller, Pravin B Shelar, Jesse Gross, Jiri Benc,
	YOSHIFUJI Hideaki/吉藤英明,
	John W. Linville

Signed-off-by: John W. Linville <linville@tuxdriver.com>
---
v7 -- initial version (numbered to match earlier patches in series)

 drivers/net/geneve.c | 28 +++++++++++++++++++++-------
 1 file changed, 21 insertions(+), 7 deletions(-)

diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c
index 44e724508c55..be532d7b879d 100644
--- a/drivers/net/geneve.c
+++ b/drivers/net/geneve.c
@@ -1006,16 +1006,30 @@ static int geneve_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb)
 	struct geneve_dev *geneve = netdev_priv(dev);
 	struct rtable *rt;
 	struct flowi4 fl4;
+#if IS_ENABLED(CONFIG_IPV6)
+	struct dst_entry *dst;
+	struct flowi6 fl6;
+#endif
 
-	if (ip_tunnel_info_af(info) != AF_INET)
-		return -EINVAL;
+	if (ip_tunnel_info_af(info) == AF_INET) {
+		rt = geneve_get_v4_rt(skb, dev, &fl4, info);
+		if (IS_ERR(rt))
+			return PTR_ERR(rt);
 
-	rt = geneve_get_v4_rt(skb, dev, &fl4, info);
-	if (IS_ERR(rt))
-		return PTR_ERR(rt);
+		ip_rt_put(rt);
+		info->key.u.ipv4.src = fl4.saddr;
+#if IS_ENABLED(CONFIG_IPV6)
+	} else if (ip_tunnel_info_af(info) == AF_INET6) {
+		dst = geneve_get_v6_dst(skb, dev, &fl6, info);
+		if (IS_ERR(dst))
+			return PTR_ERR(dst);
+
+		dst_release(dst);
+		info->key.u.ipv6.src = fl6.saddr;
+#endif
+	} else
+		return -EINVAL;
 
-	ip_rt_put(rt);
-	info->key.u.ipv4.src = fl4.saddr;
 	info->key.tp_src = udp_flow_src_port(geneve->net, skb,
 					     1, USHRT_MAX, true);
 	info->key.tp_dst = geneve->dst_port;
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* Re: [PATCH v7 3/3] geneve: add IPv6 bits to geneve_fill_metadata_dst
  2015-10-26 21:01             ` [PATCH v7 3/3] geneve: add IPv6 bits to geneve_fill_metadata_dst John W. Linville
@ 2015-10-27 12:48               ` Sergei Shtylyov
  2015-10-27 13:49               ` [PATCH v8 " John W. Linville
  1 sibling, 0 replies; 37+ messages in thread
From: Sergei Shtylyov @ 2015-10-27 12:48 UTC (permalink / raw)
  To: John W. Linville, netdev
  Cc: Dave Miller, Pravin B Shelar, Jesse Gross, Jiri Benc,
	YOSHIFUJI Hideaki/吉藤英明

Hello.

On 10/27/2015 12:01 AM, John W. Linville wrote:

> Signed-off-by: John W. Linville <linville@tuxdriver.com>
> ---
> v7 -- initial version (numbered to match earlier patches in series)
>
>   drivers/net/geneve.c | 28 +++++++++++++++++++++-------
>   1 file changed, 21 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c
> index 44e724508c55..be532d7b879d 100644
> --- a/drivers/net/geneve.c
> +++ b/drivers/net/geneve.c
> @@ -1006,16 +1006,30 @@ static int geneve_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb)
>   	struct geneve_dev *geneve = netdev_priv(dev);
>   	struct rtable *rt;
>   	struct flowi4 fl4;
> +#if IS_ENABLED(CONFIG_IPV6)
> +	struct dst_entry *dst;
> +	struct flowi6 fl6;
> +#endif
>
> -	if (ip_tunnel_info_af(info) != AF_INET)
> -		return -EINVAL;
> +	if (ip_tunnel_info_af(info) == AF_INET) {
> +		rt = geneve_get_v4_rt(skb, dev, &fl4, info);
> +		if (IS_ERR(rt))
> +			return PTR_ERR(rt);
>
> -	rt = geneve_get_v4_rt(skb, dev, &fl4, info);
> -	if (IS_ERR(rt))
> -		return PTR_ERR(rt);
> +		ip_rt_put(rt);
> +		info->key.u.ipv4.src = fl4.saddr;
> +#if IS_ENABLED(CONFIG_IPV6)
> +	} else if (ip_tunnel_info_af(info) == AF_INET6) {
> +		dst = geneve_get_v6_dst(skb, dev, &fl6, info);
> +		if (IS_ERR(dst))
> +			return PTR_ERR(dst);
> +
> +		dst_release(dst);
> +		info->key.u.ipv6.src = fl6.saddr;
> +#endif
> +	} else
> +		return -EINVAL;

    You need {} on this branch too, according to Documentation/CodingStyle.

MBR, Sergei

^ permalink raw reply	[flat|nested] 37+ messages in thread

* [PATCH v8 3/3] geneve: add IPv6 bits to geneve_fill_metadata_dst
  2015-10-26 21:01             ` [PATCH v7 3/3] geneve: add IPv6 bits to geneve_fill_metadata_dst John W. Linville
  2015-10-27 12:48               ` Sergei Shtylyov
@ 2015-10-27 13:49               ` John W. Linville
  2015-10-27 14:24                 ` Jesse Gross
  2015-10-30  3:12                 ` David Miller
  1 sibling, 2 replies; 37+ messages in thread
From: John W. Linville @ 2015-10-27 13:49 UTC (permalink / raw)
  To: netdev
  Cc: Dave Miller, Pravin B Shelar, Jesse Gross, Jiri Benc,
	YOSHIFUJI Hideaki/吉藤英明,
	Sergei Shtylyov, John W. Linville

Signed-off-by: John W. Linville <linville@tuxdriver.com>
---
v7 -- initial version (numbered to match earlier patches in series)
v8 -- fixup of bracing on an "else { return -EINVAL; }"

 drivers/net/geneve.c | 29 ++++++++++++++++++++++-------
 1 file changed, 22 insertions(+), 7 deletions(-)

diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c
index 44e724508c55..de5c30c9f059 100644
--- a/drivers/net/geneve.c
+++ b/drivers/net/geneve.c
@@ -1006,16 +1006,31 @@ static int geneve_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb)
 	struct geneve_dev *geneve = netdev_priv(dev);
 	struct rtable *rt;
 	struct flowi4 fl4;
+#if IS_ENABLED(CONFIG_IPV6)
+	struct dst_entry *dst;
+	struct flowi6 fl6;
+#endif
 
-	if (ip_tunnel_info_af(info) != AF_INET)
-		return -EINVAL;
+	if (ip_tunnel_info_af(info) == AF_INET) {
+		rt = geneve_get_v4_rt(skb, dev, &fl4, info);
+		if (IS_ERR(rt))
+			return PTR_ERR(rt);
 
-	rt = geneve_get_v4_rt(skb, dev, &fl4, info);
-	if (IS_ERR(rt))
-		return PTR_ERR(rt);
+		ip_rt_put(rt);
+		info->key.u.ipv4.src = fl4.saddr;
+#if IS_ENABLED(CONFIG_IPV6)
+	} else if (ip_tunnel_info_af(info) == AF_INET6) {
+		dst = geneve_get_v6_dst(skb, dev, &fl6, info);
+		if (IS_ERR(dst))
+			return PTR_ERR(dst);
+
+		dst_release(dst);
+		info->key.u.ipv6.src = fl6.saddr;
+#endif
+	} else {
+		return -EINVAL;
+	}
 
-	ip_rt_put(rt);
-	info->key.u.ipv4.src = fl4.saddr;
 	info->key.tp_src = udp_flow_src_port(geneve->net, skb,
 					     1, USHRT_MAX, true);
 	info->key.tp_dst = geneve->dst_port;
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* Re: [PATCH v8 3/3] geneve: add IPv6 bits to geneve_fill_metadata_dst
  2015-10-27 13:49               ` [PATCH v8 " John W. Linville
@ 2015-10-27 14:24                 ` Jesse Gross
  2015-10-30  3:12                 ` David Miller
  1 sibling, 0 replies; 37+ messages in thread
From: Jesse Gross @ 2015-10-27 14:24 UTC (permalink / raw)
  To: John W. Linville
  Cc: netdev, Dave Miller, Pravin B Shelar, Jiri Benc,
	YOSHIFUJI Hideaki/吉藤英明,
	Sergei Shtylyov

On Tue, Oct 27, 2015 at 9:49 PM, John W. Linville
<linville@tuxdriver.com> wrote:
> Signed-off-by: John W. Linville <linville@tuxdriver.com>
> ---
> v7 -- initial version (numbered to match earlier patches in series)
> v8 -- fixup of bracing on an "else { return -EINVAL; }"

Reviewed-by: Jesse Gross <jesse@nicira.com>

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v7 1/3] geneve: implement support for IPv6-based tunnels
  2015-10-26 21:01           ` [PATCH v7 1/3] " John W. Linville
  2015-10-26 21:01             ` [PATCH v7 2/3] geneve: handle ipv6 priority like ipv4 tos John W. Linville
  2015-10-26 21:01             ` [PATCH v7 3/3] geneve: add IPv6 bits to geneve_fill_metadata_dst John W. Linville
@ 2015-10-30  3:11             ` David Miller
  2 siblings, 0 replies; 37+ messages in thread
From: David Miller @ 2015-10-30  3:11 UTC (permalink / raw)
  To: linville; +Cc: netdev, pshelar, jesse, jbenc, hideaki.yoshifuji

From: "John W. Linville" <linville@tuxdriver.com>
Date: Mon, 26 Oct 2015 17:01:44 -0400

> NOTE: Link-local IPv6 addresses for remote endpoints are not supported,
> since the driver currently has no capacity for binding a geneve
> interface to a specific link.
> 
> Signed-off-by: John W. Linville <linville@tuxdriver.com>
> Reviewed-by: Jesse Gross <jesse@nicira.com>

Applied.

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v7 2/3] geneve: handle ipv6 priority like ipv4 tos
  2015-10-26 21:01             ` [PATCH v7 2/3] geneve: handle ipv6 priority like ipv4 tos John W. Linville
@ 2015-10-30  3:11               ` David Miller
  0 siblings, 0 replies; 37+ messages in thread
From: David Miller @ 2015-10-30  3:11 UTC (permalink / raw)
  To: linville; +Cc: netdev, pshelar, jesse, jbenc, hideaki.yoshifuji

From: "John W. Linville" <linville@tuxdriver.com>
Date: Mon, 26 Oct 2015 17:01:45 -0400

> Other callers of udp_tunnel6_xmit_skb just pass 0 for the prio
> argument.  Jesse Gross <jesse@nicira.com> suggested that prio is really
> the same as IPv4's tos and should be handled the same, so this is my
> interpretation of that suggestion.
> 
> Signed-off-by: John W. Linville <linville@tuxdriver.com>
> Reported-by: Jesse Gross <jesse@nicira.com>
> Reviewed-by: Jesse Gross <jesse@nicira.com>

Applied.

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v8 3/3] geneve: add IPv6 bits to geneve_fill_metadata_dst
  2015-10-27 13:49               ` [PATCH v8 " John W. Linville
  2015-10-27 14:24                 ` Jesse Gross
@ 2015-10-30  3:12                 ` David Miller
  1 sibling, 0 replies; 37+ messages in thread
From: David Miller @ 2015-10-30  3:12 UTC (permalink / raw)
  To: linville
  Cc: netdev, pshelar, jesse, jbenc, hideaki.yoshifuji, sergei.shtylyov

From: "John W. Linville" <linville@tuxdriver.com>
Date: Tue, 27 Oct 2015 09:49:00 -0400

> Signed-off-by: John W. Linville <linville@tuxdriver.com>

Applied.

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH iproute2] geneve: add support for IPv6 link partners
  2015-09-24 18:39 ` [PATCH iproute2] geneve: add support for IPv6 link partners John W. Linville
@ 2015-11-24  0:23   ` Stephen Hemminger
  0 siblings, 0 replies; 37+ messages in thread
From: Stephen Hemminger @ 2015-11-24  0:23 UTC (permalink / raw)
  To: John W. Linville; +Cc: netdev, Pravin B Shelar, Jesse Gross, davem

On Thu, 24 Sep 2015 14:39:39 -0400
"John W. Linville" <linville@tuxdriver.com> wrote:

> Signed-off-by: John W. Linville <linville@tuxdriver.com>
> ---
>  include/linux/if_link.h |  1 +
>  ip/iplink_geneve.c      | 20 +++++++++++++-------
>  2 files changed, 14 insertions(+), 7 deletions(-)

Applied, the if_link.h change was already picked up.

^ permalink raw reply	[flat|nested] 37+ messages in thread

end of thread, other threads:[~2015-11-24  0:24 UTC | newest]

Thread overview: 37+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-09-24 18:34 [RFT] geneve: implement support for IPv6-based tunnels John W. Linville
2015-09-24 18:39 ` [PATCH iproute2] geneve: add support for IPv6 link partners John W. Linville
2015-11-24  0:23   ` Stephen Hemminger
2015-09-25 12:08 ` [RFT] geneve: implement support for IPv6-based tunnels Jiri Benc
2015-09-28 19:20   ` John W. Linville
2015-09-29 16:10     ` Jiri Benc
2015-09-30 17:04 ` [RFT v2] " John W. Linville
2015-09-30 18:07   ` kbuild test robot
2015-09-30 18:34   ` [RFT v3] " John W. Linville
2015-10-01  1:55     ` kbuild test robot
2015-10-01 15:38     ` Jiri Benc
2015-10-01 16:26     ` Jesse Gross
2015-10-01 20:03       ` John W. Linville
2015-10-01 21:07         ` Jesse Gross
2015-10-20 15:11     ` [PATCH v4 1/2] " John W. Linville
2015-10-20 15:11       ` [PATCH 2/2] geneve: handle ipv6 priority like ipv4 tos John W. Linville
2015-10-21  5:13         ` Jesse Gross
2015-10-20 22:55       ` [PATCH v4 1/2] geneve: implement support for IPv6-based tunnels kbuild test robot
2015-10-21  1:52       ` YOSHIFUJI Hideaki/吉藤英明
2015-10-21 18:58         ` John W. Linville
2015-10-21  5:06       ` Jesse Gross
2015-10-22 19:45       ` [PATCH v5 " John W. Linville
2015-10-22 19:45         ` [PATCH v5 2/2] geneve: handle ipv6 priority like ipv4 tos John W. Linville
2015-10-23  4:48         ` [PATCH v5 1/2] geneve: implement support for IPv6-based tunnels YOSHIFUJI Hideaki
2015-10-23 13:38           ` John W. Linville
2015-10-23 14:40         ` [PATCH v6 " John W. Linville
2015-10-23 14:40           ` [PATCH v6 2/2] geneve: handle ipv6 priority like ipv4 tos John W. Linville
2015-10-26  4:08           ` [PATCH v6 1/2] geneve: implement support for IPv6-based tunnels Jesse Gross
2015-10-26 21:01           ` [PATCH v7 1/3] " John W. Linville
2015-10-26 21:01             ` [PATCH v7 2/3] geneve: handle ipv6 priority like ipv4 tos John W. Linville
2015-10-30  3:11               ` David Miller
2015-10-26 21:01             ` [PATCH v7 3/3] geneve: add IPv6 bits to geneve_fill_metadata_dst John W. Linville
2015-10-27 12:48               ` Sergei Shtylyov
2015-10-27 13:49               ` [PATCH v8 " John W. Linville
2015-10-27 14:24                 ` Jesse Gross
2015-10-30  3:12                 ` David Miller
2015-10-30  3:11             ` [PATCH v7 1/3] geneve: implement support for IPv6-based tunnels David Miller

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).