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>
This commit is contained in:
parent
e9c3a24b3a
commit
0f4f4ffa7b
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
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);
|
||||
if (need_csum) {
|
||||
__be32 delta = htonl(oldlen + len);
|
||||
|
||||
uh->check = ~csum_fold((__force __wsum)
|
||||
((__force u32)uh->check +
|
||||
(__force u32)delta));
|
||||
uh->check = gso_make_checksum(skb, ~uh->check);
|
||||
|
||||
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;
|
||||
|
||||
} else if (protocol == htons(ETH_P_IPV6)) {
|
||||
struct ipv6hdr *ipv6h = ipv6_hdr(skb);
|
||||
u32 len = skb->len - udp_offset;
|
||||
|
||||
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;
|
||||
|
@ -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))))
|
||||
|
@ -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)))
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user