From 213e1f54b4b331b5cfee22a4b770e24db77ec35c Mon Sep 17 00:00:00 2001 From: Joe Stringer Date: Wed, 2 Dec 2015 23:53:44 -0800 Subject: [PATCH] compat: Wrap IPv4 fragmentation. 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 Acked-by: Pravin B Shelar --- 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 78da9402b..663f5f07e 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 ead6e2904..ca06a1af1 100644 --- a/datapath/linux/compat/include/net/ip.h +++ b/datapath/linux/compat/include/net/ip.h @@ -3,6 +3,7 @@ #include_next +#include #include #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.20.1