Commit 0f4f4ffa authored by Tom Herbert's avatar Tom Herbert Committed by David S. Miller

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: default avatarTom Herbert <therbert@google.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent e9c3a24b
...@@ -45,6 +45,7 @@ enum { ...@@ -45,6 +45,7 @@ enum {
NETIF_F_GSO_IPIP_BIT, /* ... IPIP tunnel with TSO */ NETIF_F_GSO_IPIP_BIT, /* ... IPIP tunnel with TSO */
NETIF_F_GSO_SIT_BIT, /* ... SIT 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_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_MPLS_BIT, /* ... MPLS segmentation */
/**/NETIF_F_GSO_LAST = /* last bit, see GSO_MASK */ /**/NETIF_F_GSO_LAST = /* last bit, see GSO_MASK */
NETIF_F_GSO_MPLS_BIT, NETIF_F_GSO_MPLS_BIT,
......
...@@ -345,6 +345,8 @@ enum { ...@@ -345,6 +345,8 @@ enum {
SKB_GSO_UDP_TUNNEL = 1 << 9, SKB_GSO_UDP_TUNNEL = 1 << 9,
SKB_GSO_MPLS = 1 << 10, SKB_GSO_MPLS = 1 << 10,
SKB_GSO_UDP_TUNNEL_CSUM = 1 << 11,
}; };
#if BITS_PER_LONG > 32 #if BITS_PER_LONG > 32
......
...@@ -1258,6 +1258,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb, ...@@ -1258,6 +1258,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb,
SKB_GSO_SIT | SKB_GSO_SIT |
SKB_GSO_TCPV6 | SKB_GSO_TCPV6 |
SKB_GSO_UDP_TUNNEL | SKB_GSO_UDP_TUNNEL |
SKB_GSO_UDP_TUNNEL_CSUM |
SKB_GSO_MPLS | SKB_GSO_MPLS |
0))) 0)))
goto out; goto out;
......
...@@ -61,6 +61,7 @@ struct sk_buff *tcp_gso_segment(struct sk_buff *skb, ...@@ -61,6 +61,7 @@ struct sk_buff *tcp_gso_segment(struct sk_buff *skb,
SKB_GSO_SIT | SKB_GSO_SIT |
SKB_GSO_MPLS | SKB_GSO_MPLS |
SKB_GSO_UDP_TUNNEL | SKB_GSO_UDP_TUNNEL |
SKB_GSO_UDP_TUNNEL_CSUM |
0) || 0) ||
!(type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6)))) !(type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))))
goto out; goto out;
......
...@@ -2528,7 +2528,11 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb, ...@@ -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); int tnl_hlen = skb_inner_mac_header(skb) - skb_transport_header(skb);
__be16 protocol = skb->protocol; __be16 protocol = skb->protocol;
netdev_features_t enc_features; 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))) if (unlikely(!pskb_may_pull(skb, tnl_hlen)))
goto out; goto out;
...@@ -2540,6 +2544,10 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb, ...@@ -2540,6 +2544,10 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
skb->mac_len = skb_inner_network_offset(skb); skb->mac_len = skb_inner_network_offset(skb);
skb->protocol = htons(ETH_P_TEB); 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. */ /* segment inner packet. */
enc_features = skb->dev->hw_enc_features & netif_skb_features(skb); enc_features = skb->dev->hw_enc_features & netif_skb_features(skb);
segs = skb_mac_gso_segment(skb, enc_features); segs = skb_mac_gso_segment(skb, enc_features);
...@@ -2550,10 +2558,11 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb, ...@@ -2550,10 +2558,11 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
} }
outer_hlen = skb_tnl_header_len(skb); outer_hlen = skb_tnl_header_len(skb);
udp_offset = outer_hlen - tnl_hlen;
skb = segs; skb = segs;
do { do {
struct udphdr *uh; struct udphdr *uh;
int udp_offset = outer_hlen - tnl_hlen; int len;
skb_reset_inner_headers(skb); skb_reset_inner_headers(skb);
skb->encapsulation = 1; skb->encapsulation = 1;
...@@ -2564,31 +2573,20 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb, ...@@ -2564,31 +2573,20 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
skb_reset_mac_header(skb); skb_reset_mac_header(skb);
skb_set_network_header(skb, mac_len); skb_set_network_header(skb, mac_len);
skb_set_transport_header(skb, udp_offset); skb_set_transport_header(skb, udp_offset);
len = skb->len - udp_offset;
uh = udp_hdr(skb); uh = udp_hdr(skb);
uh->len = htons(skb->len - udp_offset); uh->len = htons(len);
/* csum segment if tunnel sets skb with csum. */
if (protocol == htons(ETH_P_IP) && unlikely(uh->check)) {
struct iphdr *iph = ip_hdr(skb);
uh->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, if (need_csum) {
skb->len - udp_offset, __be32 delta = htonl(oldlen + len);
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;
} else if (protocol == htons(ETH_P_IPV6)) { uh->check = ~csum_fold((__force __wsum)
struct ipv6hdr *ipv6h = ipv6_hdr(skb); ((__force u32)uh->check +
u32 len = skb->len - udp_offset; (__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) if (uh->check == 0)
uh->check = CSUM_MANGLED_0; uh->check = CSUM_MANGLED_0;
skb->ip_summed = CHECKSUM_NONE;
} }
skb->protocol = protocol; skb->protocol = protocol;
......
...@@ -56,7 +56,8 @@ static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb, ...@@ -56,7 +56,8 @@ static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb,
__wsum csum; __wsum csum;
if (skb->encapsulation && 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); segs = skb_udp_tunnel_segment(skb, features);
goto out; goto out;
} }
...@@ -71,6 +72,7 @@ static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb, ...@@ -71,6 +72,7 @@ static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb,
if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY | if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY |
SKB_GSO_UDP_TUNNEL | SKB_GSO_UDP_TUNNEL |
SKB_GSO_UDP_TUNNEL_CSUM |
SKB_GSO_IPIP | SKB_GSO_IPIP |
SKB_GSO_GRE | SKB_GSO_MPLS) || SKB_GSO_GRE | SKB_GSO_MPLS) ||
!(type & (SKB_GSO_UDP)))) !(type & (SKB_GSO_UDP))))
......
...@@ -100,6 +100,7 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, ...@@ -100,6 +100,7 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb,
SKB_GSO_IPIP | SKB_GSO_IPIP |
SKB_GSO_SIT | SKB_GSO_SIT |
SKB_GSO_UDP_TUNNEL | SKB_GSO_UDP_TUNNEL |
SKB_GSO_UDP_TUNNEL_CSUM |
SKB_GSO_MPLS | SKB_GSO_MPLS |
SKB_GSO_TCPV6 | SKB_GSO_TCPV6 |
0))) 0)))
......
...@@ -63,6 +63,7 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb, ...@@ -63,6 +63,7 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb,
if (unlikely(type & ~(SKB_GSO_UDP | if (unlikely(type & ~(SKB_GSO_UDP |
SKB_GSO_DODGY | SKB_GSO_DODGY |
SKB_GSO_UDP_TUNNEL | SKB_GSO_UDP_TUNNEL |
SKB_GSO_UDP_TUNNEL_CSUM |
SKB_GSO_GRE | SKB_GSO_GRE |
SKB_GSO_IPIP | SKB_GSO_IPIP |
SKB_GSO_SIT | SKB_GSO_SIT |
...@@ -76,7 +77,8 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb, ...@@ -76,7 +77,8 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb,
goto out; 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); segs = skb_udp_tunnel_segment(skb, features);
else { else {
/* Do software UFO. Complete and fill in the UDP checksum as HW cannot /* Do software UFO. Complete and fill in the UDP checksum as HW cannot
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment