319 lines
12 KiB
C
319 lines
12 KiB
C
/* SCCP Management (SCMG) according to ITU-T Q.713/Q.714 */
|
|
|
|
/* (C) 2021 by Harald Welte <laforge@gnumonks.org>
|
|
* All Rights reserved
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0+
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include <string.h>
|
|
|
|
#include <osmocom/core/utils.h>
|
|
#include <osmocom/core/linuxlist.h>
|
|
#include <osmocom/core/logging.h>
|
|
#include <osmocom/core/timer.h>
|
|
#include <osmocom/core/fsm.h>
|
|
|
|
#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"
|
|
#include "sccp_internal.h"
|
|
|
|
/* ITU-T Q.714 5.3.3 Subsystem allowed */
|
|
void sccp_scmg_rx_ssn_allowed(struct osmo_sccp_instance *inst, uint32_t dpc, uint32_t ssn, uint32_t smi)
|
|
{
|
|
struct osmo_scu_state_param state;
|
|
/* 1) Instruct the translation function to update the translation tables */
|
|
/* 2) Mark as "allowed" the status of that subsystem. */
|
|
/* 3) Initiate a local broadcast of "User-in-service" information for the allowed subsystem */
|
|
state = (struct osmo_scu_state_param) {
|
|
.affected_pc = dpc,
|
|
.affected_ssn = ssn,
|
|
.user_in_service = true,
|
|
.ssn_multiplicity_ind = smi,
|
|
};
|
|
sccp_lbcs_local_bcast_state(inst, &state);
|
|
/* 4) Discontinue the subsystem status test if such a test was in progress */
|
|
/* 5) Initiate a broadcast of Subsystem-Allowed messages to concerned signalling points. */
|
|
}
|
|
|
|
/* ITU-T Q.714 5.3.2 Subsystem prohibited */
|
|
void sccp_scmg_rx_ssn_prohibited(struct osmo_sccp_instance *inst, uint32_t dpc, uint32_t ssn, uint32_t smi)
|
|
{
|
|
struct osmo_scu_state_param state;
|
|
/* 1) instruct the translation function to update the translation tables */
|
|
/* 2) mark as "prohibited" the status of that subsystem */
|
|
/* 3) initiate a local broadcast of "User-out-of-service" information */
|
|
state = (struct osmo_scu_state_param) {
|
|
.affected_pc = dpc,
|
|
.affected_ssn = ssn,
|
|
.user_in_service = false,
|
|
.ssn_multiplicity_ind = smi,
|
|
};
|
|
sccp_lbcs_local_bcast_state(inst, &state);
|
|
|
|
/* 4) initiate the subsystem status test procedure if the prohibited subsystem is not local */
|
|
/* 5) initiate a broadcast of Subsystem-Prohibited messages to concerned SP */
|
|
/* 6) cancel "ignore subsystem status test" and the associated timer if in progress and if
|
|
* the newly prohibited subsystem resides at the local node. */
|
|
}
|
|
|
|
/*! brief MTP -> SNM (MTP-PAUSE.ind) - inability to providing MTP service Q.714 5.2.2 */
|
|
void sccp_scmg_rx_mtp_pause(struct osmo_sccp_instance *inst, uint32_t dpc)
|
|
{
|
|
struct osmo_scu_pcstate_param pcstate;
|
|
/* 1) Informs the translation function to update the translation tables. */
|
|
/* 2) SCCP management marks as "prohibited" the status of the remote signalling point, the
|
|
remote SCCP and each subsystem at the remote signalling point. */
|
|
/* 3) Discontinues all subsystem status tests (including SSN = 1) */
|
|
|
|
/* 4) local broadcast of "user-out-of-service" for each SSN at that dest
|
|
* [this would require us to track SSNs at each PC, which we don't] */
|
|
|
|
/* 5) local broadcast of "signaling point inaccessible" */
|
|
/* 6) local broadcast of "remote SCCP unavailable" */
|
|
pcstate = (struct osmo_scu_pcstate_param) {
|
|
.affected_pc = dpc,
|
|
.restricted_importance_level = 0,
|
|
.sp_status = OSMO_SCCP_SP_S_INACCESSIBLE,
|
|
.remote_sccp_status = OSMO_SCCP_REM_SCCP_S_UNAVAILABLE_UNKNOWN,
|
|
};
|
|
sccp_lbcs_local_bcast_pcstate(inst, &pcstate);
|
|
}
|
|
|
|
/*! brief MTP -> SNM (MTP-RESUME.ind) - ability of providing the MTP service Q.714 5.2.3 */
|
|
void sccp_scmg_rx_mtp_resume(struct osmo_sccp_instance *inst, uint32_t dpc)
|
|
{
|
|
struct osmo_scu_pcstate_param pcstate;
|
|
/* 1) Sets the congestion state of that signalling point */
|
|
/* 2) Instructs the translation function to update the translation tables. */
|
|
/* 3) Marks as "allowed" the status of that destination, and the SCCP */
|
|
/* 4) - not applicable */
|
|
/* 5) Marks as "allowed" the status of remote subsystems */
|
|
|
|
/* 6) local broadcast of "signalling point accessible" */
|
|
/* 7) local broadcast of "remote SCCP accessible" */
|
|
pcstate = (struct osmo_scu_pcstate_param) {
|
|
.affected_pc = dpc,
|
|
.restricted_importance_level = 0,
|
|
.sp_status = OSMO_SCCP_SP_S_ACCESSIBLE,
|
|
.remote_sccp_status = OSMO_SCCP_REM_SCCP_S_AVAILABLE,
|
|
};
|
|
sccp_lbcs_local_bcast_pcstate(inst, &pcstate);
|
|
|
|
/* 8) local broadcast of "user-in-service"
|
|
* [this would require us to track SSNs at each PC, which we don't] */
|
|
}
|
|
|
|
void sccp_scmg_rx_mtp_status(struct osmo_sccp_instance *inst, uint32_t dpc, enum mtp_unavail_cause cause)
|
|
{
|
|
struct osmo_scu_pcstate_param pcstate;
|
|
/* 1) Informs the translation function to update the translation tables. */
|
|
/* 2) In the case where the SCCP has received an MTP-STATUS indication primitive relating to
|
|
Mark the status of the SCCP and each SSN for the relevant destination to "prohibited"
|
|
and initiates a subsystem status test with SSN = 1. If the cause in the MTP-STATUS
|
|
indication primitive indicates "unequipped user", then no subsystem status test is
|
|
initiated. */
|
|
/* 3) Discontinues all subsystem status tests (including SSN = 1) if an MTP-STATUS
|
|
indication primitive is received with a cause of "unequipped SCCP". The SCCP
|
|
discontinues all subsystem status tests, except for SSN = 1, if an MTP-STATUS
|
|
indication primitive is received with a cause of either "unknown" or "inaccessible" */
|
|
switch (cause) {
|
|
case MTP_UNAVAIL_C_UNKNOWN:
|
|
case MTP_UNAVAIL_C_UNEQUIP_REM_USER:
|
|
case MTP_UNAVAIL_C_INACC_REM_USER:
|
|
break;
|
|
}
|
|
|
|
/* 4) local broadcast of "user-out-of-service" for each SSN at that dest
|
|
* [this would require us to track SSNs at each PC, which we don't] */
|
|
|
|
/* 6) local broadcast of "remote SCCP unavailable" */
|
|
pcstate = (struct osmo_scu_pcstate_param) {
|
|
.affected_pc = dpc,
|
|
.restricted_importance_level = 0,
|
|
.sp_status = OSMO_SCCP_SP_S_ACCESSIBLE,
|
|
.remote_sccp_status = OSMO_SCCP_REM_SCCP_S_UNAVAILABLE_UNKNOWN,
|
|
};
|
|
sccp_lbcs_local_bcast_pcstate(inst, &pcstate);
|
|
}
|
|
|
|
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 Allowed received */
|
|
static int scmg_rx_ssa(struct osmo_sccp_user *scu, const struct osmo_sccp_addr *calling_addr,
|
|
const struct osmo_sccp_addr *called_addr, const struct sccp_scmg_msg *ssa)
|
|
{
|
|
/* Q.714 5.3.3 */
|
|
if (ssa->affected_ssn == SCCP_SSN_MANAGEMENT)
|
|
return 0;
|
|
|
|
/* if the SSN is not marked as prohibited, ignore */
|
|
|
|
/* Q.714 5.3.2.2 a) */
|
|
sccp_scmg_rx_ssn_allowed(scu->inst, ssa->affected_pc, ssa->affected_ssn, ssa->smi);
|
|
|
|
/* If the remote SCCP, at which the subsystem reported in the SSA message resides, is marked
|
|
* inaccessible, then the message is treated as an implicit indication of SCCP restart */
|
|
return 0;
|
|
}
|
|
|
|
/* Subsystem Prohibited received */
|
|
static int scmg_rx_ssp(struct osmo_sccp_user *scu, const struct osmo_sccp_addr *calling_addr,
|
|
const struct osmo_sccp_addr *called_addr, const struct sccp_scmg_msg *ssp)
|
|
{
|
|
/* Q.714 5.3.2.2 a) */
|
|
sccp_scmg_rx_ssn_prohibited(scu->inst, ssp->affected_pc, ssp->affected_ssn, ssp->smi);
|
|
return 0;
|
|
}
|
|
|
|
/* 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_SSA:
|
|
return scmg_rx_ssa(scu, calling_addr, called_addr, scmg);
|
|
case SCCP_SCMG_MSGT_SSP:
|
|
return scmg_rx_ssp(scu, calling_addr, called_addr, scmg);
|
|
case SCCP_SCMG_MSGT_SST:
|
|
return scmg_rx_sst(scu, calling_addr, called_addr, scmg);
|
|
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\n",
|
|
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;
|
|
case OSMO_PRIM(OSMO_SCU_PRIM_N_PCSTATE, PRIM_OP_INDICATION):
|
|
LOGP(DLSCCP, LOGL_DEBUG, "Ignoring SCCP user primitive %s\n", osmo_scu_prim_name(oph));
|
|
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 Management", scmg_prim_cb, SCCP_SSN_MANAGEMENT);
|
|
if (!scu)
|
|
return -1;
|
|
return 0;
|
|
}
|