Skip to content

Commit 2246387

Browse files
Alexander Duyckdavem330
authored andcommitted
GSO: Provide software checksum of tunneled UDP fragmentation offload
On reviewing the code I realized that GRE and UDP tunnels could cause a kernel panic if we used GSO to segment a large UDP frame that was sent through the tunnel with an outer checksum and hardware offloads were not available. In order to correct this we need to update the feature flags that are passed to the skb_segment function so that in the event of UDP fragmentation being requested for the inner header the segmentation function will correctly generate the checksum for the payload if we cannot segment the outer header. Signed-off-by: Alexander Duyck <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent c3f463b commit 2246387

File tree

3 files changed

+37
-7
lines changed

3 files changed

+37
-7
lines changed

net/ipv4/gre_offload.c

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ static struct sk_buff *gre_gso_segment(struct sk_buff *skb,
2424
__be16 protocol = skb->protocol;
2525
u16 mac_len = skb->mac_len;
2626
int gre_offset, outer_hlen;
27-
bool need_csum;
27+
bool need_csum, ufo;
2828

2929
if (unlikely(skb_shinfo(skb)->gso_type &
3030
~(SKB_GSO_TCPV4 |
@@ -58,8 +58,20 @@ static struct sk_buff *gre_gso_segment(struct sk_buff *skb,
5858
need_csum = !!(skb_shinfo(skb)->gso_type & SKB_GSO_GRE_CSUM);
5959
skb->encap_hdr_csum = need_csum;
6060

61+
ufo = !!(skb_shinfo(skb)->gso_type & SKB_GSO_UDP);
62+
6163
features &= skb->dev->hw_enc_features;
6264

65+
/* The only checksum offload we care about from here on out is the
66+
* outer one so strip the existing checksum feature flags based
67+
* on the fact that we will be computing our checksum in software.
68+
*/
69+
if (ufo) {
70+
features &= ~NETIF_F_CSUM_MASK;
71+
if (!need_csum)
72+
features |= NETIF_F_HW_CSUM;
73+
}
74+
6375
/* segment inner packet. */
6476
segs = skb_mac_gso_segment(skb, features);
6577
if (IS_ERR_OR_NULL(segs)) {
@@ -75,8 +87,11 @@ static struct sk_buff *gre_gso_segment(struct sk_buff *skb,
7587
struct gre_base_hdr *greh;
7688
__be32 *pcsum;
7789

78-
skb_reset_inner_headers(skb);
79-
skb->encapsulation = 1;
90+
/* Set up inner headers if we are offloading inner checksum */
91+
if (skb->ip_summed == CHECKSUM_PARTIAL) {
92+
skb_reset_inner_headers(skb);
93+
skb->encapsulation = 1;
94+
}
8095

8196
skb->mac_len = mac_len;
8297
skb->protocol = protocol;

net/ipv4/udp_offload.c

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb,
3333
__be16 new_protocol, bool is_ipv6)
3434
{
3535
int tnl_hlen = skb_inner_mac_header(skb) - skb_transport_header(skb);
36+
bool remcsum, need_csum, offload_csum, ufo;
3637
struct sk_buff *segs = ERR_PTR(-EINVAL);
37-
bool remcsum, need_csum, offload_csum;
3838
struct udphdr *uh = udp_hdr(skb);
3939
u16 mac_offset = skb->mac_header;
4040
__be16 protocol = skb->protocol;
@@ -62,6 +62,8 @@ static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb,
6262
remcsum = !!(skb_shinfo(skb)->gso_type & SKB_GSO_TUNNEL_REMCSUM);
6363
skb->remcsum_offload = remcsum;
6464

65+
ufo = !!(skb_shinfo(skb)->gso_type & SKB_GSO_UDP);
66+
6567
/* Try to offload checksum if possible */
6668
offload_csum = !!(need_csum &&
6769
(skb->dev->features &
@@ -74,9 +76,9 @@ static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb,
7476
* outer one so strip the existing checksum feature flags and
7577
* instead set the flag based on our outer checksum offload value.
7678
*/
77-
if (remcsum) {
79+
if (remcsum || ufo) {
7880
features &= ~NETIF_F_CSUM_MASK;
79-
if (offload_csum)
81+
if (!need_csum || offload_csum)
8082
features |= NETIF_F_HW_CSUM;
8183
}
8284

@@ -230,6 +232,13 @@ static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb,
230232

231233
skb->ip_summed = CHECKSUM_NONE;
232234

235+
/* If there is no outer header we can fake a checksum offload
236+
* due to the fact that we have already done the checksum in
237+
* software prior to segmenting the frame.
238+
*/
239+
if (!skb->encap_hdr_csum)
240+
features |= NETIF_F_HW_CSUM;
241+
233242
/* Fragment the skb. IP headers of the fragments are updated in
234243
* inet_gso_segment()
235244
*/

net/ipv6/udp_offload.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,18 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb,
8181
csum = skb_checksum(skb, 0, skb->len, 0);
8282
uh->check = udp_v6_check(skb->len, &ipv6h->saddr,
8383
&ipv6h->daddr, csum);
84-
8584
if (uh->check == 0)
8685
uh->check = CSUM_MANGLED_0;
8786

8887
skb->ip_summed = CHECKSUM_NONE;
8988

89+
/* If there is no outer header we can fake a checksum offload
90+
* due to the fact that we have already done the checksum in
91+
* software prior to segmenting the frame.
92+
*/
93+
if (!skb->encap_hdr_csum)
94+
features |= NETIF_F_HW_CSUM;
95+
9096
/* Check if there is enough headroom to insert fragment header. */
9197
tnl_hlen = skb_tnl_header_len(skb);
9298
if (skb->mac_header < (tnl_hlen + frag_hdr_sz)) {

0 commit comments

Comments
 (0)