From 73de5c18de30dec0a0d1f31eee4fe75cbdb7fd8f Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 1 Oct 2022 22:58:26 +0700 Subject: [PATCH] Add osmo_sock_get_name_multiaddr_buf() It's similar to osmo_sock_get_name_buf() 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 | 89 ++++++++++++++++++++++++++++++++ tests/socket/socket_sctp_test.c | 6 ++- tests/socket/socket_sctp_test.ok | 8 ++- 4 files changed, 101 insertions(+), 3 deletions(-) diff --git a/include/osmocom/core/socket.h b/include/osmocom/core/socket.h index 183220d5a..ab6bedd1e 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); +int osmo_sock_get_name_multiaddr_buf(char *buf, size_t buf_len, int fd, bool print_ports); 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 34a163e2d..069b65e6c 100644 --- a/src/socket.c +++ b/src/socket.c @@ -898,6 +898,95 @@ ret_freeaddrinfo_loc: } return rc; } + +static int sctp_multiaddr_print_ctx_helper(struct osmo_strbuf *sb, int fd, bool local) +{ + struct sockaddr *addrs = NULL; + + /* 2nd parameter 0 for sctp_get?addrs() means "disregard any particular association" */ + int rc, addr_count = local ? sctp_getladdrs(fd, 0, &addrs) : sctp_getpaddrs(fd, 0, &addrs); + + 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_DEBUG, "Dropping addresses exceeding limit: %u > %u\n", addr_count, OSMO_SOCK_MAX_ADDRS); + addr_count = OSMO_SOCK_MAX_ADDRS; + } + + rc = multiaddr_strbuf(sb, NULL, 0, &addrs, addr_count); + local ? sctp_freeladdrs(addrs) : sctp_freepaddrs(addrs); + + return rc; +} + +/*! Get address/port information on socket in provided string buffer, like "r=1.2.3.4:5<->l=(6.7.8.9|4.3.2.1):10". + * \param[out] buff Destination string buffer. + * \param[in] buf_len sizeof(buff). + * \param[in] fd File descriptor of socket. + * \param[in] print_ports Flag indicating whether ports should be printed in addition to IP addresses as well. + * \return String length as returned by snprintf(), or negative on error. + */ +int osmo_sock_get_name_multiaddr_buf(char *buff, size_t buf_len, int fd, bool print_ports) +{ + int rc; + char portbuf_l[6], portbuf_r[6], + hostbuf_l[OSMO_SOCK_MAX_ADDRS * OSMO_SOCK_NAME_MAXLEN] = { 0 }, + hostbuf_r[OSMO_SOCK_MAX_ADDRS * OSMO_SOCK_NAME_MAXLEN] = { 0 }; + struct osmo_strbuf sb = { .buf = buff, .len = buf_len }; + struct osmo_strbuf sb_l = { .buf = hostbuf_l, .len = sizeof(hostbuf_l) }; + struct osmo_strbuf sb_r = { .buf = hostbuf_r, .len = sizeof(hostbuf_r) }; + + /* get local */ + rc = sctp_multiaddr_print_ctx_helper(&sb_l, fd, true); + if (rc < 0) { + OSMO_STRBUF_PRINTF(sb, ""); + return rc; + } + + if (print_ports) { + /* 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_STRBUF_PRINTF(sb, ""); + return rc; + } + } + + /* get remote */ + rc = sctp_multiaddr_print_ctx_helper(&sb_r, fd, false); + if (rc < 0) { + if (print_ports) + OSMO_STRBUF_PRINTF(sb, "r=NULL<->l=%s:%s", hostbuf_l, portbuf_l); + else + OSMO_STRBUF_PRINTF(sb, "r=NULL<->l=%s", hostbuf_l); + return 0; + } + + /* assemble */ + if (print_ports) { + rc = osmo_sock_get_remote_ip_port(fd, portbuf_r, sizeof(portbuf_r)); + if (rc < 0) { + OSMO_STRBUF_PRINTF(sb, ""); + return rc; + } + } + + if (print_ports) + OSMO_STRBUF_PRINTF(sb, "r=%s:%s<->l=%s:%s", hostbuf_r, portbuf_r, hostbuf_l, portbuf_l); + else + OSMO_STRBUF_PRINTF(sb, "r=%s<->l=%s", hostbuf_r, hostbuf_l); + + return 0; +} + #endif /* HAVE_LIBSCTP */ /*! Initialize a socket (including bind/connect) diff --git a/tests/socket/socket_sctp_test.c b/tests/socket/socket_sctp_test.c index b318fe5d1..12d25f293 100644 --- a/tests/socket/socket_sctp_test.c +++ b/tests/socket/socket_sctp_test.c @@ -60,6 +60,7 @@ static int test_sockinit2_multiaddr(const char **addrv4_loc, const char **addrv6 int fd, rc; int listen_fd_v4, listen_fd_v6; int listen_port_v4, listen_port_v6; + char buf[OSMO_SOCK_NAME_MAXLEN * OSMO_SOCK_MAX_ADDRS]; printf("Checking osmo_sock_init2_multiaddr() with bind to a random local SCTP IPv4 port\n"); @@ -129,13 +130,16 @@ 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 IPv6\n"); + 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); + rc = osmo_sock_get_name_multiaddr_buf(buf, OSMO_SOCK_NAME_MAXLEN * OSMO_SOCK_MAX_ADDRS, fd, false); + printf("Bound on %s [%d]\n", buf, rc); + close(listen_fd_v4); close(listen_fd_v6); printf("Done\n"); diff --git a/tests/socket/socket_sctp_test.ok b/tests/socket/socket_sctp_test.ok index 7608e8890..90afb162e 100644 --- a/tests/socket/socket_sctp_test.ok +++ b/tests/socket/socket_sctp_test.ok @@ -6,7 +6,9 @@ Checking osmo_sock_init2_multiaddr() for combined BIND + CONNECT 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 IPv6: +Bound on r=NULL<->l=::1 [0] 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 @@ -16,7 +18,9 @@ Checking osmo_sock_init2_multiaddr() for combined BIND + CONNECT 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 IPv6: +Bound on r=::1<->l=::1 [0] 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