aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKonstantin Khlebnikov <koct9i@gmail.com>2016-01-08 07:21:46 -0500
committerDavid S. Miller <davem@davemloft.net>2016-01-15 14:35:24 -0500
commit9207f9d45b0ad071baa128e846d7e7ed85016df3 (patch)
tree9053fd455c6a19176174674e898ee05c37724827
parent5d19c619ab3f0551864684f09c2a2f22ee972bef (diff)
net: preserve IP control block during GSO segmentation
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>
-rw-r--r--include/linux/skbuff.h3
-rw-r--r--net/core/dev.c5
-rw-r--r--net/ipv4/ip_output.c1
-rw-r--r--net/openvswitch/datapath.c5
-rw-r--r--net/xfrm/xfrm_output.c2
5 files changed, 11 insertions, 5 deletions
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 07f9ccd28654..11f935c1a090 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -3551,7 +3551,8 @@ struct skb_gso_cb {
3551 int encap_level; 3551 int encap_level;
3552 __u16 csum_start; 3552 __u16 csum_start;
3553}; 3553};
3554#define SKB_GSO_CB(skb) ((struct skb_gso_cb *)(skb)->cb) 3554#define SKB_SGO_CB_OFFSET 32
3555#define SKB_GSO_CB(skb) ((struct skb_gso_cb *)((skb)->cb + SKB_SGO_CB_OFFSET))
3555 3556
3556static inline int skb_tnl_header_len(const struct sk_buff *inner_skb) 3557static inline int skb_tnl_header_len(const struct sk_buff *inner_skb)
3557{ 3558{
diff --git a/net/core/dev.c b/net/core/dev.c
index 0ca95d5d7af0..cc9e3652cf93 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -2695,6 +2695,8 @@ static inline bool skb_needs_check(struct sk_buff *skb, bool tx_path)
2695 * 2695 *
2696 * It may return NULL if the skb requires no segmentation. This is 2696 * It may return NULL if the skb requires no segmentation. This is
2697 * only possible when GSO is used for verifying header integrity. 2697 * only possible when GSO is used for verifying header integrity.
2698 *
2699 * Segmentation preserves SKB_SGO_CB_OFFSET bytes of previous skb cb.
2698 */ 2700 */
2699struct sk_buff *__skb_gso_segment(struct sk_buff *skb, 2701struct sk_buff *__skb_gso_segment(struct sk_buff *skb,
2700 netdev_features_t features, bool tx_path) 2702 netdev_features_t features, bool tx_path)
@@ -2709,6 +2711,9 @@ struct sk_buff *__skb_gso_segment(struct sk_buff *skb,
2709 return ERR_PTR(err); 2711 return ERR_PTR(err);
2710 } 2712 }
2711 2713
2714 BUILD_BUG_ON(SKB_SGO_CB_OFFSET +
2715 sizeof(*SKB_GSO_CB(skb)) > sizeof(skb->cb));
2716
2712 SKB_GSO_CB(skb)->mac_offset = skb_headroom(skb); 2717 SKB_GSO_CB(skb)->mac_offset = skb_headroom(skb);
2713 SKB_GSO_CB(skb)->encap_level = 0; 2718 SKB_GSO_CB(skb)->encap_level = 0;
2714 2719
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index 512a44778cf2..64878efa045c 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -239,6 +239,7 @@ static int ip_finish_output_gso(struct net *net, struct sock *sk,
239 * from host network stack. 239 * from host network stack.
240 */ 240 */
241 features = netif_skb_features(skb); 241 features = netif_skb_features(skb);
242 BUILD_BUG_ON(sizeof(*IPCB(skb)) > SKB_SGO_CB_OFFSET);
242 segs = skb_gso_segment(skb, features & ~NETIF_F_GSO_MASK); 243 segs = skb_gso_segment(skb, features & ~NETIF_F_GSO_MASK);
243 if (IS_ERR_OR_NULL(segs)) { 244 if (IS_ERR_OR_NULL(segs)) {
244 kfree_skb(skb); 245 kfree_skb(skb);
diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c
index 91a8b004dc51..deadfdab1bc3 100644
--- a/net/openvswitch/datapath.c
+++ b/net/openvswitch/datapath.c
@@ -336,12 +336,10 @@ static int queue_gso_packets(struct datapath *dp, struct sk_buff *skb,
336 unsigned short gso_type = skb_shinfo(skb)->gso_type; 336 unsigned short gso_type = skb_shinfo(skb)->gso_type;
337 struct sw_flow_key later_key; 337 struct sw_flow_key later_key;
338 struct sk_buff *segs, *nskb; 338 struct sk_buff *segs, *nskb;
339 struct ovs_skb_cb ovs_cb;
340 int err; 339 int err;
341 340
342 ovs_cb = *OVS_CB(skb); 341 BUILD_BUG_ON(sizeof(*OVS_CB(skb)) > SKB_SGO_CB_OFFSET);
343 segs = __skb_gso_segment(skb, NETIF_F_SG, false); 342 segs = __skb_gso_segment(skb, NETIF_F_SG, false);
344 *OVS_CB(skb) = ovs_cb;
345 if (IS_ERR(segs)) 343 if (IS_ERR(segs))
346 return PTR_ERR(segs); 344 return PTR_ERR(segs);
347 if (segs == NULL) 345 if (segs == NULL)
@@ -359,7 +357,6 @@ static int queue_gso_packets(struct datapath *dp, struct sk_buff *skb,
359 /* Queue all of the segments. */ 357 /* Queue all of the segments. */
360 skb = segs; 358 skb = segs;
361 do { 359 do {
362 *OVS_CB(skb) = ovs_cb;
363 if (gso_type & SKB_GSO_UDP && skb != segs) 360 if (gso_type & SKB_GSO_UDP && skb != segs)
364 key = &later_key; 361 key = &later_key;
365 362
diff --git a/net/xfrm/xfrm_output.c b/net/xfrm/xfrm_output.c
index cc3676eb6239..ff4a91fcab9f 100644
--- a/net/xfrm/xfrm_output.c
+++ b/net/xfrm/xfrm_output.c
@@ -167,6 +167,8 @@ static int xfrm_output_gso(struct net *net, struct sock *sk, struct sk_buff *skb
167{ 167{
168 struct sk_buff *segs; 168 struct sk_buff *segs;
169 169
170 BUILD_BUG_ON(sizeof(*IPCB(skb)) > SKB_SGO_CB_OFFSET);
171 BUILD_BUG_ON(sizeof(*IP6CB(skb)) > SKB_SGO_CB_OFFSET);
170 segs = skb_gso_segment(skb, 0); 172 segs = skb_gso_segment(skb, 0);
171 kfree_skb(skb); 173 kfree_skb(skb);
172 if (IS_ERR(segs)) 174 if (IS_ERR(segs))