brintos

brintos / linux-shallow public Read only

0
0
Text · 11.7 KiB · 29451d2 Raw
464 lines · c
1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause2// Copyright (c) 2023 Cloudflare3 4/* Test IP_LOCAL_PORT_RANGE socket option: IPv4 + IPv6, TCP + UDP.5 *6 * Tests assume that net.ipv4.ip_local_port_range is [40000, 49999].7 * Don't run these directly but with ip_local_port_range.sh script.8 */9 10#include <fcntl.h>11#include <netinet/ip.h>12 13#include "../kselftest_harness.h"14 15#ifndef IP_LOCAL_PORT_RANGE16#define IP_LOCAL_PORT_RANGE 5117#endif18 19#ifndef IPPROTO_MPTCP20#define IPPROTO_MPTCP 26221#endif22 23static __u32 pack_port_range(__u16 lo, __u16 hi)24{25	return (hi << 16) | (lo << 0);26}27 28static void unpack_port_range(__u32 range, __u16 *lo, __u16 *hi)29{30	*lo = range & 0xffff;31	*hi = range >> 16;32}33 34static int get_so_domain(int fd)35{36	int domain, err;37	socklen_t len;38 39	len = sizeof(domain);40	err = getsockopt(fd, SOL_SOCKET, SO_DOMAIN, &domain, &len);41	if (err)42		return -1;43 44	return domain;45}46 47static int bind_to_loopback_any_port(int fd)48{49	union {50		struct sockaddr sa;51		struct sockaddr_in v4;52		struct sockaddr_in6 v6;53	} addr;54	socklen_t addr_len;55 56	memset(&addr, 0, sizeof(addr));57	switch (get_so_domain(fd)) {58	case AF_INET:59		addr.v4.sin_family = AF_INET;60		addr.v4.sin_port = htons(0);61		addr.v4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);62		addr_len = sizeof(addr.v4);63		break;64	case AF_INET6:65		addr.v6.sin6_family = AF_INET6;66		addr.v6.sin6_port = htons(0);67		addr.v6.sin6_addr = in6addr_loopback;68		addr_len = sizeof(addr.v6);69		break;70	default:71		return -1;72	}73 74	return bind(fd, &addr.sa, addr_len);75}76 77static int get_sock_port(int fd)78{79	union {80		struct sockaddr sa;81		struct sockaddr_in v4;82		struct sockaddr_in6 v6;83	} addr;84	socklen_t addr_len;85	int err;86 87	addr_len = sizeof(addr);88	memset(&addr, 0, sizeof(addr));89	err = getsockname(fd, &addr.sa, &addr_len);90	if (err)91		return -1;92 93	switch (addr.sa.sa_family) {94	case AF_INET:95		return ntohs(addr.v4.sin_port);96	case AF_INET6:97		return ntohs(addr.v6.sin6_port);98	default:99		errno = EAFNOSUPPORT;100		return -1;101	}102}103 104static int get_ip_local_port_range(int fd, __u32 *range)105{106	socklen_t len;107	__u32 val;108	int err;109 110	len = sizeof(val);111	err = getsockopt(fd, SOL_IP, IP_LOCAL_PORT_RANGE, &val, &len);112	if (err)113		return -1;114 115	*range = val;116	return 0;117}118 119FIXTURE(ip_local_port_range) {};120 121FIXTURE_SETUP(ip_local_port_range)122{123}124 125FIXTURE_TEARDOWN(ip_local_port_range)126{127}128 129FIXTURE_VARIANT(ip_local_port_range) {130	int so_domain;131	int so_type;132	int so_protocol;133};134 135FIXTURE_VARIANT_ADD(ip_local_port_range, ip4_tcp) {136	.so_domain	= AF_INET,137	.so_type	= SOCK_STREAM,138	.so_protocol	= 0,139};140 141FIXTURE_VARIANT_ADD(ip_local_port_range, ip4_udp) {142	.so_domain	= AF_INET,143	.so_type	= SOCK_DGRAM,144	.so_protocol	= 0,145};146 147FIXTURE_VARIANT_ADD(ip_local_port_range, ip4_stcp) {148	.so_domain	= AF_INET,149	.so_type	= SOCK_STREAM,150	.so_protocol	= IPPROTO_SCTP,151};152 153FIXTURE_VARIANT_ADD(ip_local_port_range, ip4_mptcp) {154	.so_domain	= AF_INET,155	.so_type	= SOCK_STREAM,156	.so_protocol	= IPPROTO_MPTCP,157};158 159FIXTURE_VARIANT_ADD(ip_local_port_range, ip6_tcp) {160	.so_domain	= AF_INET6,161	.so_type	= SOCK_STREAM,162	.so_protocol	= 0,163};164 165FIXTURE_VARIANT_ADD(ip_local_port_range, ip6_udp) {166	.so_domain	= AF_INET6,167	.so_type	= SOCK_DGRAM,168	.so_protocol	= 0,169};170 171FIXTURE_VARIANT_ADD(ip_local_port_range, ip6_stcp) {172	.so_domain	= AF_INET6,173	.so_type	= SOCK_STREAM,174	.so_protocol	= IPPROTO_SCTP,175};176 177FIXTURE_VARIANT_ADD(ip_local_port_range, ip6_mptcp) {178	.so_domain	= AF_INET6,179	.so_type	= SOCK_STREAM,180	.so_protocol	= IPPROTO_MPTCP,181};182 183TEST_F(ip_local_port_range, invalid_option_value)184{185	__u16 val16;186	__u32 val32;187	__u64 val64;188	int fd, err;189 190	fd = socket(variant->so_domain, variant->so_type, variant->so_protocol);191	ASSERT_GE(fd, 0) TH_LOG("socket failed");192 193	/* Too few bytes */194	val16 = 40000;195	err = setsockopt(fd, SOL_IP, IP_LOCAL_PORT_RANGE, &val16, sizeof(val16));196	EXPECT_TRUE(err) TH_LOG("expected setsockopt(IP_LOCAL_PORT_RANGE) to fail");197	EXPECT_EQ(errno, EINVAL);198 199	/* Empty range: low port > high port */200	val32 = pack_port_range(40222, 40111);201	err = setsockopt(fd, SOL_IP, IP_LOCAL_PORT_RANGE, &val32, sizeof(val32));202	EXPECT_TRUE(err) TH_LOG("expected setsockopt(IP_LOCAL_PORT_RANGE) to fail");203	EXPECT_EQ(errno, EINVAL);204 205	/* Too many bytes */206	val64 = pack_port_range(40333, 40444);207	err = setsockopt(fd, SOL_IP, IP_LOCAL_PORT_RANGE, &val64, sizeof(val64));208	EXPECT_TRUE(err) TH_LOG("expected setsockopt(IP_LOCAL_PORT_RANGE) to fail");209	EXPECT_EQ(errno, EINVAL);210 211	err = close(fd);212	ASSERT_TRUE(!err) TH_LOG("close failed");213}214 215TEST_F(ip_local_port_range, port_range_out_of_netns_range)216{217	const struct test {218		__u16 range_lo;219		__u16 range_hi;220	} tests[] = {221		{ 30000, 39999 }, /* socket range below netns range */222		{ 50000, 59999 }, /* socket range above netns range */223	};224	const struct test *t;225 226	for (t = tests; t < tests + ARRAY_SIZE(tests); t++) {227		/* Bind a couple of sockets, not just one, to check228		 * that the range wasn't clamped to a single port from229		 * the netns range. That is [40000, 40000] or [49999,230		 * 49999], respectively for each test case.231		 */232		int fds[2], i;233 234		TH_LOG("lo %5hu, hi %5hu", t->range_lo, t->range_hi);235 236		for (i = 0; i < ARRAY_SIZE(fds); i++) {237			int fd, err, port;238			__u32 range;239 240			fd = socket(variant->so_domain, variant->so_type, variant->so_protocol);241			ASSERT_GE(fd, 0) TH_LOG("#%d: socket failed", i);242 243			range = pack_port_range(t->range_lo, t->range_hi);244			err = setsockopt(fd, SOL_IP, IP_LOCAL_PORT_RANGE, &range, sizeof(range));245			ASSERT_TRUE(!err) TH_LOG("#%d: setsockopt(IP_LOCAL_PORT_RANGE) failed", i);246 247			err = bind_to_loopback_any_port(fd);248			ASSERT_TRUE(!err) TH_LOG("#%d: bind failed", i);249 250			/* Check that socket port range outside of ephemeral range is ignored */251			port = get_sock_port(fd);252			ASSERT_GE(port, 40000) TH_LOG("#%d: expected port within netns range", i);253			ASSERT_LE(port, 49999) TH_LOG("#%d: expected port within netns range", i);254 255			fds[i] = fd;256		}257 258		for (i = 0; i < ARRAY_SIZE(fds); i++)259			ASSERT_TRUE(close(fds[i]) == 0) TH_LOG("#%d: close failed", i);260	}261}262 263TEST_F(ip_local_port_range, single_port_range)264{265	const struct test {266		__u16 range_lo;267		__u16 range_hi;268		__u16 expected;269	} tests[] = {270		/* single port range within ephemeral range */271		{ 45000, 45000, 45000 },272		/* first port in the ephemeral range (clamp from above) */273		{ 0, 40000, 40000 },274		/* last port in the ephemeral range (clamp from below)  */275		{ 49999, 0, 49999 },276	};277	const struct test *t;278 279	for (t = tests; t < tests + ARRAY_SIZE(tests); t++) {280		int fd, err, port;281		__u32 range;282 283		TH_LOG("lo %5hu, hi %5hu, expected %5hu",284		       t->range_lo, t->range_hi, t->expected);285 286		fd = socket(variant->so_domain, variant->so_type, variant->so_protocol);287		ASSERT_GE(fd, 0) TH_LOG("socket failed");288 289		range = pack_port_range(t->range_lo, t->range_hi);290		err = setsockopt(fd, SOL_IP, IP_LOCAL_PORT_RANGE, &range, sizeof(range));291		ASSERT_TRUE(!err) TH_LOG("setsockopt(IP_LOCAL_PORT_RANGE) failed");292 293		err = bind_to_loopback_any_port(fd);294		ASSERT_TRUE(!err) TH_LOG("bind failed");295 296		port = get_sock_port(fd);297		ASSERT_EQ(port, t->expected) TH_LOG("unexpected local port");298 299		err = close(fd);300		ASSERT_TRUE(!err) TH_LOG("close failed");301	}302}303 304TEST_F(ip_local_port_range, exhaust_8_port_range)305{306	__u8 port_set = 0;307	int i, fd, err;308	__u32 range;309	__u16 port;310	int fds[8];311 312	for (i = 0; i < ARRAY_SIZE(fds); i++) {313		fd = socket(variant->so_domain, variant->so_type, variant->so_protocol);314		ASSERT_GE(fd, 0) TH_LOG("socket failed");315 316		range = pack_port_range(40000, 40007);317		err = setsockopt(fd, SOL_IP, IP_LOCAL_PORT_RANGE, &range, sizeof(range));318		ASSERT_TRUE(!err) TH_LOG("setsockopt(IP_LOCAL_PORT_RANGE) failed");319 320		err = bind_to_loopback_any_port(fd);321		ASSERT_TRUE(!err) TH_LOG("bind failed");322 323		port = get_sock_port(fd);324		ASSERT_GE(port, 40000) TH_LOG("expected port within sockopt range");325		ASSERT_LE(port, 40007) TH_LOG("expected port within sockopt range");326 327		port_set |= 1 << (port - 40000);328		fds[i] = fd;329	}330 331	/* Check that all every port from the test range is in use */332	ASSERT_EQ(port_set, 0xff) TH_LOG("expected all ports to be busy");333 334	/* Check that bind() fails because the whole range is busy */335	fd = socket(variant->so_domain, variant->so_type, variant->so_protocol);336	ASSERT_GE(fd, 0) TH_LOG("socket failed");337 338	range = pack_port_range(40000, 40007);339	err = setsockopt(fd, SOL_IP, IP_LOCAL_PORT_RANGE, &range, sizeof(range));340	ASSERT_TRUE(!err) TH_LOG("setsockopt(IP_LOCAL_PORT_RANGE) failed");341 342	err = bind_to_loopback_any_port(fd);343	ASSERT_TRUE(err) TH_LOG("expected bind to fail");344	ASSERT_EQ(errno, EADDRINUSE);345 346	err = close(fd);347	ASSERT_TRUE(!err) TH_LOG("close failed");348 349	for (i = 0; i < ARRAY_SIZE(fds); i++) {350		err = close(fds[i]);351		ASSERT_TRUE(!err) TH_LOG("close failed");352	}353}354 355TEST_F(ip_local_port_range, late_bind)356{357	union {358		struct sockaddr sa;359		struct sockaddr_in v4;360		struct sockaddr_in6 v6;361	} addr;362	socklen_t addr_len = 0;363	const int one = 1;364	int fd, err;365	__u32 range;366	__u16 port;367 368	fd = socket(variant->so_domain, variant->so_type, 0);369	ASSERT_GE(fd, 0) TH_LOG("socket failed");370 371	range = pack_port_range(40100, 40199);372	err = setsockopt(fd, SOL_IP, IP_LOCAL_PORT_RANGE, &range, sizeof(range));373	ASSERT_TRUE(!err) TH_LOG("setsockopt(IP_LOCAL_PORT_RANGE) failed");374 375	err = setsockopt(fd, SOL_IP, IP_BIND_ADDRESS_NO_PORT, &one, sizeof(one));376	ASSERT_TRUE(!err) TH_LOG("setsockopt(IP_BIND_ADDRESS_NO_PORT) failed");377 378	err = bind_to_loopback_any_port(fd);379	ASSERT_TRUE(!err) TH_LOG("bind failed");380 381	port = get_sock_port(fd);382	ASSERT_EQ(port, 0) TH_LOG("getsockname failed");383 384	/* Invalid destination */385	memset(&addr, 0, sizeof(addr));386	switch (variant->so_domain) {387	case AF_INET:388		addr.v4.sin_family = AF_INET;389		addr.v4.sin_port = htons(0);390		addr.v4.sin_addr.s_addr = htonl(INADDR_ANY);391		addr_len = sizeof(addr.v4);392		break;393	case AF_INET6:394		addr.v6.sin6_family = AF_INET6;395		addr.v6.sin6_port = htons(0);396		addr.v6.sin6_addr = in6addr_any;397		addr_len = sizeof(addr.v6);398		break;399	default:400		ASSERT_TRUE(false) TH_LOG("unsupported socket domain");401	}402 403	/* connect() doesn't need to succeed for late bind to happen */404	connect(fd, &addr.sa, addr_len);405 406	port = get_sock_port(fd);407	ASSERT_GE(port, 40100);408	ASSERT_LE(port, 40199);409 410	err = close(fd);411	ASSERT_TRUE(!err) TH_LOG("close failed");412}413 414XFAIL_ADD(ip_local_port_range, ip4_stcp, late_bind);415XFAIL_ADD(ip_local_port_range, ip6_stcp, late_bind);416 417TEST_F(ip_local_port_range, get_port_range)418{419	__u16 lo, hi;420	__u32 range;421	int fd, err;422 423	fd = socket(variant->so_domain, variant->so_type, variant->so_protocol);424	ASSERT_GE(fd, 0) TH_LOG("socket failed");425 426	/* Get range before it will be set */427	err = get_ip_local_port_range(fd, &range);428	ASSERT_TRUE(!err) TH_LOG("getsockopt(IP_LOCAL_PORT_RANGE) failed");429 430	unpack_port_range(range, &lo, &hi);431	ASSERT_EQ(lo, 0) TH_LOG("unexpected low port");432	ASSERT_EQ(hi, 0) TH_LOG("unexpected high port");433 434	range = pack_port_range(12345, 54321);435	err = setsockopt(fd, SOL_IP, IP_LOCAL_PORT_RANGE, &range, sizeof(range));436	ASSERT_TRUE(!err) TH_LOG("setsockopt(IP_LOCAL_PORT_RANGE) failed");437 438	/* Get range after it has been set */439	err = get_ip_local_port_range(fd, &range);440	ASSERT_TRUE(!err) TH_LOG("getsockopt(IP_LOCAL_PORT_RANGE) failed");441 442	unpack_port_range(range, &lo, &hi);443	ASSERT_EQ(lo, 12345) TH_LOG("unexpected low port");444	ASSERT_EQ(hi, 54321) TH_LOG("unexpected high port");445 446	/* Unset the port range  */447	range = pack_port_range(0, 0);448	err = setsockopt(fd, SOL_IP, IP_LOCAL_PORT_RANGE, &range, sizeof(range));449	ASSERT_TRUE(!err) TH_LOG("setsockopt(IP_LOCAL_PORT_RANGE) failed");450 451	/* Get range after it has been unset */452	err = get_ip_local_port_range(fd, &range);453	ASSERT_TRUE(!err) TH_LOG("getsockopt(IP_LOCAL_PORT_RANGE) failed");454 455	unpack_port_range(range, &lo, &hi);456	ASSERT_EQ(lo, 0) TH_LOG("unexpected low port");457	ASSERT_EQ(hi, 0) TH_LOG("unexpected high port");458 459	err = close(fd);460	ASSERT_TRUE(!err) TH_LOG("close failed");461}462 463TEST_HARNESS_MAIN464