udp_offload.c 19.1 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0-or-later
2 3 4 5 6 7 8 9
/*
 *	IPV4 GSO/GRO offload support
 *	Linux INET implementation
 *
 *	UDPv4 GSO support
 */

#include <linux/skbuff.h>
10
#include <net/gro.h>
11 12
#include <net/udp.h>
#include <net/protocol.h>
13
#include <net/inet_common.h>
14

15 16 17 18
static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb,
	netdev_features_t features,
	struct sk_buff *(*gso_inner_segment)(struct sk_buff *skb,
					     netdev_features_t features),
19
	__be16 new_protocol, bool is_ipv6)
20
{
21
	int tnl_hlen = skb_inner_mac_header(skb) - skb_transport_header(skb);
22
	bool remcsum, need_csum, offload_csum, gso_partial;
23
	struct sk_buff *segs = ERR_PTR(-EINVAL);
24
	struct udphdr *uh = udp_hdr(skb);
25 26
	u16 mac_offset = skb->mac_header;
	__be16 protocol = skb->protocol;
27
	u16 mac_len = skb->mac_len;
28
	int udp_offset, outer_hlen;
29
	__wsum partial;
30
	bool need_ipsec;
31 32 33 34

	if (unlikely(!pskb_may_pull(skb, tnl_hlen)))
		goto out;

35 36 37 38 39 40
	/* Adjust partial header checksum to negate old length.
	 * We cannot rely on the value contained in uh->len as it is
	 * possible that the actual value exceeds the boundaries of the
	 * 16 bit length field due to the header being added outside of an
	 * IP or IPv6 frame that was already limited to 64K - 1.
	 */
41 42 43 44 45
	if (skb_shinfo(skb)->gso_type & SKB_GSO_PARTIAL)
		partial = (__force __wsum)uh->len;
	else
		partial = (__force __wsum)htonl(skb->len);
	partial = csum_sub(csum_unfold(uh->check), partial);
46 47

	/* setup inner skb. */
48
	skb->encapsulation = 0;
49
	SKB_GSO_CB(skb)->encap_level = 0;
50 51 52
	__skb_pull(skb, tnl_hlen);
	skb_reset_mac_header(skb);
	skb_set_network_header(skb, skb_inner_network_offset(skb));
53
	skb_set_transport_header(skb, skb_inner_transport_offset(skb));
54
	skb->mac_len = skb_inner_network_offset(skb);
55
	skb->protocol = new_protocol;
56 57

	need_csum = !!(skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL_CSUM);
58
	skb->encap_hdr_csum = need_csum;
59 60

	remcsum = !!(skb_shinfo(skb)->gso_type & SKB_GSO_TUNNEL_REMCSUM);
61
	skb->remcsum_offload = remcsum;
62

63
	need_ipsec = skb_dst(skb) && dst_xfrm(skb_dst(skb));
64 65
	/* Try to offload checksum if possible */
	offload_csum = !!(need_csum &&
66
			  !need_ipsec &&
67 68 69
			  (skb->dev->features &
			   (is_ipv6 ? (NETIF_F_HW_CSUM | NETIF_F_IPV6_CSUM) :
				      (NETIF_F_HW_CSUM | NETIF_F_IP_CSUM))));
70

71
	features &= skb->dev->hw_enc_features;
72 73
	if (need_csum)
		features &= ~NETIF_F_SCTP_CRC;
74

75 76 77 78
	/* The only checksum offload we care about from here on out is the
	 * outer one so strip the existing checksum feature flags and
	 * instead set the flag based on our outer checksum offload value.
	 */
79
	if (remcsum) {
80
		features &= ~NETIF_F_CSUM_MASK;
81
		if (!need_csum || offload_csum)
82 83 84
			features |= NETIF_F_HW_CSUM;
	}

85
	/* segment inner packet. */
86
	segs = gso_inner_segment(skb, features);
87
	if (IS_ERR_OR_NULL(segs)) {
88 89 90 91 92
		skb_gso_error_unwind(skb, protocol, tnl_hlen, mac_offset,
				     mac_len);
		goto out;
	}

93 94
	gso_partial = !!(skb_shinfo(segs)->gso_type & SKB_GSO_PARTIAL);

95 96 97 98
	outer_hlen = skb_tnl_header_len(skb);
	udp_offset = outer_hlen - tnl_hlen;
	skb = segs;
	do {
99
		unsigned int len;
100

101
		if (remcsum)
102
			skb->ip_summed = CHECKSUM_NONE;
103 104 105

		/* Set up inner headers if we are offloading inner checksum */
		if (skb->ip_summed == CHECKSUM_PARTIAL) {
106 107 108
			skb_reset_inner_headers(skb);
			skb->encapsulation = 1;
		}
109 110

		skb->mac_len = mac_len;
111
		skb->protocol = protocol;
112

113
		__skb_push(skb, outer_hlen);
114 115 116
		skb_reset_mac_header(skb);
		skb_set_network_header(skb, mac_len);
		skb_set_transport_header(skb, udp_offset);
117
		len = skb->len - udp_offset;
118
		uh = udp_hdr(skb);
119 120 121 122 123

		/* If we are only performing partial GSO the inner header
		 * will be using a length value equal to only one MSS sized
		 * segment instead of the entire frame.
		 */
124
		if (gso_partial && skb_is_gso(skb)) {
125 126 127 128 129 130
			uh->len = htons(skb_shinfo(skb)->gso_size +
					SKB_GSO_CB(skb)->data_offset +
					skb->head - (unsigned char *)uh);
		} else {
			uh->len = htons(len);
		}
131

132 133 134
		if (!need_csum)
			continue;

135 136
		uh->check = ~csum_fold(csum_add(partial,
				       (__force __wsum)htonl(len)));
137

138 139
		if (skb->encapsulation || !offload_csum) {
			uh->check = gso_make_checksum(skb, ~uh->check);
140 141
			if (uh->check == 0)
				uh->check = CSUM_MANGLED_0;
142 143 144 145
		} else {
			skb->ip_summed = CHECKSUM_PARTIAL;
			skb->csum_start = skb_transport_header(skb) - skb->head;
			skb->csum_offset = offsetof(struct udphdr, check);
146 147 148 149 150 151
		}
	} while ((skb = skb->next));
out:
	return segs;
}

