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