net: Add GSO support for UDP tunnels with checksum
authorTom Herbert <therbert@google.com>
Thu, 5 Jun 2014 00:20:16 +0000 (17:20 -0700)
committerDavid S. Miller <davem@davemloft.net>
Thu, 5 Jun 2014 05:46:38 +0000 (22:46 -0700)
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>
include/linux/netdev_features.h
include/linux/skbuff.h
net/ipv4/af_inet.c
net/ipv4/tcp_offload.c
net/ipv4/udp.c
net/ipv4/udp_offload.c
net/ipv6/ip6_offload.c
net/ipv6/udp_offload.c

index c26d0ec..f1338e0 100644 (file)
@@ -45,6 +45,7 @@ enum {
        NETIF_F_GSO_IPIP_BIT,           /* ... IPIP tunnel with TSO */
        NETIF_F_GSO_SIT_BIT,            /* ... SIT tunnel with TSO */
        NETIF_F_GSO_UDP_TUNNEL_BIT,     /* ... UDP TUNNEL with TSO */
+       NETIF_F_GSO_UDP_TUNNEL_CSUM_BIT,/* ... UDP TUNNEL with TSO & CSUM */
        NETIF_F_GSO_MPLS_BIT,           /* ... MPLS segmentation */
        /**/NETIF_F_GSO_LAST =          /* last bit, see GSO_MASK */
                NETIF_F_GSO_MPLS_BIT,
index d8d397a..5a6d10a 100644 (file)
@@ -345,6 +345,8 @@ enum {
        SKB_GSO_UDP_TUNNEL = 1 << 9,
 
        SKB_GSO_MPLS = 1 << 10,
+
+       SKB_GSO_UDP_TUNNEL_CSUM = 1 << 11,
 };
 
 #if BITS_PER_LONG > 32
index 0e9bb08..0070ab8 100644 (file)
@@ -1258,6 +1258,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb,
                       SKB_GSO_SIT |
                       SKB_GSO_TCPV6 |
                       SKB_GSO_UDP_TUNNEL |
+                      SKB_GSO_UDP_TUNNEL_CSUM |
                       SKB_GSO_MPLS |
                       0)))
                goto out;
index d8de7b9..c02f2d2 100644 (file)
@@ -61,6 +61,7 @@ struct sk_buff *tcp_gso_segment(struct sk_buff *skb,
                               SKB_GSO_SIT |
                               SKB_GSO_MPLS |
                               SKB_GSO_UDP_TUNNEL |
+                              SKB_GSO_UDP_TUNNEL_CSUM |
                               0) ||
                             !(type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))))
                        goto out;
index a84f676..8d8c33d 100644 (file)
@@ -2528,7 +2528,11 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
        int tnl_hlen = skb_inner_mac_header(skb) - skb_transport_header(skb);
        __be16 protocol = skb->protocol;
        netdev_features_t enc_features;
-       int outer_hlen;
+       int udp_offset, outer_hlen;
+       unsigned int oldlen;
+       bool need_csum;
+
+       oldlen = (u16)~skb->len;
 
        if (unlikely(!pskb_may_pull(skb, tnl_hlen)))
                goto out;
@@ -2540,6 +2544,10 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
        skb->mac_len = skb_inner_network_offset(skb);
        skb->protocol = htons(ETH_P_TEB);
 
+       need_csum = !!(skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL_CSUM);
+       if (need_csum)
+               skb->encap_hdr_csum = 1;
+
        /* segment inner packet. */
        enc_features = skb->dev->hw_enc_features & netif_skb_features(skb);
        segs = skb_mac_gso_segment(skb, enc_features);
@@ -2550,10 +2558,11 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
        }
 
        outer_hlen = skb_tnl_header_len(skb);
