xref: /linux/tools/testing/selftests/net/udpgso.c (revision 3cf0a98fea776adb09087e521fe150c295a4b031)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 #define _GNU_SOURCE
4 
5 #include <stddef.h>
6 #include <arpa/inet.h>
7 #include <error.h>
8 #include <errno.h>
9 #include <net/if.h>
10 #include <linux/in.h>
11 #include <linux/netlink.h>
12 #include <linux/rtnetlink.h>
13 #include <netinet/if_ether.h>
14 #include <netinet/ip.h>
15 #include <netinet/ip6.h>
16 #include <netinet/udp.h>
17 #include <stdbool.h>
18 #include <stdlib.h>
19 #include <stdio.h>
20 #include <string.h>
21 #include <sys/ioctl.h>
22 #include <sys/socket.h>
23 #include <sys/stat.h>
24 #include <sys/time.h>
25 #include <sys/types.h>
26 #include <unistd.h>
27 
28 #ifndef ETH_MAX_MTU
29 #define ETH_MAX_MTU	0xFFFFU
30 #endif
31 
32 #ifndef UDP_SEGMENT
33 #define UDP_SEGMENT		103
34 #endif
35 
36 #ifndef UDP_MAX_SEGMENTS
37 #define UDP_MAX_SEGMENTS	(1 << 7UL)
38 #endif
39 
40 #define CONST_MTU_TEST	1500
41 
42 #define CONST_HDRLEN_V4		(sizeof(struct iphdr) + sizeof(struct udphdr))
43 #define CONST_HDRLEN_V6		(sizeof(struct ip6_hdr) + sizeof(struct udphdr))
44 
45 #define CONST_MSS_V4		(CONST_MTU_TEST - CONST_HDRLEN_V4)
46 #define CONST_MSS_V6		(CONST_MTU_TEST - CONST_HDRLEN_V6)
47 
48 #define CONST_MAX_SEGS_V4	(ETH_MAX_MTU / CONST_MSS_V4)
49 #define CONST_MAX_SEGS_V6	(ETH_MAX_MTU / CONST_MSS_V6)
50 
51 static bool		cfg_do_ipv4;
52 static bool		cfg_do_ipv6;
53 static bool		cfg_do_connected;
54 static bool		cfg_do_connectionless;
55 static bool		cfg_do_msgmore;
56 static bool		cfg_do_recv = true;
57 static bool		cfg_do_setsockopt;
58 static int		cfg_specific_test_id = -1;
59 
60 static unsigned short	cfg_port = 9000;
61 
62 static char buf[ETH_MAX_MTU];
63 
64 struct testcase {
65 	int tlen;		/* send() buffer size, may exceed mss */
66 	bool tfail;		/* send() call is expected to fail */
67 	int gso_len;		/* mss after applying gso */
68 	int r_num_mss;		/* recv(): number of calls of full mss */
69 	int r_len_last;		/* recv(): size of last non-mss dgram, if any */
70 	bool v6_ext_hdr;	/* send() dgrams with IPv6 extension headers */
71 };
72 
73 const struct in6_addr addr6 = {
74 	{ { 0xfd, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } }, /* fd00::1 */
75 };
76 
77 const struct in_addr addr4 = {
78 	__constant_htonl(0x0a000001), /* 10.0.0.1 */
79 };
80 
81 static const char ipv6_hopopts_pad1[8] = { 0 };
82 
83 struct testcase testcases_v4[] = {
84 	{
85 		/* no GSO: send a single byte */
86 		.tlen = 1,
87 		.r_len_last = 1,
88 	},
89 	{
90 		/* no GSO: send a single MSS */
91 		.tlen = CONST_MSS_V4,
92 		.r_num_mss = 1,
93 	},
94 	{
95 		/* no GSO: send a single MSS + 1B: fail */
96 		.tlen = CONST_MSS_V4 + 1,
97 		.tfail = true,
98 	},
99 	{
100 		/* send a single MSS: will fall back to no GSO */
101 		.tlen = CONST_MSS_V4,
102 		.gso_len = CONST_MSS_V4,
103 		.r_num_mss = 1,
104 	},
105 	{
106 		/* datalen <= MSS < gso_len: will fall back to no GSO */
107 		.tlen = CONST_MSS_V4,
108 		.gso_len = CONST_MSS_V4 + 1,
109 		.r_num_mss = 0,
110 		.r_len_last = CONST_MSS_V4,
111 	},
112 	{
113 		/* MSS < datalen < gso_len: fail */
114 		.tlen = CONST_MSS_V4 + 1,
115 		.gso_len = CONST_MSS_V4 + 2,
116 		.tfail = true,
117 	},
118 	{
119 		/* send a single MSS + 1B */
120 		.tlen = CONST_MSS_V4 + 1,
121 		.gso_len = CONST_MSS_V4,
122 		.r_num_mss = 1,
123 		.r_len_last = 1,
124 	},
125 	{
126 		/* send exactly 2 MSS */
127 		.tlen = CONST_MSS_V4 * 2,
128 		.gso_len = CONST_MSS_V4,
129 		.r_num_mss = 2,
130 	},
131 	{
132 		/* send 2 MSS + 1B */
133 		.tlen = (CONST_MSS_V4 * 2) + 1,
134 		.gso_len = CONST_MSS_V4,
135 		.r_num_mss = 2,
136 		.r_len_last = 1,
137 	},
138 	{
139 		/* send MAX segs */
140 		.tlen = (ETH_MAX_MTU / CONST_MSS_V4) * CONST_MSS_V4,
141 		.gso_len = CONST_MSS_V4,
142 		.r_num_mss = (ETH_MAX_MTU / CONST_MSS_V4),
143 	},
144 
145 	{
146 		/* send MAX bytes */
147 		.tlen = ETH_MAX_MTU - CONST_HDRLEN_V4,
148 		.gso_len = CONST_MSS_V4,
149 		.r_num_mss = CONST_MAX_SEGS_V4,
150 		.r_len_last = ETH_MAX_MTU - CONST_HDRLEN_V4 -
151 			      (CONST_MAX_SEGS_V4 * CONST_MSS_V4),
152 	},
153 	{
154 		/* send MAX + 1: fail */
155 		.tlen = ETH_MAX_MTU - CONST_HDRLEN_V4 + 1,
156 		.gso_len = CONST_MSS_V4,
157 		.tfail = true,
158 	},
159 	{
160 		/* send a single 1B MSS: will fall back to no GSO */
161 		.tlen = 1,
162 		.gso_len = 1,
163 		.r_num_mss = 1,
164 	},
165 	{
166 		/* send 2 1B segments */
167 		.tlen = 2,
168 		.gso_len = 1,
169 		.r_num_mss = 2,
170 	},
171 	{
172 		/* send 2B + 2B + 1B segments */
173 		.tlen = 5,
174 		.gso_len = 2,
175 		.r_num_mss = 2,
176 		.r_len_last = 1,
177 	},
178 	{
179 		/* send max number of min sized segments */
180 		.tlen = UDP_MAX_SEGMENTS,
181 		.gso_len = 1,
182 		.r_num_mss = UDP_MAX_SEGMENTS,
183 	},
184 	{
185 		/* send max number + 1 of min sized segments: fail */
186 		.tlen = UDP_MAX_SEGMENTS + 1,
187 		.gso_len = 1,
188 		.tfail = true,
189 	},
190 	{
191 		/* EOL */
192 	}
193 };
194 
195 #ifndef IP6_MAX_MTU
196 #define IP6_MAX_MTU	(ETH_MAX_MTU + sizeof(struct ip6_hdr))
197 #endif
198 
199 struct testcase testcases_v6[] = {
200 	{
201 		/* no GSO: send a single byte */
202 		.tlen = 1,
203 		.r_len_last = 1,
204 	},
205 	{
206 		/* no GSO: send a single MSS */
207 		.tlen = CONST_MSS_V6,
208 		.r_num_mss = 1,
209 	},
210 	{
211 		/* no GSO: send a single MSS + 1B: fail */
212 		.tlen = CONST_MSS_V6 + 1,
213 		.tfail = true,
214 	},
215 	{
216 		/* send a single MSS: will fall back to no GSO */
217 		.tlen = CONST_MSS_V6,
218 		.gso_len = CONST_MSS_V6,
219 		.r_num_mss = 1,
220 	},
221 	{
222 		/* datalen <= MSS < gso_len: will fall back to no GSO */
223 		.tlen = CONST_MSS_V6,
224 		.gso_len = CONST_MSS_V6 + 1,
225 		.r_num_mss = 0,
226 		.r_len_last = CONST_MSS_V6,
227 	},
228 	{
229 		/* MSS < datalen < gso_len: fail */
230 		.tlen = CONST_MSS_V6 + 1,
231 		.gso_len = CONST_MSS_V6 + 2,
232 		.tfail = true
233 	},
234 	{
235 		/* send a single MSS + 1B */
236 		.tlen = CONST_MSS_V6 + 1,
237 		.gso_len = CONST_MSS_V6,
238 		.r_num_mss = 1,
239 		.r_len_last = 1,
240 	},
241 	{
242 		/* send exactly 2 MSS */
243 		.tlen = CONST_MSS_V6 * 2,
244 		.gso_len = CONST_MSS_V6,
245 		.r_num_mss = 2,
246 	},
247 	{
248 		/* send 2 MSS + 1B */
249 		.tlen = (CONST_MSS_V6 * 2) + 1,
250 		.gso_len = CONST_MSS_V6,
251 		.r_num_mss = 2,
252 		.r_len_last = 1,
253 	},
254 	{
255 		/* send MAX segs */
256 		.tlen = (IP6_MAX_MTU / CONST_MSS_V6) * CONST_MSS_V6,
257 		.gso_len = CONST_MSS_V6,
258 		.r_num_mss = (IP6_MAX_MTU / CONST_MSS_V6),
259 	},
260 
261 	{
262 		/* send MAX bytes */
263 		.tlen = IP6_MAX_MTU - CONST_HDRLEN_V6,
264 		.gso_len = CONST_MSS_V6,
265 		.r_num_mss = CONST_MAX_SEGS_V6,
266 		.r_len_last = IP6_MAX_MTU - CONST_HDRLEN_V6 -
267 			      (CONST_MAX_SEGS_V6 * CONST_MSS_V6),
268 	},
269 	{
270 		/* send MAX + 1: fail */
271 		.tlen = IP6_MAX_MTU - CONST_HDRLEN_V6 + 1,
272 		.gso_len = CONST_MSS_V6,
273 		.tfail = true,
274 	},
275 	{
276 		/* send a single 1B MSS: will fall back to no GSO */
277 		.tlen = 1,
278 		.gso_len = 1,
279 		.r_num_mss = 1,
280 	},
281 	{
282 		/* send 2 1B segments */
283 		.tlen = 2,
284 		.gso_len = 1,
285 		.r_num_mss = 2,
286 	},
287 	{
288 		/* send 2 1B segments with extension headers */
289 		.tlen = 2,
290 		.gso_len = 1,
291 		.r_num_mss = 2,
292 		.v6_ext_hdr = true,
293 	},
294 	{
295 		/* send 2B + 2B + 1B segments */
296 		.tlen = 5,
297 		.gso_len = 2,
298 		.r_num_mss = 2,
299 		.r_len_last = 1,
300 	},
301 	{
302 		/* send max number of min sized segments */
303 		.tlen = UDP_MAX_SEGMENTS,
304 		.gso_len = 1,
305 		.r_num_mss = UDP_MAX_SEGMENTS,
306 	},
307 	{
308 		/* send max number + 1 of min sized segments: fail */
309 		.tlen = UDP_MAX_SEGMENTS + 1,
310 		.gso_len = 1,
311 		.tfail = true,
312 	},
313 	{
314 		/* EOL */
315 	}
316 };
317 
set_pmtu_discover(int fd,bool is_ipv4)318 static void set_pmtu_discover(int fd, bool is_ipv4)
319 {
320 	int level, name, val;
321 
322 	if (is_ipv4) {
323 		level	= SOL_IP;
324 		name	= IP_MTU_DISCOVER;
325 		val	= IP_PMTUDISC_DO;
326 	} else {
327 		level	= SOL_IPV6;
328 		name	= IPV6_MTU_DISCOVER;
329 		val	= IPV6_PMTUDISC_DO;
330 	}
331 
332 	if (setsockopt(fd, level, name, &val, sizeof(val)))
333 		error(1, errno, "setsockopt path mtu");
334 }
335 
get_path_mtu(int fd,bool is_ipv4)336 static unsigned int get_path_mtu(int fd, bool is_ipv4)
337 {
338 	socklen_t vallen;
339 	unsigned int mtu;
340 	int ret;
341 
342 	vallen = sizeof(mtu);
343 	if (is_ipv4)
344 		ret = getsockopt(fd, SOL_IP, IP_MTU, &mtu, &vallen);
345 	else
346 		ret = getsockopt(fd, SOL_IPV6, IPV6_MTU, &mtu, &vallen);
347 
348 	if (ret)
349 		error(1, errno, "getsockopt mtu");
350 
351 
352 	fprintf(stderr, "path mtu (read):  %u\n", mtu);
353 	return mtu;
354 }
355 
__send_one(int fd,struct msghdr * msg,int flags)356 static bool __send_one(int fd, struct msghdr *msg, int flags)
357 {
358 	int ret;
359 
360 	ret = sendmsg(fd, msg, flags);
361 	if (ret == -1 &&
362 	    (errno == EMSGSIZE || errno == ENOMEM || errno == EINVAL))
363 		return false;
364 	if (ret == -1)
365 		error(1, errno, "sendmsg");
366 	if (ret != msg->msg_iov->iov_len)
367 		error(1, 0, "sendto: %d != %llu", ret,
368 			(unsigned long long)msg->msg_iov->iov_len);
369 	if (msg->msg_flags)
370 		error(1, 0, "sendmsg: return flags 0x%x\n", msg->msg_flags);
371 
372 	return true;
373 }
374 
send_one(int fd,int len,int gso_len,struct sockaddr * addr,socklen_t alen)375 static bool send_one(int fd, int len, int gso_len,
376 		     struct sockaddr *addr, socklen_t alen)
377 {
378 	char control[CMSG_SPACE(sizeof(uint16_t))] = {0};
379 	struct msghdr msg = {0};
380 	struct iovec iov = {0};
381 	struct cmsghdr *cm;
382 
383 	iov.iov_base = buf;
384 	iov.iov_len = len;
385 
386 	msg.msg_iov = &iov;
387 	msg.msg_iovlen = 1;
388 
389 	msg.msg_name = addr;
390 	msg.msg_namelen = alen;
391 
392 	if (gso_len && !cfg_do_setsockopt) {
393 		msg.msg_control = control;
394 		msg.msg_controllen = sizeof(control);
395 
396 		cm = CMSG_FIRSTHDR(&msg);
397 		cm->cmsg_level = SOL_UDP;
398 		cm->cmsg_type = UDP_SEGMENT;
399 		cm->cmsg_len = CMSG_LEN(sizeof(uint16_t));
400 		*((uint16_t *) CMSG_DATA(cm)) = gso_len;
401 	}
402 
403 	/* If MSG_MORE, send 1 byte followed by remainder */
404 	if (cfg_do_msgmore && len > 1) {
405 		iov.iov_len = 1;
406 		if (!__send_one(fd, &msg, MSG_MORE))
407 			error(1, 0, "send 1B failed");
408 
409 		iov.iov_base++;
410 		iov.iov_len = len - 1;
411 	}
412 
413 	return __send_one(fd, &msg, 0);
414 }
415 
recv_one(int fd,int flags)416 static int recv_one(int fd, int flags)
417 {
418 	int ret;
419 
420 	ret = recv(fd, buf, sizeof(buf), flags);
421 	if (ret == -1 && errno == EAGAIN && (flags & MSG_DONTWAIT))
422 		return 0;
423 	if (ret == -1)
424 		error(1, errno, "recv");
425 
426 	return ret;
427 }
428 
run_one(struct testcase * test,int fdt,int fdr,struct sockaddr * addr,socklen_t alen)429 static void run_one(struct testcase *test, int fdt, int fdr,
430 		    struct sockaddr *addr, socklen_t alen)
431 {
432 	int i, ret, val, mss;
433 	bool sent;
434 
435 	fprintf(stderr, "ipv%d tx:%d gso:%d %s%s\n",
436 			addr->sa_family == AF_INET ? 4 : 6,
437 			test->tlen, test->gso_len,
438 			test->v6_ext_hdr ? "ext-hdr " : "",
439 			test->tfail ? "(fail)" : "");
440 
441 	if (test->v6_ext_hdr) {
442 		if (setsockopt(fdt, IPPROTO_IPV6, IPV6_HOPOPTS,
443 			       ipv6_hopopts_pad1, sizeof(ipv6_hopopts_pad1)))
444 			error(1, errno, "setsockopt ipv6 hopopts");
445 	}
446 
447 	val = test->gso_len;
448 	if (cfg_do_setsockopt) {
449 		if (setsockopt(fdt, SOL_UDP, UDP_SEGMENT, &val, sizeof(val)))
450 			error(1, errno, "setsockopt udp segment");
451 	}
452 
453 	sent = send_one(fdt, test->tlen, test->gso_len, addr, alen);
454 	if (sent && test->tfail)
455 		error(1, 0, "send succeeded while expecting failure");
456 	if (!sent && !test->tfail)
457 		error(1, 0, "send failed while expecting success");
458 
459 	if (test->v6_ext_hdr) {
460 		if (setsockopt(fdt, IPPROTO_IPV6, IPV6_HOPOPTS, NULL, 0))
461 			error(1, errno, "setsockopt ipv6 hopopts clear");
462 	}
463 
464 	if (!sent)
465 		return;
466 
467 	if (!cfg_do_recv)
468 		return;
469 
470 	if (test->gso_len)
471 		mss = test->gso_len;
472 	else
473 		mss = addr->sa_family == AF_INET ? CONST_MSS_V4 : CONST_MSS_V6;
474 
475 
476 	/* Recv all full MSS datagrams */
477 	for (i = 0; i < test->r_num_mss; i++) {
478 		ret = recv_one(fdr, 0);
479 		if (ret != mss)
480 			error(1, 0, "recv.%d: %d != %d", i, ret, mss);
481 	}
482 
483 	/* Recv the non-full last datagram, if tlen was not a multiple of mss */
484 	if (test->r_len_last) {
485 		ret = recv_one(fdr, 0);
486 		if (ret != test->r_len_last)
487 			error(1, 0, "recv.%d: %d != %d (last)",
488 			      i, ret, test->r_len_last);
489 	}
490 
491 	/* Verify received all data */
492 	ret = recv_one(fdr, MSG_DONTWAIT);
493 	if (ret)
494 		error(1, 0, "recv: unexpected datagram");
495 }
496 
run_all(int fdt,int fdr,struct sockaddr * addr,socklen_t alen)497 static void run_all(int fdt, int fdr, struct sockaddr *addr, socklen_t alen)
498 {
499 	struct testcase *tests, *test;
500 
501 	tests = addr->sa_family == AF_INET ? testcases_v4 : testcases_v6;
502 
503 	for (test = tests; test->tlen; test++) {
504 		/* if a specific test is given, then skip all others */
505 		if (cfg_specific_test_id == -1 ||
506 		    cfg_specific_test_id == test - tests)
507 			run_one(test, fdt, fdr, addr, alen);
508 	}
509 }
510 
run_test(struct sockaddr * addr,socklen_t alen)511 static void run_test(struct sockaddr *addr, socklen_t alen)
512 {
513 	struct timeval tv = { .tv_usec = 100 * 1000 };
514 	int fdr, fdt, val;
515 
516 	fdr = socket(addr->sa_family, SOCK_DGRAM, 0);
517 	if (fdr == -1)
518 		error(1, errno, "socket r");
519 
520 	if (cfg_do_recv) {
521 		if (bind(fdr, addr, alen))
522 			error(1, errno, "bind");
523 	}
524 
525 	/* Have tests fail quickly instead of hang */
526 	if (setsockopt(fdr, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)))
527 		error(1, errno, "setsockopt rcv timeout");
528 
529 	fdt = socket(addr->sa_family, SOCK_DGRAM, 0);
530 	if (fdt == -1)
531 		error(1, errno, "socket t");
532 
533 	/* Do not fragment these datagrams: only succeed if GSO works */
534 	set_pmtu_discover(fdt, addr->sa_family == AF_INET);
535 
536 	if (cfg_do_connectionless)
537 		run_all(fdt, fdr, addr, alen);
538 
539 	if (cfg_do_connected) {
540 		if (connect(fdt, addr, alen))
541 			error(1, errno, "connect");
542 
543 		val = get_path_mtu(fdt, addr->sa_family == AF_INET);
544 		if (val != CONST_MTU_TEST)
545 			error(1, 0, "bad path mtu %u\n", val);
546 
547 		run_all(fdt, fdr, addr, 0 /* use connected addr */);
548 	}
549 
550 	if (close(fdt))
551 		error(1, errno, "close t");
552 	if (close(fdr))
553 		error(1, errno, "close r");
554 }
555 
run_test_v4(void)556 static void run_test_v4(void)
557 {
558 	struct sockaddr_in addr = {0};
559 
560 	addr.sin_family = AF_INET;
561 	addr.sin_port = htons(cfg_port);
562 	addr.sin_addr = addr4;
563 
564 	run_test((void *)&addr, sizeof(addr));
565 }
566 
run_test_v6(void)567 static void run_test_v6(void)
568 {
569 	struct sockaddr_in6 addr = {0};
570 
571 	addr.sin6_family = AF_INET6;
572 	addr.sin6_port = htons(cfg_port);
573 	addr.sin6_addr = addr6;
574 
575 	run_test((void *)&addr, sizeof(addr));
576 }
577 
parse_opts(int argc,char ** argv)578 static void parse_opts(int argc, char **argv)
579 {
580 	int c;
581 
582 	while ((c = getopt(argc, argv, "46cCmRst:")) != -1) {
583 		switch (c) {
584 		case '4':
585 			cfg_do_ipv4 = true;
586 			break;
587 		case '6':
588 			cfg_do_ipv6 = true;
589 			break;
590 		case 'c':
591 			cfg_do_connected = true;
592 			break;
593 		case 'C':
594 			cfg_do_connectionless = true;
595 			break;
596 		case 'm':
597 			cfg_do_msgmore = true;
598 			break;
599 		case 'R':
600 			cfg_do_recv = false;
601 			break;
602 		case 's':
603 			cfg_do_setsockopt = true;
604 			break;
605 		case 't':
606 			cfg_specific_test_id = strtoul(optarg, NULL, 0);
607 			break;
608 		default:
609 			error(1, 0, "%s: parse error", argv[0]);
610 		}
611 	}
612 }
613 
main(int argc,char ** argv)614 int main(int argc, char **argv)
615 {
616 	parse_opts(argc, argv);
617 
618 	if (cfg_do_ipv4)
619 		run_test_v4();
620 	if (cfg_do_ipv6)
621 		run_test_v6();
622 
623 	fprintf(stderr, "OK\n");
624 	return 0;
625 }
626