diff options
| -rw-r--r-- | include/linux/netdev_features.h | 4 | ||||
| -rw-r--r-- | include/linux/netdevice.h | 1 | ||||
| -rw-r--r-- | include/linux/skbuff.h | 4 | ||||
| -rw-r--r-- | include/net/fou.h | 38 | ||||
| -rw-r--r-- | include/net/gue.h | 103 | ||||
| -rw-r--r-- | include/uapi/linux/if_tunnel.h | 1 | ||||
| -rw-r--r-- | net/core/skbuff.c | 4 | ||||
| -rw-r--r-- | net/ipv4/Kconfig | 9 | ||||
| -rw-r--r-- | net/ipv4/af_inet.c | 1 | ||||
| -rw-r--r-- | net/ipv4/fou.c | 388 | ||||
| -rw-r--r-- | net/ipv4/ip_tunnel.c | 61 | ||||
| -rw-r--r-- | net/ipv4/tcp_offload.c | 1 | ||||
| -rw-r--r-- | net/ipv4/udp_offload.c | 66 | ||||
| -rw-r--r-- | net/ipv6/ip6_offload.c | 1 | ||||
| -rw-r--r-- | net/ipv6/udp_offload.c | 1 |
15 files changed, 565 insertions, 118 deletions
diff --git a/include/linux/netdev_features.h b/include/linux/netdev_features.h index dcfdecbfa0b7..8c94b07e654a 100644 --- a/include/linux/netdev_features.h +++ b/include/linux/netdev_features.h | |||
| @@ -48,8 +48,9 @@ enum { | |||
| 48 | NETIF_F_GSO_UDP_TUNNEL_BIT, /* ... UDP TUNNEL with TSO */ | 48 | NETIF_F_GSO_UDP_TUNNEL_BIT, /* ... UDP TUNNEL with TSO */ |
| 49 | NETIF_F_GSO_UDP_TUNNEL_CSUM_BIT,/* ... UDP TUNNEL with TSO & CSUM */ | 49 | NETIF_F_GSO_UDP_TUNNEL_CSUM_BIT,/* ... UDP TUNNEL with TSO & CSUM */ |
| 50 | NETIF_F_GSO_MPLS_BIT, /* ... MPLS segmentation */ | 50 | NETIF_F_GSO_MPLS_BIT, /* ... MPLS segmentation */ |
| 51 | NETIF_F_GSO_TUNNEL_REMCSUM_BIT, /* ... TUNNEL with TSO & REMCSUM */ | ||
| 51 | /**/NETIF_F_GSO_LAST = /* last bit, see GSO_MASK */ | 52 | /**/NETIF_F_GSO_LAST = /* last bit, see GSO_MASK */ |
| 52 | NETIF_F_GSO_MPLS_BIT, | 53 | NETIF_F_GSO_TUNNEL_REMCSUM_BIT, |
| 53 | 54 | ||
| 54 | NETIF_F_FCOE_CRC_BIT, /* FCoE CRC32 */ | 55 | NETIF_F_FCOE_CRC_BIT, /* FCoE CRC32 */ |
| 55 | NETIF_F_SCTP_CSUM_BIT, /* SCTP checksum offload */ | 56 | NETIF_F_SCTP_CSUM_BIT, /* SCTP checksum offload */ |
| @@ -119,6 +120,7 @@ enum { | |||
| 119 | #define NETIF_F_GSO_UDP_TUNNEL __NETIF_F(GSO_UDP_TUNNEL) | 120 | #define NETIF_F_GSO_UDP_TUNNEL __NETIF_F(GSO_UDP_TUNNEL) |
| 120 | #define NETIF_F_GSO_UDP_TUNNEL_CSUM __NETIF_F(GSO_UDP_TUNNEL_CSUM) | 121 | #define NETIF_F_GSO_UDP_TUNNEL_CSUM __NETIF_F(GSO_UDP_TUNNEL_CSUM) |
| 121 | #define NETIF_F_GSO_MPLS __NETIF_F(GSO_MPLS) | 122 | #define NETIF_F_GSO_MPLS __NETIF_F(GSO_MPLS) |
| 123 | #define NETIF_F_GSO_TUNNEL_REMCSUM __NETIF_F(GSO_TUNNEL_REMCSUM) | ||
| 122 | #define NETIF_F_HW_VLAN_STAG_FILTER __NETIF_F(HW_VLAN_STAG_FILTER) | 124 | #define NETIF_F_HW_VLAN_STAG_FILTER __NETIF_F(HW_VLAN_STAG_FILTER) |
| 123 | #define NETIF_F_HW_VLAN_STAG_RX __NETIF_F(HW_VLAN_STAG_RX) | 125 | #define NETIF_F_HW_VLAN_STAG_RX __NETIF_F(HW_VLAN_STAG_RX) |
| 124 | #define NETIF_F_HW_VLAN_STAG_TX __NETIF_F(HW_VLAN_STAG_TX) | 126 | #define NETIF_F_HW_VLAN_STAG_TX __NETIF_F(HW_VLAN_STAG_TX) |
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 5ed05bd764dc..4767f546d7c0 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h | |||
| @@ -3584,6 +3584,7 @@ static inline bool net_gso_ok(netdev_features_t features, int gso_type) | |||
| 3584 | BUILD_BUG_ON(SKB_GSO_UDP_TUNNEL != (NETIF_F_GSO_UDP_TUNNEL >> NETIF_F_GSO_SHIFT)); | 3584 | BUILD_BUG_ON(SKB_GSO_UDP_TUNNEL != (NETIF_F_GSO_UDP_TUNNEL >> NETIF_F_GSO_SHIFT)); |
| 3585 | BUILD_BUG_ON(SKB_GSO_UDP_TUNNEL_CSUM != (NETIF_F_GSO_UDP_TUNNEL_CSUM >> NETIF_F_GSO_SHIFT)); | 3585 | BUILD_BUG_ON(SKB_GSO_UDP_TUNNEL_CSUM != (NETIF_F_GSO_UDP_TUNNEL_CSUM >> NETIF_F_GSO_SHIFT)); |
| 3586 | BUILD_BUG_ON(SKB_GSO_MPLS != (NETIF_F_GSO_MPLS >> NETIF_F_GSO_SHIFT)); | 3586 | BUILD_BUG_ON(SKB_GSO_MPLS != (NETIF_F_GSO_MPLS >> NETIF_F_GSO_SHIFT)); |
| 3587 | BUILD_BUG_ON(SKB_GSO_TUNNEL_REMCSUM != (NETIF_F_GSO_TUNNEL_REMCSUM >> NETIF_F_GSO_SHIFT)); | ||
| 3587 | 3588 | ||
| 3588 | return (features & feature) == feature; | 3589 | return (features & feature) == feature; |
| 3589 | } | 3590 | } |
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 5ad9675b6fe1..74ed34413969 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h | |||
| @@ -373,6 +373,7 @@ enum { | |||
| 373 | 373 | ||
| 374 | SKB_GSO_MPLS = 1 << 12, | 374 | SKB_GSO_MPLS = 1 << 12, |
| 375 | 375 | ||
| 376 | SKB_GSO_TUNNEL_REMCSUM = 1 << 13, | ||
| 376 | }; | 377 | }; |
| 377 | 378 | ||
| 378 | #if BITS_PER_LONG > 32 | 379 | #if BITS_PER_LONG > 32 |
| @@ -603,7 +604,8 @@ struct sk_buff { | |||
| 603 | #endif | 604 | #endif |
| 604 | __u8 ipvs_property:1; | 605 | __u8 ipvs_property:1; |
| 605 | __u8 inner_protocol_type:1; | 606 | __u8 inner_protocol_type:1; |
| 606 | /* 4 or 6 bit hole */ | 607 | __u8 remcsum_offload:1; |
| 608 | /* 3 or 5 bit hole */ | ||
| 607 | 609 | ||
| 608 | #ifdef CONFIG_NET_SCHED | 610 | #ifdef CONFIG_NET_SCHED |
| 609 | __u16 tc_index; /* traffic control index */ | 611 | __u16 tc_index; /* traffic control index */ |
diff --git a/include/net/fou.h b/include/net/fou.h new file mode 100644 index 000000000000..25b26ffcf1df --- /dev/null +++ b/include/net/fou.h | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | #ifndef __NET_FOU_H | ||
| 2 | #define __NET_FOU_H | ||
| 3 | |||
| 4 | #include <linux/skbuff.h> | ||
| 5 | |||
| 6 | #include <net/flow.h> | ||
| 7 | #include <net/gue.h> | ||
| 8 | #include <net/ip_tunnels.h> | ||
| 9 | #include <net/udp.h> | ||
| 10 | |||
| 11 | int fou_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e, | ||
| 12 | u8 *protocol, struct flowi4 *fl4); | ||
| 13 | int gue_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e, | ||
| 14 | u8 *protocol, struct flowi4 *fl4); | ||
| 15 | |||
| 16 | static size_t fou_encap_hlen(struct ip_tunnel_encap *e) | ||
| 17 | { | ||
| 18 | return sizeof(struct udphdr); | ||
| 19 | } | ||
| 20 | |||
| 21 | static size_t gue_encap_hlen(struct ip_tunnel_encap *e) | ||
| 22 | { | ||
| 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; | ||
| 36 | } | ||
| 37 | |||
| 38 | #endif | ||
diff --git a/include/net/gue.h b/include/net/gue.h index b6c332788084..3f28ec7f1c7f 100644 --- a/include/net/gue.h +++ b/include/net/gue.h | |||
| @@ -1,23 +1,116 @@ | |||
| 1 | #ifndef __NET_GUE_H | 1 | #ifndef __NET_GUE_H |
| 2 | #define __NET_GUE_H | 2 | #define __NET_GUE_H |
| 3 | 3 | ||
| 4 | /* Definitions for the GUE header, standard and private flags, lengths | ||
| 5 | * of optional fields are below. | ||
| 6 | * | ||
| 7 | * Diagram of GUE header: | ||
| 8 | * | ||
| 9 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
| 10 | * |Ver|C| Hlen | Proto/ctype | Standard flags |P| | ||
| 11 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
| 12 | * | | | ||
| 13 | * ~ Fields (optional) ~ | ||
| 14 | * | | | ||
| 15 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
| 16 | * | Private flags (optional, P bit is set) | | ||
| 17 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
| 18 | * | | | ||
| 19 | * ~ Private fields (optional) ~ | ||
| 20 | * | | | ||
| 21 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
| 22 | * | ||
| 23 | * C bit indicates contol message when set, data message when unset. | ||
| 24 | * For a control message, proto/ctype is interpreted as a type of | ||
| 25 | * control message. For data messages, proto/ctype is the IP protocol | ||
| 26 | * of the next header. | ||
| 27 | * | ||
| 28 | * P bit indicates private flags field is present. The private flags | ||
| 29 | * may refer to options placed after this field. | ||
| 30 | */ | ||
| 31 | |||
| 4 | struct guehdr { | 32 | struct guehdr { |
| 5 | union { | 33 | union { |
| 6 | struct { | 34 | struct { |
| 7 | #if defined(__LITTLE_ENDIAN_BITFIELD) | 35 | #if defined(__LITTLE_ENDIAN_BITFIELD) |
| 8 | __u8 hlen:4, | 36 | __u8 hlen:5, |
| 9 | version:4; | 37 | control:1, |
| 38 | version:2; | ||
| 10 | #elif defined (__BIG_ENDIAN_BITFIELD) | 39 | #elif defined (__BIG_ENDIAN_BITFIELD) |
| 11 | __u8 version:4, | 40 | __u8 version:2, |
| 12 | hlen:4; | 41 | control:1, |
| 42 | hlen:5; | ||
| 13 | #else | 43 | #else |
| 14 | #error "Please fix <asm/byteorder.h>" | 44 | #error "Please fix <asm/byteorder.h>" |
| 15 | #endif | 45 | #endif |
| 16 | __u8 next_hdr; | 46 | __u8 proto_ctype; |
| 17 | __u16 flags; | 47 | __u16 flags; |
| 18 | }; | 48 | }; |
| 19 | __u32 word; | 49 | __u32 word; |
| 20 | }; | 50 | }; |
| 21 | }; | 51 | }; |
| 22 | 52 | ||
| 53 | /* Standard flags in GUE header */ | ||
| 54 | |||
| 55 | #define GUE_FLAG_PRIV htons(1<<0) /* Private flags are in options */ | ||
| 56 | #define GUE_LEN_PRIV 4 | ||
| 57 | |||
| 58 | #define GUE_FLAGS_ALL (GUE_FLAG_PRIV) | ||
| 59 | |||
| 60 | /* Private flags in the private option extension */ | ||
| 61 | |||
| 62 | #define GUE_PFLAG_REMCSUM htonl(1 << 31) | ||
| 63 | #define GUE_PLEN_REMCSUM 4 | ||
| 64 | |||
| 65 | #define GUE_PFLAGS_ALL (GUE_PFLAG_REMCSUM) | ||
| 66 | |||
| 67 | /* Functions to compute options length corresponding to flags. | ||
| 68 | * If we ever have a lot of flags this can be potentially be | ||
| 69 | * converted to a more optimized algorithm (table lookup | ||
| 70 | * for instance). | ||
| 71 | */ | ||
| 72 | static inline size_t guehdr_flags_len(__be16 flags) | ||
| 73 | { | ||
| 74 | return ((flags & GUE_FLAG_PRIV) ? GUE_LEN_PRIV : 0); | ||
| 75 | } | ||
| 76 | |||
| 77 | static inline size_t guehdr_priv_flags_len(__be32 flags) | ||
| 78 | { | ||
| 79 | return 0; | ||
| 80 | } | ||
| 81 | |||
| 82 | /* Validate standard and private flags. Returns non-zero (meaning invalid) | ||
| 83 | * if there is an unknown standard or private flags, or the options length for | ||
| 84 | * the flags exceeds the options length specific in hlen of the GUE header. | ||
| 85 | */ | ||
| 86 | static inline int validate_gue_flags(struct guehdr *guehdr, | ||
| 87 | size_t optlen) | ||
| 88 | { | ||
| 89 | size_t len; | ||
| 90 | __be32 flags = guehdr->flags; | ||
| 91 | |||
| 92 | if (flags & ~GUE_FLAGS_ALL) | ||
| 93 | return 1; | ||
| 94 | |||
| 95 | len = guehdr_flags_len(flags); | ||
| 96 | if (len > optlen) | ||
| 97 | return 1; | ||
| 98 | |||
| 99 | if (flags & GUE_FLAG_PRIV) { | ||
| 100 | /* Private flags are last four bytes accounted in | ||
| 101 | * guehdr_flags_len | ||
| 102 | */ | ||
| 103 | flags = *(__be32 *)((void *)&guehdr[1] + len - GUE_LEN_PRIV); | ||
| 104 | |||
| 105 | if (flags & ~GUE_PFLAGS_ALL) | ||
| 106 | return 1; | ||
| 107 | |||
| 108 | len += guehdr_priv_flags_len(flags); | ||
| 109 | if (len > optlen) | ||
| 110 | return 1; | ||
| 111 | } | ||
| 112 | |||
| 113 | return 0; | ||
| 114 | } | ||
| 115 | |||
| 23 | #endif | 116 | #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/core/skbuff.c b/net/core/skbuff.c index e48e5c02e877..700189604f3d 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c | |||
| @@ -3013,7 +3013,7 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb, | |||
| 3013 | if (nskb->len == len + doffset) | 3013 | if (nskb->len == len + doffset) |
| 3014 | goto perform_csum_check; | 3014 | goto perform_csum_check; |
| 3015 | 3015 | ||
| 3016 | if (!sg) { | 3016 | if (!sg && !nskb->remcsum_offload) { |
| 3017 | nskb->ip_summed = CHECKSUM_NONE; | 3017 | nskb->ip_summed = CHECKSUM_NONE; |
| 3018 | nskb->csum = skb_copy_and_csum_bits(head_skb, offset, | 3018 | nskb->csum = skb_copy_and_csum_bits(head_skb, offset, |
| 3019 | skb_put(nskb, len), | 3019 | skb_put(nskb, len), |
| @@ -3085,7 +3085,7 @@ skip_fraglist: | |||
| 3085 | nskb->truesize += nskb->data_len; | 3085 | nskb->truesize += nskb->data_len; |
| 3086 | 3086 | ||
| 3087 | perform_csum_check: | 3087 | perform_csum_check: |
| 3088 | if (!csum) { | 3088 | if (!csum && !nskb->remcsum_offload) { |
| 3089 | nskb->csum = skb_checksum(nskb, doffset, | 3089 | nskb->csum = skb_checksum(nskb, doffset, |
| 3090 | nskb->len - doffset, 0); | 3090 | nskb->len - doffset, 0); |
| 3091 | nskb->ip_summed = CHECKSUM_NONE; | 3091 | nskb->ip_summed = CHECKSUM_NONE; |
diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig index e682b48e0709..bd2901604842 100644 --- a/net/ipv4/Kconfig +++ b/net/ipv4/Kconfig | |||
| @@ -322,6 +322,15 @@ config NET_FOU | |||
| 322 | network mechanisms and optimizations for UDP (such as ECMP | 322 | network mechanisms and optimizations for UDP (such as ECMP |
| 323 | and RSS) can be leveraged to provide better service. | 323 | and RSS) can be leveraged to provide better service. |
| 324 | 324 | ||
| 325 | config NET_FOU_IP_TUNNELS | ||
| 326 | bool "IP: FOU encapsulation of IP tunnels" | ||
| 327 | depends on NET_IPIP || NET_IPGRE || IPV6_SIT | ||
| 328 | select NET_FOU | ||
| 329 | ---help--- | ||
| 330 | Allow configuration of FOU or GUE encapsulation for IP tunnels. | ||
| 331 | When this option is enabled IP tunnels can be configured to use | ||
| 332 | FOU or GUE encapsulation. | ||
| 333 | |||
| 325 | config GENEVE | 334 | config GENEVE |
| 326 | tristate "Generic Network Virtualization Encapsulation (Geneve)" | 335 | tristate "Generic Network Virtualization Encapsulation (Geneve)" |
| 327 | depends on INET | 336 | depends on INET |
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 8b7fe5b03906..ed2c672c5b01 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c | |||
| @@ -1222,6 +1222,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb, | |||
| 1222 | SKB_GSO_TCPV6 | | 1222 | SKB_GSO_TCPV6 | |
| 1223 | SKB_GSO_UDP_TUNNEL | | 1223 | SKB_GSO_UDP_TUNNEL | |
| 1224 | SKB_GSO_UDP_TUNNEL_CSUM | | 1224 | SKB_GSO_UDP_TUNNEL_CSUM | |
| 1225 | SKB_GSO_TUNNEL_REMCSUM | | ||
| 1225 | SKB_GSO_MPLS | | 1226 | SKB_GSO_MPLS | |
| 1226 | 0))) | 1227 | 0))) |
| 1227 | goto out; | 1228 | goto out; |
diff --git a/net/ipv4/fou.c b/net/ipv4/fou.c index 32e78924e246..740ae099a0d9 100644 --- a/net/ipv4/fou.c +++ b/net/ipv4/fou.c | |||
| @@ -38,21 +38,17 @@ static inline struct fou *fou_from_sock(struct sock *sk) | |||
| 38 | return sk->sk_user_data; | 38 | return sk->sk_user_data; |
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | static int fou_udp_encap_recv_deliver(struct sk_buff *skb, | 41 | static void fou_recv_pull(struct sk_buff *skb, size_t len) |
| 42 | u8 protocol, size_t len) | ||
| 43 | { | 42 | { |
| 44 | struct iphdr *iph = ip_hdr(skb); | 43 | struct iphdr *iph = ip_hdr(skb); |
| 45 | 44 | ||
| 46 | /* Remove 'len' bytes from the packet (UDP header and | 45 | /* Remove 'len' bytes from the packet (UDP header and |
| 47 | * FOU header if present), modify the protocol to the one | 46 | * FOU header if present). |
| 48 | * we found, and then call rcv_encap. | ||
| 49 | */ | 47 | */ |
| 50 | iph->tot_len = htons(ntohs(iph->tot_len) - len); | 48 | iph->tot_len = htons(ntohs(iph->tot_len) - len); |
| 51 | __skb_pull(skb, len); | 49 | __skb_pull(skb, len); |
| 52 | skb_postpull_rcsum(skb, udp_hdr(skb), len); | 50 | skb_postpull_rcsum(skb, udp_hdr(skb), len); |
| 53 | skb_reset_transport_header(skb); | 51 | skb_reset_transport_header(skb); |
| 54 | |||
| 55 | return -protocol; | ||
| 56 | } | 52 | } |
| 57 | 53 | ||
| 58 | static int fou_udp_recv(struct sock *sk, struct sk_buff *skb) | 54 | static int fou_udp_recv(struct sock *sk, struct sk_buff *skb) |
| @@ -62,16 +58,78 @@ static int fou_udp_recv(struct sock *sk, struct sk_buff *skb) | |||
| 62 | if (!fou) | 58 | if (!fou) |
| 63 | return 1; | 59 | return 1; |
| 64 | 60 | ||
| 65 | return fou_udp_encap_recv_deliver(skb, fou->protocol, | 61 | fou_recv_pull(skb, sizeof(struct udphdr)); |
| 66 | sizeof(struct udphdr)); | 62 | |
| 63 | return -fou->protocol; | ||
| 64 | } | ||
| 65 | |||
| 66 | static struct guehdr *gue_remcsum(struct sk_buff *skb, struct guehdr *guehdr, | ||
| 67 | void *data, int hdrlen, u8 ipproto) | ||
| 68 | { | ||
| 69 | __be16 *pd = data; | ||
| 70 | u16 start = ntohs(pd[0]); | ||
| 71 | u16 offset = ntohs(pd[1]); | ||
| 72 | u16 poffset = 0; | ||
| 73 | u16 plen; | ||
| 74 | __wsum csum, delta; | ||
| 75 | __sum16 *psum; | ||
| 76 | |||
| 77 | if (skb->remcsum_offload) { | ||
| 78 | /* Already processed in GRO path */ | ||
| 79 | skb->remcsum_offload = 0; | ||
| 80 | return guehdr; | ||
| 81 | } | ||
| 82 | |||
| 83 | if (start > skb->len - hdrlen || | ||
| 84 | offset > skb->len - hdrlen - sizeof(u16)) | ||
| 85 | return NULL; | ||
| 86 | |||
| 87 | if (unlikely(skb->ip_summed != CHECKSUM_COMPLETE)) | ||
| 88 | __skb_checksum_complete(skb); | ||
| 89 | |||
| 90 | plen = hdrlen + offset + sizeof(u16); | ||
| 91 | if (!pskb_may_pull(skb, plen)) | ||
| 92 | return NULL; | ||
| 93 | guehdr = (struct guehdr *)&udp_hdr(skb)[1]; | ||
| 94 | |||
| 95 | if (ipproto == IPPROTO_IP && sizeof(struct iphdr) < plen) { | ||
| 96 | struct iphdr *ip = (struct iphdr *)(skb->data + hdrlen); | ||
| 97 | |||
| 98 | /* If next header happens to be IP we can skip that for the | ||
| 99 | * checksum calculation since the IP header checksum is zero | ||
| 100 | * if correct. | ||
| 101 | */ | ||
| 102 | poffset = ip->ihl * 4; | ||
| 103 | } | ||
| 104 | |||
| 105 | csum = csum_sub(skb->csum, skb_checksum(skb, poffset + hdrlen, | ||
| 106 | start - poffset - hdrlen, 0)); | ||
| 107 | |||
| 108 | /* Set derived checksum in packet */ | ||
| 109 | psum = (__sum16 *)(skb->data + hdrlen + offset); | ||
| 110 | delta = csum_sub(csum_fold(csum), *psum); | ||
| 111 | *psum = csum_fold(csum); | ||
| 112 | |||
| 113 | /* Adjust skb->csum since we changed the packet */ | ||
| 114 | skb->csum = csum_add(skb->csum, delta); | ||
| 115 | |||
| 116 | return guehdr; | ||
| 117 | } | ||
| 118 | |||
| 119 | static int gue_control_message(struct sk_buff *skb, struct guehdr *guehdr) | ||
| 120 | { | ||
| 121 | /* No support yet */ | ||
| 122 | kfree_skb(skb); | ||
| 123 | return 0; | ||
| 67 | } | 124 | } |
| 68 | 125 | ||
| 69 | static int gue_udp_recv(struct sock *sk, struct sk_buff *skb) | 126 | static int gue_udp_recv(struct sock *sk, struct sk_buff *skb) |
| 70 | { | 127 | { |
| 71 | struct fou *fou = fou_from_sock(sk); | 128 | struct fou *fou = fou_from_sock(sk); |
| 72 | size_t len; | 129 | size_t len, optlen, hdrlen; |
| 73 | struct guehdr *guehdr; | 130 | struct guehdr *guehdr; |
| 74 | struct udphdr *uh; | 131 | void *data; |
| 132 | u16 doffset = 0; | ||
| 75 | 133 | ||
| 76 | if (!fou) | 134 | if (!fou) |
| 77 | return 1; | 135 | return 1; |
| @@ -80,25 +138,61 @@ static int gue_udp_recv(struct sock *sk, struct sk_buff *skb) | |||
| 80 | if (!pskb_may_pull(skb, len)) | 138 | if (!pskb_may_pull(skb, len)) |
| 81 | goto drop; | 139 | goto drop; |
| 82 | 140 | ||
| 83 | uh = udp_hdr(skb); | 141 | guehdr = (struct guehdr *)&udp_hdr(skb)[1]; |
| 84 | guehdr = (struct guehdr *)&uh[1]; | 142 | |
| 143 | optlen = guehdr->hlen << 2; | ||
| 144 | len += optlen; | ||
| 85 | 145 | ||
| 86 | len += guehdr->hlen << 2; | ||
| 87 | if (!pskb_may_pull(skb, len)) | 146 | if (!pskb_may_pull(skb, len)) |
| 88 | goto drop; | 147 | goto drop; |
| 89 | 148 | ||
| 90 | uh = udp_hdr(skb); | 149 | /* guehdr may change after pull */ |
| 91 | guehdr = (struct guehdr *)&uh[1]; | 150 | guehdr = (struct guehdr *)&udp_hdr(skb)[1]; |
| 92 | 151 | ||
| 93 | if (guehdr->version != 0) | 152 | hdrlen = sizeof(struct guehdr) + optlen; |
| 94 | goto drop; | ||
| 95 | 153 | ||
| 96 | if (guehdr->flags) { | 154 | if (guehdr->version != 0 || validate_gue_flags(guehdr, optlen)) |
| 97 | /* No support yet */ | ||
| 98 | goto drop; | 155 | goto drop; |
| 156 | |||
| 157 | hdrlen = sizeof(struct guehdr) + optlen; | ||
| 158 | |||
| 159 | ip_hdr(skb)->tot_len = htons(ntohs(ip_hdr(skb)->tot_len) - len); | ||
| 160 | |||
| 161 | /* Pull UDP header now, skb->data points to guehdr */ | ||
| 162 | __skb_pull(skb, sizeof(struct udphdr)); | ||
| 163 | |||
| 164 | /* Pull csum through the guehdr now . This can be used if | ||
| 165 | * there is a remote checksum offload. | ||
| 166 | */ | ||
| 167 | skb_postpull_rcsum(skb, udp_hdr(skb), len); | ||
| 168 | |||
| 169 | data = &guehdr[1]; | ||
| 170 | |||
| 171 | if (guehdr->flags & GUE_FLAG_PRIV) { | ||
| 172 | __be32 flags = *(__be32 *)(data + doffset); | ||
| 173 | |||
| 174 | doffset += GUE_LEN_PRIV; | ||
| 175 | |||
| 176 | if (flags & GUE_PFLAG_REMCSUM) { | ||
| 177 | guehdr = gue_remcsum(skb, guehdr, data + doffset, | ||
| 178 | hdrlen, guehdr->proto_ctype); | ||
| 179 | if (!guehdr) | ||
| 180 | goto drop; | ||
| 181 | |||
| 182 | data = &guehdr[1]; | ||
| 183 | |||
| 184 | doffset += GUE_PLEN_REMCSUM; | ||
| 185 | } | ||
| 99 | } | 186 | } |
| 100 | 187 | ||
| 101 | return fou_udp_encap_recv_deliver(skb, guehdr->next_hdr, len); | 188 | if (unlikely(guehdr->control)) |
| 189 | return gue_control_message(skb, guehdr); | ||
| 190 | |||
| 191 | __skb_pull(skb, hdrlen); | ||
| 192 | skb_reset_transport_header(skb); | ||
| 193 | |||
| 194 | return -guehdr->proto_ctype; | ||
| 195 | |||
| 102 | drop: | 196 | drop: |
| 103 | kfree_skb(skb); | 197 | kfree_skb(skb); |
| 104 | return 0; | 198 | return 0; |
| @@ -147,6 +241,66 @@ out_unlock: | |||
| 147 | return err; | 241 | return err; |
| 148 | } | 242 | } |
| 149 | 243 | ||
| 244 | static struct guehdr *gue_gro_remcsum(struct sk_buff *skb, unsigned int off, | ||
| 245 | struct guehdr *guehdr, void *data, | ||
| 246 | size_t hdrlen, u8 ipproto) | ||
| 247 | { | ||
| 248 | __be16 *pd = data; | ||
| 249 | u16 start = ntohs(pd[0]); | ||
| 250 | u16 offset = ntohs(pd[1]); | ||
| 251 | u16 poffset = 0; | ||
| 252 | u16 plen; | ||
| 253 | void *ptr; | ||
| 254 | __wsum csum, delta; | ||
| 255 | __sum16 *psum; | ||
| 256 | |||
| 257 | if (skb->remcsum_offload) | ||
| 258 | return guehdr; | ||
| 259 | |||
| 260 | if (start > skb_gro_len(skb) - hdrlen || | ||
| 261 | offset > skb_gro_len(skb) - hdrlen - sizeof(u16) || | ||
| 262 | !NAPI_GRO_CB(skb)->csum_valid || skb->remcsum_offload) | ||
| 263 | return NULL; | ||
| 264 | |||
| 265 | plen = hdrlen + offset + sizeof(u16); | ||
| 266 | |||
| 267 | /* Pull checksum that will be written */ | ||
| 268 | if (skb_gro_header_hard(skb, off + plen)) { | ||
| 269 | guehdr = skb_gro_header_slow(skb, off + plen, off); | ||
| 270 | if (!guehdr) | ||
| 271 | return NULL; | ||
| 272 | } | ||
| 273 | |||
| 274 | ptr = (void *)guehdr + hdrlen; | ||
| 275 | |||
| 276 | if (ipproto == IPPROTO_IP && | ||
| 277 | (hdrlen + sizeof(struct iphdr) < plen)) { | ||
| 278 | struct iphdr *ip = (struct iphdr *)(ptr + hdrlen); | ||
| 279 | |||
| 280 | /* If next header happens to be IP we can skip | ||
| 281 | * that for the checksum calculation since the | ||
| 282 | * IP header checksum is zero if correct. | ||
| 283 | */ | ||
| 284 | poffset = ip->ihl * 4; | ||
| 285 | } | ||
| 286 | |||
| 287 | csum = csum_sub(NAPI_GRO_CB(skb)->csum, | ||
| 288 | csum_partial(ptr + poffset, start - poffset, 0)); | ||
| 289 | |||
| 290 | /* Set derived checksum in packet */ | ||
| 291 | psum = (__sum16 *)(ptr + offset); | ||
| 292 | delta = csum_sub(csum_fold(csum), *psum); | ||
| 293 | *psum = csum_fold(csum); | ||
| 294 | |||
| 295 | /* Adjust skb->csum since we changed the packet */ | ||
| 296 | skb->csum = csum_add(skb->csum, delta); | ||
| 297 | NAPI_GRO_CB(skb)->csum = csum_add(NAPI_GRO_CB(skb)->csum, delta); | ||
| 298 | |||
| 299 | skb->remcsum_offload = 1; | ||
| 300 | |||
| 301 | return guehdr; | ||
| 302 | } | ||
| 303 | |||
| 150 | static struct sk_buff **gue_gro_receive(struct sk_buff **head, | 304 | static struct sk_buff **gue_gro_receive(struct sk_buff **head, |
| 151 | struct sk_buff *skb) | 305 | struct sk_buff *skb) |
| 152 | { | 306 | { |
| @@ -154,38 +308,64 @@ static struct sk_buff **gue_gro_receive(struct sk_buff **head, | |||
| 154 | const struct net_offload *ops; | 308 | const struct net_offload *ops; |
| 155 | struct sk_buff **pp = NULL; | 309 | struct sk_buff **pp = NULL; |
| 156 | struct sk_buff *p; | 310 | struct sk_buff *p; |
| 157 | u8 proto; | ||
| 158 | struct guehdr *guehdr; | 311 | struct guehdr *guehdr; |
| 159 | unsigned int hlen, guehlen; | 312 | size_t len, optlen, hdrlen, off; |
| 160 | unsigned int off; | 313 | void *data; |
| 314 | u16 doffset = 0; | ||
| 161 | int flush = 1; | 315 | int flush = 1; |
| 162 | 316 | ||
| 163 | off = skb_gro_offset(skb); | 317 | off = skb_gro_offset(skb); |
| 164 | hlen = off + sizeof(*guehdr); | 318 | len = off + sizeof(*guehdr); |
| 319 | |||
| 165 | guehdr = skb_gro_header_fast(skb, off); | 320 | guehdr = skb_gro_header_fast(skb, off); |
| 166 | if (skb_gro_header_hard(skb, hlen)) { | 321 | if (skb_gro_header_hard(skb, len)) { |
| 167 | guehdr = skb_gro_header_slow(skb, hlen, off); | 322 | guehdr = skb_gro_header_slow(skb, len, off); |
| 168 | if (unlikely(!guehdr)) | 323 | if (unlikely(!guehdr)) |
| 169 | goto out; | 324 | goto out; |
| 170 | } | 325 | } |
| 171 | 326 | ||
| 172 | proto = guehdr->next_hdr; | 327 | optlen = guehdr->hlen << 2; |
| 328 | len += optlen; | ||
| 173 | 329 | ||
| 174 | rcu_read_lock(); | 330 | if (skb_gro_header_hard(skb, len)) { |
| 175 | offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads; | 331 | guehdr = skb_gro_header_slow(skb, len, off); |
| 176 | ops = rcu_dereference(offloads[proto]); | 332 | if (unlikely(!guehdr)) |
| 177 | if (WARN_ON(!ops || !ops->callbacks.gro_receive)) | 333 | goto out; |
| 178 | goto out_unlock; | 334 | } |
| 179 | 335 | ||
| 180 | guehlen = sizeof(*guehdr) + (guehdr->hlen << 2); | 336 | if (unlikely(guehdr->control) || guehdr->version != 0 || |
| 337 | validate_gue_flags(guehdr, optlen)) | ||
| 338 | goto out; | ||
| 181 | 339 | ||
| 182 | hlen = off + guehlen; | 340 | hdrlen = sizeof(*guehdr) + optlen; |
| 183 | if (skb_gro_header_hard(skb, hlen)) { | 341 | |
| 184 | guehdr = skb_gro_header_slow(skb, hlen, off); | 342 | /* Adjust NAPI_GRO_CB(skb)->csum to account for guehdr, |
| 185 | if (unlikely(!guehdr)) | 343 | * this is needed if there is a remote checkcsum offload. |
| 186 | goto out_unlock; | 344 | */ |
| 345 | skb_gro_postpull_rcsum(skb, guehdr, hdrlen); | ||
| 346 | |||
| 347 | data = &guehdr[1]; | ||
| 348 | |||
| 349 | if (guehdr->flags & GUE_FLAG_PRIV) { | ||
| 350 | __be32 flags = *(__be32 *)(data + doffset); | ||
| 351 | |||
| 352 | doffset += GUE_LEN_PRIV; | ||
| 353 | |||
| 354 | if (flags & GUE_PFLAG_REMCSUM) { | ||
| 355 | guehdr = gue_gro_remcsum(skb, off, guehdr, | ||
| 356 | data + doffset, hdrlen, | ||
| 357 | guehdr->proto_ctype); | ||
| 358 | if (!guehdr) | ||
| 359 | goto out; | ||
| 360 | |||
| 361 | data = &guehdr[1]; | ||
| 362 | |||
| 363 | doffset += GUE_PLEN_REMCSUM; | ||
| 364 | } | ||
| 187 | } | 365 | } |
| 188 | 366 | ||
| 367 | skb_gro_pull(skb, hdrlen); | ||
| 368 | |||
| 189 | flush = 0; | 369 | flush = 0; |
| 190 | 370 | ||
| 191 | for (p = *head; p; p = p->next) { | 371 | for (p = *head; p; p = p->next) { |
| @@ -197,7 +377,7 @@ static struct sk_buff **gue_gro_receive(struct sk_buff **head, | |||
| 197 | guehdr2 = (struct guehdr *)(p->data + off); | 377 | guehdr2 = (struct guehdr *)(p->data + off); |
| 198 | 378 | ||
| 199 | /* Compare base GUE header to be equal (covers | 379 | /* Compare base GUE header to be equal (covers |
| 200 | * hlen, version, next_hdr, and flags. | 380 | * hlen, version, proto_ctype, and flags. |
| 201 | */ | 381 | */ |
| 202 | if (guehdr->word != guehdr2->word) { | 382 | if (guehdr->word != guehdr2->word) { |
| 203 | NAPI_GRO_CB(p)->same_flow = 0; | 383 | NAPI_GRO_CB(p)->same_flow = 0; |
| @@ -212,10 +392,11 @@ static struct sk_buff **gue_gro_receive(struct sk_buff **head, | |||
| 212 | } | 392 | } |
| 213 | } | 393 | } |
| 214 | 394 | ||
| 215 | skb_gro_pull(skb, guehlen); | 395 | rcu_read_lock(); |
| 216 | 396 | offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads; | |
| 217 | /* Adjusted NAPI_GRO_CB(skb)->csum after skb_gro_pull()*/ | 397 | ops = rcu_dereference(offloads[guehdr->proto_ctype]); |
| 218 | skb_gro_postpull_rcsum(skb, guehdr, guehlen); | 398 | if (WARN_ON(!ops || !ops->callbacks.gro_receive)) |
| 399 | goto out_unlock; | ||
| 219 | 400 | ||
| 220 | pp = ops->callbacks.gro_receive(head, skb); | 401 | pp = ops->callbacks.gro_receive(head, skb); |
| 221 | 402 | ||
| @@ -236,7 +417,7 @@ static int gue_gro_complete(struct sk_buff *skb, int nhoff) | |||
| 236 | u8 proto; | 417 | u8 proto; |
| 237 | int err = -ENOENT; | 418 | int err = -ENOENT; |
| 238 | 419 | ||
| 239 | proto = guehdr->next_hdr; | 420 | proto = guehdr->proto_ctype; |
| 240 | 421 | ||
| 241 | guehlen = sizeof(*guehdr) + (guehdr->hlen << 2); | 422 | guehlen = sizeof(*guehdr) + (guehdr->hlen << 2); |
| 242 | 423 | ||
| @@ -487,6 +668,125 @@ static const struct genl_ops fou_nl_ops[] = { | |||
| 487 | }, | 668 | }, |
| 488 | }; | 669 | }; |
| 489 | 670 | ||
| 671 | static void fou_build_udp(struct sk_buff *skb, struct ip_tunnel_encap *e, | ||
| 672 | struct flowi4 *fl4, u8 *protocol, __be16 sport) | ||
| 673 | { | ||
| 674 | struct udphdr *uh; | ||
| 675 | |||
| 676 | skb_push(skb, sizeof(struct udphdr)); | ||
| 677 | skb_reset_transport_header(skb); | ||
| 678 | |||
| 679 | uh = udp_hdr(skb); | ||
| 680 | |||
| 681 | uh->dest = e->dport; | ||
| 682 | uh->source = sport; | ||
| 683 | uh->len = htons(skb->len); | ||
| 684 | uh->check = 0; | ||
| 685 | udp_set_csum(!(e->flags & TUNNEL_ENCAP_FLAG_CSUM), skb, | ||
| 686 | fl4->saddr, fl4->daddr, skb->len); | ||
| 687 | |||
| 688 | *protocol = IPPROTO_UDP; | ||
| 689 | } | ||
| 690 | |||
| 691 | int fou_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e, | ||
| 692 | u8 *protocol, struct flowi4 *fl4) | ||
| 693 | { | ||
| 694 | bool csum = !!(e->flags & TUNNEL_ENCAP_FLAG_CSUM); | ||
| 695 | int type = csum ? SKB_GSO_UDP_TUNNEL_CSUM : SKB_GSO_UDP_TUNNEL; | ||
| 696 | __be16 sport; | ||
| 697 | |||
| 698 | skb = iptunnel_handle_offloads(skb, csum, type); | ||
| 699 | |||
| 700 | if (IS_ERR(skb)) | ||
| 701 | return PTR_ERR(skb); | ||
| 702 | |||
| 703 | sport = e->sport ? : udp_flow_src_port(dev_net(skb->dev), | ||
| 704 | skb, 0, 0, false); | ||
| 705 | fou_build_udp(skb, e, fl4, protocol, sport); | ||
| 706 | |||
| 707 | return 0; | ||
| 708 | } | ||
| 709 | EXPORT_SYMBOL(fou_build_header); | ||
| 710 | |||
| 711 | int gue_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e, | ||
| 712 | u8 *protocol, struct flowi4 *fl4) | ||
| 713 | { | ||
| 714 | bool csum = !!(e->flags & TUNNEL_ENCAP_FLAG_CSUM); | ||
| 715 | int type = csum ? SKB_GSO_UDP_TUNNEL_CSUM : SKB_GSO_UDP_TUNNEL; | ||
| 716 | struct guehdr *guehdr; | ||
| 717 | size_t hdrlen, optlen = 0; | ||
| 718 | __be16 sport; | ||
| 719 | void *data; | ||
| 720 | bool need_priv = false; | ||
| 721 | |||
| 722 | if ((e->flags & TUNNEL_ENCAP_FLAG_REMCSUM) && | ||
| 723 | skb->ip_summed == CHECKSUM_PARTIAL) { | ||
| 724 | csum = false; | ||
| 725 | optlen += GUE_PLEN_REMCSUM; | ||
| 726 | type |= SKB_GSO_TUNNEL_REMCSUM; | ||
| 727 | need_priv = true; | ||
| 728 | } | ||
| 729 | |||
| 730 | optlen += need_priv ? GUE_LEN_PRIV : 0; | ||
| 731 | |||
| 732 | skb = iptunnel_handle_offloads(skb, csum, type); | ||
| 733 | |||
| 734 | if (IS_ERR(skb)) | ||
| 735 | return PTR_ERR(skb); | ||
| 736 | |||
| 737 | /* Get source port (based on flow hash) before skb_push */ | ||
| 738 | sport = e->sport ? : udp_flow_src_port(dev_net(skb->dev), | ||
| 739 | skb, 0, 0, false); | ||
| 740 | |||
| 741 | hdrlen = sizeof(struct guehdr) + optlen; | ||
| 742 | |||
| 743 | skb_push(skb, hdrlen); | ||
| 744 | |||
| 745 | guehdr = (struct guehdr *)skb->data; | ||
| 746 | |||
| 747 | guehdr->control = 0; | ||
| 748 | guehdr->version = 0; | ||
| 749 | guehdr->hlen = optlen >> 2; | ||
| 750 | guehdr->flags = 0; | ||
| 751 | guehdr->proto_ctype = *protocol; | ||
| 752 | |||
| 753 | data = &guehdr[1]; | ||
| 754 | |||
| 755 | if (need_priv) { | ||
| 756 | __be32 *flags = data; | ||
| 757 | |||
| 758 | guehdr->flags |= GUE_FLAG_PRIV; | ||
| 759 | *flags = 0; | ||
| 760 | data += GUE_LEN_PRIV; | ||
| 761 | |||
| 762 | if (type & SKB_GSO_TUNNEL_REMCSUM) { | ||
| 763 | u16 csum_start = skb_checksum_start_offset(skb); | ||
| 764 | __be16 *pd = data; | ||
| 765 | |||
| 766 | if (csum_start < hdrlen) | ||
| 767 | return -EINVAL; | ||
| 768 | |||
| 769 | csum_start -= hdrlen; | ||
| 770 | pd[0] = htons(csum_start); | ||
| 771 | pd[1] = htons(csum_start + skb->csum_offset); | ||
| 772 | |||
| 773 | if (!skb_is_gso(skb)) { | ||
| 774 | skb->ip_summed = CHECKSUM_NONE; | ||
| 775 | skb->encapsulation = 0; | ||
| 776 | } | ||
| 777 | |||
| 778 | *flags |= GUE_PFLAG_REMCSUM; | ||
| 779 | data += GUE_PLEN_REMCSUM; | ||
| 780 | } | ||
| 781 | |||
| 782 | } | ||
| 783 | |||
| 784 | fou_build_udp(skb, e, fl4, protocol, sport); | ||
| 785 | |||
| 786 | return 0; | ||
| 787 | } | ||
| 788 | EXPORT_SYMBOL(gue_build_header); | ||
| 789 | |||
| 490 | static int __init fou_init(void) | 790 | static int __init fou_init(void) |
| 491 | { | 791 | { |
| 492 | int ret; | 792 | int ret; |
diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c index 0bb8e141eacc..c3587e1c8b82 100644 --- a/net/ipv4/ip_tunnel.c +++ b/net/ipv4/ip_tunnel.c | |||
| @@ -56,7 +56,10 @@ | |||
| 56 | #include <net/netns/generic.h> | 56 | #include <net/netns/generic.h> |
| 57 | #include <net/rtnetlink.h> | 57 | #include <net/rtnetlink.h> |
| 58 | #include <net/udp.h> | 58 | #include <net/udp.h> |
| 59 | #include <net/gue.h> | 59 | |
| 60 | #if IS_ENABLED(CONFIG_NET_FOU) | ||
| 61 | #include <net/fou.h> | ||
| 62 | #endif | ||
| 60 | 63 | ||
| 61 | #if IS_ENABLED(CONFIG_IPV6) | 64 | #if IS_ENABLED(CONFIG_IPV6) |
| 62 | #include <net/ipv6.h> | 65 | #include <net/ipv6.h> |
| @@ -494,10 +497,12 @@ static int ip_encap_hlen(struct ip_tunnel_encap *e) | |||
| 494 | switch (e->type) { | 497 | switch (e->type) { |
| 495 | case TUNNEL_ENCAP_NONE: | 498 | case TUNNEL_ENCAP_NONE: |
| 496 | return 0; | 499 | return 0; |
| 500 | #if IS_ENABLED(CONFIG_NET_FOU) | ||
| 497 | case TUNNEL_ENCAP_FOU: | 501 | case TUNNEL_ENCAP_FOU: |
| 498 | return sizeof(struct udphdr); | 502 | return fou_encap_hlen(e); |
| 499 | case TUNNEL_ENCAP_GUE: | 503 | case TUNNEL_ENCAP_GUE: |
| 500 | return sizeof(struct udphdr) + sizeof(struct guehdr); | 504 | return gue_encap_hlen(e); |
| 505 | #endif | ||
| 501 | default: | 506 | default: |
| 502 | return -EINVAL; | 507 | return -EINVAL; |
| 503 | } | 508 | } |
| @@ -526,60 +531,18 @@ int ip_tunnel_encap_setup(struct ip_tunnel *t, | |||
| 526 | } | 531 | } |
| 527 | EXPORT_SYMBOL_GPL(ip_tunnel_encap_setup); | 532 | EXPORT_SYMBOL_GPL(ip_tunnel_encap_setup); |
| 528 | 533 | ||
| 529 | static int fou_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e, | ||
| 530 | size_t hdr_len, u8 *protocol, struct flowi4 *fl4) | ||
| 531 | { | ||
| 532 | struct udphdr *uh; | ||
| 533 | __be16 sport; | ||
| 534 | bool csum = !!(e->flags & TUNNEL_ENCAP_FLAG_CSUM); | ||
| 535 | int type = csum ? SKB_GSO_UDP_TUNNEL_CSUM : SKB_GSO_UDP_TUNNEL; | ||
| 536 | |||
| 537 | skb = iptunnel_handle_offloads(skb, csum, type); | ||
| 538 | |||
| 539 | if (IS_ERR(skb)) | ||
| 540 | return PTR_ERR(skb); | ||
| 541 | |||
| 542 | /* Get length and hash before making space in skb */ | ||
| 543 | |||
| 544 | sport = e->sport ? : udp_flow_src_port(dev_net(skb->dev), | ||
| 545 | skb, 0, 0, false); | ||
| 546 | |||
| 547 | skb_push(skb, hdr_len); | ||
| 548 | |||
| 549 | skb_reset_transport_header(skb); | ||
| 550 | uh = udp_hdr(skb); | ||
| 551 | |||
| 552 | if (e->type == TUNNEL_ENCAP_GUE) { | ||
| 553 | struct guehdr *guehdr = (struct guehdr *)&uh[1]; | ||
| 554 | |||
| 555 | guehdr->version = 0; | ||
| 556 | guehdr->hlen = 0; | ||
| 557 | guehdr->flags = 0; | ||
| 558 | guehdr->next_hdr = *protocol; | ||
| 559 | } | ||
| 560 | |||
| 561 | uh->dest = e->dport; | ||
| 562 | uh->source = sport; | ||
| 563 | uh->len = htons(skb->len); | ||
| 564 | uh->check = 0; | ||
| 565 | udp_set_csum(!(e->flags & TUNNEL_ENCAP_FLAG_CSUM), skb, | ||
| 566 | fl4->saddr, fl4->daddr, skb->len); | ||
| 567 | |||
| 568 | *protocol = IPPROTO_UDP; | ||
| 569 | |||
| 570 | return 0; | ||
| 571 | } | ||
| 572 | |||
| 573 | int ip_tunnel_encap(struct sk_buff *skb, struct ip_tunnel *t, | 534 | int ip_tunnel_encap(struct sk_buff *skb, struct ip_tunnel *t, |
| 574 | u8 *protocol, struct flowi4 *fl4) | 535 | u8 *protocol, struct flowi4 *fl4) |
| 575 | { | 536 | { |
| 576 | switch (t->encap.type) { | 537 | switch (t->encap.type) { |
| 577 | case TUNNEL_ENCAP_NONE: | 538 | case TUNNEL_ENCAP_NONE: |
| 578 | return 0; | 539 | return 0; |
| 540 | #if IS_ENABLED(CONFIG_NET_FOU) | ||
| 579 | case TUNNEL_ENCAP_FOU: | 541 | case TUNNEL_ENCAP_FOU: |
| 542 | return fou_build_header(skb, &t->encap, protocol, fl4); | ||
| 580 | case TUNNEL_ENCAP_GUE: | 543 | case TUNNEL_ENCAP_GUE: |
| 581 | return fou_build_header(skb, &t->encap, t->encap_hlen, | 544 | return gue_build_header(skb, &t->encap, protocol, fl4); |
| 582 | protocol, fl4); | 545 | #endif |
| 583 | default: | 546 | default: |
| 584 | return -EINVAL; | 547 | return -EINVAL; |
| 585 | } | 548 | } |
diff --git a/net/ipv4/tcp_offload.c b/net/ipv4/tcp_offload.c index 5b90f2f447a5..a1b2a5624f91 100644 --- a/net/ipv4/tcp_offload.c +++ b/net/ipv4/tcp_offload.c | |||
| @@ -97,6 +97,7 @@ struct sk_buff *tcp_gso_segment(struct sk_buff *skb, | |||
| 97 | SKB_GSO_MPLS | | 97 | SKB_GSO_MPLS | |
| 98 | SKB_GSO_UDP_TUNNEL | | 98 | SKB_GSO_UDP_TUNNEL | |
| 99 | SKB_GSO_UDP_TUNNEL_CSUM | | 99 | SKB_GSO_UDP_TUNNEL_CSUM | |
| 100 | SKB_GSO_TUNNEL_REMCSUM | | ||
| 100 | 0) || | 101 | 0) || |
| 101 | !(type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6)))) | 102 | !(type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6)))) |
| 102 | goto out; | 103 | goto out; |
diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c index 6480cea7aa53..0a5a70d0e84c 100644 --- a/net/ipv4/udp_offload.c +++ b/net/ipv4/udp_offload.c | |||
| @@ -29,7 +29,7 @@ static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb, | |||
| 29 | netdev_features_t features, | 29 | netdev_features_t features, |
| 30 | struct sk_buff *(*gso_inner_segment)(struct sk_buff *skb, | 30 | struct sk_buff *(*gso_inner_segment)(struct sk_buff *skb, |
| 31 | netdev_features_t features), | 31 | netdev_features_t features), |
| 32 | __be16 new_protocol) | 32 | __be16 new_protocol, bool is_ipv6) |
| 33 | { | 33 | { |
| 34 | struct sk_buff *segs = ERR_PTR(-EINVAL); | 34 | struct sk_buff *segs = ERR_PTR(-EINVAL); |
| 35 | u16 mac_offset = skb->mac_header; | 35 | u16 mac_offset = skb->mac_header; |
| @@ -39,7 +39,10 @@ static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb, | |||
| 39 | netdev_features_t enc_features; | 39 | netdev_features_t enc_features; |
| 40 | int udp_offset, outer_hlen; | 40 | int udp_offset, outer_hlen; |
| 41 | unsigned int oldlen; | 41 | unsigned int oldlen; |
| 42 | bool need_csum; | 42 | bool need_csum = !!(skb_shinfo(skb)->gso_type & |
| 43 | SKB_GSO_UDP_TUNNEL_CSUM); | ||
| 44 | bool remcsum = !!(skb_shinfo(skb)->gso_type & SKB_GSO_TUNNEL_REMCSUM); | ||
| 45 | bool offload_csum = false, dont_encap = (need_csum || remcsum); | ||
| 43 | 46 | ||
| 44 | oldlen = (u16)~skb->len; | 47 | oldlen = (u16)~skb->len; |
| 45 | 48 | ||
| @@ -52,10 +55,13 @@ static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb, | |||
| 52 | skb_set_network_header(skb, skb_inner_network_offset(skb)); | 55 | skb_set_network_header(skb, skb_inner_network_offset(skb)); |
| 53 | skb->mac_len = skb_inner_network_offset(skb); | 56 | skb->mac_len = skb_inner_network_offset(skb); |
| 54 | skb->protocol = new_protocol; | 57 | skb->protocol = new_protocol; |
| 58 | skb->encap_hdr_csum = need_csum; | ||
| 59 | skb->remcsum_offload = remcsum; | ||
| 55 | 60 | ||
| 56 | need_csum = !!(skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL_CSUM); | 61 | /* Try to offload checksum if possible */ |
| 57 | if (need_csum) | 62 | offload_csum = !!(need_csum && |
| 58 | skb->encap_hdr_csum = 1; | 63 | (skb->dev->features & |
| 64 | (is_ipv6 ? NETIF_F_V6_CSUM : NETIF_F_V4_CSUM))); | ||
| 59 | 65 | ||
| 60 | /* segment inner packet. */ | 66 | /* segment inner packet. */ |
| 61 | enc_features = skb->dev->hw_enc_features & features; | 67 | enc_features = skb->dev->hw_enc_features & features; |
| @@ -72,11 +78,21 @@ static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb, | |||
| 72 | do { | 78 | do { |
| 73 | struct udphdr *uh; | 79 | struct udphdr *uh; |
| 74 | int len; | 80 | int len; |
| 75 | 81 | __be32 delta; | |
| 76 | skb_reset_inner_headers(skb); | 82 | |
| 77 | skb->encapsulation = 1; | 83 | if (dont_encap) { |
| 84 | skb->encapsulation = 0; | ||
| 85 | skb->ip_summed = CHECKSUM_NONE; | ||
| 86 | } else { | ||
| 87 | /* Only set up inner headers if we might be offloading | ||
| 88 | * inner checksum. | ||
| 89 | */ | ||
| 90 | skb_reset_inner_headers(skb); | ||
| 91 | skb->encapsulation = 1; | ||
| 92 | } | ||
| 78 | 93 | ||
| 79 | skb->mac_len = mac_len; | 94 | skb->mac_len = mac_len; |
| 95 | skb->protocol = protocol; | ||
| 80 | 96 | ||
| 81 | skb_push(skb, outer_hlen); | 97 | skb_push(skb, outer_hlen); |
| 82 | skb_reset_mac_header(skb); | 98 | skb_reset_mac_header(skb); |
| @@ -86,19 +102,36 @@ static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb, | |||
| 86 | uh = udp_hdr(skb); | 102 | uh = udp_hdr(skb); |
| 87 | uh->len = htons(len); | 103 | uh->len = htons(len); |
| 88 | 104 | ||
| 89 | if (need_csum) { | 105 | if (!need_csum) |
| 90 | __be32 delta = htonl(oldlen + len); | 106 | continue; |
| 91 | 107 | ||
| 92 | uh->check = ~csum_fold((__force __wsum) | 108 | delta = htonl(oldlen + len); |
| 93 | ((__force u32)uh->check + | 109 | |
| 94 | (__force u32)delta)); | 110 | uh->check = ~csum_fold((__force __wsum) |
| 111 | ((__force u32)uh->check + | ||
| 112 | (__force u32)delta)); | ||
| 113 | if (offload_csum) { | ||
| 114 | skb->ip_summed = CHECKSUM_PARTIAL; | ||
| 115 | skb->csum_start = skb_transport_header(skb) - skb->head; | ||
| 116 | skb->csum_offset = offsetof(struct udphdr, check); | ||
| 117 | } else if (remcsum) { | ||
| 118 | /* Need to calculate checksum from scratch, | ||
| 119 | * inner checksums are never when doing | ||
| 120 | * remote_checksum_offload. | ||
| 121 | */ | ||
| 122 | |||
| 123 | skb->csum = skb_checksum(skb, udp_offset, | ||
| 124 | skb->len - udp_offset, | ||
| 125 | 0); | ||
| 126 | uh->check = csum_fold(skb->csum); | ||
| 127 | if (uh->check == 0) | ||
| 128 | uh->check = CSUM_MANGLED_0; | ||
| 129 | } else { | ||
| 95 | uh->check = gso_make_checksum(skb, ~uh->check); | 130 | uh->check = gso_make_checksum(skb, ~uh->check); |
| 96 | 131 | ||
| 97 | if (uh->check == 0) | 132 | if (uh->check == 0) |
| 98 | uh->check = CSUM_MANGLED_0; | 133 | uh->check = CSUM_MANGLED_0; |
| 99 | } | 134 | } |
| 100 | |||
| 101 | skb->protocol = protocol; | ||
| 102 | } while ((skb = skb->next)); | 135 | } while ((skb = skb->next)); |
| 103 | out: | 136 | out: |
| 104 | return segs; | 137 | return segs; |
| @@ -134,7 +167,7 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb, | |||
| 134 | } | 167 | } |
| 135 | 168 | ||
| 136 | segs = __skb_udp_tunnel_segment(skb, features, gso_inner_segment, | 169 | segs = __skb_udp_tunnel_segment(skb, features, gso_inner_segment, |
| 137 | protocol); | 170 | protocol, is_ipv6); |
| 138 | 171 | ||
| 139 | out_unlock: | 172 | out_unlock: |
| 140 | rcu_read_unlock(); | 173 | rcu_read_unlock(); |
| @@ -172,6 +205,7 @@ static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb, | |||
| 172 | if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY | | 205 | if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY | |
| 173 | SKB_GSO_UDP_TUNNEL | | 206 | SKB_GSO_UDP_TUNNEL | |
| 174 | SKB_GSO_UDP_TUNNEL_CSUM | | 207 | SKB_GSO_UDP_TUNNEL_CSUM | |
| 208 | SKB_GSO_TUNNEL_REMCSUM | | ||
| 175 | SKB_GSO_IPIP | | 209 | SKB_GSO_IPIP | |
| 176 | SKB_GSO_GRE | SKB_GSO_GRE_CSUM | | 210 | SKB_GSO_GRE | SKB_GSO_GRE_CSUM | |
| 177 | SKB_GSO_MPLS) || | 211 | SKB_GSO_MPLS) || |
diff --git a/net/ipv6/ip6_offload.c b/net/ipv6/ip6_offload.c index a071563a7e6e..e9767079a360 100644 --- a/net/ipv6/ip6_offload.c +++ b/net/ipv6/ip6_offload.c | |||
| @@ -78,6 +78,7 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, | |||
| 78 | SKB_GSO_SIT | | 78 | SKB_GSO_SIT | |
| 79 | SKB_GSO_UDP_TUNNEL | | 79 | SKB_GSO_UDP_TUNNEL | |
| 80 | SKB_GSO_UDP_TUNNEL_CSUM | | 80 | SKB_GSO_UDP_TUNNEL_CSUM | |
| 81 | SKB_GSO_TUNNEL_REMCSUM | | ||
| 81 | SKB_GSO_MPLS | | 82 | SKB_GSO_MPLS | |
| 82 | SKB_GSO_TCPV6 | | 83 | SKB_GSO_TCPV6 | |
| 83 | 0))) | 84 | 0))) |
diff --git a/net/ipv6/udp_offload.c b/net/ipv6/udp_offload.c index 6b8f543f6ac6..637ba2e438b7 100644 --- a/net/ipv6/udp_offload.c +++ b/net/ipv6/udp_offload.c | |||
| @@ -42,6 +42,7 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb, | |||
| 42 | SKB_GSO_DODGY | | 42 | SKB_GSO_DODGY | |
| 43 | SKB_GSO_UDP_TUNNEL | | 43 | SKB_GSO_UDP_TUNNEL | |
| 44 | SKB_GSO_UDP_TUNNEL_CSUM | | 44 | SKB_GSO_UDP_TUNNEL_CSUM | |
| 45 | SKB_GSO_TUNNEL_REMCSUM | | ||
| 45 | SKB_GSO_GRE | | 46 | SKB_GSO_GRE | |
| 46 | SKB_GSO_GRE_CSUM | | 47 | SKB_GSO_GRE_CSUM | |
| 47 | SKB_GSO_IPIP | | 48 | SKB_GSO_IPIP | |
