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 | |