netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [Patch net-next v8 00/11] vxlan: add ipv6 support
@ 2013-05-17  0:21 Cong Wang
  2013-05-17  0:21 ` [Patch net-next v8 01/11] vxlan: defer vxlan init as late as possible Cong Wang
                   ` (10 more replies)
  0 siblings, 11 replies; 23+ messages in thread
From: Cong Wang @ 2013-05-17  0:21 UTC (permalink / raw)
  To: netdev

From: Cong Wang <amwang@redhat.com>

v8: fix the bug when bindv6only=1
    fix more compile errors when IPV6=m
    complete the rest missing features for IPv6

v7: respect disable_ipv6 flag
    back to ipv4 only when ipv6 is not supported

v6: use a stub for IPv6 mcast functions
    split a few more long lines
    rebased on the latest net-next

v5: make David happy on the names of the fields
    fix my mistake during rebasing the patches
    drop the scope_id patch, because it is broken
    export in6addr_loopback
    fix a udp checksum bug
    rebased on the latest net-next

v4: rename ->sin to ->va_sin
    rename ->sin6 to ->va_sin6
    rename ->family to ->va_sa
    support ll addr
    fix more ugly #ifdef
    rebased on the latest net-next

v3: fix many coding style issues
    fix some ugly #ifdef
    rename vxlan_ip to vxlan_addr
    rename ->proto to ->family
    rename ->ip4/->ip6 to ->sin/->sin6

v2: fix some compile error when !CONFIG_IPV6
    improve some code based on Stephen's comments
    use sockaddr suggested by David


Cong Wang (11):
  vxlan: defer vxlan init as late as possible
  ipv6: make ip6_dst_hoplimit() static inline
  ipv6: move ip6_local_out into core kernel
  ipv6: export a stub for IPv6 symbols used by vxlan
  ipv6: export in6addr_loopback to modules
  vxlan: add ipv6 support
  vxlan: respect disable_ipv6 sysctl
  vxlan: add ipv6 route short circuit support
  vxlan: add ipv6 proxy support
  vxlan: respect scope_id for ll addr
  ipv6: Add generic UDP Tunnel segmentation

 drivers/net/vxlan.c          |  829 ++++++++++++++++++++++++++++++++++--------
 include/net/addrconf.h       |   18 +
 include/net/ip6_route.h      |   23 +-
 include/net/ndisc.h          |    5 +
 include/uapi/linux/if_link.h |    2 +
 net/ipv6/addrconf.c          |    9 -
 net/ipv6/addrconf_core.c     |   13 +
 net/ipv6/af_inet6.c          |   12 +
 net/ipv6/ip6_offload.c       |    4 +-
 net/ipv6/ip6_output.c        |   25 --
 net/ipv6/ndisc.c             |    8 +-
 net/ipv6/output_core.c       |   26 ++
 net/ipv6/route.c             |   19 -
 net/ipv6/udp_offload.c       |  153 ++++++---
 14 files changed, 893 insertions(+), 253 deletions(-)

-- 
1.7.7.6

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

* [Patch net-next v8 01/11] vxlan: defer vxlan init as late as possible
  2013-05-17  0:21 [Patch net-next v8 00/11] vxlan: add ipv6 support Cong Wang
@ 2013-05-17  0:21 ` Cong Wang
  2013-05-17  0:21 ` [Patch net-next v8 02/11] ipv6: make ip6_dst_hoplimit() static inline Cong Wang
                   ` (9 subsequent siblings)
  10 siblings, 0 replies; 23+ messages in thread
From: Cong Wang @ 2013-05-17  0:21 UTC (permalink / raw)
  To: netdev; +Cc: Stephen Hemminger, David S. Miller, Cong Wang

From: Cong Wang <amwang@redhat.com>

When vxlan is compiled as builtin, its init code
runs before IPv6 init, this could cause problems
if we create IPv6 socket in the latter patch.

Cc: Stephen Hemminger <stephen@networkplumber.org>
Cc: David S. Miller <davem@davemloft.net>
Signed-off-by: Cong Wang <amwang@redhat.com>
---
 drivers/net/vxlan.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
index ba81f3c..c1258c6 100644
--- a/drivers/net/vxlan.c
+++ b/drivers/net/vxlan.c
@@ -1662,7 +1662,7 @@ out2:
 out1:
 	return rc;
 }
-module_init(vxlan_init_module);
+late_initcall(vxlan_init_module);
 
 static void __exit vxlan_cleanup_module(void)
 {
-- 
1.7.7.6

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

* [Patch net-next v8 02/11] ipv6: make ip6_dst_hoplimit() static inline
  2013-05-17  0:21 [Patch net-next v8 00/11] vxlan: add ipv6 support Cong Wang
  2013-05-17  0:21 ` [Patch net-next v8 01/11] vxlan: defer vxlan init as late as possible Cong Wang
@ 2013-05-17  0:21 ` Cong Wang
  2013-05-17 13:02   ` Sergei Shtylyov
  2013-05-17 21:13   ` David Miller
  2013-05-17  0:21 ` [Patch net-next v8 03/11] ipv6: move ip6_local_out into core kernel Cong Wang
                   ` (8 subsequent siblings)
  10 siblings, 2 replies; 23+ messages in thread
From: Cong Wang @ 2013-05-17  0:21 UTC (permalink / raw)
  To: netdev; +Cc: David S. Miller, Cong Wang

From: Cong Wang <amwang@redhat.com>

It will be used by vxlan module, so move it from ipv6 module
to core kernel. I think it is small enough to be inlined.

Cc: David S. Miller <davem@davemloft.net>
Signed-off-by: Cong Wang <amwang@redhat.com>
---
 include/net/ip6_route.h |   23 +++++++++++++++++++++--
 net/ipv6/route.c        |   19 -------------------
 2 files changed, 21 insertions(+), 21 deletions(-)

diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h
index 260f83f..7e9192e 100644
--- a/include/net/ip6_route.h
+++ b/include/net/ip6_route.h
@@ -21,6 +21,7 @@ struct route_info {
 #include <net/flow.h>
 #include <net/ip6_fib.h>
 #include <net/sock.h>
+#include <net/addrconf.h>
 #include <linux/ip.h>
 #include <linux/ipv6.h>
 #include <linux/route.h>
@@ -112,8 +113,6 @@ extern struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
 					   const struct in6_addr *addr,
 					   bool anycast);
 
-extern int			ip6_dst_hoplimit(struct dst_entry *dst);
-
 /*
  *	support functions for ND
  *
@@ -201,4 +200,24 @@ static inline struct in6_addr *rt6_nexthop(struct rt6_info *rt, struct in6_addr
 	return dest;
 }
 
+#if IS_ENABLED(CONFIG_IPV6)
+static inline int ip6_dst_hoplimit(struct dst_entry *dst)
+{
+	int hoplimit = dst_metric_raw(dst, RTAX_HOPLIMIT);
+	if (hoplimit == 0) {
+		struct net_device *dev = dst->dev;
+		struct inet6_dev *idev;
+
+		rcu_read_lock();
+		idev = __in6_dev_get(dev);
+		if (idev)
+			hoplimit = idev->cnf.hop_limit;
+		else
+			hoplimit = dev_net(dev)->ipv6.devconf_all->hop_limit;
+		rcu_read_unlock();
+	}
+	return hoplimit;
+}
+#endif
+
 #endif
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index ad0aa6b..0d9c531 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -1310,25 +1310,6 @@ out:
 	return entries > rt_max_size;
 }
 
-int ip6_dst_hoplimit(struct dst_entry *dst)
-{
-	int hoplimit = dst_metric_raw(dst, RTAX_HOPLIMIT);
-	if (hoplimit == 0) {
-		struct net_device *dev = dst->dev;
-		struct inet6_dev *idev;
-
-		rcu_read_lock();
-		idev = __in6_dev_get(dev);
-		if (idev)
-			hoplimit = idev->cnf.hop_limit;
-		else
-			hoplimit = dev_net(dev)->ipv6.devconf_all->hop_limit;
-		rcu_read_unlock();
-	}
-	return hoplimit;
-}
-EXPORT_SYMBOL(ip6_dst_hoplimit);
-
 /*
  *
  */
-- 
1.7.7.6

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

* [Patch net-next v8 03/11] ipv6: move ip6_local_out into core kernel
  2013-05-17  0:21 [Patch net-next v8 00/11] vxlan: add ipv6 support Cong Wang
  2013-05-17  0:21 ` [Patch net-next v8 01/11] vxlan: defer vxlan init as late as possible Cong Wang
  2013-05-17  0:21 ` [Patch net-next v8 02/11] ipv6: make ip6_dst_hoplimit() static inline Cong Wang
@ 2013-05-17  0:21 ` Cong Wang
  2013-05-17  0:21 ` [Patch net-next v8 04/11] ipv6: export a stub for IPv6 symbols used by vxlan Cong Wang
                   ` (7 subsequent siblings)
  10 siblings, 0 replies; 23+ messages in thread
From: Cong Wang @ 2013-05-17  0:21 UTC (permalink / raw)
  To: netdev; +Cc: David S. Miller, Cong Wang

From: Cong Wang <amwang@redhat.com>

It will be used by vxlan module too.

Cc: David S. Miller <davem@davemloft.net>
Signed-off-by: Cong Wang <amwang@redhat.com>
---
 net/ipv6/ip6_output.c  |   25 -------------------------
 net/ipv6/output_core.c |   26 ++++++++++++++++++++++++++
 2 files changed, 26 insertions(+), 25 deletions(-)

diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index d2eedf1..316895e 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -56,31 +56,6 @@
 #include <net/checksum.h>
 #include <linux/mroute6.h>
 
-int __ip6_local_out(struct sk_buff *skb)
-{
-	int len;
-
-	len = skb->len - sizeof(struct ipv6hdr);
-	if (len > IPV6_MAXPLEN)
-		len = 0;
-	ipv6_hdr(skb)->payload_len = htons(len);
-
-	return nf_hook(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb, NULL,
-		       skb_dst(skb)->dev, dst_output);
-}
-
-int ip6_local_out(struct sk_buff *skb)
-{
-	int err;
-
-	err = __ip6_local_out(skb);
-	if (likely(err == 1))
-		err = dst_output(skb);
-
-	return err;
-}
-EXPORT_SYMBOL_GPL(ip6_local_out);
-
 static int ip6_finish_output2(struct sk_buff *skb)
 {
 	struct dst_entry *dst = skb_dst(skb);
diff --git a/net/ipv6/output_core.c b/net/ipv6/output_core.c
index c2e73e6..030d03f 100644
--- a/net/ipv6/output_core.c
+++ b/net/ipv6/output_core.c
@@ -74,3 +74,29 @@ int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr)
 	return offset;
 }
 EXPORT_SYMBOL(ip6_find_1stfragopt);
+
+int __ip6_local_out(struct sk_buff *skb)
+{
+	int len;
+
+	len = skb->len - sizeof(struct ipv6hdr);
+	if (len > IPV6_MAXPLEN)
+		len = 0;
+	ipv6_hdr(skb)->payload_len = htons(len);
+
+	return nf_hook(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb, NULL,
+		       skb_dst(skb)->dev, dst_output);
+}
+EXPORT_SYMBOL_GPL(__ip6_local_out);
+
+int ip6_local_out(struct sk_buff *skb)
+{
+	int err;
+
+	err = __ip6_local_out(skb);
+	if (likely(err == 1))
+		err = dst_output(skb);
+
+	return err;
+}
+EXPORT_SYMBOL_GPL(ip6_local_out);
-- 
1.7.7.6

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

* [Patch net-next v8 04/11] ipv6: export a stub for IPv6 symbols used by vxlan
  2013-05-17  0:21 [Patch net-next v8 00/11] vxlan: add ipv6 support Cong Wang
                   ` (2 preceding siblings ...)
  2013-05-17  0:21 ` [Patch net-next v8 03/11] ipv6: move ip6_local_out into core kernel Cong Wang
@ 2013-05-17  0:21 ` Cong Wang
  2013-05-17  0:21 ` [Patch net-next v8 05/11] ipv6: export in6addr_loopback to modules Cong Wang
                   ` (6 subsequent siblings)
  10 siblings, 0 replies; 23+ messages in thread
From: Cong Wang @ 2013-05-17  0:21 UTC (permalink / raw)
  To: netdev
  Cc: Ben Hutchings, Bjørn Mork, Stephen Hemminger,
	David S. Miller, Cong Wang

From: Cong Wang <amwang@redhat.com>

In case IPv6 is compiled as a module, introduce a stub
for ipv6_sock_mc_join and ipv6_sock_mc_drop etc.. It will be used
by vxlan module.

This is an ugly but easy solution for now.

Suggested-by: Ben Hutchings <bhutchings@solarflare.com>
Cc: Ben Hutchings <bhutchings@solarflare.com>
Cc: Bjørn Mork <bjorn@mork.no>
Cc: Stephen Hemminger <stephen@networkplumber.org>
Cc: David S. Miller <davem@davemloft.net>
Signed-off-by: Cong Wang <amwang@redhat.com>
---
 include/net/addrconf.h   |   14 ++++++++++++++
 net/ipv6/addrconf_core.c |    3 +++
 net/ipv6/af_inet6.c      |   11 +++++++++++
 3 files changed, 28 insertions(+), 0 deletions(-)

diff --git a/include/net/addrconf.h b/include/net/addrconf.h
index 84a6440..d09d42c 100644
--- a/include/net/addrconf.h
+++ b/include/net/addrconf.h
@@ -140,6 +140,20 @@ extern bool inet6_mc_check(struct sock *sk,
 			   const struct in6_addr *mc_addr,
 			   const struct in6_addr *src_addr);
 
+/* A stub used by vxlan module. This is ugly, ideally these
+ * symbols should be built into the core kernel.
+ */
+struct ipv6_stub {
+	int (*ipv6_sock_mc_join)(struct sock *sk, int ifindex,
+				 const struct in6_addr *addr);
+	int (*ipv6_sock_mc_drop)(struct sock *sk, int ifindex,
+				 const struct in6_addr *addr);
+	int (*ipv6_dst_lookup)(struct sock *sk, struct dst_entry **dst,
+				struct flowi6 *fl6);
+	void (*udpv6_encap_enable)(void);
+};
+extern const struct ipv6_stub *ipv6_stub __read_mostly;
+
 extern int ipv6_dev_mc_inc(struct net_device *dev, const struct in6_addr *addr);
 extern int __ipv6_dev_mc_dec(struct inet6_dev *idev, const struct in6_addr *addr);
 extern int ipv6_dev_mc_dec(struct net_device *dev, const struct in6_addr *addr);
diff --git a/net/ipv6/addrconf_core.c b/net/ipv6/addrconf_core.c
index 7210456..3807a79 100644
--- a/net/ipv6/addrconf_core.c
+++ b/net/ipv6/addrconf_core.c
@@ -97,3 +97,6 @@ int inet6addr_notifier_call_chain(unsigned long val, void *v)
 	return atomic_notifier_call_chain(&inet6addr_chain, val, v);
 }
 EXPORT_SYMBOL(inet6addr_notifier_call_chain);
+
+const struct ipv6_stub *ipv6_stub __read_mostly;
+EXPORT_SYMBOL_GPL(ipv6_stub);
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index ab5c7ad..58de055 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -808,6 +808,13 @@ static struct pernet_operations inet6_net_ops = {
 	.exit = inet6_net_exit,
 };
 
+static const struct ipv6_stub ipv6_stub_impl = {
+	.ipv6_sock_mc_join = ipv6_sock_mc_join,
+	.ipv6_sock_mc_drop = ipv6_sock_mc_drop,
+	.ipv6_dst_lookup = ip6_dst_lookup,
+	.udpv6_encap_enable = udpv6_encap_enable,
+};
+
 static int __init inet6_init(void)
 {
 	struct list_head *r;
@@ -879,6 +886,9 @@ static int __init inet6_init(void)
 	err = igmp6_init();
 	if (err)
 		goto igmp_fail;
+
+	ipv6_stub = &ipv6_stub_impl;
+
 	err = ipv6_netfilter_init();
 	if (err)
 		goto netfilter_fail;
@@ -1027,6 +1037,7 @@ static void __exit inet6_exit(void)
 	raw6_proc_exit();
 #endif
 	ipv6_netfilter_fini();
+	ipv6_stub = NULL;
 	igmp6_cleanup();
 	ndisc_cleanup();
 	ip6_mr_cleanup();
-- 
1.7.7.6

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

* [Patch net-next v8 05/11] ipv6: export in6addr_loopback to modules
  2013-05-17  0:21 [Patch net-next v8 00/11] vxlan: add ipv6 support Cong Wang
                   ` (3 preceding siblings ...)
  2013-05-17  0:21 ` [Patch net-next v8 04/11] ipv6: export a stub for IPv6 symbols used by vxlan Cong Wang
@ 2013-05-17  0:21 ` Cong Wang
  2013-05-17  0:21 ` [Patch net-next v8 06/11] vxlan: add ipv6 support Cong Wang
                   ` (5 subsequent siblings)
  10 siblings, 0 replies; 23+ messages in thread
