diff options
author | Harald Welte <laforge@osmocom.org> | 2021-02-07 17:30:26 +0100 |
---|---|---|
committer | Harald Welte <laforge@osmocom.org> | 2021-02-08 18:11:08 +0100 |
commit | d2dc5ed987a218254f8bd4aced8a1e1e613bedd7 (patch) | |
tree | b1b875f6ea043cc7092c07d6b46bd69419e8079d | |
parent | 943affdd48151dfeca009976dd78db79fb77ca55 (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.am | 3 | ||||
-rw-r--r-- | include/osmocom/sigtran/protocol/sccp_scmg.h | 25 | ||||
-rw-r--r-- | src/sccp_internal.h | 1 | ||||
-rw-r--r-- | src/sccp_scmg.c | 131 | ||||
-rw-r--r-- | src/sccp_user.c | 7 |
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(¶m->calling_addr, calling_addr, sizeof(*calling_addr)); + memcpy(¶m->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, ¶m->calling_addr, ¶m->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); |