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