From: Cong Wang @ 2013-05-17  0:21 UTC (permalink / raw)
  To: netdev; +Cc: Mike Rapoport, David S. Miller, Cong Wang

From: Cong Wang <amwang@redhat.com>

It is needed by vxlan module. Noticed by Mike.

Cc: Mike Rapoport <mike.rapoport@ravellosystems.com>
Cc: David S. Miller <davem@davemloft.net>
Signed-off-by: Cong Wang <amwang@redhat.com>
---
 net/ipv6/addrconf.c      |    9 ---------
 net/ipv6/addrconf_core.c |   10 ++++++++++
 2 files changed, 10 insertions(+), 9 deletions(-)

diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index d1ab6ab..650a109 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -238,15 +238,6 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
 	.accept_dad		= 1,
 };
 
-/* IPv6 Wildcard Address and Loopback Address defined by RFC2553 */
-const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
-const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT;
-const struct in6_addr in6addr_linklocal_allnodes = IN6ADDR_LINKLOCAL_ALLNODES_INIT;
-const struct in6_addr in6addr_linklocal_allrouters = IN6ADDR_LINKLOCAL_ALLROUTERS_INIT;
-const struct in6_addr in6addr_interfacelocal_allnodes = IN6ADDR_INTERFACELOCAL_ALLNODES_INIT;
-const struct in6_addr in6addr_interfacelocal_allrouters = IN6ADDR_INTERFACELOCAL_ALLROUTERS_INIT;
-const struct in6_addr in6addr_sitelocal_allrouters = IN6ADDR_SITELOCAL_ALLROUTERS_INIT;
-
 /* Check if a valid qdisc is available */
 static inline bool addrconf_qdisc_ok(const struct net_device *dev)
 {
diff --git a/net/ipv6/addrconf_core.c b/net/ipv6/addrconf_core.c
index 3807a79..cb991dd 100644
--- a/net/ipv6/addrconf_core.c
+++ b/net/ipv6/addrconf_core.c
@@ -100,3 +100,13 @@ EXPORT_SYMBOL(inet6addr_notifier_call_chain);
 
 const struct ipv6_stub *ipv6_stub __read_mostly;
 EXPORT_SYMBOL_GPL(ipv6_stub);
+
+/* IPv6 Wildcard Address and Loopback Address defined by RFC2553 */
+const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT;
+EXPORT_SYMBOL(in6addr_loopback);
+const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
+const struct in6_addr in6addr_linklocal_allnodes = IN6ADDR_LINKLOCAL_ALLNODES_INIT;
+const struct in6_addr in6addr_linklocal_allrouters = IN6ADDR_LINKLOCAL_ALLROUTERS_INIT;
+const struct in6_addr in6addr_interfacelocal_allnodes = IN6ADDR_INTERFACELOCAL_ALLNODES_INIT;
+const struct in6_addr in6addr_interfacelocal_allrouters = IN6ADDR_INTERFACELOCAL_ALLROUTERS_INIT;
+const struct in6_addr in6addr_sitelocal_allrouters = IN6ADDR_SITELOCAL_ALLROUTERS_INIT;
-- 
1.7.7.6

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

* [Patch net-next v8 06/11] vxlan: add ipv6 support
  2013-05-17  0:21 [Patch net-next v8 00/11] vxlan: add ipv6 support Cong Wang
                   ` (4 preceding siblings ...)
  2013-05-17  0:21 ` [Patch net-next v8 05/11] ipv6: export in6addr_loopback to modules Cong Wang
@ 2013-05-17  0:21 ` Cong Wang
  2013-05-17  0:21 ` [Patch net-next v8 07/11] vxlan: respect disable_ipv6 sysctl Cong Wang
                   ` (4 subsequent siblings)
  10 siblings, 0 replies; 23+ messages in thread
From: Cong Wang @ 2013-05-17  0:21 UTC (permalink / raw)
  To: netdev; +Cc: David Stevens, Stephen Hemminger, David S. Miller, Cong Wang

From: Cong Wang <amwang@redhat.com>

This patch adds IPv6 support to vxlan device, as the new version
RFC already mentions it:

   http://tools.ietf.org/html/draft-mahalingam-dutt-dcops-vxlan-03

Cc: David Stevens <dlstevens@us.ibm.com>
Cc: Stephen Hemminger <stephen@networkplumber.org>
Cc: David S. Miller <davem@davemloft.net>
Signed-off-by: Cong Wang <amwang@redhat.com>
---
 drivers/net/vxlan.c          |  698 +++++++++++++++++++++++++++++++++---------
 include/uapi/linux/if_link.h |    2 +
 2 files changed, 560 insertions(+), 140 deletions(-)

diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
index c1258c6..46c59a6 100644
--- a/drivers/net/vxlan.c
+++ b/drivers/net/vxlan.c
@@ -6,9 +6,6 @@
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
- *
- * TODO
- *  - IPv6 (not in RFC)
  */
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -41,6 +38,11 @@
 #include <net/inet_ecn.h>
 #include <net/net_namespace.h>
 #include <net/netns/generic.h>
+#if IS_ENABLED(CONFIG_IPV6)
+#include <net/addrconf.h>
+#include <net/ip6_route.h>
+#include <net/ip6_tunnel.h>
+#endif
 
 #define VXLAN_VERSION	"0.1"
 
@@ -55,6 +57,8 @@
 #define VXLAN_VID_MASK	(VXLAN_N_VID - 1)
 /* IP header + UDP + VXLAN + Ethernet header */
 #define VXLAN_HEADROOM (20 + 8 + 8 + 14)
+/* IPv6 header + UDP + VXLAN + Ethernet header */
+#define VXLAN6_HEADROOM (40 + 8 + 8 + 14)
 
 #define VXLAN_FLAGS 0x08000000	/* struct vxlanhdr.vx_flags required value. */
 
@@ -76,16 +80,27 @@ static bool log_ecn_error = true;
 module_param(log_ecn_error, bool, 0644);
 MODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN");
 
+#if IS_ENABLED(CONFIG_IPV6)
+static bool ipv6_disabled = false;
+#endif
+
 /* per-net private data for this module */
 static unsigned int vxlan_net_id;
 struct vxlan_net {
 	struct socket	  *sock;	/* UDP encap socket */
+	struct socket     *sock6;
 	struct hlist_head vni_list[VNI_HASH_SIZE];
 };
 
+union vxlan_addr {
+	struct sockaddr_in	sin;
+	struct sockaddr_in6	sin6;
+	struct sockaddr		sa;
+};
+
 struct vxlan_rdst {
 	struct rcu_head		 rcu;
-	__be32			 remote_ip;
+	union vxlan_addr	 remote_ip;
 	__be16			 remote_port;
 	u32			 remote_vni;
 	u32			 remote_ifindex;
@@ -109,7 +124,7 @@ struct vxlan_dev {
 	struct hlist_node hlist;
 	struct net_device *dev;
 	struct vxlan_rdst default_dst;	/* default destination */
-	__be32		  saddr;	/* source address */
+	union vxlan_addr  saddr;	/* source address */
 	__be16		  dst_port;
 	__u16		  port_min;	/* source port range */
 	__u16		  port_max;
@@ -132,6 +147,69 @@ struct vxlan_dev {
 #define VXLAN_F_L2MISS	0x08
 #define VXLAN_F_L3MISS	0x10
 
+static inline
+bool vxlan_addr_equal(const union vxlan_addr *a, const union vxlan_addr *b)
+{
+#if IS_ENABLED(CONFIG_IPV6)
+	if (a->sa.sa_family != b->sa.sa_family)
+		return false;
+	if (a->sa.sa_family == AF_INET6)
+		return ipv6_addr_equal(&a->sin6.sin6_addr, &b->sin6.sin6_addr);
+	else
+#endif
+		return a->sin.sin_addr.s_addr == b->sin.sin_addr.s_addr;
+}
+
+static inline bool vxlan_addr_any(const union vxlan_addr *ipa)
+{
+#if IS_ENABLED(CONFIG_IPV6)
+	if (ipa->sa.sa_family == AF_INET6)
+		return ipv6_addr_any(&ipa->sin6.sin6_addr);
+	else
+#endif
+		return ipa->sin.sin_addr.s_addr == htonl(INADDR_ANY);
+}
+
+static inline bool vxlan_addr_multicast(const union vxlan_addr *ipa)
+{
+#if IS_ENABLED(CONFIG_IPV6)
+	if (ipa->sa.sa_family == AF_INET6)
+		return ipv6_addr_is_multicast(&ipa->sin6.sin6_addr);
+	else
+#endif
+		return IN_MULTICAST(ntohl(ipa->sin.sin_addr.s_addr));
+}
+
+static int vxlan_nla_get_addr(union vxlan_addr *ip, struct nlattr *nla)
+{
+	if (nla_len(nla) >= sizeof(struct in6_addr)) {
+#if IS_ENABLED(CONFIG_IPV6)
+		nla_memcpy(&ip->sin6.sin6_addr, nla, sizeof(struct in6_addr));
+		ip->sa.sa_family = AF_INET6;
+		return 0;
+#else
+		return -EAFNOSUPPORT;
+#endif
+	} else if (nla_len(nla) >= sizeof(__be32)) {
+		ip->sin.sin_addr.s_addr = nla_get_be32(nla);
+		ip->sa.sa_family = AF_INET;
+		return 0;
+	} else {
+		return -EAFNOSUPPORT;
+	}
+}
+
+static int vxlan_nla_put_addr(struct sk_buff *skb, int attr,
+			      const union vxlan_addr *ip)
+{
+#if IS_ENABLED(CONFIG_IPV6)
+	if (ip->sa.sa_family == AF_INET6)
+		return nla_put(skb, attr, sizeof(struct in6_addr), &ip->sin6.sin6_addr);
+	else
+#endif
+		return nla_put_be32(skb, attr, ip->sin.sin_addr.s_addr);
+}
+
 /* salt for hash table */
 static u32 vxlan_salt __read_mostly;
 
@@ -178,7 +256,7 @@ static int vxlan_fdb_info(struct sk_buff *skb, struct vxlan_dev *vxlan,
 
 	if (type == RTM_GETNEIGH) {
 		ndm->ndm_family	= AF_INET;
-		send_ip = rdst->remote_ip != htonl(INADDR_ANY);
+		send_ip = !vxlan_addr_any(&rdst->remote_ip);
 		send_eth = !is_zero_ether_addr(fdb->eth_addr);
 	} else
 		ndm->ndm_family	= AF_BRIDGE;
@@ -190,7 +268,7 @@ static int vxlan_fdb_info(struct sk_buff *skb, struct vxlan_dev *vxlan,
 	if (send_eth && nla_put(skb, NDA_LLADDR, ETH_ALEN, &fdb->eth_addr))
 		goto nla_put_failure;
 
-	if (send_ip && nla_put_be32(skb, NDA_DST, rdst->remote_ip))
+	if (send_ip && vxlan_nla_put_addr(skb, NDA_DST, &rdst->remote_ip))
 		goto nla_put_failure;
 
 	if (rdst->remote_port && rdst->remote_port != vxlan->dst_port &&
@@ -222,7 +300,7 @@ static inline size_t vxlan_nlmsg_size(void)
 {
 	return NLMSG_ALIGN(sizeof(struct ndmsg))
 		+ nla_total_size(ETH_ALEN) /* NDA_LLADDR */
-		+ nla_total_size(sizeof(__be32)) /* NDA_DST */
+		+ nla_total_size(sizeof(struct in6_addr)) /* NDA_DST */
 		+ nla_total_size(sizeof(__be16)) /* NDA_PORT */
 		+ nla_total_size(sizeof(__be32)) /* NDA_VNI */
 		+ nla_total_size(sizeof(__u32)) /* NDA_IFINDEX */
@@ -255,14 +333,14 @@ errout:
 		rtnl_set_sk_err(net, RTNLGRP_NEIGH, err);
 }
 
-static void vxlan_ip_miss(struct net_device *dev, __be32 ipa)
+static void vxlan_ip_miss(struct net_device *dev, union vxlan_addr *ipa)
 {
 	struct vxlan_dev *vxlan = netdev_priv(dev);
 	struct vxlan_fdb f;
 
 	memset(&f, 0, sizeof f);
 	f.state = NUD_STALE;
-	f.remote.remote_ip = ipa; /* goes to NDA_DST */
+	f.remote.remote_ip = *ipa; /* goes to NDA_DST */
 	f.remote.remote_vni = VXLAN_N_VID;
 
 	vxlan_fdb_notify(vxlan, &f, RTM_GETNEIGH);
@@ -317,14 +395,14 @@ static struct vxlan_fdb *vxlan_find_mac(struct vxlan_dev *vxlan,
 }
 
 /* Add/update destinations for multicast */
-static int vxlan_fdb_append(struct vxlan_fdb *f,
-			    __be32 ip, __be16 port, __u32 vni, __u32 ifindex)
+static int vxlan_fdb_append(struct vxlan_fdb *f, union vxlan_addr *ip,
+			    __be16 port, __u32 vni, __u32 ifindex)
 {
 	struct vxlan_rdst *rd_prev, *rd;
 
 	rd_prev = NULL;
 	for (rd = &f->remote; rd; rd = rd->remote_next) {
-		if (rd->remote_ip == ip &&
+		if (vxlan_addr_equal(&rd->remote_ip, ip) &&
 		    rd->remote_port == port &&
 		    rd->remote_vni == vni &&
 		    rd->remote_ifindex == ifindex)
@@ -334,7 +412,7 @@ static int vxlan_fdb_append(struct vxlan_fdb *f,
 	rd = kmalloc(sizeof(*rd), GFP_ATOMIC);
 	if (rd == NULL)
 		return -ENOBUFS;
-	rd->remote_ip = ip;
+	rd->remote_ip = *ip;
 	rd->remote_port = port;
 	rd->remote_vni = vni;
 	rd->remote_ifindex = ifindex;
@@ -345,7 +423,7 @@ static int vxlan_fdb_append(struct vxlan_fdb *f,
 
 /* Add new entry to forwarding table -- assumes lock held */
 static int vxlan_fdb_create(struct vxlan_dev *vxlan,
-			    const u8 *mac, __be32 ip,
+			    const u8 *mac, union vxlan_addr *ip,
 			    __u16 state, __u16 flags,
 			    __be16 port, __u32 vni, __u32 ifindex,
 			    __u8 ndm_flags)
@@ -385,13 +463,20 @@ static int vxlan_fdb_create(struct vxlan_dev *vxlan,
 		if (vxlan->addrmax && vxlan->addrcnt >= vxlan->addrmax)
 			return -ENOSPC;
 
-		netdev_dbg(vxlan->dev, "add %pM -> %pI4\n", mac, &ip);
+#if IS_ENABLED(CONFIG_IPV6)
+		if (ip->sa.sa_family == AF_INET6)
+			netdev_dbg(vxlan->dev, "add %pM -> %pI6\n", mac,
+				   &ip->sin6.sin6_addr);
+		else
+#endif
+			netdev_dbg(vxlan->dev, "add %pM -> %pI4\n", mac,
+				   &ip->sin.sin_addr.s_addr);
 		f = kmalloc(sizeof(*f), GFP_ATOMIC);
 		if (!f)
 			return -ENOMEM;
 
 		notify = 1;
-		f->remote.remote_ip = ip;
+		f->remote.remote_ip = *ip;
 		f->remote.remote_port = port;
 		f->remote.remote_vni = vni;
 		f->remote.remote_ifindex = ifindex;
@@ -444,7 +529,7 @@ static int vxlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
 {
 	struct vxlan_dev *vxlan = netdev_priv(dev);
 	struct net *net = dev_net(vxlan->dev);
-	__be32 ip;
+	union vxlan_addr ip;
 	__be16 port;
 	u32 vni, ifindex;
 	int err;
@@ -458,10 +543,9 @@ static int vxlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
 	if (tb[NDA_DST] == NULL)
 		return -EINVAL;
 
-	if (nla_len(tb[NDA_DST]) != sizeof(__be32))
-		return -EAFNOSUPPORT;
-
-	ip = nla_get_be32(tb[NDA_DST]);
+	err = vxlan_nla_get_addr(&ip, tb[NDA_DST]);
+	if (err)
+		return err;
 
 	if (tb[NDA_PORT]) {
 		if (nla_len(tb[NDA_PORT]) != sizeof(__be16))
@@ -491,7 +575,7 @@ static int vxlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
 		ifindex = 0;
 
 	spin_lock_bh(&vxlan->hash_lock);
-	err = vxlan_fdb_create(vxlan, addr, ip, ndm->ndm_state, flags,
+	err = vxlan_fdb_create(vxlan, addr, &ip, ndm->ndm_state, flags,
 			       port, vni, ifindex, ndm->ndm_flags);
 	spin_unlock_bh(&vxlan->hash_lock);
 
@@ -555,7 +639,7 @@ skip:
  * and Tunnel endpoint.
  */
 static void vxlan_snoop(struct net_device *dev,
-			__be32 src_ip, const u8 *src_mac)
+			union vxlan_addr *src_ip, const u8 *src_mac)
 {
 	struct vxlan_dev *vxlan = netdev_priv(dev);
 	struct vxlan_fdb *f;
@@ -564,15 +648,25 @@ static void vxlan_snoop(struct net_device *dev,
 	f = vxlan_find_mac(vxlan, src_mac);
 	if (likely(f)) {
 		f->used = jiffies;
-		if (likely(f->remote.remote_ip == src_ip))
+		if (likely(vxlan_addr_equal(&f->remote.remote_ip, src_ip)))
 			return;
 
-		if (net_ratelimit())
-			netdev_info(dev,
-				    "%pM migrated from %pI4 to %pI4\n",
-				    src_mac, &f->remote.remote_ip, &src_ip);
+		if (net_ratelimit()) {
+#if IS_ENABLED(CONFIG_IPV6)
+			if (src_ip->sa.sa_family == AF_INET6)
+				netdev_info(dev,
+					    "%pM migrated from %pI6 to %pI6\n",
+					    src_mac, &f->remote.remote_ip.sin6.sin6_addr,
+					    &src_ip->sin6.sin6_addr);
+			else
+#endif
+				netdev_info(dev,
+					    "%pM migrated from %pI4 to %pI4\n",
+					    src_mac, &f->remote.remote_ip.sin.sin_addr.s_addr,
+					    &src_ip->sin.sin_addr.s_addr);
+		}
 
-		f->remote.remote_ip = src_ip;
+		f->remote.remote_ip = *src_ip;
 		f->updated = jiffies;
 	} else {
 		/* learned new entry */
@@ -603,7 +697,8 @@ static bool vxlan_group_used(struct vxlan_net *vn,
 			if (!netif_running(vxlan->dev))
 				continue;
 
-			if (vxlan->default_dst.remote_ip == this->default_dst.remote_ip)
+			if (vxlan_addr_equal(&vxlan->default_dst.remote_ip,
+					     &this->default_dst.remote_ip))
 				return true;
 		}
 
@@ -616,11 +711,12 @@ static int vxlan_join_group(struct net_device *dev)
 	struct vxlan_dev *vxlan = netdev_priv(dev);
 	struct vxlan_net *vn = net_generic(dev_net(dev), vxlan_net_id);
 	struct sock *sk = vn->sock->sk;
+	union vxlan_addr *ip = &vxlan->default_dst.remote_ip;
 	struct ip_mreqn mreq = {
-		.imr_multiaddr.s_addr	= vxlan->default_dst.remote_ip,
+		.imr_multiaddr.s_addr	= ip->sin.sin_addr.s_addr,
 		.imr_ifindex		= vxlan->default_dst.remote_ifindex,
 	};
-	int err;
+	int err = 0;
 
 	/* Already a member of group */
 	if (vxlan_group_used(vn, vxlan))
@@ -628,8 +724,17 @@ static int vxlan_join_group(struct net_device *dev)
 
 	/* Need to drop RTNL to call multicast join */
 	rtnl_unlock();
-	lock_sock(sk);
-	err = ip_mc_join_group(sk, &mreq);
+	if (ip->sa.sa_family == AF_INET) {
+		lock_sock(sk);
+		err = ip_mc_join_group(sk, &mreq);
+#if IS_ENABLED(CONFIG_IPV6)
+	} else {
+		sk = vn->sock6->sk;
+		lock_sock(sk);
+		err = ipv6_stub->ipv6_sock_mc_join(sk, vxlan->default_dst.remote_ifindex,
+							&ip->sin6.sin6_addr);
+#endif
+	}
 	release_sock(sk);
 	rtnl_lock();
 
@@ -644,8 +749,9 @@ static int vxlan_leave_group(struct net_device *dev)
 	struct vxlan_net *vn = net_generic(dev_net(dev), vxlan_net_id);
 	int err = 0;
 	struct sock *sk = vn->sock->sk;
+	union vxlan_addr *ip = &vxlan->default_dst.remote_ip;
 	struct ip_mreqn mreq = {
-		.imr_multiaddr.s_addr	= vxlan->default_dst.remote_ip,
+		.imr_multiaddr.s_addr	= ip->sin.sin_addr.s_addr,
 		.imr_ifindex		= vxlan->default_dst.remote_ifindex,
 	};
 
@@ -655,8 +761,17 @@ static int vxlan_leave_group(struct net_device *dev)
 
 	/* Need to drop RTNL to call multicast leave */
 	rtnl_unlock();
-	lock_sock(sk);
-	err = ip_mc_leave_group(sk, &mreq);
+	if (ip->sa.sa_family == AF_INET) {
+		lock_sock(sk);
+		err = ip_mc_leave_group(sk, &mreq);
+#if IS_ENABLED(CONFIG_IPV6)
+	 } else {
+		sk = vn->sock6->sk;
+		lock_sock(sk);
+		err = ipv6_stub->ipv6_sock_mc_drop(sk, vxlan->default_dst.remote_ifindex,
+							&ip->sin6.sin6_addr);
+#endif
+	}
 	release_sock(sk);
 	rtnl_lock();
 
@@ -666,12 +781,16 @@ static int vxlan_leave_group(struct net_device *dev)
 /* Callback from net/ipv4/udp.c to receive packets */
 static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
 {
-	struct iphdr *oip;
+	struct iphdr *oip = NULL;
+#if IS_ENABLED(CONFIG_IPV6)
+	struct ipv6hdr *oip6 = NULL;
+#endif
 	struct vxlanhdr *vxh;
 	struct vxlan_dev *vxlan;
 	struct pcpu_tstats *stats;
+	union vxlan_addr src_ip;
 	__u32 vni;
-	int err;
+	int err = 0;
 
 	/* pop off outer UDP header */
 	__skb_pull(skb, sizeof(struct udphdr));
@@ -708,7 +827,13 @@ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
 	skb_reset_mac_header(skb);
 
 	/* Re-examine inner Ethernet packet */
-	oip = ip_hdr(skb);
+	if (skb->protocol == htons(ETH_P_IP))
+		oip = ip_hdr(skb);
+#if IS_ENABLED(CONFIG_IPV6)
+	if (skb->protocol == htons(ETH_P_IPV6))
+		oip6 = ipv6_hdr(skb);
+#endif
+
 	skb->protocol = eth_type_trans(skb, vxlan->dev);
 
 	/* Ignore packet loops (and multicast echo) */
@@ -716,8 +841,19 @@ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
 			       vxlan->dev->dev_addr) == 0)
 		goto drop;
 
-	if (vxlan->flags & VXLAN_F_LEARN)
-		vxlan_snoop(skb->dev, oip->saddr, eth_hdr(skb)->h_source);
+	if (vxlan->flags & VXLAN_F_LEARN) {
+		if (oip) {
+			src_ip.sin.sin_addr.s_addr = oip->saddr;
+			src_ip.sa.sa_family = AF_INET;
+		}
+#if IS_ENABLED(CONFIG_IPV6)
+		if (oip6) {
+			src_ip.sin6.sin6_addr = oip6->saddr;
+			src_ip.sa.sa_family = AF_INET6;
+		}
+#endif
+		vxlan_snoop(skb->dev, &src_ip, eth_hdr(skb)->h_source);
+	}
 
 	__skb_tunnel_rx(skb, vxlan->dev);
 	skb_reset_network_header(skb);
@@ -733,11 +869,24 @@ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
 
 	skb->encapsulation = 0;
 
-	err = IP_ECN_decapsulate(oip, skb);
+#if IS_ENABLED(CONFIG_IPV6)
+	if (oip6)
+		err = IP6_ECN_decapsulate(oip6, skb);
+#endif
+	if (oip)
+		err = IP_ECN_decapsulate(oip, skb);
+
 	if (unlikely(err)) {
-		if (log_ecn_error)
-			net_info_ratelimited("non-ECT from %pI4 with TOS=%#x\n",
-					     &oip->saddr, oip->tos);
+		if (log_ecn_error) {
+#if IS_ENABLED(CONFIG_IPV6)
+			if (oip6)
+				net_info_ratelimited("non-ECT from %pI6\n",
+						     &oip6->saddr);
+#endif
+			if (oip)
+				net_info_ratelimited("non-ECT from %pI4 with TOS=%#x\n",
+						     &oip->saddr, oip->tos);
+		}
 		if (err > 1) {
 			++vxlan->dev->stats.rx_frame_errors;
 			++vxlan->dev->stats.rx_errors;
@@ -772,6 +921,7 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb)
 	u8 *arpptr, *sha;
 	__be32 sip, tip;
 	struct neighbour *n;
+	union vxlan_addr ipa;
 
 	if (dev->flags & IFF_NOARP)
 		goto out;
@@ -813,7 +963,7 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb)
 		}
 
 		f = vxlan_find_mac(vxlan, n->ha);
-		if (f && f->remote.remote_ip == htonl(INADDR_ANY)) {
+		if (f && vxlan_addr_any(&f->remote.remote_ip)) {
 			/* bridge-local neighbor */
 			neigh_release(n);
 			goto out;
@@ -831,8 +981,11 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb)
 
 		if (netif_rx_ni(reply) == NET_RX_DROP)
 			dev->stats.rx_dropped++;
-	} else if (vxlan->flags & VXLAN_F_L3MISS)
-		vxlan_ip_miss(dev, tip);
+	} else if (vxlan->flags & VXLAN_F_L3MISS) {
+		ipa.sin.sin_addr.s_addr = tip;
+		ipa.sa.sa_family = AF_INET;
+		vxlan_ip_miss(dev, &ipa);
+	}
 out:
 	consume_skb(skb);
 	return NETDEV_TX_OK;
@@ -854,6 +1007,14 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb)
 			return false;
 		pip = ip_hdr(skb);
 		n = neigh_lookup(&arp_tbl, &pip->daddr, dev);
+		if (!n && vxlan->flags & VXLAN_F_L3MISS) {
+			union vxlan_addr ipa;
+			ipa.sin.sin_addr.s_addr = pip->daddr;
+			ipa.sa.sa_family = AF_INET;
+			vxlan_ip_miss(dev, &ipa);
+			return false;
+		}
+
 		break;
 	default:
 		return false;
@@ -870,8 +1031,8 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb)
 		}
 		neigh_release(n);
 		return diff;
-	} else if (vxlan->flags & VXLAN_F_L3MISS)
-		vxlan_ip_miss(dev, pip->daddr);
+	}
+
 	return false;
 }
 
@@ -881,10 +1042,11 @@ static void vxlan_sock_free(struct sk_buff *skb)
 }
 
 /* On transmit, associate with the tunnel socket */
-static void vxlan_set_owner(struct net_device *dev, struct sk_buff *skb)
+static void vxlan_set_owner(struct net_device *dev, struct sk_buff *skb,
+			    bool ipv6)
 {
 	struct vxlan_net *vn = net_generic(dev_net(dev), vxlan_net_id);
-	struct sock *sk = vn->sock->sk;
+	struct sock *sk = ipv6 ? vn->sock6->sk : vn->sock->sk;
 
 	skb_orphan(skb);
 	sock_hold(sk);
@@ -930,15 +1092,26 @@ static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan,
 {
 	struct pcpu_tstats *tx_stats = this_cpu_ptr(src_vxlan->dev->tstats);
 	struct pcpu_tstats *rx_stats = this_cpu_ptr(dst_vxlan->dev->tstats);
+	union vxlan_addr loopback;
 
 	skb->pkt_type = PACKET_HOST;
 	skb->encapsulation = 0;
 	skb->dev = dst_vxlan->dev;
 	__skb_pull(skb, skb_network_offset(skb));
 
+	if (dst_vxlan->default_dst.remote_ip.sa.sa_family == AF_INET) {
+		loopback.sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+		loopback.sa.sa_family =  AF_INET;
+	}
+#if IS_ENABLED(CONFIG_IPV6)
+	else {
+		loopback.sin6.sin6_addr = in6addr_loopback;
+		loopback.sa.sa_family =  AF_INET6;
+	}
+#endif
+
 	if (dst_vxlan->flags & VXLAN_F_LEARN)
-		vxlan_snoop(skb->dev, htonl(INADDR_LOOPBACK),
-			    eth_hdr(skb)->h_source);
+		vxlan_snoop(skb->dev, &loopback, eth_hdr(skb)->h_source);
 
 	u64_stats_update_begin(&tx_stats->syncp);
 	tx_stats->tx_packets++;
@@ -960,22 +1133,29 @@ static netdev_tx_t vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
 {
 	struct vxlan_dev *vxlan = netdev_priv(dev);
 	struct rtable *rt;
-	const struct iphdr *old_iph;
+	const struct iphdr *old_iph = NULL;
 	struct iphdr *iph;
 	struct vxlanhdr *vxh;
 	struct udphdr *uh;
 	struct flowi4 fl4;
-	__be32 dst;
-	__be16 src_port, dst_port;
+#if IS_ENABLED(CONFIG_IPV6)
+	struct flowi6 fl6;
+	struct vxlan_net *vn = net_generic(dev_net(dev), vxlan_net_id);
+	struct sock *sk = vn->sock6->sk;
+	struct ipv6hdr *ip6h;
+#endif
+	const union vxlan_addr *dst;
+	struct dst_entry *ndst = NULL;
+	__be16 src_port = 0, dst_port;
         u32 vni;
 	__be16 df = 0;
 	__u8 tos, ttl;
 
 	dst_port = rdst->remote_port ? rdst->remote_port : vxlan->dst_port;
 	vni = rdst->remote_vni;
-	dst = rdst->remote_ip;
+	dst = &rdst->remote_ip;
 
-	if (!dst) {
+	if (vxlan_addr_any(dst)) {
 		if (did_rsc) {
 			/* short-circuited back to local bridge */
 			vxlan_encap_bypass(skb, vxlan, vxlan);
@@ -989,60 +1169,119 @@ static netdev_tx_t vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
 		skb->encapsulation = 1;
 	}
 
-	/* Need space for new headers (invalidates iph ptr) */
-	if (skb_cow_head(skb, VXLAN_HEADROOM))
-		goto drop;
+	ttl = vxlan->ttl;
+	tos = vxlan->tos;
+	if (dst->sa.sa_family == AF_INET) {
+		/* Need space for new headers (invalidates iph ptr) */
+		if (skb_cow_head(skb, VXLAN_HEADROOM))
+			goto drop;
 
-	old_iph = ip_hdr(skb);
+		old_iph = ip_hdr(skb);
+		if (!ttl && IN_MULTICAST(ntohl(dst->sin.sin_addr.s_addr)))
+			ttl = 1;
 
-	ttl = vxlan->ttl;
-	if (!ttl && IN_MULTICAST(ntohl(dst)))
-		ttl = 1;
+		if (tos == 1)
+			tos = ip_tunnel_get_dsfield(old_iph, skb);
 
-	tos = vxlan->tos;
-	if (tos == 1)
-		tos = ip_tunnel_get_dsfield(old_iph, skb);
-
-	src_port = vxlan_src_port(vxlan, skb);
-
-	memset(&fl4, 0, sizeof(fl4));
-	fl4.flowi4_oif = rdst->remote_ifindex;
-	fl4.flowi4_tos = RT_TOS(tos);
-	fl4.daddr = dst;
-	fl4.saddr = vxlan->saddr;
-
-	rt = ip_route_output_key(dev_net(dev), &fl4);
-	if (IS_ERR(rt)) {
-		netdev_dbg(dev, "no route to %pI4\n", &dst);
-		dev->stats.tx_carrier_errors++;
-		goto tx_error;
-	}
+		src_port = vxlan_src_port(vxlan, skb);
 
-	if (rt->dst.dev == dev) {
-		netdev_dbg(dev, "circular route to %pI4\n", &dst);
-		ip_rt_put(rt);
-		dev->stats.collisions++;
-		goto tx_error;
-	}
+		memset(&fl4, 0, sizeof(fl4));
+		fl4.flowi4_oif = rdst->remote_ifindex;
+		fl4.flowi4_tos = RT_TOS(tos);
+		fl4.daddr = dst->sin.sin_addr.s_addr;
+		fl4.saddr = vxlan->saddr.sin.sin_addr.s_addr;
+
+		rt = ip_route_output_key(dev_net(dev), &fl4);
+		if (IS_ERR(rt)) {
+			netdev_dbg(dev, "no route to %pI4\n",
+				   &dst->sin.sin_addr.s_addr);
+			dev->stats.tx_carrier_errors++;
+			goto tx_error;
+		}
+
+		if (rt->dst.dev == dev) {
+			netdev_dbg(dev, "circular route to %pI4\n",
+				   &dst->sin.sin_addr.s_addr);
+			ip_rt_put(rt);
+			dev->stats.collisions++;
+			goto tx_error;
+		}
+
+		/* Bypass encapsulation if the destination is local */
+		if (rt->rt_flags & RTCF_LOCAL &&
+		    !(rt->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST))) {
+			struct vxlan_dev *dst_vxlan;
+
+			ip_rt_put(rt);
+			dst_vxlan = vxlan_find_vni(dev_net(dev), vni);
+			if (!dst_vxlan)
+				goto tx_error;
+			vxlan_encap_bypass(skb, vxlan, dst_vxlan);
+			return NETDEV_TX_OK;
+		}
+
+		ndst = &rt->dst;
+#if IS_ENABLED(CONFIG_IPV6)
+	} else {
+		const struct ipv6hdr *old_iph6;
+		u32 flags;
+
+		/* Need space for new headers (invalidates ipv6h ptr) */
+		if (skb_cow_head(skb, VXLAN6_HEADROOM))
+			goto drop;
+
+		old_iph6 = ipv6_hdr(skb);
+		if (!ttl && ipv6_addr_is_multicast(&dst->sin6.sin6_addr))
+			ttl = 1;
+
+		if (tos == 1)
+			tos = ipv6_get_dsfield(old_iph6);
+
+		src_port = vxlan_src_port(vxlan, skb);
 
-	/* Bypass encapsulation if the destination is local */
-	if (rt->rt_flags & RTCF_LOCAL &&
-	    !(rt->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST))) {
-		struct vxlan_dev *dst_vxlan;
+		memset(&fl6, 0, sizeof(fl6));
+		fl6.flowi6_oif = rdst->remote_ifindex;
+		fl6.flowi6_tos = RT_TOS(tos);
+		fl6.daddr = dst->sin6.sin6_addr;
+		fl6.saddr = vxlan->saddr.sin6.sin6_addr;
+		fl6.flowi6_proto = skb->protocol;
 
-		ip_rt_put(rt);
-		dst_vxlan = vxlan_find_vni(dev_net(dev), vni);
-		if (!dst_vxlan)
+		if (ipv6_stub->ipv6_dst_lookup(sk, &ndst, &fl6)) {
+			netdev_dbg(dev, "no route to %pI6\n",
+				   &dst->sin6.sin6_addr);
+			dev->stats.tx_carrier_errors++;
 			goto tx_error;
-		vxlan_encap_bypass(skb, vxlan, dst_vxlan);
-		return NETDEV_TX_OK;
+		}
+
+		if (ndst->dev == dev) {
+			netdev_dbg(dev, "circular route to %pI6\n",
+				   &dst->sin6.sin6_addr);
+			dst_release(ndst);
+			dev->stats.collisions++;
+			goto tx_error;
+		}
+
+		/* Bypass encapsulation if the destination is local */
+		flags = ((struct rt6_info *)ndst)->rt6i_flags;
+		if (flags & RTF_LOCAL &&
+		    !(flags & (RTCF_BROADCAST | RTCF_MULTICAST))) {
+			struct vxlan_dev *dst_vxlan;
+
+			dst_release(ndst);
+			dst_vxlan = vxlan_find_vni(dev_net(dev), vni);
+			if (!dst_vxlan)
+				goto tx_error;
+			vxlan_encap_bypass(skb, vxlan, dst_vxlan);
+			return NETDEV_TX_OK;
+		}
+#endif
 	}
 
 	memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
 	IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED |
 			      IPSKB_REROUTED);
 	skb_dst_drop(skb);
-	skb_dst_set(skb, &rt->dst);
+	skb_dst_set(skb, ndst);
 
 	vxh = (struct vxlanhdr *) __skb_push(skb, sizeof(*vxh));
 	vxh->vx_flags = htonl(VXLAN_FLAGS);
@@ -1058,27 +1297,65 @@ static netdev_tx_t vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
 	uh->len = htons(skb->len);
 	uh->check = 0;
 
-	__skb_push(skb, sizeof(*iph));
-	skb_reset_network_header(skb);
-	iph		= ip_hdr(skb);
-	iph->version	= 4;
-	iph->ihl	= sizeof(struct iphdr) >> 2;
-	iph->frag_off	= df;
-	iph->protocol	= IPPROTO_UDP;
-	iph->tos	= ip_tunnel_ecn_encap(tos, old_iph, skb);
-	iph->daddr	= dst;
-	iph->saddr	= fl4.saddr;
-	iph->ttl	= ttl ? : ip4_dst_hoplimit(&rt->dst);
-	tunnel_ip_select_ident(skb, old_iph, &rt->dst);
-
-	nf_reset(skb);
+	if (dst->sa.sa_family == AF_INET) {
+		__skb_push(skb, sizeof(*iph));
+		skb_reset_network_header(skb);
+		iph		= ip_hdr(skb);
+		iph->version	= 4;
+		iph->ihl	= sizeof(struct iphdr) >> 2;
+		iph->frag_off	= df;
+		iph->protocol	= IPPROTO_UDP;
+		iph->tos	= ip_tunnel_ecn_encap(tos, old_iph, skb);
+		iph->daddr	= dst->sin.sin_addr.s_addr;
+		iph->saddr	= fl4.saddr;
+		iph->ttl	= ttl ? : ip4_dst_hoplimit(ndst);
+		tunnel_ip_select_ident(skb, old_iph, ndst);
+
+		vxlan_set_owner(dev, skb, false);
+#if IS_ENABLED(CONFIG_IPV6)
+	} else {
+		if (!skb_is_gso(skb) && !(ndst->dev->features & NETIF_F_IPV6_CSUM)) {
+			__wsum csum = skb_checksum(skb, 0, skb->len, 0);
+			skb->ip_summed = CHECKSUM_UNNECESSARY;
+			uh->check = csum_ipv6_magic(&fl6.saddr, &fl6.daddr, skb->len,
+						    IPPROTO_UDP, csum);
+			if (uh->check == 0)
+				uh->check = CSUM_MANGLED_0;
+		} else {
+			skb->ip_summed = CHECKSUM_PARTIAL;
+			skb->csum_start = skb_transport_header(skb) - skb->head;
+			skb->csum_offset = offsetof(struct udphdr, check);
+			uh->check = ~csum_ipv6_magic(&fl6.saddr, &fl6.daddr,
+						     skb->len, IPPROTO_UDP, 0);
+		}
 
-	vxlan_set_owner(dev, skb);
+		__skb_push(skb, sizeof(*ip6h));
+		skb_reset_network_header(skb);
+		ip6h		  = ipv6_hdr(skb);
+		ip6h->version	  = 6;
+		ip6h->priority	  = 0;
+		ip6h->flow_lbl[0] = 0;
+		ip6h->flow_lbl[1] = 0;
+		ip6h->flow_lbl[2] = 0;
+		ip6h->payload_len = htons(skb->len);
+		ip6h->nexthdr     = IPPROTO_UDP;
+		ip6h->hop_limit   = ttl ? : ip6_dst_hoplimit(ndst);
+		ip6h->daddr	  = fl6.daddr;
+		ip6h->saddr	  = fl6.saddr;
+
+		vxlan_set_owner(dev, skb, true);
+#endif
+	}
 
 	if (handle_offloads(skb))
 		goto drop;
 
-	iptunnel_xmit(skb, dev);
+#if IS_ENABLED(CONFIG_IPV6)
+	if (dst->sa.sa_family == AF_INET6)
+		ip6tunnel_xmit(skb, dev);
+	else
+#endif
+		iptunnel_xmit(skb, dev);
 	return NETDEV_TX_OK;
 
 drop:
@@ -1126,7 +1403,7 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
 	if (f == NULL) {
 		rdst0 = &vxlan->default_dst;
 
-		if (rdst0->remote_ip == htonl(INADDR_ANY) &&
+		if (vxlan_addr_any(&rdst0->remote_ip) &&
 		    (vxlan->flags & VXLAN_F_L2MISS) &&
 		    !is_multicast_ether_addr(eth->h_dest))
 			vxlan_fdb_miss(vxlan, eth->h_dest);
@@ -1204,7 +1481,7 @@ static int vxlan_open(struct net_device *dev)
 	struct vxlan_dev *vxlan = netdev_priv(dev);
 	int err;
 
-	if (IN_MULTICAST(ntohl(vxlan->default_dst.remote_ip))) {
+	if (vxlan_addr_multicast(&vxlan->default_dst.remote_ip)) {
 		err = vxlan_join_group(dev);
 		if (err)
 			return err;
@@ -1238,7 +1515,7 @@ static int vxlan_stop(struct net_device *dev)
 {
 	struct vxlan_dev *vxlan = netdev_priv(dev);
 
-	if (IN_MULTICAST(ntohl(vxlan->default_dst.remote_ip)))
+	if (vxlan_addr_multicast(&vxlan->default_dst.remote_ip))
 		vxlan_leave_group(dev);
 
 	del_timer_sync(&vxlan->age_timer);
@@ -1288,7 +1565,12 @@ static void vxlan_setup(struct net_device *dev)
 
 	eth_hw_addr_random(dev);
 	ether_setup(dev);
-	dev->hard_header_len = ETH_HLEN + VXLAN_HEADROOM;
+#if IS_ENABLED(CONFIG_IPV6)
+	if (vxlan->default_dst.remote_ip.sa.sa_family == AF_INET6)
+		dev->hard_header_len = ETH_HLEN + VXLAN6_HEADROOM;
+	else
+#endif
+		dev->hard_header_len = ETH_HLEN + VXLAN_HEADROOM;
 
 	dev->netdev_ops = &vxlan_netdev_ops;
 	dev->destructor = vxlan_free;
@@ -1326,8 +1608,10 @@ static void vxlan_setup(struct net_device *dev)
 static const struct nla_policy vxlan_policy[IFLA_VXLAN_MAX + 1] = {
 	[IFLA_VXLAN_ID]		= { .type = NLA_U32 },
 	[IFLA_VXLAN_GROUP]	= { .len = FIELD_SIZEOF(struct iphdr, daddr) },
+	[IFLA_VXLAN_GROUP6]	= { .len = sizeof(struct in6_addr) },
 	[IFLA_VXLAN_LINK]	= { .type = NLA_U32 },
 	[IFLA_VXLAN_LOCAL]	= { .len = FIELD_SIZEOF(struct iphdr, saddr) },
+	[IFLA_VXLAN_LOCAL6]	= { .len = sizeof(struct in6_addr) },
 	[IFLA_VXLAN_TOS]	= { .type = NLA_U8 },
 	[IFLA_VXLAN_TTL]	= { .type = NLA_U8 },
 	[IFLA_VXLAN_LEARNING]	= { .type = NLA_U8 },
@@ -1408,11 +1692,37 @@ static int vxlan_newlink(struct net *net, struct net_device *dev,
 	}
 	dst->remote_vni = vni;
 
-	if (data[IFLA_VXLAN_GROUP])
-		dst->remote_ip = nla_get_be32(data[IFLA_VXLAN_GROUP]);
+	if (data[IFLA_VXLAN_GROUP]) {
+		dst->remote_ip.sin.sin_addr.s_addr = nla_get_be32(data[IFLA_VXLAN_GROUP]);
+		dst->remote_ip.sa.sa_family = AF_INET;
+	} else if (data[IFLA_VXLAN_GROUP6]) {
+#if IS_ENABLED(CONFIG_IPV6)
+		if (ipv6_disabled)
+			return -EPFNOSUPPORT;
+
+		nla_memcpy(&dst->remote_ip.sin6.sin6_addr, data[IFLA_VXLAN_GROUP6],
+			   sizeof(struct in6_addr));
+		dst->remote_ip.sa.sa_family = AF_INET6;
+#else
+		return -EPFNOSUPPORT;
+#endif
+	}
 
-	if (data[IFLA_VXLAN_LOCAL])
-		vxlan->saddr = nla_get_be32(data[IFLA_VXLAN_LOCAL]);
+	if (data[IFLA_VXLAN_LOCAL]) {
+		vxlan->saddr.sin.sin_addr.s_addr = nla_get_be32(data[IFLA_VXLAN_LOCAL]);
+		vxlan->saddr.sa.sa_family = AF_INET;
+	} else if (data[IFLA_VXLAN_LOCAL6]) {
+#if IS_ENABLED(CONFIG_IPV6)
+		if (ipv6_disabled)
+			return -EPFNOSUPPORT;
+
+		nla_memcpy(&vxlan->saddr.sin6.sin6_addr, data[IFLA_VXLAN_LOCAL6],
+			   sizeof(struct in6_addr));
+		vxlan->saddr.sa.sa_family = AF_INET6;
+#else
+		return -EPFNOSUPPORT;
+#endif
+	}
 
 	if (data[IFLA_VXLAN_LINK] &&
 	    (dst->remote_ifindex = nla_get_u32(data[IFLA_VXLAN_LINK]))) {
@@ -1493,9 +1803,9 @@ static size_t vxlan_get_size(const struct net_device *dev)
 {
 
 	return nla_total_size(sizeof(__u32)) +	/* IFLA_VXLAN_ID */
-		nla_total_size(sizeof(__be32)) +/* IFLA_VXLAN_GROUP */
+		nla_total_size(sizeof(struct in6_addr)) + /* IFLA_VXLAN_GROUP{6} */
 		nla_total_size(sizeof(__u32)) +	/* IFLA_VXLAN_LINK */
-		nla_total_size(sizeof(__be32))+	/* IFLA_VXLAN_LOCAL */
+		nla_total_size(sizeof(struct in6_addr)) + /* IFLA_VXLAN_LOCAL{6} */
 		nla_total_size(sizeof(__u8)) +	/* IFLA_VXLAN_TTL */
 		nla_total_size(sizeof(__u8)) +	/* IFLA_VXLAN_TOS */
 		nla_total_size(sizeof(__u8)) +	/* IFLA_VXLAN_LEARNING */
@@ -1522,14 +1832,36 @@ static int vxlan_fill_info(struct sk_buff *skb, const struct net_device *dev)
 	if (nla_put_u32(skb, IFLA_VXLAN_ID, dst->remote_vni))
 		goto nla_put_failure;
 
-	if (dst->remote_ip && nla_put_be32(skb, IFLA_VXLAN_GROUP, dst->remote_ip))
-		goto nla_put_failure;
+	if (!vxlan_addr_any(&dst->remote_ip)) {
+		if (dst->remote_ip.sa.sa_family == AF_INET) {
+			if (nla_put_be32(skb, IFLA_VXLAN_GROUP,
+					 dst->remote_ip.sin.sin_addr.s_addr))
+				goto nla_put_failure;
+		} else {
+#if IS_ENABLED(CONFIG_IPV6)
+			if (nla_put(skb, IFLA_VXLAN_GROUP6, sizeof(struct in6_addr),
+				    &dst->remote_ip.sin6.sin6_addr))
+				goto nla_put_failure;
+#endif
+		}
+	}
 
 	if (dst->remote_ifindex && nla_put_u32(skb, IFLA_VXLAN_LINK, dst->remote_ifindex))
 		goto nla_put_failure;
 
-	if (vxlan->saddr && nla_put_be32(skb, IFLA_VXLAN_LOCAL, vxlan->saddr))
-		goto nla_put_failure;
+	if (!vxlan_addr_any(&vxlan->saddr)) {
+		if (vxlan->saddr.sa.sa_family == AF_INET) {
+			if (nla_put_be32(skb, IFLA_VXLAN_LOCAL,
+					 vxlan->saddr.sin.sin_addr.s_addr))
+				goto nla_put_failure;
+		} else {
+#if IS_ENABLED(CONFIG_IPV6)
+			if (nla_put(skb, IFLA_VXLAN_LOCAL6, sizeof(struct in6_addr),
+				    &vxlan->saddr.sin6.sin6_addr))
+				goto nla_put_failure;
+#endif
+		}
+	}
 
 	if (nla_put_u8(skb, IFLA_VXLAN_TTL, vxlan->ttl) ||
 	    nla_put_u8(skb, IFLA_VXLAN_TOS, vxlan->tos) ||
@@ -1569,18 +1901,17 @@ static struct rtnl_link_ops vxlan_link_ops __read_mostly = {
 	.fill_info	= vxlan_fill_info,
 };
 
-static __net_init int vxlan_init_net(struct net *net)
+static __net_init int create_v4_sock(struct net *net)
 {
-	struct vxlan_net *vn = net_generic(net, vxlan_net_id);
 	struct sock *sk;
+	struct vxlan_net *vn = net_generic(net, vxlan_net_id);
 	struct sockaddr_in vxlan_addr = {
 		.sin_family = AF_INET,
+		.sin_port = htons(vxlan_port),
 		.sin_addr.s_addr = htonl(INADDR_ANY),
 	};
 	int rc;
-	unsigned h;
 
-	/* Create UDP socket for encapsulation receive. */
 	rc = sock_create_kern(AF_INET, SOCK_DGRAM, IPPROTO_UDP, &vn->sock);
 	if (rc < 0) {
 		pr_debug("UDP socket create failed\n");
@@ -1590,10 +1921,8 @@ static __net_init int vxlan_init_net(struct net *net)
 	sk = vn->sock->sk;
 	sk_change_net(sk, net);
 
-	vxlan_addr.sin_port = htons(vxlan_port);
-
-	rc = kernel_bind(vn->sock, (struct sockaddr *) &vxlan_addr,
-			 sizeof(vxlan_addr));
+	rc = kernel_bind(vn->sock, (struct sockaddr *)&vxlan_addr,
+			 sizeof(struct sockaddr_in));
 	if (rc < 0) {
 		pr_debug("bind for UDP socket %pI4:%u (%d)\n",
 			 &vxlan_addr.sin_addr, ntohs(vxlan_addr.sin_port), rc);
@@ -1604,11 +1933,94 @@ static __net_init int vxlan_init_net(struct net *net)
 
 	/* Disable multicast loopback */
 	inet_sk(sk)->mc_loop = 0;
+	/* Mark socket as an encapsulation socket. */
+	udp_sk(sk)->encap_type = 1;
+	udp_sk(sk)->encap_rcv = vxlan_udp_encap_recv;
 
+	return 0;
+}
+
+/* Create UDP socket for encapsulation receive. AF_INET6 socket
+ * could be used for both IPv4 and IPv6 communications, but
+ * users may set bindv6only=1.
+ */
+#if IS_ENABLED(CONFIG_IPV6)
+static __net_init int create_v6_sock(struct net *net)
+{
+	struct sock *sk;
+	struct vxlan_net *vn = net_generic(net, vxlan_net_id);
+	struct sockaddr_in6 vxlan_addr = {
+		.sin6_family = AF_INET6,
+		.sin6_port = htons(vxlan_port),
+	};
+	int rc, val = 1;
+
+	rc = sock_create_kern(AF_INET6, SOCK_DGRAM, IPPROTO_UDP, &vn->sock6);
+	if (rc < 0)
+		return rc;
+
+	/* Put in proper namespace */
+	sk = vn->sock6->sk;
+	sk_change_net(sk, net);
+
+	kernel_setsockopt(vn->sock6, SOL_IPV6, IPV6_V6ONLY,
+			  (char *)&val, sizeof(val));
+	rc = kernel_bind(vn->sock6, (struct sockaddr *)&vxlan_addr,
+			 sizeof(struct sockaddr_in6));
+	if (rc < 0) {
+		pr_debug("bind for UDP socket %pI6:%u (%d)\n",
+			 &vxlan_addr.sin6_addr, ntohs(vxlan_addr.sin6_port), rc);
+		sk_release_kernel(sk);
+		vn->sock6 = NULL;
+		return rc;
+	}
+
+	/* At this point, IPv6 module should have been loaded in
+	 * sock_create_kern().
+	 */
+	BUG_ON(!ipv6_stub);
+
+	/* Disable multicast loopback */
+	inet_sk(sk)->mc_loop = 0;
 	/* Mark socket as an encapsulation socket. */
 	udp_sk(sk)->encap_type = 1;
 	udp_sk(sk)->encap_rcv = vxlan_udp_encap_recv;
+
+	return 0;
+}
+
+static __net_init int create_sock(struct net *net)
+{
+	int rc;
+	rc = create_v6_sock(net);
+	if (rc < 0) {
+		pr_info("UDP IPv6 socket create failed, disable IPv6\n");
+		ipv6_disabled = true;
+	}
+
+	return create_v4_sock(net);
+}
+#else
+static __net_init int create_sock(struct net *net)
+{
+	return create_v4_sock(net);
+}
+#endif
+
+static __net_init int vxlan_init_net(struct net *net)
+{
+	struct vxlan_net *vn = net_generic(net, vxlan_net_id);
+	int rc;
+	unsigned h;
+
+	rc = create_sock(net);
+	if (rc < 0)
+		return rc;
+
 	udp_encap_enable();
+#if IS_ENABLED(CONFIG_IPV6)
+	ipv6_stub->udpv6_encap_enable();
+#endif
 
 	for (h = 0; h < VNI_HASH_SIZE; ++h)
 		INIT_HLIST_HEAD(&vn->vni_list[h]);
@@ -1632,6 +2044,12 @@ static __net_exit void vxlan_exit_net(struct net *net)
 		sk_release_kernel(vn->sock->sk);
 		vn->sock = NULL;
 	}
+#if IS_ENABLED(CONFIG_IPV6)
+	if (vn->sock6) {
+		sk_release_kernel(vn->sock6->sk);
+		vn->sock6 = NULL;
+	}
+#endif
 }
 
 static struct pernet_operations vxlan_net_ops = {
diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
index b05823c..f7bed18 100644
--- a/include/uapi/linux/if_link.h
+++ b/include/uapi/linux/if_link.h
@@ -311,6 +311,8 @@ enum {
 	IFLA_VXLAN_L2MISS,
 	IFLA_VXLAN_L3MISS,
 	IFLA_VXLAN_PORT,	/* destination port */
+	IFLA_VXLAN_GROUP6,
+	IFLA_VXLAN_LOCAL6,
 	__IFLA_VXLAN_MAX
 };
 #define IFLA_VXLAN_MAX	(__IFLA_VXLAN_MAX - 1)
-- 
1.7.7.6

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

* [Patch net-next v8 07/11] vxlan: respect disable_ipv6 sysctl
  2013-05-17  0:21 [Patch net-next v8 00/11] vxlan: add ipv6 support Cong Wang
                   ` (5 preceding siblings ...)
  2013-05-17  0:21 ` [Patch net-next v8 06/11] vxlan: add ipv6 support Cong Wang
@ 2013-05-17  0:21 ` Cong Wang
  2013-05-17 13:07   ` Sergei Shtylyov
  2013-05-17  0:21 ` [Patch net-next v8 08/11] vxlan: add ipv6 route short circuit support Cong Wang
                   ` (3 subsequent siblings)
  10 siblings, 1 reply; 23+ messages in thread
From: Cong Wang @ 2013-05-17  0:21 UTC (permalink / raw)
  To: netdev; +Cc: David Miller, Cong Wang

From: Cong Wang <amwang@redhat.com>

When disable_ipv6 is set, we should not allow IPv6 vxlan
device created on top of it.

Cc: David Miller <davem@davemloft.net>
Signed-off-by: Cong Wang <amwang@redhat.com>
---
 drivers/net/vxlan.c |   14 ++++++++++++++
 1 files changed, 14 insertions(+), 0 deletions(-)

diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
index 46c59a6..1ee79e0 100644
--- a/drivers/net/vxlan.c
+++ b/drivers/net/vxlan.c
@@ -1681,6 +1681,7 @@ static int vxlan_newlink(struct net *net, struct net_device *dev,
 	struct vxlan_rdst *dst = &vxlan->default_dst;
 	__u32 vni;
 	int err;
+	bool use_ipv6 = false;
 
 	if (!data[IFLA_VXLAN_ID])
 		return -EINVAL;
@@ -1703,6 +1704,7 @@ static int vxlan_newlink(struct net *net, struct net_device *dev,
 		nla_memcpy(&dst->remote_ip.sin6.sin6_addr, data[IFLA_VXLAN_GROUP6],
 			   sizeof(struct in6_addr));
 		dst->remote_ip.sa.sa_family = AF_INET6;
+		use_ipv6 = true;
 #else
 		return -EPFNOSUPPORT;
 #endif
@@ -1719,6 +1721,7 @@ static int vxlan_newlink(struct net *net, struct net_device *dev,
 		nla_memcpy(&vxlan->saddr.sin6.sin6_addr, data[IFLA_VXLAN_LOCAL6],
 			   sizeof(struct in6_addr));
 		vxlan->saddr.sa.sa_family = AF_INET6;
+		use_ipv6 = true;
 #else
 		return -EPFNOSUPPORT;
 #endif
@@ -1734,6 +1737,17 @@ static int vxlan_newlink(struct net *net, struct net_device *dev,
 			return -ENODEV;
 		}
 
+#if IS_ENABLED(CONFIG_IPV6)
+		if (use_ipv6) {
+			struct inet6_dev *idev = in6_dev_get(lowerdev);
+			if (idev && idev->cnf.disable_ipv6) {
+				pr_info("IPv6 is disabled via sysctl\n");
+				return -EPERM;
+			}
+		}
+#else
+		BUG_ON(use_ipv6);
+#endif
 		if (!tb[IFLA_MTU])
 			dev->mtu = lowerdev->mtu - VXLAN_HEADROOM;
 
-- 
1.7.7.6

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

* [Patch net-next v8 08/11] vxlan: add ipv6 route short circuit support
  2013-05-17  0:21 [Patch net-next v8 00/11] vxlan: add ipv6 support Cong Wang
                   ` (6 preceding siblings ...)
  2013-05-17  0:21 ` [Patch net-next v8 07/11] vxlan: respect disable_ipv6 sysctl Cong Wang
@ 2013-05-17  0:21 ` Cong Wang
  2013-05-17  0:21 ` [Patch net-next v8 09/11] vxlan: add ipv6 proxy support Cong Wang
                   ` (2 subsequent siblings)
  10 siblings, 0 replies; 23+ messages in thread
From: Cong Wang @ 2013-05-17  0:21 UTC (permalink / raw)
  To: netdev; +Cc: David S. Miller, David Stevens, Cong Wang

From: Cong Wang <amwang@redhat.com>

route short circuit only has IPv4 part, this patch adds
the IPv6 part.

Cc: David S. Miller <davem@davemloft.net>
Cc: David Stevens <dlstevens@us.ibm.com>
Signed-off-by: Cong Wang <amwang@redhat.com>
---
 drivers/net/vxlan.c |   28 ++++++++++++++++++++++++++--
 1 files changed, 26 insertions(+), 2 deletions(-)

diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
index 1ee79e0..04fd499 100644
--- a/drivers/net/vxlan.c
+++ b/drivers/net/vxlan.c
@@ -995,7 +995,6 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb)
 {
 	struct vxlan_dev *vxlan = netdev_priv(dev);
 	struct neighbour *n;
-	struct iphdr *pip;
 
 	if (is_multicast_ether_addr(eth_hdr(skb)->h_dest))
 		return false;
@@ -1003,6 +1002,9 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb)
 	n = NULL;
 	switch (ntohs(eth_hdr(skb)->h_proto)) {
 	case ETH_P_IP:
+	{
+		struct iphdr *pip;
+
 		if (!pskb_may_pull(skb, sizeof(struct iphdr)))
 			return false;
 		pip = ip_hdr(skb);
@@ -1016,6 +1018,27 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb)
 		}
 
 		break;
