[ovs-dev] [PATCH v4 15/28] datapath: compat: Add support for IPv6 UDP tunnel segmentation.

Pravin B Shelar pshelar at ovn.org
Fri Jul 8 00:23:51 UTC 2016


Next patch adds support for IPV6 Geneve and VXLAN, But support for UDP
segmentation is available on all supported kernel.
Following patch adds support for UDP tunnels over IPv6 for such kernels.

Signed-off-by: Pravin B Shelar <pshelar at ovn.org>
Acked-by: Jesse Gross <jesse at kernel.org>
---
 datapath/linux/compat/gso.c                    | 50 ++++++++++++++++++++-
 datapath/linux/compat/gso.h                    |  4 ++
 datapath/linux/compat/include/net/ip6_tunnel.h |  1 -
 datapath/linux/compat/udp_tunnel.c             | 61 +++++++++++++++-----------
 4 files changed, 88 insertions(+), 28 deletions(-)

diff --git a/datapath/linux/compat/gso.c b/datapath/linux/compat/gso.c
index 27834d2..8812253 100644
--- a/datapath/linux/compat/gso.c
+++ b/datapath/linux/compat/gso.c
@@ -25,6 +25,7 @@
 #include <linux/icmp.h>
 #include <linux/in.h>
 #include <linux/ip.h>
+#include <linux/ipv6.h>
 #include <linux/kernel.h>
 #include <linux/kmod.h>
 #include <linux/netdevice.h>
@@ -179,7 +180,8 @@ static __be16 __skb_network_protocol(struct sk_buff *skb)
 
 static struct sk_buff *tnl_skb_gso_segment(struct sk_buff *skb,
 					   netdev_features_t features,
-					   bool tx_path)
+					   bool tx_path,
+					   sa_family_t sa_family)
 {
 	void *iph = skb_network_header(skb);
 	int pkt_hlen = skb_inner_network_offset(skb); /* inner l2 + tunnel hdr. */
@@ -191,6 +193,7 @@ static struct sk_buff *tnl_skb_gso_segment(struct sk_buff *skb,
 	__be16 proto = skb->protocol;
 	char cb[sizeof(skb->cb)];
 
+	OVS_GSO_CB(skb)->ipv6 = (sa_family == AF_INET6);
 	/* setup whole inner packet to get protocol. */
 	__skb_pull(skb, mac_offset);
 	skb->protocol = __skb_network_protocol(skb);
@@ -267,7 +270,7 @@ int rpl_ip_local_out(struct sk_buff *skb)
 
 		iph = ip_hdr(skb);
 		id = ntohs(iph->id);
-		skb = tnl_skb_gso_segment(skb, 0, false);
+		skb = tnl_skb_gso_segment(skb, 0, false, AF_INET);
 		if (!skb || IS_ERR(skb))
 			return 0;
 	}  else if (skb->ip_summed == CHECKSUM_PARTIAL) {
@@ -295,4 +298,47 @@ int rpl_ip_local_out(struct sk_buff *skb)
 }
 EXPORT_SYMBOL_GPL(rpl_ip_local_out);
 
+static int output_ipv6(struct sk_buff *skb)
+{
+	int ret = NETDEV_TX_OK;
+	int err;
+
+	memset(IP6CB(skb), 0, sizeof (*IP6CB(skb)));
+#undef ip6_local_out
+	err = ip6_local_out(skb);
+	if (unlikely(net_xmit_eval(err)))
+		ret = err;
+
+	return ret;
+}
+
+int rpl_ip6_local_out(struct sk_buff *skb)
+{
+	int ret = NETDEV_TX_OK;
+
+	if (!OVS_GSO_CB(skb)->fix_segment)
+		return output_ipv6(skb);
+
+	if (skb_is_gso(skb)) {
+		skb = tnl_skb_gso_segment(skb, 0, false, AF_INET6);
+		if (!skb || IS_ERR(skb))
+			return 0;
+	}  else if (skb->ip_summed == CHECKSUM_PARTIAL) {
+		int err;
+
+		err = skb_checksum_help(skb);
+		if (unlikely(err))
+			return 0;
+	}
+
+	while (skb) {
+		struct sk_buff *next_skb = skb->next;
+
+		skb->next = NULL;
+		ret = output_ipv6(skb);
+		skb = next_skb;
+	}
+	return ret;
+}
+EXPORT_SYMBOL_GPL(rpl_ip6_local_out);
 #endif /* 3.18 */
diff --git a/datapath/linux/compat/gso.h b/datapath/linux/compat/gso.h
index 0e95970..f082be1 100644
--- a/datapath/linux/compat/gso.h
+++ b/datapath/linux/compat/gso.h
@@ -23,6 +23,7 @@ struct ovs_gso_cb {
 #ifndef HAVE_INNER_NETWORK_HEADER
 	unsigned int	inner_network_header;
 #endif
+	bool ipv6;
 };
 #define OVS_GSO_CB(skb) ((struct ovs_gso_cb *)(skb)->cb)
 
@@ -148,6 +149,9 @@ static inline int skb_inner_mac_offset(const struct sk_buff *skb)
 	return skb_inner_mac_header(skb) - skb->data;
 }
 
