From 1d9166c8c915c14f67612ace8a9449aa3049c2f9 Mon Sep 17 00:00:00 2001 From: "Alex Xu (Hello71)" Date: Sat, 21 May 2022 15:21:25 -0400 Subject: [PATCH] Enable IP_BIND_ADDRESS_NO_PORT if supported Signed-off-by: David Goulet --- changes/ip_bind_address_no_port | 5 +++++ src/core/mainloop/connection.c | 24 ++++++++++++++++++++++++ src/lib/sandbox/sandbox.c | 8 ++++++++ 3 files changed, 37 insertions(+) create mode 100644 changes/ip_bind_address_no_port diff --git a/changes/ip_bind_address_no_port b/changes/ip_bind_address_no_port new file mode 100644 index 0000000000..9c4f712a9e --- /dev/null +++ b/changes/ip_bind_address_no_port @@ -0,0 +1,5 @@ + o Minor features (relays): + - Set the Linux-specific IP_BIND_ADDRESS_NO_PORT option on outgoing + sockets, allowing relays using OutboundBindAddress to make more outgoing + connections than ephemeral ports, as long as they are to separate + destinations. Related to issue 40597; patch by Alex Xu (Hello71). diff --git a/src/core/mainloop/connection.c b/src/core/mainloop/connection.c index f2fc5ea3fb..cf25213cb1 100644 --- a/src/core/mainloop/connection.c +++ b/src/core/mainloop/connection.c @@ -2229,6 +2229,30 @@ connection_connect_sockaddr,(connection_t *conn, */ connection_check_oos(get_n_open_sockets(), 0); + /* From ip(7): Inform the kernel to not reserve an ephemeral port when using + * bind(2) with a port number of 0. The port will later be automatically + * chosen at connect(2) time, in a way that allows sharing a source port as + * long as the 4-tuple is unique. + * + * This is needed for relays using OutboundBindAddresses because the port + * value in the bind address is set to 0. */ +#ifdef IP_BIND_ADDRESS_NO_PORT + static int try_ip_bind_address_no_port = 1; + if (bindaddr && try_ip_bind_address_no_port && + setsockopt(s, SOL_IP, IP_BIND_ADDRESS_NO_PORT, &(int){1}, sizeof(int))) { + if (errno == EINVAL) { + log_notice(LD_NET, "Tor was built with support for " + "IP_BIND_ADDRESS_NO_PORT, but the current kernel " + "doesn't support it. This might cause Tor to run out " + "of ephemeral ports more quickly."); + try_ip_bind_address_no_port = 0; + } else { + log_warn(LD_NET, "Error setting IP_BIND_ADDRESS_NO_PORT on new " + "connection: %s", tor_socket_strerror(errno)); + } + } +#endif + if (bindaddr && bind(s, bindaddr, bindaddr_len) < 0) { *socket_error = tor_socket_errno(s); log_warn(LD_NET,"Error binding network socket: %s", diff --git a/src/lib/sandbox/sandbox.c b/src/lib/sandbox/sandbox.c index cc00d2048f..6800fa062b 100644 --- a/src/lib/sandbox/sandbox.c +++ b/src/lib/sandbox/sandbox.c @@ -954,6 +954,14 @@ sb_setsockopt(scmp_filter_ctx ctx, sandbox_cfg_t *filter) return rc; #endif /* defined(IPV6_V6ONLY) */ +#ifdef IP_BIND_ADDRESS_NO_PORT + rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt), + SCMP_CMP(1, SCMP_CMP_EQ, SOL_IP), + SCMP_CMP(2, SCMP_CMP_EQ, IP_BIND_ADDRESS_NO_PORT)); + if (rc) + return rc; +#endif + return 0; }