152 153 154 155
struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
				       netdev_features_t features,
				       bool is_ipv6)
{
Eric Dumazet's avatar
Eric Dumazet committed
156
	const struct net_offload __rcu **offloads;
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
	__be16 protocol = skb->protocol;
	const struct net_offload *ops;
	struct sk_buff *segs = ERR_PTR(-EINVAL);
	struct sk_buff *(*gso_inner_segment)(struct sk_buff *skb,
					     netdev_features_t features);

	rcu_read_lock();

	switch (skb->inner_protocol_type) {
	case ENCAP_TYPE_ETHER:
		protocol = skb->inner_protocol;
		gso_inner_segment = skb_mac_gso_segment;
		break;
	case ENCAP_TYPE_IPPROTO:
		offloads = is_ipv6 ? inet6_offloads : inet_offloads;
		ops = rcu_dereference(offloads[skb->inner_ipproto]);
		if (!ops || !ops->callbacks.gso_segment)
			goto out_unlock;
		gso_inner_segment = ops->callbacks.gso_segment;
		break;
	default:
		goto out_unlock;
	}

	segs = __skb_udp_tunnel_segment(skb, features, gso_inner_segment,
182
					protocol, is_ipv6);
183 184 185 186 187 188

out_unlock:
	rcu_read_unlock();

	return segs;
}
189
EXPORT_SYMBOL(skb_udp_tunnel_segment);
190

191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248
static void __udpv4_gso_segment_csum(struct sk_buff *seg,
				     __be32 *oldip, __be32 *newip,
				     __be16 *oldport, __be16 *newport)
{
	struct udphdr *uh;
	struct iphdr *iph;

	if (*oldip == *newip && *oldport == *newport)
		return;

	uh = udp_hdr(seg);
	iph = ip_hdr(seg);

	if (uh->check) {
		inet_proto_csum_replace4(&uh->check, seg, *oldip, *newip,
					 true);
		inet_proto_csum_replace2(&uh->check, seg, *oldport, *newport,
					 false);
		if (!uh->check)
			uh->check = CSUM_MANGLED_0;
	}
	*oldport = *newport;

	csum_replace4(&iph->check, *oldip, *newip);
	*oldip = *newip;
}

static struct sk_buff *__udpv4_gso_segment_list_csum(struct sk_buff *segs)
{
	struct sk_buff *seg;
	struct udphdr *uh, *uh2;
	struct iphdr *iph, *iph2;