+	}
+#if IS_ENABLED(CONFIG_IPV6)
+	case ETH_P_IPV6:
+	{
+		struct ipv6hdr *pip6;
+
+		if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
+			return false;
+		pip6 = ipv6_hdr(skb);
+		n = neigh_lookup(&nd_tbl, &pip6->daddr, dev);
+		if (!n && vxlan->flags & VXLAN_F_L3MISS) {
+			union vxlan_addr ipa;
+			ipa.sin6.sin6_addr = pip6->daddr;
+			ipa.sa.sa_family = AF_INET6;
+			vxlan_ip_miss(dev, &ipa);
+			return false;
+		}
+
+		break;
+	}
+#endif
 	default:
 		return false;
 	}
@@ -1394,7 +1417,8 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
 	did_rsc = false;
 
 	if (f && (f->flags & NTF_ROUTER) && (vxlan->flags & VXLAN_F_RSC) &&
-	    ntohs(eth->h_proto) == ETH_P_IP) {
+	    (ntohs(eth->h_proto) == ETH_P_IP ||
+	     ntohs(eth->h_proto) == ETH_P_IPV6)) {
 		did_rsc = route_shortcircuit(dev, skb);
 		if (did_rsc)
 			f = vxlan_find_mac(vxlan, eth->h_dest);
-- 
1.7.7.6

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

* [Patch net-next v8 09/11] vxlan: add ipv6 proxy support
  2013-05-17  0:21 [Patch net-next v8 00/11] vxlan: add ipv6 support Cong Wang
                   ` (7 preceding siblings ...)
  2013-05-17  0:21 ` [Patch net-next v8 08/11] vxlan: add ipv6 route short circuit support Cong Wang
@ 2013-05-17  0:21 ` Cong Wang
  2013-05-17  0:21 ` [Patch net-next v8 10/11] vxlan: respect scope_id for ll addr Cong Wang
  2013-05-17  0:21 ` [Patch net-next v8 11/11] ipv6: Add generic UDP Tunnel segmentation Cong Wang
  10 siblings, 0 replies; 23+ messages in thread
From: Cong Wang @ 2013-05-17  0:21 UTC (permalink / raw)
  To: netdev; +Cc: David S. Miller, David Stevens, Cong Wang

From: Cong Wang <amwang@redhat.com>

This patch adds the IPv6 version of "arp_reduce", ndisc_send_na()
will be needed.

Cc: David S. Miller <davem@davemloft.net>
Cc: David Stevens <dlstevens@us.ibm.com>
Signed-off-by: Cong Wang <amwang@redhat.com>
---
 drivers/net/vxlan.c    |   81 ++++++++++++++++++++++++++++++++++++++++++++++-
 include/net/addrconf.h |    4 ++
 include/net/ndisc.h    |    5 +++
 net/ipv6/af_inet6.c    |    1 +
 net/ipv6/ndisc.c       |    8 ++--
 5 files changed, 93 insertions(+), 6 deletions(-)

diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
index 04fd499..f4d46bf 100644
--- a/drivers/net/vxlan.c
+++ b/drivers/net/vxlan.c
@@ -991,6 +991,76 @@ out:
 	return NETDEV_TX_OK;
 }
 
+#if IS_ENABLED(CONFIG_IPV6)
+static int neigh_reduce(struct net_device *dev, struct sk_buff *skb)
+{
+	struct vxlan_dev *vxlan = netdev_priv(dev);
+	struct neighbour *n;
+	union vxlan_addr ipa;
+	const struct ipv6hdr *iphdr;
+	const struct in6_addr *saddr, *daddr;
+	struct nd_msg *msg;
+	struct inet6_dev *in6_dev = NULL;
+
+	in6_dev = in6_dev_get(dev);
+	if (!in6_dev)
+		goto consume;
+
+	if (skb->len < sizeof(struct ipv6hdr) + sizeof(struct nd_msg) ||
+	    !pskb_may_pull(skb, skb->len))
+		goto out;
+
+	iphdr = ipv6_hdr(skb);
+	saddr = &iphdr->saddr;
+	daddr = &iphdr->daddr;
+
+	if (iphdr->nexthdr != IPPROTO_ICMPV6)
+		goto out;
+
+	if (ipv6_addr_loopback(daddr) ||
+	    ipv6_addr_is_multicast(daddr))
+		goto out;
+
+	msg = (struct nd_msg *)skb_transport_header(skb);
+	if (msg->icmph.icmp6_code != 0 ||
+	    msg->icmph.icmp6_type != NDISC_NEIGHBOUR_SOLICITATION)
+		goto out;
+
+	n = neigh_lookup(&nd_tbl, daddr, dev);
+
+	if (n) {
+		struct vxlan_fdb *f;
+
+		if (!(n->nud_state & NUD_CONNECTED)) {
+			neigh_release(n);
+			goto out;
+		}
+
+		f = vxlan_find_mac(vxlan, n->ha);
+		if (f && vxlan_addr_any(&f->remote.remote_ip)) {
+			/* bridge-local neighbor */
+			neigh_release(n);
+			goto out;
+		}
+
+		ipv6_stub->ndisc_send_na(dev, n, saddr, &msg->target,
+					 !!in6_dev->cnf.forwarding,
+					 true, false, false);
+		neigh_release(n);
+	} else if (vxlan->flags & VXLAN_F_L3MISS) {
+		ipa.sin6.sin6_addr = *daddr;
+		ipa.sa.sa_family = AF_INET6;
+		vxlan_ip_miss(dev, &ipa);
+	}
+
+out:
+	in6_dev_put(in6_dev);
+consume:
+	consume_skb(skb);
+	return NETDEV_TX_OK;
+}
+#endif
+
 static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb)
 {
 	struct vxlan_dev *vxlan = netdev_priv(dev);
@@ -1410,8 +1480,15 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
 	skb_reset_mac_header(skb);
 	eth = eth_hdr(skb);
 
-	if ((vxlan->flags & VXLAN_F_PROXY) && ntohs(eth->h_proto) == ETH_P_ARP)
-		return arp_reduce(dev, skb);
+	if ((vxlan->flags & VXLAN_F_PROXY)) {
+		if (ntohs(eth->h_proto) == ETH_P_ARP)
+			return arp_reduce(dev, skb);
+#if IS_ENABLED(CONFIG_IPV6)
+		else if (ntohs(eth->h_proto) == ETH_P_IPV6 &&
+			 ipv6_hdr(skb)->nexthdr == IPPROTO_ICMPV6)
+			return neigh_reduce(dev, skb);
+#endif
+	}
 
 	f = vxlan_find_mac(vxlan, eth->h_dest);
 	did_rsc = false;
diff --git a/include/net/addrconf.h b/include/net/addrconf.h
index d09d42c..34bccff 100644
--- a/include/net/addrconf.h
+++ b/include/net/addrconf.h
@@ -151,6 +151,10 @@ struct ipv6_stub {
 	int (*ipv6_dst_lookup)(struct sock *sk, struct dst_entry **dst,
 				struct flowi6 *fl6);
 	void (*udpv6_encap_enable)(void);
+	void (*ndisc_send_na)(struct net_device *dev, struct neighbour *neigh,
+			      const struct in6_addr *daddr,
+			      const struct in6_addr *solicited_addr,
+			      bool router, bool solicited, bool override, bool inc_opt);
 };
 extern const struct ipv6_stub *ipv6_stub __read_mostly;
 
diff --git a/include/net/ndisc.h b/include/net/ndisc.h
index 745bf74..ec2da56 100644
--- a/include/net/ndisc.h
+++ b/include/net/ndisc.h
@@ -204,6 +204,11 @@ extern void			ndisc_send_ns(struct net_device *dev,
 extern void			ndisc_send_rs(struct net_device *dev,
 					      const struct in6_addr *saddr,
 					      const struct in6_addr *daddr);
+extern void			ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
+					      const struct in6_addr *daddr,
+					      const struct in6_addr *solicited_addr,
+					      bool router, bool solicited, bool override,
+					      bool inc_opt);
 
 extern void			ndisc_send_redirect(struct sk_buff *skb,
 						    const struct in6_addr *target);
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index 58de055..d80fe10 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -813,6 +813,7 @@ static const struct ipv6_stub ipv6_stub_impl = {
 	.ipv6_sock_mc_drop = ipv6_sock_mc_drop,
 	.ipv6_dst_lookup = ip6_dst_lookup,
 	.udpv6_encap_enable = udpv6_encap_enable,
+	.ndisc_send_na = ndisc_send_na,
 };
 
 static int __init inet6_init(void)
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 2712ab2..a17e4d0 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -462,10 +462,10 @@ static void ndisc_send_skb(struct sk_buff *skb,
 	rcu_read_unlock();
 }
 
-static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
-			  const struct in6_addr *daddr,
-			  const struct in6_addr *solicited_addr,
-			  bool router, bool solicited, bool override, bool inc_opt)
+void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
+		   const struct in6_addr *daddr,
+		   const struct in6_addr *solicited_addr,
+		   bool router, bool solicited, bool override, bool inc_opt)
 {
 	struct sk_buff *skb;
 	struct in6_addr tmpaddr;
-- 
1.7.7.6

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

* [Patch net-next v8 10/11] vxlan: respect scope_id for ll addr
  2013-05-17  0:21 [Patch net-next v8 00/11] vxlan: add ipv6 support Cong Wang
                   ` (8 preceding siblings ...)
  2013-05-17  0:21 ` [Patch net-next v8 09/11] vxlan: add ipv6 proxy support Cong Wang
@ 2013-05-17  0:21 ` Cong Wang
  2013-05-17  0:21 ` [Patch net-next v8 11/11] ipv6: Add generic UDP Tunnel segmentation Cong Wang
  10 siblings, 0 replies; 23+ messages in thread
From: Cong Wang @ 2013-05-17  0:21 UTC (permalink / raw)
  To: netdev; +Cc: David Stevens, David S. Miller, Cong Wang

From: Cong Wang <amwang@redhat.com>

As pointed out by David, we should take care of scope id for ll
addr, and use it for route lookup.

Cc: David Stevens <dlstevens@us.ibm.com>
Cc: David S. Miller <davem@davemloft.net>
Signed-off-by: Cong Wang <amwang@redhat.com>
---
 drivers/net/vxlan.c |    8 +++++++-
 1 files changed, 7 insertions(+), 1 deletions(-)

diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
index f4d46bf..68ebfa4 100644
--- a/drivers/net/vxlan.c
+++ b/drivers/net/vxlan.c
@@ -1237,7 +1237,7 @@ static netdev_tx_t vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
 	struct sock *sk = vn->sock6->sk;
 	struct ipv6hdr *ip6h;
 #endif
-	const union vxlan_addr *dst;
+	union vxlan_addr *dst;
 	struct dst_entry *ndst = NULL;
 	__be16 src_port = 0, dst_port;
         u32 vni;
@@ -1332,6 +1332,12 @@ static netdev_tx_t vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
 
 		src_port = vxlan_src_port(vxlan, skb);
 
+		if (ipv6_addr_type(&dst->sin6.sin6_addr) & IPV6_ADDR_LINKLOCAL) {
+			dst->sin6.sin6_scope_id = ipv6_iface_scope_id(&dst->sin6.sin6_addr,
+								rdst->remote_ifindex);
+			rdst->remote_ifindex = dst->sin6.sin6_scope_id;
+		}
+
 		memset(&fl6, 0, sizeof(fl6));
 		fl6.flowi6_oif = rdst->remote_ifindex;
 		fl6.flowi6_tos = RT_TOS(tos);
-- 
1.7.7.6

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

* [Patch net-next v8 11/11] ipv6: Add generic UDP Tunnel segmentation
  2013-05-17  0:21 [Patch net-next v8 00/11] vxlan: add ipv6 support Cong Wang
                   ` (9 preceding siblings ...)
  2013-05-17  0:21 ` [Patch net-next v8 10/11] vxlan: respect scope_id for ll addr Cong Wang
@ 2013-05-17  0:21 ` Cong Wang
  10 siblings, 0 replies; 23+ messages in thread
From: Cong Wang @ 2013-05-17  0:21 UTC (permalink / raw)
  To: netdev
  Cc: Jesse Gross, Pravin B Shelar, Stephen Hemminger, David S. Miller,
	Cong Wang

From: Cong Wang <amwang@redhat.com>

Similar to commit 731362674580cb0c696cd1b1a03d8461a10cf90a
(tunneling: Add generic Tunnel segmentation)

This patch adds generic tunneling offloading support for IPv6-UDP
based tunnels.

This can be used by tunneling protocols like VXLAN.

Cc: Jesse Gross <jesse@nicira.com>
Cc: Pravin B Shelar <pshelar@nicira.com>
Cc: Stephen Hemminger <stephen@networkplumber.org>
Cc: David S. Miller <davem@davemloft.net>
Signed-off-by: Cong Wang <amwang@redhat.com>
---
 net/ipv6/ip6_offload.c |    4 +-
 net/ipv6/udp_offload.c |  153 +++++++++++++++++++++++++++++++++---------------
 2 files changed, 108 insertions(+), 49 deletions(-)

diff --git a/net/ipv6/ip6_offload.c b/net/ipv6/ip6_offload.c
index 71b766e..87fbf2e 100644
--- a/net/ipv6/ip6_offload.c
+++ b/net/ipv6/ip6_offload.c
@@ -91,6 +91,7 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb,
 	unsigned int unfrag_ip6hlen;
 	u8 *prevhdr;
 	int offset = 0;
+	bool tunnel;
 
 	if (unlikely(skb_shinfo(skb)->gso_type &
 		     ~(SKB_GSO_UDP |
@@ -105,6 +106,7 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb,
 	if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h))))
 		goto out;
 
