162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-or-later */
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#ifndef _NET_IPV6_GRO_H
462306a36Sopenharmony_ci#define _NET_IPV6_GRO_H
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/indirect_call_wrapper.h>
762306a36Sopenharmony_ci#include <linux/ip.h>
862306a36Sopenharmony_ci#include <linux/ipv6.h>
962306a36Sopenharmony_ci#include <net/ip6_checksum.h>
1062306a36Sopenharmony_ci#include <linux/skbuff.h>
1162306a36Sopenharmony_ci#include <net/udp.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_cistruct napi_gro_cb {
1462306a36Sopenharmony_ci	union {
1562306a36Sopenharmony_ci		struct {
1662306a36Sopenharmony_ci			/* Virtual address of skb_shinfo(skb)->frags[0].page + offset. */
1762306a36Sopenharmony_ci			void	*frag0;
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci			/* Length of frag0. */
2062306a36Sopenharmony_ci			unsigned int frag0_len;
2162306a36Sopenharmony_ci		};
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci		struct {
2462306a36Sopenharmony_ci			/* used in skb_gro_receive() slow path */
2562306a36Sopenharmony_ci			struct sk_buff *last;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci			/* jiffies when first packet was created/queued */
2862306a36Sopenharmony_ci			unsigned long age;
2962306a36Sopenharmony_ci		};
3062306a36Sopenharmony_ci	};
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	/* This indicates where we are processing relative to skb->data. */
3362306a36Sopenharmony_ci	int	data_offset;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	/* This is non-zero if the packet cannot be merged with the new skb. */
3662306a36Sopenharmony_ci	u16	flush;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	/* Save the IP ID here and check when we get to the transport layer */
3962306a36Sopenharmony_ci	u16	flush_id;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	/* Number of segments aggregated. */
4262306a36Sopenharmony_ci	u16	count;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	/* Used in ipv6_gro_receive() and foo-over-udp */
4562306a36Sopenharmony_ci	u16	proto;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci/* Used in napi_gro_cb::free */
4862306a36Sopenharmony_ci#define NAPI_GRO_FREE             1
4962306a36Sopenharmony_ci#define NAPI_GRO_FREE_STOLEN_HEAD 2
5062306a36Sopenharmony_ci	/* portion of the cb set to zero at every gro iteration */
5162306a36Sopenharmony_ci	struct_group(zeroed,
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci		/* Start offset for remote checksum offload */
5462306a36Sopenharmony_ci		u16	gro_remcsum_start;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci		/* This is non-zero if the packet may be of the same flow. */
5762306a36Sopenharmony_ci		u8	same_flow:1;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci		/* Used in tunnel GRO receive */
6062306a36Sopenharmony_ci		u8	encap_mark:1;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci		/* GRO checksum is valid */
6362306a36Sopenharmony_ci		u8	csum_valid:1;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci		/* Number of checksums via CHECKSUM_UNNECESSARY */
6662306a36Sopenharmony_ci		u8	csum_cnt:3;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci		/* Free the skb? */
6962306a36Sopenharmony_ci		u8	free:2;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci		/* Used in foo-over-udp, set in udp[46]_gro_receive */
7262306a36Sopenharmony_ci		u8	is_ipv6:1;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci		/* Used in GRE, set in fou/gue_gro_receive */
7562306a36Sopenharmony_ci		u8	is_fou:1;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci		/* Used to determine if flush_id can be ignored */
7862306a36Sopenharmony_ci		u8	is_atomic:1;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci		/* Number of gro_receive callbacks this packet already went through */
8162306a36Sopenharmony_ci		u8 recursion_counter:4;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci		/* GRO is done by frag_list pointer chaining. */
8462306a36Sopenharmony_ci		u8	is_flist:1;
8562306a36Sopenharmony_ci	);
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	/* used to support CHECKSUM_COMPLETE for tunneling protocols */
8862306a36Sopenharmony_ci	__wsum	csum;
8962306a36Sopenharmony_ci};
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci#define NAPI_GRO_CB(skb) ((struct napi_gro_cb *)(skb)->cb)
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci#define GRO_RECURSION_LIMIT 15
9462306a36Sopenharmony_cistatic inline int gro_recursion_inc_test(struct sk_buff *skb)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	return ++NAPI_GRO_CB(skb)->recursion_counter == GRO_RECURSION_LIMIT;
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_citypedef struct sk_buff *(*gro_receive_t)(struct list_head *, struct sk_buff *);
10062306a36Sopenharmony_cistatic inline struct sk_buff *call_gro_receive(gro_receive_t cb,
10162306a36Sopenharmony_ci					       struct list_head *head,
10262306a36Sopenharmony_ci					       struct sk_buff *skb)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	if (unlikely(gro_recursion_inc_test(skb))) {
10562306a36Sopenharmony_ci		NAPI_GRO_CB(skb)->flush |= 1;
10662306a36Sopenharmony_ci		return NULL;
10762306a36Sopenharmony_ci	}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	return cb(head, skb);
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_citypedef struct sk_buff *(*gro_receive_sk_t)(struct sock *, struct list_head *,
11362306a36Sopenharmony_ci					    struct sk_buff *);
11462306a36Sopenharmony_cistatic inline struct sk_buff *call_gro_receive_sk(gro_receive_sk_t cb,
11562306a36Sopenharmony_ci						  struct sock *sk,
11662306a36Sopenharmony_ci						  struct list_head *head,
11762306a36Sopenharmony_ci						  struct sk_buff *skb)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	if (unlikely(gro_recursion_inc_test(skb))) {
12062306a36Sopenharmony_ci		NAPI_GRO_CB(skb)->flush |= 1;
12162306a36Sopenharmony_ci		return NULL;
12262306a36Sopenharmony_ci	}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	return cb(sk, head, skb);
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistatic inline unsigned int skb_gro_offset(const struct sk_buff *skb)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	return NAPI_GRO_CB(skb)->data_offset;
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistatic inline unsigned int skb_gro_len(const struct sk_buff *skb)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	return skb->len - NAPI_GRO_CB(skb)->data_offset;
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic inline void skb_gro_pull(struct sk_buff *skb, unsigned int len)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	NAPI_GRO_CB(skb)->data_offset += len;
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistatic inline void *skb_gro_header_fast(struct sk_buff *skb,
14362306a36Sopenharmony_ci					unsigned int offset)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	return NAPI_GRO_CB(skb)->frag0 + offset;
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cistatic inline int skb_gro_header_hard(struct sk_buff *skb, unsigned int hlen)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	return NAPI_GRO_CB(skb)->frag0_len < hlen;
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic inline void skb_gro_frag0_invalidate(struct sk_buff *skb)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	NAPI_GRO_CB(skb)->frag0 = NULL;
15662306a36Sopenharmony_ci	NAPI_GRO_CB(skb)->frag0_len = 0;
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_cistatic inline void *skb_gro_header_slow(struct sk_buff *skb, unsigned int hlen,
16062306a36Sopenharmony_ci					unsigned int offset)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	if (!pskb_may_pull(skb, hlen))
16362306a36Sopenharmony_ci		return NULL;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	skb_gro_frag0_invalidate(skb);
16662306a36Sopenharmony_ci	return skb->data + offset;
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic inline void *skb_gro_header(struct sk_buff *skb,
17062306a36Sopenharmony_ci					unsigned int hlen, unsigned int offset)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	void *ptr;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	ptr = skb_gro_header_fast(skb, offset);
17562306a36Sopenharmony_ci	if (skb_gro_header_hard(skb, hlen))
17662306a36Sopenharmony_ci		ptr = skb_gro_header_slow(skb, hlen, offset);
17762306a36Sopenharmony_ci	return ptr;
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic inline void *skb_gro_network_header(struct sk_buff *skb)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	return (NAPI_GRO_CB(skb)->frag0 ?: skb->data) +
18362306a36Sopenharmony_ci	       skb_network_offset(skb);
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_cistatic inline __wsum inet_gro_compute_pseudo(struct sk_buff *skb, int proto)
18762306a36Sopenharmony_ci{
18862306a36Sopenharmony_ci	const struct iphdr *iph = skb_gro_network_header(skb);
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	return csum_tcpudp_nofold(iph->saddr, iph->daddr,
19162306a36Sopenharmony_ci				  skb_gro_len(skb), proto, 0);
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_cistatic inline void skb_gro_postpull_rcsum(struct sk_buff *skb,
19562306a36Sopenharmony_ci					const void *start, unsigned int len)
19662306a36Sopenharmony_ci{
19762306a36Sopenharmony_ci	if (NAPI_GRO_CB(skb)->csum_valid)
19862306a36Sopenharmony_ci		NAPI_GRO_CB(skb)->csum = wsum_negate(csum_partial(start, len,
19962306a36Sopenharmony_ci						wsum_negate(NAPI_GRO_CB(skb)->csum)));
20062306a36Sopenharmony_ci}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci/* GRO checksum functions. These are logical equivalents of the normal
20362306a36Sopenharmony_ci * checksum functions (in skbuff.h) except that they operate on the GRO
20462306a36Sopenharmony_ci * offsets and fields in sk_buff.
20562306a36Sopenharmony_ci */
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci__sum16 __skb_gro_checksum_complete(struct sk_buff *skb);
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_cistatic inline bool skb_at_gro_remcsum_start(struct sk_buff *skb)
21062306a36Sopenharmony_ci{
21162306a36Sopenharmony_ci	return (NAPI_GRO_CB(skb)->gro_remcsum_start == skb_gro_offset(skb));
21262306a36Sopenharmony_ci}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_cistatic inline bool __skb_gro_checksum_validate_needed(struct sk_buff *skb,
21562306a36Sopenharmony_ci						      bool zero_okay,
21662306a36Sopenharmony_ci						      __sum16 check)
21762306a36Sopenharmony_ci{
21862306a36Sopenharmony_ci	return ((skb->ip_summed != CHECKSUM_PARTIAL ||
21962306a36Sopenharmony_ci		skb_checksum_start_offset(skb) <
22062306a36Sopenharmony_ci		 skb_gro_offset(skb)) &&
22162306a36Sopenharmony_ci		!skb_at_gro_remcsum_start(skb) &&
22262306a36Sopenharmony_ci		NAPI_GRO_CB(skb)->csum_cnt == 0 &&
22362306a36Sopenharmony_ci		(!zero_okay || check));
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistatic inline __sum16 __skb_gro_checksum_validate_complete(struct sk_buff *skb,
22762306a36Sopenharmony_ci							   __wsum psum)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	if (NAPI_GRO_CB(skb)->csum_valid &&
23062306a36Sopenharmony_ci	    !csum_fold(csum_add(psum, NAPI_GRO_CB(skb)->csum)))
23162306a36Sopenharmony_ci		return 0;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	NAPI_GRO_CB(skb)->csum = psum;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	return __skb_gro_checksum_complete(skb);
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_cistatic inline void skb_gro_incr_csum_unnecessary(struct sk_buff *skb)
23962306a36Sopenharmony_ci{
24062306a36Sopenharmony_ci	if (NAPI_GRO_CB(skb)->csum_cnt > 0) {
24162306a36Sopenharmony_ci		/* Consume a checksum from CHECKSUM_UNNECESSARY */
24262306a36Sopenharmony_ci		NAPI_GRO_CB(skb)->csum_cnt--;
24362306a36Sopenharmony_ci	} else {
24462306a36Sopenharmony_ci		/* Update skb for CHECKSUM_UNNECESSARY and csum_level when we
24562306a36Sopenharmony_ci		 * verified a new top level checksum or an encapsulated one
24662306a36Sopenharmony_ci		 * during GRO. This saves work if we fallback to normal path.
24762306a36Sopenharmony_ci		 */
24862306a36Sopenharmony_ci		__skb_incr_checksum_unnecessary(skb);
24962306a36Sopenharmony_ci	}
25062306a36Sopenharmony_ci}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci#define __skb_gro_checksum_validate(skb, proto, zero_okay, check,	\
25362306a36Sopenharmony_ci				    compute_pseudo)			\
25462306a36Sopenharmony_ci({									\
25562306a36Sopenharmony_ci	__sum16 __ret = 0;						\
25662306a36Sopenharmony_ci	if (__skb_gro_checksum_validate_needed(skb, zero_okay, check))	\
25762306a36Sopenharmony_ci		__ret = __skb_gro_checksum_validate_complete(skb,	\
25862306a36Sopenharmony_ci				compute_pseudo(skb, proto));		\
25962306a36Sopenharmony_ci	if (!__ret)							\
26062306a36Sopenharmony_ci		skb_gro_incr_csum_unnecessary(skb);			\
26162306a36Sopenharmony_ci	__ret;								\
26262306a36Sopenharmony_ci})
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci#define skb_gro_checksum_validate(skb, proto, compute_pseudo)		\
26562306a36Sopenharmony_ci	__skb_gro_checksum_validate(skb, proto, false, 0, compute_pseudo)
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci#define skb_gro_checksum_validate_zero_check(skb, proto, check,		\
26862306a36Sopenharmony_ci					     compute_pseudo)		\
26962306a36Sopenharmony_ci	__skb_gro_checksum_validate(skb, proto, true, check, compute_pseudo)
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci#define skb_gro_checksum_simple_validate(skb)				\
27262306a36Sopenharmony_ci	__skb_gro_checksum_validate(skb, 0, false, 0, null_compute_pseudo)
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_cistatic inline bool __skb_gro_checksum_convert_check(struct sk_buff *skb)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	return (NAPI_GRO_CB(skb)->csum_cnt == 0 &&
27762306a36Sopenharmony_ci		!NAPI_GRO_CB(skb)->csum_valid);
27862306a36Sopenharmony_ci}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_cistatic inline void __skb_gro_checksum_convert(struct sk_buff *skb,
28162306a36Sopenharmony_ci					      __wsum pseudo)
28262306a36Sopenharmony_ci{
28362306a36Sopenharmony_ci	NAPI_GRO_CB(skb)->csum = ~pseudo;
28462306a36Sopenharmony_ci	NAPI_GRO_CB(skb)->csum_valid = 1;
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci#define skb_gro_checksum_try_convert(skb, proto, compute_pseudo)	\
28862306a36Sopenharmony_cido {									\
28962306a36Sopenharmony_ci	if (__skb_gro_checksum_convert_check(skb))			\
29062306a36Sopenharmony_ci		__skb_gro_checksum_convert(skb, 			\
29162306a36Sopenharmony_ci					   compute_pseudo(skb, proto));	\
29262306a36Sopenharmony_ci} while (0)
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_cistruct gro_remcsum {
29562306a36Sopenharmony_ci	int offset;
29662306a36Sopenharmony_ci	__wsum delta;
29762306a36Sopenharmony_ci};
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_cistatic inline void skb_gro_remcsum_init(struct gro_remcsum *grc)
30062306a36Sopenharmony_ci{
30162306a36Sopenharmony_ci	grc->offset = 0;
30262306a36Sopenharmony_ci	grc->delta = 0;
30362306a36Sopenharmony_ci}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_cistatic inline void *skb_gro_remcsum_process(struct sk_buff *skb, void *ptr,
30662306a36Sopenharmony_ci					    unsigned int off, size_t hdrlen,
30762306a36Sopenharmony_ci					    int start, int offset,
30862306a36Sopenharmony_ci					    struct gro_remcsum *grc,
30962306a36Sopenharmony_ci					    bool nopartial)
31062306a36Sopenharmony_ci{
31162306a36Sopenharmony_ci	__wsum delta;
31262306a36Sopenharmony_ci	size_t plen = hdrlen + max_t(size_t, offset + sizeof(u16), start);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	BUG_ON(!NAPI_GRO_CB(skb)->csum_valid);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	if (!nopartial) {
31762306a36Sopenharmony_ci		NAPI_GRO_CB(skb)->gro_remcsum_start = off + hdrlen + start;
31862306a36Sopenharmony_ci		return ptr;
31962306a36Sopenharmony_ci	}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	ptr = skb_gro_header(skb, off + plen, off);
32262306a36Sopenharmony_ci	if (!ptr)
32362306a36Sopenharmony_ci		return NULL;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	delta = remcsum_adjust(ptr + hdrlen, NAPI_GRO_CB(skb)->csum,
32662306a36Sopenharmony_ci			       start, offset);
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	/* Adjust skb->csum since we changed the packet */
32962306a36Sopenharmony_ci	NAPI_GRO_CB(skb)->csum = csum_add(NAPI_GRO_CB(skb)->csum, delta);
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	grc->offset = off + hdrlen + offset;
33262306a36Sopenharmony_ci	grc->delta = delta;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	return ptr;
33562306a36Sopenharmony_ci}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_cistatic inline void skb_gro_remcsum_cleanup(struct sk_buff *skb,
33862306a36Sopenharmony_ci					   struct gro_remcsum *grc)
33962306a36Sopenharmony_ci{
34062306a36Sopenharmony_ci	void *ptr;
34162306a36Sopenharmony_ci	size_t plen = grc->offset + sizeof(u16);
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	if (!grc->delta)
34462306a36Sopenharmony_ci		return;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	ptr = skb_gro_header(skb, plen, grc->offset);
34762306a36Sopenharmony_ci	if (!ptr)
34862306a36Sopenharmony_ci		return;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	remcsum_unadjust((__sum16 *)ptr, grc->delta);
35162306a36Sopenharmony_ci}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci#ifdef CONFIG_XFRM_OFFLOAD
35462306a36Sopenharmony_cistatic inline void skb_gro_flush_final(struct sk_buff *skb, struct sk_buff *pp, int flush)
35562306a36Sopenharmony_ci{
35662306a36Sopenharmony_ci	if (PTR_ERR(pp) != -EINPROGRESS)
35762306a36Sopenharmony_ci		NAPI_GRO_CB(skb)->flush |= flush;
35862306a36Sopenharmony_ci}
35962306a36Sopenharmony_cistatic inline void skb_gro_flush_final_remcsum(struct sk_buff *skb,
36062306a36Sopenharmony_ci					       struct sk_buff *pp,
36162306a36Sopenharmony_ci					       int flush,
36262306a36Sopenharmony_ci					       struct gro_remcsum *grc)
36362306a36Sopenharmony_ci{
36462306a36Sopenharmony_ci	if (PTR_ERR(pp) != -EINPROGRESS) {
36562306a36Sopenharmony_ci		NAPI_GRO_CB(skb)->flush |= flush;
36662306a36Sopenharmony_ci		skb_gro_remcsum_cleanup(skb, grc);
36762306a36Sopenharmony_ci		skb->remcsum_offload = 0;
36862306a36Sopenharmony_ci	}
36962306a36Sopenharmony_ci}
37062306a36Sopenharmony_ci#else
37162306a36Sopenharmony_cistatic inline void skb_gro_flush_final(struct sk_buff *skb, struct sk_buff *pp, int flush)
37262306a36Sopenharmony_ci{
37362306a36Sopenharmony_ci	NAPI_GRO_CB(skb)->flush |= flush;
37462306a36Sopenharmony_ci}
37562306a36Sopenharmony_cistatic inline void skb_gro_flush_final_remcsum(struct sk_buff *skb,
37662306a36Sopenharmony_ci					       struct sk_buff *pp,
37762306a36Sopenharmony_ci					       int flush,
37862306a36Sopenharmony_ci					       struct gro_remcsum *grc)
37962306a36Sopenharmony_ci{
38062306a36Sopenharmony_ci	NAPI_GRO_CB(skb)->flush |= flush;
38162306a36Sopenharmony_ci	skb_gro_remcsum_cleanup(skb, grc);
38262306a36Sopenharmony_ci	skb->remcsum_offload = 0;
38362306a36Sopenharmony_ci}
38462306a36Sopenharmony_ci#endif
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ciINDIRECT_CALLABLE_DECLARE(struct sk_buff *ipv6_gro_receive(struct list_head *,
38762306a36Sopenharmony_ci							   struct sk_buff *));
38862306a36Sopenharmony_ciINDIRECT_CALLABLE_DECLARE(int ipv6_gro_complete(struct sk_buff *, int));
38962306a36Sopenharmony_ciINDIRECT_CALLABLE_DECLARE(struct sk_buff *inet_gro_receive(struct list_head *,
39062306a36Sopenharmony_ci							   struct sk_buff *));
39162306a36Sopenharmony_ciINDIRECT_CALLABLE_DECLARE(int inet_gro_complete(struct sk_buff *, int));
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ciINDIRECT_CALLABLE_DECLARE(struct sk_buff *udp4_gro_receive(struct list_head *,
39462306a36Sopenharmony_ci							   struct sk_buff *));
39562306a36Sopenharmony_ciINDIRECT_CALLABLE_DECLARE(int udp4_gro_complete(struct sk_buff *, int));
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ciINDIRECT_CALLABLE_DECLARE(struct sk_buff *udp6_gro_receive(struct list_head *,
39862306a36Sopenharmony_ci							   struct sk_buff *));
39962306a36Sopenharmony_ciINDIRECT_CALLABLE_DECLARE(int udp6_gro_complete(struct sk_buff *, int));
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci#define indirect_call_gro_receive_inet(cb, f2, f1, head, skb)	\
40262306a36Sopenharmony_ci({								\
40362306a36Sopenharmony_ci	unlikely(gro_recursion_inc_test(skb)) ?			\
40462306a36Sopenharmony_ci		NAPI_GRO_CB(skb)->flush |= 1, NULL :		\
40562306a36Sopenharmony_ci		INDIRECT_CALL_INET(cb, f2, f1, head, skb);	\
40662306a36Sopenharmony_ci})
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_cistruct sk_buff *udp_gro_receive(struct list_head *head, struct sk_buff *skb,
40962306a36Sopenharmony_ci				struct udphdr *uh, struct sock *sk);
41062306a36Sopenharmony_ciint udp_gro_complete(struct sk_buff *skb, int nhoff, udp_lookup_t lookup);
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_cistatic inline struct udphdr *udp_gro_udphdr(struct sk_buff *skb)
41362306a36Sopenharmony_ci{
41462306a36Sopenharmony_ci	struct udphdr *uh;
41562306a36Sopenharmony_ci	unsigned int hlen, off;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	off  = skb_gro_offset(skb);
41862306a36Sopenharmony_ci	hlen = off + sizeof(*uh);
41962306a36Sopenharmony_ci	uh   = skb_gro_header(skb, hlen, off);
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	return uh;
42262306a36Sopenharmony_ci}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_cistatic inline __wsum ip6_gro_compute_pseudo(struct sk_buff *skb, int proto)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci	const struct ipv6hdr *iph = skb_gro_network_header(skb);
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	return ~csum_unfold(csum_ipv6_magic(&iph->saddr, &iph->daddr,
42962306a36Sopenharmony_ci					    skb_gro_len(skb), proto, 0));
43062306a36Sopenharmony_ci}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ciint skb_gro_receive(struct sk_buff *p, struct sk_buff *skb);
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci/* Pass the currently batched GRO_NORMAL SKBs up to the stack. */
43562306a36Sopenharmony_cistatic inline void gro_normal_list(struct napi_struct *napi)
43662306a36Sopenharmony_ci{
43762306a36Sopenharmony_ci	if (!napi->rx_count)
43862306a36Sopenharmony_ci		return;
43962306a36Sopenharmony_ci	netif_receive_skb_list_internal(&napi->rx_list);
44062306a36Sopenharmony_ci	INIT_LIST_HEAD(&napi->rx_list);
44162306a36Sopenharmony_ci	napi->rx_count = 0;
44262306a36Sopenharmony_ci}
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci/* Queue one GRO_NORMAL SKB up for list processing. If batch size exceeded,
44562306a36Sopenharmony_ci * pass the whole batch up to the stack.
44662306a36Sopenharmony_ci */
44762306a36Sopenharmony_cistatic inline void gro_normal_one(struct napi_struct *napi, struct sk_buff *skb, int segs)
44862306a36Sopenharmony_ci{
44962306a36Sopenharmony_ci	list_add_tail(&skb->list, &napi->rx_list);
45062306a36Sopenharmony_ci	napi->rx_count += segs;
45162306a36Sopenharmony_ci	if (napi->rx_count >= READ_ONCE(gro_normal_batch))
45262306a36Sopenharmony_ci		gro_normal_list(napi);
45362306a36Sopenharmony_ci}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci/* This function is the alternative of 'inet_iif' and 'inet_sdif'
45662306a36Sopenharmony_ci * functions in case we can not rely on fields of IPCB.
45762306a36Sopenharmony_ci *
45862306a36Sopenharmony_ci * The caller must verify skb_valid_dst(skb) is false and skb->dev is initialized.
45962306a36Sopenharmony_ci * The caller must hold the RCU read lock.
46062306a36Sopenharmony_ci */
46162306a36Sopenharmony_cistatic inline void inet_get_iif_sdif(const struct sk_buff *skb, int *iif, int *sdif)
46262306a36Sopenharmony_ci{
46362306a36Sopenharmony_ci	*iif = inet_iif(skb) ?: skb->dev->ifindex;
46462306a36Sopenharmony_ci	*sdif = 0;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_NET_L3_MASTER_DEV)
46762306a36Sopenharmony_ci	if (netif_is_l3_slave(skb->dev)) {
46862306a36Sopenharmony_ci		struct net_device *master = netdev_master_upper_dev_get_rcu(skb->dev);
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci		*sdif = *iif;
47162306a36Sopenharmony_ci		*iif = master ? master->ifindex : 0;
47262306a36Sopenharmony_ci	}
47362306a36Sopenharmony_ci#endif
47462306a36Sopenharmony_ci}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci/* This function is the alternative of 'inet6_iif' and 'inet6_sdif'
47762306a36Sopenharmony_ci * functions in case we can not rely on fields of IP6CB.
47862306a36Sopenharmony_ci *
47962306a36Sopenharmony_ci * The caller must verify skb_valid_dst(skb) is false and skb->dev is initialized.
48062306a36Sopenharmony_ci * The caller must hold the RCU read lock.
48162306a36Sopenharmony_ci */
48262306a36Sopenharmony_cistatic inline void inet6_get_iif_sdif(const struct sk_buff *skb, int *iif, int *sdif)
48362306a36Sopenharmony_ci{
48462306a36Sopenharmony_ci	/* using skb->dev->ifindex because skb_dst(skb) is not initialized */
48562306a36Sopenharmony_ci	*iif = skb->dev->ifindex;
48662306a36Sopenharmony_ci	*sdif = 0;
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_NET_L3_MASTER_DEV)
48962306a36Sopenharmony_ci	if (netif_is_l3_slave(skb->dev)) {
49062306a36Sopenharmony_ci		struct net_device *master = netdev_master_upper_dev_get_rcu(skb->dev);
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci		*sdif = *iif;
49362306a36Sopenharmony_ci		*iif = master ? master->ifindex : 0;
49462306a36Sopenharmony_ci	}
49562306a36Sopenharmony_ci#endif
49662306a36Sopenharmony_ci}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ciextern struct list_head offload_base;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci#endif /* _NET_IPV6_GRO_H */
501