pespin has uploaded this change for review. (
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,012 insertions(+), 922 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/libosmo-sigtran refs/changes/82/40582/1
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..a90657d
--- /dev/null
+++ b/src/sccp_instance.c
@@ -0,0 +1,923 @@
+/* SCCP Instance related routines */
+
+/* (C) 2017 by Harald Welte <laforge(a)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_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;
+}
+
+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);
+}
+
+/***********************************************************************
+ * 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..a9ffa51 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,
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..a90dc71 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
***********************************************************************/
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"
--
To view, visit
https://gerrit.osmocom.org/c/libosmo-sigtran/+/40582?usp=email
To unsubscribe, or for help writing mail filters, visit
https://gerrit.osmocom.org/settings?usp=email
Gerrit-MessageType: newchange
Gerrit-Project: libosmo-sigtran
Gerrit-Branch: master
Gerrit-Change-Id: I632f1825fe696bf73c0f9220dc8e8463337d8ba8
Gerrit-Change-Number: 40582
Gerrit-PatchSet: 1
Gerrit-Owner: pespin <pespin(a)sysmocom.de>