162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-or-later */
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#ifndef _NET_GSO_H
462306a36Sopenharmony_ci#define _NET_GSO_H
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/skbuff.h>
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci/* Keeps track of mac header offset relative to skb->head.
962306a36Sopenharmony_ci * It is useful for TSO of Tunneling protocol. e.g. GRE.
1062306a36Sopenharmony_ci * For non-tunnel skb it points to skb_mac_header() and for
1162306a36Sopenharmony_ci * tunnel skb it points to outer mac header.
1262306a36Sopenharmony_ci * Keeps track of level of encapsulation of network headers.
1362306a36Sopenharmony_ci */
1462306a36Sopenharmony_cistruct skb_gso_cb {
1562306a36Sopenharmony_ci	union {
1662306a36Sopenharmony_ci		int	mac_offset;
1762306a36Sopenharmony_ci		int	data_offset;
1862306a36Sopenharmony_ci	};
1962306a36Sopenharmony_ci	int	encap_level;
2062306a36Sopenharmony_ci	__wsum	csum;
2162306a36Sopenharmony_ci	__u16	csum_start;
2262306a36Sopenharmony_ci};
2362306a36Sopenharmony_ci#define SKB_GSO_CB_OFFSET	32
2462306a36Sopenharmony_ci#define SKB_GSO_CB(skb) ((struct skb_gso_cb *)((skb)->cb + SKB_GSO_CB_OFFSET))
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic inline int skb_tnl_header_len(const struct sk_buff *inner_skb)
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci	return (skb_mac_header(inner_skb) - inner_skb->head) -
2962306a36Sopenharmony_ci		SKB_GSO_CB(inner_skb)->mac_offset;
3062306a36Sopenharmony_ci}
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic inline int gso_pskb_expand_head(struct sk_buff *skb, int extra)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	int new_headroom, headroom;
3562306a36Sopenharmony_ci	int ret;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	headroom = skb_headroom(skb);
3862306a36Sopenharmony_ci	ret = pskb_expand_head(skb, extra, 0, GFP_ATOMIC);
3962306a36Sopenharmony_ci	if (ret)
4062306a36Sopenharmony_ci		return ret;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	new_headroom = skb_headroom(skb);
4362306a36Sopenharmony_ci	SKB_GSO_CB(skb)->mac_offset += (new_headroom - headroom);
4462306a36Sopenharmony_ci	return 0;
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic inline void gso_reset_checksum(struct sk_buff *skb, __wsum res)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	/* Do not update partial checksums if remote checksum is enabled. */
5062306a36Sopenharmony_ci	if (skb->remcsum_offload)
5162306a36Sopenharmony_ci		return;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	SKB_GSO_CB(skb)->csum = res;
5462306a36Sopenharmony_ci	SKB_GSO_CB(skb)->csum_start = skb_checksum_start(skb) - skb->head;
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci/* Compute the checksum for a gso segment. First compute the checksum value
5862306a36Sopenharmony_ci * from the start of transport header to SKB_GSO_CB(skb)->csum_start, and
5962306a36Sopenharmony_ci * then add in skb->csum (checksum from csum_start to end of packet).
6062306a36Sopenharmony_ci * skb->csum and csum_start are then updated to reflect the checksum of the
6162306a36Sopenharmony_ci * resultant packet starting from the transport header-- the resultant checksum
6262306a36Sopenharmony_ci * is in the res argument (i.e. normally zero or ~ of checksum of a pseudo
6362306a36Sopenharmony_ci * header.
6462306a36Sopenharmony_ci */
6562306a36Sopenharmony_cistatic inline __sum16 gso_make_checksum(struct sk_buff *skb, __wsum res)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	unsigned char *csum_start = skb_transport_header(skb);
6862306a36Sopenharmony_ci	int plen = (skb->head + SKB_GSO_CB(skb)->csum_start) - csum_start;
6962306a36Sopenharmony_ci	__wsum partial = SKB_GSO_CB(skb)->csum;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	SKB_GSO_CB(skb)->csum = res;
7262306a36Sopenharmony_ci	SKB_GSO_CB(skb)->csum_start = csum_start - skb->head;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	return csum_fold(csum_partial(csum_start, plen, partial));
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistruct sk_buff *__skb_gso_segment(struct sk_buff *skb,
7862306a36Sopenharmony_ci				  netdev_features_t features, bool tx_path);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistatic inline struct sk_buff *skb_gso_segment(struct sk_buff *skb,
8162306a36Sopenharmony_ci					      netdev_features_t features)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	return __skb_gso_segment(skb, features, true);
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistruct sk_buff *skb_eth_gso_segment(struct sk_buff *skb,
8762306a36Sopenharmony_ci				    netdev_features_t features, __be16 type);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistruct sk_buff *skb_mac_gso_segment(struct sk_buff *skb,
9062306a36Sopenharmony_ci				    netdev_features_t features);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cibool skb_gso_validate_network_len(const struct sk_buff *skb, unsigned int mtu);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cibool skb_gso_validate_mac_len(const struct sk_buff *skb, unsigned int len);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistatic inline void skb_gso_error_unwind(struct sk_buff *skb, __be16 protocol,
9762306a36Sopenharmony_ci					int pulled_hlen, u16 mac_offset,
9862306a36Sopenharmony_ci					int mac_len)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	skb->protocol = protocol;
10162306a36Sopenharmony_ci	skb->encapsulation = 1;
10262306a36Sopenharmony_ci	skb_push(skb, pulled_hlen);
10362306a36Sopenharmony_ci	skb_reset_transport_header(skb);
10462306a36Sopenharmony_ci	skb->mac_header = mac_offset;
10562306a36Sopenharmony_ci	skb->network_header = skb->mac_header + mac_len;
10662306a36Sopenharmony_ci	skb->mac_len = mac_len;
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci#endif /* _NET_GSO_H */
110