+	tunnel = skb->encapsulation;
 	ipv6h = ipv6_hdr(skb);
 	__skb_pull(skb, sizeof(*ipv6h));
 	segs = ERR_PTR(-EPROTONOSUPPORT);
@@ -125,7 +127,7 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb,
 		ipv6h = ipv6_hdr(skb);
 		ipv6h->payload_len = htons(skb->len - skb->mac_len -
 					   sizeof(*ipv6h));
-		if (proto == IPPROTO_UDP) {
+		if (!tunnel && proto == IPPROTO_UDP) {
 			unfrag_ip6hlen = ip6_find_1stfragopt(skb, &prevhdr);
 			fptr = (struct frag_hdr *)(skb_network_header(skb) +
 				unfrag_ip6hlen);
diff --git a/net/ipv6/udp_offload.c b/net/ipv6/udp_offload.c
index 3bb3a89..2c3fa3b 100644
--- a/net/ipv6/udp_offload.c
+++ b/net/ipv6/udp_offload.c
@@ -21,26 +21,79 @@ static int udp6_ufo_send_check(struct sk_buff *skb)
 	const struct ipv6hdr *ipv6h;
 	struct udphdr *uh;
 
-	/* UDP Tunnel offload on ipv6 is not yet supported. */
-	if (skb->encapsulation)
-		return -EINVAL;
-
 	if (!pskb_may_pull(skb, sizeof(*uh)))
 		return -EINVAL;
 
-	ipv6h = ipv6_hdr(skb);
-	uh = udp_hdr(skb);
+	if (likely(!skb->encapsulation)) {
+		ipv6h = ipv6_hdr(skb);
+		uh = udp_hdr(skb);
+
+		uh->check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr, skb->len,
+					     IPPROTO_UDP, 0);
+		skb->csum_start = skb_transport_header(skb) - skb->head;
+		skb->csum_offset = offsetof(struct udphdr, check);
+		skb->ip_summed = CHECKSUM_PARTIAL;
+	}
 
-	uh->check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr, skb->len,
-				     IPPROTO_UDP, 0);
-	skb->csum_start = skb_transport_header(skb) - skb->head;
-	skb->csum_offset = offsetof(struct udphdr, check);
-	skb->ip_summed = CHECKSUM_PARTIAL;
 	return 0;
 }
 
