brintos

brintos / linux-shallow public Read only

0
0
Text · 12.5 KiB · 3f2fca0 Raw
600 lines · c
1// SPDX-License-Identifier: GPL-2.02 3#define _GNU_SOURCE4 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_MTU29#define ETH_MAX_MTU	0xFFFFU30#endif31 32#ifndef UDP_SEGMENT33#define UDP_SEGMENT		10334#endif35 36#ifndef UDP_MAX_SEGMENTS37#define UDP_MAX_SEGMENTS	(1 << 7UL)38#endif39 40#define CONST_MTU_TEST	150041 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 51static bool		cfg_do_ipv4;52static bool		cfg_do_ipv6;53static bool		cfg_do_connected;54static bool		cfg_do_connectionless;55static bool		cfg_do_msgmore;56static bool		cfg_do_recv = true;57static bool		cfg_do_setsockopt;58static int		cfg_specific_test_id = -1;59 60static unsigned short	cfg_port = 9000;61 62static char buf[ETH_MAX_MTU];63 64struct 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 73const 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 77const struct in_addr addr4 = {78	__constant_htonl(0x0a000001), /* 10.0.0.1 */79};80 81static const char ipv6_hopopts_pad1[8] = { 0 };82 83struct 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_MTU183#define IP6_MAX_MTU	(ETH_MAX_MTU + sizeof(struct ip6_hdr))184#endif185 186struct 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 292static 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 310static 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	else320		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 330static 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 349static 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 390static 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 403static 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	else447		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 471static 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 485static 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 530static 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 541static 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 552static 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 588int 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