aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4/udp.c
diff options
context:
space:
mode:
authorTom Herbert <therbert@google.com>2014-06-04 20:20:16 -0400
committerDavid S. Miller <davem@davemloft.net>2014-06-05 01:46:38 -0400
commit0f4f4ffa7b7c3d29d0537a126145c9f8d8ed5dbc (patch)
tree03c42c474aacb185f7401acdffea600476d1d6e1 /net/ipv4/udp.c
parente9c3a24b3ace90b428848fdaf772ed264982abcc (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.c40
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;