+static struct sk_buff *skb_udp6_tunnel_segment(struct sk_buff *skb,
+					       netdev_features_t features)
+{
+	struct sk_buff *segs = ERR_PTR(-EINVAL);
+	int mac_len = skb->mac_len;
+	int tnl_hlen = skb_inner_mac_header(skb) - skb_transport_header(skb);
+	int outer_hlen;
+	netdev_features_t enc_features;
+
+	if (unlikely(!pskb_may_pull(skb, tnl_hlen)))
+		goto out;
+
+	skb->encapsulation = 0;
+	__skb_pull(skb, tnl_hlen);
+	skb_reset_mac_header(skb);
+	skb_set_network_header(skb, skb_inner_network_offset(skb));
+	skb->mac_len = skb_inner_network_offset(skb);
+
+	/* segment inner packet. */
+	enc_features = skb->dev->hw_enc_features & netif_skb_features(skb);
+	segs = skb_mac_gso_segment(skb, enc_features);
+	if (!segs || IS_ERR(segs))
+		goto out;
+
+	outer_hlen = skb_tnl_header_len(skb);
+	skb = segs;
+	do {
+		struct udphdr *uh;
+		struct ipv6hdr *ipv6h;
+		int udp_offset = outer_hlen - tnl_hlen;
+		u32 len;
+
+		skb->mac_len = mac_len;
+
+		skb_push(skb, outer_hlen);
+		skb_reset_mac_header(skb);
+		skb_set_network_header(skb, mac_len);
+		skb_set_transport_header(skb, udp_offset);
+		uh = udp_hdr(skb);
+		uh->len = htons(skb->len - udp_offset);
+		ipv6h = ipv6_hdr(skb);
+		len = skb->len - udp_offset;
+
+		uh->check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr,
+					     len, IPPROTO_UDP, 0);
+		uh->check = csum_fold(skb_checksum(skb, udp_offset, len, 0));
+		if (uh->check == 0)
+			uh->check = CSUM_MANGLED_0;
+		skb->ip_summed = CHECKSUM_NONE;
+	} while ((skb = skb->next));
+out:
+	return segs;
+}
+
 static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb,