	seg = segs;
	uh = udp_hdr(seg);
	iph = ip_hdr(seg);

	if ((udp_hdr(seg)->dest == udp_hdr(seg->next)->dest) &&
	    (udp_hdr(seg)->source == udp_hdr(seg->next)->source) &&
	    (ip_hdr(seg)->daddr == ip_hdr(seg->next)->daddr) &&
	    (ip_hdr(seg)->saddr == ip_hdr(seg->next)->saddr))
		return segs;

	while ((seg = seg->next)) {
		uh2 = udp_hdr(seg);
		iph2 = ip_hdr(seg);

		__udpv4_gso_segment_csum(seg,
					 &iph2->saddr, &iph->saddr,
					 &uh2->source, &uh->source);
		__udpv4_gso_segment_csum(seg,
					 &iph2->daddr, &iph->daddr,
					 &uh2->dest, &uh->dest);
	}

	return segs;
}

249
static struct sk_buff *__udp_gso_segment_list(struct sk_buff *skb,
250 251
					      netdev_features_t features,
					      bool is_ipv6)
252 253 254 255 256 257 258 259 260
{
	unsigned int mss = skb_shinfo(skb)->gso_size;

	skb = skb_segment_list(skb, features, skb_mac_header_len(skb));
	if (IS_ERR(skb))
		return skb;

	udp_hdr(skb)->len = htons(sizeof(struct udphdr) + mss);

261
	return is_ipv6 ? skb : __udpv4_gso_segment_list_csum(skb);
262 263
}

Willem de Bruijn's avatar
Willem de Bruijn committed
264
struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,
265
				  netdev_features_t features, bool is_ipv6)
Willem de Bruijn's avatar
Willem de Bruijn committed
266
{
267 268
	struct sock *sk = gso_skb->sk;
	unsigned int sum_truesize = 0;
Willem de Bruijn's avatar
Willem de Bruijn committed
269 270
	struct sk_buff *segs, *seg;
	struct udphdr *uh;
271
	unsigned int mss;
272
	bool copy_dtor;
273 274
	__sum16 check;
	__be16 newlen;
Willem de Bruijn's avatar
Willem de Bruijn committed
275

276
	if (skb_shinfo(gso_skb)->gso_type & SKB_GSO_FRAGLIST)
277
		return __udp_gso_segment_list(gso_skb, features, is_ipv6);
278

279
	mss = skb_shinfo(gso_skb)->gso_size;
Willem de Bruijn's avatar
Willem de Bruijn committed
280 281 282 283 284
	if (gso_skb->len <= sizeof(*uh) + mss)
		return ERR_PTR(-EINVAL);

	skb_pull(gso_skb, sizeof(*uh));

285
	/* clear destructor to avoid skb_segment assigning it to tail */
286 287 288
	copy_dtor = gso_skb->destructor == sock_wfree;
	if (copy_dtor)
		gso_skb->destructor = NULL;
289

Willem de Bruijn's avatar
Willem de Bruijn committed
290
	segs = skb_segment(gso_skb, features);
291
	if (IS_ERR_OR_NULL(segs)) {
292 293
		if (copy_dtor)
			gso_skb->destructor = sock_wfree;
Willem de Bruijn's avatar
Willem de Bruijn committed
294
		return segs;
295
	}
Willem de Bruijn's avatar
Willem de Bruijn committed
296

297 298 299 300 301 302 303
	/* GSO partial and frag_list segmentation only requires splitting
	 * the frame into an MSS multiple and possibly a remainder, both
	 * cases return a GSO skb. So update the mss now.
	 */
	if (skb_is_gso(segs))
		mss *= skb_shinfo(segs)->gso_segs;

304 305
	seg = segs;
	uh = udp_hdr(seg);
306

307 308 309 310 311
	/* preserve TX timestamp flags and TS key for first segment */
	skb_shinfo(seg)->tskey = skb_shinfo(gso_skb)->tskey;
	skb_shinfo(seg)->tx_flags |=
			(skb_shinfo(gso_skb)->tx_flags & SKBTX_ANY_TSTAMP);

312 313 314 315
	/* compute checksum adjustment based on old length versus new */
	newlen = htons(sizeof(*uh) + mss);
	check = csum16_add(csum16_sub(uh->check, uh->len), newlen);

316
	for (;;) {
317 318 319 320 321
		if (copy_dtor) {
			seg->destructor = sock_wfree;
			seg->sk = sk;
			sum_truesize += seg->truesize;
		}
Willem de Bruijn's avatar
Willem de Bruijn committed
322

323 324
		if (!seg->next)
			break;
325 326 327

		uh->len = newlen;
		uh->check = check;
328

329 330 331 332 333 334
		if (seg->ip_summed == CHECKSUM_PARTIAL)
			gso_reset_checksum(seg, ~check);
		else
			uh->check = gso_make_checksum(seg, ~check) ? :
				    CSUM_MANGLED_0;

335 336
		seg = seg->next;
		uh = udp_hdr(seg);
Willem de Bruijn's avatar
Willem de Bruijn committed
337 338
	}

339 340 341 342 343 344 345 346
	/* last packet can be partial gso_size, account for that in checksum */
	newlen = htons(skb_tail_pointer(seg) - skb_transport_header(seg) +
		       seg->data_len);
	check = csum16_add(csum16_sub(uh->check, uh->len), newlen);

	uh->len = newlen;
	uh->check = check;

347 348 349 350 351
	if (seg->ip_summed == CHECKSUM_PARTIAL)
		gso_reset_checksum(seg, ~check);
	else
		uh->check = gso_make_checksum(seg, ~check) ? : CSUM_MANGLED_0;

352
	/* update refcount for the packet */
353 354 355 356 357 358 359 360 361 362 363
	if (copy_dtor) {
		int delta = sum_truesize - gso_skb->truesize;

		/* In some pathological cases, delta can be negative.
		 * We need to either use refcount_add() or refcount_sub_and_test()
		 */
		if (likely(delta >= 0))
			refcount_add(delta, &sk->sk_wmem_alloc);
		else
			WARN_ON_ONCE(refcount_sub_and_test(-delta, &sk->sk_wmem_alloc));
	}
Willem de Bruijn's avatar
Willem de Bruijn committed
364 365 366 367
	return segs;
}
EXPORT_SYMBOL_GPL(__udp_gso_segment);

