lists.osmocom.org
Sign In Sign Up
Manage this list Sign In Sign Up

Keyboard Shortcuts

Thread View

  • j: Next unread message
  • k: Previous unread message
  • j a: Jump to all threads
  • j l: Jump to MailingList overview

gerrit-log

Thread Start a new thread
Download
Threads by month
  • ----- 2026 -----
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2025 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2024 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2023 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2022 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
gerrit-log@lists.osmocom.org

September 2023

  • 1 participants
  • 1947 discussions
[S] Change in libosmo-sccp[master]: ss7: Drop unneeded else branch after early return
by pespin 07 Sep '23

07 Sep '23
pespin has uploaded this change for review. ( https://gerrit.osmocom.org/c/libosmo-sccp/+/34328?usp=email ) Change subject: ss7: Drop unneeded else branch after early return ...................................................................... ss7: Drop unneeded else branch after early return Change-Id: I845be6bfbf36ec8a38697ca92420483a9d41aa6c --- M src/osmo_ss7.c 1 file changed, 11 insertions(+), 3 deletions(-) git pull ssh://gerrit.osmocom.org:29418/libosmo-sccp refs/changes/28/34328/1 diff --git a/src/osmo_ss7.c b/src/osmo_ss7.c index d097fc9..2ba9766 100644 --- a/src/osmo_ss7.c +++ b/src/osmo_ss7.c @@ -1569,10 +1569,9 @@ LOGP(DLSS7, LOGL_NOTICE, "Default IPv6 address %s not supported: %s\n", host, gai_strerror(rc)); return false; - } else { - freeaddrinfo(result); - return true; } + freeaddrinfo(result); + return true; } /* Set default values for local and remote peer hosts if they are not yet set. -- To view, visit https://gerrit.osmocom.org/c/libosmo-sccp/+/34328?usp=email To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings Gerrit-Project: libosmo-sccp Gerrit-Branch: master Gerrit-Change-Id: I845be6bfbf36ec8a38697ca92420483a9d41aa6c Gerrit-Change-Number: 34328 Gerrit-PatchSet: 1 Gerrit-Owner: pespin <pespin(a)sysmocom.de> Gerrit-MessageType: newchange
1 0
0 0
[XL] Change in libosmo-sccp[master]: ss7: Split asp and xua_server into their own files
by pespin 07 Sep '23

07 Sep '23
pespin has uploaded this change for review. ( https://gerrit.osmocom.org/c/libosmo-sccp/+/34329?usp=email ) Change subject: ss7: Split asp and xua_server into their own files ...................................................................... ss7: Split asp and xua_server into their own files This allows having a clearer picture of the several entities involved, as well as simplifying the files. In the future, we can do the same for AS, route, etc. and leave osmo_ss7 for the "instance" object. Change-Id: I3d43268459d61b0b9f9bec34bf31dc0851fa5e48 --- M src/Makefile.am M src/osmo_ss7.c A src/osmo_ss7_asp.c A src/osmo_ss7_xua_srv.c M src/ss7_internal.h 5 files changed, 1,588 insertions(+), 1,449 deletions(-) git pull ssh://gerrit.osmocom.org:29418/libosmo-sccp refs/changes/29/34329/1 diff --git a/src/Makefile.am b/src/Makefile.am index 78336d1..d8f0bb6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -31,7 +31,8 @@ sccp2sua.c sccp_scrc.c sccp_sclc.c sccp_scoc.c \ sccp_user.c sccp_types.c sccp_lbcs.c sccp_scmg.c \ xua_rkm.c xua_shared.c xua_default_lm_fsm.c \ - osmo_ss7.c osmo_ss7_hmrt.c xua_asp_fsm.c xua_as_fsm.c \ + osmo_ss7.c osmo_ss7_asp.c osmo_ss7_hmrt.c osmo_ss7_xua_srv.c \ + xua_asp_fsm.c xua_as_fsm.c \ xua_snm.c osmo_ss7_vty.c sccp_vty.c ipa.c libosmo_sigtran_la_LDFLAGS = -version-info $(LIBVERSION) -no-undefined -export-symbols-regex '^osmo_' libosmo_sigtran_la_LIBADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) \ diff --git a/src/osmo_ss7.c b/src/osmo_ss7.c index 2ba9766..9265898 100644 --- a/src/osmo_ss7.c +++ b/src/osmo_ss7.c @@ -1,6 +1,7 @@ /* Core SS7 Instance/Linkset/Link/AS/ASP Handling */ /* (C) 2015-2017 by Harald Welte <laforge(a)gnumonks.org> + * (C) 2023 by sysmocom s.f.m.c. GmbH <info(a)sysmocom.de> * All Rights Reserved * * SPDX-License-Identifier: GPL-2.0+ @@ -55,7 +56,7 @@ #define MAX_PC_STR_LEN 32 -static bool ss7_initialized = false; +bool ss7_initialized = false; LLIST_HEAD(osmo_ss7_instances); static int32_t next_rctx = 1; @@ -76,33 +77,6 @@ { 0, NULL } }; -struct value_string osmo_ss7_asp_protocol_vals[] = { - { OSMO_SS7_ASP_PROT_NONE, "none" }, - { OSMO_SS7_ASP_PROT_SUA, "sua" }, - { OSMO_SS7_ASP_PROT_M3UA, "m3ua" }, - { OSMO_SS7_ASP_PROT_IPA, "ipa" }, - { 0, NULL } -}; - -const struct value_string osmo_ss7_asp_role_names[] = { - { OSMO_SS7_ASP_ROLE_ASP, "ASP" }, - { OSMO_SS7_ASP_ROLE_SG, "SG" }, - { OSMO_SS7_ASP_ROLE_IPSP, "IPSP" }, - { 0, NULL } -}; - -static int asp_proto_to_ip_proto(enum osmo_ss7_asp_protocol proto) -{ - switch (proto) { - case OSMO_SS7_ASP_PROT_IPA: - return IPPROTO_TCP; - case OSMO_SS7_ASP_PROT_SUA: - case OSMO_SS7_ASP_PROT_M3UA: - default: - return IPPROTO_SCTP; - } -} - int osmo_ss7_find_free_rctx(struct osmo_ss7_instance *inst) { int32_t rctx; @@ -125,60 +99,6 @@ return -1; } -static int _setsockopt_peer_primary_addr(int fd, const struct osmo_sockaddr *saddr) -{ - int rc; - - struct sctp_setpeerprim so_sctp_setpeerprim = {0}; - - /* rfc6458 sec 8: "For the one-to-one style sockets and branched-off one-to-many - * style sockets (see Section 9.2), this association ID parameter is ignored" - */ - - /* NOTE: Requires setting: - * - sysctl net.sctp.addip_enable = 1, otherwise EPERM is returned - * - sysctl net.sctp.auth_enable = 1, RFC 5061 4.2.7 "An implementation supporting this - * extension MUST list the ASCONF,the ASCONF-ACK, and the AUTH chunks in - * its INIT and INIT-ACK parameters." - */ - - so_sctp_setpeerprim.sspp_addr = saddr->u.sas; - rc = setsockopt(fd, IPPROTO_SCTP, SCTP_SET_PEER_PRIMARY_ADDR, - &so_sctp_setpeerprim, sizeof(so_sctp_setpeerprim)); - if (rc < 0) { - char buf[128]; - int err = errno; - strerror_r(err, (char *)buf, sizeof(buf)); - LOGP(DLSS7, LOGL_ERROR, "setsockopt(SCTP_SET_PEER_PRIMARY_ADDR, %s) failed: %s%s\n", - osmo_sockaddr_to_str(saddr), buf, - err == EPERM ? " (EPERM: Make sure you have sysctl 'net.sctp.auth_enable' " - "and 'net.sctp.addip_enable' set to 1)" : ""); - } - return rc; -} - -static int _setsockopt_primary_addr(int fd, const struct osmo_sockaddr *saddr) -{ - int rc; - - struct sctp_prim so_sctp_prim = {0}; - - /* rfc6458 sec 8: "For the one-to-one style sockets and branched-off one-to-many - * style sockets (see Section 9.2), this association ID parameter is ignored" - */ - - so_sctp_prim.ssp_addr = saddr->u.sas; - rc = setsockopt(fd, IPPROTO_SCTP, SCTP_PRIMARY_ADDR, - &so_sctp_prim, sizeof(so_sctp_prim)); - if (rc < 0) { - char buf[128]; - strerror_r(errno, (char *)buf, sizeof(buf)); - LOGP(DLSS7, LOGL_ERROR, "setsockopt(SCTP_PRIMARY_ADDR, %s) failed: %s\n", - osmo_sockaddr_to_str(saddr), buf); - } - return rc; -} - /*********************************************************************** * SS7 Point Code Parsing / Printing ***********************************************************************/ @@ -380,21 +300,6 @@ } } -static const uint16_t prot2port[] = { - [OSMO_SS7_ASP_PROT_NONE] = 0, - [OSMO_SS7_ASP_PROT_SUA] = SUA_PORT, - [OSMO_SS7_ASP_PROT_M3UA] = M3UA_PORT, - [OSMO_SS7_ASP_PROT_IPA] = 5000, -}; - -int osmo_ss7_asp_protocol_port(enum osmo_ss7_asp_protocol prot) -{ - if (prot >= ARRAY_SIZE(prot2port)) - return -EINVAL; - else - return prot2port[prot]; -} - /*********************************************************************** * SS7 Instance ***********************************************************************/ @@ -1208,347 +1113,7 @@ return as->fi->state == XUA_AS_S_DOWN; } -/*********************************************************************** - * SS7 Application Server Process - ***********************************************************************/ - -enum ss7_asp_ctr { - SS7_ASP_CTR_PKT_RX_TOTAL, - SS7_ASP_CTR_PKT_RX_UNKNOWN, - SS7_ASP_CTR_PKT_TX_TOTAL, -}; - -static const struct rate_ctr_desc ss7_asp_rcd[] = { - [SS7_ASP_CTR_PKT_RX_TOTAL] = { "rx:packets:total", "Total number of packets received" }, - [SS7_ASP_CTR_PKT_RX_UNKNOWN] = { "rx:packets:unknown", "Number of packets received for unknown PPID" }, - [SS7_ASP_CTR_PKT_TX_TOTAL] = { "tx:packets:total", "Total number of packets transmitted" }, -}; - -static const struct rate_ctr_group_desc ss7_asp_rcgd = { - .group_name_prefix = "sigtran_asp", - .group_description = "SIGTRAN Application Server Process", - .num_ctr = ARRAY_SIZE(ss7_asp_rcd), - .ctr_desc = ss7_asp_rcd, -}; -static unsigned int g_ss7_asp_rcg_idx; - -void osmo_ss7_asp_peer_init(struct osmo_ss7_asp_peer *peer) -{ - memset(peer, 0, sizeof(*peer)); - peer->idx_primary = -1; -} - -int osmo_ss7_asp_peer_snprintf(char* buf, size_t buf_len, struct osmo_ss7_asp_peer *peer) -{ - int len = 0, offset = 0, rem = buf_len; - int ret, i; - char *after; - char *primary; - - if (buf_len < 3) - return -EINVAL; - - if (peer->host_cnt > 1) { - ret = snprintf(buf, rem, "("); - if (ret < 0) - return ret; - OSMO_SNPRINTF_RET(ret, rem, offset, len); - } - for (i = 0; i < peer->host_cnt; i++) { - primary = (peer->idx_primary == i) ? "*" : ""; - if (peer->host_cnt == 1) - after = ""; - else - after = (i == (peer->host_cnt - 1)) ? ")" : "|"; - ret = snprintf(buf + offset, rem, "%s%s%s", peer->host[i] ? : "0.0.0.0", primary, after); - OSMO_SNPRINTF_RET(ret, rem, offset, len); - } - ret = snprintf(buf + offset, rem, ":%u", peer->port); - if (ret < 0) - return ret; - OSMO_SNPRINTF_RET(ret, rem, offset, len); - - return len; -} - -/*! \brief Set (copy) addresses for a given ASP peer. Previous addresses are freed. - * \param[in] peer Application Server Process peer whose addresses are to be set. - * \param[in] talloc_ctx talloc context used to allocate new addresses. - * \param[in] hosts Array of strings containing IP addresses. - * \param[in] host_cnt Number of strings in hosts - * \param[in] idx_primary Index in "hosts" array marking the SCTP Primary Address, -1 if no explicit Primary Address set - * \returns 0 on success; negative otherwise */ -int osmo_ss7_asp_peer_set_hosts2(struct osmo_ss7_asp_peer *peer, void *talloc_ctx, const char *const*hosts, size_t host_cnt, int idx_primary) -{ - int i = 0; - - if (idx_primary >= (int)host_cnt || idx_primary < -1) - return -EINVAL; - - if (host_cnt > ARRAY_SIZE(peer->host)) - return -EINVAL; - - for (; i < host_cnt; i++) - osmo_talloc_replace_string(talloc_ctx, &peer->host[i], hosts[i]); - for (; i < peer->host_cnt; i++) { - talloc_free(peer->host[i]); - peer->host[i] = NULL; - } - - peer->host_cnt = host_cnt; - peer->idx_primary = idx_primary; - return 0; -} - -/*! \brief Set (copy) addresses for a given ASP peer. Previous addresses are freed. - * \param[in] peer Application Server Process peer whose addresses are to be set. - * \param[in] talloc_ctx talloc context used to allocate new addresses. - * \param[in] hosts Array of strings containing IP addresses. - * \param[in] host_cnt Number of strings in hosts - * \returns 0 on success; negative otherwise */ -int osmo_ss7_asp_peer_set_hosts(struct osmo_ss7_asp_peer *peer, void *talloc_ctx, const char *const*hosts, size_t host_cnt) -{ - return osmo_ss7_asp_peer_set_hosts2(peer, talloc_ctx, hosts, host_cnt, -1); -} - -/* Is string formatted IPv4/v6 addr considered IN(6)ADDR_ANY? */ -static inline bool host_is_ip_anyaddr(const char *host, bool is_v6) -{ - /* NULL addr is resolved as 0.0.0.0 (IPv4) by getaddrinfo(), most - * probably for backward-compatibility reasons. - */ - return is_v6 ? (host && !strcmp(host, "::")) - : (!host || !strcmp(host, "0.0.0.0")); -} - -/*! \brief Append (copy) address to a given ASP peer. Previous addresses are kept. - * \param[in] peer Application Server Process peer the address is appended to. - * \param[in] talloc_ctx talloc context used to allocate new address. - * \param[in] host string containing an IP address. - * \param[in] is_primary_addr whether this IP address is to be added as SCTP Primary Address - * \returns 0 on success; negative otherwise */ -int osmo_ss7_asp_peer_add_host2(struct osmo_ss7_asp_peer *peer, void *talloc_ctx, - const char *host, bool is_primary_addr) -{ - int i; - bool new_is_v6 = osmo_ip_str_type(host) == AF_INET6; - bool new_is_any = host_is_ip_anyaddr(host, new_is_v6); - struct osmo_sockaddr_str addr_str; - - if (osmo_sockaddr_str_from_str(&addr_str, host, 0) < 0) - return -EINVAL; - - if (new_is_any) { - /* Makes no sense to have INET(6)_ANY many times, or INET(6)_ANY - * together with specific addresses, be it of same or different - * IP version: */ - if (peer->host_cnt != 0) - return -EINVAL; - - /* Makes no sense to have INET(6)_ANY as primary: */ - if (is_primary_addr) - return -EINVAL; - - if (peer->host_cnt >= ARRAY_SIZE(peer->host)) - return -EINVAL; - osmo_talloc_replace_string(talloc_ctx, &peer->host[peer->host_cnt], host); - peer->host_cnt++; - return 0; - } - - /* Makes no sense to add specific address to set if INET(6)_ANY - * is already set, be it from same or different IP version: */ - for (i = 0; i < peer->host_cnt; i++) { - bool iter_is_v6 = osmo_ip_str_type(peer->host[i]) == AF_INET6; - if (host_is_ip_anyaddr(peer->host[i], iter_is_v6)) - return -EINVAL; - } - /* Reached this point, no INET(6)_ANY address is set nor we are adding an INET(6)_ANY address. */ - - /* Check if address already exists, and then if primary flags need to be changed: */ - for (i = 0; i < peer->host_cnt; i++) { - struct osmo_sockaddr_str it_addr_str; - bool it_is_primary; - osmo_sockaddr_str_from_str(&it_addr_str, peer->host[i], 0); - - if (osmo_sockaddr_str_cmp(&addr_str, &it_addr_str) != 0) - continue; - it_is_primary = (peer->idx_primary == i); - if (is_primary_addr == it_is_primary) { - /* Nothing to do, return below */ - } else if (is_primary_addr && !it_is_primary) { - /* Mark it as primary: */ - peer->idx_primary = i; - } else { /* if (!is_primary_addr && it_is_primary) { */ - /* mark it as non-primary: */ - peer->idx_primary = -1; - } - return 0; - } - - if (peer->host_cnt >= ARRAY_SIZE(peer->host)) - return -EINVAL; - - osmo_talloc_replace_string(talloc_ctx, &peer->host[peer->host_cnt], host); - if (is_primary_addr) - peer->idx_primary = peer->host_cnt; - peer->host_cnt++; - return 0; -} - -/*! \brief Append (copy) address to a given ASP peer. Previous addresses are kept. - * \param[in] peer Application Server Process peer the address is appended to. - * \param[in] talloc_ctx talloc context used to allocate new address. - * \param[in] host string containing an IP address. - * \returns 0 on success; negative otherwise */ -int osmo_ss7_asp_peer_add_host(struct osmo_ss7_asp_peer *peer, void *talloc_ctx, - const char *host) -{ - return osmo_ss7_asp_peer_add_host2(peer, talloc_ctx, host, false); -} - -static int asp_apply_peer_primary_address(const struct osmo_ss7_asp *asp) -{ - struct osmo_fd *ofd; - struct osmo_sockaddr_str addr_str; - struct osmo_sockaddr addr; - uint16_t local_port; - int rc; - - /* No SCTP Peer Primary Address explicitly configured, do nothing. */ - if (asp->cfg.local.idx_primary == -1) - return 0; - OSMO_ASSERT(asp->cfg.local.idx_primary < asp->cfg.local.host_cnt); - - if (asp->cfg.is_server) - ofd = osmo_stream_srv_get_ofd(asp->server); - else - ofd = osmo_stream_cli_get_ofd(asp->client); - - if (asp->cfg.local.port == 0) { - char port_buf[16]; - osmo_sock_get_local_ip_port(ofd->fd, port_buf, sizeof(port_buf)); - local_port = atoi(port_buf); - } else { - local_port = asp->cfg.local.port; - } - rc = osmo_sockaddr_str_from_str(&addr_str, - asp->cfg.local.host[asp->cfg.local.idx_primary], - local_port); - if (rc < 0) - return rc; - rc = osmo_sockaddr_str_to_sockaddr(&addr_str, &addr.u.sas); - if (rc < 0) - return rc; - LOGPASP(asp, DLSS7, LOGL_INFO, "Set Peer's Primary Address %s\n", - osmo_sockaddr_to_str(&addr)); - rc = _setsockopt_peer_primary_addr(ofd->fd, &addr); - - return rc; -} - -static int asp_apply_primary_address(const struct osmo_ss7_asp *asp) -{ - struct osmo_fd *ofd; - struct osmo_sockaddr_str addr_str; - struct osmo_sockaddr addr; - int rc; - - /* No SCTP Primary Address explicitly configured, do nothing. */ - if (asp->cfg.remote.idx_primary == -1) - return 0; - OSMO_ASSERT(asp->cfg.remote.idx_primary < asp->cfg.remote.host_cnt); - - if (asp->cfg.is_server) - ofd = osmo_stream_srv_get_ofd(asp->server); - else - ofd = osmo_stream_cli_get_ofd(asp->client); - - rc = osmo_sockaddr_str_from_str(&addr_str, - asp->cfg.remote.host[asp->cfg.remote.idx_primary], - asp->cfg.remote.port); - if (rc < 0) - return rc; - rc = osmo_sockaddr_str_to_sockaddr(&addr_str, &addr.u.sas); - if (rc < 0) - return rc; - LOGPASP(asp, DLSS7, LOGL_INFO, "Set Primary Address %s\n", - osmo_sockaddr_to_str(&addr)); - rc = _setsockopt_primary_addr(ofd->fd, &addr); - return rc; -} - -/* Returns whether the address signalled in the SCTP_PEER_ADDR_CHANGE matches - * the user-configured Primary Address. */ -static bool sctp_peer_addr_change_ev_addr_matches_our_primary(const struct osmo_ss7_asp *asp, - const union sctp_notification *notif) -{ - const char *primary_str; - int primary_str_type; - struct osmo_sockaddr ev_addr, primary; - - OSMO_ASSERT(asp->cfg.remote.idx_primary >= 0); - - primary_str = asp->cfg.remote.host[asp->cfg.remote.idx_primary]; - primary_str_type = osmo_ip_str_type(primary_str); - memcpy(&ev_addr.u.sas, &notif->sn_paddr_change.spc_aaddr, sizeof(ev_addr.u.sas)); - - /* This whole switch is to properly compare addresses (take into account v6mapped IPv4 addresses): */ - switch (ev_addr.u.sa.sa_family) { - case AF_INET: - switch (primary_str_type) { - case AF_INET: - primary = ev_addr; /* Copy AF + port */ - inet_pton(AF_INET, primary_str, &primary.u.sin.sin_addr); - return (osmo_sockaddr_cmp(&primary, &ev_addr) == 0); - case AF_INET6: - /* for sure not the same */ - return false; - } - return false; - case AF_INET6: - /* "ev_addr" can either be a IPv6 addr or a v6-mapped IPv4 - * address. Compare both as IPv6 (or v6-mapped IPv4) addresses. */ - primary = ev_addr; /* Copy AF + port */ - inet_pton(AF_INET6, primary_str, &primary.u.sin6.sin6_addr); - return (osmo_sockaddr_cmp(&primary, &ev_addr) == 0); - default: - return false; - } -} - -/* Simple SCTP Path-manager tracking/driving the VTY-user-configured primary - * address against the kernel when assoc state changes: */ -static void asp_handle_sctp_notif_monitor_primary_address(const struct osmo_ss7_asp *asp, - const union sctp_notification *notif) -{ - bool match; - - if (asp->cfg.remote.idx_primary == -1) - return; - if (notif->sn_header.sn_type != SCTP_PEER_ADDR_CHANGE) - return; - - switch (notif->sn_paddr_change.spc_state) { - case SCTP_ADDR_AVAILABLE: - case SCTP_ADDR_ADDED: - case SCTP_ADDR_CONFIRMED: - /* If our primary addr became available/added/confirmed, set it */ - match = sctp_peer_addr_change_ev_addr_matches_our_primary(asp, notif); - if (match) - asp_apply_primary_address(asp); - break; - case SCTP_ADDR_MADE_PRIM: - /* If another primary addr was made primary, overwrite it by setting it again */ - match = sctp_peer_addr_change_ev_addr_matches_our_primary(asp, notif); - if (!match) - asp_apply_primary_address(asp); - default: - break; - } -} - -static bool ipv6_sctp_supported(const char *host, bool bind) +bool ss7_ipv6_sctp_supported(const char *host, bool bind) { int rc; struct addrinfo hints; @@ -1574,46 +1139,6 @@ return true; } -/* Set default values for local and remote peer hosts if they are not yet set. - * \param[in] asp ASP for which to set default hosts. - * \returns true if values where changed, false otherwise. - * - * If the ASP is already started, osmo_ss7_asp_restart() must be called - * afterwards in order to apply the new settings. - * This API is internal, hence doesn't appear in osmo_ss7.h - */ -bool ss7_asp_set_default_peer_hosts(struct osmo_ss7_asp *asp) -{ - bool changed = false; - /* If no local addr was set */ - if (!asp->cfg.local.host_cnt) { - bool rem_has_v4 = false, rem_has_v6 = false; - int i; - for (i = 0; i < asp->cfg.remote.host_cnt; i++) { - if (osmo_ip_str_type(asp->cfg.remote.host[i]) == AF_INET6) - rem_has_v6 = true; - else - rem_has_v4 = true; - } - /* "::" Covers both IPv4 and IPv6, but if only IPv4 - * address are set on the remote side, IPv4 on the local - * side must be set too */ - if (ipv6_sctp_supported("::", true) && !(rem_has_v4 && !rem_has_v6)) - osmo_ss7_asp_peer_add_host(&asp->cfg.local, asp, "::"); - else - osmo_ss7_asp_peer_add_host(&asp->cfg.local, asp, "0.0.0.0"); - changed = true; - } - /* If no remote addr was set */ - if (!asp->cfg.remote.host_cnt) { - osmo_ss7_asp_peer_add_host(&asp->cfg.remote, asp, "127.0.0.1"); - if (ipv6_sctp_supported("::1", false)) - osmo_ss7_asp_peer_add_host(&asp->cfg.remote, asp, "::1"); - changed = true; - } - return changed; -} - struct osmo_ss7_asp * osmo_ss7_asp_find_by_name(struct osmo_ss7_instance *inst, const char *name) { @@ -1627,133 +1152,6 @@ return NULL; } -static uint16_t get_in_port(struct sockaddr *sa) -{ - switch (sa->sa_family) { - case AF_INET: - return (((struct sockaddr_in*)sa)->sin_port); - case AF_INET6: - return (((struct sockaddr_in6*)sa)->sin6_port); - default: - return 0; - } -} - -/* Converts string representation of v4-mappend-on-v6 IP addr to a pure IPv4 address. - * Example: ::ffff:172.18.19.200 => 172.18.19.200 - */ -static void chop_v4_mapped_on_v6_prefix(char* buf) -{ - char *last_colon; - size_t len; - char *first_dot = strchr(buf, '.'); - - if (!first_dot) - return; /* Not an IPv4-mappend-on-v6 string representation, nothing to do */ - last_colon = strrchr(buf, ':'); - if (!last_colon) - return; /* pure IPv4 address, nothing to do */ - - len = strlen(last_colon + 1); - memmove(buf, last_colon + 1, len); - buf[len] = '\0'; -} - -/*! \brief Find an ASP definition matching the local+remote IP/PORT of given fd - * \param[in] fd socket descriptor of given socket - * \returns SS7 ASP in case a matching one is found; NULL otherwise */ -static struct osmo_ss7_asp * -osmo_ss7_asp_find_by_socket_addr(int fd) -{ - struct osmo_ss7_instance *inst; - struct sockaddr_storage sa_l, sa_r; - socklen_t sa_len_l = sizeof(sa_l); - socklen_t sa_len_r = sizeof(sa_r); - char hostbuf_l[64], hostbuf_r[64]; - uint16_t local_port, remote_port; - bool loc_is_v6, rem_is_v6; - int rc; - int i; - - OSMO_ASSERT(ss7_initialized); - /* convert local and remote IP to string */ - rc = getsockname(fd, (struct sockaddr*)&sa_l, &sa_len_l); - if (rc < 0) - return NULL; - rc = getnameinfo((struct sockaddr*)&sa_l, sa_len_l, - hostbuf_l, sizeof(hostbuf_l), - NULL, 0, NI_NUMERICHOST); - if (rc < 0) - return NULL; - local_port = ntohs(get_in_port((struct sockaddr*)&sa_l)); - - rc = getpeername(fd, (struct sockaddr*)&sa_r, &sa_len_r); - if (rc < 0) - return NULL; - rc = getnameinfo((struct sockaddr*)&sa_r, sa_len_r, - hostbuf_r, sizeof(hostbuf_r), - NULL, 0, NI_NUMERICHOST); - if (rc < 0) - return NULL; - remote_port = ntohs(get_in_port((struct sockaddr*)&sa_r)); - - /* If multi-home is used with both IPv4 and IPv6, then the socket is - * AF_INET6, and then returned IPv4 addresses are actually v6mapped ones. - * We need to convert them to IPv4 before matching. - */ - chop_v4_mapped_on_v6_prefix(hostbuf_l); - chop_v4_mapped_on_v6_prefix(hostbuf_r); - loc_is_v6 = osmo_ip_str_type(hostbuf_l) == AF_INET6; - rem_is_v6 = osmo_ip_str_type(hostbuf_r) == AF_INET6; - - /* check all instances for any ASP definition matching the - * address combination of local/remote ip/port */ - llist_for_each_entry(inst, &osmo_ss7_instances, list) { - struct osmo_ss7_asp *asp; - llist_for_each_entry(asp, &inst->asp_list, list) { - if (asp->cfg.local.port != local_port) - continue; - if (asp->cfg.remote.port && asp->cfg.remote.port != remote_port) - continue; - - for (i = 0; i < asp->cfg.local.host_cnt; i++) { - bool iter_is_v6 = osmo_ip_str_type(asp->cfg.local.host[i]) == AF_INET6; - bool iter_is_anyaddr = host_is_ip_anyaddr(asp->cfg.local.host[i], iter_is_v6); - /* "::" (v6) covers "0.0.0.0" (v4), but not otherwise */ - if (iter_is_v6 != loc_is_v6 && - !(iter_is_v6 && iter_is_anyaddr)) - continue; - if (iter_is_anyaddr || - !strcmp(asp->cfg.local.host[i], hostbuf_l)) - break; - } - if (i == asp->cfg.local.host_cnt) - continue; /* didn't match any local.host */ - - /* If no remote host was set, it's probably a server and hence we match any cli src */ - if (asp->cfg.remote.host_cnt) { - for (i = 0; i < asp->cfg.remote.host_cnt; i++) { - bool iter_is_v6 = osmo_ip_str_type(asp->cfg.remote.host[i]) == AF_INET6; - bool iter_is_anyaddr = host_is_ip_anyaddr(asp->cfg.remote.host[i], iter_is_v6); - /* "::" (v6) covers "0.0.0.0" (v4), but not otherwise */ - if (iter_is_v6 != rem_is_v6 && - !(iter_is_v6 && iter_is_anyaddr)) - continue; - if (iter_is_anyaddr || - !strcmp(asp->cfg.remote.host[i], hostbuf_r)) - break; - } - if (i == asp->cfg.remote.host_cnt) - continue; /* didn't match any remote.host */ - } - - return asp; - } - } - - return NULL; -} - /*! \brief Find an ASP that matches the given protocol. * \param[in] as Application Server in which to look for \ref asp * \returns SS7 ASP in case a matching one is found; NULL otherwise */ @@ -1789,38 +1187,6 @@ return asp; } -static struct osmo_ss7_asp * -osmo_ss7_asp_alloc(struct osmo_ss7_instance *inst, const char *name, - uint16_t remote_port, uint16_t local_port, - enum osmo_ss7_asp_protocol proto) -{ - struct osmo_ss7_asp *asp = talloc_zero(inst, struct osmo_ss7_asp); - asp->ctrg = rate_ctr_group_alloc(asp, &ss7_asp_rcgd, g_ss7_asp_rcg_idx++); - if (!asp->ctrg) { - talloc_free(asp); - return NULL; - } - rate_ctr_group_set_name(asp->ctrg, name); - asp->inst = inst; - osmo_ss7_asp_peer_init(&asp->cfg.remote); - asp->cfg.remote.port = remote_port; - osmo_ss7_asp_peer_init(&asp->cfg.local); - asp->cfg.local.port = local_port; - asp->cfg.proto = proto; - asp->cfg.name = talloc_strdup(asp, name); - - asp->cfg.T_defs_lm = talloc_memdup(asp, ss7_asp_lm_timer_defaults, - sizeof(ss7_asp_lm_timer_defaults)); - osmo_tdefs_reset(asp->cfg.T_defs_lm); - - llist_add_tail(&asp->list, &inst->asp_list); - - /* The SUA code internally needs SCCP to work */ - if (proto == OSMO_SS7_ASP_PROT_SUA) - osmo_ss7_ensure_sccp(inst); - return asp; -} - struct osmo_ss7_asp * osmo_ss7_asp_find_or_create(struct osmo_ss7_instance *inst, const char *name, uint16_t remote_port, uint16_t local_port, @@ -1838,630 +1204,9 @@ return asp; } - return osmo_ss7_asp_alloc(inst, name, remote_port, local_port, proto); + return ss7_asp_alloc(inst, name, remote_port, local_port, proto); } -void osmo_ss7_asp_destroy(struct osmo_ss7_asp *asp) -{ - struct osmo_ss7_as *as; - - OSMO_ASSERT(ss7_initialized); - LOGPASP(asp, DLSS7, LOGL_INFO, "Destroying ASP\n"); - - if (asp->server) - osmo_stream_srv_destroy(asp->server); - if (asp->client) - osmo_stream_cli_destroy(asp->client); - if (asp->fi) - osmo_fsm_inst_term(asp->fi, OSMO_FSM_TERM_REQUEST, NULL); - if (asp->xua_server) - llist_del(&asp->siblings); - - /* unlink from all ASs we are part of */ - llist_for_each_entry(as, &asp->inst->as_list, list) { - unsigned int i; - for (i = 0; i < ARRAY_SIZE(as->cfg.asps); i++) { - if (as->cfg.asps[i] == asp) { - as->cfg.asps[i] = NULL; - } - } - } - /* unlink from ss7_instance */ - asp->inst = NULL; - llist_del(&asp->list); - rate_ctr_group_free(asp->ctrg); - /* release memory */ - talloc_free(asp); -} - -static int xua_cli_read_cb(struct osmo_stream_cli *conn); -static int ipa_cli_read_cb(struct osmo_stream_cli *conn); -static int xua_cli_connect_cb(struct osmo_stream_cli *cli); - -int osmo_ss7_asp_restart(struct osmo_ss7_asp *asp) -{ - int rc; - char bufloc[512], bufrem[512]; - - OSMO_ASSERT(ss7_initialized); - osmo_ss7_asp_peer_snprintf(bufloc, sizeof(bufloc), &asp->cfg.local); - osmo_ss7_asp_peer_snprintf(bufrem, sizeof(bufrem), &asp->cfg.remote); - LOGPASP(asp, DLSS7, LOGL_INFO, "Restarting ASP %s, r=%s<->l=%s\n", - asp->cfg.name, bufrem, bufloc); - - if (!asp->cfg.is_server) { - /* We are in client mode now */ - if (asp->server) { - /* if we previously were in server mode, - * destroy it */ - osmo_stream_srv_destroy(asp->server); - asp->server = NULL; - } - if (!asp->client) - asp->client = osmo_stream_cli_create(asp); - if (!asp->client) { - LOGPASP(asp, DLSS7, LOGL_ERROR, "Unable to create stream" - " client for ASP %s\n", asp->cfg.name); - return -1; - } - osmo_stream_cli_set_name(asp->client, asp->cfg.name); - osmo_stream_cli_set_nodelay(asp->client, true); - osmo_stream_cli_set_addrs(asp->client, (const char**)asp->cfg.remote.host, asp->cfg.remote.host_cnt); - osmo_stream_cli_set_port(asp->client, asp->cfg.remote.port); - osmo_stream_cli_set_local_addrs(asp->client, (const char**)asp->cfg.local.host, asp->cfg.local.host_cnt); - osmo_stream_cli_set_local_port(asp->client, asp->cfg.local.port); - osmo_stream_cli_set_proto(asp->client, asp_proto_to_ip_proto(asp->cfg.proto)); - osmo_stream_cli_set_reconnect_timeout(asp->client, 5); - osmo_stream_cli_set_connect_cb(asp->client, xua_cli_connect_cb); - if (asp->cfg.proto == OSMO_SS7_ASP_PROT_IPA) - osmo_stream_cli_set_read_cb(asp->client, ipa_cli_read_cb); - else - osmo_stream_cli_set_read_cb(asp->client, xua_cli_read_cb); - osmo_stream_cli_set_data(asp->client, asp); - rc = osmo_stream_cli_open(asp->client); - if (rc < 0) { - LOGPASP(asp, DLSS7, LOGL_ERROR, "Unable to open stream" - " client for ASP %s, %s ==> %s\n", asp->cfg.name, bufloc, bufrem); - /* we don't return error in here because osmo_stream_cli_open() - will continue to retry (due to timeout being explicitly set with - osmo_stream_cli_set_reconnect_timeout() above) to connect so the error is transient */ - } - } else { - /* We are in server mode now */ - if (asp->client) { - /* if we previously were in client mode, - * destroy it */ - osmo_stream_cli_destroy(asp->client); - asp->client = NULL; - } - /* FIXME: ensure we have a SCTP server */ - LOGPASP(asp, DLSS7, LOGL_NOTICE, "ASP Restart for server " - "not implemented yet!\n"); - } - - /* (re)start the ASP FSM */ - if (asp->fi) - osmo_fsm_inst_term(asp->fi, OSMO_FSM_TERM_REQUEST, NULL); - asp->fi = xua_asp_fsm_start(asp, asp->cfg.role, LOGL_DEBUG); - - return 0; -} - -bool osmo_ss7_asp_active(const struct osmo_ss7_asp *asp) -{ - if (!asp->fi) - return false; - return asp->fi->state == XUA_ASP_S_ACTIVE; -} - -/*********************************************************************** - * libosmo-netif integration for SCTP stream server/client - ***********************************************************************/ - -static int get_logevel_by_sn_type(int sn_type) -{ - switch (sn_type) { - case SCTP_ADAPTATION_INDICATION: - case SCTP_PEER_ADDR_CHANGE: -#ifdef SCTP_AUTHENTICATION_INDICATION - case SCTP_AUTHENTICATION_INDICATION: -#endif -#ifdef SCTP_SENDER_DRY_EVENT - case SCTP_SENDER_DRY_EVENT: -#endif - return LOGL_INFO; - case SCTP_ASSOC_CHANGE: - return LOGL_NOTICE; - case SCTP_SHUTDOWN_EVENT: - case SCTP_PARTIAL_DELIVERY_EVENT: - return LOGL_NOTICE; - case SCTP_SEND_FAILED: - case SCTP_REMOTE_ERROR: - return LOGL_ERROR; - default: - return LOGL_NOTICE; - } -} - -static void log_sctp_notification(struct osmo_ss7_asp *asp, const char *pfx, - union sctp_notification *notif) -{ - int log_level; - - LOGPASP(asp, DLSS7, LOGL_INFO, "%s SCTP NOTIFICATION %u flags=0x%0x\n", - pfx, notif->sn_header.sn_type, - notif->sn_header.sn_flags); - - log_level = get_logevel_by_sn_type(notif->sn_header.sn_type); - - switch (notif->sn_header.sn_type) { - case SCTP_ASSOC_CHANGE: - LOGPASP(asp, DLSS7, log_level, "%s SCTP_ASSOC_CHANGE: %s\n", - pfx, osmo_sctp_assoc_chg_str(notif->sn_assoc_change.sac_state)); - break; - case SCTP_PEER_ADDR_CHANGE: - { - char addr_str[INET6_ADDRSTRLEN + 10]; - struct sockaddr_storage sa = notif->sn_paddr_change.spc_aaddr; - osmo_sockaddr_to_str_buf(addr_str, sizeof(addr_str), (const struct osmo_sockaddr *)&sa); - LOGPASP(asp, DLSS7, log_level, "%s SCTP_PEER_ADDR_CHANGE: %s %s err=%s\n", - pfx, osmo_sctp_paddr_chg_str(notif->sn_paddr_change.spc_state), addr_str, - (notif->sn_paddr_change.spc_state == SCTP_ADDR_UNREACHABLE) ? - osmo_sctp_sn_error_str(notif->sn_paddr_change.spc_error) : "None"); - } - break; - default: - LOGPASP(asp, DLSS7, log_level, "%s %s\n", - pfx, osmo_sctp_sn_type_str(notif->sn_header.sn_type)); - break; - } -} - -/* netif code tells us we can read something from the socket */ -static int ipa_srv_conn_cb(struct osmo_stream_srv *conn) -{ - struct osmo_fd *ofd = osmo_stream_srv_get_ofd(conn); - struct osmo_ss7_asp *asp = osmo_stream_srv_get_data(conn); - struct msgb *msg = NULL; - int rc; - - /* read IPA message from socket and process it */ - rc = ipa_msg_recv_buffered(ofd->fd, &msg, &asp->pending_msg); - LOGPASP(asp, DLSS7, LOGL_DEBUG, "%s(): ipa_msg_recv_buffered() returned %d\n", - __func__, rc); - if (rc <= 0) { - if (rc == -EAGAIN) { - /* more data needed */ - return 0; - } - osmo_stream_srv_destroy(conn); - return rc; - } - if (osmo_ipa_process_msg(msg) < 0) { - LOGPASP(asp, DLSS7, LOGL_ERROR, "Bad IPA message\n"); - osmo_stream_srv_destroy(conn); - msgb_free(msg); - return -1; - } - msg->dst = asp; - - return ipa_rx_msg(asp, msg, ofd->fd & 0xf); -} - -/* netif code tells us we can read something from the socket */ -static int xua_srv_conn_cb(struct osmo_stream_srv *conn) -{ - struct osmo_ss7_asp *asp = osmo_stream_srv_get_data(conn); - struct msgb *msg = m3ua_msgb_alloc("xUA Server Rx"); - unsigned int ppid; - int flags; - int rc; - - if (!msg) - return -ENOMEM; - - /* read xUA message from socket and process it */ - rc = osmo_stream_srv_recv(conn, msg); - flags = msgb_sctp_msg_flags(msg); - - LOGPASP(asp, DLSS7, LOGL_DEBUG, "%s(): sctp_recvmsg() returned %d (flags=0x%x)\n", - __func__, rc, flags); - - if (flags & OSMO_STREAM_SCTP_MSG_FLAGS_NOTIFICATION) { - union sctp_notification *notif = (union sctp_notification *) msgb_data(msg); - log_sctp_notification(asp, "xUA CLNT", notif); - asp_handle_sctp_notif_monitor_primary_address(asp, notif); - - switch (notif->sn_header.sn_type) { - case SCTP_ASSOC_CHANGE: - if (notif->sn_assoc_change.sac_state == SCTP_RESTART) - xua_asp_send_xlm_prim_simple(asp, OSMO_XLM_PRIM_M_SCTP_RESTART, - PRIM_OP_INDICATION); - default: - break; - } - - if (rc == 0) { - osmo_stream_srv_destroy(conn); - rc = -EBADF; - } else { - rc = 0; - } - goto out; - } - if (rc < 0) { - osmo_stream_srv_destroy(conn); - rc = -EBADF; - goto out; - } else if (rc == 0) { - osmo_stream_srv_destroy(conn); - rc = -EBADF; - goto out; - } - - ppid = msgb_sctp_ppid(msg); - msg->dst = asp; - rate_ctr_inc2(asp->ctrg, SS7_ASP_CTR_PKT_RX_TOTAL); - - if (ppid == SUA_PPID && asp->cfg.proto == OSMO_SS7_ASP_PROT_SUA) - rc = sua_rx_msg(asp, msg); - else if (ppid == M3UA_PPID && asp->cfg.proto == OSMO_SS7_ASP_PROT_M3UA) - rc = m3ua_rx_msg(asp, msg); - else - rc = ss7_asp_rx_unknown(asp, ppid, msg); - -out: - msgb_free(msg); - return rc; -} - -/* client has established SCTP connection to server */ -static int xua_cli_connect_cb(struct osmo_stream_cli *cli) -{ - struct osmo_fd *ofd = osmo_stream_cli_get_ofd(cli); - struct osmo_ss7_asp *asp = osmo_stream_cli_get_data(cli); - int rc = 0; - - /* update the socket name */ - talloc_free(asp->sock_name); - asp->sock_name = osmo_sock_get_name(asp, ofd->fd); - - LOGPASP(asp, DLSS7, LOGL_INFO, "Client connected %s\n", asp->sock_name); - - /* Now that we have the conn in place, the local/remote addresses are - * fed and the local port is known for sure. Apply SCTP Primary addresses - * if needed: - */ - if (asp->cfg.proto != OSMO_SS7_ASP_PROT_IPA) { - rc = asp_apply_peer_primary_address(asp); - rc = asp_apply_primary_address(asp); - } - - if (asp->lm && asp->lm->prim_cb) { - /* Notify layer manager that a connection has been - * established */ - xua_asp_send_xlm_prim_simple(asp, OSMO_XLM_PRIM_M_SCTP_ESTABLISH, PRIM_OP_INDICATION); - } else { - /* directly as the ASP FSM to start by sending an ASP-UP ... */ - osmo_fsm_inst_dispatch(asp->fi, XUA_ASP_E_M_ASP_UP_REQ, NULL); - } - - return rc; -} - -static void xua_cli_close(struct osmo_stream_cli *cli) -{ - struct osmo_ss7_asp *asp = osmo_stream_cli_get_data(cli); - - osmo_stream_cli_close(cli); - osmo_fsm_inst_dispatch(asp->fi, XUA_ASP_E_SCTP_COMM_DOWN_IND, asp); - /* send M-SCTP_RELEASE.ind to XUA Layer Manager */ - xua_asp_send_xlm_prim_simple(asp, OSMO_XLM_PRIM_M_SCTP_RELEASE, PRIM_OP_INDICATION); -} - -static void xua_cli_close_and_reconnect(struct osmo_stream_cli *cli) -{ - xua_cli_close(cli); - osmo_stream_cli_reconnect(cli); -} - -/* read call-back for IPA/SCCPlite socket */ -static int ipa_cli_read_cb(struct osmo_stream_cli *conn) -{ - struct osmo_fd *ofd = osmo_stream_cli_get_ofd(conn); - struct osmo_ss7_asp *asp = osmo_stream_cli_get_data(conn); - struct msgb *msg = NULL; - int rc; - - /* read IPA message from socket and process it */ - rc = ipa_msg_recv_buffered(ofd->fd, &msg, &asp->pending_msg); - LOGPASP(asp, DLSS7, LOGL_DEBUG, "%s(): ipa_msg_recv_buffered() returned %d\n", - __func__, rc); - if (rc <= 0) { - if (rc == -EAGAIN) { - /* more data needed */ - return 0; - } - xua_cli_close_and_reconnect(conn); - return rc; - } - if (osmo_ipa_process_msg(msg) < 0) { - LOGPASP(asp, DLSS7, LOGL_ERROR, "Bad IPA message\n"); - xua_cli_close_and_reconnect(conn); - msgb_free(msg); - return -1; - } - msg->dst = asp; - rate_ctr_inc2(asp->ctrg, SS7_ASP_CTR_PKT_RX_TOTAL); - return ipa_rx_msg(asp, msg, ofd->fd & 0xf); -} - -static int xua_cli_read_cb(struct osmo_stream_cli *conn) -{ - struct osmo_ss7_asp *asp = osmo_stream_cli_get_data(conn); - struct msgb *msg = m3ua_msgb_alloc("xUA Client Rx"); - unsigned int ppid; - int flags; - int rc; - - if (!msg) - return -ENOMEM; - - /* read xUA message from socket and process it */ - rc = osmo_stream_cli_recv(conn, msg); - flags = msgb_sctp_msg_flags(msg); - - LOGPASP(asp, DLSS7, LOGL_DEBUG, "%s(): sctp_recvmsg() returned %d (flags=0x%x)\n", - __func__, rc, flags); - - if (flags & OSMO_STREAM_SCTP_MSG_FLAGS_NOTIFICATION) { - union sctp_notification *notif = (union sctp_notification *) msgb_data(msg); - log_sctp_notification(asp, "xUA CLNT", notif); - asp_handle_sctp_notif_monitor_primary_address(asp, notif); - - switch (notif->sn_header.sn_type) { - case SCTP_ASSOC_CHANGE: - if (notif->sn_assoc_change.sac_state == SCTP_RESTART) - xua_asp_send_xlm_prim_simple(asp, OSMO_XLM_PRIM_M_SCTP_RESTART, - PRIM_OP_INDICATION); - default: - break; - } - - if (rc == 0) - xua_cli_close_and_reconnect(conn); - rc = 0; - goto out; - } - if (rc < 0) { - xua_cli_close_and_reconnect(conn); - goto out; - } else if (rc == 0) { - xua_cli_close_and_reconnect(conn); - goto out; - } - - ppid = msgb_sctp_ppid(msg); - msg->dst = asp; - rate_ctr_inc2(asp->ctrg, SS7_ASP_CTR_PKT_RX_TOTAL); - - if (ppid == SUA_PPID && asp->cfg.proto == OSMO_SS7_ASP_PROT_SUA) - rc = sua_rx_msg(asp, msg); - else if (ppid == M3UA_PPID && asp->cfg.proto == OSMO_SS7_ASP_PROT_M3UA) - rc = m3ua_rx_msg(asp, msg); - else - rc = ss7_asp_rx_unknown(asp, ppid, msg); - -out: - msgb_free(msg); - return rc; -} - -static int xua_srv_conn_closed_cb(struct osmo_stream_srv *srv) -{ - struct osmo_ss7_asp *asp = osmo_stream_srv_get_data(srv); - - LOGP(DLSS7, LOGL_INFO, "%s: connection closed\n", asp ? asp->cfg.name : "?"); - - if (!asp) - return 0; - - /* notify ASP FSM and everyone else */ - osmo_fsm_inst_dispatch(asp->fi, XUA_ASP_E_SCTP_COMM_DOWN_IND, NULL); - - /* delete any RKM-dynamically allocated ASs for this ASP */ - xua_rkm_cleanup_dyn_as_for_asp(asp); - - /* send M-SCTP_RELEASE.ind to Layer Manager */ - xua_asp_send_xlm_prim_simple(asp, OSMO_XLM_PRIM_M_SCTP_RELEASE, PRIM_OP_INDICATION); - - asp->server = NULL; - - /* if we were dynamically allocated at accept_cb() time, let's - * self-destruct now. A new connection will re-create the ASP. */ - if (asp->dyn_allocated) { - /* avoid re-entrance via osmo_stream_srv_destroy() which - * called us */ - osmo_ss7_asp_destroy(asp); - } - - return 0; -} - - -/* server has accept()ed a new SCTP association, let's find the ASP for - * it (if any) */ -static int xua_accept_cb(struct osmo_stream_srv_link *link, int fd) -{ - struct osmo_xua_server *oxs = osmo_stream_srv_link_get_data(link); - struct osmo_stream_srv *srv; - struct osmo_ss7_asp *asp; - char *sock_name = osmo_sock_get_name(link, fd); - const char *proto_name = get_value_string(osmo_ss7_asp_protocol_vals, oxs->cfg.proto); - int rc = 0; - - LOGP(DLSS7, LOGL_INFO, "%s: New %s connection accepted\n", sock_name, proto_name); - - if (oxs->cfg.proto == OSMO_SS7_ASP_PROT_IPA) { - srv = osmo_stream_srv_create(oxs, link, fd, - ipa_srv_conn_cb, - xua_srv_conn_closed_cb, NULL); - } else { - srv = osmo_stream_srv_create(oxs, link, fd, - xua_srv_conn_cb, - xua_srv_conn_closed_cb, NULL); - } - if (!srv) { - LOGP(DLSS7, LOGL_ERROR, "%s: Unable to create stream server " - "for connection\n", sock_name); - close(fd); - talloc_free(sock_name); - return -1; - } - - asp = osmo_ss7_asp_find_by_socket_addr(fd); - if (asp) { - LOGP(DLSS7, LOGL_INFO, "%s: matched connection to ASP %s\n", - sock_name, asp->cfg.name); - /* we need to check if we already have a socket associated, and close it. Otherwise it might - * happen that both the listen-fd for this accept() and the old socket are marked 'readable' - * during the same scheduling interval, and we're processing them in the "wrong" order, i.e. - * we first see the accept of the new fd before we see the close on the old fd */ - if (asp->server) { - LOGPASP(asp, DLSS7, LOGL_FATAL, "accept of new connection from %s before old was closed " - "-> close old one\n", sock_name); - osmo_stream_srv_set_data(asp->server, NULL); - osmo_stream_srv_destroy(asp->server); - asp->server = NULL; - } - } else { - if (!oxs->cfg.accept_dyn_reg) { - LOGP(DLSS7, LOGL_NOTICE, "%s: %s connection without matching " - "ASP definition and no dynamic registration enabled, terminating\n", - sock_name, proto_name); - } else { - char namebuf[32]; - static uint32_t dyn_asp_num = 0; - snprintf(namebuf, sizeof(namebuf), "asp-dyn-%u", dyn_asp_num++); - asp = osmo_ss7_asp_find_or_create(oxs->inst, namebuf, 0, 0, - oxs->cfg.proto); - if (asp) { - char hostbuf[INET6_ADDRSTRLEN]; - const char *hostbuf_ptr = &hostbuf[0]; - char portbuf[16]; - - osmo_sock_get_ip_and_port(fd, hostbuf, sizeof(hostbuf), portbuf, sizeof(portbuf), false); - LOGP(DLSS7, LOGL_INFO, "%s: created dynamic ASP %s\n", - sock_name, asp->cfg.name); - asp->cfg.is_server = true; - asp->cfg.role = OSMO_SS7_ASP_ROLE_SG; - asp->cfg.local.port = oxs->cfg.local.port; - asp->cfg.remote.port = atoi(portbuf); - asp->dyn_allocated = true; - asp->server = srv; - osmo_ss7_asp_peer_set_hosts(&asp->cfg.local, asp, - (const char* const*)oxs->cfg.local.host, - oxs->cfg.local.host_cnt); - osmo_ss7_asp_peer_set_hosts(&asp->cfg.remote, asp, - &hostbuf_ptr, 1); - osmo_ss7_asp_restart(asp); - } - } - if (!asp) { - osmo_stream_srv_destroy(srv); - talloc_free(sock_name); - return -1; - } - llist_add_tail(&asp->siblings, &oxs->asp_list); - } - - /* update the ASP reference back to the server over which the - * connection came in */ - asp->server = srv; - asp->xua_server = oxs; - - /* update the ASP socket name */ - talloc_free(asp->sock_name); - asp->sock_name = talloc_reparent(link, asp, sock_name); - osmo_stream_srv_set_name(asp->server, asp->cfg.name); - /* make sure the conn_cb() is called with the asp as private - * data */ - osmo_stream_srv_set_data(srv, asp); - - if (oxs->cfg.proto != OSMO_SS7_ASP_PROT_IPA) { - rc = asp_apply_peer_primary_address(asp); - rc = asp_apply_primary_address(asp); - } - - /* send M-SCTP_ESTABLISH.ind to Layer Manager */ - osmo_fsm_inst_dispatch(asp->fi, XUA_ASP_E_SCTP_EST_IND, 0); - xua_asp_send_xlm_prim_simple(asp, OSMO_XLM_PRIM_M_SCTP_ESTABLISH, PRIM_OP_INDICATION); - - return rc; -} - -/*! \brief send a fully encoded msgb via a given ASP - * \param[in] asp Application Server Process through which to send - * \param[in] msg message buffer to transmit. Ownership transferred. - * \returns 0 on success; negative in case of error */ -int osmo_ss7_asp_send(struct osmo_ss7_asp *asp, struct msgb *msg) -{ - OSMO_ASSERT(ss7_initialized); - - switch (asp->cfg.proto) { - case OSMO_SS7_ASP_PROT_SUA: - msgb_sctp_ppid(msg) = SUA_PPID; - break; - case OSMO_SS7_ASP_PROT_M3UA: - msgb_sctp_ppid(msg) = M3UA_PPID; - break; - case OSMO_SS7_ASP_PROT_IPA: - break; - default: - OSMO_ASSERT(0); - } - - rate_ctr_inc2(asp->ctrg, SS7_ASP_CTR_PKT_TX_TOTAL); - - if (asp->cfg.is_server) { - if (!asp->server) { - LOGPASP(asp, DLSS7, LOGL_ERROR, "Cannot transmit, no asp->server\n"); - /* FIXME: what to do here? delete the route? send DUNA? */ - msgb_free(msg); - return -EIO; - } - osmo_stream_srv_send(asp->server, msg); - } else { - if (!asp->client) { - LOGPASP(asp, DLSS7, LOGL_ERROR, "Cannot transmit, no asp->client\n"); - /* FIXME: what to do here? delete the route? send DUNA? */ - msgb_free(msg); - return -EIO; - } - if (!osmo_stream_cli_is_connected(asp->client)) { - LOGPASP(asp, DLSS7, LOGL_ERROR, "Cannot transmit, asp->client not connected\n"); - msgb_free(msg); - return -EIO; - } - osmo_stream_cli_send(asp->client, msg); - } - - return 0; -} - -void osmo_ss7_asp_disconnect(struct osmo_ss7_asp *asp) -{ - if (asp->server) - osmo_stream_srv_destroy(asp->server); - /* the close_cb() will handle the remaining cleanup here */ - else if (asp->client) - xua_cli_close_and_reconnect(asp->client); -} - -/*********************************************************************** - * SS7 xUA Server - ***********************************************************************/ - struct osmo_xua_server * osmo_ss7_xua_server_find(struct osmo_ss7_instance *inst, enum osmo_ss7_asp_protocol proto, uint16_t local_port) @@ -2477,136 +1222,6 @@ return NULL; } -/*! \brief create a new xUA server configured with given ip/port - * \param[in] ctx talloc allocation context - * \param[in] proto protocol (xUA variant) to use - * \param[in] local_port local SCTP port to bind/listen to - * \param[in] local_host local IP address to bind/listen to (optional) - * \returns callee-allocated \ref osmo_xua_server in case of success - */ -struct osmo_xua_server * -osmo_ss7_xua_server_create(struct osmo_ss7_instance *inst, enum osmo_ss7_asp_protocol proto, - uint16_t local_port, const char *local_host) -{ - struct osmo_xua_server *oxs = talloc_zero(inst, struct osmo_xua_server); - - OSMO_ASSERT(ss7_initialized); - if (!oxs) - return NULL; - - LOGP(DLSS7, LOGL_INFO, "Creating %s Server %s:%u\n", - get_value_string(osmo_ss7_asp_protocol_vals, proto), local_host, local_port); - - INIT_LLIST_HEAD(&oxs->asp_list); - - oxs->cfg.proto = proto; - oxs->cfg.local.port = local_port; - - oxs->server = osmo_stream_srv_link_create(oxs); - osmo_stream_srv_link_set_name(oxs->server, osmo_ss7_asp_protocol_name(proto)); - osmo_stream_srv_link_set_data(oxs->server, oxs); - osmo_stream_srv_link_set_accept_cb(oxs->server, xua_accept_cb); - - osmo_stream_srv_link_set_nodelay(oxs->server, true); - osmo_stream_srv_link_set_port(oxs->server, oxs->cfg.local.port); - osmo_stream_srv_link_set_proto(oxs->server, asp_proto_to_ip_proto(proto)); - - osmo_ss7_xua_server_set_local_host(oxs, local_host); - - LOGP(DLSS7, LOGL_INFO, "Created %s server on %s:%" PRIu16 "\n", - get_value_string(osmo_ss7_asp_protocol_vals, proto), local_host, local_port); - - oxs->inst = inst; - llist_add_tail(&oxs->list, &inst->xua_servers); - - /* The SUA code internally needs SCCP to work */ - if (proto == OSMO_SS7_ASP_PROT_SUA) - osmo_ss7_ensure_sccp(inst); - - return oxs; -} - -/*! \brief Set the xUA server to bind/listen to the currently configured ip/port - * \param[in] xs xUA server to operate - * \returns 0 on success, negative value on error. - */ -int -osmo_ss7_xua_server_bind(struct osmo_xua_server *xs) -{ - char buf[512]; - int rc; - const char *proto = get_value_string(osmo_ss7_asp_protocol_vals, xs->cfg.proto); - - rc = osmo_ss7_asp_peer_snprintf(buf, sizeof(buf), &xs->cfg.local); - if (rc < 0) { - LOGP(DLSS7, LOGL_INFO, "Failed parsing %s Server osmo_ss7_asp_peer\n", proto); - } else { - LOGP(DLSS7, LOGL_INFO, "(Re)binding %s Server to %s\n", - proto, buf); - } - return osmo_stream_srv_link_open(xs->server); -} - -int -osmo_ss7_xua_server_set_local_host(struct osmo_xua_server *xs, const char *local_host) -{ - return osmo_ss7_xua_server_set_local_hosts(xs, &local_host, 1); -} - -int -osmo_ss7_xua_server_set_local_hosts(struct osmo_xua_server *xs, const char **local_hosts, size_t local_host_cnt) -{ - int rc; - OSMO_ASSERT(ss7_initialized); - - rc = osmo_ss7_asp_peer_set_hosts(&xs->cfg.local, xs, local_hosts, local_host_cnt); - if (rc < 0) - return rc; - return osmo_stream_srv_link_set_addrs(xs->server, (const char **)xs->cfg.local.host, xs->cfg.local.host_cnt); -} - -int -osmo_ss7_xua_server_add_local_host(struct osmo_xua_server *xs, const char *local_host) -{ - int rc; - - rc = osmo_ss7_asp_peer_add_host(&xs->cfg.local, xs, local_host); - if (rc < 0) - return rc; - return osmo_stream_srv_link_set_addrs(xs->server, (const char **)xs->cfg.local.host, xs->cfg.local.host_cnt); -} - -bool ss7_xua_server_set_default_local_hosts(struct osmo_xua_server *oxs) -{ - /* If no local addr was set, or erased after _create(): */ - if (!oxs->cfg.local.host_cnt) { - /* "::" Covers both IPv4 and IPv6 */ - if (ipv6_sctp_supported("::", true)) - osmo_ss7_xua_server_set_local_host(oxs, "::"); - else - osmo_ss7_xua_server_set_local_host(oxs, "0.0.0.0"); - return true; - } - return false; -} - -void osmo_ss7_xua_server_destroy(struct osmo_xua_server *xs) -{ - struct osmo_ss7_asp *asp, *asp2; - - if (xs->server) { - osmo_stream_srv_link_close(xs->server); - osmo_stream_srv_link_destroy(xs->server); - } - /* iterate and close all connections established in relation - * with this server */ - llist_for_each_entry_safe(asp, asp2, &xs->asp_list, siblings) - osmo_ss7_asp_destroy(asp); - - llist_del(&xs->list); - talloc_free(xs); -} - bool osmo_ss7_pc_is_local(struct osmo_ss7_instance *inst, uint32_t pc) { OSMO_ASSERT(ss7_initialized); @@ -2694,62 +1309,3 @@ } return false; } - -static osmo_ss7_asp_rx_unknown_cb *g_osmo_ss7_asp_rx_unknown_cb; - -int ss7_asp_rx_unknown(struct osmo_ss7_asp *asp, int ppid_mux, struct msgb *msg) -{ - rate_ctr_inc2(asp->ctrg, SS7_ASP_CTR_PKT_RX_UNKNOWN); - - if (g_osmo_ss7_asp_rx_unknown_cb) - return (*g_osmo_ss7_asp_rx_unknown_cb)(asp, ppid_mux, msg); - - switch(asp->cfg.proto) { - case OSMO_SS7_ASP_PROT_IPA: - LOGPASP(asp, DLSS7, LOGL_NOTICE, "Rx IPA for unknown Stream ID 0x%02x: %s\n", - ppid_mux, msgb_hexdump(msg)); - break; - default: - LOGPASP(asp, DLSS7, LOGL_NOTICE, "Rx SCTP chunk for unknown PPID %u: %s\n", - ppid_mux, msgb_hexdump(msg)); - break; - } - return 0; -} - -/*! Get the logging subsystem for a given ASP. Used by generic code. */ -int osmo_ss7_asp_get_log_subsys(const struct osmo_ss7_asp *asp) -{ - switch (asp->cfg.proto) { - case OSMO_SS7_ASP_PROT_M3UA: - return DLM3UA; - case OSMO_SS7_ASP_PROT_SUA: - return DLSUA; - default: - return DLSS7; - } -} - -/*! \brief Get the name of a given ASP - * \param[in] asp The ASP for which the name is requested - * \returns The name of the ASP, or NULL if not set - */ -const char *osmo_ss7_asp_get_name(const struct osmo_ss7_asp *asp) -{ - return asp->cfg.name; -} - -/*! \brief Get the proto of a given ASP - * \param[in] asp The ASP for which the proto is requested - * \returns The proto of the ASP - */ -enum osmo_ss7_asp_protocol osmo_ss7_asp_get_proto(const struct osmo_ss7_asp *asp) -{ - return asp->cfg.proto; -} - -/*! Register a call-back function for unknown SCTP PPID / IPA Stream ID */ -void osmo_ss7_register_rx_unknown_cb(osmo_ss7_asp_rx_unknown_cb *cb) -{ - g_osmo_ss7_asp_rx_unknown_cb = cb; -} diff --git a/src/osmo_ss7_asp.c b/src/osmo_ss7_asp.c new file mode 100644 index 0000000..c282730 --- /dev/null +++ b/src/osmo_ss7_asp.c @@ -0,0 +1,1246 @@ +/* Core SS7 ASP Handling */ + +/* (C) 2015-2017 by Harald Welte <laforge(a)gnumonks.org> + * (C) 2023 by sysmocom s.f.m.c. GmbH <info(a)sysmocom.de> + * All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0+ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <inttypes.h> +#include <netdb.h> +#include <netinet/in.h> +#include <netinet/sctp.h> + +#include <osmocom/sigtran/osmo_ss7.h> +#include <osmocom/sigtran/mtp_sap.h> +#include <osmocom/sigtran/protocol/mtp.h> +#include <osmocom/sigtran/protocol/sua.h> +#include <osmocom/sigtran/protocol/m3ua.h> + +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/select.h> +#include <osmocom/core/utils.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/logging.h> +#include <osmocom/core/msgb.h> +#include <osmocom/core/socket.h> +#include <osmocom/core/sockaddr_str.h> + +#include <osmocom/netif/stream.h> +#include <osmocom/netif/ipa.h> +#include <osmocom/netif/sctp.h> + +#include "sccp_internal.h" +#include "xua_internal.h" +#include "ss7_internal.h" +#include "xua_asp_fsm.h" +#include "xua_as_fsm.h" + +static int _setsockopt_peer_primary_addr(int fd, const struct osmo_sockaddr *saddr) +{ + int rc; + + struct sctp_setpeerprim so_sctp_setpeerprim = {0}; + + /* rfc6458 sec 8: "For the one-to-one style sockets and branched-off one-to-many + * style sockets (see Section 9.2), this association ID parameter is ignored" + */ + + /* NOTE: Requires setting: + * - sysctl net.sctp.addip_enable = 1, otherwise EPERM is returned + * - sysctl net.sctp.auth_enable = 1, RFC 5061 4.2.7 "An implementation supporting this + * extension MUST list the ASCONF,the ASCONF-ACK, and the AUTH chunks in + * its INIT and INIT-ACK parameters." + */ + + so_sctp_setpeerprim.sspp_addr = saddr->u.sas; + rc = setsockopt(fd, IPPROTO_SCTP, SCTP_SET_PEER_PRIMARY_ADDR, + &so_sctp_setpeerprim, sizeof(so_sctp_setpeerprim)); + if (rc < 0) { + char buf[128]; + int err = errno; + strerror_r(err, (char *)buf, sizeof(buf)); + LOGP(DLSS7, LOGL_ERROR, "setsockopt(SCTP_SET_PEER_PRIMARY_ADDR, %s) failed: %s%s\n", + osmo_sockaddr_to_str(saddr), buf, + err == EPERM ? " (EPERM: Make sure you have sysctl 'net.sctp.auth_enable' " + "and 'net.sctp.addip_enable' set to 1)" : ""); + } + return rc; +} + +static int _setsockopt_primary_addr(int fd, const struct osmo_sockaddr *saddr) +{ + int rc; + + struct sctp_prim so_sctp_prim = {0}; + + /* rfc6458 sec 8: "For the one-to-one style sockets and branched-off one-to-many + * style sockets (see Section 9.2), this association ID parameter is ignored" + */ + + so_sctp_prim.ssp_addr = saddr->u.sas; + rc = setsockopt(fd, IPPROTO_SCTP, SCTP_PRIMARY_ADDR, + &so_sctp_prim, sizeof(so_sctp_prim)); + if (rc < 0) { + char buf[128]; + strerror_r(errno, (char *)buf, sizeof(buf)); + LOGP(DLSS7, LOGL_ERROR, "setsockopt(SCTP_PRIMARY_ADDR, %s) failed: %s\n", + osmo_sockaddr_to_str(saddr), buf); + } + return rc; +} + +/*********************************************************************** + * SS7 Application Server Process + ***********************************************************************/ + +struct value_string osmo_ss7_asp_protocol_vals[] = { + { OSMO_SS7_ASP_PROT_NONE, "none" }, + { OSMO_SS7_ASP_PROT_SUA, "sua" }, + { OSMO_SS7_ASP_PROT_M3UA, "m3ua" }, + { OSMO_SS7_ASP_PROT_IPA, "ipa" }, + { 0, NULL } +}; + +const struct value_string osmo_ss7_asp_role_names[] = { + { OSMO_SS7_ASP_ROLE_ASP, "ASP" }, + { OSMO_SS7_ASP_ROLE_SG, "SG" }, + { OSMO_SS7_ASP_ROLE_IPSP, "IPSP" }, + { 0, NULL } +}; + +int ss7_asp_proto_to_ip_proto(enum osmo_ss7_asp_protocol proto) +{ + switch (proto) { + case OSMO_SS7_ASP_PROT_IPA: + return IPPROTO_TCP; + case OSMO_SS7_ASP_PROT_SUA: + case OSMO_SS7_ASP_PROT_M3UA: + default: + return IPPROTO_SCTP; + } +} + +static const uint16_t prot2port[] = { + [OSMO_SS7_ASP_PROT_NONE] = 0, + [OSMO_SS7_ASP_PROT_SUA] = SUA_PORT, + [OSMO_SS7_ASP_PROT_M3UA] = M3UA_PORT, + [OSMO_SS7_ASP_PROT_IPA] = 5000, +}; + +int osmo_ss7_asp_protocol_port(enum osmo_ss7_asp_protocol prot) +{ + if (prot >= ARRAY_SIZE(prot2port)) + return -EINVAL; + else + return prot2port[prot]; +} + +static const struct rate_ctr_desc ss7_asp_rcd[] = { + [SS7_ASP_CTR_PKT_RX_TOTAL] = { "rx:packets:total", "Total number of packets received" }, + [SS7_ASP_CTR_PKT_RX_UNKNOWN] = { "rx:packets:unknown", "Number of packets received for unknown PPID" }, + [SS7_ASP_CTR_PKT_TX_TOTAL] = { "tx:packets:total", "Total number of packets transmitted" }, +}; + +static const struct rate_ctr_group_desc ss7_asp_rcgd = { + .group_name_prefix = "sigtran_asp", + .group_description = "SIGTRAN Application Server Process", + .num_ctr = ARRAY_SIZE(ss7_asp_rcd), + .ctr_desc = ss7_asp_rcd, +}; +static unsigned int g_ss7_asp_rcg_idx; + +void osmo_ss7_asp_peer_init(struct osmo_ss7_asp_peer *peer) +{ + memset(peer, 0, sizeof(*peer)); + peer->idx_primary = -1; +} + +int osmo_ss7_asp_peer_snprintf(char *buf, size_t buf_len, struct osmo_ss7_asp_peer *peer) +{ + int len = 0, offset = 0, rem = buf_len; + int ret, i; + char *after; + char *primary; + + if (buf_len < 3) + return -EINVAL; + + if (peer->host_cnt > 1) { + ret = snprintf(buf, rem, "("); + if (ret < 0) + return ret; + OSMO_SNPRINTF_RET(ret, rem, offset, len); + } + for (i = 0; i < peer->host_cnt; i++) { + primary = (peer->idx_primary == i) ? "*" : ""; + if (peer->host_cnt == 1) + after = ""; + else + after = (i == (peer->host_cnt - 1)) ? ")" : "|"; + ret = snprintf(buf + offset, rem, "%s%s%s", peer->host[i] ? : "0.0.0.0", primary, after); + OSMO_SNPRINTF_RET(ret, rem, offset, len); + } + ret = snprintf(buf + offset, rem, ":%u", peer->port); + if (ret < 0) + return ret; + OSMO_SNPRINTF_RET(ret, rem, offset, len); + + return len; +} + +/*! \brief Set (copy) addresses for a given ASP peer. Previous addresses are freed. + * \param[in] peer Application Server Process peer whose addresses are to be set. + * \param[in] talloc_ctx talloc context used to allocate new addresses. + * \param[in] hosts Array of strings containing IP addresses. + * \param[in] host_cnt Number of strings in hosts + * \param[in] idx_primary Index in "hosts" array marking the SCTP Primary Address, -1 if no explicit Primary Address set + * \returns 0 on success; negative otherwise */ +int osmo_ss7_asp_peer_set_hosts2(struct osmo_ss7_asp_peer *peer, void *talloc_ctx, const char *const*hosts, size_t host_cnt, int idx_primary) +{ + int i = 0; + + if (idx_primary >= (int)host_cnt || idx_primary < -1) + return -EINVAL; + + if (host_cnt > ARRAY_SIZE(peer->host)) + return -EINVAL; + + for (; i < host_cnt; i++) + osmo_talloc_replace_string(talloc_ctx, &peer->host[i], hosts[i]); + for (; i < peer->host_cnt; i++) { + talloc_free(peer->host[i]); + peer->host[i] = NULL; + } + + peer->host_cnt = host_cnt; + peer->idx_primary = idx_primary; + return 0; +} + +/*! \brief Set (copy) addresses for a given ASP peer. Previous addresses are freed. + * \param[in] peer Application Server Process peer whose addresses are to be set. + * \param[in] talloc_ctx talloc context used to allocate new addresses. + * \param[in] hosts Array of strings containing IP addresses. + * \param[in] host_cnt Number of strings in hosts + * \returns 0 on success; negative otherwise */ +int osmo_ss7_asp_peer_set_hosts(struct osmo_ss7_asp_peer *peer, void *talloc_ctx, const char *const*hosts, size_t host_cnt) +{ + return osmo_ss7_asp_peer_set_hosts2(peer, talloc_ctx, hosts, host_cnt, -1); +} + +/* Is string formatted IPv4/v6 addr considered IN(6)ADDR_ANY? */ +static inline bool host_is_ip_anyaddr(const char *host, bool is_v6) +{ + /* NULL addr is resolved as 0.0.0.0 (IPv4) by getaddrinfo(), most + * probably for backward-compatibility reasons. + */ + return is_v6 ? (host && !strcmp(host, "::")) + : (!host || !strcmp(host, "0.0.0.0")); +} + +/*! \brief Append (copy) address to a given ASP peer. Previous addresses are kept. + * \param[in] peer Application Server Process peer the address is appended to. + * \param[in] talloc_ctx talloc context used to allocate new address. + * \param[in] host string containing an IP address. + * \param[in] is_primary_addr whether this IP address is to be added as SCTP Primary Address + * \returns 0 on success; negative otherwise */ +int osmo_ss7_asp_peer_add_host2(struct osmo_ss7_asp_peer *peer, void *talloc_ctx, + const char *host, bool is_primary_addr) +{ + int i; + bool new_is_v6 = osmo_ip_str_type(host) == AF_INET6; + bool new_is_any = host_is_ip_anyaddr(host, new_is_v6); + struct osmo_sockaddr_str addr_str; + + if (osmo_sockaddr_str_from_str(&addr_str, host, 0) < 0) + return -EINVAL; + + if (new_is_any) { + /* Makes no sense to have INET(6)_ANY many times, or INET(6)_ANY + * together with specific addresses, be it of same or different + * IP version: */ + if (peer->host_cnt != 0) + return -EINVAL; + + /* Makes no sense to have INET(6)_ANY as primary: */ + if (is_primary_addr) + return -EINVAL; + + if (peer->host_cnt >= ARRAY_SIZE(peer->host)) + return -EINVAL; + osmo_talloc_replace_string(talloc_ctx, &peer->host[peer->host_cnt], host); + peer->host_cnt++; + return 0; + } + + /* Makes no sense to add specific address to set if INET(6)_ANY + * is already set, be it from same or different IP version: */ + for (i = 0; i < peer->host_cnt; i++) { + bool iter_is_v6 = osmo_ip_str_type(peer->host[i]) == AF_INET6; + if (host_is_ip_anyaddr(peer->host[i], iter_is_v6)) + return -EINVAL; + } + /* Reached this point, no INET(6)_ANY address is set nor we are adding an INET(6)_ANY address. */ + + /* Check if address already exists, and then if primary flags need to be changed: */ + for (i = 0; i < peer->host_cnt; i++) { + struct osmo_sockaddr_str it_addr_str; + bool it_is_primary; + osmo_sockaddr_str_from_str(&it_addr_str, peer->host[i], 0); + + if (osmo_sockaddr_str_cmp(&addr_str, &it_addr_str) != 0) + continue; + it_is_primary = (peer->idx_primary == i); + if (is_primary_addr == it_is_primary) { + /* Nothing to do, return below */ + } else if (is_primary_addr && !it_is_primary) { + /* Mark it as primary: */ + peer->idx_primary = i; + } else { /* if (!is_primary_addr && it_is_primary) { */ + /* mark it as non-primary: */ + peer->idx_primary = -1; + } + return 0; + } + + if (peer->host_cnt >= ARRAY_SIZE(peer->host)) + return -EINVAL; + + osmo_talloc_replace_string(talloc_ctx, &peer->host[peer->host_cnt], host); + if (is_primary_addr) + peer->idx_primary = peer->host_cnt; + peer->host_cnt++; + return 0; +} + +/*! \brief Append (copy) address to a given ASP peer. Previous addresses are kept. + * \param[in] peer Application Server Process peer the address is appended to. + * \param[in] talloc_ctx talloc context used to allocate new address. + * \param[in] host string containing an IP address. + * \returns 0 on success; negative otherwise */ +int osmo_ss7_asp_peer_add_host(struct osmo_ss7_asp_peer *peer, void *talloc_ctx, + const char *host) +{ + return osmo_ss7_asp_peer_add_host2(peer, talloc_ctx, host, false); +} + +int ss7_asp_apply_peer_primary_address(const struct osmo_ss7_asp *asp) +{ + struct osmo_fd *ofd; + struct osmo_sockaddr_str addr_str; + struct osmo_sockaddr addr; + uint16_t local_port; + int rc; + + /* No SCTP Peer Primary Address explicitly configured, do nothing. */ + if (asp->cfg.local.idx_primary == -1) + return 0; + OSMO_ASSERT(asp->cfg.local.idx_primary < asp->cfg.local.host_cnt); + + if (asp->cfg.is_server) + ofd = osmo_stream_srv_get_ofd(asp->server); + else + ofd = osmo_stream_cli_get_ofd(asp->client); + + if (asp->cfg.local.port == 0) { + char port_buf[16]; + osmo_sock_get_local_ip_port(ofd->fd, port_buf, sizeof(port_buf)); + local_port = atoi(port_buf); + } else { + local_port = asp->cfg.local.port; + } + rc = osmo_sockaddr_str_from_str(&addr_str, + asp->cfg.local.host[asp->cfg.local.idx_primary], + local_port); + if (rc < 0) + return rc; + rc = osmo_sockaddr_str_to_sockaddr(&addr_str, &addr.u.sas); + if (rc < 0) + return rc; + LOGPASP(asp, DLSS7, LOGL_INFO, "Set Peer's Primary Address %s\n", + osmo_sockaddr_to_str(&addr)); + rc = _setsockopt_peer_primary_addr(ofd->fd, &addr); + + return rc; +} + +int ss7_asp_apply_primary_address(const struct osmo_ss7_asp *asp) +{ + struct osmo_fd *ofd; + struct osmo_sockaddr_str addr_str; + struct osmo_sockaddr addr; + int rc; + + /* No SCTP Primary Address explicitly configured, do nothing. */ + if (asp->cfg.remote.idx_primary == -1) + return 0; + OSMO_ASSERT(asp->cfg.remote.idx_primary < asp->cfg.remote.host_cnt); + + if (asp->cfg.is_server) + ofd = osmo_stream_srv_get_ofd(asp->server); + else + ofd = osmo_stream_cli_get_ofd(asp->client); + + rc = osmo_sockaddr_str_from_str(&addr_str, + asp->cfg.remote.host[asp->cfg.remote.idx_primary], + asp->cfg.remote.port); + if (rc < 0) + return rc; + rc = osmo_sockaddr_str_to_sockaddr(&addr_str, &addr.u.sas); + if (rc < 0) + return rc; + LOGPASP(asp, DLSS7, LOGL_INFO, "Set Primary Address %s\n", + osmo_sockaddr_to_str(&addr)); + rc = _setsockopt_primary_addr(ofd->fd, &addr); + return rc; +} + +/* Returns whether the address signalled in the SCTP_PEER_ADDR_CHANGE matches + * the user-configured Primary Address. */ +static bool sctp_peer_addr_change_ev_addr_matches_our_primary(const struct osmo_ss7_asp *asp, + const union sctp_notification *notif) +{ + const char *primary_str; + int primary_str_type; + struct osmo_sockaddr ev_addr, primary; + + OSMO_ASSERT(asp->cfg.remote.idx_primary >= 0); + + primary_str = asp->cfg.remote.host[asp->cfg.remote.idx_primary]; + primary_str_type = osmo_ip_str_type(primary_str); + memcpy(&ev_addr.u.sas, &notif->sn_paddr_change.spc_aaddr, sizeof(ev_addr.u.sas)); + + /* This whole switch is to properly compare addresses (take into account v6mapped IPv4 addresses): */ + switch (ev_addr.u.sa.sa_family) { + case AF_INET: + switch (primary_str_type) { + case AF_INET: + primary = ev_addr; /* Copy AF + port */ + inet_pton(AF_INET, primary_str, &primary.u.sin.sin_addr); + return (osmo_sockaddr_cmp(&primary, &ev_addr) == 0); + case AF_INET6: + /* for sure not the same */ + return false; + } + return false; + case AF_INET6: + /* "ev_addr" can either be a IPv6 addr or a v6-mapped IPv4 + * address. Compare both as IPv6 (or v6-mapped IPv4) addresses. */ + primary = ev_addr; /* Copy AF + port */ + inet_pton(AF_INET6, primary_str, &primary.u.sin6.sin6_addr); + return (osmo_sockaddr_cmp(&primary, &ev_addr) == 0); + default: + return false; + } +} + +/* Simple SCTP Path-manager tracking/driving the VTY-user-configured primary + * address against the kernel when assoc state changes: */ +static void asp_handle_sctp_notif_monitor_primary_address(const struct osmo_ss7_asp *asp, + const union sctp_notification *notif) +{ + bool match; + + if (asp->cfg.remote.idx_primary == -1) + return; + if (notif->sn_header.sn_type != SCTP_PEER_ADDR_CHANGE) + return; + + switch (notif->sn_paddr_change.spc_state) { + case SCTP_ADDR_AVAILABLE: + case SCTP_ADDR_ADDED: + case SCTP_ADDR_CONFIRMED: + /* If our primary addr became available/added/confirmed, set it */ + match = sctp_peer_addr_change_ev_addr_matches_our_primary(asp, notif); + if (match) + ss7_asp_apply_primary_address(asp); + break; + case SCTP_ADDR_MADE_PRIM: + /* If another primary addr was made primary, overwrite it by setting it again */ + match = sctp_peer_addr_change_ev_addr_matches_our_primary(asp, notif); + if (!match) + ss7_asp_apply_primary_address(asp); + default: + break; + } +} + +/* Set default values for local and remote peer hosts if they are not yet set. + * \param[in] asp ASP for which to set default hosts. + * \returns true if values where changed, false otherwise. + * + * If the ASP is already started, osmo_ss7_asp_restart() must be called + * afterwards in order to apply the new settings. + * This API is internal, hence doesn't appear in osmo_ss7.h + */ +bool ss7_asp_set_default_peer_hosts(struct osmo_ss7_asp *asp) +{ + bool changed = false; + /* If no local addr was set */ + if (!asp->cfg.local.host_cnt) { + bool rem_has_v4 = false, rem_has_v6 = false; + int i; + for (i = 0; i < asp->cfg.remote.host_cnt; i++) { + if (osmo_ip_str_type(asp->cfg.remote.host[i]) == AF_INET6) + rem_has_v6 = true; + else + rem_has_v4 = true; + } + /* "::" Covers both IPv4 and IPv6, but if only IPv4 + * address are set on the remote side, IPv4 on the local + * side must be set too */ + if (ss7_ipv6_sctp_supported("::", true) && !(rem_has_v4 && !rem_has_v6)) + osmo_ss7_asp_peer_add_host(&asp->cfg.local, asp, "::"); + else + osmo_ss7_asp_peer_add_host(&asp->cfg.local, asp, "0.0.0.0"); + changed = true; + } + /* If no remote addr was set */ + if (!asp->cfg.remote.host_cnt) { + osmo_ss7_asp_peer_add_host(&asp->cfg.remote, asp, "127.0.0.1"); + if (ss7_ipv6_sctp_supported("::1", false)) + osmo_ss7_asp_peer_add_host(&asp->cfg.remote, asp, "::1"); + changed = true; + } + return changed; +} + +static uint16_t get_in_port(struct sockaddr *sa) +{ + switch (sa->sa_family) { + case AF_INET: + return (((struct sockaddr_in *)sa)->sin_port); + case AF_INET6: + return (((struct sockaddr_in6 *)sa)->sin6_port); + default: + return 0; + } +} + +/* Converts string representation of v4-mappend-on-v6 IP addr to a pure IPv4 address. + * Example: ::ffff:172.18.19.200 => 172.18.19.200 + */ +static void chop_v4_mapped_on_v6_prefix(char *buf) +{ + char *last_colon; + size_t len; + char *first_dot = strchr(buf, '.'); + + if (!first_dot) + return; /* Not an IPv4-mappend-on-v6 string representation, nothing to do */ + last_colon = strrchr(buf, ':'); + if (!last_colon) + return; /* pure IPv4 address, nothing to do */ + + len = strlen(last_colon + 1); + memmove(buf, last_colon + 1, len); + buf[len] = '\0'; +} + +/*! \brief Find an ASP definition matching the local+remote IP/PORT of given fd + * \param[in] fd socket descriptor of given socket + * \returns SS7 ASP in case a matching one is found; NULL otherwise */ +struct osmo_ss7_asp * +ss7_asp_find_by_socket_addr(int fd) +{ + struct osmo_ss7_instance *inst; + struct sockaddr_storage sa_l, sa_r; + socklen_t sa_len_l = sizeof(sa_l); + socklen_t sa_len_r = sizeof(sa_r); + char hostbuf_l[64], hostbuf_r[64]; + uint16_t local_port, remote_port; + bool loc_is_v6, rem_is_v6; + int rc; + int i; + + OSMO_ASSERT(ss7_initialized); + /* convert local and remote IP to string */ + rc = getsockname(fd, (struct sockaddr *)&sa_l, &sa_len_l); + if (rc < 0) + return NULL; + rc = getnameinfo((struct sockaddr *)&sa_l, sa_len_l, + hostbuf_l, sizeof(hostbuf_l), + NULL, 0, NI_NUMERICHOST); + if (rc < 0) + return NULL; + local_port = ntohs(get_in_port((struct sockaddr *)&sa_l)); + + rc = getpeername(fd, (struct sockaddr *)&sa_r, &sa_len_r); + if (rc < 0) + return NULL; + rc = getnameinfo((struct sockaddr *)&sa_r, sa_len_r, + hostbuf_r, sizeof(hostbuf_r), + NULL, 0, NI_NUMERICHOST); + if (rc < 0) + return NULL; + remote_port = ntohs(get_in_port((struct sockaddr *)&sa_r)); + + /* If multi-home is used with both IPv4 and IPv6, then the socket is + * AF_INET6, and then returned IPv4 addresses are actually v6mapped ones. + * We need to convert them to IPv4 before matching. + */ + chop_v4_mapped_on_v6_prefix(hostbuf_l); + chop_v4_mapped_on_v6_prefix(hostbuf_r); + loc_is_v6 = osmo_ip_str_type(hostbuf_l) == AF_INET6; + rem_is_v6 = osmo_ip_str_type(hostbuf_r) == AF_INET6; + + /* check all instances for any ASP definition matching the + * address combination of local/remote ip/port */ + llist_for_each_entry(inst, &osmo_ss7_instances, list) { + struct osmo_ss7_asp *asp; + llist_for_each_entry(asp, &inst->asp_list, list) { + if (asp->cfg.local.port != local_port) + continue; + if (asp->cfg.remote.port && asp->cfg.remote.port != remote_port) + continue; + + for (i = 0; i < asp->cfg.local.host_cnt; i++) { + bool iter_is_v6 = osmo_ip_str_type(asp->cfg.local.host[i]) == AF_INET6; + bool iter_is_anyaddr = host_is_ip_anyaddr(asp->cfg.local.host[i], iter_is_v6); + /* "::" (v6) covers "0.0.0.0" (v4), but not otherwise */ + if (iter_is_v6 != loc_is_v6 && + !(iter_is_v6 && iter_is_anyaddr)) + continue; + if (iter_is_anyaddr || + !strcmp(asp->cfg.local.host[i], hostbuf_l)) + break; + } + if (i == asp->cfg.local.host_cnt) + continue; /* didn't match any local.host */ + + /* If no remote host was set, it's probably a server and hence we match any cli src */ + if (asp->cfg.remote.host_cnt) { + for (i = 0; i < asp->cfg.remote.host_cnt; i++) { + bool iter_is_v6 = osmo_ip_str_type(asp->cfg.remote.host[i]) == AF_INET6; + bool iter_is_anyaddr = host_is_ip_anyaddr(asp->cfg.remote.host[i], iter_is_v6); + /* "::" (v6) covers "0.0.0.0" (v4), but not otherwise */ + if (iter_is_v6 != rem_is_v6 && + !(iter_is_v6 && iter_is_anyaddr)) + continue; + if (iter_is_anyaddr || + !strcmp(asp->cfg.remote.host[i], hostbuf_r)) + break; + } + if (i == asp->cfg.remote.host_cnt) + continue; /* didn't match any remote.host */ + } + + return asp; + } + } + + return NULL; +} + +struct osmo_ss7_asp *ss7_asp_alloc(struct osmo_ss7_instance *inst, const char *name, + uint16_t remote_port, uint16_t local_port, + enum osmo_ss7_asp_protocol proto) +{ + struct osmo_ss7_asp *asp = talloc_zero(inst, struct osmo_ss7_asp); + asp->ctrg = rate_ctr_group_alloc(asp, &ss7_asp_rcgd, g_ss7_asp_rcg_idx++); + if (!asp->ctrg) { + talloc_free(asp); + return NULL; + } + rate_ctr_group_set_name(asp->ctrg, name); + asp->inst = inst; + osmo_ss7_asp_peer_init(&asp->cfg.remote); + asp->cfg.remote.port = remote_port; + osmo_ss7_asp_peer_init(&asp->cfg.local); + asp->cfg.local.port = local_port; + asp->cfg.proto = proto; + asp->cfg.name = talloc_strdup(asp, name); + + asp->cfg.T_defs_lm = talloc_memdup(asp, ss7_asp_lm_timer_defaults, + sizeof(ss7_asp_lm_timer_defaults)); + osmo_tdefs_reset(asp->cfg.T_defs_lm); + + llist_add_tail(&asp->list, &inst->asp_list); + + /* The SUA code internally needs SCCP to work */ + if (proto == OSMO_SS7_ASP_PROT_SUA) + osmo_ss7_ensure_sccp(inst); + return asp; +} + +void osmo_ss7_asp_destroy(struct osmo_ss7_asp *asp) +{ + struct osmo_ss7_as *as; + + OSMO_ASSERT(ss7_initialized); + LOGPASP(asp, DLSS7, LOGL_INFO, "Destroying ASP\n"); + + if (asp->server) + osmo_stream_srv_destroy(asp->server); + if (asp->client) + osmo_stream_cli_destroy(asp->client); + if (asp->fi) + osmo_fsm_inst_term(asp->fi, OSMO_FSM_TERM_REQUEST, NULL); + if (asp->xua_server) + llist_del(&asp->siblings); + + /* unlink from all ASs we are part of */ + llist_for_each_entry(as, &asp->inst->as_list, list) { + unsigned int i; + for (i = 0; i < ARRAY_SIZE(as->cfg.asps); i++) { + if (as->cfg.asps[i] == asp) + as->cfg.asps[i] = NULL; + } + } + /* unlink from ss7_instance */ + asp->inst = NULL; + llist_del(&asp->list); + rate_ctr_group_free(asp->ctrg); + /* release memory */ + talloc_free(asp); +} + +static int xua_cli_read_cb(struct osmo_stream_cli *conn); +static int ipa_cli_read_cb(struct osmo_stream_cli *conn); +static int xua_cli_connect_cb(struct osmo_stream_cli *cli); + +int osmo_ss7_asp_restart(struct osmo_ss7_asp *asp) +{ + int rc; + char bufloc[512], bufrem[512]; + + OSMO_ASSERT(ss7_initialized); + osmo_ss7_asp_peer_snprintf(bufloc, sizeof(bufloc), &asp->cfg.local); + osmo_ss7_asp_peer_snprintf(bufrem, sizeof(bufrem), &asp->cfg.remote); + LOGPASP(asp, DLSS7, LOGL_INFO, "Restarting ASP %s, r=%s<->l=%s\n", + asp->cfg.name, bufrem, bufloc); + + if (!asp->cfg.is_server) { + /* We are in client mode now */ + if (asp->server) { + /* if we previously were in server mode, + * destroy it */ + osmo_stream_srv_destroy(asp->server); + asp->server = NULL; + } + if (!asp->client) + asp->client = osmo_stream_cli_create(asp); + if (!asp->client) { + LOGPASP(asp, DLSS7, LOGL_ERROR, "Unable to create stream" + " client for ASP %s\n", asp->cfg.name); + return -1; + } + osmo_stream_cli_set_name(asp->client, asp->cfg.name); + osmo_stream_cli_set_nodelay(asp->client, true); + osmo_stream_cli_set_addrs(asp->client, (const char **)asp->cfg.remote.host, asp->cfg.remote.host_cnt); + osmo_stream_cli_set_port(asp->client, asp->cfg.remote.port); + osmo_stream_cli_set_local_addrs(asp->client, (const char **)asp->cfg.local.host, asp->cfg.local.host_cnt); + osmo_stream_cli_set_local_port(asp->client, asp->cfg.local.port); + osmo_stream_cli_set_proto(asp->client, ss7_asp_proto_to_ip_proto(asp->cfg.proto)); + osmo_stream_cli_set_reconnect_timeout(asp->client, 5); + osmo_stream_cli_set_connect_cb(asp->client, xua_cli_connect_cb); + if (asp->cfg.proto == OSMO_SS7_ASP_PROT_IPA) + osmo_stream_cli_set_read_cb(asp->client, ipa_cli_read_cb); + else + osmo_stream_cli_set_read_cb(asp->client, xua_cli_read_cb); + osmo_stream_cli_set_data(asp->client, asp); + rc = osmo_stream_cli_open(asp->client); + if (rc < 0) { + LOGPASP(asp, DLSS7, LOGL_ERROR, "Unable to open stream" + " client for ASP %s, %s ==> %s\n", asp->cfg.name, bufloc, bufrem); + /* we don't return error in here because osmo_stream_cli_open() + will continue to retry (due to timeout being explicitly set with + osmo_stream_cli_set_reconnect_timeout() above) to connect so the error is transient */ + } + } else { + /* We are in server mode now */ + if (asp->client) { + /* if we previously were in client mode, + * destroy it */ + osmo_stream_cli_destroy(asp->client); + asp->client = NULL; + } + /* FIXME: ensure we have a SCTP server */ + LOGPASP(asp, DLSS7, LOGL_NOTICE, "ASP Restart for server " + "not implemented yet!\n"); + } + + /* (re)start the ASP FSM */ + if (asp->fi) + osmo_fsm_inst_term(asp->fi, OSMO_FSM_TERM_REQUEST, NULL); + asp->fi = xua_asp_fsm_start(asp, asp->cfg.role, LOGL_DEBUG); + + return 0; +} + +bool osmo_ss7_asp_active(const struct osmo_ss7_asp *asp) +{ + if (!asp->fi) + return false; + return asp->fi->state == XUA_ASP_S_ACTIVE; +} + +/*********************************************************************** + * libosmo-netif integration for SCTP stream server/client + ***********************************************************************/ + +static int get_logevel_by_sn_type(int sn_type) +{ + switch (sn_type) { + case SCTP_ADAPTATION_INDICATION: + case SCTP_PEER_ADDR_CHANGE: +#ifdef SCTP_AUTHENTICATION_INDICATION + case SCTP_AUTHENTICATION_INDICATION: +#endif +#ifdef SCTP_SENDER_DRY_EVENT + case SCTP_SENDER_DRY_EVENT: +#endif + return LOGL_INFO; + case SCTP_ASSOC_CHANGE: + return LOGL_NOTICE; + case SCTP_SHUTDOWN_EVENT: + case SCTP_PARTIAL_DELIVERY_EVENT: + return LOGL_NOTICE; + case SCTP_SEND_FAILED: + case SCTP_REMOTE_ERROR: + return LOGL_ERROR; + default: + return LOGL_NOTICE; + } +} + +static void log_sctp_notification(struct osmo_ss7_asp *asp, const char *pfx, + union sctp_notification *notif) +{ + int log_level; + + LOGPASP(asp, DLSS7, LOGL_INFO, "%s SCTP NOTIFICATION %u flags=0x%0x\n", + pfx, notif->sn_header.sn_type, + notif->sn_header.sn_flags); + + log_level = get_logevel_by_sn_type(notif->sn_header.sn_type); + + switch (notif->sn_header.sn_type) { + case SCTP_ASSOC_CHANGE: + LOGPASP(asp, DLSS7, log_level, "%s SCTP_ASSOC_CHANGE: %s\n", + pfx, osmo_sctp_assoc_chg_str(notif->sn_assoc_change.sac_state)); + break; + case SCTP_PEER_ADDR_CHANGE: + { + char addr_str[INET6_ADDRSTRLEN + 10]; + struct sockaddr_storage sa = notif->sn_paddr_change.spc_aaddr; + osmo_sockaddr_to_str_buf(addr_str, sizeof(addr_str), (const struct osmo_sockaddr *)&sa); + LOGPASP(asp, DLSS7, log_level, "%s SCTP_PEER_ADDR_CHANGE: %s %s err=%s\n", + pfx, osmo_sctp_paddr_chg_str(notif->sn_paddr_change.spc_state), addr_str, + (notif->sn_paddr_change.spc_state == SCTP_ADDR_UNREACHABLE) ? + osmo_sctp_sn_error_str(notif->sn_paddr_change.spc_error) : "None"); + } + break; + default: + LOGPASP(asp, DLSS7, log_level, "%s %s\n", + pfx, osmo_sctp_sn_type_str(notif->sn_header.sn_type)); + break; + } +} + +/* netif code tells us we can read something from the socket */ +int ss7_asp_ipa_srv_conn_cb(struct osmo_stream_srv *conn) +{ + struct osmo_fd *ofd = osmo_stream_srv_get_ofd(conn); + struct osmo_ss7_asp *asp = osmo_stream_srv_get_data(conn); + struct msgb *msg = NULL; + int rc; + + /* read IPA message from socket and process it */ + rc = ipa_msg_recv_buffered(ofd->fd, &msg, &asp->pending_msg); + LOGPASP(asp, DLSS7, LOGL_DEBUG, "%s(): ipa_msg_recv_buffered() returned %d\n", + __func__, rc); + if (rc <= 0) { + if (rc == -EAGAIN) { + /* more data needed */ + return 0; + } + osmo_stream_srv_destroy(conn); + return rc; + } + if (osmo_ipa_process_msg(msg) < 0) { + LOGPASP(asp, DLSS7, LOGL_ERROR, "Bad IPA message\n"); + osmo_stream_srv_destroy(conn); + msgb_free(msg); + return -1; + } + msg->dst = asp; + + return ipa_rx_msg(asp, msg, ofd->fd & 0xf); +} + +/* netif code tells us we can read something from the socket */ +int ss7_asp_xua_srv_conn_cb(struct osmo_stream_srv *conn) +{ + struct osmo_ss7_asp *asp = osmo_stream_srv_get_data(conn); + struct msgb *msg = m3ua_msgb_alloc("xUA Server Rx"); + unsigned int ppid; + int flags; + int rc; + + if (!msg) + return -ENOMEM; + + /* read xUA message from socket and process it */ + rc = osmo_stream_srv_recv(conn, msg); + flags = msgb_sctp_msg_flags(msg); + + LOGPASP(asp, DLSS7, LOGL_DEBUG, "%s(): sctp_recvmsg() returned %d (flags=0x%x)\n", + __func__, rc, flags); + + if (flags & OSMO_STREAM_SCTP_MSG_FLAGS_NOTIFICATION) { + union sctp_notification *notif = (union sctp_notification *) msgb_data(msg); + log_sctp_notification(asp, "xUA CLNT", notif); + asp_handle_sctp_notif_monitor_primary_address(asp, notif); + + switch (notif->sn_header.sn_type) { + case SCTP_ASSOC_CHANGE: + if (notif->sn_assoc_change.sac_state == SCTP_RESTART) + xua_asp_send_xlm_prim_simple(asp, OSMO_XLM_PRIM_M_SCTP_RESTART, + PRIM_OP_INDICATION); + default: + break; + } + + if (rc == 0) { + osmo_stream_srv_destroy(conn); + rc = -EBADF; + } else { + rc = 0; + } + goto out; + } + if (rc < 0) { + osmo_stream_srv_destroy(conn); + rc = -EBADF; + goto out; + } else if (rc == 0) { + osmo_stream_srv_destroy(conn); + rc = -EBADF; + goto out; + } + + ppid = msgb_sctp_ppid(msg); + msg->dst = asp; + rate_ctr_inc2(asp->ctrg, SS7_ASP_CTR_PKT_RX_TOTAL); + + if (ppid == SUA_PPID && asp->cfg.proto == OSMO_SS7_ASP_PROT_SUA) + rc = sua_rx_msg(asp, msg); + else if (ppid == M3UA_PPID && asp->cfg.proto == OSMO_SS7_ASP_PROT_M3UA) + rc = m3ua_rx_msg(asp, msg); + else + rc = ss7_asp_rx_unknown(asp, ppid, msg); + +out: + msgb_free(msg); + return rc; +} + +/* client has established SCTP connection to server */ +static int xua_cli_connect_cb(struct osmo_stream_cli *cli) +{ + struct osmo_fd *ofd = osmo_stream_cli_get_ofd(cli); + struct osmo_ss7_asp *asp = osmo_stream_cli_get_data(cli); + int rc = 0; + + /* update the socket name */ + talloc_free(asp->sock_name); + asp->sock_name = osmo_sock_get_name(asp, ofd->fd); + + LOGPASP(asp, DLSS7, LOGL_INFO, "Client connected %s\n", asp->sock_name); + + /* Now that we have the conn in place, the local/remote addresses are + * fed and the local port is known for sure. Apply SCTP Primary addresses + * if needed: + */ + if (asp->cfg.proto != OSMO_SS7_ASP_PROT_IPA) { + rc = ss7_asp_apply_peer_primary_address(asp); + rc = ss7_asp_apply_primary_address(asp); + } + + if (asp->lm && asp->lm->prim_cb) { + /* Notify layer manager that a connection has been + * established */ + xua_asp_send_xlm_prim_simple(asp, OSMO_XLM_PRIM_M_SCTP_ESTABLISH, PRIM_OP_INDICATION); + } else { + /* directly as the ASP FSM to start by sending an ASP-UP ... */ + osmo_fsm_inst_dispatch(asp->fi, XUA_ASP_E_M_ASP_UP_REQ, NULL); + } + + return rc; +} + +static void xua_cli_close(struct osmo_stream_cli *cli) +{ + struct osmo_ss7_asp *asp = osmo_stream_cli_get_data(cli); + + osmo_stream_cli_close(cli); + osmo_fsm_inst_dispatch(asp->fi, XUA_ASP_E_SCTP_COMM_DOWN_IND, asp); + /* send M-SCTP_RELEASE.ind to XUA Layer Manager */ + xua_asp_send_xlm_prim_simple(asp, OSMO_XLM_PRIM_M_SCTP_RELEASE, PRIM_OP_INDICATION); +} + +static void xua_cli_close_and_reconnect(struct osmo_stream_cli *cli) +{ + xua_cli_close(cli); + osmo_stream_cli_reconnect(cli); +} + +/* read call-back for IPA/SCCPlite socket */ +static int ipa_cli_read_cb(struct osmo_stream_cli *conn) +{ + struct osmo_fd *ofd = osmo_stream_cli_get_ofd(conn); + struct osmo_ss7_asp *asp = osmo_stream_cli_get_data(conn); + struct msgb *msg = NULL; + int rc; + + /* read IPA message from socket and process it */ + rc = ipa_msg_recv_buffered(ofd->fd, &msg, &asp->pending_msg); + LOGPASP(asp, DLSS7, LOGL_DEBUG, "%s(): ipa_msg_recv_buffered() returned %d\n", + __func__, rc); + if (rc <= 0) { + if (rc == -EAGAIN) { + /* more data needed */ + return 0; + } + xua_cli_close_and_reconnect(conn); + return rc; + } + if (osmo_ipa_process_msg(msg) < 0) { + LOGPASP(asp, DLSS7, LOGL_ERROR, "Bad IPA message\n"); + xua_cli_close_and_reconnect(conn); + msgb_free(msg); + return -1; + } + msg->dst = asp; + rate_ctr_inc2(asp->ctrg, SS7_ASP_CTR_PKT_RX_TOTAL); + return ipa_rx_msg(asp, msg, ofd->fd & 0xf); +} + +static int xua_cli_read_cb(struct osmo_stream_cli *conn) +{ + struct osmo_ss7_asp *asp = osmo_stream_cli_get_data(conn); + struct msgb *msg = m3ua_msgb_alloc("xUA Client Rx"); + unsigned int ppid; + int flags; + int rc; + + if (!msg) + return -ENOMEM; + + /* read xUA message from socket and process it */ + rc = osmo_stream_cli_recv(conn, msg); + flags = msgb_sctp_msg_flags(msg); + + LOGPASP(asp, DLSS7, LOGL_DEBUG, "%s(): sctp_recvmsg() returned %d (flags=0x%x)\n", + __func__, rc, flags); + + if (flags & OSMO_STREAM_SCTP_MSG_FLAGS_NOTIFICATION) { + union sctp_notification *notif = (union sctp_notification *) msgb_data(msg); + log_sctp_notification(asp, "xUA CLNT", notif); + asp_handle_sctp_notif_monitor_primary_address(asp, notif); + + switch (notif->sn_header.sn_type) { + case SCTP_ASSOC_CHANGE: + if (notif->sn_assoc_change.sac_state == SCTP_RESTART) + xua_asp_send_xlm_prim_simple(asp, OSMO_XLM_PRIM_M_SCTP_RESTART, + PRIM_OP_INDICATION); + default: + break; + } + + if (rc == 0) + xua_cli_close_and_reconnect(conn); + rc = 0; + goto out; + } + if (rc < 0) { + xua_cli_close_and_reconnect(conn); + goto out; + } else if (rc == 0) { + xua_cli_close_and_reconnect(conn); + goto out; + } + + ppid = msgb_sctp_ppid(msg); + msg->dst = asp; + rate_ctr_inc2(asp->ctrg, SS7_ASP_CTR_PKT_RX_TOTAL); + + if (ppid == SUA_PPID && asp->cfg.proto == OSMO_SS7_ASP_PROT_SUA) + rc = sua_rx_msg(asp, msg); + else if (ppid == M3UA_PPID && asp->cfg.proto == OSMO_SS7_ASP_PROT_M3UA) + rc = m3ua_rx_msg(asp, msg); + else + rc = ss7_asp_rx_unknown(asp, ppid, msg); + +out: + msgb_free(msg); + return rc; +} + +int ss7_asp_xua_srv_conn_closed_cb(struct osmo_stream_srv *srv) +{ + struct osmo_ss7_asp *asp = osmo_stream_srv_get_data(srv); + + LOGP(DLSS7, LOGL_INFO, "%s: connection closed\n", asp ? asp->cfg.name : "?"); + + if (!asp) + return 0; + + /* notify ASP FSM and everyone else */ + osmo_fsm_inst_dispatch(asp->fi, XUA_ASP_E_SCTP_COMM_DOWN_IND, NULL); + + /* delete any RKM-dynamically allocated ASs for this ASP */ + xua_rkm_cleanup_dyn_as_for_asp(asp); + + /* send M-SCTP_RELEASE.ind to Layer Manager */ + xua_asp_send_xlm_prim_simple(asp, OSMO_XLM_PRIM_M_SCTP_RELEASE, PRIM_OP_INDICATION); + + asp->server = NULL; + + /* if we were dynamically allocated at accept_cb() time, let's + * self-destruct now. A new connection will re-create the ASP. */ + if (asp->dyn_allocated) { + /* avoid re-entrance via osmo_stream_srv_destroy() which + * called us */ + osmo_ss7_asp_destroy(asp); + } + + return 0; +} + +/*! \brief send a fully encoded msgb via a given ASP + * \param[in] asp Application Server Process through which to send + * \param[in] msg message buffer to transmit. Ownership transferred. + * \returns 0 on success; negative in case of error */ +int osmo_ss7_asp_send(struct osmo_ss7_asp *asp, struct msgb *msg) +{ + OSMO_ASSERT(ss7_initialized); + + switch (asp->cfg.proto) { + case OSMO_SS7_ASP_PROT_SUA: + msgb_sctp_ppid(msg) = SUA_PPID; + break; + case OSMO_SS7_ASP_PROT_M3UA: + msgb_sctp_ppid(msg) = M3UA_PPID; + break; + case OSMO_SS7_ASP_PROT_IPA: + break; + default: + OSMO_ASSERT(0); + } + + rate_ctr_inc2(asp->ctrg, SS7_ASP_CTR_PKT_TX_TOTAL); + + if (asp->cfg.is_server) { + if (!asp->server) { + LOGPASP(asp, DLSS7, LOGL_ERROR, "Cannot transmit, no asp->server\n"); + /* FIXME: what to do here? delete the route? send DUNA? */ + msgb_free(msg); + return -EIO; + } + osmo_stream_srv_send(asp->server, msg); + } else { + if (!asp->client) { + LOGPASP(asp, DLSS7, LOGL_ERROR, "Cannot transmit, no asp->client\n"); + /* FIXME: what to do here? delete the route? send DUNA? */ + msgb_free(msg); + return -EIO; + } + if (!osmo_stream_cli_is_connected(asp->client)) { + LOGPASP(asp, DLSS7, LOGL_ERROR, "Cannot transmit, asp->client not connected\n"); + msgb_free(msg); + return -EIO; + } + osmo_stream_cli_send(asp->client, msg); + } + + return 0; +} + +void osmo_ss7_asp_disconnect(struct osmo_ss7_asp *asp) +{ + if (asp->server) + osmo_stream_srv_destroy(asp->server); + /* the close_cb() will handle the remaining cleanup here */ + else if (asp->client) + xua_cli_close_and_reconnect(asp->client); +} + +static osmo_ss7_asp_rx_unknown_cb *g_osmo_ss7_asp_rx_unknown_cb; + +/*! Register a call-back function for unknown SCTP PPID / IPA Stream ID */ +void osmo_ss7_register_rx_unknown_cb(osmo_ss7_asp_rx_unknown_cb *cb) +{ + g_osmo_ss7_asp_rx_unknown_cb = cb; +} + +int ss7_asp_rx_unknown(struct osmo_ss7_asp *asp, int ppid_mux, struct msgb *msg) +{ + rate_ctr_inc2(asp->ctrg, SS7_ASP_CTR_PKT_RX_UNKNOWN); + + if (g_osmo_ss7_asp_rx_unknown_cb) + return (*g_osmo_ss7_asp_rx_unknown_cb)(asp, ppid_mux, msg); + + switch (asp->cfg.proto) { + case OSMO_SS7_ASP_PROT_IPA: + LOGPASP(asp, DLSS7, LOGL_NOTICE, "Rx IPA for unknown Stream ID 0x%02x: %s\n", + ppid_mux, msgb_hexdump(msg)); + break; + default: + LOGPASP(asp, DLSS7, LOGL_NOTICE, "Rx SCTP chunk for unknown PPID %u: %s\n", + ppid_mux, msgb_hexdump(msg)); + break; + } + return 0; +} + +/*! Get the logging subsystem for a given ASP. Used by generic code. */ +int osmo_ss7_asp_get_log_subsys(const struct osmo_ss7_asp *asp) +{ + switch (asp->cfg.proto) { + case OSMO_SS7_ASP_PROT_M3UA: + return DLM3UA; + case OSMO_SS7_ASP_PROT_SUA: + return DLSUA; + default: + return DLSS7; + } +} + +/*! \brief Get the name of a given ASP + * \param[in] asp The ASP for which the name is requested + * \returns The name of the ASP, or NULL if not set + */ +const char *osmo_ss7_asp_get_name(const struct osmo_ss7_asp *asp) +{ + return asp->cfg.name; +} + +/*! \brief Get the proto of a given ASP + * \param[in] asp The ASP for which the proto is requested + * \returns The proto of the ASP + */ +enum osmo_ss7_asp_protocol osmo_ss7_asp_get_proto(const struct osmo_ss7_asp *asp) +{ + return asp->cfg.proto; +} diff --git a/src/osmo_ss7_xua_srv.c b/src/osmo_ss7_xua_srv.c new file mode 100644 index 0000000..04ae893 --- /dev/null +++ b/src/osmo_ss7_xua_srv.c @@ -0,0 +1,300 @@ +/* Core SS7 xUA Server */ + +/* (C) 2015-2017 by Harald Welte <laforge(a)gnumonks.org> + * (C) 2023 by sysmocom s.f.m.c. GmbH <info(a)sysmocom.de> + * All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0+ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <inttypes.h> +#include <netdb.h> +#include <netinet/in.h> +#include <netinet/sctp.h> + +#include <osmocom/sigtran/osmo_ss7.h> +#include <osmocom/sigtran/mtp_sap.h> +#include <osmocom/sigtran/protocol/mtp.h> +#include <osmocom/sigtran/protocol/sua.h> +#include <osmocom/sigtran/protocol/m3ua.h> + +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/select.h> +#include <osmocom/core/utils.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/logging.h> +#include <osmocom/core/msgb.h> +#include <osmocom/core/socket.h> +#include <osmocom/core/sockaddr_str.h> + +#include <osmocom/netif/stream.h> +#include <osmocom/netif/ipa.h> +#include <osmocom/netif/sctp.h> + +#include "sccp_internal.h" +#include "xua_internal.h" +#include "ss7_internal.h" +#include "xua_asp_fsm.h" +#include "xua_as_fsm.h" + +/*********************************************************************** + * SS7 xUA Server + ***********************************************************************/ + +/* server has accept()ed a new SCTP association, let's find the ASP for + * it (if any) */ +static int xua_accept_cb(struct osmo_stream_srv_link *link, int fd) +{ + struct osmo_xua_server *oxs = osmo_stream_srv_link_get_data(link); + struct osmo_stream_srv *srv; + struct osmo_ss7_asp *asp; + char *sock_name = osmo_sock_get_name(link, fd); + const char *proto_name = get_value_string(osmo_ss7_asp_protocol_vals, oxs->cfg.proto); + int rc = 0; + + LOGP(DLSS7, LOGL_INFO, "%s: New %s connection accepted\n", sock_name, proto_name); + + if (oxs->cfg.proto == OSMO_SS7_ASP_PROT_IPA) { + srv = osmo_stream_srv_create(oxs, link, fd, + ss7_asp_ipa_srv_conn_cb, + ss7_asp_xua_srv_conn_closed_cb, NULL); + } else { + srv = osmo_stream_srv_create(oxs, link, fd, + ss7_asp_xua_srv_conn_cb, + ss7_asp_xua_srv_conn_closed_cb, NULL); + } + if (!srv) { + LOGP(DLSS7, LOGL_ERROR, "%s: Unable to create stream server " + "for connection\n", sock_name); + close(fd); + talloc_free(sock_name); + return -1; + } + + asp = ss7_asp_find_by_socket_addr(fd); + if (asp) { + LOGP(DLSS7, LOGL_INFO, "%s: matched connection to ASP %s\n", + sock_name, asp->cfg.name); + /* we need to check if we already have a socket associated, and close it. Otherwise it might + * happen that both the listen-fd for this accept() and the old socket are marked 'readable' + * during the same scheduling interval, and we're processing them in the "wrong" order, i.e. + * we first see the accept of the new fd before we see the close on the old fd */ + if (asp->server) { + LOGPASP(asp, DLSS7, LOGL_FATAL, "accept of new connection from %s before old was closed " + "-> close old one\n", sock_name); + osmo_stream_srv_set_data(asp->server, NULL); + osmo_stream_srv_destroy(asp->server); + asp->server = NULL; + } + } else { + if (!oxs->cfg.accept_dyn_reg) { + LOGP(DLSS7, LOGL_NOTICE, "%s: %s connection without matching " + "ASP definition and no dynamic registration enabled, terminating\n", + sock_name, proto_name); + } else { + char namebuf[32]; + static uint32_t dyn_asp_num = 0; + snprintf(namebuf, sizeof(namebuf), "asp-dyn-%u", dyn_asp_num++); + asp = osmo_ss7_asp_find_or_create(oxs->inst, namebuf, 0, 0, + oxs->cfg.proto); + if (asp) { + char hostbuf[INET6_ADDRSTRLEN]; + const char *hostbuf_ptr = &hostbuf[0]; + char portbuf[16]; + + osmo_sock_get_ip_and_port(fd, hostbuf, sizeof(hostbuf), portbuf, sizeof(portbuf), false); + LOGP(DLSS7, LOGL_INFO, "%s: created dynamic ASP %s\n", + sock_name, asp->cfg.name); + asp->cfg.is_server = true; + asp->cfg.role = OSMO_SS7_ASP_ROLE_SG; + asp->cfg.local.port = oxs->cfg.local.port; + asp->cfg.remote.port = atoi(portbuf); + asp->dyn_allocated = true; + asp->server = srv; + osmo_ss7_asp_peer_set_hosts(&asp->cfg.local, asp, + (const char * const*)oxs->cfg.local.host, + oxs->cfg.local.host_cnt); + osmo_ss7_asp_peer_set_hosts(&asp->cfg.remote, asp, + &hostbuf_ptr, 1); + osmo_ss7_asp_restart(asp); + } + } + if (!asp) { + osmo_stream_srv_destroy(srv); + talloc_free(sock_name); + return -1; + } + llist_add_tail(&asp->siblings, &oxs->asp_list); + } + + /* update the ASP reference back to the server over which the + * connection came in */ + asp->server = srv; + asp->xua_server = oxs; + + /* update the ASP socket name */ + talloc_free(asp->sock_name); + asp->sock_name = talloc_reparent(link, asp, sock_name); + osmo_stream_srv_set_name(asp->server, asp->cfg.name); + /* make sure the conn_cb() is called with the asp as private + * data */ + osmo_stream_srv_set_data(srv, asp); + + if (oxs->cfg.proto != OSMO_SS7_ASP_PROT_IPA) { + rc = ss7_asp_apply_peer_primary_address(asp); + rc = ss7_asp_apply_primary_address(asp); + } + + /* send M-SCTP_ESTABLISH.ind to Layer Manager */ + osmo_fsm_inst_dispatch(asp->fi, XUA_ASP_E_SCTP_EST_IND, 0); + xua_asp_send_xlm_prim_simple(asp, OSMO_XLM_PRIM_M_SCTP_ESTABLISH, PRIM_OP_INDICATION); + + return rc; +} + +/*! \brief create a new xUA server configured with given ip/port + * \param[in] ctx talloc allocation context + * \param[in] proto protocol (xUA variant) to use + * \param[in] local_port local SCTP port to bind/listen to + * \param[in] local_host local IP address to bind/listen to (optional) + * \returns callee-allocated \ref osmo_xua_server in case of success + */ +struct osmo_xua_server * +osmo_ss7_xua_server_create(struct osmo_ss7_instance *inst, enum osmo_ss7_asp_protocol proto, + uint16_t local_port, const char *local_host) +{ + struct osmo_xua_server *oxs = talloc_zero(inst, struct osmo_xua_server); + + OSMO_ASSERT(ss7_initialized); + if (!oxs) + return NULL; + + LOGP(DLSS7, LOGL_INFO, "Creating %s Server %s:%u\n", + get_value_string(osmo_ss7_asp_protocol_vals, proto), local_host, local_port); + + INIT_LLIST_HEAD(&oxs->asp_list); + + oxs->cfg.proto = proto; + oxs->cfg.local.port = local_port; + + oxs->server = osmo_stream_srv_link_create(oxs); + osmo_stream_srv_link_set_name(oxs->server, osmo_ss7_asp_protocol_name(proto)); + osmo_stream_srv_link_set_data(oxs->server, oxs); + osmo_stream_srv_link_set_accept_cb(oxs->server, xua_accept_cb); + + osmo_stream_srv_link_set_nodelay(oxs->server, true); + osmo_stream_srv_link_set_port(oxs->server, oxs->cfg.local.port); + osmo_stream_srv_link_set_proto(oxs->server, ss7_asp_proto_to_ip_proto(proto)); + + osmo_ss7_xua_server_set_local_host(oxs, local_host); + + LOGP(DLSS7, LOGL_INFO, "Created %s server on %s:%" PRIu16 "\n", + get_value_string(osmo_ss7_asp_protocol_vals, proto), local_host, local_port); + + oxs->inst = inst; + llist_add_tail(&oxs->list, &inst->xua_servers); + + /* The SUA code internally needs SCCP to work */ + if (proto == OSMO_SS7_ASP_PROT_SUA) + osmo_ss7_ensure_sccp(inst); + + return oxs; +} + +/*! \brief Set the xUA server to bind/listen to the currently configured ip/port + * \param[in] xs xUA server to operate + * \returns 0 on success, negative value on error. + */ +int +osmo_ss7_xua_server_bind(struct osmo_xua_server *xs) +{ + char buf[512]; + int rc; + const char *proto = get_value_string(osmo_ss7_asp_protocol_vals, xs->cfg.proto); + + rc = osmo_ss7_asp_peer_snprintf(buf, sizeof(buf), &xs->cfg.local); + if (rc < 0) { + LOGP(DLSS7, LOGL_INFO, "Failed parsing %s Server osmo_ss7_asp_peer\n", proto); + } else { + LOGP(DLSS7, LOGL_INFO, "(Re)binding %s Server to %s\n", + proto, buf); + } + return osmo_stream_srv_link_open(xs->server); +} + +int +osmo_ss7_xua_server_set_local_host(struct osmo_xua_server *xs, const char *local_host) +{ + return osmo_ss7_xua_server_set_local_hosts(xs, &local_host, 1); +} + +int +osmo_ss7_xua_server_set_local_hosts(struct osmo_xua_server *xs, const char **local_hosts, size_t local_host_cnt) +{ + int rc; + OSMO_ASSERT(ss7_initialized); + + rc = osmo_ss7_asp_peer_set_hosts(&xs->cfg.local, xs, local_hosts, local_host_cnt); + if (rc < 0) + return rc; + return osmo_stream_srv_link_set_addrs(xs->server, (const char **)xs->cfg.local.host, xs->cfg.local.host_cnt); +} + +int +osmo_ss7_xua_server_add_local_host(struct osmo_xua_server *xs, const char *local_host) +{ + int rc; + + rc = osmo_ss7_asp_peer_add_host(&xs->cfg.local, xs, local_host); + if (rc < 0) + return rc; + return osmo_stream_srv_link_set_addrs(xs->server, (const char **)xs->cfg.local.host, xs->cfg.local.host_cnt); +} + +bool ss7_xua_server_set_default_local_hosts(struct osmo_xua_server *oxs) +{ + /* If no local addr was set, or erased after _create(): */ + if (!oxs->cfg.local.host_cnt) { + /* "::" Covers both IPv4 and IPv6 */ + if (ss7_ipv6_sctp_supported("::", true)) + osmo_ss7_xua_server_set_local_host(oxs, "::"); + else + osmo_ss7_xua_server_set_local_host(oxs, "0.0.0.0"); + return true; + } + return false; +} + +void osmo_ss7_xua_server_destroy(struct osmo_xua_server *xs) +{ + struct osmo_ss7_asp *asp, *asp2; + + if (xs->server) { + osmo_stream_srv_link_close(xs->server); + osmo_stream_srv_link_destroy(xs->server); + } + /* iterate and close all connections established in relation + * with this server */ + llist_for_each_entry_safe(asp, asp2, &xs->asp_list, siblings) + osmo_ss7_asp_destroy(asp); + + llist_del(&xs->list); + talloc_free(xs); +} diff --git a/src/ss7_internal.h b/src/ss7_internal.h index f6669e2..34378e8 100644 --- a/src/ss7_internal.h +++ b/src/ss7_internal.h @@ -5,10 +5,31 @@ #include <stdbool.h> #include <osmocom/sigtran/osmo_ss7.h> +extern bool ss7_initialized; + +bool ss7_ipv6_sctp_supported(const char *host, bool bind); + +struct osmo_ss7_asp *ss7_asp_alloc(struct osmo_ss7_instance *inst, const char *name, + uint16_t remote_port, uint16_t local_port, + enum osmo_ss7_asp_protocol proto); bool ss7_asp_set_default_peer_hosts(struct osmo_ss7_asp *asp); +struct osmo_ss7_asp *ss7_asp_find_by_socket_addr(int fd); +int ss7_asp_proto_to_ip_proto(enum osmo_ss7_asp_protocol proto); +int ss7_asp_ipa_srv_conn_cb(struct osmo_stream_srv *conn); +int ss7_asp_xua_srv_conn_cb(struct osmo_stream_srv *conn); +int ss7_asp_xua_srv_conn_closed_cb(struct osmo_stream_srv *srv); +int ss7_asp_apply_peer_primary_address(const struct osmo_ss7_asp *asp); +int ss7_asp_apply_primary_address(const struct osmo_ss7_asp *asp); + bool ss7_xua_server_set_default_local_hosts(struct osmo_xua_server *oxs); enum ss7_as_ctr { SS7_AS_CTR_RX_MSU_TOTAL, SS7_AS_CTR_TX_MSU_TOTAL, }; + +enum ss7_asp_ctr { + SS7_ASP_CTR_PKT_RX_TOTAL, + SS7_ASP_CTR_PKT_RX_UNKNOWN, + SS7_ASP_CTR_PKT_TX_TOTAL, +}; -- To view, visit https://gerrit.osmocom.org/c/libosmo-sccp/+/34329?usp=email To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings Gerrit-Project: libosmo-sccp Gerrit-Branch: master Gerrit-Change-Id: I3d43268459d61b0b9f9bec34bf31dc0851fa5e48 Gerrit-Change-Number: 34329 Gerrit-PatchSet: 1 Gerrit-Owner: pespin <pespin(a)sysmocom.de> Gerrit-MessageType: newchange
1 0
0 0
[S] Change in libosmo-sccp[master]: ss7: Rename internal APIs to avoid exporting them in so file
by pespin 07 Sep '23

07 Sep '23
pespin has uploaded this change for review. ( https://gerrit.osmocom.org/c/libosmo-sccp/+/34327?usp=email ) Change subject: ss7: Rename internal APIs to avoid exporting them in so file ...................................................................... ss7: Rename internal APIs to avoid exporting them in so file Change-Id: Ibb22c1dc1ec73fc574cc3d38664874114f681c37 --- M src/osmo_ss7.c M src/osmo_ss7_vty.c M src/sccp_user.c M src/ss7_internal.h 4 files changed, 16 insertions(+), 7 deletions(-) git pull ssh://gerrit.osmocom.org:29418/libosmo-sccp refs/changes/27/34327/1 diff --git a/src/osmo_ss7.c b/src/osmo_ss7.c index 440014a..d097fc9 100644 --- a/src/osmo_ss7.c +++ b/src/osmo_ss7.c @@ -1583,7 +1583,7 @@ * afterwards in order to apply the new settings. * This API is internal, hence doesn't appear in osmo_ss7.h */ -bool osmo_ss7_asp_set_default_peer_hosts(struct osmo_ss7_asp *asp) +bool ss7_asp_set_default_peer_hosts(struct osmo_ss7_asp *asp) { bool changed = false; /* If no local addr was set */ @@ -2577,7 +2577,7 @@ return osmo_stream_srv_link_set_addrs(xs->server, (const char **)xs->cfg.local.host, xs->cfg.local.host_cnt); } -bool osmo_ss7_xua_server_set_default_local_hosts(struct osmo_xua_server *oxs) +bool ss7_xua_server_set_default_local_hosts(struct osmo_xua_server *oxs) { /* If no local addr was set, or erased after _create(): */ if (!oxs->cfg.local.host_cnt) { diff --git a/src/osmo_ss7_vty.c b/src/osmo_ss7_vty.c index 44ba780..df4c8fc 100644 --- a/src/osmo_ss7_vty.c +++ b/src/osmo_ss7_vty.c @@ -2071,7 +2071,7 @@ case L_CS7_ASP_NODE: asp = vty->index; /* Make sure proper defaults values are set */ - osmo_ss7_asp_set_default_peer_hosts(asp); + ss7_asp_set_default_peer_hosts(asp); osmo_ss7_asp_restart(asp); vty->node = L_CS7_NODE; vty->index = asp->inst; @@ -2089,7 +2089,7 @@ case L_CS7_XUA_NODE: oxs = vty->index; /* If no local addr was set, or erased after _create(): */ - osmo_ss7_xua_server_set_default_local_hosts(oxs); + ss7_xua_server_set_default_local_hosts(oxs); if (osmo_ss7_xua_server_bind(oxs) < 0) vty_out(vty, "%% Unable to bind xUA server to IP(s)%s", VTY_NEWLINE); vty->node = L_CS7_NODE; diff --git a/src/sccp_user.c b/src/sccp_user.c index 382e1fe..94b6d02 100644 --- a/src/sccp_user.c +++ b/src/sccp_user.c @@ -643,7 +643,7 @@ if (default_remote_ip) osmo_ss7_asp_peer_set_hosts(&asp->cfg.remote, asp, &default_remote_ip, 1); /* Make sure proper defaults are applied if app didn't provide specific default values */ - osmo_ss7_asp_set_default_peer_hosts(asp); + ss7_asp_set_default_peer_hosts(asp); asp->simple_client_allocated = true; } diff --git a/src/ss7_internal.h b/src/ss7_internal.h index 712a6ed..f6669e2 100644 --- a/src/ss7_internal.h +++ b/src/ss7_internal.h @@ -5,8 +5,8 @@ #include <stdbool.h> #include <osmocom/sigtran/osmo_ss7.h> -bool osmo_ss7_asp_set_default_peer_hosts(struct osmo_ss7_asp *asp); -bool osmo_ss7_xua_server_set_default_local_hosts(struct osmo_xua_server *oxs); +bool ss7_asp_set_default_peer_hosts(struct osmo_ss7_asp *asp); +bool ss7_xua_server_set_default_local_hosts(struct osmo_xua_server *oxs); enum ss7_as_ctr { SS7_AS_CTR_RX_MSU_TOTAL, -- To view, visit https://gerrit.osmocom.org/c/libosmo-sccp/+/34327?usp=email To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings Gerrit-Project: libosmo-sccp Gerrit-Branch: master Gerrit-Change-Id: Ibb22c1dc1ec73fc574cc3d38664874114f681c37 Gerrit-Change-Number: 34327 Gerrit-PatchSet: 1 Gerrit-Owner: pespin <pespin(a)sysmocom.de> Gerrit-MessageType: newchange
1 0
0 0
[S] Change in osmo-ttcn3-hacks[master]: MNCC_EncDec.c: fix missing SDP in enc of mncc_rtp
by pespin 07 Sep '23

07 Sep '23
Attention is currently required from: neels. pespin has posted comments on this change. ( https://gerrit.osmocom.org/c/osmo-ttcn3-hacks/+/34326?usp=email ) Change subject: MNCC_EncDec.c: fix missing SDP in enc of mncc_rtp ...................................................................... Patch Set 1: Code-Review+1 -- To view, visit https://gerrit.osmocom.org/c/osmo-ttcn3-hacks/+/34326?usp=email To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings Gerrit-Project: osmo-ttcn3-hacks Gerrit-Branch: master Gerrit-Change-Id: Ice4e9ac63d9d8c8bff33298eb1e1dcb413179cc9 Gerrit-Change-Number: 34326 Gerrit-PatchSet: 1 Gerrit-Owner: neels <nhofmeyr(a)sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: pespin <pespin(a)sysmocom.de> Gerrit-Attention: neels <nhofmeyr(a)sysmocom.de> Gerrit-Comment-Date: Thu, 07 Sep 2023 09:05:09 +0000 Gerrit-HasComments: No Gerrit-Has-Labels: Yes Gerrit-MessageType: comment
1 0
0 0
[L] Change in libosmo-netif[master]: stream: Add client-side (segmentation) support for IPA
by osmith 07 Sep '23

07 Sep '23
Attention is currently required from: arehbein, daniel, fixeria, pespin. osmith has posted comments on this change. ( https://gerrit.osmocom.org/c/libosmo-netif/+/33198?usp=email ) Change subject: stream: Add client-side (segmentation) support for IPA ...................................................................... Patch Set 21: (1 comment) Patchset: PS21: > Sorry for the false alarm @osmith@sysmocom.de , and thanks for taking a look. […] I guess printing the timestamp infront of the log should just be removed? from a quick glance this seems to be the problem, if there is more load on the machine then it may pause for a bit before doing the next loop iteration, which leads to getting different timestamps. -- To view, visit https://gerrit.osmocom.org/c/libosmo-netif/+/33198?usp=email To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings Gerrit-Project: libosmo-netif Gerrit-Branch: master Gerrit-Change-Id: I822abf52c6ae396c90b5c50228a0a39c848d3de6 Gerrit-Change-Number: 33198 Gerrit-PatchSet: 21 Gerrit-Owner: arehbein <arehbein(a)sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: laforge <laforge(a)osmocom.org> Gerrit-Reviewer: pespin <pespin(a)sysmocom.de> Gerrit-CC: daniel <dwillmann(a)sysmocom.de> Gerrit-CC: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-CC: osmith <osmith(a)sysmocom.de> Gerrit-Attention: arehbein <arehbein(a)sysmocom.de> Gerrit-Attention: pespin <pespin(a)sysmocom.de> Gerrit-Attention: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Attention: daniel <dwillmann(a)sysmocom.de> Gerrit-Comment-Date: Thu, 07 Sep 2023 08:46:35 +0000 Gerrit-HasComments: Yes Gerrit-Has-Labels: No Comment-In-Reply-To: osmith <osmith(a)sysmocom.de> Comment-In-Reply-To: arehbein <arehbein(a)sysmocom.de> Comment-In-Reply-To: daniel <dwillmann(a)sysmocom.de> Gerrit-MessageType: comment
1 0
0 0
[S] Change in osmo-ttcn3-hacks[master]: MNCC_EncDec.c: fix missing SDP in enc of mncc_rtp
by neels 07 Sep '23

07 Sep '23
neels has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmo-ttcn3-hacks/+/34326?usp=email ) Change subject: MNCC_EncDec.c: fix missing SDP in enc of mncc_rtp ...................................................................... MNCC_EncDec.c: fix missing SDP in enc of mncc_rtp Change-Id: Ice4e9ac63d9d8c8bff33298eb1e1dcb413179cc9 --- M library/MNCC_EncDec.cc 1 file changed, 13 insertions(+), 0 deletions(-) git pull ssh://gerrit.osmocom.org:29418/osmo-ttcn3-hacks refs/changes/26/34326/1 diff --git a/library/MNCC_EncDec.cc b/library/MNCC_EncDec.cc index ffc2aa8..0119863 100644 --- a/library/MNCC_EncDec.cc +++ b/library/MNCC_EncDec.cc @@ -229,6 +229,10 @@ } rtp.payload_type = in.u().rtp().payload__type(); rtp.payload_msg_type = in.u().rtp().payload__msg__type(); + if (in.u().rtp().sdp().is_value()) { + const CHARSTRING &sdp = in.u().rtp().sdp(); + strncpy(rtp.sdp, sdp, sizeof(rtp.sdp)); + } ret_val = OCTETSTRING(sizeof(rtp), (uint8_t *) &rtp); break; case MNCC__MsgUnion::ALT_hello: -- To view, visit https://gerrit.osmocom.org/c/osmo-ttcn3-hacks/+/34326?usp=email To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings Gerrit-Project: osmo-ttcn3-hacks Gerrit-Branch: master Gerrit-Change-Id: Ice4e9ac63d9d8c8bff33298eb1e1dcb413179cc9 Gerrit-Change-Number: 34326 Gerrit-PatchSet: 1 Gerrit-Owner: neels <nhofmeyr(a)sysmocom.de> Gerrit-MessageType: newchange
1 0
0 0
[M] Change in osmo-bsc[master]: bts_siemens_bs11: remove ip.access nanoBTS specific code
by pespin 06 Sep '23

06 Sep '23
Attention is currently required from: fixeria, laforge. pespin has posted comments on this change. ( https://gerrit.osmocom.org/c/osmo-bsc/+/34321?usp=email ) Change subject: bts_siemens_bs11: remove ip.access nanoBTS specific code ...................................................................... Patch Set 2: Code-Review+1 -- To view, visit https://gerrit.osmocom.org/c/osmo-bsc/+/34321?usp=email To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings Gerrit-Project: osmo-bsc Gerrit-Branch: master Gerrit-Change-Id: I2db92fe448b1f4cd25c1e5c6e1bb9593795c9cb2 Gerrit-Change-Number: 34321 Gerrit-PatchSet: 2 Gerrit-Owner: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: laforge <laforge(a)osmocom.org> Gerrit-Reviewer: pespin <pespin(a)sysmocom.de> Gerrit-Attention: laforge <laforge(a)osmocom.org> Gerrit-Attention: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Comment-Date: Wed, 06 Sep 2023 18:11:32 +0000 Gerrit-HasComments: No Gerrit-Has-Labels: Yes Gerrit-MessageType: comment
1 0
0 0
[M] Change in osmo-bsc[master]: abis_nm: parse feature flags in NM_ATT_IPACC_SUPP_FEATURES
by pespin 06 Sep '23

06 Sep '23
Attention is currently required from: fixeria, laforge. pespin has posted comments on this change. ( https://gerrit.osmocom.org/c/osmo-bsc/+/34323?usp=email ) Change subject: abis_nm: parse feature flags in NM_ATT_IPACC_SUPP_FEATURES ...................................................................... Patch Set 2: Code-Review+1 -- To view, visit https://gerrit.osmocom.org/c/osmo-bsc/+/34323?usp=email To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings Gerrit-Project: osmo-bsc Gerrit-Branch: master Gerrit-Change-Id: I64cffa0bdead3617cc169c83b0f6ddf74f0866a7 Gerrit-Change-Number: 34323 Gerrit-PatchSet: 2 Gerrit-Owner: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: laforge <laforge(a)osmocom.org> Gerrit-Reviewer: pespin <pespin(a)sysmocom.de> Gerrit-Attention: laforge <laforge(a)osmocom.org> Gerrit-Attention: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Comment-Date: Wed, 06 Sep 2023 18:10:38 +0000 Gerrit-HasComments: No Gerrit-Has-Labels: Yes Gerrit-MessageType: comment
1 0
0 0
[L] Change in libosmo-netif[master]: stream: Add client-side (segmentation) support for IPA
by arehbein 06 Sep '23

06 Sep '23
Attention is currently required from: daniel, fixeria, osmith, pespin. arehbein has posted comments on this change. ( https://gerrit.osmocom.org/c/libosmo-netif/+/33198?usp=email ) Change subject: stream: Add client-side (segmentation) support for IPA ...................................................................... Patch Set 21: (1 comment) Patchset: PS21: > Funny enough this seems to be the issue arehbein is having with timestamps Sorry for the false alarm @osmith@sysmocom.de , and thanks for taking a look. @dwillmann@sysmocom.de yeah, very much looks like it. Can't reproduce it anymore right now... the local build showed no issues right before the upload. It might be interesting to try the Jenkins build again a few times and see if it fails for the exact same job. -- To view, visit https://gerrit.osmocom.org/c/libosmo-netif/+/33198?usp=email To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings Gerrit-Project: libosmo-netif Gerrit-Branch: master Gerrit-Change-Id: I822abf52c6ae396c90b5c50228a0a39c848d3de6 Gerrit-Change-Number: 33198 Gerrit-PatchSet: 21 Gerrit-Owner: arehbein <arehbein(a)sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: laforge <laforge(a)osmocom.org> Gerrit-Reviewer: pespin <pespin(a)sysmocom.de> Gerrit-CC: daniel <dwillmann(a)sysmocom.de> Gerrit-CC: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-CC: osmith <osmith(a)sysmocom.de> Gerrit-Attention: osmith <osmith(a)sysmocom.de> Gerrit-Attention: pespin <pespin(a)sysmocom.de> Gerrit-Attention: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Attention: daniel <dwillmann(a)sysmocom.de> Gerrit-Comment-Date: Wed, 06 Sep 2023 17:37:43 +0000 Gerrit-HasComments: Yes Gerrit-Has-Labels: No Comment-In-Reply-To: osmith <osmith(a)sysmocom.de> Comment-In-Reply-To: arehbein <arehbein(a)sysmocom.de> Comment-In-Reply-To: daniel <dwillmann(a)sysmocom.de> Gerrit-MessageType: comment
1 0
0 0
[M] Change in osmo-bsc[master]: abis_nm: parse feature flags in NM_ATT_IPACC_SUPP_FEATURES
by fixeria 06 Sep '23

06 Sep '23
Attention is currently required from: laforge, pespin. fixeria has posted comments on this change. ( https://gerrit.osmocom.org/c/osmo-bsc/+/34323?usp=email ) Change subject: abis_nm: parse feature flags in NM_ATT_IPACC_SUPP_FEATURES ...................................................................... Patch Set 2: (2 comments) Patchset: PS1: > 20230906160221009 DNM abis_nm. […] Thanks for testing! I fixed wrong value-string for the `Ciphering algorithm` IE. File src/osmo-bsc/abis_nm.c: https://gerrit.osmocom.org/c/osmo-bsc/+/34323/comment/7d1c9c68_838ba75d PS1, Line 615: LOGPFOH(DNM, LOGL_ERROR, foh, "%s '%s' is supported\n", text, vs->str); > ERROR is too high here. INFO. Done -- To view, visit https://gerrit.osmocom.org/c/osmo-bsc/+/34323?usp=email To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings Gerrit-Project: osmo-bsc Gerrit-Branch: master Gerrit-Change-Id: I64cffa0bdead3617cc169c83b0f6ddf74f0866a7 Gerrit-Change-Number: 34323 Gerrit-PatchSet: 2 Gerrit-Owner: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: laforge <laforge(a)osmocom.org> Gerrit-CC: pespin <pespin(a)sysmocom.de> Gerrit-Attention: laforge <laforge(a)osmocom.org> Gerrit-Attention: pespin <pespin(a)sysmocom.de> Gerrit-Comment-Date: Wed, 06 Sep 2023 17:28:47 +0000 Gerrit-HasComments: Yes Gerrit-Has-Labels: No Comment-In-Reply-To: pespin <pespin(a)sysmocom.de> Comment-In-Reply-To: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-MessageType: comment
1 0
0 0
  • ← Newer
  • 1
  • ...
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • ...
  • 195
  • Older →

HyperKitty Powered by HyperKitty version 1.3.12.