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
This commit is contained in:
Max 2022-09-29 11:53:11 +07:00
parent e2ac1a1b4c
commit 3e65693b4d
4 changed files with 143 additions and 15 deletions

View File

@ -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);

View File

@ -254,6 +254,95 @@ static int multiaddr_strbuf(struct osmo_strbuf *sb,
return sb->chars_needed;
}
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, "<error-in-sctp_getladdrs>");
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, "<error-in-osmo_sock_get_local_ip_port>");
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, "<error-in-osmo_sock_get_local_ip_port>");
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 */
static int osmo_sock_init_tail(int fd, uint16_t type, unsigned int flags)

View File

@ -60,8 +60,10 @@ 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");
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,6 +73,9 @@ static int test_sockinit2_multiaddr(const char **addrv4_loc, const char **addrv6
rc = fcntl(listen_fd_v4, F_GETFL);
OSMO_ASSERT(!(rc & O_NONBLOCK));
rc = osmo_sock_get_name_multiaddr_buf(buf, OSMO_SOCK_NAME_MAXLEN * OSMO_SOCK_MAX_ADDRS, listen_fd_v4, false);
printf("Bound on %s [%d]\n", buf, rc);
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");
@ -85,7 +90,7 @@ static int test_sockinit2_multiaddr(const char **addrv4_loc, const char **addrv6
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 +98,10 @@ 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);
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(fd);
printf("Checking osmo_sock_init2_multiaddr() for invalid flags\n");
@ -101,13 +110,16 @@ 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);
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);
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,20 +134,26 @@ 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");
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);
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");

View File

@ -1,22 +1,42 @@
Checking osmo_sock_init2_multiaddr() with bind to a random local SCTP IPv4 port
[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.1.188) [0]
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() for OSMO_SOCK_F_NONBLOCK:
Bound on r=NULL<->l=(127.0.0.1|192.168.1.188) [0]
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.1.188)<->l=127.0.0.1 [0]
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.1.188)<->l=127.0.0.1 [0]
[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
[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) [0]
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() for OSMO_SOCK_F_NONBLOCK:
Bound on r=NULL<->l=(127.0.0.1|127.0.0.2) [0]
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)<->l=(127.0.0.1|127.0.0.2) [0]
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)<->l=(127.0.0.1|127.0.0.2) [0]
[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