368 369
static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb,
					 netdev_features_t features)
370 371
{
	struct sk_buff *segs = ERR_PTR(-EINVAL);
372 373 374 375
	unsigned int mss;
	__wsum csum;
	struct udphdr *uh;
	struct iphdr *iph;
376 377

	if (skb->encapsulation &&
378
	    (skb_shinfo(skb)->gso_type &
379
	     (SKB_GSO_UDP_TUNNEL|SKB_GSO_UDP_TUNNEL_CSUM))) {
380
		segs = skb_udp_tunnel_segment(skb, features, false);
381 382 383
		goto out;
	}

Willem de Bruijn's avatar
Willem de Bruijn committed
384
	if (!(skb_shinfo(skb)->gso_type & (SKB_GSO_UDP | SKB_GSO_UDP_L4)))
385 386
		goto out;

387 388 389
	if (!pskb_may_pull(skb, sizeof(struct udphdr)))
		goto out;

Willem de Bruijn's avatar
Willem de Bruijn committed
390
	if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4)
391
		return __udp_gso_segment(skb, features, false);
Willem de Bruijn's avatar
Willem de Bruijn committed
392

393 394 395 396 397 398 399 400
	mss = skb_shinfo(skb)->gso_size;
	if (unlikely(skb->len <= mss))
		goto out;

	/* Do software UFO. Complete and fill in the UDP checksum as
	 * HW cannot do checksum of UDP packets sent as multiple
	 * IP fragments.
	 */
401

402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424
	uh = udp_hdr(skb);
	iph = ip_hdr(skb);

	uh->check = 0;
	csum = skb_checksum(skb, 0, skb->len, 0);
	uh->check = udp_v4_check(skb->len, iph->saddr, iph->daddr, csum);
	if (uh->check == 0)
		uh->check = CSUM_MANGLED_0;

	skb->ip_summed = CHECKSUM_UNNECESSARY;

	/* If there is no outer header we can fake a checksum offload
	 * due to the fact that we have already done the checksum in
	 * software prior to segmenting the frame.
	 */
	if (!skb->encap_hdr_csum)
		features |= NETIF_F_HW_CSUM;

	/* Fragment the skb. IP headers of the fragments are updated in
	 * inet_gso_segment()
	 */
	segs = skb_segment(skb, features);
out:
425 426 427
	return segs;
}

