[ovs-dev] [PATCHv2 08/20] compat: Wrap IPv4 fragmentation.

Joe Stringer joe at ovn.org
Thu Dec 3 07:53:44 UTC 2015


From: Joe Stringer <joestringer at nicira.com>

Most kernels provide some form of ip fragmentation. However, until
recently many of them would always send ICMP responses for over_MTU
packets, even when operating in bridge mode. Backport the check to
ensure this doesn't occur.

Signed-off-by: Joe Stringer <joestringer at nicira.com>
---
v2: Fix build on newer RHEL kernels.
---
 acinclude.m4                           |  4 +++
 datapath/linux/compat/include/net/ip.h | 46 ++++++++++++++++++++++++++++++++++
 2 files changed, 50 insertions(+)

diff --git a/acinclude.m4 b/acinclude.m4
index 78da9402b2c0..663f5f07edea 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -347,6 +347,7 @@ AC_DEFUN([OVS_CHECK_LINUX_COMPAT], [
 
   OVS_GREP_IFELSE([$KSRC/include/net/ip.h], [inet_get_local_port_range.*net],
                   [OVS_DEFINE([HAVE_INET_GET_LOCAL_PORT_RANGE_USING_NET])])
+  OVS_GREP_IFELSE([$KSRC/include/net/ip.h], [ip_do_fragment])
   OVS_GREP_IFELSE([$KSRC/include/net/ip.h], [ip_is_fragment])
   OVS_GREP_IFELSE([$KSRC/include/net/ip.h], [ip_skb_dst_mtu])
   OVS_GREP_IFELSE([$KSRC/include/net/dst_metadata.h], [metadata_dst])
@@ -473,6 +474,9 @@ AC_DEFUN([OVS_CHECK_LINUX_COMPAT], [
   OVS_GREP_IFELSE([$KSRC/include/net/gre.h], [gre_handle_offloads])
   OVS_GREP_IFELSE([$KSRC/include/net/ipv6.h], [IP6_FH_F_SKIP_RH])
   OVS_GREP_IFELSE([$KSRC/include/net/ipv6.h], [ip6_local_out_sk])
+  OVS_GREP_IFELSE([$KSRC/include/net/ip6_route.h], [ip6_frag.*sock],
+                  [OVS_DEFINE([HAVE_IP_FRAGMENT_TAKES_SOCK])])
+
   OVS_GREP_IFELSE([$KSRC/include/net/netlink.h], [nla_get_be16])
   OVS_GREP_IFELSE([$KSRC/include/net/netlink.h], [nla_put_be16])
   OVS_GREP_IFELSE([$KSRC/include/net/netlink.h], [nla_put_be32])
diff --git a/datapath/linux/compat/include/net/ip.h b/datapath/linux/compat/include/net/ip.h
index ead6e2904ba7..ca06a1af1c07 100644
--- a/datapath/linux/compat/include/net/ip.h
+++ b/datapath/linux/compat/include/net/ip.h
@@ -3,6 +3,7 @@
 
 #include_next <net/ip.h>
 
+#include <net/route.h>
 #include <linux/version.h>
 
 #ifndef HAVE_IP_IS_FRAGMENT
@@ -61,4 +62,49 @@ static inline unsigned int rpl_ip_skb_dst_mtu(const struct sk_buff *skb)
 #define ip_skb_dst_mtu rpl_ip_skb_dst_mtu
 #endif /* HAVE_IP_SKB_DST_MTU */
 
+#ifdef HAVE_IP_FRAGMENT_TAKES_SOCK
+#define OVS_VPORT_OUTPUT_PARAMS struct sock *sock, struct sk_buff *skb
+#else
+#define OVS_VPORT_OUTPUT_PARAMS struct sk_buff *skb
+#endif
+
+#ifdef OVS_FRAGMENT_BACKPORT
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,2,0)
+static inline bool ip_defrag_user_in_between(u32 user,
+					     enum ip_defrag_users lower_bond,
+					     enum ip_defrag_users upper_bond)
+{
+	return user >= lower_bond && user <= upper_bond;
+}
+#endif
+
+#ifndef HAVE_IP_DO_FRAGMENT
+static inline int rpl_ip_do_fragment(struct sock *sk, struct sk_buff *skb,
+				     int (*output)(OVS_VPORT_OUTPUT_PARAMS))
+{
+	unsigned int mtu = ip_skb_dst_mtu(skb);
+	struct iphdr *iph = ip_hdr(skb);
+	struct rtable *rt = skb_rtable(skb);
+	struct net_device *dev = rt->dst.dev;
+
+	if (unlikely(((iph->frag_off & htons(IP_DF)) && !skb->ignore_df) ||
+		     (IPCB(skb)->frag_max_size &&
+		      IPCB(skb)->frag_max_size > mtu))) {
+
+		pr_warn("Dropping packet in ip_do_fragment()\n");
+		IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGFAILS);
+		kfree_skb(skb);
+		return -EMSGSIZE;
+	}
+
+#ifndef HAVE_IP_FRAGMENT_TAKES_SOCK
+	return ip_fragment(skb, output);
+#else
+	return ip_fragment(sk, skb, output);
+#endif
+}
+#define ip_do_fragment rpl_ip_do_fragment
+#endif /* IP_DO_FRAGMENT */
+#endif /* OVS_FRAGMENT_BACKPORT */
 #endif
-- 
2.1.4




More information about the dev mailing list