aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHarald Welte <laforge@osmocom.org>2021-02-07 17:30:26 +0100
committerHarald Welte <laforge@osmocom.org>2021-02-08 18:11:08 +0100
commitd2dc5ed987a218254f8bd4aced8a1e1e613bedd7 (patch)
treeb1b875f6ea043cc7092c07d6b46bd69419e8079d
parent943affdd48151dfeca009976dd78db79fb77ca55 (diff)
sccp: Add minimalistic SCMG implementation
SCMG (SCCP Management) is a special sub-system that normally resides at SSN=1. In Osmocom we so far ignored its existence. However, in terms of interop with other implementation, we should implement at least some basic features. The only procedure implemented in this initial commit is the response to an incoming SST (Subsystem Test) message. If we don't respond to this message, a remote SCCP entity could assume the SSN is dead on our side, rendering communication impossible. Change-Id: I04b162476f7652ef0540b5ea7299e9447efd1d09
-rw-r--r--include/osmocom/sigtran/Makefile.am3
-rw-r--r--include/osmocom/sigtran/protocol/sccp_scmg.h25
-rw-r--r--src/sccp_internal.h1
-rw-r--r--src/sccp_scmg.c131
-rw-r--r--src/sccp_user.c7
5 files changed, 166 insertions, 1 deletions
diff --git a/include/osmocom/sigtran/Makefile.am b/include/osmocom/sigtran/Makefile.am
index 0aa90cb..d9cc20c 100644
--- a/include/osmocom/sigtran/Makefile.am
+++ b/include/osmocom/sigtran/Makefile.am
@@ -3,5 +3,6 @@ sigtran_HEADERS = xua_types.h xua_msg.h m2ua_types.h sccp_sap.h \
sigtrandir = $(includedir)/osmocom/sigtran
-sigtran_prot_HEADERS = protocol/sua.h protocol/m3ua.h protocol/mtp.h
+sigtran_prot_HEADERS = protocol/sua.h protocol/m3ua.h protocol/mtp.h \
+ protocol/sccp_scmg.h
sigtran_protdir = $(includedir)/osmocom/sigtran/protocol
diff --git a/include/osmocom/sigtran/protocol/sccp_scmg.h b/include/osmocom/sigtran/protocol/sccp_scmg.h
new file mode 100644
index 0000000..5d97828
--- /dev/null
+++ b/include/osmocom/sigtran/protocol/sccp_scmg.h
@@ -0,0 +1,25 @@
+#pragma once
+
+/* SCCP Management as per Section 5.3 of ITU-T Q.713 */
+
+enum sccp_scmg_msg_type {
+ SCCP_SCMG_MSGT_SSA = 0x01, /* Subsystem Allowed */
+ SCCP_SCMG_MSGT_SSP = 0x02, /* Subsystem Prohibited */
+ SCCP_SCMG_MSGT_SST = 0x03, /* Subsystem Status Test */
+ SCCP_SCMG_MSGT_SOR = 0x04, /* Subsystem Out-of-service Request */
+ SCCP_SCMG_MSGT_SOG = 0x05, /* Subsystem Out-of-service Grant */
+ SCCP_SCMG_MSGT_SSC = 0x06, /* Subsystem Congested */
+};
+
+struct sccp_scmg_msg {
+ uint8_t msg_type; /* enum sccp_scmg_msg_type */
+ uint8_t affected_ssn;
+ uint16_t affected_pc;
+ uint8_t smi;
+ /* one octet, only in case of SSC */
+ uint8_t ssc_congestion_lvl[0];
+} __attribute__ ((packed));
+
+extern const struct value_string sccp_scmg_msgt_names[];
+static inline const char *sccp_scmg_msgt_name(enum sccp_scmg_msg_type msgt)
+{ return get_value_string(sccp_scmg_msgt_names, msgt); }
diff --git a/src/sccp_internal.h b/src/sccp_internal.h
index 6ad7ee3..6bd2af9 100644
--- a/src/sccp_internal.h
+++ b/src/sccp_internal.h
@@ -139,3 +139,4 @@ void sccp_lbcs_local_bcast_state(struct osmo_sccp_instance *inst,
/* SCCP Management (SCMG) */
void sccp_scmg_rx_mtp_pause(struct osmo_sccp_instance *inst, uint32_t dpc);
void sccp_scmg_rx_mtp_resume(struct osmo_sccp_instance *inst, uint32_t dpc);
+int sccp_scmg_init(struct osmo_sccp_instance *inst);
diff --git a/src/sccp_scmg.c b/src/sccp_scmg.c
index 4a8ccf6..0e3905d 100644
--- a/src/sccp_scmg.c
+++ b/src/sccp_scmg.c
@@ -30,6 +30,7 @@
#include <osmocom/sigtran/sccp_sap.h>
#include <osmocom/sigtran/protocol/sua.h>
+#include <osmocom/sigtran/protocol/sccp_scmg.h>
#include <osmocom/sccp/sccp_types.h>
#include "xua_internal.h"
@@ -81,3 +82,133 @@ void sccp_scmg_rx_mtp_resume(struct osmo_sccp_instance *inst, uint32_t dpc)
/* 8) local broadcast of "user-in-service"
* [this would require us to track SSNs at each PC, which we don't] */
}
+
+const struct value_string sccp_scmg_msgt_names[] = {
+ { SCCP_SCMG_MSGT_SSA, "SSA (Subsystem Allowed)" },
+ { SCCP_SCMG_MSGT_SSP, "SSP (Subsystem Prohibited)" },
+ { SCCP_SCMG_MSGT_SST, "SST (Subsystem Status Test)" },
+ { SCCP_SCMG_MSGT_SOR, "SOR (Subsystem Out-of-service Request)" },
+ { SCCP_SCMG_MSGT_SOG, "SOG (Subsystem Out-of-service Grant)" },
+ { SCCP_SCMG_MSGT_SSC, "SSC (Subsystem Congested)" },
+ { 0, NULL }
+};
+
+static int sccp_scmg_tx(struct osmo_sccp_user *scu, const struct osmo_sccp_addr *calling_addr,
+ const struct osmo_sccp_addr *called_addr,
+ uint8_t msg_type, uint8_t ssn, uint16_t pc, uint8_t smi, uint8_t *ssc_cong_lvl)
+{
+ struct msgb *msg = sccp_msgb_alloc(__func__);
+ struct osmo_scu_prim *prim;
+ struct osmo_scu_unitdata_param *param;
+ struct sccp_scmg_msg *scmg;
+
+ /* fill primitive header */
+ prim = (struct osmo_scu_prim *) msgb_put(msg, sizeof(*prim));
+ param = &prim->u.unitdata;
+ memcpy(&param->calling_addr, calling_addr, sizeof(*calling_addr));
+ memcpy(&param->called_addr, called_addr, sizeof(*called_addr));
+ osmo_prim_init(&prim->oph, SCCP_SAP_USER, OSMO_SCU_PRIM_N_UNITDATA, PRIM_OP_REQUEST, msg);
+
+ /* Fill the actual SCMG message */
+ msg->l2h = msgb_put(msg, sizeof(*scmg));
+ scmg = (struct sccp_scmg_msg *) msg->l2h;
+ scmg->msg_type = msg_type;
+ scmg->affected_ssn = ssn;
+ scmg->affected_pc = pc;
+ scmg->smi = smi;
+
+ /* add congestion level in case of SSC message */
+ if (msg_type == SCCP_SCMG_MSGT_SSC) {
+ msgb_put(msg, 1);
+ OSMO_ASSERT(ssc_cong_lvl);
+ scmg->ssc_congestion_lvl[1] = *ssc_cong_lvl;
+ }
+
+ return osmo_sccp_user_sap_down(scu, &prim->oph);
+}
+
+
+/* Subsystem Test received */
+static int scmg_rx_sst(struct osmo_sccp_user *scu, const struct osmo_sccp_addr *calling_addr,
+ const struct osmo_sccp_addr *called_addr, const struct sccp_scmg_msg *sst)
+{
+ /* Q.714 5.3.4.3 Actions at the receiving side (of SST) */
+
+ /* check "ignore subsystem status test" and bail out */
+ /* check if SSN in question is available. If yes, return SSA. If not, ignore */
+ scu = sccp_user_find(scu->inst, sst->affected_ssn, sst->affected_pc);
+ if (!scu)
+ return 0;
+
+ /* is subsystem available? */
+ if (0 /* !subsys_available(scu) */)
+ return 0;
+
+ return sccp_scmg_tx(scu, called_addr, calling_addr, SCCP_SCMG_MSGT_SSA,
+ sst->affected_ssn, sst->affected_pc, 0, NULL);
+}
+
+static int scmg_rx(struct osmo_sccp_user *scu, const struct osmo_sccp_addr *calling_addr,
+ const struct osmo_sccp_addr *called_addr, const struct sccp_scmg_msg *scmg)
+{
+ switch (scmg->msg_type) {
+ case SCCP_SCMG_MSGT_SST:
+ return scmg_rx_sst(scu, calling_addr, called_addr, scmg);
+ case SCCP_SCMG_MSGT_SSP:
+ case SCCP_SCMG_MSGT_SSA:
+ case SCCP_SCMG_MSGT_SOR:
+ case SCCP_SCMG_MSGT_SOG:
+ case SCCP_SCMG_MSGT_SSC:
+ default:
+ LOGP(DLSCCP, LOGL_NOTICE, "Rx unsupported SCCP SCMG %s, ignoring",
+ sccp_scmg_msgt_name(scmg->msg_type));
+ break;
+ }
+ return 0;
+}
+
+/* main entry point for SCCP user primitives from SCRC/SCOC */
+static int scmg_prim_cb(struct osmo_prim_hdr *oph, void *_scu)
+{
+ struct osmo_sccp_user *scu = _scu;
+ struct osmo_scu_prim *prim = (struct osmo_scu_prim *) oph;
+ struct osmo_scu_unitdata_param *param;
+ struct sccp_scmg_msg *scmg;
+ int rc = 0;
+
+ switch (OSMO_PRIM_HDR(oph)) {
+ case OSMO_PRIM(OSMO_SCU_PRIM_N_UNITDATA, PRIM_OP_INDICATION):
+ param = &prim->u.unitdata;
+ scmg = msgb_l2(oph->msg);
+ /* ensure minimum length based on message type */
+ if (msgb_l2len(oph->msg) < sizeof(*scmg)) {
+ rc = -1;
+ break;
+ }
+ if (scmg->msg_type == SCCP_SCMG_MSGT_SSC && msgb_l2len(oph->msg) < sizeof(*scmg)+1) {
+ rc = -1;
+ break;
+ }
+ /* interestingly, PC is specified to be encoded in little endian ?!? */
+ scmg->affected_pc = osmo_load16le(&scmg->affected_pc);
+ rc = scmg_rx(scu, &param->calling_addr, &param->called_addr, scmg);
+ break;
+ default:
+ LOGP(DLSCCP, LOGL_ERROR, "unsupported SCCP user primitive %s\n",
+ osmo_scu_prim_name(oph));
+ break;
+ }
+
+ msgb_free(oph->msg);
+ return rc;
+}
+
+/* register SCMG as SCCP user for SSN=1 */
+int sccp_scmg_init(struct osmo_sccp_instance *inst)
+{
+ struct osmo_sccp_user *scu;
+ scu = osmo_sccp_user_bind(inst, "SCCP Maangement", scmg_prim_cb, SCCP_SSN_MANAGEMENT);
+ if (!scu)
+ return -1;
+ return 0;
+}
diff --git a/src/sccp_user.c b/src/sccp_user.c
index 880cab1..d8e78f0 100644
--- a/src/sccp_user.c
+++ b/src/sccp_user.c
@@ -220,6 +220,7 @@ 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)
@@ -235,6 +236,12 @@ osmo_sccp_instance_create(struct osmo_ss7_instance *ss7, void *priv)
inst->ss7_user.prim_cb = mtp_user_prim_cb;
inst->ss7_user.priv = inst;
+ rc = sccp_scmg_init(inst);
+ if (rc < 0) {
+ talloc_free(inst);
+ return NULL;
+ }
+
osmo_ss7_user_register(ss7, MTP_SI_SCCP, &inst->ss7_user);
llist_add_tail(&inst->list, &sccp_instances);