This is merely a historical archive of years 2008-2021, before the migration to mailman3.
A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.
lynxis lazus gerrit-no-reply at lists.osmocom.orglynxis lazus has submitted this change. ( https://gerrit.osmocom.org/c/libosmocore/+/19143 ) Change subject: socket: introduce osmo_sock_init_osa & osmo_sock_init_osa_ofd ...................................................................... socket: introduce osmo_sock_init_osa & osmo_sock_init_osa_ofd osmo_sock_init_osa() takes osmo_sockaddr* as local and remote endpoints to setup a socket. Change-Id: I1eece543e3241ef0e095eb63bb831f7c15a16794 --- M include/osmocom/core/socket.h M src/socket.c M tests/socket/socket_test.c M tests/socket/socket_test.err M tests/socket/socket_test.ok 5 files changed, 310 insertions(+), 0 deletions(-) Approvals: Jenkins Builder: Verified laforge: Looks good to me, but someone else must approve pespin: Looks good to me, but someone else must approve lynxis lazus: Looks good to me, approved diff --git a/include/osmocom/core/socket.h b/include/osmocom/core/socket.h index e6a6bd5..4441449 100644 --- a/include/osmocom/core/socket.h +++ b/include/osmocom/core/socket.h @@ -58,6 +58,11 @@ const char **local_hosts, size_t local_hosts_cnt, uint16_t local_port, const char **remote_hosts, size_t remote_hosts_cnt, uint16_t remote_port, unsigned int flags); +int osmo_sock_init_osa(uint16_t type, uint8_t proto, + const struct osmo_sockaddr *local, + const struct osmo_sockaddr *remote, + unsigned int flags); + int osmo_sock_init_ofd(struct osmo_fd *ofd, int family, int type, int proto, const char *host, uint16_t port, unsigned int flags); @@ -65,6 +70,11 @@ const char *local_host, uint16_t local_port, const char *remote_host, uint16_t remote_port, unsigned int flags); +int osmo_sock_init_osa_ofd(struct osmo_fd *ofd, int type, int proto, + const struct osmo_sockaddr *local, + const struct osmo_sockaddr *remote, + unsigned int flags); + int osmo_sock_init_sa(struct sockaddr *ss, uint16_t type, uint8_t proto, unsigned int flags); diff --git a/src/socket.c b/src/socket.c index 56f01dc..6c06063 100644 --- a/src/socket.c +++ b/src/socket.c @@ -34,6 +34,7 @@ #include <osmocom/core/logging.h> #include <osmocom/core/select.h> #include <osmocom/core/socket.h> +#include <osmocom/core/sockaddr_str.h> #include <osmocom/core/talloc.h> #include <osmocom/core/utils.h> @@ -153,6 +154,28 @@ return sfd; } +static int socket_helper_osa(const struct osmo_sockaddr *addr, uint16_t type, uint8_t proto, unsigned int flags) +{ + int sfd, on = 1; + + sfd = socket(addr->u.sa.sa_family, type, proto); + if (sfd == -1) { + LOGP(DLGLOBAL, LOGL_ERROR, + "unable to create socket: %s\n", strerror(errno)); + return sfd; + } + if (flags & OSMO_SOCK_F_NONBLOCK) { + if (ioctl(sfd, FIONBIO, (unsigned char *)&on) < 0) { + LOGP(DLGLOBAL, LOGL_ERROR, + "cannot set this socket unblocking: %s\n", + strerror(errno)); + close(sfd); + sfd = -EINVAL; + } + } + return sfd; +} + #ifdef HAVE_LIBSCTP /* Fill buf with a string representation of the address set, in the form: * buf_len == 0: "()" @@ -434,6 +457,135 @@ return sfd; } +#define _SOCKADDR_TO_STR(dest, sockaddr) do { \ + if (osmo_sockaddr_str_from_sockaddr(&dest, &sockaddr->u.sas)) \ + osmo_strlcpy(dest.ip, "Invalid IP", 11); \ + } while (0) + +/*! Initialize a socket (including bind and/or connect) + * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC + * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM + * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP + * \param[in] local local address + * \param[in] remote remote address + * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT + * \returns socket file descriptor on success; negative on error + * + * This function creates a new socket of the + * \a type and \a proto and optionally binds it to the \a local + * as well as optionally connects it to the \a remote + * depending on the value * of \a flags parameter. + * + * As opposed to \ref osmo_sock_init(), this function allows to combine + * the \ref OSMO_SOCK_F_BIND and \ref OSMO_SOCK_F_CONNECT flags. This + * is useful if you want to connect to a remote host/port, but still + * want to bind that socket to either a specific local alias IP and/or a + * specific local source port. + * + * You must specify either \ref OSMO_SOCK_F_BIND, or \ref + * OSMO_SOCK_F_CONNECT, or both. + * + * If \ref OSMO_SOCK_F_NONBLOCK is specified, the socket will be set to + * non-blocking mode. + */ +int osmo_sock_init_osa(uint16_t type, uint8_t proto, + const struct osmo_sockaddr *local, + const struct osmo_sockaddr *remote, + unsigned int flags) +{ + int sfd = -1, rc, on = 1; + struct osmo_sockaddr_str sastr = {}; + + if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) == 0) { + LOGP(DLGLOBAL, LOGL_ERROR, "invalid: you have to specify either " + "BIND or CONNECT flags\n"); + return -EINVAL; + } + + if ((flags & OSMO_SOCK_F_BIND) && !local) { + LOGP(DLGLOBAL, LOGL_ERROR, "invalid argument. Cannot BIND when local is NULL\n"); + return -EINVAL; + } + + if ((flags & OSMO_SOCK_F_CONNECT) && !remote) { + LOGP(DLGLOBAL, LOGL_ERROR, "invalid argument. Cannot CONNECT when remote is NULL\n"); + return -EINVAL; + } + + if ((flags & OSMO_SOCK_F_BIND) && + (flags & OSMO_SOCK_F_CONNECT) && + local->u.sa.sa_family != remote->u.sa.sa_family) { + LOGP(DLGLOBAL, LOGL_ERROR, "invalid: the family for " + "local and remote endpoint must be same.\n"); + return -EINVAL; + } + + /* figure out local side of socket */ + if (flags & OSMO_SOCK_F_BIND) { + sfd = socket_helper_osa(local, type, proto, flags); + if (sfd < 0) { + _SOCKADDR_TO_STR(sastr, local); + LOGP(DLGLOBAL, LOGL_ERROR, "no suitable local addr found for: %s:%u\n", + sastr.ip, sastr.port); + return -ENODEV; + } + + if (proto != IPPROTO_UDP || (flags & OSMO_SOCK_F_UDP_REUSEADDR)) { + rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, + &on, sizeof(on)); + if (rc < 0) { + _SOCKADDR_TO_STR(sastr, local); + LOGP(DLGLOBAL, LOGL_ERROR, + "cannot setsockopt socket:" + " %s:%u: %s\n", + sastr.ip, sastr.port, + strerror(errno)); + close(sfd); + return rc; + } + } + + if (bind(sfd, &local->u.sa, sizeof(struct osmo_sockaddr)) == -1) { + _SOCKADDR_TO_STR(sastr, local); + LOGP(DLGLOBAL, LOGL_ERROR, "unable to bind socket: %s:%u: %s\n", + sastr.ip, sastr.port, strerror(errno)); + close(sfd); + return -1; + } + } + + /* Reached this point, if OSMO_SOCK_F_BIND then sfd is valid (>=0) or it + was already closed and func returned. If OSMO_SOCK_F_BIND is not + set, then sfd = -1 */ + + /* figure out remote side of socket */ + if (flags & OSMO_SOCK_F_CONNECT) { + if (sfd < 0) { + sfd = socket_helper_osa(remote, type, proto, flags); + if (sfd < 0) { + return sfd; + } + } + + rc = connect(sfd, &remote->u.sa, sizeof(struct osmo_sockaddr)); + if (rc != 0 && errno != EINPROGRESS) { + _SOCKADDR_TO_STR(sastr, remote); + LOGP(DLGLOBAL, LOGL_ERROR, "unable to connect socket: %s:%u: %s\n", + sastr.ip, sastr.port, strerror(errno)); + close(sfd); + return rc; + } + } + + rc = osmo_sock_init_tail(sfd, type, flags); + if (rc < 0) { + close(sfd); + sfd = -1; + } + + return sfd; +} + #ifdef HAVE_LIBSCTP /* Check whether there's an IPv6 Addr as first option of any addrinfo item in the addrinfo set */ @@ -860,6 +1012,13 @@ local_port, remote_host, remote_port, flags)); } +int osmo_sock_init_osa_ofd(struct osmo_fd *ofd, int type, int proto, + const struct osmo_sockaddr *local, + const struct osmo_sockaddr *remote, unsigned int flags) +{ + return osmo_fd_init_ofd(ofd, osmo_sock_init_osa(type, proto, local, remote, flags)); +} + /*! Initialize a socket and fill \ref sockaddr * \param[out] ss socket address (will be filled in) * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM diff --git a/tests/socket/socket_test.c b/tests/socket/socket_test.c index 64e48bc..671177f 100644 --- a/tests/socket/socket_test.c +++ b/tests/socket/socket_test.c @@ -186,6 +186,133 @@ return 0; } +static int test_sockinit_osa(void) +{ + int fd, rc; + char *name; + + struct osmo_sockaddr localhost4 = {}; + struct osmo_sockaddr localhost6 = {}; + struct osmo_sockaddr localhost4_noport = {}; + struct osmo_sockaddr localhost6_noport = {}; + struct osmo_sockaddr any4 = {}; + struct osmo_sockaddr any6 = {}; + struct osmo_sockaddr invalid = {}; + + localhost4.u.sin = (struct sockaddr_in){ + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr("127.0.0.1"), + .sin_port = htons(42), + }; + + localhost6.u.sin6 = (struct sockaddr_in6){ + .sin6_family = AF_INET6, + .sin6_port = htons(42), + }; + inet_pton(AF_INET6, "::1", &localhost6.u.sin6.sin6_addr); + + localhost4_noport = localhost4; + localhost4_noport.u.sin.sin_port = htons(0); + localhost6_noport = localhost6; + localhost6_noport.u.sin6.sin6_port = htons(0); + + any4.u.sin = (struct sockaddr_in){ + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr("0.0.0.0"), + .sin_port = htons(0), + }; + any6.u.sin6 = (struct sockaddr_in6){ + .sin6_family = AF_INET6, + .sin6_port = htons(0), + }; + inet_pton(AF_INET6, "::", &any6.u.sin6.sin6_addr); + + invalid.u.sa.sa_family = AF_UNSPEC; + + printf("Checking osmo_sock_init_osa() with bind to a random local UDP port\n"); + fd = osmo_sock_init_osa(SOCK_DGRAM, IPPROTO_UDP, + &any4, NULL, OSMO_SOCK_F_BIND); + OSMO_ASSERT(fd >= 0); + name = osmo_sock_get_name(ctx, fd); + /* expect it to be not connected. We cannot match on INADDR_ANY, + * as apparently that won't work on FreeBSD if there's only one + * address (e.g. 137.0.0.1) assigned to the entire system, like + * the Osmocom FreeBSD build slaves */ + OSMO_ASSERT(!strncmp(name, "(r=NULL<->", 9)); + talloc_free(name); + /* expect it to be blocking */ + rc = fcntl(fd, F_GETFL); + OSMO_ASSERT(!(rc & O_NONBLOCK)); + close(fd); + + printf("Checking osmo_sock_init_osa() IPv4 for OSMO_SOCK_F_NONBLOCK\n"); + fd = osmo_sock_init_osa(SOCK_DGRAM, IPPROTO_UDP, + &any4, NULL, OSMO_SOCK_F_BIND|OSMO_SOCK_F_NONBLOCK); + OSMO_ASSERT(fd >= 0); + /* expect it to be blocking */ + rc = fcntl(fd, F_GETFL); + OSMO_ASSERT(rc & O_NONBLOCK); + close(fd); + + printf("Checking osmo_sock_init_osa() IPv6 for OSMO_SOCK_F_NONBLOCK\n"); + fd = osmo_sock_init_osa(SOCK_DGRAM, IPPROTO_UDP, + &any6, NULL, OSMO_SOCK_F_BIND|OSMO_SOCK_F_NONBLOCK); + OSMO_ASSERT(fd >= 0); + /* expect it to be blocking */ + rc = fcntl(fd, F_GETFL); + OSMO_ASSERT(rc & O_NONBLOCK); + close(fd); + + printf("Checking osmo_sock_init_osa() for invalid flags\n"); + fd = osmo_sock_init_osa(SOCK_DGRAM, IPPROTO_UDP, &any4, NULL, 0); + OSMO_ASSERT(fd < 0); + + printf("Checking osmo_sock_init_osa() for combined BIND + CONNECT on IPv4\n"); + fd = osmo_sock_init_osa(SOCK_DGRAM, IPPROTO_UDP, &localhost4_noport, &localhost4, + OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT); + OSMO_ASSERT(fd >= 0); + name = osmo_sock_get_name(ctx, fd); +#ifndef __FreeBSD__ + /* For some reason, on the jenkins.osmocom.org build slave with + * FreeBSD 10 inside a jail, it fails. Works fine on laforge's + * FreeBSD 10 or 11 VM at home */ + OSMO_ASSERT(!strncmp(name, "(r=127.0.0.1:42<->l=127.0.0.1", 29)); +#endif + close(fd); + + printf("Checking osmo_sock_init_osa() for combined BIND + CONNECT on IPv6\n"); + fd = osmo_sock_init_osa(SOCK_DGRAM, IPPROTO_UDP, &localhost6_noport, &localhost6, + OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT); + OSMO_ASSERT(fd >= 0); + name = osmo_sock_get_name(ctx, fd); +#ifndef __FreeBSD__ + /* For some reason, on the jenkins.osmocom.org build slave with + * FreeBSD 10 inside a jail, it fails. Works fine on laforge's + * FreeBSD 10 or 11 VM at home */ + OSMO_ASSERT(!strncmp(name, "(r=::1:42<->l=::1", 17)); +#endif + close(fd); + + printf("Checking osmo_sock_init_osa() must fail on mixed IPv4 & IPv6\n"); + fd = osmo_sock_init_osa(SOCK_DGRAM, IPPROTO_UDP, &localhost4_noport, &localhost6, + OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT); + OSMO_ASSERT(fd < 0); + + printf("Checking osmo_sock_init_osa() must fail on mixed IPv6 & IPv4\n"); + fd = osmo_sock_init_osa(SOCK_DGRAM, IPPROTO_UDP, &localhost6_noport, &localhost4, + OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT); + OSMO_ASSERT(fd < 0); + + printf("Checking osmo_sock_init_osa() must fail on invalid osmo_sockaddr\n"); + fd = osmo_sock_init_osa(SOCK_DGRAM, IPPROTO_UDP, &invalid, &localhost4, + OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT); + OSMO_ASSERT(fd < 0); + + talloc_free(name); + + return 0; +} + const struct log_info_cat default_categories[] = { }; @@ -204,6 +331,7 @@ test_sockinit(); test_sockinit2(); test_get_ip_and_port(); + test_sockinit_osa(); return EXIT_SUCCESS; } diff --git a/tests/socket/socket_test.err b/tests/socket/socket_test.err index 0f0f8da..3c198ac 100644 --- a/tests/socket/socket_test.err +++ b/tests/socket/socket_test.err @@ -2,3 +2,7 @@ invalid: you have to specify either BIND or CONNECT flags Unable to find a common protocol (IPv4 or IPv6) for local host: 127.0.0.1 and remote host: ::1. Unable to find a common protocol (IPv4 or IPv6) for local host: ::1 and remote host: 127.0.0.1. +invalid: you have to specify either BIND or CONNECT flags +invalid: the family for local and remote endpoint must be same. +invalid: the family for local and remote endpoint must be same. +invalid: the family for local and remote endpoint must be same. diff --git a/tests/socket/socket_test.ok b/tests/socket/socket_test.ok index 589036f..9a52d44 100644 --- a/tests/socket/socket_test.ok +++ b/tests/socket/socket_test.ok @@ -12,3 +12,12 @@ Checking osmo_sock_init2(AF_UNSPEC) BIND on IPv4 Checking test_get_ip_and_port() for combined BIND + CONNECT on IPv4 Checking test_get_ip_and_port() for combined BIND + CONNECT on IPv6 +Checking osmo_sock_init_osa() with bind to a random local UDP port +Checking osmo_sock_init_osa() IPv4 for OSMO_SOCK_F_NONBLOCK +Checking osmo_sock_init_osa() IPv6 for OSMO_SOCK_F_NONBLOCK +Checking osmo_sock_init_osa() for invalid flags +Checking osmo_sock_init_osa() for combined BIND + CONNECT on IPv4 +Checking osmo_sock_init_osa() for combined BIND + CONNECT on IPv6 +Checking osmo_sock_init_osa() must fail on mixed IPv4 & IPv6 +Checking osmo_sock_init_osa() must fail on mixed IPv6 & IPv4 +Checking osmo_sock_init_osa() must fail on invalid osmo_sockaddr -- To view, visit https://gerrit.osmocom.org/c/libosmocore/+/19143 To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings Gerrit-Project: libosmocore Gerrit-Branch: master Gerrit-Change-Id: I1eece543e3241ef0e095eb63bb831f7c15a16794 Gerrit-Change-Number: 19143 Gerrit-PatchSet: 20 Gerrit-Owner: lynxis lazus <lynxis at fe80.eu> Gerrit-Assignee: pespin <pespin at sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: laforge <laforge at osmocom.org> Gerrit-Reviewer: lynxis lazus <lynxis at fe80.eu> Gerrit-Reviewer: pespin <pespin at sysmocom.de> Gerrit-CC: neels <nhofmeyr at sysmocom.de> Gerrit-MessageType: merged -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20200906/c82e4171/attachment.htm>