-	netdev_features_t features)
+					 netdev_features_t features)
 {
 	struct sk_buff *segs = ERR_PTR(-EINVAL);
 	unsigned int mss;
@@ -73,43 +126,47 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb,
 		goto out;
 	}
 
-	/* Do software UFO. Complete and fill in the UDP checksum as HW cannot
-	 * do checksum of UDP packets sent as multiple IP fragments.
-	 */
-	offset = skb_checksum_start_offset(skb);
-	csum = skb_checksum(skb, offset, skb->len - offset, 0);
-	offset += skb->csum_offset;
-	*(__sum16 *)(skb->data + offset) = csum_fold(csum);
-	skb->ip_summed = CHECKSUM_NONE;
-
-	/* Check if there is enough headroom to insert fragment header. */
-	if ((skb_mac_header(skb) < skb->head + frag_hdr_sz) &&
-	    pskb_expand_head(skb, frag_hdr_sz, 0, GFP_ATOMIC))
-		goto out;
+	if (skb->encapsulation && skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL)
+		segs = skb_udp6_tunnel_segment(skb, features);
+	else {
+		/* Do software UFO. Complete and fill in the UDP checksum as HW cannot
+		 * do checksum of UDP packets sent as multiple IP fragments.
+		 */
+		offset = skb_checksum_start_offset(skb);
+		csum = skb_checksum(skb, offset, skb->len - offset, 0);
+		offset += skb->csum_offset;
+		*(__sum16 *)(skb->data + offset) = csum_fold(csum);
+		skb->ip_summed = CHECKSUM_NONE;
+
+		/* Check if there is enough headroom to insert fragment header. */
+		if ((skb_mac_header(skb) < skb->head + frag_hdr_sz) &&
+		    pskb_expand_head(skb, frag_hdr_sz, 0, GFP_ATOMIC))
+			goto out;
 
-	/* Find the unfragmentable header and shift it left by frag_hdr_sz
-	 * bytes to insert fragment header.
-	 */
-	unfrag_ip6hlen = ip6_find_1stfragopt(skb, &prevhdr);
-	nexthdr = *prevhdr;
-	*prevhdr = NEXTHDR_FRAGMENT;
-	unfrag_len = skb_network_header(skb) - skb_mac_header(skb) +
-		     unfrag_ip6hlen;
-	mac_start = skb_mac_header(skb);
-	memmove(mac_start-frag_hdr_sz, mac_start, unfrag_len);
-
-	skb->mac_header -= frag_hdr_sz;
-	skb->network_header -= frag_hdr_sz;
-
-	fptr = (struct frag_hdr *)(skb_network_header(skb) + unfrag_ip6hlen);
-	fptr->nexthdr = nexthdr;
-	fptr->reserved = 0;
-	ipv6_select_ident(fptr, (struct rt6_info *)skb_dst(skb));
-
-	/* Fragment the skb. ipv6 header and the remaining fields of the
-	 * fragment header are updated in ipv6_gso_segment()
-	 */
-	segs = skb_segment(skb, features);
+		/* Find the unfragmentable header and shift it left by frag_hdr_sz
+		 * bytes to insert fragment header.
+		 */
+		unfrag_ip6hlen = ip6_find_1stfragopt(skb, &prevhdr);
+		nexthdr = *prevhdr;
+		*prevhdr = NEXTHDR_FRAGMENT;
+		unfrag_len = skb_network_header(skb) - skb_mac_header(skb) +
+			     unfrag_ip6hlen;
+		mac_start = skb_mac_header(skb);
+		memmove(mac_start-frag_hdr_sz, mac_start, unfrag_len);
+
+		skb->mac_header -= frag_hdr_sz;
+		skb->network_header -= frag_hdr_sz;
+
+		fptr = (struct frag_hdr *)(skb_network_header(skb) + unfrag_ip6hlen);
+		fptr->nexthdr = nexthdr;
+		fptr->reserved = 0;
+		ipv6_select_ident(fptr, (struct rt6_info *)skb_dst(skb));
+
+		/* Fragment the skb. ipv6 header and the remaining fields of the
+		 * fragment header are updated in ipv6_gso_segment()
+		 */
+		segs = skb_segment(skb, features);
+	}
 
 out:
 	return segs;