428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454
static int skb_gro_receive_list(struct sk_buff *p, struct sk_buff *skb)
{
	if (unlikely(p->len + skb->len >= 65536))
		return -E2BIG;

	if (NAPI_GRO_CB(p)->last == p)
		skb_shinfo(p)->frag_list = skb;
	else
		NAPI_GRO_CB(p)->last->next = skb;

	skb_pull(skb, skb_gro_offset(skb));

	NAPI_GRO_CB(p)->last = skb;
	NAPI_GRO_CB(p)->count++;
	p->data_len += skb->len;

	/* sk owenrship - if any - completely transferred to the aggregated packet */
	skb->destructor = NULL;
	p->truesize += skb->truesize;
	p->len += skb->len;

	NAPI_GRO_CB(skb)->same_flow = 1;

	return 0;
}


455 456 457 458
#define UDP_GRO_CNT_MAX 64
static struct sk_buff *udp_gro_receive_segment(struct list_head *head,
					       struct sk_buff *skb)
{
459
	struct udphdr *uh = udp_gro_udphdr(skb);
460 461 462
	struct sk_buff *pp = NULL;
	struct udphdr *uh2;
	struct sk_buff *p;
463
	unsigned int ulen;
464
	int ret = 0;
465 466 467 468 469 470 471

	/* requires non zero csum, for symmetry with GSO */
	if (!uh->check) {
		NAPI_GRO_CB(skb)->flush = 1;
		return NULL;
	}

472 473 474 475 476 477
	/* Do not deal with padded or malicious packets, sorry ! */
	ulen = ntohs(uh->len);
	if (ulen <= sizeof(*uh) || ulen != skb_gro_len(skb)) {
		NAPI_GRO_CB(skb)->flush = 1;
		return NULL;
	}
478 479 480 481 482 483 484 485 486 487 488 489 490 491 492
	/* pull encapsulating udp header */
	skb_gro_pull(skb, sizeof(struct udphdr));

	list_for_each_entry(p, head, list) {
		if (!NAPI_GRO_CB(p)->same_flow)
			continue;

		uh2 = udp_hdr(p);

		/* Match ports only, as csum is always non zero */
		if ((*(u32 *)&uh->source != *(u32 *)&uh2->source)) {
			NAPI_GRO_CB(p)->same_flow = 0;
			continue;
		}

493 494 495 496 497
		if (NAPI_GRO_CB(skb)->is_flist != NAPI_GRO_CB(p)->is_flist) {
			NAPI_GRO_CB(skb)->flush = 1;
			return p;
		}

498 499
		/* Terminate the flow on len mismatch or if it grow "too much".
		 * Under small packet flood GRO count could elsewhere grow a lot
500
		 * leading to excessive truesize values.
501 502
		 * On len mismatch merge the first packet shorter than gso_size,
		 * otherwise complete the GRO packet.
503
		 */
504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526
		if (ulen > ntohs(uh2->len)) {
			pp = p;
		} else {
			if (NAPI_GRO_CB(skb)->is_flist) {
				if (!pskb_may_pull(skb, skb_gro_offset(skb))) {
					NAPI_GRO_CB(skb)->flush = 1;
					return NULL;
				}
				if ((skb->ip_summed != p->ip_summed) ||
				    (skb->csum_level != p->csum_level)) {
					NAPI_GRO_CB(skb)->flush = 1;
					return NULL;
				}
				ret = skb_gro_receive_list(p, skb);
			} else {
				skb_gro_postpull_rcsum(skb, uh,
						       sizeof(struct udphdr));

				ret = skb_gro_receive(p, skb);
			}
		}

		if (ret || ulen != ntohs(uh2->len) ||
527 528 529 530 531 532 533 534 535 536
		    NAPI_GRO_CB(p)->count >= UDP_GRO_CNT_MAX)
			pp = p;

		return pp;
	}

	/* mismatch, but we never need to flush */
	return NULL;
}

537
struct sk_buff *udp_gro_receive(struct list_head *head, struct sk_buff *skb,
538
				struct udphdr *uh, struct sock *sk)