+#define ip6_local_out rpl_ip6_local_out
+int rpl_ip6_local_out(struct sk_buff *skb);
+
 #define skb_reset_inner_headers rpl_skb_reset_inner_headers
 static inline void skb_reset_inner_headers(struct sk_buff *skb)
 {
diff --git a/datapath/linux/compat/include/net/ip6_tunnel.h b/datapath/linux/compat/include/net/ip6_tunnel.h
index b807c2c..a47aa3a 100644
--- a/datapath/linux/compat/include/net/ip6_tunnel.h
+++ b/datapath/linux/compat/include/net/ip6_tunnel.h
@@ -16,7 +16,6 @@ static inline void ip6tunnel_xmit(struct sock *sk, struct sk_buff *skb,
 	int pkt_len, err;
 
 	pkt_len = skb->len - skb_inner_network_offset(skb);
-	/* TODO: Fix GSO for ipv6 */
 #ifdef HAVE_IP6_LOCAL_OUT_SK
 	err = ip6_local_out_sk(sk, skb);
 #else
diff --git a/datapath/linux/compat/udp_tunnel.c b/datapath/linux/compat/udp_tunnel.c
index 7c0d34b..a80511b 100644
--- a/datapath/linux/compat/udp_tunnel.c
+++ b/datapath/linux/compat/udp_tunnel.c
@@ -141,31 +141,6 @@ void rpl_setup_udp_tunnel_sock(struct net *net, struct socket *sock,
 }
 EXPORT_SYMBOL_GPL(rpl_setup_udp_tunnel_sock);
 
-void ovs_udp_gso(struct sk_buff *skb)
-{
-	int udp_offset = skb_transport_offset(skb);
-	struct udphdr *uh;
-
-	uh = udp_hdr(skb);
-	uh->len = htons(skb->len - udp_offset);
-}
-EXPORT_SYMBOL_GPL(ovs_udp_gso);
-
-void ovs_udp_csum_gso(struct sk_buff *skb)
-{
-	struct iphdr *iph = ip_hdr(skb);
-	int udp_offset = skb_transport_offset(skb);
-
-	ovs_udp_gso(skb);
-
-	/* csum segment if tunnel sets skb with csum. The cleanest way
-	 * to do this just to set it up from scratch. */
-	skb->ip_summed = CHECKSUM_NONE;
-	udp_set_csum(false, skb, iph->saddr, iph->daddr,
-		     skb->len - udp_offset);
-}
-EXPORT_SYMBOL_GPL(ovs_udp_csum_gso);
-
 void rpl_udp_tunnel_xmit_skb(struct rtable *rt, struct sock *sk,
 			    struct sk_buff *skb, __be32 src, __be32 dst,
 			    __u8 tos, __u8 ttl, __be16 df, __be16 src_port,
@@ -288,4 +263,40 @@ int rpl_udp_tunnel6_xmit_skb(struct dst_entry *dst, struct sock *sk,
 	return 0;
 }
 #endif
+
+void ovs_udp_gso(struct sk_buff *skb)
+{
+	int udp_offset = skb_transport_offset(skb);
+	struct udphdr *uh;
+
+	uh = udp_hdr(skb);
+	uh->len = htons(skb->len - udp_offset);
+}
+EXPORT_SYMBOL_GPL(ovs_udp_gso);
+
+void ovs_udp_csum_gso(struct sk_buff *skb)
+{
+	int udp_offset = skb_transport_offset(skb);
+
+	ovs_udp_gso(skb);
+
+	if (!OVS_GSO_CB(skb)->ipv6) {
+		struct iphdr *iph = ip_hdr(skb);
+
+		/* csum segment if tunnel sets skb with csum. The cleanest way
+		 * to do this just to set it up from scratch. */
+		udp_set_csum(false, skb, iph->saddr, iph->daddr,
+				skb->len - udp_offset);
+#if IS_ENABLED(CONFIG_IPV6)
+	} else {
+		struct ipv6hdr *ip6h;
+
+		ip6h = ipv6_hdr(skb);
+		udp6_set_csum(false, skb, &ip6h->saddr, &ip6h->daddr,
+			      skb->len - udp_offset);
+#endif
+	}
+}
+EXPORT_SYMBOL_GPL(ovs_udp_csum_gso);
+
 #endif
-- 
1.9.1




More information about the dev mailing list