1*d457a0e3SEric Dumazet // SPDX-License-Identifier: GPL-2.0-or-later
2*d457a0e3SEric Dumazet #include <linux/skbuff.h>
3*d457a0e3SEric Dumazet #include <linux/sctp.h>
4*d457a0e3SEric Dumazet #include <net/gso.h>
5*d457a0e3SEric Dumazet #include <net/gro.h>
6*d457a0e3SEric Dumazet
7*d457a0e3SEric Dumazet /**
8*d457a0e3SEric Dumazet * skb_eth_gso_segment - segmentation handler for ethernet protocols.
9*d457a0e3SEric Dumazet * @skb: buffer to segment
10*d457a0e3SEric Dumazet * @features: features for the output path (see dev->features)
11*d457a0e3SEric Dumazet * @type: Ethernet Protocol ID
12*d457a0e3SEric Dumazet */
skb_eth_gso_segment(struct sk_buff * skb,netdev_features_t features,__be16 type)13*d457a0e3SEric Dumazet struct sk_buff *skb_eth_gso_segment(struct sk_buff *skb,
14*d457a0e3SEric Dumazet netdev_features_t features, __be16 type)
15*d457a0e3SEric Dumazet {
16*d457a0e3SEric Dumazet struct sk_buff *segs = ERR_PTR(-EPROTONOSUPPORT);
17*d457a0e3SEric Dumazet struct packet_offload *ptype;
18*d457a0e3SEric Dumazet
19*d457a0e3SEric Dumazet rcu_read_lock();
20*d457a0e3SEric Dumazet list_for_each_entry_rcu(ptype, &offload_base, list) {
21*d457a0e3SEric Dumazet if (ptype->type == type && ptype->callbacks.gso_segment) {
22*d457a0e3SEric Dumazet segs = ptype->callbacks.gso_segment(skb, features);
23*d457a0e3SEric Dumazet break;
24*d457a0e3SEric Dumazet }
25*d457a0e3SEric Dumazet }
26*d457a0e3SEric Dumazet rcu_read_unlock();
27*d457a0e3SEric Dumazet
28*d457a0e3SEric Dumazet return segs;
29*d457a0e3SEric Dumazet }
30*d457a0e3SEric Dumazet EXPORT_SYMBOL(skb_eth_gso_segment);
31*d457a0e3SEric Dumazet
32*d457a0e3SEric Dumazet /**
33*d457a0e3SEric Dumazet * skb_mac_gso_segment - mac layer segmentation handler.
34*d457a0e3SEric Dumazet * @skb: buffer to segment
35*d457a0e3SEric Dumazet * @features: features for the output path (see dev->features)
36*d457a0e3SEric Dumazet */
skb_mac_gso_segment(struct sk_buff * skb,netdev_features_t features)37*d457a0e3SEric Dumazet struct sk_buff *skb_mac_gso_segment(struct sk_buff *skb,
38*d457a0e3SEric Dumazet netdev_features_t features)
39*d457a0e3SEric Dumazet {
40*d457a0e3SEric Dumazet struct sk_buff *segs = ERR_PTR(-EPROTONOSUPPORT);
41*d457a0e3SEric Dumazet struct packet_offload *ptype;
42*d457a0e3SEric Dumazet int vlan_depth = skb->mac_len;
43*d457a0e3SEric Dumazet __be16 type = skb_network_protocol(skb, &vlan_depth);
44*d457a0e3SEric Dumazet
45*d457a0e3SEric Dumazet if (unlikely(!type))
46*d457a0e3SEric Dumazet return ERR_PTR(-EINVAL);
47*d457a0e3SEric Dumazet
48*d457a0e3SEric Dumazet __skb_pull(skb, vlan_depth);
49*d457a0e3SEric Dumazet
50*d457a0e3SEric Dumazet rcu_read_lock();
51*d457a0e3SEric Dumazet list_for_each_entry_rcu(ptype, &offload_base, list) {
52*d457a0e3SEric Dumazet if (ptype->type == type && ptype->callbacks.gso_segment) {
53*d457a0e3SEric Dumazet segs = ptype->callbacks.gso_segment(skb, features);
54*d457a0e3SEric Dumazet break;
55*d457a0e3SEric Dumazet }
56*d457a0e3SEric Dumazet }
57*d457a0e3SEric Dumazet rcu_read_unlock();
58*d457a0e3SEric Dumazet
59*d457a0e3SEric Dumazet __skb_push(skb, skb->data - skb_mac_header(skb));
60*d457a0e3SEric Dumazet
61*d457a0e3SEric Dumazet return segs;
62*d457a0e3SEric Dumazet }
63*d457a0e3SEric Dumazet EXPORT_SYMBOL(skb_mac_gso_segment);
64*d457a0e3SEric Dumazet /* openvswitch calls this on rx path, so we need a different check.
65*d457a0e3SEric Dumazet */
skb_needs_check(const struct sk_buff * skb,bool tx_path)66*d457a0e3SEric Dumazet static bool skb_needs_check(const struct sk_buff *skb, bool tx_path)
67*d457a0e3SEric Dumazet {
68*d457a0e3SEric Dumazet if (tx_path)
69*d457a0e3SEric Dumazet return skb->ip_summed != CHECKSUM_PARTIAL &&
70*d457a0e3SEric Dumazet skb->ip_summed != CHECKSUM_UNNECESSARY;
71*d457a0e3SEric Dumazet
72*d457a0e3SEric Dumazet return skb->ip_summed == CHECKSUM_NONE;
73*d457a0e3SEric Dumazet }
74*d457a0e3SEric Dumazet
75*d457a0e3SEric Dumazet /**
76*d457a0e3SEric Dumazet * __skb_gso_segment - Perform segmentation on skb.
77*d457a0e3SEric Dumazet * @skb: buffer to segment
78*d457a0e3SEric Dumazet * @features: features for the output path (see dev->features)
79*d457a0e3SEric Dumazet * @tx_path: whether it is called in TX path
80*d457a0e3SEric Dumazet *
81*d457a0e3SEric Dumazet * This function segments the given skb and returns a list of segments.
82*d457a0e3SEric Dumazet *
83*d457a0e3SEric Dumazet * It may return NULL if the skb requires no segmentation. This is
84*d457a0e3SEric Dumazet * only possible when GSO is used for verifying header integrity.
85*d457a0e3SEric Dumazet *
86*d457a0e3SEric Dumazet * Segmentation preserves SKB_GSO_CB_OFFSET bytes of previous skb cb.
87*d457a0e3SEric Dumazet */
__skb_gso_segment(struct sk_buff * skb,netdev_features_t features,bool tx_path)88*d457a0e3SEric Dumazet struct sk_buff *__skb_gso_segment(struct sk_buff *skb,
89*d457a0e3SEric Dumazet netdev_features_t features, bool tx_path)
90*d457a0e3SEric Dumazet {
91*d457a0e3SEric Dumazet struct sk_buff *segs;
92*d457a0e3SEric Dumazet
93*d457a0e3SEric Dumazet if (unlikely(skb_needs_check(skb, tx_path))) {
94*d457a0e3SEric Dumazet int err;
95*d457a0e3SEric Dumazet
96*d457a0e3SEric Dumazet /* We're going to init ->check field in TCP or UDP header */
97*d457a0e3SEric Dumazet err = skb_cow_head(skb, 0);
98*d457a0e3SEric Dumazet if (err < 0)
99*d457a0e3SEric Dumazet return ERR_PTR(err);
100*d457a0e3SEric Dumazet }
101*d457a0e3SEric Dumazet
102*d457a0e3SEric Dumazet /* Only report GSO partial support if it will enable us to
103*d457a0e3SEric Dumazet * support segmentation on this frame without needing additional
104*d457a0e3SEric Dumazet * work.
105*d457a0e3SEric Dumazet */
106*d457a0e3SEric Dumazet if (features & NETIF_F_GSO_PARTIAL) {
107*d457a0e3SEric Dumazet netdev_features_t partial_features = NETIF_F_GSO_ROBUST;
108*d457a0e3SEric Dumazet struct net_device *dev = skb->dev;
109*d457a0e3SEric Dumazet
110*d457a0e3SEric Dumazet partial_features |= dev->features & dev->gso_partial_features;
111*d457a0e3SEric Dumazet if (!skb_gso_ok(skb, features | partial_features))
112*d457a0e3SEric Dumazet features &= ~NETIF_F_GSO_PARTIAL;
113*d457a0e3SEric Dumazet }
114*d457a0e3SEric Dumazet
115*d457a0e3SEric Dumazet BUILD_BUG_ON(SKB_GSO_CB_OFFSET +
116*d457a0e3SEric Dumazet sizeof(*SKB_GSO_CB(skb)) > sizeof(skb->cb));
117*d457a0e3SEric Dumazet
118*d457a0e3SEric Dumazet SKB_GSO_CB(skb)->mac_offset = skb_headroom(skb);
119*d457a0e3SEric Dumazet SKB_GSO_CB(skb)->encap_level = 0;
120*d457a0e3SEric Dumazet
121*d457a0e3SEric Dumazet skb_reset_mac_header(skb);
122*d457a0e3SEric Dumazet skb_reset_mac_len(skb);
123*d457a0e3SEric Dumazet
124*d457a0e3SEric Dumazet segs = skb_mac_gso_segment(skb, features);
125*d457a0e3SEric Dumazet
126*d457a0e3SEric Dumazet if (segs != skb && unlikely(skb_needs_check(skb, tx_path) && !IS_ERR(segs)))
127*d457a0e3SEric Dumazet skb_warn_bad_offload(skb);
128*d457a0e3SEric Dumazet
129*d457a0e3SEric Dumazet return segs;
130*d457a0e3SEric Dumazet }
131*d457a0e3SEric Dumazet EXPORT_SYMBOL(__skb_gso_segment);
132*d457a0e3SEric Dumazet
133*d457a0e3SEric Dumazet /**
134*d457a0e3SEric Dumazet * skb_gso_transport_seglen - Return length of individual segments of a gso packet
135*d457a0e3SEric Dumazet *
136*d457a0e3SEric Dumazet * @skb: GSO skb
137*d457a0e3SEric Dumazet *
138*d457a0e3SEric Dumazet * skb_gso_transport_seglen is used to determine the real size of the
139*d457a0e3SEric Dumazet * individual segments, including Layer4 headers (TCP/UDP).
140*d457a0e3SEric Dumazet *
141*d457a0e3SEric Dumazet * The MAC/L2 or network (IP, IPv6) headers are not accounted for.
142*d457a0e3SEric Dumazet */
skb_gso_transport_seglen(const struct sk_buff * skb)143*d457a0e3SEric Dumazet static unsigned int skb_gso_transport_seglen(const struct sk_buff *skb)
144*d457a0e3SEric Dumazet {
145*d457a0e3SEric Dumazet const struct skb_shared_info *shinfo = skb_shinfo(skb);
146*d457a0e3SEric Dumazet unsigned int thlen = 0;
147*d457a0e3SEric Dumazet
148*d457a0e3SEric Dumazet if (skb->encapsulation) {
149*d457a0e3SEric Dumazet thlen = skb_inner_transport_header(skb) -
150*d457a0e3SEric Dumazet skb_transport_header(skb);
151*d457a0e3SEric Dumazet
152*d457a0e3SEric Dumazet if (likely(shinfo->gso_type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6)))
153*d457a0e3SEric Dumazet thlen += inner_tcp_hdrlen(skb);
154*d457a0e3SEric Dumazet } else if (likely(shinfo->gso_type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))) {
155*d457a0e3SEric Dumazet thlen = tcp_hdrlen(skb);
156*d457a0e3SEric Dumazet } else if (unlikely(skb_is_gso_sctp(skb))) {
157*d457a0e3SEric Dumazet thlen = sizeof(struct sctphdr);
158*d457a0e3SEric Dumazet } else if (shinfo->gso_type & SKB_GSO_UDP_L4) {
159*d457a0e3SEric Dumazet thlen = sizeof(struct udphdr);
160*d457a0e3SEric Dumazet }
161*d457a0e3SEric Dumazet /* UFO sets gso_size to the size of the fragmentation
162*d457a0e3SEric Dumazet * payload, i.e. the size of the L4 (UDP) header is already
163*d457a0e3SEric Dumazet * accounted for.
164*d457a0e3SEric Dumazet */
165*d457a0e3SEric Dumazet return thlen + shinfo->gso_size;
166*d457a0e3SEric Dumazet }
167*d457a0e3SEric Dumazet
168*d457a0e3SEric Dumazet /**
169*d457a0e3SEric Dumazet * skb_gso_network_seglen - Return length of individual segments of a gso packet
170*d457a0e3SEric Dumazet *
171*d457a0e3SEric Dumazet * @skb: GSO skb
172*d457a0e3SEric Dumazet *
173*d457a0e3SEric Dumazet * skb_gso_network_seglen is used to determine the real size of the
174*d457a0e3SEric Dumazet * individual segments, including Layer3 (IP, IPv6) and L4 headers (TCP/UDP).
175*d457a0e3SEric Dumazet *
176*d457a0e3SEric Dumazet * The MAC/L2 header is not accounted for.
177*d457a0e3SEric Dumazet */
skb_gso_network_seglen(const struct sk_buff * skb)178*d457a0e3SEric Dumazet static unsigned int skb_gso_network_seglen(const struct sk_buff *skb)
179*d457a0e3SEric Dumazet {
180*d457a0e3SEric Dumazet unsigned int hdr_len = skb_transport_header(skb) -
181*d457a0e3SEric Dumazet skb_network_header(skb);
182*d457a0e3SEric Dumazet
183*d457a0e3SEric Dumazet return hdr_len + skb_gso_transport_seglen(skb);
184*d457a0e3SEric Dumazet }
185*d457a0e3SEric Dumazet
186*d457a0e3SEric Dumazet /**
187*d457a0e3SEric Dumazet * skb_gso_mac_seglen - Return length of individual segments of a gso packet
188*d457a0e3SEric Dumazet *
189*d457a0e3SEric Dumazet * @skb: GSO skb
190*d457a0e3SEric Dumazet *
191*d457a0e3SEric Dumazet * skb_gso_mac_seglen is used to determine the real size of the
192*d457a0e3SEric Dumazet * individual segments, including MAC/L2, Layer3 (IP, IPv6) and L4
193*d457a0e3SEric Dumazet * headers (TCP/UDP).
194*d457a0e3SEric Dumazet */
skb_gso_mac_seglen(const struct sk_buff * skb)195*d457a0e3SEric Dumazet static unsigned int skb_gso_mac_seglen(const struct sk_buff *skb)
196*d457a0e3SEric Dumazet {
197*d457a0e3SEric Dumazet unsigned int hdr_len = skb_transport_header(skb) - skb_mac_header(skb);
198*d457a0e3SEric Dumazet
199*d457a0e3SEric Dumazet return hdr_len + skb_gso_transport_seglen(skb);
200*d457a0e3SEric Dumazet }
201*d457a0e3SEric Dumazet
202*d457a0e3SEric Dumazet /**
203*d457a0e3SEric Dumazet * skb_gso_size_check - check the skb size, considering GSO_BY_FRAGS
204*d457a0e3SEric Dumazet *
205*d457a0e3SEric Dumazet * There are a couple of instances where we have a GSO skb, and we
206*d457a0e3SEric Dumazet * want to determine what size it would be after it is segmented.
207*d457a0e3SEric Dumazet *
208*d457a0e3SEric Dumazet * We might want to check:
209*d457a0e3SEric Dumazet * - L3+L4+payload size (e.g. IP forwarding)
210*d457a0e3SEric Dumazet * - L2+L3+L4+payload size (e.g. sanity check before passing to driver)
211*d457a0e3SEric Dumazet *
212*d457a0e3SEric Dumazet * This is a helper to do that correctly considering GSO_BY_FRAGS.
213*d457a0e3SEric Dumazet *
214*d457a0e3SEric Dumazet * @skb: GSO skb
215*d457a0e3SEric Dumazet *
216*d457a0e3SEric Dumazet * @seg_len: The segmented length (from skb_gso_*_seglen). In the
217*d457a0e3SEric Dumazet * GSO_BY_FRAGS case this will be [header sizes + GSO_BY_FRAGS].
218*d457a0e3SEric Dumazet *
219*d457a0e3SEric Dumazet * @max_len: The maximum permissible length.
220*d457a0e3SEric Dumazet *
221*d457a0e3SEric Dumazet * Returns true if the segmented length <= max length.
222*d457a0e3SEric Dumazet */
skb_gso_size_check(const struct sk_buff * skb,unsigned int seg_len,unsigned int max_len)223*d457a0e3SEric Dumazet static inline bool skb_gso_size_check(const struct sk_buff *skb,
224*d457a0e3SEric Dumazet unsigned int seg_len,
225*d457a0e3SEric Dumazet unsigned int max_len) {
226*d457a0e3SEric Dumazet const struct skb_shared_info *shinfo = skb_shinfo(skb);
227*d457a0e3SEric Dumazet const struct sk_buff *iter;
228*d457a0e3SEric Dumazet
229*d457a0e3SEric Dumazet if (shinfo->gso_size != GSO_BY_FRAGS)
230*d457a0e3SEric Dumazet return seg_len <= max_len;
231*d457a0e3SEric Dumazet
232*d457a0e3SEric Dumazet /* Undo this so we can re-use header sizes */
233*d457a0e3SEric Dumazet seg_len -= GSO_BY_FRAGS;
234*d457a0e3SEric Dumazet
235*d457a0e3SEric Dumazet skb_walk_frags(skb, iter) {
236*d457a0e3SEric Dumazet if (seg_len + skb_headlen(iter) > max_len)
237*d457a0e3SEric Dumazet return false;
238*d457a0e3SEric Dumazet }
239*d457a0e3SEric Dumazet
240*d457a0e3SEric Dumazet return true;
241*d457a0e3SEric Dumazet }
242*d457a0e3SEric Dumazet
243*d457a0e3SEric Dumazet /**
244*d457a0e3SEric Dumazet * skb_gso_validate_network_len - Will a split GSO skb fit into a given MTU?
245*d457a0e3SEric Dumazet *
246*d457a0e3SEric Dumazet * @skb: GSO skb
247*d457a0e3SEric Dumazet * @mtu: MTU to validate against
248*d457a0e3SEric Dumazet *
249*d457a0e3SEric Dumazet * skb_gso_validate_network_len validates if a given skb will fit a
250*d457a0e3SEric Dumazet * wanted MTU once split. It considers L3 headers, L4 headers, and the
251*d457a0e3SEric Dumazet * payload.
252*d457a0e3SEric Dumazet */
skb_gso_validate_network_len(const struct sk_buff * skb,unsigned int mtu)253*d457a0e3SEric Dumazet bool skb_gso_validate_network_len(const struct sk_buff *skb, unsigned int mtu)
254*d457a0e3SEric Dumazet {
255*d457a0e3SEric Dumazet return skb_gso_size_check(skb, skb_gso_network_seglen(skb), mtu);
256*d457a0e3SEric Dumazet }
257*d457a0e3SEric Dumazet EXPORT_SYMBOL_GPL(skb_gso_validate_network_len);
258*d457a0e3SEric Dumazet
259*d457a0e3SEric Dumazet /**
260*d457a0e3SEric Dumazet * skb_gso_validate_mac_len - Will a split GSO skb fit in a given length?
261*d457a0e3SEric Dumazet *
262*d457a0e3SEric Dumazet * @skb: GSO skb
263*d457a0e3SEric Dumazet * @len: length to validate against
264*d457a0e3SEric Dumazet *
265*d457a0e3SEric Dumazet * skb_gso_validate_mac_len validates if a given skb will fit a wanted
266*d457a0e3SEric Dumazet * length once split, including L2, L3 and L4 headers and the payload.
267*d457a0e3SEric Dumazet */
skb_gso_validate_mac_len(const struct sk_buff * skb,unsigned int len)268*d457a0e3SEric Dumazet bool skb_gso_validate_mac_len(const struct sk_buff *skb, unsigned int len)
269*d457a0e3SEric Dumazet {
270*d457a0e3SEric Dumazet return skb_gso_size_check(skb, skb_gso_mac_seglen(skb), len);
271*d457a0e3SEric Dumazet }
272*d457a0e3SEric Dumazet EXPORT_SYMBOL_GPL(skb_gso_validate_mac_len);
273*d457a0e3SEric Dumazet
274