aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4
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
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')
-rw-r--r--net/ipv4/af_inet.c1
-rw-r--r--net/ipv4/tcp_offload.c1
-rw-r--r--net/ipv4/udp.c40
-rw-r--r--net/ipv4/udp_offload.c4
4 files changed, 24 insertions, 22 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))))