Commit 9207f9d4 authored by Konstantin Khlebnikov's avatar Konstantin Khlebnikov Committed by David S. Miller

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: default avatarKonstantin Khlebnikov <koct9i@gmail.com>
Link: http://lkml.kernel.org/r/CALYGNiP-0MZ-FExV2HutTvE9U-QQtkKSoE--KN=JQE5STYsjAA@mail.gmail.comSigned-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 5d19c619
...@@ -3551,7 +3551,8 @@ struct skb_gso_cb { ...@@ -3551,7 +3551,8 @@ struct skb_gso_cb {
int encap_level; int encap_level;
__u16 csum_start; __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) static inline int skb_tnl_header_len(const struct sk_buff *inner_skb)
{ {
......
...@@ -2695,6 +2695,8 @@ static inline bool skb_needs_check(struct sk_buff *skb, bool tx_path) ...@@ -2695,6 +2695,8 @@ static inline bool skb_needs_check(struct sk_buff *skb, bool tx_path)
* *
* It may return NULL if the skb requires no segmentation. This is * It may return NULL if the skb requires no segmentation. This is
* only possible when GSO is used for verifying header integrity. * 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, struct sk_buff *__skb_gso_segment(struct sk_buff *skb,
netdev_features_t features, bool tx_path) netdev_features_t features, bool tx_path)
...@@ -2709,6 +2711,9 @@ struct sk_buff *__skb_gso_segment(struct sk_buff *skb, ...@@ -2709,6 +2711,9 @@ struct sk_buff *__skb_gso_segment(struct sk_buff *skb,
return ERR_PTR(err); 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)->mac_offset = skb_headroom(skb);
SKB_GSO_CB(skb)->encap_level = 0; SKB_GSO_CB(skb)->encap_level = 0;
......
...@@ -239,6 +239,7 @@ static int ip_finish_output_gso(struct net *net, struct sock *sk, ...@@ -239,6 +239,7 @@ static int ip_finish_output_gso(struct net *net, struct sock *sk,
* from host network stack. * from host network stack.
*/ */
features = netif_skb_features(skb); 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); segs = skb_gso_segment(skb, features & ~NETIF_F_GSO_MASK);
if (IS_ERR_OR_NULL(segs)) { if (IS_ERR_OR_NULL(segs)) {
kfree_skb(skb); kfree_skb(skb);
......
...@@ -336,12 +336,10 @@ static int queue_gso_packets(struct datapath *dp, struct sk_buff *skb, ...@@ -336,12 +336,10 @@ static int queue_gso_packets(struct datapath *dp, struct sk_buff *skb,
unsigned short gso_type = skb_shinfo(skb)->gso_type; unsigned short gso_type = skb_shinfo(skb)->gso_type;
struct sw_flow_key later_key; struct sw_flow_key later_key;
struct sk_buff *segs, *nskb; struct sk_buff *segs, *nskb;
struct ovs_skb_cb ovs_cb;
int err; 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); segs = __skb_gso_segment(skb, NETIF_F_SG, false);
*OVS_CB(skb) = ovs_cb;
if (IS_ERR(segs)) if (IS_ERR(segs))
return PTR_ERR(segs); return PTR_ERR(segs);
if (segs == NULL) if (segs == NULL)
...@@ -359,7 +357,6 @@ static int queue_gso_packets(struct datapath *dp, struct sk_buff *skb, ...@@ -359,7 +357,6 @@ static int queue_gso_packets(struct datapath *dp, struct sk_buff *skb,
/* Queue all of the segments. */ /* Queue all of the segments. */
skb = segs; skb = segs;
do { do {
*OVS_CB(skb) = ovs_cb;
if (gso_type & SKB_GSO_UDP && skb != segs) if (gso_type & SKB_GSO_UDP && skb != segs)
key = &later_key; key = &later_key;
......
...@@ -167,6 +167,8 @@ static int xfrm_output_gso(struct net *net, struct sock *sk, struct sk_buff *skb ...@@ -167,6 +167,8 @@ static int xfrm_output_gso(struct net *net, struct sock *sk, struct sk_buff *skb
{ {
struct sk_buff *segs; 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); segs = skb_gso_segment(skb, 0);
kfree_skb(skb); kfree_skb(skb);
if (IS_ERR(segs)) if (IS_ERR(segs))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment