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 c26d0ec2ef3a90b7a988da8c31f9667716c78392..f1338e0f98669d8833d31886413107d6094465ef 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 d8d397acb52c2469bb6978b76738cf4f1c9a5ac2..5a6d10a538f5820b003887100f837fdb4a767e6e 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 0e9bb08a91e424ca1f5c0351825aaa32d0dc3734..0070ab87109b6ee83d29a331744b209cd2fd2354 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 d8de7b9e07201c25c8b075fe714489d4a29623b9..c02f2d2e7babf831f1f85c28b9f291264969558a 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 a84f6762ea9e5bd13cda3518a05f9c1d217c78eb..8d8c33d84c9a08cb93cebb688dfadb0deb8a7d20 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 88b4023ecfcfc85df907ff7472354084b3b16264..5c23f4765af9214ee285052a7b38d9d064bb9b27 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 b2f091566f88453bce5eb3c33b47e1c9c040447c..d54c5744e3db5f742f22f70b9b4c9b8a72a9d77c 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 b261ee8b83fc87ddabcb447d1235ea8b67429de2..79da8b305cedbac881e5c2f275ee500de03995c6 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