| From foo@baz Tue Jan 26 21:37:04 PST 2016 |
| From: Konstantin Khlebnikov <koct9i@gmail.com> |
| Date: Fri, 8 Jan 2016 15:21:46 +0300 |
| Subject: net: preserve IP control block during GSO segmentation |
| |
| From: Konstantin Khlebnikov <koct9i@gmail.com> |
| |
| [ Upstream commit 9207f9d45b0ad071baa128e846d7e7ed85016df3 ] |
| |
| Skb_gso_segment() uses skb control block during segmentation. |
| This patch adds 32-bytes room for previous control block which |
| will be copied into all resulting segments. |
| |
| This patch fixes kernel crash during fragmenting forwarded packets. |
| Fragmentation requires valid IP CB in skb for clearing ip options. |
| Also patch removes custom save/restore in ovs code, now it's redundant. |
| |
| Signed-off-by: Konstantin Khlebnikov <koct9i@gmail.com> |
| Link: http://lkml.kernel.org/r/CALYGNiP-0MZ-FExV2HutTvE9U-QQtkKSoE--KN=JQE5STYsjAA@mail.gmail.com |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| include/linux/skbuff.h | 3 ++- |
| net/core/dev.c | 5 +++++ |
| net/ipv4/ip_output.c | 1 + |
| net/openvswitch/datapath.c | 5 +---- |
| net/xfrm/xfrm_output.c | 2 ++ |
| 5 files changed, 11 insertions(+), 5 deletions(-) |
| |
| --- a/include/linux/skbuff.h |
| +++ b/include/linux/skbuff.h |
| @@ -3320,7 +3320,8 @@ struct skb_gso_cb { |
| int encap_level; |
| __u16 csum_start; |
| }; |
| -#define SKB_GSO_CB(skb) ((struct skb_gso_cb *)(skb)->cb) |
| +#define SKB_SGO_CB_OFFSET 32 |
| +#define SKB_GSO_CB(skb) ((struct skb_gso_cb *)((skb)->cb + SKB_SGO_CB_OFFSET)) |
| |
| static inline int skb_tnl_header_len(const struct sk_buff *inner_skb) |
| { |
| --- a/net/core/dev.c |
| +++ b/net/core/dev.c |
| @@ -2479,6 +2479,8 @@ static inline bool skb_needs_check(struc |
| * |
| * It may return NULL if the skb requires no segmentation. This is |
| * only possible when GSO is used for verifying header integrity. |
| + * |
| + * Segmentation preserves SKB_SGO_CB_OFFSET bytes of previous skb cb. |
| */ |
| struct sk_buff *__skb_gso_segment(struct sk_buff *skb, |
| netdev_features_t features, bool tx_path) |
| @@ -2493,6 +2495,9 @@ struct sk_buff *__skb_gso_segment(struct |
| return ERR_PTR(err); |
| } |
| |
| + BUILD_BUG_ON(SKB_SGO_CB_OFFSET + |
| + sizeof(*SKB_GSO_CB(skb)) > sizeof(skb->cb)); |
| + |
| SKB_GSO_CB(skb)->mac_offset = skb_headroom(skb); |
| SKB_GSO_CB(skb)->encap_level = 0; |
| |
| --- a/net/ipv4/ip_output.c |
| +++ b/net/ipv4/ip_output.c |
| @@ -235,6 +235,7 @@ static int ip_finish_output_gso(struct s |
| * from host network stack. |
| */ |
| features = netif_skb_features(skb); |
| + BUILD_BUG_ON(sizeof(*IPCB(skb)) > SKB_SGO_CB_OFFSET); |
| segs = skb_gso_segment(skb, features & ~NETIF_F_GSO_MASK); |
| if (IS_ERR_OR_NULL(segs)) { |
| kfree_skb(skb); |
| --- a/net/openvswitch/datapath.c |
| +++ b/net/openvswitch/datapath.c |
| @@ -337,12 +337,10 @@ static int queue_gso_packets(struct data |
| unsigned short gso_type = skb_shinfo(skb)->gso_type; |
| struct sw_flow_key later_key; |
| struct sk_buff *segs, *nskb; |
| - struct ovs_skb_cb ovs_cb; |
| int err; |
| |
| - ovs_cb = *OVS_CB(skb); |
| + BUILD_BUG_ON(sizeof(*OVS_CB(skb)) > SKB_SGO_CB_OFFSET); |
| segs = __skb_gso_segment(skb, NETIF_F_SG, false); |
| - *OVS_CB(skb) = ovs_cb; |
| if (IS_ERR(segs)) |
| return PTR_ERR(segs); |
| if (segs == NULL) |
| @@ -360,7 +358,6 @@ static int queue_gso_packets(struct data |
| /* Queue all of the segments. */ |
| skb = segs; |
| do { |
| - *OVS_CB(skb) = ovs_cb; |
| if (gso_type & SKB_GSO_UDP && skb != segs) |
| key = &later_key; |
| |
| --- a/net/xfrm/xfrm_output.c |
| +++ b/net/xfrm/xfrm_output.c |
| @@ -153,6 +153,8 @@ static int xfrm_output_gso(struct sock * |
| { |
| struct sk_buff *segs; |
| |
| + BUILD_BUG_ON(sizeof(*IPCB(skb)) > SKB_SGO_CB_OFFSET); |
| + BUILD_BUG_ON(sizeof(*IP6CB(skb)) > SKB_SGO_CB_OFFSET); |
| segs = skb_gso_segment(skb, 0); |
| kfree_skb(skb); |
| if (IS_ERR(segs)) |