539
{
540 541
	struct sk_buff *pp = NULL;
	struct sk_buff *p;
Tom Herbert's avatar
Tom Herbert committed
542 543
	struct udphdr *uh2;
	unsigned int off = skb_gro_offset(skb);
544 545
	int flush = 1;

546 547 548
	/* we can do L4 aggregation only if the packet can't land in a tunnel
	 * otherwise we could corrupt the inner stream
	 */
549
	NAPI_GRO_CB(skb)->is_flist = 0;
550 551 552
	if (!sk || !udp_sk(sk)->gro_receive) {
		if (skb->dev->features & NETIF_F_GRO_FRAGLIST)
			NAPI_GRO_CB(skb)->is_flist = sk ? !udp_sk(sk)->gro_enabled : 1;
553

554 555
		if ((!sk && (skb->dev->features & NETIF_F_GRO_UDP_FWD)) ||
		    (sk && udp_sk(sk)->gro_enabled) || NAPI_GRO_CB(skb)->is_flist)
556 557 558 559
			return call_gro_receive(udp_gro_receive_segment, head, skb);

		/* no GRO, be sure flush the current packet */
		goto out;
560 561
	}

562
	if (NAPI_GRO_CB(skb)->encap_mark ||
563
	    (uh->check && skb->ip_summed != CHECKSUM_PARTIAL &&
564
	     NAPI_GRO_CB(skb)->csum_cnt == 0 &&
565
	     !NAPI_GRO_CB(skb)->csum_valid))
566
		goto out;
567

568 569
	/* mark that this skb passed once through the tunnel gro layer */
	NAPI_GRO_CB(skb)->encap_mark = 1;
570 571 572

	flush = 0;

573
	list_for_each_entry(p, head, list) {
574 575 576 577
		if (!NAPI_GRO_CB(p)->same_flow)
			continue;

		uh2 = (struct udphdr   *)(p->data + off);
Tom Herbert's avatar
Tom Herbert committed
578 579 580 581 582 583

		/* Match ports and either checksums are either both zero
		 * or nonzero.
		 */
		if ((*(u32 *)&uh->source != *(u32 *)&uh2->source) ||
		    (!uh->check ^ !uh2->check)) {
584 585 586 587 588 589
			NAPI_GRO_CB(p)->same_flow = 0;
			continue;
		}
	}

	skb_gro_pull(skb, sizeof(struct udphdr)); /* pull encapsulating udp header */
590
	skb_gro_postpull_rcsum(skb, uh, sizeof(struct udphdr));
591
	pp = call_gro_receive_sk(udp_sk(sk)->gro_receive, sk, head, skb);
592

593
out:
594
	skb_gro_flush_final(skb, pp, flush);
595 596
	return pp;
}
597
EXPORT_SYMBOL(udp_gro_receive);
598

599 600 601 602 603 604 605 606 607 608
static struct sock *udp4_gro_lookup_skb(struct sk_buff *skb, __be16 sport,
					__be16 dport)
{
	const struct iphdr *iph = skb_gro_network_header(skb);

	return __udp4_lib_lookup(dev_net(skb->dev), iph->saddr, sport,
				 iph->daddr, dport, inet_iif(skb),
				 inet_sdif(skb), &udp_table, NULL);
}

609 610
INDIRECT_CALLABLE_SCOPE
struct sk_buff *udp4_gro_receive(struct list_head *head, struct sk_buff *skb)
Tom Herbert's avatar
Tom Herbert committed
611 612
{
	struct udphdr *uh = udp_gro_udphdr(skb);
613
	struct sock *sk = NULL;
614
	struct sk_buff *pp;
Tom Herbert's avatar
Tom Herbert committed
615

616
	if (unlikely(!uh))
617
		goto flush;
Tom Herbert's avatar
Tom Herbert committed
618

619
	/* Don't bother verifying checksum if we're going to flush anyway. */
620
	if (NAPI_GRO_CB(skb)->flush)
621 622 623 624 625 626
		goto skip;

	if (skb_gro_checksum_validate_zero_check(skb, IPPROTO_UDP, uh->check,
						 inet_gro_compute_pseudo))
		goto flush;
	else if (uh->check)
627
		skb_gro_checksum_try_convert(skb, IPPROTO_UDP,
628 629
					     inet_gro_compute_pseudo);
skip:
630
	NAPI_GRO_CB(skb)->is_ipv6 = 0;
631 632 633 634

	if (static_branch_unlikely(&udp_encap_needed_key))
		sk = udp4_gro_lookup_skb(skb, uh->source, uh->dest);

635 636
	pp = udp_gro_receive(head, skb, uh, sk);
	return pp;
637 638 639 640

flush:
	NAPI_GRO_CB(skb)->flush = 1;
	return NULL;
Tom Herbert's avatar
Tom Herbert committed
641 642
}

