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 <yinpei...@huawei.com> Signed-off-by: Jesse Gross <je...@nicira.com> --- v2: Use existing backported skb offset APIs to avoid introducing additional version checks. --- datapath/linux/compat/gso.h | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/datapath/linux/compat/gso.h b/datapath/linux/compat/gso.h index 337d13a..0fffdbf 100644 --- a/datapath/linux/compat/gso.h +++ b/datapath/linux/compat/gso.h @@ -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) { @@ -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) @@ -125,6 +142,29 @@ static inline void skb_reset_inner_headers(struct sk_buff *skb) skb_set_inner_network_header(skb, skb_network_offset(skb)); skb_set_inner_transport_header(skb, skb_transport_offset(skb)); } + +static inline int rpl_pskb_expand_head(struct sk_buff *skb, int nhead, + int ntail, gfp_t gfp_mask) +{ + int err; + unsigned 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); + + 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; +} +#define pskb_expand_head rpl_pskb_expand_head + #endif /* 3.18 */ #endif -- 1.9.1 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev