xref: /OK3568_Linux_fs/kernel/net/ipv4/gre_offload.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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