643 644 645 646 647 648 649 650 651 652
static int udp_gro_complete_segment(struct sk_buff *skb)
{
	struct udphdr *uh = udp_hdr(skb);

	skb->csum_start = (unsigned char *)uh - skb->head;
	skb->csum_offset = offsetof(struct udphdr, check);
	skb->ip_summed = CHECKSUM_PARTIAL;

	skb_shinfo(skb)->gso_segs = NAPI_GRO_CB(skb)->count;
	skb_shinfo(skb)->gso_type |= SKB_GSO_UDP_L4;
653 654 655 656

	if (skb->encapsulation)
		skb->inner_transport_header = skb->transport_header;

657 658 659
	return 0;
}

660 661
int udp_gro_complete(struct sk_buff *skb, int nhoff,
		     udp_lookup_t lookup)
662 663 664
{
	__be16 newlen = htons(skb->len - nhoff);
	struct udphdr *uh = (struct udphdr *)(skb->data + nhoff);
665
	struct sock *sk;
666
	int err;
667 668 669 670

	uh->len = newlen;

	rcu_read_lock();
671 672
	sk = INDIRECT_CALL_INET(lookup, udp6_lib_lookup_skb,
				udp4_lib_lookup_skb, skb, uh->source, uh->dest);
673
	if (sk && udp_sk(sk)->gro_complete) {
674 675 676
		skb_shinfo(skb)->gso_type = uh->check ? SKB_GSO_UDP_TUNNEL_CSUM
					: SKB_GSO_UDP_TUNNEL;

677 678 679 680 681
		/* clear the encap mark, so that inner frag_list gro_complete
		 * can take place
		 */
		NAPI_GRO_CB(skb)->encap_mark = 0;

682 683 684 685
		/* Set encapsulation before calling into inner gro_complete()
		 * functions to make them set up the inner offsets.
		 */
		skb->encapsulation = 1;
686 687
		err = udp_sk(sk)->gro_complete(sk, skb,
				nhoff + sizeof(struct udphdr));
688 689
	} else {
		err = udp_gro_complete_segment(skb);
690
	}
691
	rcu_read_unlock();
692 693 694 695

	if (skb->remcsum_offload)
		skb_shinfo(skb)->gso_type |= SKB_GSO_TUNNEL_REMCSUM;

696 697
	return err;
}
698
EXPORT_SYMBOL(udp_gro_complete);
699

700
INDIRECT_CALLABLE_SCOPE int udp4_gro_complete(struct sk_buff *skb, int nhoff)
Tom Herbert's avatar
Tom Herbert committed
701 702 703 704
{
	const struct iphdr *iph = ip_hdr(skb);
	struct udphdr *uh = (struct udphdr *)(skb->data + nhoff);

705 706
	/* do fraglist only if there is no outer UDP encap (or we already processed it) */
	if (NAPI_GRO_CB(skb)->is_flist && !NAPI_GRO_CB(skb)->encap_mark) {
707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722
		uh->len = htons(skb->len - nhoff);

		skb_shinfo(skb)->gso_type |= (SKB_GSO_FRAGLIST|SKB_GSO_UDP_L4);
		skb_shinfo(skb)->gso_segs = NAPI_GRO_CB(skb)->count;

		if (skb->ip_summed == CHECKSUM_UNNECESSARY) {
			if (skb->csum_level < SKB_MAX_CSUM_LEVEL)
				skb->csum_level++;
		} else {
			skb->ip_summed = CHECKSUM_UNNECESSARY;
			skb->csum_level = 0;
		}

		return 0;
	}

723
	if (uh->check)
Tom Herbert's avatar
Tom Herbert committed
724 725 726
		uh->check = ~udp_v4_check(skb->len - nhoff, iph->saddr,
					  iph->daddr, 0);

727
	return udp_gro_complete(skb, nhoff, udp4_lib_lookup_skb);
Tom Herbert's avatar
Tom Herbert committed
728 729
}

730 731
static const struct net_offload udpv4_offload = {
	.callbacks = {
732
		.gso_segment = udp4_ufo_fragment,
Tom Herbert's avatar
Tom Herbert committed
733 734
		.gro_receive  =	udp4_gro_receive,
		.gro_complete =	udp4_gro_complete,
735 736 737 738 739 740 741
	},
};

int __init udpv4_offload_init(void)
{
	return inet_add_offload(&udpv4_offload, IPPROTO_UDP);
}