Skip to content

Commit

Permalink
datapath: Update inner offsets when expanding headroom.
Browse files Browse the repository at this point in the history
skb protocol offsets are relative to the beginning of the
buffer and therefore must be updated if the buffer size is
expanded. Kernel functions do this automatically for existing
fields but obviously not for anything that we backport. This
introduces a wrapper for pskb_expand_head() to update the
inner protocol fields that we have backported.

Without this, a kernel crash can be triggered with tunnel
packets that do not have enough headroom and need to be
segmented. pskb_expand_head() is called in directly through
skb_cow_head() at the beginning of each of the tunnel transmit
routines.

Reported-by: Yinpeijun <[email protected]>
Signed-off-by: Jesse Gross <[email protected]>
Acked-by: Pravin B Shelar <[email protected]>
  • Loading branch information
jessegross committed Apr 13, 2015
1 parent 64d1568 commit a75776e
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 1 deletion.
17 changes: 17 additions & 0 deletions datapath/linux/compat/gso.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,15 @@ static inline int skb_inner_network_offset(const struct sk_buff *skb)
return skb_inner_network_header(skb) - skb->data;
}

/* We don't actually store the transport offset on backports because
* we don't use it anywhere. Slightly rename this version to avoid
* future users from picking it up accidentially.
*/
static inline int ovs_skb_inner_transport_offset(const struct sk_buff *skb)
{
return 0;
}

static inline void skb_set_inner_network_header(const struct sk_buff *skb,
int offset)
{
Expand All @@ -62,6 +71,14 @@ static inline void skb_set_inner_network_header(const struct sk_buff *skb,
static inline void skb_set_inner_transport_header(const struct sk_buff *skb,
int offset)
{ }

#else

static inline int ovs_skb_inner_transport_offset(const struct sk_buff *skb)
{
return skb_inner_transport_header(skb) - skb->data;
}

#endif

#if LINUX_VERSION_CODE < KERNEL_VERSION(3,11,0)
Expand Down
14 changes: 13 additions & 1 deletion datapath/linux/compat/include/linux/skbuff.h
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
#ifndef __LINUX_SKBUFF_WRAPPER_H
#define __LINUX_SKBUFF_WRAPPER_H 1

#include <linux/version.h>
#include <linux/types.h>

#if LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0)
/* This should be before skbuff.h to make sure that we rewrite
* the calls there. */
struct sk_buff;

int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail,
gfp_t gfp_mask);
#define pskb_expand_head rpl_pskb_expand_head
#endif

#include_next <linux/skbuff.h>

#include <linux/jhash.h>
#include <linux/version.h>

#if LINUX_VERSION_CODE < KERNEL_VERSION(3,9,0)
#define SKB_GSO_GRE 0
Expand Down
27 changes: 27 additions & 0 deletions datapath/linux/compat/skbuff-openvswitch.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include <linux/skbuff.h>
#include <linux/if_vlan.h>

#include "gso.h"

#if !defined(HAVE_SKB_WARN_LRO) && defined(NETIF_F_LRO)

#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
Expand Down Expand Up @@ -234,3 +236,28 @@ int skb_vlan_push(struct sk_buff *skb, __be16 vlan_proto, u16 vlan_tci)
return 0;
}
#endif

#if LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0)
int rpl_pskb_expand_head(struct sk_buff *skb, int nhead, int ntail,
gfp_t gfp_mask)
{
int err;
int inner_mac_offset, inner_nw_offset, inner_transport_offset;

inner_mac_offset = skb_inner_mac_offset(skb);
inner_nw_offset = skb_inner_network_offset(skb);
inner_transport_offset = ovs_skb_inner_transport_offset(skb);

#undef pskb_expand_head
err = pskb_expand_head(skb, nhead, ntail, gfp_mask);
if (err)
return err;

skb_set_inner_mac_header(skb, inner_mac_offset);
skb_set_inner_network_header(skb, inner_nw_offset);
skb_set_inner_transport_header(skb, inner_transport_offset);

return 0;
}
EXPORT_SYMBOL(rpl_pskb_expand_head);
#endif

0 comments on commit a75776e

Please sign in to comment.