Add support for LCLS to the MSC

This commit is largely based on work by
Max <msuraev@sysmocom.de>

Adds LCLS parameters for A-interface transactions
This commit also adds a vty option to facilitate globally
disabling LCLS for all calls on this MSC.

Add a global call reference (GCR) to MNCC and therefore
bump the MNCC version to version 8. (This commit has to be
merged at the same time as the corresponing commit in the
osmo-sip-connector for mncc-external use.)

Depends: osmo-sip-connector Id40d7e0fed9356f801b3627c118150055e7232b1
Change-Id: I705c860e51637b4537cad65a330ecbaaca96dd5b
This commit is contained in:
Keith Whyte 2021-05-16 02:59:52 +02:00 committed by laforge
parent 890ece1277
commit a1a70be593
13 changed files with 134 additions and 12 deletions

View File

@ -8,6 +8,7 @@ enum {
DCC,
DMM,
DRR,
DLCLS,
DMNCC,
DPAG,
DMSC,

View File

@ -260,6 +260,10 @@ struct gsm_network {
/* Whether to use call waiting on the network */
bool call_waiting;
/* Whether to use lcls on the network */
bool lcls_permitted;
char *sms_db_file_path;
};

View File

@ -26,6 +26,7 @@
#include <osmocom/core/linuxlist.h>
#include <osmocom/gsm/mncc.h>
#include <osmocom/gsm/gsm29205.h>
#include <stdint.h>
#include <netinet/in.h>
@ -160,7 +161,7 @@ struct gsm_mncc {
unsigned char lchan_type;
unsigned char lchan_mode;
struct osmo_gcr_parsed gcr;
/* A buffer to contain SDP ('\0' terminated) */
char sdp[1024];
};
@ -171,7 +172,7 @@ struct gsm_data_frame {
unsigned char data[0];
};
#define MNCC_SOCK_VERSION 7
#define MNCC_SOCK_VERSION 8
struct gsm_mncc_hello {
uint32_t msg_type;
uint32_t version;

View File

@ -88,6 +88,7 @@ struct ran_assignment_command {
uint8_t osmux_cid;
bool call_id_present;
uint32_t call_id;
struct osmo_lcls *lcls;
};
struct ran_cipher_mode_command {

View File

@ -100,6 +100,7 @@ struct gsm_trans {
struct osmo_timer_list timer_guard;
struct gsm_mncc msg; /* stores setup/disconnect/release message */
bool mncc_initiated; /* Whether an MNCC Release is necessary on failure */
struct osmo_lcls *lcls;
} cc;
struct {
struct gsm411_smc_inst smc_inst;
@ -145,6 +146,8 @@ struct gsm_trans *trans_find_by_sm_rp_mr(const struct gsm_network *net,
const struct vlr_subscr *vsub,
uint8_t sm_rp_mr);
struct osmo_lcls *trans_lcls_compose(const struct gsm_trans *trans, bool use_lac);
struct gsm_trans *trans_alloc(struct gsm_network *net,
struct vlr_subscr *vsub,
enum trans_type type, uint8_t trans_id,

View File

@ -318,6 +318,16 @@ static void cc_paging_cb(struct msc_a *msc_a, struct gsm_trans *trans)
msc_a_get(msc_a, MSC_A_USE_CC);
trans->msc_a = msc_a;
trans->paging_request = NULL;
/* Get the GCR from the MO call leg (if any). */
if (!trans->cc.lcls) {
trans->cc.lcls = trans_lcls_compose(trans, true);
if (trans->cc.lcls) {
trans->cc.lcls->gcr = trans->cc.msg.gcr;
trans->cc.lcls->gcr_available = true;
}
}
osmo_fsm_inst_dispatch(msc_a->c.fi, MSC_A_EV_TRANSACTION_ACCEPTED, trans);
/* send SETUP request to called party */
gsm48_cc_tx_setup(trans, &trans->cc.msg);
@ -502,6 +512,14 @@ static int gsm48_cc_rx_setup(struct gsm_trans *trans, struct msgb *msg)
memset(&setup, 0, sizeof(struct gsm_mncc));
setup.callref = trans->callref;
/* New Global Call Reference */
if (!trans->cc.lcls)
trans->cc.lcls = trans_lcls_compose(trans, true);
/* Pass the LCLS GCR on to the MT call leg via MNCC */
if (trans->cc.lcls)
setup.gcr = trans->cc.lcls->gcr;
tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0);
/* emergency setup is identified by msg_type */
if (msg_type == GSM48_MT_CC_EMERG_SETUP) {

View File

@ -569,6 +569,7 @@ static void msc_a_call_leg_ran_local_addr_available(struct msc_a *msc_a)
.osmux_cid = msc_a->cc.call_leg->rtp[RTP_TO_RAN]->local_osmux_cid,
.call_id_present = true,
.call_id = cc_trans->callref,
.lcls = cc_trans->cc.lcls,
},
};
if (msc_a_ran_down(msc_a, MSC_ROLE_I, &msg)) {
@ -1506,6 +1507,13 @@ int msc_a_ran_dec_from_msc_i(struct msc_a *msc_a, struct msc_a_ran_dec_data *d)
rc = msc_a_up_ho(msc_a, d, MSC_HO_EV_RX_FAILURE);
break;
case RAN_MSG_LCLS_STATUS:
/* The BSS sends us LCLS_STATUS. We do nothing for now, but it is not an error. */
LOG_MSC_A(msc_a, LOGL_DEBUG, "LCLS_STATUS (%s) received from MSC-I\n",
gsm0808_lcls_status_name(msg->lcls_status.status));
rc = 0;
break;
default:
LOG_MSC_A(msc_a, LOGL_ERROR, "Message from MSC-I not implemented: %s\n", ran_msg_type_name(msg->msg_type));
rc = -ENOTSUP;

View File

@ -499,6 +499,24 @@ DEFUN(cfg_msc_no_assign_tmsi, cfg_msc_no_assign_tmsi_cmd,
return CMD_SUCCESS;
}
DEFUN_ATTR(cfg_msc_lcls_disable, cfg_msc_lcls_disable_cmd,
"lcls-permitted",
"Globally allow LCLS (Local Call Local Switch) for all calls on this MSC.\n",
CMD_ATTR_IMMEDIATE)
{
gsmnet->lcls_permitted = true;
return CMD_SUCCESS;
}
DEFUN_ATTR(cfg_msc_no_lcls_disable, cfg_msc_no_lcls_disable_cmd,
"no lcls-permitted",
NO_STR "Globally disable LCLS (Local Call Local Switch) for all calls on this MSC.\n",
CMD_ATTR_IMMEDIATE)
{
gsmnet->lcls_permitted = false;
return CMD_SUCCESS;
}
DEFUN(cfg_msc_cs7_instance_a,
cfg_msc_cs7_instance_a_cmd,
"cs7-instance-a <0-15>",
@ -764,6 +782,8 @@ static int config_write_msc(struct vty *vty)
gsmnet->ncss_guard_timeout, VTY_NEWLINE);
vty_out(vty, " %sassign-tmsi%s",
gsmnet->vlr->cfg.assign_tmsi? "" : "no ", VTY_NEWLINE);
if (gsmnet->lcls_permitted)
vty_out(vty, " lcls-permitted%s", VTY_NEWLINE);
vty_out(vty, " cs7-instance-a %u%s", gsmnet->a.cs7_instance,
VTY_NEWLINE);
@ -2083,6 +2103,8 @@ void msc_vty_init(struct gsm_network *msc_network)
install_node(&msc_node, config_write_msc);
install_element(MSC_NODE, &cfg_sms_database_cmd);
install_element(MSC_NODE, &cfg_msc_assign_tmsi_cmd);
install_element(MSC_NODE, &cfg_msc_lcls_disable_cmd);
install_element(MSC_NODE, &cfg_msc_no_lcls_disable_cmd);
install_element(MSC_NODE, &cfg_msc_mncc_internal_cmd);
install_element(MSC_NODE, &cfg_msc_mncc_external_cmd);
install_element(MSC_NODE, &cfg_msc_mncc_guard_timeout_cmd);

View File

@ -1004,7 +1004,8 @@ static struct msgb *ran_a_make_assignment_command(struct osmo_fsm_inst *log_fi,
if(ac->call_id_present == true)
call_id = &ac->call_id;
msg = gsm0808_create_ass(ac->channel_type, NULL, use_rtp_addr, use_scl, call_id);
msg = gsm0808_create_ass2(ac->channel_type, NULL, use_rtp_addr, use_scl, call_id,
NULL, ac->lcls);
if (ac->osmux_present)
_gsm0808_assignment_extend_osmux(msg, ac->osmux_cid);
return msg;

View File

@ -110,6 +110,66 @@ struct gsm_trans *trans_find_by_sm_rp_mr(const struct gsm_network *net,
return NULL;
}
struct osmo_lcls *trans_lcls_compose(const struct gsm_trans *trans, bool use_lac)
{
if (!trans->net->a.sri->sccp)
return NULL;
struct osmo_ss7_instance *ss7 = osmo_sccp_get_ss7(trans->net->a.sri->sccp);
struct osmo_lcls *lcls;
uint8_t w = osmo_ss7_pc_width(&ss7->cfg.pc_fmt);
if (!trans) {
LOGP(DCC, LOGL_ERROR, "LCLS: unable to fill parameters for unallocated transaction\n");
return NULL;
}
if (!trans->net->lcls_permitted) {
LOGP(DCC, LOGL_NOTICE, "LCLS disabled globally\n");
return NULL;
}
if (!trans->msc_a) {
LOGP(DCC, LOGL_ERROR, "LCLS: unable to fill parameters for transaction without connection\n");
return NULL;
}
if (trans->msc_a->c.ran->type != OSMO_RAT_GERAN_A) {
LOGP(DCC, LOGL_ERROR, "LCLS: only A interface is supported at the moment\n");
return NULL;
}
lcls = talloc_zero(trans, struct osmo_lcls);
if (!lcls) {
LOGP(DCC, LOGL_ERROR, "LCLS: failed to allocate osmo_lcls\n");
return NULL;
}
LOGP(DCC, LOGL_INFO, "LCLS: using %u bits (%u bytes) for node ID\n", w, w / 8);
lcls->gcr.net_len = 3;
lcls->gcr.node = ss7->cfg.primary_pc;
/* net id from Q.1902.3 3-5 bytes, this function gives 3 bytes exactly */
osmo_plmn_to_bcd(lcls->gcr.net, &trans->net->plmn);
osmo_store32be(trans->callref, lcls->gcr.cr);
osmo_store16be(use_lac ? trans->msc_a->via_cell.lai.lac : trans->msc_a->via_cell.cell_identity, lcls->gcr.cr + 3);
LOGP(DCC, LOGL_INFO, "LCLS: allocated %s-based CR-ID %s\n", use_lac ? "LAC" : "CI",
osmo_hexdump(lcls->gcr.cr, 5));
lcls->config = GSM0808_LCLS_CFG_BOTH_WAY;
lcls->control = GSM0808_LCLS_CSC_CONNECT;
lcls->corr_needed = true;
lcls->gcr_available = true;
LOGP(DCC, LOGL_DEBUG, "Filled %s\n", osmo_lcls_dump(lcls));
LOGP(DCC, LOGL_DEBUG, "Filled %s\n", osmo_gcr_dump(lcls));
return lcls;
}
static const char *trans_vsub_use(enum trans_type type)
{
return get_value_string_or_null(trans_type_names, type) ? : "trans-type-unknown";

View File

@ -258,6 +258,7 @@ struct gsm_network *msc_network_alloc(void *ctx,
mgcp_client_conf_init(&net->mgw.conf);
net->call_waiting = true;
net->lcls_permitted = false;
net->mgw.tdefs = g_mgw_tdefs;
osmo_tdefs_reset(net->mgw.tdefs);

View File

@ -1,15 +1,15 @@
test_sdp_termination()
struct gsm_mncc:
empty SDP: len=1860 sdplen=1026 sdp="\0" rc=0
empty SDP, shortest possible: len=835 sdplen=1 sdp="\0" rc=0
empty SDP, zero len: len=834 sdplen=0 sdp=- rc=-22
terminated SDP str: len=1860 sdplen=1026 sdp="Privacy is a desirable marketing option\0" rc=0
terminated SDP str, shortest possible: len=874 sdplen=40 sdp="Privacy is a desirable marketing option\0" rc=0
terminated SDP str, but len excludes nul: len=873 sdplen=39 sdp="Privacy is a desirable marketing option" rc=-22
terminated SDP str, but len too short: len=857 sdplen=23 sdp="Privacy is a desirable " rc=-22
len way too short: len=10 sdplen=-824 sdp=- rc=-22
len zero: len=0 sdplen=-834 sdp=- rc=-22
empty SDP: len=1872 sdplen=1024 sdp="\0" rc=0
empty SDP, shortest possible: len=849 sdplen=1 sdp="\0" rc=0
empty SDP, zero len: len=848 sdplen=0 sdp=- rc=-22
terminated SDP str: len=1872 sdplen=1024 sdp="Privacy is a desirable marketing option\0" rc=0
terminated SDP str, shortest possible: len=888 sdplen=40 sdp="Privacy is a desirable marketing option\0" rc=0
terminated SDP str, but len excludes nul: len=887 sdplen=39 sdp="Privacy is a desirable marketing option" rc=-22
terminated SDP str, but len too short: len=871 sdplen=23 sdp="Privacy is a desirable " rc=-22
len way too short: len=10 sdplen=-838 sdp=- rc=-22
len zero: len=0 sdplen=-848 sdp=- rc=-22
struct gsm_mncc_rtp:
empty SDP: len=1168 sdplen=1024 sdp="\0" rc=0

View File

@ -48,6 +48,8 @@ OsmoMSC(config-msc)# list
...
sms-database PATH
assign-tmsi
lcls-permitted
no lcls-permitted
mncc internal
mncc external MNCC_SOCKET_PATH
mncc guard-timeout <0-255>