From f04dea03082177551776bab56334e1596c846089 Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 9 Aug 2022 01:09:02 +0700 Subject: [PATCH] Add osmo_sock_get_name_multiaddr() It's similar to osmo_sock_get_name() but supports multi-homed SCTP sockets bound to several addresses. Related: OS#5581 Change-Id: If76595ebd1cf26ba904887a36c4cc14a1b5c4521 --- include/osmocom/core/socket.h | 1 + src/socket.c | 98 ++++++++++++++++++++++++++++++++ tests/socket/socket_sctp_test.c | 38 ++++++++++--- tests/socket/socket_sctp_test.ok | 58 ++++++++++++++----- 4 files changed, 172 insertions(+), 23 deletions(-) diff --git a/include/osmocom/core/socket.h b/include/osmocom/core/socket.h index 183220d5a..190f436fb 100644 --- a/include/osmocom/core/socket.h +++ b/include/osmocom/core/socket.h @@ -105,6 +105,7 @@ int osmo_sock_unix_init_ofd(struct osmo_fd *ofd, uint16_t type, uint8_t proto, const char *socket_path, unsigned int flags); char *osmo_sock_get_name(const void *ctx, int fd); +char *osmo_sock_get_name_multiaddr(const void *ctx, int fd); const char *osmo_sock_get_name2(int fd); char *osmo_sock_get_name2_c(const void *ctx, int fd); int osmo_sock_get_name_buf(char *str, size_t str_len, int fd); diff --git a/src/socket.c b/src/socket.c index cd253e927..4c8a9a7ef 100644 --- a/src/socket.c +++ b/src/socket.c @@ -230,6 +230,104 @@ static int multiaddr_snprintf(char* buf, size_t buf_len, const char **hosts, siz talloc_free(ctx); return ret; } + +static int sctp_addr_count_helper(int addr_count, bool local) +{ + if (addr_count == -1) { /* We expect this to fail if corresponding side of the socket is not connected */ + LOGP(DLGLOBAL, LOGL_DEBUG, "Error occured while calling sctp_get%caddrs()\n", local ? 'l' : 'p'); + return -EFAULT; + } + + if (addr_count == 0) { + LOGP(DLGLOBAL, LOGL_DEBUG, "Calling sctp_get%caddrs() on unbound socket\n", local ? 'l' : 'p'); + return -ENOTCONN; + } + + if (addr_count > OSMO_SOCK_MAX_ADDRS) { + LOGP(DLGLOBAL, LOGL_NOTICE, "Got more than %u addresses from sctp_get%caddrs(): result will be truncated\n", + OSMO_SOCK_MAX_ADDRS, local ? 'l' : 'p'); + } + + return addr_count; +} + +static int sctp_multiaddr_print_ctx_helper(const void *ctx, int fd, char *str, size_t strlen, bool local) +{ + struct sockaddr *addrs = NULL; + + /* 2nd parameter 0 for sctp_get?addrs() means "disregard any particular association" */ + int i, addr_count = sctp_addr_count_helper(local ? sctp_getladdrs(fd, 0, &addrs) : sctp_getpaddrs(fd, 0, &addrs), + local); + if (addr_count < 0) { + local ? sctp_freeladdrs(addrs) : sctp_freepaddrs(addrs); + return addr_count; + } + + if (addr_count > OSMO_SOCK_MAX_ADDRS) + addr_count = OSMO_SOCK_MAX_ADDRS; + + struct osmo_sockaddr_str *sa_strs[addr_count]; + + for (i = 0; i < addr_count; i++) { + sa_strs[i] = talloc_zero(ctx, struct osmo_sockaddr_str); + int rc = osmo_sockaddr_str_from_sockaddr(sa_strs[i], (const struct sockaddr_storage *)&addrs[i]); + if (rc < 0) + return rc; + } + + local ? sctp_freeladdrs(addrs) : sctp_freepaddrs(addrs); + + return osmo_sockaddr_strs_to_str(str, strlen, (const struct osmo_sockaddr_str **)sa_strs, addr_count); +} + +/*! Get address/port information on socket in dyn-alloc string like "r=1.2.3.4:5<->l=(6.7.8.9|4.3.2.1):10". + * Multiple addresses (e. g. SCTP) are properly supported. + * \param[in] ctx talloc context from which to allocate string buffer + * \param[in] fd file descriptor of socket + * \returns string identifying the connection of this socket, talloc'd from ctx. + */ +char *osmo_sock_get_name_multiaddr(const void *ctx, int fd) +{ + char portbuf_l[6], portbuf_r[6], + hostbuf_l[OSMO_SOCK_MAX_ADDRS * OSMO_SOCK_NAME_MAXLEN], + hostbuf_r[OSMO_SOCK_MAX_ADDRS * OSMO_SOCK_NAME_MAXLEN]; + int rc; + char *str = NULL; + + /* get local */ + + rc = sctp_multiaddr_print_ctx_helper(ctx, fd, hostbuf_l, sizeof(hostbuf_l), true); + if (rc < 0) { + osmo_talloc_asprintf(ctx, str, ""); + return str; + } + + /* according to https://datatracker.ietf.org/doc/html/rfc4960 multi-homed SCTP has to use the same port number */ + rc = osmo_sock_get_local_ip_port(fd, portbuf_l, sizeof(portbuf_l)); + if (rc < 0) { + osmo_talloc_asprintf(ctx, str, ""); + return str; + } + + /* get remote */ + + rc = sctp_multiaddr_print_ctx_helper(ctx, fd, hostbuf_r, sizeof(hostbuf_r), false); + if (rc < 0) { + osmo_talloc_asprintf(ctx, str, "r=NULL<->l=%s:%s", hostbuf_l, portbuf_l); + return str; + } + + rc = osmo_sock_get_remote_ip_port(fd, portbuf_r, sizeof(portbuf_r)); + if (rc < 0) { + osmo_talloc_asprintf(ctx, str, ""); + return str; + } + + osmo_talloc_asprintf(ctx, str, "r=%s:%s<->l=%s:%s", hostbuf_r, portbuf_r, hostbuf_l, portbuf_l); + + return str; +} + #endif /* HAVE_LIBSCTP */ static int osmo_sock_init_tail(int fd, uint16_t type, unsigned int flags) diff --git a/tests/socket/socket_sctp_test.c b/tests/socket/socket_sctp_test.c index b318fe5d1..c9fdc64eb 100644 --- a/tests/socket/socket_sctp_test.c +++ b/tests/socket/socket_sctp_test.c @@ -61,7 +61,8 @@ static int test_sockinit2_multiaddr(const char **addrv4_loc, const char **addrv6 int listen_fd_v4, listen_fd_v6; int listen_port_v4, listen_port_v6; - printf("Checking osmo_sock_init2_multiaddr() with bind to a random local SCTP IPv4 port\n"); + printf("\n[%zu] Checking osmo_sock_init2_multiaddr() with bind to a random local SCTP IPv4 port:\n", + addrv4_size); listen_fd_v4 = osmo_sock_init2_multiaddr(AF_INET, SOCK_STREAM, IPPROTO_SCTP, addrv4_loc, addrv4_size, 0, @@ -71,9 +72,12 @@ static int test_sockinit2_multiaddr(const char **addrv4_loc, const char **addrv6 rc = fcntl(listen_fd_v4, F_GETFL); OSMO_ASSERT(!(rc & O_NONBLOCK)); + printf("Bound on %s\n", osmo_sock_get_name_multiaddr(ctx, listen_fd_v4)); + listen_port_v4 = sock_get_local_port(listen_fd_v4, false); - printf("Checking osmo_sock_init2_multiaddr() with bind to a random local SCTP IPv6 port\n"); + printf("\n[%zu] Checking osmo_sock_init2_multiaddr() with bind to a random local SCTP IPv6 port:\n", + addrv6_size); listen_fd_v6 = osmo_sock_init2_multiaddr(AF_INET6, SOCK_STREAM, IPPROTO_SCTP, addrv6_loc, addrv6_size, 0, @@ -83,9 +87,11 @@ static int test_sockinit2_multiaddr(const char **addrv4_loc, const char **addrv6 rc = fcntl(listen_fd_v6, F_GETFL); OSMO_ASSERT(!(rc & O_NONBLOCK)); + printf("Bound on %s\n", osmo_sock_get_name_multiaddr(ctx, listen_fd_v6)); + listen_port_v6 = sock_get_local_port(listen_fd_v6, true); - printf("Checking osmo_sock_init2_multiaddr() for OSMO_SOCK_F_NONBLOCK\n"); + printf("\n[%zu] Checking osmo_sock_init2_multiaddr() for OSMO_SOCK_F_NONBLOCK:\n", addrv4_size); fd = osmo_sock_init2_multiaddr(AF_INET, SOCK_STREAM, IPPROTO_SCTP, addrv4_loc, addrv4_size, 0, NULL, 0, 0, OSMO_SOCK_F_BIND|OSMO_SOCK_F_NONBLOCK); @@ -93,6 +99,9 @@ static int test_sockinit2_multiaddr(const char **addrv4_loc, const char **addrv6 /* expect it to be blocking */ rc = fcntl(fd, F_GETFL); OSMO_ASSERT(rc & O_NONBLOCK); + + printf("Bound on %s\n", osmo_sock_get_name_multiaddr(ctx, fd)); + close(fd); printf("Checking osmo_sock_init2_multiaddr() for invalid flags\n"); @@ -101,13 +110,15 @@ static int test_sockinit2_multiaddr(const char **addrv4_loc, const char **addrv6 NULL, 0, 0, 0); OSMO_ASSERT(fd < 0); - printf("Checking osmo_sock_init2_multiaddr() for combined BIND + CONNECT\n"); + printf("\n[%zu] Checking osmo_sock_init2_multiaddr() for combined BIND + CONNECT:\n", addrv4_size); fd = osmo_sock_init2_multiaddr(AF_INET, SOCK_STREAM, IPPROTO_SCTP, addrv4_rem, addrv4_size, 0, addrv4_rem, addrv4_size, listen_port_v4, OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT); OSMO_ASSERT(fd >= 0); + printf("Bound on %s\n", osmo_sock_get_name_multiaddr(ctx, fd)); + printf("Checking osmo_sock_init2_multiaddr(AF_UNSPEC) must fail on mixed IPv4 & IPv6\n"); fd = osmo_sock_init2_multiaddr(AF_UNSPEC, SOCK_STREAM, IPPROTO_SCTP, addrv4_rem, addrv4_size, 0, @@ -122,23 +133,27 @@ static int test_sockinit2_multiaddr(const char **addrv4_loc, const char **addrv6 OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT); OSMO_ASSERT(fd < 0); - printf("Checking osmo_sock_init2_multiaddr(AF_UNSPEC) BIND + CONNECT on IPv4\n"); + printf("\n[%zu] Checking osmo_sock_init2_multiaddr(AF_UNSPEC) BIND + CONNECT on IPv4:\n", addrv4_size); fd = osmo_sock_init2_multiaddr(AF_UNSPEC, SOCK_STREAM, IPPROTO_SCTP, addrv4_rem, addrv4_size, 0, addrv4_rem, addrv4_size, listen_port_v4, OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT); OSMO_ASSERT(fd >= 0); - printf("Checking osmo_sock_init2_multiaddr(AF_UNSPEC) BIND + CONNECT on IPv6\n"); + printf("Bound on %s\n", osmo_sock_get_name_multiaddr(ctx, fd)); + + printf("\n[%zu] Checking osmo_sock_init2_multiaddr(AF_UNSPEC) BIND + CONNECT on IPv6:\n", addrv6_size); fd = osmo_sock_init2_multiaddr(AF_UNSPEC, SOCK_STREAM, IPPROTO_SCTP, addrv6_rem, addrv6_size, 0, addrv6_rem, addrv6_size, listen_port_v6, OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT); OSMO_ASSERT(fd >= 0); + printf("Bound on %s\n", osmo_sock_get_name_multiaddr(ctx, fd)); + close(listen_fd_v4); close(listen_fd_v6); - printf("Done\n"); + printf("Done\n\n"); return 0; } @@ -184,20 +199,24 @@ static int test_sockinit2_multiaddr_mixed(void) NULL, 0, 0, OSMO_SOCK_F_BIND); OSMO_ASSERT(listen_fd < 0); - printf("Checking osmo_sock_init2_multiaddr(AF_UNSPEC) BIND on AF_UNSPEC IPv4+v6 succeeds\n"); + printf("\n[%zu] Checking osmo_sock_init2_multiaddr(AF_UNSPEC) BIND on AF_UNSPEC IPv4+v6 succeeds:\n", addr_size); listen_fd = osmo_sock_init2_multiaddr(AF_UNSPEC, SOCK_STREAM, IPPROTO_SCTP, addr_localhost, addr_size, 0, NULL, 0, 0, OSMO_SOCK_F_BIND); OSMO_ASSERT(listen_fd >= 0); + printf("Bound on %s\n", osmo_sock_get_name_multiaddr(ctx, listen_fd)); + listen_port = sock_get_local_port(listen_fd, true); - printf("Checking osmo_sock_init2_multiaddr(AF_UNSPEC) BIND + CONNECT on IPv4\n"); + printf("\n[%zu] Checking osmo_sock_init2_multiaddr(AF_UNSPEC) BIND + CONNECT on IPv4:\n", addr_size); fd = osmo_sock_init2_multiaddr(AF_UNSPEC, SOCK_STREAM, IPPROTO_SCTP, addr_localhost, addr_size, 0, addr_localhost, addr_size, listen_port, OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT); OSMO_ASSERT(fd >= 0); + + printf("Bound on %s\n", osmo_sock_get_name_multiaddr(ctx, fd)); close(fd); close(listen_fd); @@ -222,6 +241,7 @@ int main(int argc, char *argv[]) log_set_print_category(osmo_stderr_target, 0); log_set_print_category_hex(osmo_stderr_target, 0); #ifdef HAVE_LIBSCTP + srand(666); test_sockinit2_multiaddr_simple(); test_sockinit2_multiaddr_several(); test_sockinit2_multiaddr_mixed(); diff --git a/tests/socket/socket_sctp_test.ok b/tests/socket/socket_sctp_test.ok index 7608e8890..ea6174018 100644 --- a/tests/socket/socket_sctp_test.ok +++ b/tests/socket/socket_sctp_test.ok @@ -1,24 +1,54 @@ -Checking osmo_sock_init2_multiaddr() with bind to a random local SCTP IPv4 port -Checking osmo_sock_init2_multiaddr() with bind to a random local SCTP IPv6 port -Checking osmo_sock_init2_multiaddr() for OSMO_SOCK_F_NONBLOCK + +[1] Checking osmo_sock_init2_multiaddr() with bind to a random local SCTP IPv4 port: +Bound on r=NULL<->l=(127.0.0.1|192.168.43.112):37678 + +[1] Checking osmo_sock_init2_multiaddr() with bind to a random local SCTP IPv6 port: +Bound on r=NULL<->l=([::ffff:127.0.0.1]|[0:0:a00:c59e::]|[::1:100:0]|[a00:c59e:0:0:fe80::]):50590 + +[1] Checking osmo_sock_init2_multiaddr() for OSMO_SOCK_F_NONBLOCK: +Bound on r=NULL<->l=(127.0.0.1|192.168.43.112):52990 Checking osmo_sock_init2_multiaddr() for invalid flags -Checking osmo_sock_init2_multiaddr() for combined BIND + CONNECT + +[1] Checking osmo_sock_init2_multiaddr() for combined BIND + CONNECT: +Bound on r=(127.0.0.1|192.168.43.112):37678<->l=127.0.0.1:49762 Checking osmo_sock_init2_multiaddr(AF_UNSPEC) must fail on mixed IPv4 & IPv6 Checking osmo_sock_init2_multiaddr(AF_UNSPEC) must fail on mixed IPv6 & IPv4 -Checking osmo_sock_init2_multiaddr(AF_UNSPEC) BIND + CONNECT on IPv4 -Checking osmo_sock_init2_multiaddr(AF_UNSPEC) BIND + CONNECT on IPv6 + +[1] Checking osmo_sock_init2_multiaddr(AF_UNSPEC) BIND + CONNECT on IPv4: +Bound on r=(127.0.0.1|192.168.43.112):37678<->l=127.0.0.1:47727 + +[1] Checking osmo_sock_init2_multiaddr(AF_UNSPEC) BIND + CONNECT on IPv6: +Bound on r=NULL<->l=[::1]:38193 Done -Checking osmo_sock_init2_multiaddr() with bind to a random local SCTP IPv4 port -Checking osmo_sock_init2_multiaddr() with bind to a random local SCTP IPv6 port -Checking osmo_sock_init2_multiaddr() for OSMO_SOCK_F_NONBLOCK + + +[2] Checking osmo_sock_init2_multiaddr() with bind to a random local SCTP IPv4 port: +Bound on r=NULL<->l=(127.0.0.1|127.0.0.2):37742 + +[1] Checking osmo_sock_init2_multiaddr() with bind to a random local SCTP IPv6 port: +Bound on r=NULL<->l=[::1]:48810 + +[2] Checking osmo_sock_init2_multiaddr() for OSMO_SOCK_F_NONBLOCK: +Bound on r=NULL<->l=(127.0.0.1|127.0.0.2):59017 Checking osmo_sock_init2_multiaddr() for invalid flags -Checking osmo_sock_init2_multiaddr() for combined BIND + CONNECT + +[2] Checking osmo_sock_init2_multiaddr() for combined BIND + CONNECT: +Bound on r=(127.0.0.1|127.0.0.2):37742<->l=(127.0.0.1|127.0.0.2):38654 Checking osmo_sock_init2_multiaddr(AF_UNSPEC) must fail on mixed IPv4 & IPv6 Checking osmo_sock_init2_multiaddr(AF_UNSPEC) must fail on mixed IPv6 & IPv4 -Checking osmo_sock_init2_multiaddr(AF_UNSPEC) BIND + CONNECT on IPv4 -Checking osmo_sock_init2_multiaddr(AF_UNSPEC) BIND + CONNECT on IPv6 + +[2] Checking osmo_sock_init2_multiaddr(AF_UNSPEC) BIND + CONNECT on IPv4: +Bound on r=(127.0.0.1|127.0.0.2):37742<->l=(127.0.0.1|127.0.0.2):40892 + +[1] Checking osmo_sock_init2_multiaddr(AF_UNSPEC) BIND + CONNECT on IPv6: +Bound on r=[::1]:48810<->l=[::1]:41262 Done + Checking osmo_sock_init2_multiaddr(AF_UNSPEC) BIND on AF_INET IPv4+v6 fails Checking osmo_sock_init2_multiaddr(AF_UNSPEC) BIND on AF_INET6 IPv4+v6 fails -Checking osmo_sock_init2_multiaddr(AF_UNSPEC) BIND on AF_UNSPEC IPv4+v6 succeeds -Checking osmo_sock_init2_multiaddr(AF_UNSPEC) BIND + CONNECT on IPv4 + +[3] Checking osmo_sock_init2_multiaddr(AF_UNSPEC) BIND on AF_UNSPEC IPv4+v6 succeeds: +Bound on r=NULL<->l=([::ffff:127.0.0.1]|[0:0:a00:92de::]|[::ffff:7f00:2:0:0]):37598 + +[3] Checking osmo_sock_init2_multiaddr(AF_UNSPEC) BIND + CONNECT on IPv4: +Bound on r=([::ffff:127.0.0.1]|[0:0:a00:92de::]|[::ffff:7f00:2:0:0]):37598<->l=([::ffff:127.0.0.1]|[0:0:a00:95bd::]|[::ffff:7f00:2:0:0]):38333