@@ -29,7 +29,7 @@ static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb,
29
29
netdev_features_t features ,
30
30
struct sk_buff * (* gso_inner_segment )(struct sk_buff * skb ,
31
31
netdev_features_t features ),
32
- __be16 new_protocol )
32
+ __be16 new_protocol , bool is_ipv6 )
33
33
{
34
34
struct sk_buff * segs = ERR_PTR (- EINVAL );
35
35
u16 mac_offset = skb -> mac_header ;
@@ -39,7 +39,9 @@ static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb,
39
39
netdev_features_t enc_features ;
40
40
int udp_offset , outer_hlen ;
41
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 offload_csum = false, dont_encap = need_csum ;
43
45
44
46
oldlen = (u16 )~skb -> len ;
45
47
@@ -52,10 +54,12 @@ static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb,
52
54
skb_set_network_header (skb , skb_inner_network_offset (skb ));
53
55
skb -> mac_len = skb_inner_network_offset (skb );
54
56
skb -> protocol = new_protocol ;
57
+ skb -> encap_hdr_csum = need_csum ;
55
58
56
- need_csum = !!(skb_shinfo (skb )-> gso_type & SKB_GSO_UDP_TUNNEL_CSUM );
57
- if (need_csum )
58
- skb -> encap_hdr_csum = 1 ;
59
+ /* Try to offload checksum if possible */
60
+ offload_csum = !!(need_csum &&
61
+ (skb -> dev -> features &
62
+ (is_ipv6 ? NETIF_F_V6_CSUM : NETIF_F_V4_CSUM )));
59
63
60
64
/* segment inner packet. */
61
65
enc_features = skb -> dev -> hw_enc_features & features ;
@@ -72,11 +76,21 @@ static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb,
72
76
do {
73
77
struct udphdr * uh ;
74
78
int len ;
75
-
76
- skb_reset_inner_headers (skb );
77
- skb -> encapsulation = 1 ;
79
+ __be32 delta ;
80
+
81
+ if (dont_encap ) {
82
+ skb -> encapsulation = 0 ;
83
+ skb -> ip_summed = CHECKSUM_NONE ;
84
+ } else {
85
+ /* Only set up inner headers if we might be offloading
86
+ * inner checksum.
87
+ */
88
+ skb_reset_inner_headers (skb );
89
+ skb -> encapsulation = 1 ;
90
+ }
78
91
79
92
skb -> mac_len = mac_len ;
93
+ skb -> protocol = protocol ;
80
94
81
95
skb_push (skb , outer_hlen );
82
96
skb_reset_mac_header (skb );
@@ -86,19 +100,25 @@ static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb,
86
100
uh = udp_hdr (skb );
87
101
uh -> len = htons (len );
88
102
89
- if (need_csum ) {
90
- __be32 delta = htonl (oldlen + len );
103
+ if (!need_csum )
104
+ continue ;
105
+
106
+ delta = htonl (oldlen + len );
107
+
108
+ uh -> check = ~csum_fold ((__force __wsum )
109
+ ((__force u32 )uh -> check +
110
+ (__force u32 )delta ));
91
111
92
- uh -> check = ~csum_fold ((__force __wsum )
93
- ((__force u32 )uh -> check +
94
- (__force u32 )delta ));
112
+ if (offload_csum ) {
113
+ skb -> ip_summed = CHECKSUM_PARTIAL ;
114
+ skb -> csum_start = skb_transport_header (skb ) - skb -> head ;
115
+ skb -> csum_offset = offsetof(struct udphdr , check );
116
+ } else {
95
117
uh -> check = gso_make_checksum (skb , ~uh -> check );
96
118
97
119
if (uh -> check == 0 )
98
120
uh -> check = CSUM_MANGLED_0 ;
99
121
}
100
-
101
- skb -> protocol = protocol ;
102
122
} while ((skb = skb -> next ));
103
123
out :
104
124
return segs ;
@@ -134,7 +154,7 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
134
154
}
135
155
136
156
segs = __skb_udp_tunnel_segment (skb , features , gso_inner_segment ,
137
- protocol );
157
+ protocol , is_ipv6 );
138
158
139
159
out_unlock :
140
160
rcu_read_unlock ();
0 commit comments