Expand the ip_local_port_range tests to check that when using IP_LOCAL_PORT_RANGE socket option: 1) We can share the local port as long as there is no IP address conflict with any other socket. Covered by tcp_port_reuse__no_ip_conflict* tests. 2) We cannot share the local port with wildcard sockets or when there is a local IP conflict. Covered by tcp_port_reuse__ip_conflict* tests. 3) We cannot share the local IP and port to connect to different remote IPs if the port bucket is in non-reuseable state, Corner case covered by tcp_port_reuse__ip_port_conflict_with_unique_dst_after_bind test. Signed-off-by: Jakub Sitnicki --- tools/testing/selftests/net/ip_local_port_range.c | 525 +++++++++++++++++++++ tools/testing/selftests/net/ip_local_port_range.sh | 14 +- 2 files changed, 534 insertions(+), 5 deletions(-) diff --git a/tools/testing/selftests/net/ip_local_port_range.c b/tools/testing/selftests/net/ip_local_port_range.c index 29451d2244b7..33288732dc1b 100644 --- a/tools/testing/selftests/net/ip_local_port_range.c +++ b/tools/testing/selftests/net/ip_local_port_range.c @@ -9,6 +9,7 @@ #include #include +#include #include "../kselftest_harness.h" @@ -20,6 +21,15 @@ #define IPPROTO_MPTCP 262 #endif +static const int ONE = 1; + +__attribute__((nonnull)) static inline void close_fd(int *fd) +{ + close(*fd); +} + +#define __close_fd __attribute__((cleanup(close_fd))) + static __u32 pack_port_range(__u16 lo, __u16 hi) { return (hi << 16) | (lo << 0); @@ -116,6 +126,81 @@ static int get_ip_local_port_range(int fd, __u32 *range) return 0; } +struct sockaddr_inet { + union { + struct sockaddr_storage ss; + struct sockaddr_in6 v6; + struct sockaddr_in v4; + struct sockaddr sa; + }; + socklen_t len; +}; + +static void make_inet_addr(int af, const char *ip, __u16 port, + struct sockaddr_inet *addr) +{ + memset(addr, 0, sizeof(*addr)); + + switch (af) { + case AF_INET: + addr->len = sizeof(addr->v4); + addr->v4.sin_family = af; + addr->v4.sin_port = htons(port); + inet_pton(af, ip, &addr->v4.sin_addr); + break; + case AF_INET6: + addr->len = sizeof(addr->v6); + addr->v6.sin6_family = af; + addr->v6.sin6_port = htons(port); + inet_pton(af, ip, &addr->v6.sin6_addr); + break; + } +} + +static bool is_v4mapped(const struct sockaddr_inet *a) +{ + return (a->sa.sa_family == AF_INET6 && + IN6_IS_ADDR_V4MAPPED(&a->v6.sin6_addr)); +} + +static void v4mapped_to_ipv4(struct sockaddr_inet *a) +{ + in_port_t port = a->v6.sin6_port; + in_addr_t ip4 = *(in_addr_t *)&a->v6.sin6_addr.s6_addr[12]; + + memset(a, 0, sizeof(*a)); + a->len = sizeof(a->v4); + a->v4.sin_family = AF_INET; + a->v4.sin_port = port; + a->v4.sin_addr.s_addr = ip4; +} + +static void ipv4_to_v4mapped(struct sockaddr_inet *a) +{ + in_port_t port = a->v4.sin_port; + in_addr_t ip4 = a->v4.sin_addr.s_addr; + + memset(a, 0, sizeof(*a)); + a->len = sizeof(a->v6); + a->v6.sin6_family = AF_INET6; + a->v6.sin6_port = port; + a->v6.sin6_addr.s6_addr[10] = 0xff; + a->v6.sin6_addr.s6_addr[11] = 0xff; + memcpy(&a->v6.sin6_addr.s6_addr[12], &ip4, sizeof(ip4)); +} + +static __u16 inet_port(const struct sockaddr_inet *a) +{ + switch (a->sa.sa_family) { + case AF_INET: + return ntohs(a->v4.sin_port); + case AF_INET6: + return ntohs(a->v6.sin6_port); + default: + return 0; + } +} + FIXTURE(ip_local_port_range) {}; FIXTURE_SETUP(ip_local_port_range) @@ -460,4 +545,444 @@ TEST_F(ip_local_port_range, get_port_range) ASSERT_TRUE(!err) TH_LOG("close failed"); } +FIXTURE(tcp_port_reuse__no_ip_conflict) {}; +FIXTURE_SETUP(tcp_port_reuse__no_ip_conflict) {} +FIXTURE_TEARDOWN(tcp_port_reuse__no_ip_conflict) {} + +FIXTURE_VARIANT(tcp_port_reuse__no_ip_conflict) { + int af_one; + const char *ip_one; + int af_two; + const char *ip_two; +}; + +FIXTURE_VARIANT_ADD(tcp_port_reuse__no_ip_conflict, ipv4) { + .af_one = AF_INET, + .ip_one = "127.0.0.1", + .af_two = AF_INET, + .ip_two = "127.0.0.2", +}; + +FIXTURE_VARIANT_ADD(tcp_port_reuse__no_ip_conflict, ipv6_v4mapped) { + .af_one = AF_INET6, + .ip_one = "::ffff:127.0.0.1", + .af_two = AF_INET, + .ip_two = "127.0.0.2", +}; + +FIXTURE_VARIANT_ADD(tcp_port_reuse__no_ip_conflict, ipv6) { + .af_one = AF_INET6, + .ip_one = "2001:db8::1", + .af_two = AF_INET6, + .ip_two = "2001:db8::2", +}; + +/* Check that a connected socket, which is using IP_LOCAL_PORT_RANGE to relax + * port search restrictions at connect() time, can share a local port with a + * listening socket bound to a different IP. + */ +TEST_F(tcp_port_reuse__no_ip_conflict, share_port_with_listening_socket) +{ + const typeof(variant) v = variant; + struct sockaddr_inet addr; + __close_fd int ln = -1; + __close_fd int c = -1; + __close_fd int p = -1; + __u32 range; + int r; + + /* Listen on :40000 */ + ln = socket(v->af_one, SOCK_STREAM, 0); + ASSERT_GE(ln, 0) TH_LOG("socket"); + + r = setsockopt(ln, SOL_SOCKET, SO_REUSEADDR, &ONE, sizeof(ONE)); + ASSERT_EQ(r, 0) TH_LOG("setsockopt(SO_REUSEADDR)"); + + make_inet_addr(v->af_one, v->ip_one, 40000, &addr); + r = bind(ln, &addr.sa, addr.len); + ASSERT_EQ(r, 0) TH_LOG("bind(:40000)"); + + r = listen(ln, 1); + ASSERT_EQ(r, 0) TH_LOG("listen"); + + /* Connect from :40000 to :40000 */ + c = socket(v->af_two, SOCK_STREAM, 0); + ASSERT_GE(c, 0) TH_LOG("socket"); + + r = setsockopt(c, SOL_IP, IP_BIND_ADDRESS_NO_PORT, &ONE, sizeof(ONE)); + ASSERT_EQ(r, 0) TH_LOG("setsockopt(IP_BIND_ADDRESS_NO_PORT)"); + + range = pack_port_range(40000, 40000); + r = setsockopt(c, SOL_IP, IP_LOCAL_PORT_RANGE, &range, sizeof(range)); + ASSERT_EQ(r, 0) TH_LOG("setsockopt(IP_LOCAL_PORT_RANGE)"); + + make_inet_addr(v->af_two, v->ip_two, 0, &addr); + r = bind(c, &addr.sa, addr.len); + ASSERT_EQ(r, 0) TH_LOG("bind(:0)"); + + make_inet_addr(v->af_one, v->ip_one, 40000, &addr); + if (is_v4mapped(&addr)) + v4mapped_to_ipv4(&addr); + r = connect(c, &addr.sa, addr.len); + EXPECT_EQ(r, 0) TH_LOG("connect(:40000)"); + EXPECT_EQ(get_sock_port(c), 40000); +} + +/* Check that a connected socket, which is using IP_LOCAL_PORT_RANGE to relax + * port search restrictions at connect() time, can share a local port with + * another connected socket bound to a different IP without + * IP_BIND_ADDRESS_NO_PORT enabled. + */ +TEST_F(tcp_port_reuse__no_ip_conflict, share_port_with_connected_socket) +{ + const typeof(variant) v = variant; + struct sockaddr_inet dst = {}; + struct sockaddr_inet src = {}; + __close_fd int ln = -1; + __close_fd int c1 = -1; + __close_fd int c2 = -1; + __u32 range; + __u16 port; + int r; + + /* Listen on wildcard. Same family as . */ + ln = socket(v->af_two, SOCK_STREAM, 0); + ASSERT_GE(ln, 0) TH_LOG("socket"); + + r = setsockopt(ln, SOL_SOCKET, SO_REUSEADDR, &ONE, sizeof(ONE)); + ASSERT_EQ(r, 0) TH_LOG("setsockopt(SO_REUSEADDR"); + + r = listen(ln, 2); + ASSERT_EQ(r, 0) TH_LOG("listen"); + + dst.len = sizeof(dst.ss); + r = getsockname(ln, &dst.sa, &dst.len); + ASSERT_EQ(r, 0) TH_LOG("getsockname"); + + /* Connect from but without IP_BIND_ADDRESS_NO_PORT */ + c1 = socket(v->af_one, SOCK_STREAM, 0); + ASSERT_GE(c1, 0) TH_LOG("socket"); + + make_inet_addr(v->af_one, v->ip_one, 0, &src); + r = bind(c1, &src.sa, src.len); + ASSERT_EQ(r, 0) TH_LOG("bind"); + + if (src.sa.sa_family == AF_INET6 && dst.sa.sa_family == AF_INET) + ipv4_to_v4mapped(&dst); + r = connect(c1, &dst.sa, dst.len); + ASSERT_EQ(r, 0) TH_LOG("connect"); + + src.len = sizeof(src.ss); + r = getsockname(c1, &src.sa, &src.len); + ASSERT_EQ(r, 0) TH_LOG("getsockname"); + + /* Connect from : with IP_BIND_ADDRESS_NO_PORT */ + c2 = socket(v->af_two, SOCK_STREAM, 0); + ASSERT_GE(c2, 0) TH_LOG("socket"); + + r = setsockopt(c2, SOL_IP, IP_BIND_ADDRESS_NO_PORT, &ONE, sizeof(ONE)); + ASSERT_EQ(r, 0) TH_LOG("setsockopt(IP_BIND_ADDRESS_NO_PORT)"); + + port = inet_port(&src); + range = pack_port_range(port, port); + r = setsockopt(c2, SOL_IP, IP_LOCAL_PORT_RANGE, &range, sizeof(range)); + ASSERT_EQ(r, 0) TH_LOG("setsockopt(IP_LOCAL_PORT_RANGE)"); + + make_inet_addr(v->af_two, v->ip_two, 0, &src); + r = bind(c2, &src.sa, src.len); + ASSERT_EQ(r, 0) TH_LOG("bind"); + + if (is_v4mapped(&dst)) + v4mapped_to_ipv4(&dst); + r = connect(c2, &dst.sa, dst.len); + EXPECT_EQ(r, 0) TH_LOG("connect"); + EXPECT_EQ(get_sock_port(c2), port); +} + +/* Check that a connected socket, which is using IP_LOCAL_PORT_RANGE to relax + * port search restrictions at connect() time, can share a local port with an + * IPv6 wildcard socket which is not dualstack (v6-only). + */ +TEST(tcp_port_reuse__no_ip_conflict_wildcard_v6only) +{ + struct sockaddr_inet addr; + __close_fd int ln4 = -1; + __close_fd int ln6 = -1; + __close_fd int c = -1; + __u32 range; + int r; + + /* Listen on [::]:40000 (v6only) */ + ln6 = socket(AF_INET6, SOCK_STREAM, 0); + ASSERT_GE(ln6, 0) TH_LOG("socket"); + + r = setsockopt(ln6, SOL_SOCKET, SO_REUSEADDR, &ONE, sizeof(ONE)); + ASSERT_EQ(r, 0) TH_LOG("setsockopt(SO_REUSEADDR)"); + + r = setsockopt(ln6, IPPROTO_IPV6, IPV6_V6ONLY, &ONE, sizeof(ONE)); + ASSERT_EQ(r, 0) TH_LOG("setsockopt(IPV6_V6ONLY)"); + + make_inet_addr(AF_INET6, "::", 40000, &addr); + r = bind(ln6, &addr.sa, addr.len); + ASSERT_EQ(r, 0) TH_LOG("bind([::]:40000)"); + + r = listen(ln6, 1); + ASSERT_EQ(r, 0) TH_LOG("listen"); + + /* Listen on 127.0.0.1:30000 */ + ln4 = socket(AF_INET, SOCK_STREAM, 0); + ASSERT_GE(ln4, 0) TH_LOG("socket"); + + r = setsockopt(ln4, SOL_SOCKET, SO_REUSEADDR, &ONE, sizeof(ONE)); + ASSERT_EQ(r, 0) TH_LOG("setsockopt(SO_REUSEADDR)"); + + make_inet_addr(AF_INET, "127.0.0.1", 30000, &addr); + r = bind(ln4, &addr.sa, addr.len); + ASSERT_EQ(r, 0) TH_LOG("bind(127.0.0.1:30000)"); + + r = listen(ln4, 1); + ASSERT_EQ(r, 0) TH_LOG("listen"); + + /* Connect from 127.0.0.1:40000 to 127.0.0.1:30000*/ + c = socket(AF_INET, SOCK_STREAM, 0); + ASSERT_GE(c, 0) TH_LOG("socket"); + + range = pack_port_range(40000, 40000); + r = setsockopt(c, SOL_IP, IP_LOCAL_PORT_RANGE, &range, sizeof(range)); + ASSERT_EQ(r, 0) TH_LOG("setsockopt(IP_LOCAL_PORT_RANGE)"); + + r = connect(c, &addr.sa, addr.len); + EXPECT_EQ(r, 0) TH_LOG("connect(127.0.0.1:30000)"); + EXPECT_EQ(get_sock_port(c), 40000); +} + +/* Check that two sockets can share the local IP and the ephemeral port when the + * destination address differs. + */ +TEST(tcp_port_reuse__no_ip_conflict_with_unique_dst) +{ + struct sockaddr_inet addr; + __close_fd int ln = -1; + __close_fd int c1 = -1; + __close_fd int c2 = -1; + __u32 range; + int r; + + /* Listen on 0.0.0.0:30000 */ + ln = socket(AF_INET, SOCK_STREAM, 0); + ASSERT_GE(ln, 0) TH_LOG("socket"); + + r = setsockopt(ln, SOL_SOCKET, SO_REUSEADDR, &ONE, sizeof(ONE)); + ASSERT_EQ(r, 0) TH_LOG("setsockopt(SO_REUSEADDR)"); + + make_inet_addr(AF_INET, "0.0.0.0", 30000, &addr); + r = bind(ln, &addr.sa, addr.len); + ASSERT_EQ(r, 0) TH_LOG("bind"); + + r = listen(ln, 2); + ASSERT_EQ(r, 0) TH_LOG("listen"); + + /* Connect from 127.0.0.1:40000 to 127.1.1.1:30000 */ + c1 = socket(AF_INET, SOCK_STREAM, 0); + ASSERT_GE(c1, 0) TH_LOG("socket"); + + range = pack_port_range(40000, 40000); + r = setsockopt(c1, SOL_IP, IP_LOCAL_PORT_RANGE, &range, sizeof(range)); + ASSERT_EQ(r, 0) TH_LOG("setsockopt(IP_LOCAL_PORT_RANGE)"); + + make_inet_addr(AF_INET, "127.1.1.1", 30000, &addr); + r = connect(c1, &addr.sa, addr.len); + ASSERT_EQ(r, 0) TH_LOG("connect(127.1.1.1:30000)"); + ASSERT_EQ(get_sock_port(c1), 40000); + + /* Connect from 127.0.0.1:40000 to 127.2.2.2:30000 */ + c2 = socket(AF_INET, SOCK_STREAM, 0); + ASSERT_GE(c2, 0) TH_LOG("socket"); + + range = pack_port_range(40000, 40000); + r = setsockopt(c2, SOL_IP, IP_LOCAL_PORT_RANGE, &range, sizeof(range)); + ASSERT_EQ(r, 0) TH_LOG("setsockopt(IP_LOCAL_PORT_RANGE)"); + + make_inet_addr(AF_INET, "127.2.2.2", 30000, &addr); + r = connect(c2, &addr.sa, addr.len); + EXPECT_EQ(r, 0) TH_LOG("connect(127.1.1.1:30000)"); + EXPECT_EQ(get_sock_port(c2), 40000); +} + +FIXTURE(tcp_port_reuse__ip_conflict) {}; +FIXTURE_SETUP(tcp_port_reuse__ip_conflict) {} +FIXTURE_TEARDOWN(tcp_port_reuse__ip_conflict) {} + +FIXTURE_VARIANT(tcp_port_reuse__ip_conflict) { + int af_one; + const char *ip_one; + int af_two; + const char *ip_two; +}; + +FIXTURE_VARIANT_ADD(tcp_port_reuse__ip_conflict, ipv4) { + .af_one = AF_INET, + .ip_one = "127.0.0.1", + .af_two = AF_INET, + .ip_two = "127.0.0.1", +}; + +FIXTURE_VARIANT_ADD(tcp_port_reuse__ip_conflict, ipv6_v4mapped) { + .af_one = AF_INET6, + .ip_one = "::ffff:127.0.0.1", + .af_two = AF_INET, + .ip_two = "127.0.0.1", +}; + +FIXTURE_VARIANT_ADD(tcp_port_reuse__ip_conflict, ipv6) { + .af_one = AF_INET6, + .ip_one = "2001:db8::1", + .af_two = AF_INET6, + .ip_two = "2001:db8::1", +}; + +FIXTURE_VARIANT_ADD(tcp_port_reuse__ip_conflict, ipv4_wildcard) { + .af_one = AF_INET, + .ip_one = "0.0.0.0", + .af_two = AF_INET, + .ip_two = "127.0.0.1", +}; + +FIXTURE_VARIANT_ADD(tcp_port_reuse__ip_conflict, ipv6_v4mapped_wildcard) { + .af_one = AF_INET6, + .ip_one = "::ffff:0.0.0.0", + .af_two = AF_INET, + .ip_two = "127.0.0.1", +}; + +FIXTURE_VARIANT_ADD(tcp_port_reuse__ip_conflict, ipv6_wildcard) { + .af_one = AF_INET6, + .ip_one = "::", + .af_two = AF_INET6, + .ip_two = "2001:db8::1", +}; + +FIXTURE_VARIANT_ADD(tcp_port_reuse__ip_conflict, dualstack_wildcard) { + .af_one = AF_INET6, + .ip_one = "::", + .af_two = AF_INET6, + .ip_two = "127.0.0.1", +}; + +/* Check that a socket, which using IP_LOCAL_PORT_RANGE to relax local port + * search restrictions at connect() time, can't share a local port with a + * listening socket when there is IP address conflict. + */ +TEST_F(tcp_port_reuse__ip_conflict, cannot_share_port) +{ + const typeof(variant) v = variant; + struct sockaddr_inet dst, src; + __close_fd int ln = -1; + __close_fd int c = -1; + __u32 range; + int r; + + /* Listen on :40000 */ + ln = socket(v->af_one, SOCK_STREAM, 0); + ASSERT_GE(ln, 0) TH_LOG("socket"); + + r = setsockopt(ln, SOL_SOCKET, SO_REUSEADDR, &ONE, sizeof(ONE)); + ASSERT_EQ(r, 0) TH_LOG("setsockopt(SO_REUSEADDR)"); + + make_inet_addr(v->af_one, v->ip_one, 40000, &dst); + r = bind(ln, &dst.sa, dst.len); + ASSERT_EQ(r, 0) TH_LOG("bind(:40000)"); + + r = listen(ln, 1); + ASSERT_EQ(r, 0) TH_LOG("listen"); + + /* Attempt to connect from :40000 */ + c = socket(v->af_two, SOCK_STREAM, 0); + ASSERT_GE(c, 0) TH_LOG("socket"); + + r = setsockopt(c, SOL_IP, IP_BIND_ADDRESS_NO_PORT, &ONE, sizeof(ONE)); + ASSERT_EQ(r, 0) TH_LOG("setsockopt(IP_BIND_ADDRESS_NO_PORT)"); + + range = pack_port_range(40000, 40000); + r = setsockopt(c, SOL_IP, IP_LOCAL_PORT_RANGE, &range, sizeof(range)); + ASSERT_EQ(r, 0) TH_LOG("setsockopt(IP_LOCAL_PORT_RANGE)"); + + make_inet_addr(v->af_two, v->ip_two, 0, &src); + r = bind(c, &src.sa, src.len); + ASSERT_EQ(r, 0) TH_LOG("bind(:40000)"); + + if (is_v4mapped(&dst)) + v4mapped_to_ipv4(&dst); + r = connect(c, &dst.sa, dst.len); + EXPECT_EQ(r, -1) TH_LOG("connect(*:40000)"); + EXPECT_EQ(errno, EADDRNOTAVAIL); +} + +/* Demonstrate that a local IP and port can't be shared any more, even when the + * remote address is unique, after explicitly binding to that port. + */ +TEST(tcp_port_reuse__ip_port_conflict_with_unique_dst_after_bind) +{ + struct sockaddr_inet addr; + __close_fd int ln = -1; + __close_fd int c1 = -1; + __close_fd int c2 = -1; + __u32 range; + int s, r; + + /* Listen on 0.0.0.0:30000 */ + ln = socket(AF_INET, SOCK_STREAM, 0); + ASSERT_GE(ln, 0) TH_LOG("socket"); + + r = setsockopt(ln, SOL_SOCKET, SO_REUSEADDR, &ONE, sizeof(ONE)); + ASSERT_EQ(r, 0) TH_LOG("setsockopt(SO_REUSEADDR)"); + + make_inet_addr(AF_INET, "0.0.0.0", 30000, &addr); + r = bind(ln, &addr.sa, addr.len); + ASSERT_EQ(r, 0) TH_LOG("bind(0.0.0.0:30000)"); + + r = listen(ln, 2); + ASSERT_EQ(r, 0) TH_LOG("listen"); + + /* Connect from 127.0.0.1:40000 to 127.1.1.1:30000 */ + c1 = socket(AF_INET, SOCK_STREAM, 0); + ASSERT_GE(c1, 0) TH_LOG("socket"); + + range = pack_port_range(40000, 40000); + r = setsockopt(c1, SOL_IP, IP_LOCAL_PORT_RANGE, &range, sizeof(range)); + ASSERT_EQ(r, 0) TH_LOG("setsockopt(IP_LOCAL_PORT_RANGE)"); + + make_inet_addr(AF_INET, "127.1.1.1", 30000, &addr); + r = connect(c1, &addr.sa, addr.len); + ASSERT_EQ(r, 0) TH_LOG("connect(127.1.1.1:30000)"); + ASSERT_EQ(get_sock_port(c1), 40000); + + /* Block the port. Bind to 127.9.9.9:40000 and unbind immediately */ + s = socket(AF_INET, SOCK_STREAM, 0); + ASSERT_GE(s, 0) TH_LOG("socket"); + + r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &ONE, sizeof(ONE)); + ASSERT_EQ(r, 0) TH_LOG("setsockopt(SO_REUSEADDR)"); + + make_inet_addr(AF_INET, "127.9.9.9", 40000, &addr); + r = bind(s, &addr.sa, addr.len); + ASSERT_EQ(r, 0) TH_LOG("bind(127.9.9.9:40000)"); + + r = close(s); + ASSERT_EQ(r, 0) TH_LOG("close"); + + /* Connect from 127.0.0.1:40000 to 127.2.2.2:30000 */ + c2 = socket(AF_INET, SOCK_STREAM, 0); + ASSERT_GE(c2, 0) TH_LOG("socket"); + + range = pack_port_range(40000, 40000); + r = setsockopt(c2, SOL_IP, IP_LOCAL_PORT_RANGE, &range, sizeof(range)); + ASSERT_EQ(r, 0) TH_LOG("setsockopt(IP_LOCAL_PORT_RANGE)"); + + make_inet_addr(AF_INET, "127.2.2.2", 30000, &addr); + r = connect(c2, &addr.sa, addr.len); + EXPECT_EQ(r, -1) TH_LOG("connect(127.1.1.1:30000)"); + EXPECT_EQ(errno, EADDRNOTAVAIL); +} + TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/net/ip_local_port_range.sh b/tools/testing/selftests/net/ip_local_port_range.sh index 4ff746db1256..3fc151545b2d 100755 --- a/tools/testing/selftests/net/ip_local_port_range.sh +++ b/tools/testing/selftests/net/ip_local_port_range.sh @@ -1,7 +1,11 @@ -#!/bin/sh +#!/bin/bash # SPDX-License-Identifier: GPL-2.0 -./in_netns.sh \ - sh -c 'sysctl -q -w net.mptcp.enabled=1 && \ - sysctl -q -w net.ipv4.ip_local_port_range="40000 49999" && \ - ./ip_local_port_range' +./in_netns.sh sh <(cat <<-EOF + sysctl -q -w net.mptcp.enabled=1 + sysctl -q -w net.ipv4.ip_local_port_range="40000 49999" + ip -6 addr add dev lo 2001:db8::1/32 nodad + ip -6 addr add dev lo 2001:db8::2/32 nodad + exec ./ip_local_port_range +EOF +) -- 2.43.0