diff options
author | Tom Herbert <therbert@google.com> | 2014-11-04 12:06:56 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-11-05 16:30:03 -0500 |
commit | b17f709a24013fcbb257f6f89b4d81ac9fdf0d18 (patch) | |
tree | 2d279bf83c7bdbe770a90f784b6c3e2b78e7237d | |
parent | c1aa8347e73e4092411fbd96cc59531fb7e76d04 (diff) |
gue: TX support for using remote checksum offload option
Add if_tunnel flag TUNNEL_ENCAP_FLAG_REMCSUM to configure
remote checksum offload on an IP tunnel. Add logic in gue_build_header
to insert remote checksum offload option.
Signed-off-by: Tom Herbert <therbert@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/net/fou.h | 14 | ||||
-rw-r--r-- | include/uapi/linux/if_tunnel.h | 1 | ||||
-rw-r--r-- | net/ipv4/fou.c | 35 |
3 files changed, 46 insertions, 4 deletions
diff --git a/include/net/fou.h b/include/net/fou.h index cf4ce8874f92..25b26ffcf1df 100644 --- a/include/net/fou.h +++ b/include/net/fou.h | |||
@@ -20,7 +20,19 @@ static size_t fou_encap_hlen(struct ip_tunnel_encap *e) | |||
20 | 20 | ||
21 | static size_t gue_encap_hlen(struct ip_tunnel_encap *e) | 21 | static size_t gue_encap_hlen(struct ip_tunnel_encap *e) |
22 | { | 22 | { |
23 | return sizeof(struct udphdr) + sizeof(struct guehdr); | 23 | size_t len; |
24 | bool need_priv = false; | ||
25 | |||
26 | len = sizeof(struct udphdr) + sizeof(struct guehdr); | ||
27 | |||
28 | if (e->flags & TUNNEL_ENCAP_FLAG_REMCSUM) { | ||
29 | len += GUE_PLEN_REMCSUM; | ||
30 | need_priv = true; | ||
31 | } | ||
32 | |||
33 | len += need_priv ? GUE_LEN_PRIV : 0; | ||
34 | |||
35 | return len; | ||
24 | } | 36 | } |
25 | 37 | ||
26 | #endif | 38 | #endif |
diff --git a/include/uapi/linux/if_tunnel.h b/include/uapi/linux/if_tunnel.h index 280d9e092283..bd3cc11a431f 100644 --- a/include/uapi/linux/if_tunnel.h +++ b/include/uapi/linux/if_tunnel.h | |||
@@ -69,6 +69,7 @@ enum tunnel_encap_types { | |||
69 | 69 | ||
70 | #define TUNNEL_ENCAP_FLAG_CSUM (1<<0) | 70 | #define TUNNEL_ENCAP_FLAG_CSUM (1<<0) |
71 | #define TUNNEL_ENCAP_FLAG_CSUM6 (1<<1) | 71 | #define TUNNEL_ENCAP_FLAG_CSUM6 (1<<1) |
72 | #define TUNNEL_ENCAP_FLAG_REMCSUM (1<<2) | ||
72 | 73 | ||
73 | /* SIT-mode i_flags */ | 74 | /* SIT-mode i_flags */ |
74 | #define SIT_ISATAP 0x0001 | 75 | #define SIT_ISATAP 0x0001 |
diff --git a/net/ipv4/fou.c b/net/ipv4/fou.c index a3b8c5b36303..fb0db99adf9e 100644 --- a/net/ipv4/fou.c +++ b/net/ipv4/fou.c | |||
@@ -562,11 +562,19 @@ int gue_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e, | |||
562 | bool csum = !!(e->flags & TUNNEL_ENCAP_FLAG_CSUM); | 562 | bool csum = !!(e->flags & TUNNEL_ENCAP_FLAG_CSUM); |
563 | int type = csum ? SKB_GSO_UDP_TUNNEL_CSUM : SKB_GSO_UDP_TUNNEL; | 563 | int type = csum ? SKB_GSO_UDP_TUNNEL_CSUM : SKB_GSO_UDP_TUNNEL; |
564 | struct guehdr *guehdr; | 564 | struct guehdr *guehdr; |
565 | size_t optlen = 0; | 565 | size_t hdrlen, optlen = 0; |
566 | __be16 sport; | 566 | __be16 sport; |
567 | void *data; | 567 | void *data; |
568 | bool need_priv = false; | 568 | bool need_priv = false; |
569 | 569 | ||
570 | if ((e->flags & TUNNEL_ENCAP_FLAG_REMCSUM) && | ||
571 | skb->ip_summed == CHECKSUM_PARTIAL) { | ||
572 | csum = false; | ||
573 | optlen += GUE_PLEN_REMCSUM; | ||
574 | type |= SKB_GSO_TUNNEL_REMCSUM; | ||
575 | need_priv = true; | ||
576 | } | ||
577 | |||
570 | optlen += need_priv ? GUE_LEN_PRIV : 0; | 578 | optlen += need_priv ? GUE_LEN_PRIV : 0; |
571 | 579 | ||
572 | skb = iptunnel_handle_offloads(skb, csum, type); | 580 | skb = iptunnel_handle_offloads(skb, csum, type); |
@@ -578,7 +586,9 @@ int gue_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e, | |||
578 | sport = e->sport ? : udp_flow_src_port(dev_net(skb->dev), | 586 | sport = e->sport ? : udp_flow_src_port(dev_net(skb->dev), |
579 | skb, 0, 0, false); | 587 | skb, 0, 0, false); |
580 | 588 | ||
581 | skb_push(skb, sizeof(struct guehdr) + optlen); | 589 | hdrlen = sizeof(struct guehdr) + optlen; |
590 | |||
591 | skb_push(skb, hdrlen); | ||
582 | 592 | ||
583 | guehdr = (struct guehdr *)skb->data; | 593 | guehdr = (struct guehdr *)skb->data; |
584 | 594 | ||
@@ -597,7 +607,26 @@ int gue_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e, | |||
597 | *flags = 0; | 607 | *flags = 0; |
598 | data += GUE_LEN_PRIV; | 608 | data += GUE_LEN_PRIV; |
599 | 609 | ||
600 | /* Add private flags */ | 610 | if (type & SKB_GSO_TUNNEL_REMCSUM) { |
611 | u16 csum_start = skb_checksum_start_offset(skb); | ||
612 | __be16 *pd = data; | ||
613 | |||
614 | if (csum_start < hdrlen) | ||
615 | return -EINVAL; | ||
616 | |||
617 | csum_start -= hdrlen; | ||
618 | pd[0] = htons(csum_start); | ||
619 | pd[1] = htons(csum_start + skb->csum_offset); | ||
620 | |||
621 | if (!skb_is_gso(skb)) { | ||
622 | skb->ip_summed = CHECKSUM_NONE; | ||
623 | skb->encapsulation = 0; | ||
624 | } | ||
625 | |||
626 | *flags |= GUE_PFLAG_REMCSUM; | ||
627 | data += GUE_PLEN_REMCSUM; | ||
628 | } | ||
629 | |||
601 | } | 630 | } |
602 | 631 | ||
603 | fou_build_udp(skb, e, fl4, protocol, sport); | 632 | fou_build_udp(skb, e, fl4, protocol, sport); |