-- 
1.7.7.6

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

* Re: [Patch net-next v8 02/11] ipv6: make ip6_dst_hoplimit() static inline
  2013-05-17  0:21 ` [Patch net-next v8 02/11] ipv6: make ip6_dst_hoplimit() static inline Cong Wang
@ 2013-05-17 13:02   ` Sergei Shtylyov
  2013-05-17 21:13   ` David Miller
  1 sibling, 0 replies; 23+ messages in thread
From: Sergei Shtylyov @ 2013-05-17 13:02 UTC (permalink / raw)
  To: Cong Wang; +Cc: netdev, David S. Miller

Hello.

On 17-05-2013 4:21, Cong Wang wrote:

> From: Cong Wang <amwang@redhat.com>

> It will be used by vxlan module, so move it from ipv6 module
> to core kernel. I think it is small enough to be inlined.

> Cc: David S. Miller <davem@davemloft.net>
> Signed-off-by: Cong Wang <amwang@redhat.com>
> ---
>   include/net/ip6_route.h |   23 +++++++++++++++++++++--
>   net/ipv6/route.c        |   19 -------------------
>   2 files changed, 21 insertions(+), 21 deletions(-)

> diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h
> index 260f83f..7e9192e 100644
> --- a/include/net/ip6_route.h
> +++ b/include/net/ip6_route.h
[...]
> @@ -201,4 +200,24 @@ static inline struct in6_addr *rt6_nexthop(struct rt6_info *rt, struct in6_addr
>   	return dest;
>   }
>
> +#if IS_ENABLED(CONFIG_IPV6)
> +static inline int ip6_dst_hoplimit(struct dst_entry *dst)
> +{
> +	int hoplimit = dst_metric_raw(dst, RTAX_HOPLIMIT);

     Empty line wouldn't hurt here, after the declaration, like below...

> +	if (hoplimit == 0) {
> +		struct net_device *dev = dst->dev;
> +		struct inet6_dev *idev;
> +
> +		rcu_read_lock();
> +		idev = __in6_dev_get(dev);
> +		if (idev)
> +			hoplimit = idev->cnf.hop_limit;
> +		else
> +			hoplimit = dev_net(dev)->ipv6.devconf_all->hop_limit;
> +		rcu_read_unlock();
> +	}
> +	return hoplimit;
> +}
> +#endif
> +

WBR, Sergei

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

* Re: [Patch net-next v8 07/11] vxlan: respect disable_ipv6 sysctl
  2013-05-17  0:21 ` [Patch net-next v8 07/11] vxlan: respect disable_ipv6 sysctl Cong Wang
@ 2013-05-17 13:07   ` Sergei Shtylyov
  0 siblings, 0 replies; 23+ messages in thread
From: Sergei Shtylyov @ 2013-05-17 13:07 UTC (permalink / raw)
  To: Cong Wang; +Cc: netdev, David Miller

On 17-05-2013 4:21, Cong Wang wrote:

> From: Cong Wang <amwang@redhat.com>

> When disable_ipv6 is set, we should not allow IPv6 vxlan
> device created on top of it.

> Cc: David Miller <davem@davemloft.net>
> Signed-off-by: Cong Wang <amwang@redhat.com>
> ---
>   drivers/net/vxlan.c |   14 ++++++++++++++
>   1 files changed, 14 insertions(+), 0 deletions(-)

> diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
> index 46c59a6..1ee79e0 100644
> --- a/drivers/net/vxlan.c
> +++ b/drivers/net/vxlan.c
[...]
> @@ -1734,6 +1737,17 @@ static int vxlan_newlink(struct net *net, struct net_device *dev,
>   			return -ENODEV;
>   		}
>
> +#if IS_ENABLED(CONFIG_IPV6)

    Why not:

	if (IS_ENABLED(CONFIG_IPV6))

    #if's in the function body are frowned upon.


> +		if (use_ipv6) {
> +			struct inet6_dev *idev = in6_dev_get(lowerdev);

    Empty line wouldn't hurt here, after declaration...

> +			if (idev && idev->cnf.disable_ipv6) {
> +				pr_info("IPv6 is disabled via sysctl\n");
> +				return -EPERM;
> +			}
> +		}
> +#else
> +		BUG_ON(use_ipv6);
> +#endif

WBR, Sergei

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

* Re: [Patch net-next v8 02/11] ipv6: make ip6_dst_hoplimit() static inline
  2013-05-17  0:21 ` [Patch net-next v8 02/11] ipv6: make ip6_dst_hoplimit() static inline Cong Wang
  2013-05-17 13:02   ` Sergei Shtylyov
@ 2013-05-17 21:13   ` David Miller
  2013-05-22  4:54     ` Cong Wang
  1 sibling, 1 reply; 23+ messages in thread
From: David Miller @ 2013-05-17 21:13 UTC (permalink / raw)
  To: amwang; +Cc: netdev

From: Cong Wang <amwang@redhat.com>
Date: Fri, 17 May 2013 08:21:30 +0800

> @@ -201,4 +200,24 @@ static inline struct in6_addr *rt6_nexthop(struct rt6_info *rt, struct in6_addr
>  	return dest;
>  }
>  
> +#if IS_ENABLED(CONFIG_IPV6)
> +static inline int ip6_dst_hoplimit(struct dst_entry *dst)
> +{
> +	int hoplimit = dst_metric_raw(dst, RTAX_HOPLIMIT);
> +	if (hoplimit == 0) {
> +		struct net_device *dev = dst->dev;
> +		struct inet6_dev *idev;
> +
> +		rcu_read_lock();
> +		idev = __in6_dev_get(dev);
> +		if (idev)
> +			hoplimit = idev->cnf.hop_limit;
> +		else
> +			hoplimit = dev_net(dev)->ipv6.devconf_all->hop_limit;
> +		rcu_read_unlock();
> +	}
> +	return hoplimit;
> +}
> +#endif

Create a dummy stub version in an #else branch here, so that you have
to ifdef less in vxlan.c

In fact I think you can avoid nearly every ifdef in vxlan.c if you apply
this technique throughout your changes.

Please do that and resubmit this series.

Thanks.

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

* Re: [Patch net-next v8 02/11] ipv6: make ip6_dst_hoplimit() static inline
  2013-05-17 21:13   ` David Miller
