diff options
author | David S. Miller <davem@davemloft.net> | 2014-10-20 12:38:19 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-10-20 12:38:19 -0400 |
commit | d10845fc85b2e690b5f6425c5ba4df33a073fbc9 (patch) | |
tree | 14c772873becfbdfc26a1cf0c9d967ffd118f9d8 | |
parent | ce8ec4896749783bd6cdc457e6012cfc18e09c8b (diff) | |
parent | f993bc25e5196e60514c216d0bca0f600de64af8 (diff) |
Merge branch 'gso_encap_fixes'
Florian Westphal says:
====================
net: minor gso encapsulation fixes
The following series fixes a minor bug in the gso segmentation handlers
when encapsulation offload is used.
Theoretically this could cause kernel panic when the stack tries
to software-segment such a GRE offload packet, but it looks like there
is only one affected call site (tbf scheduler) and it handles NULL
return value.
I've included a followup patch to add IS_ERR_OR_NULL checks where needed.
While looking into this, I also found that size computation of the individual
segments is incorrect if skb->encapsulation is set.
Please see individual patches for delta vs. v1.
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | net/core/skbuff.c | 13 | ||||
-rw-r--r-- | net/ipv4/af_inet.c | 2 | ||||
-rw-r--r-- | net/ipv4/gre_offload.c | 2 | ||||
-rw-r--r-- | net/ipv4/ip_output.c | 2 | ||||
-rw-r--r-- | net/ipv4/udp_offload.c | 2 | ||||
-rw-r--r-- | net/ipv6/ip6_offload.c | 2 | ||||
-rw-r--r-- | net/mpls/mpls_gso.c | 2 | ||||
-rw-r--r-- | net/netfilter/nfnetlink_queue_core.c | 2 | ||||
-rw-r--r-- | net/openvswitch/datapath.c | 2 | ||||
-rw-r--r-- | net/xfrm/xfrm_output.c | 2 |
10 files changed, 21 insertions, 10 deletions
diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 61059a05ec95..c16615bfb61e 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c | |||
@@ -4070,15 +4070,22 @@ EXPORT_SYMBOL_GPL(skb_scrub_packet); | |||
4070 | unsigned int skb_gso_transport_seglen(const struct sk_buff *skb) | 4070 | unsigned int skb_gso_transport_seglen(const struct sk_buff *skb) |
4071 | { | 4071 | { |
4072 | const struct skb_shared_info *shinfo = skb_shinfo(skb); | 4072 | const struct skb_shared_info *shinfo = skb_shinfo(skb); |
4073 | unsigned int thlen = 0; | ||
4073 | 4074 | ||
4074 | if (likely(shinfo->gso_type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))) | 4075 | if (skb->encapsulation) { |
4075 | return tcp_hdrlen(skb) + shinfo->gso_size; | 4076 | thlen = skb_inner_transport_header(skb) - |
4077 | skb_transport_header(skb); | ||
4076 | 4078 | ||
4079 | if (likely(shinfo->gso_type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))) | ||
4080 | thlen += inner_tcp_hdrlen(skb); | ||
4081 | } else if (likely(shinfo->gso_type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))) { | ||
4082 | thlen = tcp_hdrlen(skb); | ||
4083 | } | ||
4077 | /* UFO sets gso_size to the size of the fragmentation | 4084 | /* UFO sets gso_size to the size of the fragmentation |
4078 | * payload, i.e. the size of the L4 (UDP) header is already | 4085 | * payload, i.e. the size of the L4 (UDP) header is already |
4079 | * accounted for. | 4086 | * accounted for. |
4080 | */ | 4087 | */ |
4081 | return shinfo->gso_size; | 4088 | return thlen + shinfo->gso_size; |
4082 | } | 4089 | } |
4083 | EXPORT_SYMBOL_GPL(skb_gso_transport_seglen); | 4090 | EXPORT_SYMBOL_GPL(skb_gso_transport_seglen); |
4084 | 4091 | ||
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 92db7a69f2b9..8b7fe5b03906 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c | |||
@@ -1246,7 +1246,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb, | |||
1246 | 1246 | ||
1247 | encap = SKB_GSO_CB(skb)->encap_level > 0; | 1247 | encap = SKB_GSO_CB(skb)->encap_level > 0; |
1248 | if (encap) | 1248 | if (encap) |
1249 | features = skb->dev->hw_enc_features & netif_skb_features(skb); | 1249 | features &= skb->dev->hw_enc_features; |
1250 | SKB_GSO_CB(skb)->encap_level += ihl; | 1250 | SKB_GSO_CB(skb)->encap_level += ihl; |
1251 | 1251 | ||
1252 | skb_reset_transport_header(skb); | 1252 | skb_reset_transport_header(skb); |
diff --git a/net/ipv4/gre_offload.c b/net/ipv4/gre_offload.c index ccda09628de7..f6e345c0bc23 100644 --- a/net/ipv4/gre_offload.c +++ b/net/ipv4/gre_offload.c | |||
@@ -68,7 +68,7 @@ static struct sk_buff *gre_gso_segment(struct sk_buff *skb, | |||
68 | skb->mac_len = skb_inner_network_offset(skb); | 68 | skb->mac_len = skb_inner_network_offset(skb); |
69 | 69 | ||
70 | /* segment inner packet. */ | 70 | /* segment inner packet. */ |
71 | enc_features = skb->dev->hw_enc_features & netif_skb_features(skb); | 71 | enc_features = skb->dev->hw_enc_features & features; |
72 | segs = skb_mac_gso_segment(skb, enc_features); | 72 | segs = skb_mac_gso_segment(skb, enc_features); |
73 | if (IS_ERR_OR_NULL(segs)) { | 73 | if (IS_ERR_OR_NULL(segs)) { |
74 | skb_gso_error_unwind(skb, protocol, ghl, mac_offset, mac_len); | 74 | skb_gso_error_unwind(skb, protocol, ghl, mac_offset, mac_len); |
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 88e5ef2c7f51..bc6471d4abcd 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c | |||
@@ -231,7 +231,7 @@ static int ip_finish_output_gso(struct sk_buff *skb) | |||
231 | */ | 231 | */ |
232 | features = netif_skb_features(skb); | 232 | features = netif_skb_features(skb); |
233 | segs = skb_gso_segment(skb, features & ~NETIF_F_GSO_MASK); | 233 | segs = skb_gso_segment(skb, features & ~NETIF_F_GSO_MASK); |
234 | if (IS_ERR(segs)) { | 234 | if (IS_ERR_OR_NULL(segs)) { |
235 | kfree_skb(skb); | 235 | kfree_skb(skb); |
236 | return -ENOMEM; | 236 | return -ENOMEM; |
237 | } | 237 | } |
diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c index 507310ef4b56..6480cea7aa53 100644 --- a/net/ipv4/udp_offload.c +++ b/net/ipv4/udp_offload.c | |||
@@ -58,7 +58,7 @@ static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb, | |||
58 | skb->encap_hdr_csum = 1; | 58 | skb->encap_hdr_csum = 1; |
59 | 59 | ||
60 | /* segment inner packet. */ | 60 | /* segment inner packet. */ |
61 | enc_features = skb->dev->hw_enc_features & netif_skb_features(skb); | 61 | enc_features = skb->dev->hw_enc_features & features; |
62 | segs = gso_inner_segment(skb, enc_features); | 62 | segs = gso_inner_segment(skb, enc_features); |
63 | if (IS_ERR_OR_NULL(segs)) { | 63 | if (IS_ERR_OR_NULL(segs)) { |
64 | skb_gso_error_unwind(skb, protocol, tnl_hlen, mac_offset, | 64 | skb_gso_error_unwind(skb, protocol, tnl_hlen, mac_offset, |
diff --git a/net/ipv6/ip6_offload.c b/net/ipv6/ip6_offload.c index 91014d32488d..a071563a7e6e 100644 --- a/net/ipv6/ip6_offload.c +++ b/net/ipv6/ip6_offload.c | |||
@@ -90,7 +90,7 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, | |||
90 | 90 | ||
91 | encap = SKB_GSO_CB(skb)->encap_level > 0; | 91 | encap = SKB_GSO_CB(skb)->encap_level > 0; |
92 | if (encap) | 92 | if (encap) |
93 | features = skb->dev->hw_enc_features & netif_skb_features(skb); | 93 | features &= skb->dev->hw_enc_features; |
94 | SKB_GSO_CB(skb)->encap_level += sizeof(*ipv6h); | 94 | SKB_GSO_CB(skb)->encap_level += sizeof(*ipv6h); |
95 | 95 | ||
96 | ipv6h = ipv6_hdr(skb); | 96 | ipv6h = ipv6_hdr(skb); |
diff --git a/net/mpls/mpls_gso.c b/net/mpls/mpls_gso.c index e28ed2ef5b06..f0f5309a2d72 100644 --- a/net/mpls/mpls_gso.c +++ b/net/mpls/mpls_gso.c | |||
@@ -48,7 +48,7 @@ static struct sk_buff *mpls_gso_segment(struct sk_buff *skb, | |||
48 | __skb_push(skb, skb->mac_len); | 48 | __skb_push(skb, skb->mac_len); |
49 | 49 | ||
50 | /* Segment inner packet. */ | 50 | /* Segment inner packet. */ |
51 | mpls_features = skb->dev->mpls_features & netif_skb_features(skb); | 51 | mpls_features = skb->dev->mpls_features & features; |
52 | segs = skb_mac_gso_segment(skb, mpls_features); | 52 | segs = skb_mac_gso_segment(skb, mpls_features); |
53 | 53 | ||
54 | 54 | ||
diff --git a/net/netfilter/nfnetlink_queue_core.c b/net/netfilter/nfnetlink_queue_core.c index a82077d9f59b..7c60ccd61a3e 100644 --- a/net/netfilter/nfnetlink_queue_core.c +++ b/net/netfilter/nfnetlink_queue_core.c | |||
@@ -665,7 +665,7 @@ nfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum) | |||
665 | * returned by nf_queue. For instance, callers rely on -ECANCELED to | 665 | * returned by nf_queue. For instance, callers rely on -ECANCELED to |
666 | * mean 'ignore this hook'. | 666 | * mean 'ignore this hook'. |
667 | */ | 667 | */ |
668 | if (IS_ERR(segs)) | 668 | if (IS_ERR_OR_NULL(segs)) |
669 | goto out_err; | 669 | goto out_err; |
670 | queued = 0; | 670 | queued = 0; |
671 | err = 0; | 671 | err = 0; |
diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index 2e31d9e7f4dc..e6d7255183eb 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c | |||
@@ -324,6 +324,8 @@ static int queue_gso_packets(struct datapath *dp, struct sk_buff *skb, | |||
324 | segs = __skb_gso_segment(skb, NETIF_F_SG, false); | 324 | segs = __skb_gso_segment(skb, NETIF_F_SG, false); |
325 | if (IS_ERR(segs)) | 325 | if (IS_ERR(segs)) |
326 | return PTR_ERR(segs); | 326 | return PTR_ERR(segs); |
327 | if (segs == NULL) | ||
328 | return -EINVAL; | ||
327 | 329 | ||
328 | /* Queue all of the segments. */ | 330 | /* Queue all of the segments. */ |
329 | skb = segs; | 331 | skb = segs; |
diff --git a/net/xfrm/xfrm_output.c b/net/xfrm/xfrm_output.c index 499d6c18a8ce..7c532856b398 100644 --- a/net/xfrm/xfrm_output.c +++ b/net/xfrm/xfrm_output.c | |||
@@ -157,6 +157,8 @@ static int xfrm_output_gso(struct sk_buff *skb) | |||
157 | kfree_skb(skb); | 157 | kfree_skb(skb); |
158 | if (IS_ERR(segs)) | 158 | if (IS_ERR(segs)) |
159 | return PTR_ERR(segs); | 159 | return PTR_ERR(segs); |
160 | if (segs == NULL) | ||
161 | return -EINVAL; | ||
160 | 162 | ||
161 | do { | 163 | do { |
162 | struct sk_buff *nskb = segs->next; | 164 | struct sk_buff *nskb = segs->next; |