pespin has submitted this change. ( https://gerrit.osmocom.org/c/libosmo-sigtran/+/40582?usp=email )
Change subject: sccp: Split sccp_instance code from sccp_user.c to its own file ......................................................................
sccp: Split sccp_instance code from sccp_user.c to its own file
Move code logic affecting management of struct sccp_instance to its own file. Previously, code was messily split between sccp_scoc.c and sccp_user.c.
Change-Id: I632f1825fe696bf73c0f9220dc8e8463337d8ba8 --- M src/Makefile.am M src/sccp_connection.c M src/sccp_helpers.c A src/sccp_instance.c A src/sccp_instance.h M src/sccp_internal.h M src/sccp_lbcs.c M src/sccp_sap.c M src/sccp_sclc.c M src/sccp_scmg.c M src/sccp_scoc.c M src/sccp_scoc_fsm.c M src/sccp_scrc.c M src/sccp_user.c M src/sccp_user.h M src/sccp_vty.c M src/ss7_vty.c 17 files changed, 1,023 insertions(+), 935 deletions(-)
Approvals: osmith: Looks good to me, but someone else must approve Jenkins Builder: Verified laforge: Looks good to me, but someone else must approve pespin: Looks good to me, approved
diff --git a/src/Makefile.am b/src/Makefile.am index 714bf02..0dc965e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -5,6 +5,7 @@ noinst_HEADERS = \ sccp_connection.h \ sccp_scoc_fsm.h \ + sccp_instance.h \ sccp_internal.h \ sccp_user.h \ ss7_as.h \ @@ -48,6 +49,7 @@ sccp2sua.c \ sccp_connection.c \ sccp_helpers.c \ + sccp_instance.c \ sccp_lbcs.c \ sccp_sap.c \ sccp_sclc.c \ diff --git a/src/sccp_connection.c b/src/sccp_connection.c index a8d2876..10c2760 100644 --- a/src/sccp_connection.c +++ b/src/sccp_connection.c @@ -40,6 +40,7 @@ #include <osmocom/sigtran/sccp_helpers.h> #include <osmocom/sccp/sccp_types.h>
+#include "sccp_instance.h" #include "sccp_internal.h" #include "sccp_connection.h" #include "sccp_scoc_fsm.h" diff --git a/src/sccp_helpers.c b/src/sccp_helpers.c index e86aa32..696f7a7 100644 --- a/src/sccp_helpers.c +++ b/src/sccp_helpers.c @@ -34,6 +34,7 @@ #include <osmocom/sigtran/sccp_helpers.h>
#include "ss7_internal.h" +#include "sccp_instance.h" #include "sccp_internal.h" #include "sccp_user.h"
diff --git a/src/sccp_instance.c b/src/sccp_instance.c new file mode 100644 index 0000000..10ef570 --- /dev/null +++ b/src/sccp_instance.c @@ -0,0 +1,934 @@ +/* SCCP Instance related routines */ + +/* (C) 2017 by Harald Welte laforge@gnumonks.org + * All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0+ + * + * based on my 2011 Erlang implementation osmo_ss7/src/sua_sccp_conv.erl + * + * References: ITU-T Q.713 and IETF RFC 3868 + * + * 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 <stdbool.h> +#include <string.h> +#include <limits.h> + +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/logging.h> + +#include <osmocom/sigtran/osmo_ss7.h> +#include <osmocom/sigtran/sccp_sap.h> +#include <osmocom/sigtran/mtp_sap.h> +#include <osmocom/sigtran/protocol/mtp.h> +#include <osmocom/sigtran/sccp_helpers.h> +#include <osmocom/sccp/sccp_types.h> + +#include "sccp_connection.h" +#include "sccp_internal.h" +#include "sccp_instance.h" +#include "sccp_user.h" +#include "xua_internal.h" +#include "ss7_as.h" +#include "ss7_asp.h" +#include "ss7_route.h" +#include "ss7_route_table.h" +#include "ss7_internal.h" +#include "ss7_xua_srv.h" + +/*********************************************************************** + * Timer Handling + ***********************************************************************/ + +/* Mostly pasted from Appendix C.4 of ITU-T Q.714 (05/2001) -- some of their descriptions are quite + * unintelligible out of context, for which we have our own description here. */ +const struct osmo_tdef osmo_sccp_timer_defaults[OSMO_SCCP_TIMERS_LEN] = { + { .T = OSMO_SCCP_TIMER_CONN_EST, .default_val = 1*60, .unit = OSMO_TDEF_S, + .desc = "Waiting for connection confirm message, 1 to 2 minutes" }, + { .T = OSMO_SCCP_TIMER_IAS, .default_val = 7*60, .unit = OSMO_TDEF_S, + .desc = "Send keep-alive: on an idle connection, delay before sending an Idle Timer message, 5 to 10 minutes" }, /* RFC 3868 Ch. 8. */ + { .T = OSMO_SCCP_TIMER_IAR, .default_val = 15*60, .unit = OSMO_TDEF_S, + .desc = "Receive keep-alive: on an idle connection, delay until considering a connection as stale, 11 to 21 minutes" }, /* RFC 3868 Ch. 8. */ + { .T = OSMO_SCCP_TIMER_REL, .default_val = 10, .unit = OSMO_TDEF_S, + .desc = "Waiting for release complete message, 10 to 20 seconds" }, + { .T = OSMO_SCCP_TIMER_REPEAT_REL, .default_val = 10, .unit = OSMO_TDEF_S, + .desc = "Waiting for release complete message; or to repeat sending released message after the initial expiry, 10 to 20 seconds" }, + { .T = OSMO_SCCP_TIMER_INT, .default_val = 1*60, .unit = OSMO_TDEF_S, + .desc = "Waiting for release complete message; or to release connection resources, freeze the LRN and " + "alert a maintenance function after the initial expiry, extending to 1 minute" }, + { .T = OSMO_SCCP_TIMER_GUARD, .default_val = 23*60, .unit = OSMO_TDEF_S, + .desc = "Waiting to resume normal procedure for temporary connection sections during the restart procedure, 23 to 25 minutes" }, + { .T = OSMO_SCCP_TIMER_RESET, .default_val = 10, .unit = OSMO_TDEF_S, + .desc = "Waiting to release temporary connection section or alert maintenance function after reset request message is sent, 10 to 20 seconds" }, + { .T = OSMO_SCCP_TIMER_REASSEMBLY, .default_val = 10, .unit = OSMO_TDEF_S, + .desc = "Waiting to receive all the segments of the remaining segments, single segmented message after receiving the first segment, 10 to 20 seconds" }, + {} +}; + +/* Appendix C.4 of ITU-T Q.714 */ +const struct value_string osmo_sccp_timer_names[] = { + { OSMO_SCCP_TIMER_CONN_EST, "conn_est" }, + { OSMO_SCCP_TIMER_IAS, "ias" }, + { OSMO_SCCP_TIMER_IAR, "iar" }, + { OSMO_SCCP_TIMER_REL, "rel" }, + { OSMO_SCCP_TIMER_REPEAT_REL, "repeat_rel" }, + { OSMO_SCCP_TIMER_INT, "int" }, + { OSMO_SCCP_TIMER_GUARD, "guard" }, + { OSMO_SCCP_TIMER_RESET, "reset" }, + { OSMO_SCCP_TIMER_REASSEMBLY, "reassembly" }, + {} +}; + +osmo_static_assert(ARRAY_SIZE(osmo_sccp_timer_defaults) == (OSMO_SCCP_TIMERS_LEN) && + ARRAY_SIZE(osmo_sccp_timer_names) == (OSMO_SCCP_TIMERS_LEN), + assert_osmo_sccp_timers_count); + +/*! \brief Find a SCCP User registered for given PC+SSN or SSN only + * First search all users with a valid PC for a full PC+SSN match. + * If no such match was found, search all users with an invalid PC for an SSN-only match. + * \param[in] inst SCCP Instance in which to search + * \param[in] ssn Sub-System Number to search for + * \param[in] pc Point Code to search for + * \returns Matching SCCP User; NULL if none found */ +struct osmo_sccp_user * +sccp_user_find(struct osmo_sccp_instance *inst, uint16_t ssn, uint32_t pc) +{ + struct osmo_sccp_user *scu; + + if (osmo_ss7_pc_is_valid(pc)) { + /* First try to find match for PC + SSN */ + llist_for_each_entry(scu, &inst->users, list) { + if (osmo_ss7_pc_is_valid(scu->pc) && scu->pc == pc && scu->ssn == ssn) + return scu; + } + } + + /* Then try to match on SSN only */ + llist_for_each_entry(scu, &inst->users, list) { + if (!osmo_ss7_pc_is_valid(scu->pc) && scu->ssn == ssn) + return scu; + } + + return NULL; +} + +/*! Find a SCCP User registered for given PC+SSN or SSN only. + * First search all users with a valid PC for a full PC+SSN match. + * If no match was found, search all users with an invalid PC for an SSN-only match. + * \param[in] inst SCCP Instance in which to search. + * \param[in] ssn Sub-System Number to search for. + * \param[in] pc Point Code to search for. + * \returns Matching SCCP User; NULL if none found. + */ +struct osmo_sccp_user * +osmo_sccp_user_find(struct osmo_sccp_instance *inst, uint16_t ssn, uint32_t pc) +{ + return sccp_user_find(inst, ssn, pc); +} + +/*! \brief Bind a SCCP User to a given Point Code + * \param[in] inst SCCP Instance + * \param[in] name human-readable name + * \param[in] prim_cb User provided callback to pass a primitive/msg up the stack + * \param[in] ssn Sub-System Number to bind to + * \param[in] pc Point Code to bind to, or OSMO_SS7_PC_INVALID if none. + * \returns Callee-allocated SCCP User on success; negative otherwise + * + * Ownership of oph->msg in prim_cb is transferred to the user of the + * registered callback when called. + */ +static struct osmo_sccp_user * +sccp_user_bind_pc(struct osmo_sccp_instance *inst, const char *name, + osmo_prim_cb prim_cb, uint16_t ssn, uint32_t pc) +{ + struct osmo_sccp_user *scu; + + scu = sccp_user_find(inst, ssn, pc); + if (scu) { + LOGPSCI(inst, LOGL_ERROR, + "Cannot bind user '%s' to SSN=%u PC=%s, this SSN and PC" + " is already bound by '%s'\n", + name, ssn, osmo_ss7_pointcode_print(inst->ss7, pc), scu->name); + return NULL; + } + + LOGPSCI(inst, LOGL_INFO, "Binding user '%s' to SSN=%u PC=%s\n", + name, ssn, osmo_ss7_pointcode_print(inst->ss7, pc)); + + scu = sccp_user_alloc(inst, name, prim_cb, ssn, pc); + return scu; +} + +/*! \brief Bind a given SCCP User to a given SSN+PC + * \param[in] inst SCCP Instance + * \param[in] name human-readable name + * \param[in] prim_cb User provided callback to pass a primitive/msg up the stack + * \param[in] ssn Sub-System Number to bind to + * \param[in] pc Point Code to bind to + * \returns Callee-allocated SCCP User on success; negative otherwise + * + * Ownership of oph->msg in prim_cb is transferred to the user of the + * registered callback when called. + */ +struct osmo_sccp_user * +osmo_sccp_user_bind_pc(struct osmo_sccp_instance *inst, const char *name, + osmo_prim_cb prim_cb, uint16_t ssn, uint32_t pc) +{ + return sccp_user_bind_pc(inst, name, prim_cb, ssn, pc); +} + +/*! \brief Bind a given SCCP User to a given SSN (at any PC) + * \param[in] inst SCCP Instance + * \param[in] name human-readable name + * \param[in] prim_cb User provided callback to pass a primitive/msg up the stack + * \param[in] ssn Sub-System Number to bind to + * \returns Callee-allocated SCCP User on success; negative otherwise + * + * Ownership of oph->msg in prim_cb is transferred to the user of the + * registered callback when called. + */ +struct osmo_sccp_user * +osmo_sccp_user_bind(struct osmo_sccp_instance *inst, const char *name, + osmo_prim_cb prim_cb, uint16_t ssn) +{ + return sccp_user_bind_pc(inst, name, prim_cb, ssn, OSMO_SS7_PC_INVALID); +} + + +/* prim_cb handed to MTP code for incoming MTP-TRANSFER.ind */ +static int mtp_user_prim_cb(struct osmo_prim_hdr *oph, void *ctx) +{ + struct osmo_sccp_instance *inst = ctx; + struct osmo_mtp_prim *omp = (struct osmo_mtp_prim *)oph; + struct xua_msg *xua; + int rc; + + OSMO_ASSERT(oph->sap == MTP_SAP_USER); + + switch (OSMO_PRIM(oph->primitive, oph->operation)) { + case OSMO_PRIM(OSMO_MTP_PRIM_TRANSFER, PRIM_OP_INDICATION): + /* Convert from SCCP to SUA in xua_msg format */ + xua = osmo_sccp_to_xua(oph->msg); + if (!xua) { + LOGPSCI(inst, LOGL_ERROR, "Couldn't convert SCCP to SUA: %s\n", + msgb_hexdump(oph->msg)); + rc = -1; + break; + } + xua->mtp = omp->u.transfer; + /* hand this primitive into SCCP via the SCRC code */ + rc = scrc_rx_mtp_xfer_ind_xua(inst, xua); + xua_msg_free(xua); + break; + default: + LOGPSCI(inst, LOGL_ERROR, "Unknown primitive %u:%u receivd\n", + oph->primitive, oph->operation); + rc = -1; + } + msgb_free(oph->msg); + return rc; +} + +static LLIST_HEAD(sccp_instances); + +/*! \brief create a SCCP Instance and register it as user with SS7 inst + * \param[in] ss7 SS7 instance to which this SCCP instance belongs + * \param[in] priv private data to be stored within SCCP instance + * \returns callee-allocated SCCP instance on success; NULL on error */ +struct osmo_sccp_instance * +osmo_sccp_instance_create(struct osmo_ss7_instance *ss7, void *priv) +{ + struct osmo_sccp_instance *inst; + int rc; + + inst = talloc_zero(ss7, struct osmo_sccp_instance); + if (!inst) + return NULL; + + inst->ss7 = ss7; + inst->priv = priv; + INIT_LLIST_HEAD(&inst->users); + + inst->max_optional_data = SCCP_MAX_OPTIONAL_DATA; + + inst->tdefs = talloc_memdup(inst, osmo_sccp_timer_defaults, + sizeof(osmo_sccp_timer_defaults)); + osmo_tdefs_reset(inst->tdefs); + + rc = sccp_scmg_init(inst); + if (rc < 0) { + talloc_free(inst); + return NULL; + } + + inst->ss7_user = osmo_ss7_user_create(ss7, "SCCP"); + osmo_ss7_user_set_prim_cb(inst->ss7_user, mtp_user_prim_cb); + osmo_ss7_user_set_priv(inst->ss7_user, inst); + osmo_ss7_user_register(ss7, MTP_SI_SCCP, inst->ss7_user); + + llist_add_tail(&inst->list, &sccp_instances); + + return inst; +} + +static void sccp_flush_connections(struct osmo_sccp_instance *inst) +{ + struct rb_node *node; + while ((node = rb_first(&inst->connections))) { + struct sccp_connection *conn = container_of(node, struct sccp_connection, node); + sccp_conn_free(conn); + } + +} + +void osmo_sccp_instance_destroy(struct osmo_sccp_instance *inst) +{ + struct osmo_sccp_user *scu, *scu2; + + inst->ss7->sccp = NULL; + osmo_ss7_user_unregister(inst->ss7, MTP_SI_SCCP, inst->ss7_user); + osmo_ss7_user_destroy(inst->ss7_user); + inst->ss7_user = NULL; + + llist_for_each_entry_safe(scu, scu2, &inst->users, list) { + osmo_sccp_user_unbind(scu); + } + sccp_flush_connections(inst); + llist_del(&inst->list); + talloc_free(inst); +} + +void osmo_sccp_set_priv(struct osmo_sccp_instance *sccp, void *priv) +{ + sccp->priv = priv; +} + +void *osmo_sccp_get_priv(struct osmo_sccp_instance *sccp) +{ + return sccp->priv; +} + +/*! \brief derive a basic local SCCP-Address from a given SCCP instance. + * \param[out] dest_addr pointer to output address memory + * \param[in] inst SCCP instance + * \param[in] ssn Subsystem Number */ +void osmo_sccp_local_addr_by_instance(struct osmo_sccp_addr *dest_addr, + const struct osmo_sccp_instance *inst, + uint32_t ssn) +{ + struct osmo_ss7_instance *ss7; + + OSMO_ASSERT(dest_addr); + OSMO_ASSERT(inst); + ss7 = inst->ss7; + OSMO_ASSERT(ss7); + + *dest_addr = (struct osmo_sccp_addr){}; + + osmo_sccp_make_addr_pc_ssn(dest_addr, ss7->cfg.primary_pc, ssn); +} + +/*! \brief check whether a given SCCP-Address is consistent. + * \param[in] addr SCCP address to check + * \param[in] presence mask with minimum required address components + * \returns true when address data seems plausible */ +bool osmo_sccp_check_addr(struct osmo_sccp_addr *addr, uint32_t presence) +{ + /* Minimum requirements do not match */ + if ((addr->presence & presence) != presence) + return false; + + /* GT ranges */ + if (addr->presence & OSMO_SCCP_ADDR_T_GT) { + if (addr->gt.gti > 15) + return false; + if (addr->gt.npi > 15) + return false; + if (addr->gt.nai > 127) + return false; + } + + /* Routing by GT, but no GT present */ + if (addr->ri == OSMO_SCCP_RI_GT + && !(addr->presence & OSMO_SCCP_ADDR_T_GT)) + return false; + + /* Routing by PC/SSN, but no PC/SSN present */ + if (addr->ri == OSMO_SCCP_RI_SSN_PC) { + if ((addr->presence & OSMO_SCCP_ADDR_T_PC) == 0) + return false; + if ((addr->presence & OSMO_SCCP_ADDR_T_SSN) == 0) + return false; + } + + if (addr->ri == OSMO_SCCP_RI_SSN_IP) { + if ((addr->presence & OSMO_SCCP_ADDR_T_IPv4) == 0 && + (addr->presence & OSMO_SCCP_ADDR_T_IPv6) == 0) + return false; + } + + return true; +} + +/*! Compare two SCCP Global Titles. + * \param[in] a left side. + * \param[in] b right side. + * \return -1 if a < b, 1 if a > b, and 0 if a == b. + */ +int osmo_sccp_gt_cmp(const struct osmo_sccp_gt *a, const struct osmo_sccp_gt *b) +{ + if (a == b) + return 0; + if (!a) + return -1; + if (!b) + return 1; + return memcmp(a, b, sizeof(*a)); +} + +/*! Compare two SCCP addresses by given presence criteria. + * Any OSMO_SCCP_ADDR_T_* type not set in presence_criteria is ignored. + * In case all bits are set in presence_criteria, the comparison is in the order of: + * OSMO_SCCP_ADDR_T_GT, OSMO_SCCP_ADDR_T_PC, OSMO_SCCP_ADDR_T_IPv4, OSMO_SCCP_ADDR_T_IPv6, OSMO_SCCP_ADDR_T_SSN. + * The SCCP addresses' Routing Indicator is not compared, see osmo_sccp_addr_ri_cmp(). + * \param[in] a left side. + * \param[in] b right side. + * \param[in] presence_criteria A bitmask of OSMO_SCCP_ADDR_T_* values, or OSMO_SCCP_ADDR_T_MASK to compare all parts, + * except the routing indicator. + * \return -1 if a < b, 1 if a > b, and 0 if all checked values match. + */ +int osmo_sccp_addr_cmp(const struct osmo_sccp_addr *a, const struct osmo_sccp_addr *b, uint32_t presence_criteria) +{ + int rc; + if (a == b) + return 0; + if (!a) + return -1; + if (!b) + return 1; + + if (presence_criteria & OSMO_SCCP_ADDR_T_GT) { + if ((a->presence & OSMO_SCCP_ADDR_T_GT) != (b->presence & OSMO_SCCP_ADDR_T_GT)) + return (b->presence & OSMO_SCCP_ADDR_T_GT) ? -1 : 1; + rc = osmo_sccp_gt_cmp(&a->gt, &b->gt); + if (rc) + return rc; + } + + if (presence_criteria & OSMO_SCCP_ADDR_T_PC) { + if ((a->presence & OSMO_SCCP_ADDR_T_PC) != (b->presence & OSMO_SCCP_ADDR_T_PC)) + return (b->presence & OSMO_SCCP_ADDR_T_PC) ? -1 : 1; + + if ((a->presence & OSMO_SCCP_ADDR_T_PC) + && a->pc != b->pc) + return (a->pc < b->pc) ? -1 : 1; + } + + if (presence_criteria & OSMO_SCCP_ADDR_T_IPv4) { + if ((a->presence & OSMO_SCCP_ADDR_T_IPv4) != (b->presence & OSMO_SCCP_ADDR_T_IPv4)) + return (b->presence & OSMO_SCCP_ADDR_T_IPv4) ? -1 : 1; + rc = memcmp(&a->ip.v4, &b->ip.v4, sizeof(a->ip.v4)); + if (rc) + return rc; + } + + if (presence_criteria & OSMO_SCCP_ADDR_T_IPv6) { + if ((a->presence & OSMO_SCCP_ADDR_T_IPv6) != (b->presence & OSMO_SCCP_ADDR_T_IPv6)) + return (b->presence & OSMO_SCCP_ADDR_T_IPv6) ? -1 : 1; + rc = memcmp(&a->ip.v6, &b->ip.v6, sizeof(a->ip.v6)); + if (rc) + return rc; + } + + if (presence_criteria & OSMO_SCCP_ADDR_T_SSN) { + if ((a->presence & OSMO_SCCP_ADDR_T_SSN) != (b->presence & OSMO_SCCP_ADDR_T_SSN)) + return (b->presence & OSMO_SCCP_ADDR_T_SSN) ? -1 : 1; + if (a->ssn != b->ssn) + return (a->ssn < b->ssn) ? -1 : 1; + } + + return 0; +} + +/*! Compare the routing information of two SCCP addresses. + * Compare the ri of a and b, and, if equal, return osmo_sccp_addr_cmp() with presence criteria selected according to + * ri. + * \param[in] a left side. + * \param[in] b right side. + * \return -1 if a < b, 1 if a > b, and 0 if a == b. + */ +int osmo_sccp_addr_ri_cmp(const struct osmo_sccp_addr *a, const struct osmo_sccp_addr *b) +{ + uint32_t presence_criteria; + if (a == b) + return 0; + if (!a) + return -1; + if (!b) + return 1; + if (a->ri != b->ri) + return (a->ri < b->ri) ? -1 : 1; + switch (a->ri) { + case OSMO_SCCP_RI_NONE: + return 0; + case OSMO_SCCP_RI_GT: + presence_criteria = OSMO_SCCP_ADDR_T_GT; + break; + case OSMO_SCCP_RI_SSN_PC: + presence_criteria = OSMO_SCCP_ADDR_T_SSN | OSMO_SCCP_ADDR_T_PC; + break; + case OSMO_SCCP_RI_SSN_IP: + /* Pick IPv4 or v6 depending on what a->presence indicates. */ + presence_criteria = OSMO_SCCP_ADDR_T_SSN | (a->presence & (OSMO_SCCP_ADDR_T_IPv4 | OSMO_SCCP_ADDR_T_IPv6)); + break; + default: + return 0; + } + + return osmo_sccp_addr_cmp(a, b, presence_criteria); +} + +/*********************************************************************** + * Convenience function for CLIENT + ***********************************************************************/ + + /* Returns whether AS is already associated to any AS. + * Helper function for osmo_sccp_simple_client_on_ss7_id(). */ +static bool asp_serves_some_as(const struct osmo_ss7_asp *asp) +{ + struct osmo_ss7_as *as_i; + llist_for_each_entry(as_i, &asp->inst->as_list, list) { + if (osmo_ss7_as_has_asp(as_i, asp)) + return true; + } + return false; +} + +/*! \brief request an sccp client instance + * \param[in] ctx talloc context + * \param[in] ss7_id of the SS7/CS7 instance + * \param[in] name human readable name + * \param[in] default_pc pointcode to be used on missing VTY setting + * \param[in] prot protocol to be used (e.g OSMO_SS7_ASP_PROT_M3UA) + * \param[in] default_local_port local port to be used on missing VTY setting + * \param[in] default_local_ip local IP-address to be used on missing VTY setting (NULL: use library own defaults) + * \param[in] default_remote_port remote port to be used on missing VTY setting + * \param[in] default_remote_ip remote IP-address to be used on missing VTY setting (NULL: use library own defaults) + * \returns callee-allocated SCCP instance on success; NULL on error */ + +struct osmo_sccp_instance * +osmo_sccp_simple_client_on_ss7_id(void *ctx, uint32_t ss7_id, const char *name, + uint32_t default_pc, + enum osmo_ss7_asp_protocol prot, + int default_local_port, + const char *default_local_ip, + int default_remote_port, + const char *default_remote_ip) +{ + struct osmo_ss7_instance *ss7; + bool ss7_created = false; + struct osmo_ss7_as *as; + bool as_created = false; + struct osmo_ss7_route *rt; + bool rt_created = false; + struct osmo_ss7_asp *asp; + bool asp_created = false; + char *as_name, *asp_name = NULL; + int trans_proto; + + trans_proto = ss7_default_trans_proto_for_asp_proto(prot); + + /*! The function will examine the given CS7 instance and its sub + * components (as, asp, etc.). If necessary it will allocate + * the missing components. If no CS7 instance can be detected + * under the caller supplied ID, a new instance will be created + * beforehand. */ + + /* Choose default ports when the caller does not supply valid port + * numbers. */ + if (!default_remote_port || default_remote_port < 0) + default_remote_port = osmo_ss7_asp_protocol_port(prot); + if (default_local_port < 0) + default_local_port = osmo_ss7_asp_protocol_port(prot); + + /* Check if there is already an ss7 instance present under + * the given id. If not, we will create a new one. */ + ss7 = osmo_ss7_instance_find(ss7_id); + if (!ss7) { + LOGP(DLSCCP, LOGL_NOTICE, "%s: Creating SS7 instance\n", + name); + + /* Create a new ss7 instance */ + ss7 = osmo_ss7_instance_find_or_create(ctx, ss7_id); + if (!ss7) { + LOGP(DLSCCP, LOGL_ERROR, + "Failed to find or create SS7 instance\n"); + return NULL; + } + + /* Setup primary pointcode + * NOTE: This means that the user must set the pointcode to a + * proper value when a cs7 instance is defined via the VTY. */ + ss7->cfg.primary_pc = default_pc; + ss7_created = true; + } + + /* In case no valid point-code has been configured via the VTY, we + * will fall back to the default pointcode. */ + if (!osmo_ss7_pc_is_valid(ss7->cfg.primary_pc)) { + LOGP(DLSCCP, LOGL_ERROR, + "SS7 instance %u: no primary point-code set, using default point-code\n", + ss7->cfg.id); + ss7->cfg.primary_pc = default_pc; + } + + LOGP(DLSCCP, LOGL_NOTICE, "%s: Using SS7 instance %u, pc:%s\n", name, + ss7->cfg.id, osmo_ss7_pointcode_print(ss7, ss7->cfg.primary_pc)); + + /* Check if there is already an application server that matches + * the protocol we intend to use. If not, we will create one. */ + as = osmo_ss7_as_find_by_proto(ss7, prot); + if (!as) { + LOGP(DLSCCP, LOGL_NOTICE, "%s: Creating AS instance\n", + name); + as_name = talloc_asprintf(ctx, "as-clnt-%s", name); + as = osmo_ss7_as_find_or_create(ss7, as_name, prot); + talloc_free(as_name); + if (!as) + goto out_ss7; + as_created = true; + as->cfg.routing_key.pc = ss7->cfg.primary_pc; + as->simple_client_allocated = true; + } + LOGP(DLSCCP, LOGL_NOTICE, "%s: Using AS instance %s\n", name, + as->cfg.name); + + /* Create a default dynamic route if necessary */ + rt = ss7_route_table_find_route_by_dpc_mask(ss7->rtable_system, 0, 0, true); + if (!rt) { + LOGP(DLSCCP, LOGL_NOTICE, "%s: Creating default route\n", name); + rt = ss7_route_create(ss7->rtable_system, 0, 0, + true, as->cfg.name); + if (!rt) + goto out_as; + rt_created = true; + } + + /* Check if we do already have an application server process + * that is associated with the application server we have chosen + * the application server process must also match the protocol + * we intend to use. */ + asp = osmo_ss7_asp_find_by_proto(as, prot); + if (!asp) { + /* Check if the user has created an ASP for this proto that is not added on any AS yet. */ + struct osmo_ss7_asp *asp_i; + llist_for_each_entry(asp_i, &ss7->asp_list, list) { + if (asp_i->cfg.proto != prot) + continue; + if (asp_serves_some_as(asp_i)) { + /* This ASP is already on another AS. + * If it was on this AS, we'd have found it above. */ + continue; + } + /* This ASP matches the protocol and is not yet associated to any AS. Use it. */ + asp = asp_i; + LOGP(DLSCCP, LOGL_NOTICE, "%s: ASP %s for %s is not associated with any AS, using it\n", + name, asp->cfg.name, osmo_ss7_asp_protocol_name(prot)); + ss7_as_add_asp(as, asp); + /* ASP became associated to a new AS, hence it needs to be + * restarted to announce/register its Routing Context. + * Make sure proper defaults are applied if app didn't + * provide specific default values, then restart the ASP: */ + ss7_asp_restart_after_reconfigure(asp); + break; + } + if (!asp) { + asp_name = talloc_asprintf(ctx, "asp-clnt-%s", name); + LOGP(DLSCCP, LOGL_NOTICE, "%s: No unassociated ASP for %s, creating new ASP %s\n", + name, osmo_ss7_asp_protocol_name(prot), asp_name); + asp = osmo_ss7_asp_find_or_create2(ss7, asp_name, + default_remote_port, + default_local_port, + trans_proto, prot); + talloc_free(asp_name); + if (!asp) + goto out_rt; + asp_created = true; + asp->simple_client_allocated = true; + /* Ensure that the ASP we use is set to operate as a client. */ + asp->cfg.is_server = false; + /* Ensure that the ASP we use is set to role ASP. */ + asp->cfg.role = OSMO_SS7_ASP_ROLE_ASP; + if (default_local_ip) + ss7_asp_peer_set_hosts(&asp->cfg.local, asp, &default_local_ip, 1); + if (default_remote_ip) + ss7_asp_peer_set_hosts(&asp->cfg.remote, asp, &default_remote_ip, 1); + ss7_as_add_asp(as, asp); + /* Make sure proper defaults are applied if app didn't + provide specific default values, then restart the ASP: */ + ss7_asp_restart_after_reconfigure(asp); + } + } + + /* Extra sanity checks if the ASP asp-clnt-* was pre-configured over VTY: */ + if (!asp->simple_client_allocated) { + /* Forbid ASPs defined through VTY that are not entirely + * configured. "role" and "transport-role" must be explicitly provided: + */ + if (!asp->cfg.role_set_by_vty) { + LOGP(DLSCCP, LOGL_ERROR, + "%s: ASP %s defined in VTY but 'role' was not set there, please set it.\n", + name, asp->cfg.name); + goto out_asp; + } + if (!asp->cfg.trans_role_set_by_vty) { + LOGP(DLSCCP, LOGL_ERROR, + "%s: ASP %s defined in VTY but 'transport-role' was not set there, please set it.\n", + name, asp->cfg.name); + goto out_asp; + } + + /* If ASP was configured through VTY it may be explicitly configured as + * SCTP server. It may be a bit confusing since this function is to create + * a "SCCP simple client", but this allows users of this API such as + * osmo-hnbgw to support transport-role server if properly configured through VTY. + */ + if (asp->cfg.is_server) { + struct osmo_xua_server *xs; + LOGP(DLSCCP, LOGL_NOTICE, + "%s: Requesting an SCCP simple client on ASP %s configured with 'transport-role server'\n", + name, asp->cfg.name); + xs = ss7_xua_server_find2(ss7, + asp->cfg.trans_proto, prot, + asp->cfg.local.port); + if (!xs) { + LOGP(DLSCCP, LOGL_ERROR, "%s: Requesting an SCCP simple client on ASP %s configured " + "with 'transport-role server' but no matching xUA server was configured!\n", + name, asp->cfg.name); + goto out_asp; + } + } + /* ASP was already started here previously by VTY go_parent. */ + } + + LOGP(DLSCCP, LOGL_NOTICE, "%s: Using ASP instance %s\n", name, + asp->cfg.name); + + osmo_ss7_ensure_sccp(ss7); + if (!ss7->sccp) + goto out_asp; + + return ss7->sccp; + +out_asp: + if (asp_created) + osmo_ss7_asp_destroy(asp); +out_rt: + if (rt_created) + ss7_route_destroy(rt); +out_as: + if (as_created) + osmo_ss7_as_destroy(as); +out_ss7: + if (ss7_created) + osmo_ss7_instance_destroy(ss7); + + return NULL; +} + +/*! \brief request an sccp client instance + * \param[in] ctx talloc context + * \param[in] name human readable name + * \param[in] default_pc pointcode to be used on missing VTY setting + * \param[in] prot protocol to be used (e.g OSMO_SS7_ASP_PROT_M3UA) + * \param[in] default_local_port local port to be used on missing VTY setting + * \param[in] default_local_ip local IP-address to be used on missing VTY setting + * \param[in] default_remote_port remote port to be used on missing VTY setting + * \param[in] default_remote_ip remote IP-address to be used on missing VTY setting + * \returns callee-allocated SCCP instance on success; NULL on error */ +struct osmo_sccp_instance * +osmo_sccp_simple_client(void *ctx, const char *name, uint32_t default_pc, + enum osmo_ss7_asp_protocol prot, int default_local_port, + const char *default_local_ip, int default_remote_port, + const char *default_remote_ip) +{ + /*! This is simplified version of osmo_sccp_simple_client_on_ss7_id(). + * the only difference is that the ID of the CS7 instance will be + * set to 0 statically */ + + return osmo_sccp_simple_client_on_ss7_id(ctx, 0, name, default_pc, prot, + default_local_port, + default_local_ip, + default_remote_port, + default_remote_ip); +} + +/*********************************************************************** + * Convenience function for SERVER + ***********************************************************************/ + +struct osmo_sccp_instance * +osmo_sccp_simple_server_on_ss7_id(void *ctx, uint32_t ss7_id, uint32_t pc, + enum osmo_ss7_asp_protocol prot, + int local_port, const char *local_ip) +{ + struct osmo_ss7_instance *ss7; + struct osmo_xua_server *xs; + int trans_proto; + int rc; + + trans_proto = ss7_default_trans_proto_for_asp_proto(prot); + + if (local_port < 0) + local_port = osmo_ss7_asp_protocol_port(prot); + + /* allocate + initialize SS7 instance */ + ss7 = osmo_ss7_instance_find_or_create(ctx, ss7_id); + if (!ss7) + return NULL; + ss7->cfg.primary_pc = pc; + + xs = ss7_xua_server_create2(ss7, trans_proto, prot, local_port, local_ip); + if (!xs) + goto out_ss7; + + rc = ss7_xua_server_bind(xs); + if (rc < 0) + goto out_xs; + + /* Allocate SCCP stack */ + osmo_ss7_ensure_sccp(ss7); + if (!ss7->sccp) + goto out_xs; + + return ss7->sccp; + +out_xs: + ss7_xua_server_destroy(xs); +out_ss7: + osmo_ss7_instance_destroy(ss7); + + return NULL; +} + +struct osmo_sccp_instance * +osmo_sccp_simple_server(void *ctx, uint32_t pc, + enum osmo_ss7_asp_protocol prot, int local_port, + const char *local_ip) +{ + return osmo_sccp_simple_server_on_ss7_id(ctx, 0, pc, prot, + local_port, local_ip); +} + +struct osmo_sccp_instance * +osmo_sccp_simple_server_add_clnt(struct osmo_sccp_instance *inst, + enum osmo_ss7_asp_protocol prot, + const char *name, uint32_t pc, + int local_port, int remote_port, + const char *remote_ip) +{ + struct osmo_ss7_instance *ss7 = inst->ss7; + struct osmo_ss7_as *as; + struct osmo_ss7_route *rt; + struct osmo_ss7_asp *asp; + struct osmo_xua_server *oxs; + char *as_name, *asp_name; + int trans_proto; + + trans_proto = ss7_default_trans_proto_for_asp_proto(prot); + + if (local_port < 0) + local_port = osmo_ss7_asp_protocol_port(prot); + + if (remote_port < 0) + remote_port = osmo_ss7_asp_protocol_port(prot); + + as_name = talloc_asprintf(ss7, "as-srv-%s", name); + asp_name = talloc_asprintf(ss7, "asp-srv-%s", name); + + /* application server */ + as = osmo_ss7_as_find_or_create(ss7, as_name, prot); + if (!as) + goto out_strings; + + /* route only selected PC to the client */ + rt = ss7_route_create(ss7->rtable_system, pc, 0xffff, true, as_name); + if (!rt) + goto out_as; + + asp = osmo_ss7_asp_find_or_create2(ss7, asp_name, + remote_port, local_port, + trans_proto, prot); + if (!asp) + goto out_rt; + oxs = ss7_xua_server_find2(ss7, asp->cfg.trans_proto, prot, local_port); + if (!oxs) + goto out_asp; + if (ss7_asp_peer_set_hosts(&asp->cfg.local, asp, + (const char * const*)oxs->cfg.local.host, + oxs->cfg.local.host_cnt) < 0) + goto out_asp; + if (ss7_asp_peer_add_host(&asp->cfg.remote, asp, remote_ip) < 0) + goto out_asp; + asp->cfg.is_server = true; + asp->cfg.role = OSMO_SS7_ASP_ROLE_SG; + ss7_as_add_asp(as, asp); + talloc_free(asp_name); + talloc_free(as_name); + osmo_ss7_asp_restart(asp); + + return ss7->sccp; + +out_asp: + osmo_ss7_asp_destroy(asp); +out_rt: + ss7_route_destroy(rt); +out_as: + osmo_ss7_as_destroy(as); +out_strings: + talloc_free(as_name); + talloc_free(asp_name); + + return NULL; +} + +/*! Adjust the upper bound for the optional data length (the payload) for CR, CC, CREF and RLSD messages. + * For any Optional Data part larger than this value in octets, send CR, CC, CREF and RLSD messages without any payload, + * and send the data payload in a separate Data Form 1 message. ITU-T Q.713 sections 4.2 thru 4.5 define a limit of 130 + * bytes for the 'Data' parameter. This limit can be adjusted here. May be useful for interop with nonstandard SCCP + * peers. + * \param[in] sccp SCCP instance to reconfigure. + * \param[in] val Number of bytes to set as upper bound for the optional data length, or pass a negative value to set + * the standard value of SCCP_MAX_OPTIONAL_DATA == 130, which conforms to ITU-T Q.713. + */ +void osmo_sccp_set_max_optional_data(struct osmo_sccp_instance *inst, int val) +{ + if (!inst) + return; + if (val < 0) + val = SCCP_MAX_OPTIONAL_DATA; + inst->max_optional_data = val; +} + +/*! \brief get the SS7 instance that is related to the given SCCP instance + * \param[in] sccp SCCP instance + * \returns SS7 instance; NULL if sccp was NULL */ +struct osmo_ss7_instance *osmo_sccp_get_ss7(const struct osmo_sccp_instance *sccp) +{ + if (!sccp) + return NULL; + return sccp->ss7; +} diff --git a/src/sccp_instance.h b/src/sccp_instance.h new file mode 100644 index 0000000..a3f6750 --- /dev/null +++ b/src/sccp_instance.h @@ -0,0 +1,67 @@ +#pragma once + +#include <inttypes.h> + +#include <osmocom/core/fsm.h> +#include <osmocom/core/prim.h> +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/linuxrbtree.h> +#include <osmocom/core/tdef.h> +#include <osmocom/sigtran/sccp_sap.h> +#include <osmocom/sigtran/osmo_ss7.h> +#include <osmocom/sigtran/protocol/mtp.h> + +struct osmo_ss7_user; + +/* Appendix C.4 of Q.714 */ +enum osmo_sccp_timer { + /* 0 kept unused on purpose since it's handled specially by osmo_fsm */ + OSMO_SCCP_TIMER_CONN_EST = 1, + OSMO_SCCP_TIMER_IAS, + OSMO_SCCP_TIMER_IAR, + OSMO_SCCP_TIMER_REL, + OSMO_SCCP_TIMER_REPEAT_REL, + OSMO_SCCP_TIMER_INT, + OSMO_SCCP_TIMER_GUARD, + OSMO_SCCP_TIMER_RESET, + OSMO_SCCP_TIMER_REASSEMBLY, + /* This must remain the last item: */ + OSMO_SCCP_TIMERS_LEN +}; + +extern const struct osmo_tdef osmo_sccp_timer_defaults[OSMO_SCCP_TIMERS_LEN]; + +extern const struct value_string osmo_sccp_timer_names[]; +static inline const char *osmo_sccp_timer_name(enum osmo_sccp_timer val) +{ return get_value_string(osmo_sccp_timer_names, val); } + +/* an instance of the SCCP stack */ +struct osmo_sccp_instance { + /* entry in global list of ss7 instances */ + struct llist_head list; + /* rbtree root of 'struct sccp_connection' in this instance */ + struct rb_root connections; + /* list of SCCP users in this instance */ + struct llist_head users; + /* routing context to be used in all outbound messages */ + uint32_t route_ctx; + /* next connection ID to allocate */ + uint32_t next_id; + struct osmo_ss7_instance *ss7; + void *priv; + + struct osmo_ss7_user *ss7_user; + + struct osmo_tdef *tdefs; + + uint32_t max_optional_data; +}; + +struct osmo_sccp_user * +sccp_user_find(struct osmo_sccp_instance *inst, uint16_t ssn, uint32_t pc); + +#define _LOGPSCI(sci, subsys, level, fmt, args ...) \ + _LOGSS7((sci)->ss7, subsys, level, "SCCP(rctx=%" PRIu32 ") " fmt, (sci)->route_ctx, ## args) +#define LOGPSCI(sci, level, fmt, args ...) \ + _LOGPSCI(sci, DLSCCP, level, fmt, ## args) + diff --git a/src/sccp_internal.h b/src/sccp_internal.h index 5490fdb..8a69e19 100644 --- a/src/sccp_internal.h +++ b/src/sccp_internal.h @@ -11,11 +11,10 @@ #include <osmocom/sigtran/osmo_ss7.h> #include <osmocom/sigtran/protocol/mtp.h>
-#include "ss7_user.h" - #define SCCP_STR "Signalling Connection Control Part\n"
-struct xua_msg; +struct osmo_sccp_instance; +struct osmo_sccp_user;
/* SCCP addressbook */ extern struct llist_head sccp_address_book_global; @@ -30,62 +29,11 @@ const struct osmo_ss7_instance *inst); struct osmo_sccp_addr_entry *addr_entry_by_name_global(const char *name);
-/* Appendix C.4 of Q.714 */ -enum osmo_sccp_timer { - /* 0 kept unused on purpose since it's handled specially by osmo_fsm */ - OSMO_SCCP_TIMER_CONN_EST = 1, - OSMO_SCCP_TIMER_IAS, - OSMO_SCCP_TIMER_IAR, - OSMO_SCCP_TIMER_REL, - OSMO_SCCP_TIMER_REPEAT_REL, - OSMO_SCCP_TIMER_INT, - OSMO_SCCP_TIMER_GUARD, - OSMO_SCCP_TIMER_RESET, - OSMO_SCCP_TIMER_REASSEMBLY, - /* This must remain the last item: */ - OSMO_SCCP_TIMERS_LEN -}; - -extern const struct osmo_tdef osmo_sccp_timer_defaults[OSMO_SCCP_TIMERS_LEN]; - -extern const struct value_string osmo_sccp_timer_names[]; -static inline const char *osmo_sccp_timer_name(enum osmo_sccp_timer val) -{ return get_value_string(osmo_sccp_timer_names, val); } - -/* an instance of the SCCP stack */ -struct osmo_sccp_instance { - /* entry in global list of ss7 instances */ - struct llist_head list; - /* rbtree root of 'struct sccp_connection' in this instance */ - struct rb_root connections; - /* list of SCCP users in this instance */ - struct llist_head users; - /* routing context to be used in all outbound messages */ - uint32_t route_ctx; - /* next connection ID to allocate */ - uint32_t next_id; - struct osmo_ss7_instance *ss7; - void *priv; - - struct osmo_ss7_user *ss7_user; - - struct osmo_tdef *tdefs; - - uint32_t max_optional_data; -}; -struct sccp_connection *sccp_find_conn_by_id(const struct osmo_sccp_instance *inst, uint32_t id); -#define _LOGPSCI(sci, subsys, level, fmt, args ...) \ - _LOGSS7((sci)->ss7, subsys, level, "SCCP(rctx=%" PRIu32 ") " fmt, (sci)->route_ctx, ## args) -#define LOGPSCI(sci, level, fmt, args ...) \ - _LOGPSCI(sci, DLSCCP, level, fmt, ## args) - - extern int DSCCP;
struct xua_msg;
-struct osmo_sccp_user * -sccp_user_find(struct osmo_sccp_instance *inst, uint16_t ssn, uint32_t pc); +struct sccp_connection *sccp_find_conn_by_id(const struct osmo_sccp_instance *inst, uint32_t id);
/* Message from SCOC -> SCRC */ int sccp_scrc_rx_scoc_conn_msg(struct osmo_sccp_instance *inst, @@ -104,8 +52,6 @@ void sccp_scoc_rx_scrc_rout_fail(struct osmo_sccp_instance *inst, struct xua_msg *xua, uint32_t cause);
-void sccp_scoc_flush_connections(struct osmo_sccp_instance *inst); - /* Message from SCRC -> SCLC */ int sccp_sclc_rx_from_scrc(struct osmo_sccp_instance *inst, struct xua_msg *xua); diff --git a/src/sccp_lbcs.c b/src/sccp_lbcs.c index 34b6181..8dd636f 100644 --- a/src/sccp_lbcs.c +++ b/src/sccp_lbcs.c @@ -33,6 +33,7 @@ #include <osmocom/sccp/sccp_types.h>
#include "xua_internal.h" +#include "sccp_instance.h" #include "sccp_internal.h" #include "sccp_user.h"
diff --git a/src/sccp_sap.c b/src/sccp_sap.c index 45e4dfc..00b2563 100644 --- a/src/sccp_sap.c +++ b/src/sccp_sap.c @@ -29,6 +29,7 @@ #include "sccp_connection.h" #include "sccp_scoc_fsm.h" #include "sccp_internal.h" +#include "sccp_instance.h" #include "sccp_user.h"
const struct value_string osmo_scu_prim_type_names[] = { diff --git a/src/sccp_sclc.c b/src/sccp_sclc.c index b8df996..88574c0 100644 --- a/src/sccp_sclc.c +++ b/src/sccp_sclc.c @@ -58,6 +58,7 @@
#include "xua_internal.h" #include "ss7_internal.h" +#include "sccp_instance.h" #include "sccp_internal.h" #include "sccp_user.h"
diff --git a/src/sccp_scmg.c b/src/sccp_scmg.c index ee86954..2ffedce 100644 --- a/src/sccp_scmg.c +++ b/src/sccp_scmg.c @@ -34,6 +34,7 @@ #include <osmocom/sccp/sccp_types.h>
#include "xua_internal.h" +#include "sccp_instance.h" #include "ss7_internal.h" #include "sccp_internal.h" #include "sccp_user.h" diff --git a/src/sccp_scoc.c b/src/sccp_scoc.c index b460d7d..fa96803 100644 --- a/src/sccp_scoc.c +++ b/src/sccp_scoc.c @@ -64,6 +64,7 @@ #include "xua_internal.h" #include "sccp_connection.h" #include "sccp_scoc_fsm.h" +#include "sccp_instance.h" #include "sccp_internal.h" #include "sccp_user.h" #include "ss7_internal.h" @@ -86,53 +87,6 @@ };
/*********************************************************************** - * Timer Handling - ***********************************************************************/ - -/* Mostly pasted from Appendix C.4 of ITU-T Q.714 (05/2001) -- some of their descriptions are quite - * unintelligible out of context, for which we have our own description here. */ -const struct osmo_tdef osmo_sccp_timer_defaults[OSMO_SCCP_TIMERS_LEN] = { - { .T = OSMO_SCCP_TIMER_CONN_EST, .default_val = 1*60, .unit = OSMO_TDEF_S, - .desc = "Waiting for connection confirm message, 1 to 2 minutes" }, - { .T = OSMO_SCCP_TIMER_IAS, .default_val = 7*60, .unit = OSMO_TDEF_S, - .desc = "Send keep-alive: on an idle connection, delay before sending an Idle Timer message, 5 to 10 minutes" }, /* RFC 3868 Ch. 8. */ - { .T = OSMO_SCCP_TIMER_IAR, .default_val = 15*60, .unit = OSMO_TDEF_S, - .desc = "Receive keep-alive: on an idle connection, delay until considering a connection as stale, 11 to 21 minutes" }, /* RFC 3868 Ch. 8. */ - { .T = OSMO_SCCP_TIMER_REL, .default_val = 10, .unit = OSMO_TDEF_S, - .desc = "Waiting for release complete message, 10 to 20 seconds" }, - { .T = OSMO_SCCP_TIMER_REPEAT_REL, .default_val = 10, .unit = OSMO_TDEF_S, - .desc = "Waiting for release complete message; or to repeat sending released message after the initial expiry, 10 to 20 seconds" }, - { .T = OSMO_SCCP_TIMER_INT, .default_val = 1*60, .unit = OSMO_TDEF_S, - .desc = "Waiting for release complete message; or to release connection resources, freeze the LRN and " - "alert a maintenance function after the initial expiry, extending to 1 minute" }, - { .T = OSMO_SCCP_TIMER_GUARD, .default_val = 23*60, .unit = OSMO_TDEF_S, - .desc = "Waiting to resume normal procedure for temporary connection sections during the restart procedure, 23 to 25 minutes" }, - { .T = OSMO_SCCP_TIMER_RESET, .default_val = 10, .unit = OSMO_TDEF_S, - .desc = "Waiting to release temporary connection section or alert maintenance function after reset request message is sent, 10 to 20 seconds" }, - { .T = OSMO_SCCP_TIMER_REASSEMBLY, .default_val = 10, .unit = OSMO_TDEF_S, - .desc = "Waiting to receive all the segments of the remaining segments, single segmented message after receiving the first segment, 10 to 20 seconds" }, - {} -}; - -/* Appendix C.4 of ITU-T Q.714 */ -const struct value_string osmo_sccp_timer_names[] = { - { OSMO_SCCP_TIMER_CONN_EST, "conn_est" }, - { OSMO_SCCP_TIMER_IAS, "ias" }, - { OSMO_SCCP_TIMER_IAR, "iar" }, - { OSMO_SCCP_TIMER_REL, "rel" }, - { OSMO_SCCP_TIMER_REPEAT_REL, "repeat_rel" }, - { OSMO_SCCP_TIMER_INT, "int" }, - { OSMO_SCCP_TIMER_GUARD, "guard" }, - { OSMO_SCCP_TIMER_RESET, "reset" }, - { OSMO_SCCP_TIMER_REASSEMBLY, "reassembly" }, - {} -}; - -osmo_static_assert(ARRAY_SIZE(osmo_sccp_timer_defaults) == (OSMO_SCCP_TIMERS_LEN) && - ARRAY_SIZE(osmo_sccp_timer_names) == (OSMO_SCCP_TIMERS_LEN), - assert_osmo_sccp_timers_count); - -/*********************************************************************** * SUA Instance and Connection handling ***********************************************************************/
@@ -865,14 +819,3 @@ /* Dispatch event to existing connection */ osmo_fsm_inst_dispatch(conn->fi, event, xua); } - -void sccp_scoc_flush_connections(struct osmo_sccp_instance *inst) -{ - struct rb_node *node; - while ((node = rb_first(&inst->connections))) { - struct sccp_connection *conn = container_of(node, struct sccp_connection, node); - sccp_conn_free(conn); - } - -} - diff --git a/src/sccp_scoc_fsm.c b/src/sccp_scoc_fsm.c index dbee91e..ce186a8 100644 --- a/src/sccp_scoc_fsm.c +++ b/src/sccp_scoc_fsm.c @@ -45,8 +45,9 @@
#include "xua_internal.h" #include "sccp_connection.h" -#include "sccp_scoc_fsm.h" +#include "sccp_instance.h" #include "sccp_internal.h" +#include "sccp_scoc_fsm.h" #include "sccp_user.h" #include "ss7_internal.h" #include "ss7_instance.h" diff --git a/src/sccp_scrc.c b/src/sccp_scrc.c index d7a6428..fdbfd3c 100644 --- a/src/sccp_scrc.c +++ b/src/sccp_scrc.c @@ -33,6 +33,7 @@ #include <osmocom/sigtran/protocol/sua.h> #include <osmocom/sigtran/protocol/mtp.h>
+#include "sccp_instance.h" #include "sccp_internal.h" #include "ss7_as.h" #include "ss7_instance.h" diff --git a/src/sccp_user.c b/src/sccp_user.c index 8219829..2ec2f28 100644 --- a/src/sccp_user.c +++ b/src/sccp_user.c @@ -37,6 +37,7 @@ #include <osmocom/sigtran/sccp_helpers.h> #include <osmocom/sccp/sccp_types.h>
+#include "sccp_instance.h" #include "sccp_internal.h" #include "sccp_user.h" #include "xua_internal.h" @@ -47,78 +48,11 @@ #include "ss7_internal.h" #include "ss7_xua_srv.h"
-/*! \brief Find a SCCP User registered for given PC+SSN or SSN only - * First search all users with a valid PC for a full PC+SSN match. - * If no such match was found, search all users with an invalid PC for an SSN-only match. - * \param[in] inst SCCP Instance in which to search - * \param[in] ssn Sub-System Number to search for - * \param[in] pc Point Code to search for - * \returns Matching SCCP User; NULL if none found */ -struct osmo_sccp_user * -sccp_user_find(struct osmo_sccp_instance *inst, uint16_t ssn, uint32_t pc) +struct osmo_sccp_user *sccp_user_alloc(struct osmo_sccp_instance *inst, const char *name, + osmo_prim_cb prim_cb, uint16_t ssn, uint32_t pc) { struct osmo_sccp_user *scu;
- if (osmo_ss7_pc_is_valid(pc)) { - /* First try to find match for PC + SSN */ - llist_for_each_entry(scu, &inst->users, list) { - if (osmo_ss7_pc_is_valid(scu->pc) && scu->pc == pc && scu->ssn == ssn) - return scu; - } - } - - /* Then try to match on SSN only */ - llist_for_each_entry(scu, &inst->users, list) { - if (!osmo_ss7_pc_is_valid(scu->pc) && scu->ssn == ssn) - return scu; - } - - return NULL; -} - -/*! Find a SCCP User registered for given PC+SSN or SSN only. - * First search all users with a valid PC for a full PC+SSN match. - * If no match was found, search all users with an invalid PC for an SSN-only match. - * \param[in] inst SCCP Instance in which to search. - * \param[in] ssn Sub-System Number to search for. - * \param[in] pc Point Code to search for. - * \returns Matching SCCP User; NULL if none found. - */ -struct osmo_sccp_user * -osmo_sccp_user_find(struct osmo_sccp_instance *inst, uint16_t ssn, uint32_t pc) -{ - return sccp_user_find(inst, ssn, pc); -} - -/*! \brief Bind a SCCP User to a given Point Code - * \param[in] inst SCCP Instance - * \param[in] name human-readable name - * \param[in] prim_cb User provided callback to pass a primitive/msg up the stack - * \param[in] ssn Sub-System Number to bind to - * \param[in] pc Point Code to bind to, or OSMO_SS7_PC_INVALID if none. - * \returns Callee-allocated SCCP User on success; negative otherwise - * - * Ownership of oph->msg in prim_cb is transferred to the user of the - * registered callback when called. - */ -static struct osmo_sccp_user * -sccp_user_bind_pc(struct osmo_sccp_instance *inst, const char *name, - osmo_prim_cb prim_cb, uint16_t ssn, uint32_t pc) -{ - struct osmo_sccp_user *scu; - - scu = sccp_user_find(inst, ssn, pc); - if (scu) { - LOGPSCI(inst, LOGL_ERROR, - "Cannot bind user '%s' to SSN=%u PC=%s, this SSN and PC" - " is already bound by '%s'\n", - name, ssn, osmo_ss7_pointcode_print(inst->ss7, pc), scu->name); - return NULL; - } - - LOGPSCI(inst, LOGL_INFO, "Binding user '%s' to SSN=%u PC=%s\n", - name, ssn, osmo_ss7_pointcode_print(inst->ss7, pc)); - scu = talloc_zero(inst, struct osmo_sccp_user); scu->name = talloc_strdup(scu, name); scu->inst = inst; @@ -130,41 +64,6 @@ return scu; }
-/*! \brief Bind a given SCCP User to a given SSN+PC - * \param[in] inst SCCP Instance - * \param[in] name human-readable name - * \param[in] prim_cb User provided callback to pass a primitive/msg up the stack - * \param[in] ssn Sub-System Number to bind to - * \param[in] pc Point Code to bind to - * \returns Callee-allocated SCCP User on success; negative otherwise - * - * Ownership of oph->msg in prim_cb is transferred to the user of the - * registered callback when called. - */ -struct osmo_sccp_user * -osmo_sccp_user_bind_pc(struct osmo_sccp_instance *inst, const char *name, - osmo_prim_cb prim_cb, uint16_t ssn, uint32_t pc) -{ - return sccp_user_bind_pc(inst, name, prim_cb, ssn, pc); -} - -/*! \brief Bind a given SCCP User to a given SSN (at any PC) - * \param[in] inst SCCP Instance - * \param[in] name human-readable name - * \param[in] prim_cb User provided callback to pass a primitive/msg up the stack - * \param[in] ssn Sub-System Number to bind to - * \returns Callee-allocated SCCP User on success; negative otherwise - * - * Ownership of oph->msg in prim_cb is transferred to the user of the - * registered callback when called. - */ -struct osmo_sccp_user * -osmo_sccp_user_bind(struct osmo_sccp_instance *inst, const char *name, - osmo_prim_cb prim_cb, uint16_t ssn) -{ - return sccp_user_bind_pc(inst, name, prim_cb, ssn, OSMO_SS7_PC_INVALID); -} - /*! \brief Unbind a given SCCP user * \param[in] scu SCCP User which is to be un-bound. Will be destroyed * at the time this function returns. */ @@ -200,289 +99,6 @@ return scu->prim_cb(&prim->oph, scu); }
-/* prim_cb handed to MTP code for incoming MTP-TRANSFER.ind */ -static int mtp_user_prim_cb(struct osmo_prim_hdr *oph, void *ctx) -{ - struct osmo_sccp_instance *inst = ctx; - struct osmo_mtp_prim *omp = (struct osmo_mtp_prim *)oph; - struct xua_msg *xua; - int rc; - - OSMO_ASSERT(oph->sap == MTP_SAP_USER); - - switch OSMO_PRIM(oph->primitive, oph->operation) { - case OSMO_PRIM(OSMO_MTP_PRIM_TRANSFER, PRIM_OP_INDICATION): - /* Convert from SCCP to SUA in xua_msg format */ - xua = osmo_sccp_to_xua(oph->msg); - if (!xua) { - LOGPSCI(inst, LOGL_ERROR, "Couldn't convert SCCP to SUA: %s\n", - msgb_hexdump(oph->msg)); - rc = -1; - break; - } - xua->mtp = omp->u.transfer; - /* hand this primitive into SCCP via the SCRC code */ - rc = scrc_rx_mtp_xfer_ind_xua(inst, xua); - xua_msg_free(xua); - break; - default: - LOGPSCI(inst, LOGL_ERROR, "Unknown primitive %u:%u receivd\n", - oph->primitive, oph->operation); - rc = -1; - } - msgb_free(oph->msg); - return rc; -} - -static LLIST_HEAD(sccp_instances); - -/*! \brief create a SCCP Instance and register it as user with SS7 inst - * \param[in] ss7 SS7 instance to which this SCCP instance belongs - * \param[in] priv private data to be stored within SCCP instance - * \returns callee-allocated SCCP instance on success; NULL on error */ -struct osmo_sccp_instance * -osmo_sccp_instance_create(struct osmo_ss7_instance *ss7, void *priv) -{ - struct osmo_sccp_instance *inst; - int rc; - - inst = talloc_zero(ss7, struct osmo_sccp_instance); - if (!inst) - return NULL; - - inst->ss7 = ss7; - inst->priv = priv; - INIT_LLIST_HEAD(&inst->users); - - inst->max_optional_data = SCCP_MAX_OPTIONAL_DATA; - - inst->tdefs = talloc_memdup(inst, osmo_sccp_timer_defaults, - sizeof(osmo_sccp_timer_defaults)); - osmo_tdefs_reset(inst->tdefs); - - rc = sccp_scmg_init(inst); - if (rc < 0) { - talloc_free(inst); - return NULL; - } - - inst->ss7_user = osmo_ss7_user_create(ss7, "SCCP"); - osmo_ss7_user_set_prim_cb(inst->ss7_user, mtp_user_prim_cb); - osmo_ss7_user_set_priv(inst->ss7_user, inst); - osmo_ss7_user_register(ss7, MTP_SI_SCCP, inst->ss7_user); - - llist_add_tail(&inst->list, &sccp_instances); - - return inst; -} - -void osmo_sccp_instance_destroy(struct osmo_sccp_instance *inst) -{ - struct osmo_sccp_user *scu, *scu2; - - inst->ss7->sccp = NULL; - osmo_ss7_user_unregister(inst->ss7, MTP_SI_SCCP, inst->ss7_user); - osmo_ss7_user_destroy(inst->ss7_user); - inst->ss7_user = NULL; - - llist_for_each_entry_safe(scu, scu2, &inst->users, list) { - osmo_sccp_user_unbind(scu); - } - sccp_scoc_flush_connections(inst); - llist_del(&inst->list); - talloc_free(inst); -} - -void osmo_sccp_set_priv(struct osmo_sccp_instance *sccp, void *priv) -{ - sccp->priv = priv; -} - -void *osmo_sccp_get_priv(struct osmo_sccp_instance *sccp) -{ - return sccp->priv; -} - -/*! \brief derive a basic local SCCP-Address from a given SCCP instance. - * \param[out] dest_addr pointer to output address memory - * \param[in] inst SCCP instance - * \param[in] ssn Subsystem Number */ -void osmo_sccp_local_addr_by_instance(struct osmo_sccp_addr *dest_addr, - const struct osmo_sccp_instance *inst, - uint32_t ssn) -{ - struct osmo_ss7_instance *ss7; - - OSMO_ASSERT(dest_addr); - OSMO_ASSERT(inst); - ss7 = inst->ss7; - OSMO_ASSERT(ss7); - - *dest_addr = (struct osmo_sccp_addr){}; - - osmo_sccp_make_addr_pc_ssn(dest_addr, ss7->cfg.primary_pc, ssn); -} - -/*! \brief check whether a given SCCP-Address is consistent. - * \param[in] addr SCCP address to check - * \param[in] presence mask with minimum required address components - * \returns true when address data seems plausible */ -bool osmo_sccp_check_addr(struct osmo_sccp_addr *addr, uint32_t presence) -{ - /* Minimum requirements do not match */ - if ((addr->presence & presence) != presence) - return false; - - /* GT ranges */ - if (addr->presence & OSMO_SCCP_ADDR_T_GT) { - if (addr->gt.gti > 15) - return false; - if (addr->gt.npi > 15) - return false; - if (addr->gt.nai > 127) - return false; - } - - /* Routing by GT, but no GT present */ - if (addr->ri == OSMO_SCCP_RI_GT - && !(addr->presence & OSMO_SCCP_ADDR_T_GT)) - return false; - - /* Routing by PC/SSN, but no PC/SSN present */ - if (addr->ri == OSMO_SCCP_RI_SSN_PC) { - if ((addr->presence & OSMO_SCCP_ADDR_T_PC) == 0) - return false; - if ((addr->presence & OSMO_SCCP_ADDR_T_SSN) == 0) - return false; - } - - if (addr->ri == OSMO_SCCP_RI_SSN_IP) { - if ((addr->presence & OSMO_SCCP_ADDR_T_IPv4) == 0 && - (addr->presence & OSMO_SCCP_ADDR_T_IPv6) == 0) - return false; - } - - return true; -} - -/*! Compare two SCCP Global Titles. - * \param[in] a left side. - * \param[in] b right side. - * \return -1 if a < b, 1 if a > b, and 0 if a == b. - */ -int osmo_sccp_gt_cmp(const struct osmo_sccp_gt *a, const struct osmo_sccp_gt *b) -{ - if (a == b) - return 0; - if (!a) - return -1; - if (!b) - return 1; - return memcmp(a, b, sizeof(*a)); -} - -/*! Compare two SCCP addresses by given presence criteria. - * Any OSMO_SCCP_ADDR_T_* type not set in presence_criteria is ignored. - * In case all bits are set in presence_criteria, the comparison is in the order of: - * OSMO_SCCP_ADDR_T_GT, OSMO_SCCP_ADDR_T_PC, OSMO_SCCP_ADDR_T_IPv4, OSMO_SCCP_ADDR_T_IPv6, OSMO_SCCP_ADDR_T_SSN. - * The SCCP addresses' Routing Indicator is not compared, see osmo_sccp_addr_ri_cmp(). - * \param[in] a left side. - * \param[in] b right side. - * \param[in] presence_criteria A bitmask of OSMO_SCCP_ADDR_T_* values, or OSMO_SCCP_ADDR_T_MASK to compare all parts, - * except the routing indicator. - * \return -1 if a < b, 1 if a > b, and 0 if all checked values match. - */ -int osmo_sccp_addr_cmp(const struct osmo_sccp_addr *a, const struct osmo_sccp_addr *b, uint32_t presence_criteria) -{ - int rc; - if (a == b) - return 0; - if (!a) - return -1; - if (!b) - return 1; - - if (presence_criteria & OSMO_SCCP_ADDR_T_GT) { - if ((a->presence & OSMO_SCCP_ADDR_T_GT) != (b->presence & OSMO_SCCP_ADDR_T_GT)) - return (b->presence & OSMO_SCCP_ADDR_T_GT) ? -1 : 1; - rc = osmo_sccp_gt_cmp(&a->gt, &b->gt); - if (rc) - return rc; - } - - if (presence_criteria & OSMO_SCCP_ADDR_T_PC) { - if ((a->presence & OSMO_SCCP_ADDR_T_PC) != (b->presence & OSMO_SCCP_ADDR_T_PC)) - return (b->presence & OSMO_SCCP_ADDR_T_PC) ? -1 : 1; - - if ((a->presence & OSMO_SCCP_ADDR_T_PC) - && a->pc != b->pc) - return (a->pc < b->pc)? -1 : 1; - } - - if (presence_criteria & OSMO_SCCP_ADDR_T_IPv4) { - if ((a->presence & OSMO_SCCP_ADDR_T_IPv4) != (b->presence & OSMO_SCCP_ADDR_T_IPv4)) - return (b->presence & OSMO_SCCP_ADDR_T_IPv4) ? -1 : 1; - rc = memcmp(&a->ip.v4, &b->ip.v4, sizeof(a->ip.v4)); - if (rc) - return rc; - } - - if (presence_criteria & OSMO_SCCP_ADDR_T_IPv6) { - if ((a->presence & OSMO_SCCP_ADDR_T_IPv6) != (b->presence & OSMO_SCCP_ADDR_T_IPv6)) - return (b->presence & OSMO_SCCP_ADDR_T_IPv6) ? -1 : 1; - rc = memcmp(&a->ip.v6, &b->ip.v6, sizeof(a->ip.v6)); - if (rc) - return rc; - } - - if (presence_criteria & OSMO_SCCP_ADDR_T_SSN) { - if ((a->presence & OSMO_SCCP_ADDR_T_SSN) != (b->presence & OSMO_SCCP_ADDR_T_SSN)) - return (b->presence & OSMO_SCCP_ADDR_T_SSN) ? -1 : 1; - if (a->ssn != b->ssn) - return (a->ssn < b->ssn) ? -1 : 1; - } - - return 0; -} - -/*! Compare the routing information of two SCCP addresses. - * Compare the ri of a and b, and, if equal, return osmo_sccp_addr_cmp() with presence criteria selected according to - * ri. - * \param[in] a left side. - * \param[in] b right side. - * \return -1 if a < b, 1 if a > b, and 0 if a == b. - */ -int osmo_sccp_addr_ri_cmp(const struct osmo_sccp_addr *a, const struct osmo_sccp_addr *b) -{ - uint32_t presence_criteria; - if (a == b) - return 0; - if (!a) - return -1; - if (!b) - return 1; - if (a->ri != b->ri) - return (a->ri < b->ri) ? -1 : 1; - switch (a->ri) { - case OSMO_SCCP_RI_NONE: - return 0; - case OSMO_SCCP_RI_GT: - presence_criteria = OSMO_SCCP_ADDR_T_GT; - break; - case OSMO_SCCP_RI_SSN_PC: - presence_criteria = OSMO_SCCP_ADDR_T_SSN | OSMO_SCCP_ADDR_T_PC; - break; - case OSMO_SCCP_RI_SSN_IP: - /* Pick IPv4 or v6 depending on what a->presence indicates. */ - presence_criteria = OSMO_SCCP_ADDR_T_SSN | (a->presence & (OSMO_SCCP_ADDR_T_IPv4 | OSMO_SCCP_ADDR_T_IPv6)); - break; - default: - return 0; - } - - return osmo_sccp_addr_cmp(a, b, presence_criteria); -} - /*! Compose a human readable string to describe the SCCP user's connection. * The output follows ['<scu.name>':]<local-sccp-addr>, e.g. "'OsmoHNBW':RI=SSN_PC,PC=0.23.5,SSN=RANAP", * or just "RI=SSN_PC,PC=0.23.5,SSN=RANAP" if no scu->name is set. @@ -504,438 +120,6 @@ return buf; }
-/*********************************************************************** - * Convenience function for CLIENT - ***********************************************************************/ - - /* Returns whether AS is already associated to any AS. - * Helper function for osmo_sccp_simple_client_on_ss7_id(). */ -static bool asp_serves_some_as(const struct osmo_ss7_asp *asp) -{ - struct osmo_ss7_as *as_i; - llist_for_each_entry(as_i, &asp->inst->as_list, list) { - if (osmo_ss7_as_has_asp(as_i, asp)) - return true; - } - return false; -} - -/*! \brief request an sccp client instance - * \param[in] ctx talloc context - * \param[in] ss7_id of the SS7/CS7 instance - * \param[in] name human readable name - * \param[in] default_pc pointcode to be used on missing VTY setting - * \param[in] prot protocol to be used (e.g OSMO_SS7_ASP_PROT_M3UA) - * \param[in] default_local_port local port to be used on missing VTY setting - * \param[in] default_local_ip local IP-address to be used on missing VTY setting (NULL: use library own defaults) - * \param[in] default_remote_port remote port to be used on missing VTY setting - * \param[in] default_remote_ip remote IP-address to be used on missing VTY setting (NULL: use library own defaults) - * \returns callee-allocated SCCP instance on success; NULL on error */ - -struct osmo_sccp_instance * -osmo_sccp_simple_client_on_ss7_id(void *ctx, uint32_t ss7_id, const char *name, - uint32_t default_pc, - enum osmo_ss7_asp_protocol prot, - int default_local_port, - const char *default_local_ip, - int default_remote_port, - const char *default_remote_ip) -{ - struct osmo_ss7_instance *ss7; - bool ss7_created = false; - struct osmo_ss7_as *as; - bool as_created = false; - struct osmo_ss7_route *rt; - bool rt_created = false; - struct osmo_ss7_asp *asp; - bool asp_created = false; - char *as_name, *asp_name = NULL; - int trans_proto; - - trans_proto = ss7_default_trans_proto_for_asp_proto(prot); - - /*! The function will examine the given CS7 instance and its sub - * components (as, asp, etc.). If necessary it will allocate - * the missing components. If no CS7 instance can be detected - * under the caller supplied ID, a new instance will be created - * beforehand. */ - - /* Choose default ports when the caller does not supply valid port - * numbers. */ - if (!default_remote_port || default_remote_port < 0) - default_remote_port = osmo_ss7_asp_protocol_port(prot); - if (default_local_port < 0) - default_local_port = osmo_ss7_asp_protocol_port(prot); - - /* Check if there is already an ss7 instance present under - * the given id. If not, we will create a new one. */ - ss7 = osmo_ss7_instance_find(ss7_id); - if (!ss7) { - LOGP(DLSCCP, LOGL_NOTICE, "%s: Creating SS7 instance\n", - name); - - /* Create a new ss7 instance */ - ss7 = osmo_ss7_instance_find_or_create(ctx, ss7_id); - if (!ss7) { - LOGP(DLSCCP, LOGL_ERROR, - "Failed to find or create SS7 instance\n"); - return NULL; - } - - /* Setup primary pointcode - * NOTE: This means that the user must set the pointcode to a - * proper value when a cs7 instance is defined via the VTY. */ - ss7->cfg.primary_pc = default_pc; - ss7_created = true; - } - - /* In case no valid point-code has been configured via the VTY, we - * will fall back to the default pointcode. */ - if (!osmo_ss7_pc_is_valid(ss7->cfg.primary_pc)) { - LOGP(DLSCCP, LOGL_ERROR, - "SS7 instance %u: no primary point-code set, using default point-code\n", - ss7->cfg.id); - ss7->cfg.primary_pc = default_pc; - } - - LOGP(DLSCCP, LOGL_NOTICE, "%s: Using SS7 instance %u, pc:%s\n", name, - ss7->cfg.id, osmo_ss7_pointcode_print(ss7, ss7->cfg.primary_pc)); - - /* Check if there is already an application server that matches - * the protocol we intend to use. If not, we will create one. */ - as = osmo_ss7_as_find_by_proto(ss7, prot); - if (!as) { - LOGP(DLSCCP, LOGL_NOTICE, "%s: Creating AS instance\n", - name); - as_name = talloc_asprintf(ctx, "as-clnt-%s", name); - as = osmo_ss7_as_find_or_create(ss7, as_name, prot); - talloc_free(as_name); - if (!as) - goto out_ss7; - as_created = true; - as->cfg.routing_key.pc = ss7->cfg.primary_pc; - as->simple_client_allocated = true; - } - LOGP(DLSCCP, LOGL_NOTICE, "%s: Using AS instance %s\n", name, - as->cfg.name); - - /* Create a default dynamic route if necessary */ - rt = ss7_route_table_find_route_by_dpc_mask(ss7->rtable_system, 0, 0, true); - if (!rt) { - LOGP(DLSCCP, LOGL_NOTICE, "%s: Creating default route\n", name); - rt = ss7_route_create(ss7->rtable_system, 0, 0, - true, as->cfg.name); - if (!rt) - goto out_as; - rt_created = true; - } - - /* Check if we do already have an application server process - * that is associated with the application server we have choosen - * the application server process must also match the protocol - * we intend to use. */ - asp = osmo_ss7_asp_find_by_proto(as, prot); - if (!asp) { - /* Check if the user has created an ASP for this proto that is not added on any AS yet. */ - struct osmo_ss7_asp *asp_i; - llist_for_each_entry(asp_i, &ss7->asp_list, list) { - if (asp_i->cfg.proto != prot) - continue; - if (asp_serves_some_as(asp_i)) { - /* This ASP is already on another AS. - * If it was on this AS, we'd have found it above. */ - continue; - } - /* This ASP matches the protocol and is not yet associated to any AS. Use it. */ - asp = asp_i; - LOGP(DLSCCP, LOGL_NOTICE, "%s: ASP %s for %s is not associated with any AS, using it\n", - name, asp->cfg.name, osmo_ss7_asp_protocol_name(prot)); - ss7_as_add_asp(as, asp); - /* ASP became associated to a new AS, hence it needs to be - * restarted to announce/register its Routing Context. - * Make sure proper defaults are applied if app didn't - * provide specific default values, then restart the ASP: */ - ss7_asp_restart_after_reconfigure(asp); - break; - } - if (!asp) { - asp_name = talloc_asprintf(ctx, "asp-clnt-%s", name); - LOGP(DLSCCP, LOGL_NOTICE, "%s: No unassociated ASP for %s, creating new ASP %s\n", - name, osmo_ss7_asp_protocol_name(prot), asp_name); - asp = osmo_ss7_asp_find_or_create2(ss7, asp_name, - default_remote_port, - default_local_port, - trans_proto, prot); - talloc_free(asp_name); - if (!asp) - goto out_rt; - asp_created = true; - asp->simple_client_allocated = true; - /* Ensure that the ASP we use is set to operate as a client. */ - asp->cfg.is_server = false; - /* Ensure that the ASP we use is set to role ASP. */ - asp->cfg.role = OSMO_SS7_ASP_ROLE_ASP; - if (default_local_ip) - ss7_asp_peer_set_hosts(&asp->cfg.local, asp, &default_local_ip, 1); - if (default_remote_ip) - ss7_asp_peer_set_hosts(&asp->cfg.remote, asp, &default_remote_ip, 1); - ss7_as_add_asp(as, asp); - /* Make sure proper defaults are applied if app didn't - provide specific default values, then restart the ASP: */ - ss7_asp_restart_after_reconfigure(asp); - } - } - - /* Extra sanity checks if the ASP asp-clnt-* was pre-configured over VTY: */ - if (!asp->simple_client_allocated) { - /* Forbid ASPs defined through VTY that are not entirely - * configured. "role" and "transport-role" must be explicitly provided: - */ - if (!asp->cfg.role_set_by_vty) { - LOGP(DLSCCP, LOGL_ERROR, - "%s: ASP %s defined in VTY but 'role' was not set there, please set it.\n", - name, asp->cfg.name); - goto out_asp; - } - if (!asp->cfg.trans_role_set_by_vty) { - LOGP(DLSCCP, LOGL_ERROR, - "%s: ASP %s defined in VTY but 'transport-role' was not set there, please set it.\n", - name, asp->cfg.name); - goto out_asp; - } - - /* If ASP was configured through VTY it may be explicitly configured as - * SCTP server. It may be a bit confusing since this function is to create - * a "SCCP simple client", but this allows users of this API such as - * osmo-hnbgw to support transport-role server if properly configured through VTY. - */ - if (asp->cfg.is_server) { - struct osmo_xua_server *xs; - LOGP(DLSCCP, LOGL_NOTICE, - "%s: Requesting an SCCP simple client on ASP %s configured with 'transport-role server'\n", - name, asp->cfg.name); - xs = ss7_xua_server_find2(ss7, - asp->cfg.trans_proto, prot, - asp->cfg.local.port); - if (!xs) { - LOGP(DLSCCP, LOGL_ERROR, "%s: Requesting an SCCP simple client on ASP %s configured " - "with 'transport-role server' but no matching xUA server was configured!\n", - name, asp->cfg.name); - goto out_asp; - } - } - /* ASP was already started here previously by VTY go_parent. */ - } - - LOGP(DLSCCP, LOGL_NOTICE, "%s: Using ASP instance %s\n", name, - asp->cfg.name); - - osmo_ss7_ensure_sccp(ss7); - if (!ss7->sccp) - goto out_asp; - - return ss7->sccp; - -out_asp: - if (asp_created) - osmo_ss7_asp_destroy(asp); -out_rt: - if (rt_created) - ss7_route_destroy(rt); -out_as: - if (as_created) - osmo_ss7_as_destroy(as); -out_ss7: - if (ss7_created) - osmo_ss7_instance_destroy(ss7); - - return NULL; -} - -/*! \brief request an sccp client instance - * \param[in] ctx talloc context - * \param[in] name human readable name - * \param[in] default_pc pointcode to be used on missing VTY setting - * \param[in] prot protocol to be used (e.g OSMO_SS7_ASP_PROT_M3UA) - * \param[in] default_local_port local port to be used on missing VTY setting - * \param[in] default_local_ip local IP-address to be used on missing VTY setting - * \param[in] default_remote_port remote port to be used on missing VTY setting - * \param[in] default_remote_ip remote IP-address to be used on missing VTY setting - * \returns callee-allocated SCCP instance on success; NULL on error */ -struct osmo_sccp_instance * -osmo_sccp_simple_client(void *ctx, const char *name, uint32_t default_pc, - enum osmo_ss7_asp_protocol prot, int default_local_port, - const char *default_local_ip, int default_remote_port, - const char *default_remote_ip) -{ - /*! This is simplified version of osmo_sccp_simple_client_on_ss7_id(). - * the only difference is that the ID of the CS7 instance will be - * set to 0 statically */ - - return osmo_sccp_simple_client_on_ss7_id(ctx, 0, name, default_pc, prot, - default_local_port, - default_local_ip, - default_remote_port, - default_remote_ip); -} - -/*********************************************************************** - * Convenience function for SERVER - ***********************************************************************/ - -struct osmo_sccp_instance * -osmo_sccp_simple_server_on_ss7_id(void *ctx, uint32_t ss7_id, uint32_t pc, - enum osmo_ss7_asp_protocol prot, - int local_port, const char *local_ip) -{ - struct osmo_ss7_instance *ss7; - struct osmo_xua_server *xs; - int trans_proto; - int rc; - - trans_proto = ss7_default_trans_proto_for_asp_proto(prot); - - if (local_port < 0) - local_port = osmo_ss7_asp_protocol_port(prot); - - /* allocate + initialize SS7 instance */ - ss7 = osmo_ss7_instance_find_or_create(ctx, ss7_id); - if (!ss7) - return NULL; - ss7->cfg.primary_pc = pc; - - xs = ss7_xua_server_create2(ss7, trans_proto, prot, local_port, local_ip); - if (!xs) - goto out_ss7; - - rc = ss7_xua_server_bind(xs); - if (rc < 0) - goto out_xs; - - /* Allocate SCCP stack */ - osmo_ss7_ensure_sccp(ss7); - if (!ss7->sccp) - goto out_xs; - - return ss7->sccp; - -out_xs: - ss7_xua_server_destroy(xs); -out_ss7: - osmo_ss7_instance_destroy(ss7); - - return NULL; -} - -struct osmo_sccp_instance * -osmo_sccp_simple_server(void *ctx, uint32_t pc, - enum osmo_ss7_asp_protocol prot, int local_port, - const char *local_ip) -{ - return osmo_sccp_simple_server_on_ss7_id(ctx, 0, pc, prot, - local_port, local_ip); -} - -struct osmo_sccp_instance * -osmo_sccp_simple_server_add_clnt(struct osmo_sccp_instance *inst, - enum osmo_ss7_asp_protocol prot, - const char *name, uint32_t pc, - int local_port, int remote_port, - const char *remote_ip) -{ - struct osmo_ss7_instance *ss7 = inst->ss7; - struct osmo_ss7_as *as; - struct osmo_ss7_route *rt; - struct osmo_ss7_asp *asp; - struct osmo_xua_server *oxs; - char *as_name, *asp_name; - int trans_proto; - - trans_proto = ss7_default_trans_proto_for_asp_proto(prot); - - if (local_port < 0) - local_port = osmo_ss7_asp_protocol_port(prot); - - if (remote_port < 0) - remote_port = osmo_ss7_asp_protocol_port(prot); - - as_name = talloc_asprintf(ss7, "as-srv-%s", name); - asp_name = talloc_asprintf(ss7, "asp-srv-%s", name); - - /* application server */ - as = osmo_ss7_as_find_or_create(ss7, as_name, prot); - if (!as) - goto out_strings; - - /* route only selected PC to the client */ - rt = ss7_route_create(ss7->rtable_system, pc, 0xffff, true, as_name); - if (!rt) - goto out_as; - - asp = osmo_ss7_asp_find_or_create2(ss7, asp_name, - remote_port, local_port, - trans_proto, prot); - if (!asp) - goto out_rt; - oxs = ss7_xua_server_find2(ss7, asp->cfg.trans_proto, prot, local_port); - if (!oxs) - goto out_asp; - if (ss7_asp_peer_set_hosts(&asp->cfg.local, asp, - (const char* const*)oxs->cfg.local.host, - oxs->cfg.local.host_cnt) < 0) - goto out_asp; - if (ss7_asp_peer_add_host(&asp->cfg.remote, asp, remote_ip) < 0) - goto out_asp; - asp->cfg.is_server = true; - asp->cfg.role = OSMO_SS7_ASP_ROLE_SG; - ss7_as_add_asp(as, asp); - talloc_free(asp_name); - talloc_free(as_name); - osmo_ss7_asp_restart(asp); - - return ss7->sccp; - -out_asp: - osmo_ss7_asp_destroy(asp); -out_rt: - ss7_route_destroy(rt); -out_as: - osmo_ss7_as_destroy(as); -out_strings: - talloc_free(as_name); - talloc_free(asp_name); - - return NULL; -} - -/*! Adjust the upper bound for the optional data length (the payload) for CR, CC, CREF and RLSD messages. - * For any Optional Data part larger than this value in octets, send CR, CC, CREF and RLSD messages without any payload, - * and send the data payload in a separate Data Form 1 message. ITU-T Q.713 sections 4.2 thru 4.5 define a limit of 130 - * bytes for the 'Data' parameter. This limit can be adjusted here. May be useful for interop with nonstandard SCCP - * peers. - * \param[in] sccp SCCP instance to reconfigure. - * \param[in] val Number of bytes to set as upper bound for the optional data length, or pass a negative value to set - * the standard value of SCCP_MAX_OPTIONAL_DATA == 130, which conforms to ITU-T Q.713. - */ -void osmo_sccp_set_max_optional_data(struct osmo_sccp_instance *inst, int val) -{ - if (!inst) - return; - if (val < 0) - val = SCCP_MAX_OPTIONAL_DATA; - inst->max_optional_data = val; -} - -/*! \brief get the SS7 instance that is related to the given SCCP instance - * \param[in] sccp SCCP instance - * \returns SS7 instance; NULL if sccp was NULL */ -struct osmo_ss7_instance *osmo_sccp_get_ss7(const struct osmo_sccp_instance *sccp) -{ - if (!sccp) - return NULL; - return sccp->ss7; -} - /*! \brief get the SCCP instance that is related to the given sccp user * \param[in] scu SCCP user * \returns SCCP instance; NULL if scu was NULL */ diff --git a/src/sccp_user.h b/src/sccp_user.h index b0b73fe..95b37ee 100644 --- a/src/sccp_user.h +++ b/src/sccp_user.h @@ -32,6 +32,8 @@ struct osmo_fsm_inst *as_fi; };
+struct osmo_sccp_user *sccp_user_alloc(struct osmo_sccp_instance *inst, const char *name, + osmo_prim_cb prim_cb, uint16_t ssn, uint32_t pc);
int sccp_user_prim_up(struct osmo_sccp_user *scut, struct osmo_scu_prim *prim);
diff --git a/src/sccp_vty.c b/src/sccp_vty.c index c8d143f..5243346 100644 --- a/src/sccp_vty.c +++ b/src/sccp_vty.c @@ -41,6 +41,7 @@
#include "xua_internal.h" #include "sccp_connection.h" +#include "sccp_instance.h" #include "sccp_internal.h" #include "sccp_user.h" #include "ss7_instance.h" diff --git a/src/ss7_vty.c b/src/ss7_vty.c index a8869d4..d774236 100644 --- a/src/ss7_vty.c +++ b/src/ss7_vty.c @@ -45,6 +45,7 @@ #include "ss7_route.h" #include "ss7_route_table.h" #include "ss7_internal.h" +#include "ss7_user.h" #include "ss7_vty.h" #include "ss7_xua_srv.h"