@ 2013-05-22  4:54     ` Cong Wang
  2013-05-22  7:14       ` David Miller
  0 siblings, 1 reply; 23+ messages in thread
From: Cong Wang @ 2013-05-22  4:54 UTC (permalink / raw)
  To: David Miller; +Cc: netdev

On Fri, 2013-05-17 at 14:13 -0700, David Miller wrote:
> From: Cong Wang <amwang@redhat.com>
> Date: Fri, 17 May 2013 08:21:30 +0800
> 
> > @@ -201,4 +200,24 @@ static inline struct in6_addr *rt6_nexthop(struct rt6_info *rt, struct in6_addr
> >  	return dest;
> >  }
> >  
> > +#if IS_ENABLED(CONFIG_IPV6)
> > +static inline int ip6_dst_hoplimit(struct dst_entry *dst)
> > +{
> > +	int hoplimit = dst_metric_raw(dst, RTAX_HOPLIMIT);
> > +	if (hoplimit == 0) {
> > +		struct net_device *dev = dst->dev;
> > +		struct inet6_dev *idev;
> > +
> > +		rcu_read_lock();
> > +		idev = __in6_dev_get(dev);
> > +		if (idev)
> > +			hoplimit = idev->cnf.hop_limit;
> > +		else
> > +			hoplimit = dev_net(dev)->ipv6.devconf_all->hop_limit;
> > +		rcu_read_unlock();
> > +	}
> > +	return hoplimit;
> > +}
> > +#endif
> 
> Create a dummy stub version in an #else branch here, so that you have
> to ifdef less in vxlan.c

The reason why we need #if IS_ENABLED(CONFIG_IPV6) here is that
dev_net(dev)->ipv6 is defined only in such case, not just for its caller
in vxlan. Nor I think anyone will seriously call ip6_dst_hoplimit()
for !CONFIG_IPV6 case, since its name is obvious.

> 
> In fact I think you can avoid nearly every ifdef in vxlan.c if you apply
> this technique throughout your changes.
> 
> Please do that and resubmit this series.
> 

Actually that is exactly what I _did_ in v1 or RFC, IIRC, it is David
Stevens who prefers to use #ifdef inside these functions, so I changed
it based on his suggestion.

I myself don't have any strong opinion here, either is okay, I just
don't like changing it again and again. :)

Thanks.

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

* Re: [Patch net-next v8 02/11] ipv6: make ip6_dst_hoplimit() static inline
  2013-05-22  4:54     ` Cong Wang
@ 2013-05-22  7:14       ` David Miller
  2013-05-22 10:28         ` Cong Wang
  0 siblings, 1 reply; 23+ messages in thread
From: David Miller @ 2013-05-22  7:14 UTC (permalink / raw)
  To: amwang; +Cc: netdev

From: Cong Wang <amwang@redhat.com>
Date: Wed, 22 May 2013 12:54:13 +0800

> Actually that is exactly what I _did_ in v1 or RFC, IIRC, it is David
> Stevens who prefers to use #ifdef inside these functions, so I changed
> it based on his suggestion.
> 
> I myself don't have any strong opinion here, either is okay, I just
> don't like changing it again and again. :)

The driver looks like complete shit with all the ifdefs in there,
this isn't the BSD kernel.

I do not want to seem them there at all.

You can abstract everything behind helper functions in a header
file, keep the mess there.

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

* Re: [Patch net-next v8 02/11] ipv6: make ip6_dst_hoplimit() static inline
  2013-05-22  7:14       ` David Miller
@ 2013-05-22 10:28         ` Cong Wang
  2013-05-22 15:50           ` Mike Rapoport
  0 siblings, 1 reply; 23+ messages in thread
From: Cong Wang @ 2013-05-22 10:28 UTC (permalink / raw)
  To: David Miller; +Cc: netdev

On Wed, 2013-05-22 at 00:14 -0700, David Miller wrote:
> From: Cong Wang <amwang@redhat.com>
> Date: Wed, 22 May 2013 12:54:13 +0800
> 
> > Actually that is exactly what I _did_ in v1 or RFC, IIRC, it is David
> > Stevens who prefers to use #ifdef inside these functions, so I changed
> > it based on his suggestion.
> > 
> > I myself don't have any strong opinion here, either is okay, I just
> > don't like changing it again and again. :)
> 
> The driver looks like complete shit with all the ifdefs in there,
> this isn't the BSD kernel.
> 
> I do not want to seem them there at all.
> 
> You can abstract everything behind helper functions in a header
> file, keep the mess there.

Alright, I will change all such functions in vxlan.c back to what you
are suggesting.

For example, change

static inline
bool vxlan_addr_equal(const union vxlan_addr *a, const union vxlan_addr
*b)
{
#if IS_ENABLED(CONFIG_IPV6)
       if (a->sa.sa_family != b->sa.sa_family)
               return false;
       if (a->sa.sa_family == AF_INET6)
               return ipv6_addr_equal(&a->sin6.sin6_addr,
&b->sin6.sin6_addr);
       else
#endif
               return a->sin.sin_addr.s_addr == b->sin.sin_addr.s_addr;
}

to

#if IS_ENABLED(CONFIG_IPV6)
static inline
bool vxlan_addr_equal(const union vxlan_addr *a, const union vxlan_addr
*b)
{
       if (a->sa.sa_family != b->sa.sa_family)
               return false;
       if (a->sa.sa_family == AF_INET6)
               return ipv6_addr_equal(&a->sin6.sin6_addr,
&b->sin6.sin6_addr);
       else
               return a->sin.sin_addr.s_addr == b->sin.sin_addr.s_addr;
}
#else
static inline
bool vxlan_addr_equal(const union vxlan_addr *a, const union vxlan_addr
*b)
{
	return a->sin.sin_addr.s_addr == b->sin.sin_addr.s_addr;
}
#endif

just in case I misunderstand you.

Thanks.

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

* Re: [Patch net-next v8 02/11] ipv6: make ip6_dst_hoplimit() static inline
  2013-05-22 10:28         ` Cong Wang
@ 2013-05-22 15:50           ` Mike Rapoport
  2013-05-22 16:03             ` Cong Wang
  0 siblings, 1 reply; 23+ messages in thread
From: Mike Rapoport @ 2013-05-22 15:50 UTC (permalink / raw)
  To: Cong Wang; +Cc: David Miller, netdev

On Wed, May 22, 2013 at 1:28 PM, Cong Wang <amwang@redhat.com> wrote:
> On Wed, 2013-05-22 at 00:14 -0700, David Miller wrote:
>> From: Cong Wang <amwang@redhat.com>
>> Date: Wed, 22 May 2013 12:54:13 +0800
>>
>
> Alright, I will change all such functions in vxlan.c back to what you
> are suggesting.
>
> For example, change
>
> static inline
> bool vxlan_addr_equal(const union vxlan_addr *a, const union vxlan_addr
> *b)
> {
> #if IS_ENABLED(CONFIG_IPV6)
>        if (a->sa.sa_family != b->sa.sa_family)
>                return false;
>        if (a->sa.sa_family == AF_INET6)
>                return ipv6_addr_equal(&a->sin6.sin6_addr,
> &b->sin6.sin6_addr);
>        else
> #endif
>                return a->sin.sin_addr.s_addr == b->sin.sin_addr.s_addr;
> }
>
> to
>
> #if IS_ENABLED(CONFIG_IPV6)
> static inline
> bool vxlan_addr_equal(const union vxlan_addr *a, const union vxlan_addr
> *b)
> {
>        if (a->sa.sa_family != b->sa.sa_family)
>                return false;
>        if (a->sa.sa_family == AF_INET6)
>                return ipv6_addr_equal(&a->sin6.sin6_addr,
> &b->sin6.sin6_addr);
>        else
>                return a->sin.sin_addr.s_addr == b->sin.sin_addr.s_addr;
> }
> #else
> static inline
> bool vxlan_addr_equal(const union vxlan_addr *a, const union vxlan_addr
> *b)
> {
>         return a->sin.sin_addr.s_addr == b->sin.sin_addr.s_addr;
> }
> #endif

I think you can just drop #ifdefs in 90% of the cases rather than
create two versions of code for IPv4 and IPv6....

> just in case I misunderstand you.
>
> Thanks.
>
> --
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html



--
Sincerely yours,
Mike.

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

* Re: [Patch net-next v8 02/11] ipv6: make ip6_dst_hoplimit() static inline
  2013-05-22 15:50           ` Mike Rapoport
@ 2013-05-22 16:03             ` Cong Wang
  2013-05-22 16:10               ` Mike Rapoport
  0 siblings, 1 reply; 23+ messages in thread
From: Cong Wang @ 2013-05-22 16:03 UTC (permalink / raw)
  To: Mike Rapoport; +Cc: David Miller, netdev



----- Original Message -----
> 
> I think you can just drop #ifdefs in 90% of the cases rather than
> create two versions of code for IPv4 and IPv6....
> 

I know we can use memcmp(), but comparing 16+ bytes even for IPv4 is not
a good idea, also we have to zalloc() every instance of union vxlan_addr.

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

* Re: [Patch net-next v8 02/11] ipv6: make ip6_dst_hoplimit() static inline
  2013-05-22 16:03             ` Cong Wang
@ 2013-05-22 16:10               ` Mike Rapoport
  2013-05-24  5:10                 ` Cong Wang
  2013-05-24  5:15                 ` Cong Wang
  0 siblings, 2 replies; 23+ messages in thread
From: Mike Rapoport @ 2013-05-22 16:10 UTC (permalink / raw)
  To: Cong Wang; +Cc: David Miller, netdev

On Wed, May 22, 2013 at 12:03:23PM -0400, Cong Wang wrote:
> 
> 
> ----- Original Message -----
> > 
> > I think you can just drop #ifdefs in 90% of the cases rather than
> > create two versions of code for IPv4 and IPv6....
> > 
> 
> I know we can use memcmp(), but comparing 16+ bytes even for IPv4 is not
> a good idea, also we have to zalloc() every instance of union vxlan_addr.

I've lost you here... Why not just:

static inline
bool vxlan_addr_equal(const union vxlan_addr *a, const union vxlan_addr *b)
{
       if (a->sa.sa_family != b->sa.sa_family)
               return false;
       if (a->sa.sa_family == AF_INET6)
               return ipv6_addr_equal(&a->sin6.sin6_addr, &b->sin6.sin6_addr);
       else
               return a->sin.sin_addr.s_addr == b->sin.sin_addr.s_addr;
}

--
Sincrely yours,
Mike.

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

* Re: [Patch net-next v8 02/11] ipv6: make ip6_dst_hoplimit() static inline
  2013-05-22 16:10               ` Mike Rapoport
@ 2013-05-24  5:10                 ` Cong Wang
  2013-05-24  5:15                 ` Cong Wang
  1 sibling, 0 replies; 23+ messages in thread
From: Cong Wang @ 2013-05-24  5:10 UTC (permalink / raw)
  To: Mike Rapoport; +Cc: David Miller, netdev

On Wed, 2013-05-22 at 19:10 +0300, Mike Rapoport wrote:
> I've lost you here... Why not just:
> 
> static inline
> bool vxlan_addr_equal(const union vxlan_addr *a, const union vxlan_addr *b)
> {
>        if (a->sa.sa_family != b->sa.sa_family)
>                return false;
>        if (a->sa.sa_family == AF_INET6)
>                return ipv6_addr_equal(&a->sin6.sin6_addr, &b->sin6.sin6_addr);
>        else
>                return a->sin.sin_addr.s_addr == b->sin.sin_addr.s_addr;
> }

I see your point now, but for !CONFIG_IPV6, the first two 'if' is
obviously useless. Is GCC smart enough to know ->sa.sa_family ==
AF_INET4 is always true in such case? I doubt...

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

* Re: [Patch net-next v8 02/11] ipv6: make ip6_dst_hoplimit() static inline
  2013-05-22 16:10               ` Mike Rapoport
  2013-05-24  5:10                 ` Cong Wang
@ 2013-05-24  5:15                 ` Cong Wang
  1 sibling, 0 replies; 23+ messages in thread
From: Cong Wang @ 2013-05-24  5:15 UTC (permalink / raw)
  To: Mike Rapoport; +Cc: David Miller, netdev

On Wed, 2013-05-22 at 19:10 +0300, Mike Rapoport wrote:
> I've lost you here... Why not just:
> 
> static inline
> bool vxlan_addr_equal(const union vxlan_addr *a, const union vxlan_addr *b)
> {
>        if (a->sa.sa_family != b->sa.sa_family)
>                return false;
>        if (a->sa.sa_family == AF_INET6)
>                return ipv6_addr_equal(&a->sin6.sin6_addr, &b->sin6.sin6_addr);
>        else
>                return a->sin.sin_addr.s_addr == b->sin.sin_addr.s_addr;
> }

I see your point now, but for !CONFIG_IPV6, the first two 'if' is
obviously useless. Is GCC smart enough to know ->sa.sa_family ==
AF_INET4 is always true in such case? I doubt...

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

end of thread, other threads:[~2013-05-24  5:15 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-05-17  0:21 [Patch net-next v8 00/11] vxlan: add ipv6 support Cong Wang
2013-05-17  0:21 ` [Patch net-next v8 01/11] vxlan: defer vxlan init as late as possible Cong Wang
2013-05-17  0:21 ` [Patch net-next v8 02/11] ipv6: make ip6_dst_hoplimit() static inline Cong Wang
2013-05-17 13:02   ` Sergei Shtylyov
2013-05-17 21:13   ` David Miller
2013-05-22  4:54     ` Cong Wang
2013-05-22  7:14       ` David Miller
2013-05-22 10:28         ` Cong Wang
2013-05-22 15:50           ` Mike Rapoport
2013-05-22 16:03             ` Cong Wang
2013-05-22 16:10               ` Mike Rapoport
2013-05-24  5:10                 ` Cong Wang
2013-05-24  5:15                 ` Cong Wang
2013-05-17  0:21 ` [Patch net-next v8 03/11] ipv6: move ip6_local_out into core kernel Cong Wang
2013-05-17  0:21 ` [Patch net-next v8 04/11] ipv6: export a stub for IPv6 symbols used by vxlan Cong Wang
2013-05-17  0:21 ` [Patch net-next v8 05/11] ipv6: export in6addr_loopback to modules Cong Wang
2013-05-17  0:21 ` [Patch net-next v8 06/11] vxlan: add ipv6 support Cong Wang
2013-05-17  0:21 ` [Patch net-next v8 07/11] vxlan: respect disable_ipv6 sysctl Cong Wang
2013-05-17 13:07   ` Sergei Shtylyov
2013-05-17  0:21 ` [Patch net-next v8 08/11] vxlan: add ipv6 route short circuit support Cong Wang
2013-05-17  0:21 ` [Patch net-next v8 09/11] vxlan: add ipv6 proxy support Cong Wang
2013-05-17  0:21 ` [Patch net-next v8 10/11] vxlan: respect scope_id for ll addr Cong Wang
2013-05-17  0:21 ` [Patch net-next v8 11/11] ipv6: Add generic UDP Tunnel segmentation Cong Wang

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).