+       udp_offset = outer_hlen - tnl_hlen;
        skb = segs;
        do {
                struct udphdr *uh;
-               int udp_offset = outer_hlen - tnl_hlen;
+               int len;
 
                skb_reset_inner_headers(skb);
                skb->encapsulation = 1;
@@ -2564,31 +2573,20 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
                skb_reset_mac_header(skb);
                skb_set_network_header(skb, mac_len);
                skb_set_transport_header(skb, udp_offset);
+               len = skb->len - udp_offset;
                uh = udp_hdr(skb);
-               uh->len = htons(skb->len - udp_offset);
-
-               /* csum segment if tunnel sets skb with csum. */
-               if (protocol == htons(ETH_P_IP) && unlikely(uh->check)) {
-                       struct iphdr *iph = ip_hdr(skb);
+               uh->len = htons(len);
 
-                       uh->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr,
-                                                      skb->len - udp_offset,
-                                                      IPPROTO_UDP, 0);
-                       uh->check = csum_fold(skb_checksum(skb, udp_offset,
-                                                          skb->len - udp_offset, 0));
-                       if (uh->check == 0)
-                               uh->check = CSUM_MANGLED_0;
+               if (need_csum) {
+                       __be32 delta = htonl(oldlen + len);
 
-               } else if (protocol == htons(ETH_P_IPV6)) {
-                       struct ipv6hdr *ipv6h = ipv6_hdr(skb);
-                       u32 len = skb->len - udp_offset;
+                       uh->check = ~csum_fold((__force __wsum)
+                                              ((__force u32)uh->check +
+                                               (__force u32)delta));
+                       uh->check = gso_make_checksum(skb, ~uh->check);
 
-                       uh->check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr,
-                                                    len, IPPROTO_UDP, 0);
-                       uh->check = csum_fold(skb_checksum(skb, udp_offset, len, 0));
                        if (uh->check == 0)
                                uh->check = CSUM_MANGLED_0;
-                       skb->ip_summed = CHECKSUM_NONE;
                }
 
                skb->protocol = protocol;
index 88b4023..5c23f47 100644 (file)
@@ -56,7 +56,8 @@ static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb,
        __wsum csum;
 
        if (skb->encapsulation &&
-           skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL) {
+           (skb_shinfo(skb)->gso_type &
+            (SKB_GSO_UDP_TUNNEL|SKB_GSO_UDP_TUNNEL_CSUM))) {
                segs = skb_udp_tunnel_segment(skb, features);
                goto out;
        }
@@ -71,6 +72,7 @@ static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb,
 
                if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY |
                                      SKB_GSO_UDP_TUNNEL |
+                                     SKB_GSO_UDP_TUNNEL_CSUM |
                                      SKB_GSO_IPIP |
                                      SKB_GSO_GRE | SKB_GSO_MPLS) ||
                             !(type & (SKB_GSO_UDP))))
index b2f0915..d54c574 100644 (file)
@@ -100,6 +100,7 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb,
                       SKB_GSO_IPIP |
                       SKB_GSO_SIT |
                       SKB_GSO_UDP_TUNNEL |
+                      SKB_GSO_UDP_TUNNEL_CSUM |
                       SKB_GSO_MPLS |
                       SKB_GSO_TCPV6 |
                       0)))
index b261ee8..79da8b3 100644 (file)
@@ -63,6 +63,7 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb,
                if (unlikely(type & ~(SKB_GSO_UDP |
                                      SKB_GSO_DODGY |
                                      SKB_GSO_UDP_TUNNEL |
+                                     SKB_GSO_UDP_TUNNEL_CSUM |
                                      SKB_GSO_GRE |
                                      SKB_GSO_IPIP |
                                      SKB_GSO_SIT |
@@ -76,7 +77,8 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb,
                goto out;
        }
 
-       if (skb->encapsulation && skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL)
+       if (skb->encapsulation && skb_shinfo(skb)->gso_type &
+           (SKB_GSO_UDP_TUNNEL|SKB_GSO_UDP_TUNNEL_CSUM))
                segs = skb_udp_tunnel_segment(skb, features);
        else {
                /* Do software UFO. Complete and fill in the UDP checksum as HW cannot