1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * IPV4 GSO/GRO offload support
4*4882a593Smuzhiyun * Linux INET implementation
5*4882a593Smuzhiyun *
6*4882a593Smuzhiyun * GRE GSO support
7*4882a593Smuzhiyun */
8*4882a593Smuzhiyun
9*4882a593Smuzhiyun #include <linux/skbuff.h>
10*4882a593Smuzhiyun #include <linux/init.h>
11*4882a593Smuzhiyun #include <net/protocol.h>
12*4882a593Smuzhiyun #include <net/gre.h>
13*4882a593Smuzhiyun
gre_gso_segment(struct sk_buff * skb,netdev_features_t features)14*4882a593Smuzhiyun static struct sk_buff *gre_gso_segment(struct sk_buff *skb,
15*4882a593Smuzhiyun netdev_features_t features)
16*4882a593Smuzhiyun {
17*4882a593Smuzhiyun int tnl_hlen = skb_inner_mac_header(skb) - skb_transport_header(skb);
18*4882a593Smuzhiyun bool need_csum, need_recompute_csum, gso_partial;
19*4882a593Smuzhiyun struct sk_buff *segs = ERR_PTR(-EINVAL);
20*4882a593Smuzhiyun u16 mac_offset = skb->mac_header;
21*4882a593Smuzhiyun __be16 protocol = skb->protocol;
22*4882a593Smuzhiyun u16 mac_len = skb->mac_len;
23*4882a593Smuzhiyun int gre_offset, outer_hlen;
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun if (!skb->encapsulation)
26*4882a593Smuzhiyun goto out;
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun if (unlikely(tnl_hlen < sizeof(struct gre_base_hdr)))
29*4882a593Smuzhiyun goto out;
30*4882a593Smuzhiyun
31*4882a593Smuzhiyun if (unlikely(!pskb_may_pull(skb, tnl_hlen)))
32*4882a593Smuzhiyun goto out;
33*4882a593Smuzhiyun
34*4882a593Smuzhiyun /* setup inner skb. */
35*4882a593Smuzhiyun skb->encapsulation = 0;
36*4882a593Smuzhiyun SKB_GSO_CB(skb)->encap_level = 0;
37*4882a593Smuzhiyun __skb_pull(skb, tnl_hlen);
38*4882a593Smuzhiyun skb_reset_mac_header(skb);
39*4882a593Smuzhiyun skb_set_network_header(skb, skb_inner_network_offset(skb));
40*4882a593Smuzhiyun skb->mac_len = skb_inner_network_offset(skb);
41*4882a593Smuzhiyun skb->protocol = skb->inner_protocol;
42*4882a593Smuzhiyun
43*4882a593Smuzhiyun need_csum = !!(skb_shinfo(skb)->gso_type & SKB_GSO_GRE_CSUM);
44*4882a593Smuzhiyun need_recompute_csum = skb->csum_not_inet;
45*4882a593Smuzhiyun skb->encap_hdr_csum = need_csum;
46*4882a593Smuzhiyun
47*4882a593Smuzhiyun features &= skb->dev->hw_enc_features;
48*4882a593Smuzhiyun
49*4882a593Smuzhiyun /* segment inner packet. */
50*4882a593Smuzhiyun segs = skb_mac_gso_segment(skb, features);
51*4882a593Smuzhiyun if (IS_ERR_OR_NULL(segs)) {
52*4882a593Smuzhiyun skb_gso_error_unwind(skb, protocol, tnl_hlen, mac_offset,
53*4882a593Smuzhiyun mac_len);
54*4882a593Smuzhiyun goto out;
55*4882a593Smuzhiyun }
56*4882a593Smuzhiyun
57*4882a593Smuzhiyun gso_partial = !!(skb_shinfo(segs)->gso_type & SKB_GSO_PARTIAL);
58*4882a593Smuzhiyun
59*4882a593Smuzhiyun outer_hlen = skb_tnl_header_len(skb);
60*4882a593Smuzhiyun gre_offset = outer_hlen - tnl_hlen;
61*4882a593Smuzhiyun skb = segs;
62*4882a593Smuzhiyun do {
63*4882a593Smuzhiyun struct gre_base_hdr *greh;
64*4882a593Smuzhiyun __sum16 *pcsum;
65*4882a593Smuzhiyun
66*4882a593Smuzhiyun /* Set up inner headers if we are offloading inner checksum */
67*4882a593Smuzhiyun if (skb->ip_summed == CHECKSUM_PARTIAL) {
68*4882a593Smuzhiyun skb_reset_inner_headers(skb);
69*4882a593Smuzhiyun skb->encapsulation = 1;
70*4882a593Smuzhiyun }
71*4882a593Smuzhiyun
72*4882a593Smuzhiyun skb->mac_len = mac_len;
73*4882a593Smuzhiyun skb->protocol = protocol;
74*4882a593Smuzhiyun
75*4882a593Smuzhiyun __skb_push(skb, outer_hlen);
76*4882a593Smuzhiyun skb_reset_mac_header(skb);
77*4882a593Smuzhiyun skb_set_network_header(skb, mac_len);
78*4882a593Smuzhiyun skb_set_transport_header(skb, gre_offset);
79*4882a593Smuzhiyun
80*4882a593Smuzhiyun if (!need_csum)
81*4882a593Smuzhiyun continue;
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun greh = (struct gre_base_hdr *)skb_transport_header(skb);
84*4882a593Smuzhiyun pcsum = (__sum16 *)(greh + 1);
85*4882a593Smuzhiyun
86*4882a593Smuzhiyun if (gso_partial && skb_is_gso(skb)) {
87*4882a593Smuzhiyun unsigned int partial_adj;
88*4882a593Smuzhiyun
89*4882a593Smuzhiyun /* Adjust checksum to account for the fact that
90*4882a593Smuzhiyun * the partial checksum is based on actual size
91*4882a593Smuzhiyun * whereas headers should be based on MSS size.
92*4882a593Smuzhiyun */
93*4882a593Smuzhiyun partial_adj = skb->len + skb_headroom(skb) -
94*4882a593Smuzhiyun SKB_GSO_CB(skb)->data_offset -
95*4882a593Smuzhiyun skb_shinfo(skb)->gso_size;
96*4882a593Smuzhiyun *pcsum = ~csum_fold((__force __wsum)htonl(partial_adj));
97*4882a593Smuzhiyun } else {
98*4882a593Smuzhiyun *pcsum = 0;
99*4882a593Smuzhiyun }
100*4882a593Smuzhiyun
101*4882a593Smuzhiyun *(pcsum + 1) = 0;
102*4882a593Smuzhiyun if (need_recompute_csum && !skb_is_gso(skb)) {
103*4882a593Smuzhiyun __wsum csum;
104*4882a593Smuzhiyun
105*4882a593Smuzhiyun csum = skb_checksum(skb, gre_offset,
106*4882a593Smuzhiyun skb->len - gre_offset, 0);
107*4882a593Smuzhiyun *pcsum = csum_fold(csum);
108*4882a593Smuzhiyun } else {
109*4882a593Smuzhiyun *pcsum = gso_make_checksum(skb, 0);
110*4882a593Smuzhiyun }
111*4882a593Smuzhiyun } while ((skb = skb->next));
112*4882a593Smuzhiyun out:
113*4882a593Smuzhiyun return segs;
114*4882a593Smuzhiyun }
115*4882a593Smuzhiyun
gre_gro_receive(struct list_head * head,struct sk_buff * skb)116*4882a593Smuzhiyun static struct sk_buff *gre_gro_receive(struct list_head *head,
117*4882a593Smuzhiyun struct sk_buff *skb)
118*4882a593Smuzhiyun {
119*4882a593Smuzhiyun struct sk_buff *pp = NULL;
120*4882a593Smuzhiyun struct sk_buff *p;
121*4882a593Smuzhiyun const struct gre_base_hdr *greh;
122*4882a593Smuzhiyun unsigned int hlen, grehlen;
123*4882a593Smuzhiyun unsigned int off;
124*4882a593Smuzhiyun int flush = 1;
125*4882a593Smuzhiyun struct packet_offload *ptype;
126*4882a593Smuzhiyun __be16 type;
127*4882a593Smuzhiyun
128*4882a593Smuzhiyun if (NAPI_GRO_CB(skb)->encap_mark)
129*4882a593Smuzhiyun goto out;
130*4882a593Smuzhiyun
131*4882a593Smuzhiyun NAPI_GRO_CB(skb)->encap_mark = 1;
132*4882a593Smuzhiyun
133*4882a593Smuzhiyun off = skb_gro_offset(skb);
134*4882a593Smuzhiyun hlen = off + sizeof(*greh);
135*4882a593Smuzhiyun greh = skb_gro_header_fast(skb, off);
136*4882a593Smuzhiyun if (skb_gro_header_hard(skb, hlen)) {
137*4882a593Smuzhiyun greh = skb_gro_header_slow(skb, hlen, off);
138*4882a593Smuzhiyun if (unlikely(!greh))
139*4882a593Smuzhiyun goto out;
140*4882a593Smuzhiyun }
141*4882a593Smuzhiyun
142*4882a593Smuzhiyun /* Only support version 0 and K (key), C (csum) flags. Note that
143*4882a593Smuzhiyun * although the support for the S (seq#) flag can be added easily
144*4882a593Smuzhiyun * for GRO, this is problematic for GSO hence can not be enabled
145*4882a593Smuzhiyun * here because a GRO pkt may end up in the forwarding path, thus
146*4882a593Smuzhiyun * requiring GSO support to break it up correctly.
147*4882a593Smuzhiyun */
148*4882a593Smuzhiyun if ((greh->flags & ~(GRE_KEY|GRE_CSUM)) != 0)
149*4882a593Smuzhiyun goto out;
150*4882a593Smuzhiyun
151*4882a593Smuzhiyun /* We can only support GRE_CSUM if we can track the location of
152*4882a593Smuzhiyun * the GRE header. In the case of FOU/GUE we cannot because the
153*4882a593Smuzhiyun * outer UDP header displaces the GRE header leaving us in a state
154*4882a593Smuzhiyun * of limbo.
155*4882a593Smuzhiyun */
156*4882a593Smuzhiyun if ((greh->flags & GRE_CSUM) && NAPI_GRO_CB(skb)->is_fou)
157*4882a593Smuzhiyun goto out;
158*4882a593Smuzhiyun
159*4882a593Smuzhiyun type = greh->protocol;
160*4882a593Smuzhiyun
161*4882a593Smuzhiyun rcu_read_lock();
162*4882a593Smuzhiyun ptype = gro_find_receive_by_type(type);
163*4882a593Smuzhiyun if (!ptype)
164*4882a593Smuzhiyun goto out_unlock;
165*4882a593Smuzhiyun
166*4882a593Smuzhiyun grehlen = GRE_HEADER_SECTION;
167*4882a593Smuzhiyun
168*4882a593Smuzhiyun if (greh->flags & GRE_KEY)
169*4882a593Smuzhiyun grehlen += GRE_HEADER_SECTION;
170*4882a593Smuzhiyun
171*4882a593Smuzhiyun if (greh->flags & GRE_CSUM)
172*4882a593Smuzhiyun grehlen += GRE_HEADER_SECTION;
173*4882a593Smuzhiyun
174*4882a593Smuzhiyun hlen = off + grehlen;
175*4882a593Smuzhiyun if (skb_gro_header_hard(skb, hlen)) {
176*4882a593Smuzhiyun greh = skb_gro_header_slow(skb, hlen, off);
177*4882a593Smuzhiyun if (unlikely(!greh))
178*4882a593Smuzhiyun goto out_unlock;
179*4882a593Smuzhiyun }
180*4882a593Smuzhiyun
181*4882a593Smuzhiyun /* Don't bother verifying checksum if we're going to flush anyway. */
182*4882a593Smuzhiyun if ((greh->flags & GRE_CSUM) && !NAPI_GRO_CB(skb)->flush) {
183*4882a593Smuzhiyun if (skb_gro_checksum_simple_validate(skb))
184*4882a593Smuzhiyun goto out_unlock;
185*4882a593Smuzhiyun
186*4882a593Smuzhiyun skb_gro_checksum_try_convert(skb, IPPROTO_GRE,
187*4882a593Smuzhiyun null_compute_pseudo);
188*4882a593Smuzhiyun }
189*4882a593Smuzhiyun
190*4882a593Smuzhiyun list_for_each_entry(p, head, list) {
191*4882a593Smuzhiyun const struct gre_base_hdr *greh2;
192*4882a593Smuzhiyun
193*4882a593Smuzhiyun if (!NAPI_GRO_CB(p)->same_flow)
194*4882a593Smuzhiyun continue;
195*4882a593Smuzhiyun
196*4882a593Smuzhiyun /* The following checks are needed to ensure only pkts
197*4882a593Smuzhiyun * from the same tunnel are considered for aggregation.
198*4882a593Smuzhiyun * The criteria for "the same tunnel" includes:
199*4882a593Smuzhiyun * 1) same version (we only support version 0 here)
200*4882a593Smuzhiyun * 2) same protocol (we only support ETH_P_IP for now)
201*4882a593Smuzhiyun * 3) same set of flags
202*4882a593Smuzhiyun * 4) same key if the key field is present.
203*4882a593Smuzhiyun */
204*4882a593Smuzhiyun greh2 = (struct gre_base_hdr *)(p->data + off);
205*4882a593Smuzhiyun
206*4882a593Smuzhiyun if (greh2->flags != greh->flags ||
207*4882a593Smuzhiyun greh2->protocol != greh->protocol) {
208*4882a593Smuzhiyun NAPI_GRO_CB(p)->same_flow = 0;
209*4882a593Smuzhiyun continue;
210*4882a593Smuzhiyun }
211*4882a593Smuzhiyun if (greh->flags & GRE_KEY) {
212*4882a593Smuzhiyun /* compare keys */
213*4882a593Smuzhiyun if (*(__be32 *)(greh2+1) != *(__be32 *)(greh+1)) {
214*4882a593Smuzhiyun NAPI_GRO_CB(p)->same_flow = 0;
215*4882a593Smuzhiyun continue;
216*4882a593Smuzhiyun }
217*4882a593Smuzhiyun }
218*4882a593Smuzhiyun }
219*4882a593Smuzhiyun
220*4882a593Smuzhiyun skb_gro_pull(skb, grehlen);
221*4882a593Smuzhiyun
222*4882a593Smuzhiyun /* Adjusted NAPI_GRO_CB(skb)->csum after skb_gro_pull()*/
223*4882a593Smuzhiyun skb_gro_postpull_rcsum(skb, greh, grehlen);
224*4882a593Smuzhiyun
225*4882a593Smuzhiyun pp = call_gro_receive(ptype->callbacks.gro_receive, head, skb);
226*4882a593Smuzhiyun flush = 0;
227*4882a593Smuzhiyun
228*4882a593Smuzhiyun out_unlock:
229*4882a593Smuzhiyun rcu_read_unlock();
230*4882a593Smuzhiyun out:
231*4882a593Smuzhiyun skb_gro_flush_final(skb, pp, flush);
232*4882a593Smuzhiyun
233*4882a593Smuzhiyun return pp;
234*4882a593Smuzhiyun }
235*4882a593Smuzhiyun
gre_gro_complete(struct sk_buff * skb,int nhoff)236*4882a593Smuzhiyun static int gre_gro_complete(struct sk_buff *skb, int nhoff)
237*4882a593Smuzhiyun {
238*4882a593Smuzhiyun struct gre_base_hdr *greh = (struct gre_base_hdr *)(skb->data + nhoff);
239*4882a593Smuzhiyun struct packet_offload *ptype;
240*4882a593Smuzhiyun unsigned int grehlen = sizeof(*greh);
241*4882a593Smuzhiyun int err = -ENOENT;
242*4882a593Smuzhiyun __be16 type;
243*4882a593Smuzhiyun
244*4882a593Smuzhiyun skb->encapsulation = 1;
245*4882a593Smuzhiyun skb_shinfo(skb)->gso_type = SKB_GSO_GRE;
246*4882a593Smuzhiyun
247*4882a593Smuzhiyun type = greh->protocol;
248*4882a593Smuzhiyun if (greh->flags & GRE_KEY)
249*4882a593Smuzhiyun grehlen += GRE_HEADER_SECTION;
250*4882a593Smuzhiyun
251*4882a593Smuzhiyun if (greh->flags & GRE_CSUM)
252*4882a593Smuzhiyun grehlen += GRE_HEADER_SECTION;
253*4882a593Smuzhiyun
254*4882a593Smuzhiyun rcu_read_lock();
255*4882a593Smuzhiyun ptype = gro_find_complete_by_type(type);
256*4882a593Smuzhiyun if (ptype)
257*4882a593Smuzhiyun err = ptype->callbacks.gro_complete(skb, nhoff + grehlen);
258*4882a593Smuzhiyun
259*4882a593Smuzhiyun rcu_read_unlock();
260*4882a593Smuzhiyun
261*4882a593Smuzhiyun skb_set_inner_mac_header(skb, nhoff + grehlen);
262*4882a593Smuzhiyun
263*4882a593Smuzhiyun return err;
264*4882a593Smuzhiyun }
265*4882a593Smuzhiyun
266*4882a593Smuzhiyun static const struct net_offload gre_offload = {
267*4882a593Smuzhiyun .callbacks = {
268*4882a593Smuzhiyun .gso_segment = gre_gso_segment,
269*4882a593Smuzhiyun .gro_receive = gre_gro_receive,
270*4882a593Smuzhiyun .gro_complete = gre_gro_complete,
271*4882a593Smuzhiyun },
272*4882a593Smuzhiyun };
273*4882a593Smuzhiyun
gre_offload_init(void)274*4882a593Smuzhiyun static int __init gre_offload_init(void)
275*4882a593Smuzhiyun {
276*4882a593Smuzhiyun int err;
277*4882a593Smuzhiyun
278*4882a593Smuzhiyun err = inet_add_offload(&gre_offload, IPPROTO_GRE);
279*4882a593Smuzhiyun #if IS_ENABLED(CONFIG_IPV6)
280*4882a593Smuzhiyun if (err)
281*4882a593Smuzhiyun return err;
282*4882a593Smuzhiyun
283*4882a593Smuzhiyun err = inet6_add_offload(&gre_offload, IPPROTO_GRE);
284*4882a593Smuzhiyun if (err)
285*4882a593Smuzhiyun inet_del_offload(&gre_offload, IPPROTO_GRE);
286*4882a593Smuzhiyun #endif
287*4882a593Smuzhiyun
288*4882a593Smuzhiyun return err;
289*4882a593Smuzhiyun }
290*4882a593Smuzhiyun device_initcall(gre_offload_init);
291