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 | |
| 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')
| -rw-r--r-- | net/ipv4/af_inet.c | 1 | ||||
| -rw-r--r-- | net/ipv4/tcp_offload.c | 1 | ||||
| -rw-r--r-- | net/ipv4/udp.c | 40 | ||||
| -rw-r--r-- | net/ipv4/udp_offload.c | 4 | ||||
| -rw-r--r-- | net/ipv6/ip6_offload.c | 1 | ||||
| -rw-r--r-- | net/ipv6/udp_offload.c | 4 |
6 files changed, 28 insertions, 23 deletions
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 0e9bb08a91e4..0070ab87109b 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c | |||
| @@ -1258,6 +1258,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb, | |||
| 1258 | SKB_GSO_SIT | | 1258 | SKB_GSO_SIT | |
| 1259 | SKB_GSO_TCPV6 | | 1259 | SKB_GSO_TCPV6 | |
| 1260 | SKB_GSO_UDP_TUNNEL | | 1260 | SKB_GSO_UDP_TUNNEL | |
| 1261 | SKB_GSO_UDP_TUNNEL_CSUM | | ||
| 1261 | SKB_GSO_MPLS | | 1262 | SKB_GSO_MPLS | |
| 1262 | 0))) | 1263 | 0))) |
| 1263 | goto out; | 1264 | goto out; |
diff --git a/net/ipv4/tcp_offload.c b/net/ipv4/tcp_offload.c index d8de7b9e0720..c02f2d2e7bab 100644 --- a/net/ipv4/tcp_offload.c +++ b/net/ipv4/tcp_offload.c | |||
| @@ -61,6 +61,7 @@ struct sk_buff *tcp_gso_segment(struct sk_buff *skb, | |||
| 61 | SKB_GSO_SIT | | 61 | SKB_GSO_SIT | |
| 62 | SKB_GSO_MPLS | | 62 | SKB_GSO_MPLS | |
| 63 | SKB_GSO_UDP_TUNNEL | | 63 | SKB_GSO_UDP_TUNNEL | |
| 64 | SKB_GSO_UDP_TUNNEL_CSUM | | ||
| 64 | 0) || | 65 | 0) || |
| 65 | !(type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6)))) | 66 | !(type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6)))) |
| 66 | goto out; | 67 | goto out; |
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; |
diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c index 88b4023ecfcf..5c23f4765af9 100644 --- a/net/ipv4/udp_offload.c +++ b/net/ipv4/udp_offload.c | |||
| @@ -56,7 +56,8 @@ static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb, | |||
| 56 | __wsum csum; | 56 | __wsum csum; |
| 57 | 57 | ||
| 58 | if (skb->encapsulation && | 58 | if (skb->encapsulation && |
| 59 | skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL) { | 59 | (skb_shinfo(skb)->gso_type & |
| 60 | (SKB_GSO_UDP_TUNNEL|SKB_GSO_UDP_TUNNEL_CSUM))) { | ||
| 60 | segs = skb_udp_tunnel_segment(skb, features); | 61 | segs = skb_udp_tunnel_segment(skb, features); |
| 61 | goto out; | 62 | goto out; |
| 62 | } | 63 | } |
| @@ -71,6 +72,7 @@ static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb, | |||
| 71 | 72 | ||
| 72 | if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY | | 73 | if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY | |
| 73 | SKB_GSO_UDP_TUNNEL | | 74 | SKB_GSO_UDP_TUNNEL | |
| 75 | SKB_GSO_UDP_TUNNEL_CSUM | | ||
| 74 | SKB_GSO_IPIP | | 76 | SKB_GSO_IPIP | |
| 75 | SKB_GSO_GRE | SKB_GSO_MPLS) || | 77 | SKB_GSO_GRE | SKB_GSO_MPLS) || |
| 76 | !(type & (SKB_GSO_UDP)))) | 78 | !(type & (SKB_GSO_UDP)))) |
diff --git a/net/ipv6/ip6_offload.c b/net/ipv6/ip6_offload.c index b2f091566f88..d54c5744e3db 100644 --- a/net/ipv6/ip6_offload.c +++ b/net/ipv6/ip6_offload.c | |||
| @@ -100,6 +100,7 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, | |||
| 100 | SKB_GSO_IPIP | | 100 | SKB_GSO_IPIP | |
| 101 | SKB_GSO_SIT | | 101 | SKB_GSO_SIT | |
| 102 | SKB_GSO_UDP_TUNNEL | | 102 | SKB_GSO_UDP_TUNNEL | |
| 103 | SKB_GSO_UDP_TUNNEL_CSUM | | ||
| 103 | SKB_GSO_MPLS | | 104 | SKB_GSO_MPLS | |
| 104 | SKB_GSO_TCPV6 | | 105 | SKB_GSO_TCPV6 | |
| 105 | 0))) | 106 | 0))) |
diff --git a/net/ipv6/udp_offload.c b/net/ipv6/udp_offload.c index b261ee8b83fc..79da8b305ced 100644 --- a/net/ipv6/udp_offload.c +++ b/net/ipv6/udp_offload.c | |||
| @@ -63,6 +63,7 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb, | |||
| 63 | if (unlikely(type & ~(SKB_GSO_UDP | | 63 | if (unlikely(type & ~(SKB_GSO_UDP | |
| 64 | SKB_GSO_DODGY | | 64 | SKB_GSO_DODGY | |
| 65 | SKB_GSO_UDP_TUNNEL | | 65 | SKB_GSO_UDP_TUNNEL | |
| 66 | SKB_GSO_UDP_TUNNEL_CSUM | | ||
| 66 | SKB_GSO_GRE | | 67 | SKB_GSO_GRE | |
| 67 | SKB_GSO_IPIP | | 68 | SKB_GSO_IPIP | |
| 68 | SKB_GSO_SIT | | 69 | SKB_GSO_SIT | |
| @@ -76,7 +77,8 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb, | |||
| 76 | goto out; | 77 | goto out; |
| 77 | } | 78 | } |
| 78 | 79 | ||
| 79 | if (skb->encapsulation && skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL) | 80 | if (skb->encapsulation && skb_shinfo(skb)->gso_type & |
| 81 | (SKB_GSO_UDP_TUNNEL|SKB_GSO_UDP_TUNNEL_CSUM)) | ||
| 80 | segs = skb_udp_tunnel_segment(skb, features); | 82 | segs = skb_udp_tunnel_segment(skb, features); |
| 81 | else { | 83 | else { |
| 82 | /* Do software UFO. Complete and fill in the UDP checksum as HW cannot | 84 | /* Do software UFO. Complete and fill in the UDP checksum as HW cannot |
