diff options
author | Tom Herbert <therbert@google.com> | 2014-06-04 20:20:16 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-06-05 01:46:38 -0400 |
commit | 0f4f4ffa7b7c3d29d0537a126145c9f8d8ed5dbc (patch) | |
tree | 03c42c474aacb185f7401acdffea600476d1d6e1 /net/ipv4/udp.c | |
parent | e9c3a24b3ace90b428848fdaf772ed264982abcc (diff) |
net: Add GSO support for UDP tunnels with checksum
Added a new netif feature for GSO_UDP_TUNNEL_CSUM. This indicates
that a device is capable of computing the UDP checksum in the
encapsulating header of a UDP tunnel.
Signed-off-by: Tom Herbert <therbert@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4/udp.c')
-rw-r--r-- | net/ipv4/udp.c | 40 |
1 files changed, 19 insertions, 21 deletions
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index a84f6762ea9e..8d8c33d84c9a 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c | |||
@@ -2528,7 +2528,11 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb, | |||
2528 | int tnl_hlen = skb_inner_mac_header(skb) - skb_transport_header(skb); | 2528 | int tnl_hlen = skb_inner_mac_header(skb) - skb_transport_header(skb); |
2529 | __be16 protocol = skb->protocol; | 2529 | __be16 protocol = skb->protocol; |
2530 | netdev_features_t enc_features; | 2530 | netdev_features_t enc_features; |
2531 | int outer_hlen; | 2531 | int udp_offset, outer_hlen; |
2532 | unsigned int oldlen; | ||
2533 | bool need_csum; | ||
2534 | |||
2535 | oldlen = (u16)~skb->len; | ||
2532 | 2536 | ||
2533 | if (unlikely(!pskb_may_pull(skb, tnl_hlen))) | 2537 | if (unlikely(!pskb_may_pull(skb, tnl_hlen))) |
2534 | goto out; | 2538 | goto out; |
@@ -2540,6 +2544,10 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb, | |||
2540 | skb->mac_len = skb_inner_network_offset(skb); | 2544 | skb->mac_len = skb_inner_network_offset(skb); |
2541 | skb->protocol = htons(ETH_P_TEB); | 2545 | skb->protocol = htons(ETH_P_TEB); |
2542 | 2546 | ||
2547 | need_csum = !!(skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL_CSUM); | ||
2548 | if (need_csum) | ||
2549 | skb->encap_hdr_csum = 1; | ||
2550 | |||
2543 | /* segment inner packet. */ | 2551 | /* segment inner packet. */ |
2544 | enc_features = skb->dev->hw_enc_features & netif_skb_features(skb); | 2552 | enc_features = skb->dev->hw_enc_features & netif_skb_features(skb); |
2545 | segs = skb_mac_gso_segment(skb, enc_features); | 2553 | segs = skb_mac_gso_segment(skb, enc_features); |
@@ -2550,10 +2558,11 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb, | |||
2550 | } | 2558 | } |
2551 | 2559 | ||
2552 | outer_hlen = skb_tnl_header_len(skb); | 2560 | outer_hlen = skb_tnl_header_len(skb); |
2561 | udp_offset = outer_hlen - tnl_hlen; | ||
2553 | skb = segs; | 2562 | skb = segs; |
2554 | do { | 2563 | do { |
2555 | struct udphdr *uh; | 2564 | struct udphdr *uh; |
2556 | int udp_offset = outer_hlen - tnl_hlen; | 2565 | int len; |
2557 | 2566 | ||
2558 | skb_reset_inner_headers(skb); | 2567 | skb_reset_inner_headers(skb); |
2559 | skb->encapsulation = 1; | 2568 | skb->encapsulation = 1; |
@@ -2564,31 +2573,20 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb, | |||
2564 | skb_reset_mac_header(skb); | 2573 | skb_reset_mac_header(skb); |
2565 | skb_set_network_header(skb, mac_len); | 2574 | skb_set_network_header(skb, mac_len); |
2566 | skb_set_transport_header(skb, udp_offset); | 2575 | skb_set_transport_header(skb, udp_offset); |
2576 | len = skb->len - udp_offset; | ||
2567 | uh = udp_hdr(skb); | 2577 | uh = udp_hdr(skb); |
2568 | uh->len = htons(skb->len - udp_offset); | 2578 | uh->len = htons(len); |
2569 | |||
2570 | /* csum segment if tunnel sets skb with csum. */ | ||
2571 | if (protocol == htons(ETH_P_IP) && unlikely(uh->check)) { | ||
2572 | struct iphdr *iph = ip_hdr(skb); | ||
2573 | 2579 | ||
2574 | uh->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, | 2580 | if (need_csum) { |
2575 | skb->len - udp_offset, | 2581 | __be32 delta = htonl(oldlen + len); |
2576 | IPPROTO_UDP, 0); | ||
2577 | uh->check = csum_fold(skb_checksum(skb, udp_offset, | ||
2578 | skb->len - udp_offset, 0)); | ||
2579 | if (uh->check == 0) | ||
2580 | uh->check = CSUM_MANGLED_0; | ||
2581 | 2582 | ||
2582 | } else if (protocol == htons(ETH_P_IPV6)) { | 2583 | uh->check = ~csum_fold((__force __wsum) |
2583 | struct ipv6hdr *ipv6h = ipv6_hdr(skb); | 2584 | ((__force u32)uh->check + |
2584 | u32 len = skb->len - udp_offset; | 2585 | (__force u32)delta)); |
2586 | uh->check = gso_make_checksum(skb, ~uh->check); | ||
2585 | 2587 | ||
2586 | uh->check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr, | ||
2587 | len, IPPROTO_UDP, 0); | ||
2588 | uh->check = csum_fold(skb_checksum(skb, udp_offset, len, 0)); | ||
2589 | if (uh->check == 0) | 2588 | if (uh->check == 0) |
2590 | uh->check = CSUM_MANGLED_0; | 2589 | uh->check = CSUM_MANGLED_0; |
2591 | skb->ip_summed = CHECKSUM_NONE; | ||
2592 | } | 2590 | } |
2593 | 2591 | ||
2594 | skb->protocol = protocol; | 2592 | skb->protocol = protocol; |