aboutsummaryrefslogtreecommitdiffstats
path: root/src/libmsc/gsm_04_08_cc.c
diff options
context:
space:
mode:
authorNeels Hofmeyr <neels@hofmeyr.de>2019-10-21 03:24:11 +0200
committerNeels Hofmeyr <neels@hofmeyr.de>2020-01-06 18:00:40 +0100
commitf31a1ccd9a3eb474936f5b946287581514b29436 (patch)
treeec59ae18c6e54b3530bf2f56e09d99bc23390b0d /src/libmsc/gsm_04_08_cc.c
parent02dd265d68b771bf315cfe6620c9b2371edea828 (diff)
add full SDP codec information to the MNCC socket
This way osmo-msc can benefit from the complete codec information received via SIP, which was so far terminated at osmo-sip-connector. osmo-sip-connector could/should have translated the received SDP to MNCC bearer_cap, but this was never implemented properly. Since osmo-msc already handles SDP towards the MGW, it makes most sense to pass SDP to osmo-msc transparently. To be able to send a valid RTP IP:port in the SDP upon the first MNCC_SETUP_IND going out, move the CN side CRCX to the very start of establishing a voice call. As a result, first create MGW conns for both RAN and CN before starting. The voice_call_full.msc chart shows the change in message sequence for MO and MT voice calls. Implement cc_sdp.c, which accumulates codec information from various sources (MS, BSS, Assignment, remote call leg) and provides filtering to get the available set of codecs at any point in time. Implement codec_sdp_cc_t9n.c, to translate between SDP and the various libosmo-mgcp-client, CC and BSSMAP representations of codecs: - Speech Version, - Permitted Speech, - Speech Codec Type, - default Payload Type numbers, - enum mgcp_codecs, - FR/HR compatibility - SDP audio codec names, - various AMR configurations. A codec_map lists these relations in one large data record. Various functions provide conversions by traversing this map. Add trans->cc.mnccc_release_sent: so far, avoiding to send an MNCC release during trans_free() was done by setting the callref = 0. But that also skips CC Release. On codec mismatch, we send a specific MNCC error code but still want a normal CC Release: hence send the MNCC message, set mnccc_release_sent = true and do normal CC Release in trans_free(). (A better way to do this would be to adopt the mncc_call FSM from inter-MSC handover also for local voice calls, but that is out of scope for now. I want to try that soon, as time permits.) Change-Id: I8c3b2de53ffae4ec3a66b9dabf308c290a2c999f
Diffstat (limited to 'src/libmsc/gsm_04_08_cc.c')
-rw-r--r--src/libmsc/gsm_04_08_cc.c408
1 files changed, 346 insertions, 62 deletions
diff --git a/src/libmsc/gsm_04_08_cc.c b/src/libmsc/gsm_04_08_cc.c
index 8cfb6117d..c92e3676b 100644
--- a/src/libmsc/gsm_04_08_cc.c
+++ b/src/libmsc/gsm_04_08_cc.c
@@ -55,6 +55,9 @@
#include <osmocom/msc/rtp_stream.h>
#include <osmocom/msc/mncc_call.h>
#include <osmocom/msc/msc_t.h>
+#include <osmocom/msc/sdp_msg.h>
+#include <osmocom/msc/cc_sdp.h>
+#include <osmocom/msc/codec_sdp_cc_t9n.h>
#include <osmocom/gsm/gsm48.h>
#include <osmocom/gsm/gsm0480.h>
@@ -254,8 +257,12 @@ static int mncc_recvmsg(struct gsm_network *net, struct gsm_trans *trans,
int mncc_release_ind(struct gsm_network *net, struct gsm_trans *trans,
uint32_t callref, int location, int value)
{
+ /* BEWARE: trans may be passed as NULL to reply to invalid MNCC requests */
struct gsm_mncc rel;
+ if (trans && trans->cc.mncc_release_sent)
+ return 0;
+
memset(&rel, 0, sizeof(rel));
rel.callref = callref;
mncc_set_cause(&rel, location, value);
@@ -498,6 +505,8 @@ static int gsm48_cc_rx_setup(struct gsm_trans *trans, struct msgb *msg)
memset(&setup, 0, sizeof(struct gsm_mncc));
setup.callref = trans->callref;
+ OSMO_ASSERT(trans->msc_a);
+
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) {
@@ -567,25 +576,77 @@ static int gsm48_cc_rx_setup(struct gsm_trans *trans, struct msgb *msg)
TLVP_VAL(&tp, GSM48_IE_CC_CAP)-1);
}
- new_cc_state(trans, GSM_CSTATE_INITIATED);
+ cc_sdp_init(&trans->cc.sdp, trans->msc_a->c.ran->type,
+ setup.fields & MNCC_F_BEARER_CAP ? &trans->bearer_cap : NULL,
+ &trans->msc_a->cc.codec_list_bss_supported);
+ cc_sdp_filter(&trans->cc.sdp);
LOG_TRANS(trans, setup.emergency ? LOGL_NOTICE : LOGL_INFO, "%sSETUP to %s\n",
setup.emergency ? "EMERGENCY_" : "", setup.called.number);
+ LOG_TRANS(trans, LOGL_DEBUG, "codecs: %s\n", cc_sdp_name(&trans->cc.sdp));
rate_ctr_inc(&trans->net->msc_ctrs->ctr[MSC_CTR_CALL_MO_SETUP]);
+ new_cc_state(trans, GSM_CSTATE_INITIATED);
+
+ /* To complete the MNCC_SETUP_IND, we need to provide an RTP address and port. First instruct the MGW to create
+ * a CN-side RTP conn, and continue with MNCC_SETUP_IND once that is done. Leave trans.cc in GSM_CSTATE_NULL and
+ * note down the msg_type to indicate that we indeed composed an MNCC_SETUP_IND for later. */
+ setup.msg_type = MNCC_SETUP_IND;
+ trans->cc.msg = setup;
+ return msc_a_try_call_assignment(trans);
+ /* continue in gsm48_cc_rx_setup_cn_local_rtp_port_known() */
+}
+
+/* Callback for MNCC_SETUP_IND waiting for the core network RTP port to be established by the MGW (via msc_a) */
+void gsm48_cc_rx_setup_cn_local_rtp_port_known(struct gsm_trans *trans)
+{
+ struct msc_a *msc_a = trans->msc_a;
+ struct gsm_mncc setup = trans->cc.msg;
+ struct osmo_sockaddr_str *rtp_cn_local;
+ struct sdp_msg *sdp;
+
+ if (trans->cc.state != GSM_CSTATE_INITIATED
+ || setup.msg_type != MNCC_SETUP_IND) {
+ LOG_TRANS(trans, LOGL_ERROR,
+ "Unexpected CC state. Expected GSM_CSTATE_NULL and a buffered MNCC_SETUP_IND message,"
+ " found CC state %d and msg_type %s\n",
+ trans->cc.state, get_mncc_name(setup.msg_type));
+ trans->callref = 0;
+ trans_free(trans);
+ return;
+ }
+
+ if (!msc_a) {
+ LOG_TRANS(trans, LOGL_ERROR, "No connection for CC trans\n");
+ trans->callref = 0;
+ trans_free(trans);
+ return;
+ }
+
+ /* 'setup' above has taken the value of trans->cc.msg, we can now clear that. */
+ trans->cc.msg = (struct gsm_mncc){};
+
+ /* Insert the CN side RTP port now available into SDP and compose SDP string */
+ rtp_cn_local = call_leg_local_ip(msc_a->cc.call_leg, RTP_TO_CN);
+ if (!osmo_sockaddr_str_is_nonzero(rtp_cn_local)) {
+ LOG_TRANS(trans, LOGL_ERROR, "Cannot compose SDP for MNCC_SETUP_IND: no RTP set up for the CN side\n");
+ trans_free(trans);
+ return;
+ }
+
+ cc_sdp_filter(&trans->cc.sdp);
+ sdp = &trans->cc.sdp.result;
+ sdp->rtp = *rtp_cn_local;
+ sdp_msg_to_str(setup.sdp, sizeof(setup.sdp), sdp);
+
/* indicate setup to MNCC */
mncc_recvmsg(trans->net, trans, MNCC_SETUP_IND, &setup);
-
- /* MNCC code will modify the channel asynchronously, we should
- * ipaccess-bind only after the modification has been made to the
- * lchan->tch_mode */
- return 0;
}
static int gsm48_cc_tx_setup(struct gsm_trans *trans, void *arg)
{
- struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC STUP");
+ struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC SETUP");
struct gsm48_hdr *gh;
struct gsm_mncc *setup = arg;
int rc, trans_id;
@@ -622,15 +683,65 @@ static int gsm48_cc_tx_setup(struct gsm_trans *trans, void *arg)
gh->msg_type = GSM48_MT_CC_SETUP;
- gsm48_start_cc_timer(trans, 0x303, GSM48_T303);
+ /* We must not pass bearer_cap to cc_sdp_init(), because we haven't received the MS's Bearer Capabilities yet;
+ * the Bearer Capabilities handled here are actually the remote call leg's Bearer Capabilities to be passed on
+ * during the CC Setup. */
+ cc_sdp_init(&trans->cc.sdp, trans->msc_a->c.ran->type, NULL,
+ &trans->msc_a->cc.codec_list_bss_supported);
+
+ /* sdp.remote: if SDP is included in the MNCC, take that as definitive list of remote audio codecs. */
+ if (setup->sdp[0]) {
+ rc = sdp_msg_from_str(&trans->cc.sdp.remote, setup->sdp);
+ if (rc)
+ LOG_TRANS(trans, LOGL_ERROR, "Failed to parse remote call leg SDP: %d\n", rc);
+ }
+
+ /* sdp.remote: if there is no SDP information or we failed to parse it, try using the Bearer Capability from
+ * MNCC, if any. */
+ if (!trans->cc.sdp.remote.audio_codecs.count && (setup->fields & MNCC_F_BEARER_CAP)) {
+ trans->cc.sdp.remote = (struct sdp_msg){};
+ sdp_audio_codecs_from_bearer_cap(&trans->cc.sdp.remote.audio_codecs,
+ &setup->bearer_cap);
+ }
+
+ if (!trans->cc.sdp.remote.audio_codecs.count)
+ LOG_TRANS(trans, LOGL_ERROR,
+ "Got no information of remote audio codecs: neither SDP nor Bearer Capability. Trying anyway.\n");
+
+ /* Translate SDP to bearer capability Speech Version entries.
+ * If we supported transcoding, this could add arbitrary speech versions.
+ * For now add speech_ver entries for each codec in the SDP that matches a GSM speech_ver constant. */
+ cc_sdp_filter(&trans->cc.sdp);
+ LOG_TRANS(trans, LOGL_DEBUG, "codecs: %s\n", cc_sdp_name(&trans->cc.sdp));
+ trans->bearer_cap = (struct gsm_mncc_bearer_cap){
+ .speech_ver = { -1 },
+ };
+ sdp_audio_codecs_to_bearer_cap(&trans->bearer_cap, &trans->cc.sdp.result.audio_codecs);
+ rc = bearer_cap_set_radio(&trans->bearer_cap);
+ if (rc) {
+ LOG_TRANS(trans, LOGL_ERROR, "Error composing Bearer Capability for CC Setup\n");
+ trans_free(trans);
+ msgb_free(msg);
+ return rc;
+ }
- /* bearer capability */
- if (setup->fields & MNCC_F_BEARER_CAP) {
- /* Create a copy of the bearer capability in the transaction struct, so we
- * can use this information later */
- memcpy(&trans->bearer_cap, &setup->bearer_cap, sizeof(trans->bearer_cap));
- gsm48_encode_bearer_cap(msg, 0, &setup->bearer_cap);
+ /* If no resulting codecs remain, error out. If the MGW were able to transcode, we would just use unidentical
+ * codecs on each conn of the MGW endpoint. */
+ if (trans->bearer_cap.speech_ver[0] == -1) {
+ LOG_TRANS(trans, LOGL_ERROR, "%s: no codec match possible: %s\n",
+ get_mncc_name(setup->msg_type), cc_sdp_name(&trans->cc.sdp));
+
+ /* incompatible codecs */
+ rc = mncc_release_ind(trans->net, trans, trans->callref,
+ GSM48_CAUSE_LOC_PRN_S_LU,
+ GSM48_CC_CAUSE_INCOMPAT_DEST /* TODO: correct cause code? */);
+ trans->cc.mncc_release_sent = true;
+ trans_free(trans);
+ msgb_free(msg);
+ return rc;
}
+ gsm48_encode_bearer_cap(msg, 0, &trans->bearer_cap);
+
/* facility */
if (setup->fields & MNCC_F_FACILITY)
gsm48_encode_facility(msg, 0, &setup->facility);
@@ -657,6 +768,8 @@ static int gsm48_cc_tx_setup(struct gsm_trans *trans, void *arg)
rate_ctr_inc(&trans->net->msc_ctrs->ctr[MSC_CTR_CALL_MT_SETUP]);
+ gsm48_start_cc_timer(trans, 0x303, GSM48_T303);
+
return trans_tx_gsm48(trans, msg);
}
@@ -691,9 +804,14 @@ static int gsm48_cc_rx_call_conf(struct gsm_trans *trans, struct msgb *msg)
/* Create a copy of the bearer capability
* in the transaction struct, so we can use
* this information later */
- memcpy(&trans->bearer_cap,&call_conf.bearer_cap,
+ memcpy(&trans->bearer_cap, &call_conf.bearer_cap,
sizeof(trans->bearer_cap));
+
+ /* Note MS codec capabilities for codec negotiation */
+ trans->cc.sdp.ms = (struct sdp_audio_codecs){};
+ sdp_audio_codecs_from_bearer_cap(&trans->cc.sdp.ms, &call_conf.bearer_cap);
}
+
/* cause */
if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) {
call_conf.fields |= MNCC_F_CAUSE;
@@ -710,8 +828,6 @@ static int gsm48_cc_rx_call_conf(struct gsm_trans *trans, struct msgb *msg)
/* IMSI of called subscriber */
OSMO_STRLCPY_ARRAY(call_conf.imsi, trans->vsub->imsi);
- new_cc_state(trans, GSM_CSTATE_MO_TERM_CALL_CONF);
-
/* Assign call (if not done yet) */
rc = msc_a_try_call_assignment(trans);
@@ -720,8 +836,53 @@ static int gsm48_cc_rx_call_conf(struct gsm_trans *trans, struct msgb *msg)
if (rc)
return rc;
- return mncc_recvmsg(trans->net, trans, MNCC_CALL_CONF_IND,
- &call_conf);
+ /* Directly ack with MNCC_CALL_CONF_IND, not yet containing SDP or RTP IP:port information. */
+ new_cc_state(trans, GSM_CSTATE_MO_TERM_CALL_CONF);
+ return mncc_recvmsg(trans->net, trans, MNCC_CALL_CONF_IND, &call_conf);
+}
+
+static int mncc_recv_rtp(struct gsm_network *net, struct gsm_trans *trans, uint32_t callref,
+ int cmd, struct osmo_sockaddr_str *rtp_addr, uint32_t payload_type,
+ uint32_t payload_msg_type, const struct sdp_msg *sdp);
+
+int gsm48_cc_mt_rtp_port_and_codec_known(struct gsm_trans *trans)
+{
+ struct msc_a *msc_a = trans->msc_a;
+ struct osmo_sockaddr_str *rtp_cn_local;
+ struct rtp_stream *rtp_ran;
+ struct gsm_mncc_rtp;
+
+ if (!msc_a) {
+ LOG_TRANS(trans, LOGL_ERROR, "No connection for CC trans\n");
+ trans->callref = 0;
+ trans_free(trans);
+ return -EINVAL;
+ }
+
+ /* Set chosen codec in SDP. This is the result of the Assignment, the actual codec the BSS has chosen for this
+ * MT side. */
+ rtp_ran = msc_a->cc.call_leg->rtp[RTP_TO_RAN];
+ if (!rtp_ran->codecs_known) {
+ LOG_TRANS(trans, LOGL_ERROR, "RAN codecs not known but should be, cannot continue.\n");
+ trans_free(trans);
+ return -EINVAL;
+ }
+ trans->cc.sdp.assignment = rtp_ran->codecs.codec[0];
+
+ /* Insert the CN side RTP port now available into SDP */
+ rtp_cn_local = call_leg_local_ip(msc_a->cc.call_leg, RTP_TO_CN);
+ if (!rtp_cn_local) {
+ LOG_TRANS(trans, LOGL_ERROR, "Cannot compose SDP for MNCC_RTP_CREATE: no RTP set up for the CN side\n");
+ trans_free(trans);
+ return -EINVAL;
+ }
+ trans->cc.sdp.result.rtp = *rtp_cn_local;
+
+ cc_sdp_filter(&trans->cc.sdp);
+ LOG_TRANS(trans, LOGL_DEBUG, "codecs: %s\n", cc_sdp_name(&trans->cc.sdp));
+
+ return mncc_recv_rtp(msc_a_net(msc_a), trans, trans->callref, MNCC_RTP_CREATE, rtp_cn_local, 0, 0,
+ &trans->cc.sdp.result);
}
static int gsm48_cc_tx_call_proc_and_assign(struct gsm_trans *trans, void *arg)
@@ -790,6 +951,10 @@ static int gsm48_cc_rx_alerting(struct gsm_trans *trans, struct msgb *msg)
new_cc_state(trans, GSM_CSTATE_CALL_RECEIVED);
+ cc_sdp_filter(&trans->cc.sdp);
+ LOG_TRANS(trans, LOGL_DEBUG, "codecs: %s\n", cc_sdp_name(&trans->cc.sdp));
+ sdp_msg_to_str(alerting.sdp, sizeof(alerting.sdp), &trans->cc.sdp.result);
+
return mncc_recvmsg(trans->net, trans, MNCC_ALERT_IND,
&alerting);
}
@@ -814,6 +979,19 @@ static int gsm48_cc_tx_alerting(struct gsm_trans *trans, void *arg)
new_cc_state(trans, GSM_CSTATE_CALL_DELIVERED);
+ if (alerting->sdp[0]) {
+ struct call_leg *cl = trans->msc_a->cc.call_leg;
+ struct rtp_stream *rtp_cn = cl ? cl->rtp[RTP_TO_CN] : NULL;
+ sdp_msg_from_str(&trans->cc.sdp.remote, alerting->sdp);
+ LOG_TRANS(trans, LOGL_DEBUG, "%s codecs: %s\n",
+ get_mncc_name(alerting->msg_type),
+ cc_sdp_name(&trans->cc.sdp));
+ if (rtp_cn) {
+ rtp_stream_set_remote_addr_and_codecs(rtp_cn, &trans->cc.sdp.remote);
+ rtp_stream_commit(rtp_cn);
+ }
+ }
+
return trans_tx_gsm48(trans, msg);
}
@@ -860,6 +1038,20 @@ static int gsm48_cc_tx_connect(struct gsm_trans *trans, void *arg)
new_cc_state(trans, GSM_CSTATE_CONNECT_IND);
+ /* Received an MNCC_SETUP_RSP with the remote leg's SDP information. Apply codec choice. */
+ if (connect->sdp[0]) {
+ struct call_leg *cl = trans->msc_a->cc.call_leg;
+ struct rtp_stream *rtp_cn = cl ? cl->rtp[RTP_TO_CN] : NULL;
+ sdp_msg_from_str(&trans->cc.sdp.remote, connect->sdp);
+ LOG_TRANS(trans, LOGL_DEBUG, "%s codecs: %s\n",
+ get_mncc_name(connect->msg_type),
+ cc_sdp_name(&trans->cc.sdp));
+ if (rtp_cn) {
+ rtp_stream_set_remote_addr_and_codecs(rtp_cn, &trans->cc.sdp.remote);
+ rtp_stream_commit(rtp_cn);
+ }
+ }
+
return trans_tx_gsm48(trans, msg);
}
@@ -902,6 +1094,8 @@ static int gsm48_cc_rx_connect(struct gsm_trans *trans, struct msgb *msg)
new_cc_state(trans, GSM_CSTATE_CONNECT_REQUEST);
rate_ctr_inc(&trans->net->msc_ctrs->ctr[MSC_CTR_CALL_MT_CONNECT]);
+ cc_sdp_filter(&trans->cc.sdp);
+ sdp_msg_to_str(connect.sdp, sizeof(connect.sdp), &trans->cc.sdp.result);
return mncc_recvmsg(trans->net, trans, MNCC_SETUP_CNF, &connect);
}
@@ -1027,7 +1221,6 @@ static int gsm48_cc_rx_release(struct gsm_trans *trans, struct msgb *msg)
unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
struct tlv_parsed tp;
struct gsm_mncc rel;
- int rc;
gsm48_stop_cc_timer(trans);
@@ -1059,14 +1252,16 @@ static int gsm48_cc_rx_release(struct gsm_trans *trans, struct msgb *msg)
TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1);
}
- if (trans->cc.state == GSM_CSTATE_RELEASE_REQ) {
- /* release collision 5.4.5 */
- rc = mncc_recvmsg(trans->net, trans, MNCC_REL_CNF, &rel);
- } else {
- rc = gsm48_tx_simple(trans->msc_a,
- GSM48_PDISC_CC | (trans->transaction_id << 4),
- GSM48_MT_CC_RELEASE_COMPL);
- rc = mncc_recvmsg(trans->net, trans, MNCC_REL_IND, &rel);
+ if (!trans->cc.mncc_release_sent) {
+ if (trans->cc.state == GSM_CSTATE_RELEASE_REQ) {
+ /* release collision 5.4.5 */
+ mncc_recvmsg(trans->net, trans, MNCC_REL_CNF, &rel);
+ } else {
+ gsm48_tx_simple(trans->msc_a,
+ GSM48_PDISC_CC | (trans->transaction_id << 4),
+ GSM48_MT_CC_RELEASE_COMPL);
+ mncc_recvmsg(trans->net, trans, MNCC_REL_IND, &rel);
+ }
}
new_cc_state(trans, GSM_CSTATE_NULL);
@@ -1074,7 +1269,7 @@ static int gsm48_cc_rx_release(struct gsm_trans *trans, struct msgb *msg)
trans->callref = 0;
trans_free(trans);
- return rc;
+ return 0;
}
static int gsm48_cc_tx_release(struct gsm_trans *trans, void *arg)
@@ -1153,19 +1348,21 @@ static int gsm48_cc_rx_release_compl(struct gsm_trans *trans, struct msgb *msg)
TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1);
}
- if (trans->callref) {
- switch (trans->cc.state) {
- case GSM_CSTATE_CALL_PRESENT:
- rc = mncc_recvmsg(trans->net, trans,
- MNCC_REJ_IND, &rel);
- break;
- case GSM_CSTATE_RELEASE_REQ:
- rc = mncc_recvmsg(trans->net, trans,
- MNCC_REL_CNF, &rel);
- break;
- default:
- rc = mncc_recvmsg(trans->net, trans,
- MNCC_REL_IND, &rel);
+ if (!trans->cc.mncc_release_sent) {
+ if (trans->callref) {
+ switch (trans->cc.state) {
+ case GSM_CSTATE_CALL_PRESENT:
+ rc = mncc_recvmsg(trans->net, trans,
+ MNCC_REJ_IND, &rel);
+ break;
+ case GSM_CSTATE_RELEASE_REQ:
+ rc = mncc_recvmsg(trans->net, trans,
+ MNCC_REL_CNF, &rel);
+ break;
+ default:
+ rc = mncc_recvmsg(trans->net, trans,
+ MNCC_REL_IND, &rel);
+ }
}
}
@@ -1612,7 +1809,7 @@ static int gsm48_cc_rx_userinfo(struct gsm_trans *trans, struct msgb *msg)
static int mncc_recv_rtp(struct gsm_network *net, struct gsm_trans *trans, uint32_t callref,
int cmd, struct osmo_sockaddr_str *rtp_addr, uint32_t payload_type,
- uint32_t payload_msg_type)
+ uint32_t payload_msg_type, const struct sdp_msg *sdp)
{
uint8_t data[sizeof(struct gsm_mncc)];
struct gsm_mncc_rtp *rtp;
@@ -1628,12 +1825,18 @@ static int mncc_recv_rtp(struct gsm_network *net, struct gsm_trans *trans, uint3
}
rtp->payload_type = payload_type;
rtp->payload_msg_type = payload_msg_type;
+ if (sdp) {
+ LOG_TRANS(trans, LOGL_DEBUG, "%s SDP: %s\n",
+ get_mncc_name(rtp->msg_type),
+ sdp_msg_name(sdp));
+ sdp_msg_to_str(rtp->sdp, sizeof(rtp->sdp), sdp);
+ }
return mncc_recvmsg(net, trans, cmd, (struct gsm_mncc *)data);
}
static void mncc_recv_rtp_err(struct gsm_network *net, struct gsm_trans *trans, uint32_t callref, int cmd)
{
- mncc_recv_rtp(net, trans, callref, cmd, NULL, 0, 0);
+ mncc_recv_rtp(net, trans, callref, cmd, NULL, 0, 0, NULL);
}
static int tch_rtp_create(struct gsm_network *net, uint32_t callref)
@@ -1659,6 +1862,57 @@ static int tch_rtp_create(struct gsm_network *net, uint32_t callref)
return msc_a_try_call_assignment(trans);
}
+int cc_cn_local_rtp_port_known(struct gsm_trans *cc_trans)
+{
+ switch(cc_trans->cc.state) {
+ case GSM_CSTATE_INITIATED:
+ if (cc_trans->cc.msg.msg_type != MNCC_SETUP_IND) {
+ LOG_TRANS(cc_trans, LOGL_ERROR, "Assuming MO call, expected MNCC_SETUP_IND to be prepared\n");
+ return -EINVAL;
+ }
+ /* This is the MO call leg, waiting for a CN RTP be able to send initial MNCC_SETUP_IND. */
+ gsm48_cc_rx_setup_cn_local_rtp_port_known(cc_trans);
+ return 0;
+
+ case GSM_CSTATE_MO_TERM_CALL_CONF:
+ /* This is the MT call leg, waiting for a CN RTP to be able to send MNCC_CALL_CONF_IND. */
+ return gsm48_cc_mt_rtp_port_and_codec_known(cc_trans);
+
+ default:
+ LOG_TRANS(cc_trans, LOGL_ERROR, "CN RTP address available, but in unexpected state %d\n",
+ cc_trans->cc.state);
+ return -EINVAL;
+ }
+}
+
+int cc_assignment_done(struct gsm_trans *trans)
+{
+ struct msc_a *msc_a = trans->msc_a;
+
+ switch (trans->cc.state) {
+ case GSM_CSTATE_INITIATED:
+ case GSM_CSTATE_MO_CALL_PROC:
+ /* MO call */
+ break;
+
+ case GSM_CSTATE_CALL_RECEIVED:
+ case GSM_CSTATE_MO_TERM_CALL_CONF:
+ /* MT call */
+ break;
+
+ default:
+ LOG_TRANS(trans, LOGL_ERROR, "Assignment done in unexpected CC state: %d\n", trans->cc.state);
+ return -EINVAL;
+ }
+
+ if (!call_leg_local_ip(msc_a->cc.call_leg, RTP_TO_CN)) {
+ LOG_TRANS(trans, LOGL_DEBUG,
+ "Assignment complete, but still waiting for the CRCX OK on the CN side RTP\n");
+ return 0;
+ }
+ return gsm48_tch_rtp_create(trans);
+}
+
/* Trigger TCH_RTP_CREATE acknowledgement */
int gsm48_tch_rtp_create(struct gsm_trans *trans)
{
@@ -1670,30 +1924,32 @@ int gsm48_tch_rtp_create(struct gsm_trans *trans)
struct call_leg *cl = msc_a->cc.call_leg;
struct osmo_sockaddr_str *rtp_cn_local;
struct rtp_stream *rtp_cn = cl ? cl->rtp[RTP_TO_CN] : NULL;
- uint32_t payload_type;
- int payload_msg_type;
- const struct mgcp_conn_peer *mgcp_info;
+ int mncc_payload_msg_type;
+ struct sdp_audio_codec *codec;
+ const struct codec_mapping *m;
if (!rtp_cn) {
LOG_TRANS_CAT(trans, DMNCC, LOGL_ERROR, "Cannot RTP CREATE to MNCC, no RTP set up for the CN side\n");
return -EINVAL;
}
- if (!rtp_cn->codec_known) {
+ cc_sdp_filter(&trans->cc.sdp);
+ LOG_TRANS(trans, LOGL_DEBUG, "codecs: %s\n", cc_sdp_name(&trans->cc.sdp));
+
+ if (!trans->cc.sdp.result.audio_codecs.count) {
LOG_TRANS_CAT(trans, DMNCC, LOGL_ERROR,
- "Cannot RTP CREATE to MNCC, no codec set up for the RTP CN side\n");
+ "Cannot RTP CREATE to MNCC, there is no codec available\n");
return -EINVAL;
}
- /* Codec */
- payload_msg_type = mgcp_codec_to_mncc_payload_msg_type(rtp_cn->codec);
+ /* Modify the MGW endpoint if necessary, usually this should already match and not cause MGCP. */
+ rtp_stream_set_codecs(rtp_cn, &trans->cc.sdp.result.audio_codecs);
+ rtp_stream_commit(rtp_cn);
- /* Payload Type number */
- mgcp_info = osmo_mgcpc_ep_ci_get_rtp_info(rtp_cn->ci);
- if (mgcp_info && mgcp_info->ptmap_len)
- payload_type = map_codec_to_pt(mgcp_info->ptmap, mgcp_info->ptmap_len, rtp_cn->codec);
- else
- payload_type = rtp_cn->codec;
+ /* Populate the legacy MNCC codec elements: payload_type and payload_msg_type */
+ codec = &rtp_cn->codecs.codec[0];
+ m = codec_mapping_by_subtype_name(codec->subtype_name);
+ mncc_payload_msg_type = m ? m->mncc_payload_msg_type : 0;
rtp_cn_local = call_leg_local_ip(cl, RTP_TO_CN);
if (!rtp_cn_local) {
@@ -1701,7 +1957,9 @@ int gsm48_tch_rtp_create(struct gsm_trans *trans)
return -EINVAL;
}
- return mncc_recv_rtp(net, trans, trans->callref, MNCC_RTP_CREATE, rtp_cn_local, payload_type, payload_msg_type);
+ return mncc_recv_rtp(net, trans, trans->callref, MNCC_RTP_CREATE, rtp_cn_local,
+ codec->payload_type, mncc_payload_msg_type,
+ &trans->cc.sdp.result);
}
static int tch_rtp_connect(struct gsm_network *net, const struct gsm_mncc_rtp *rtp)
@@ -1709,7 +1967,6 @@ static int tch_rtp_connect(struct gsm_network *net, const struct gsm_mncc_rtp *r
struct gsm_trans *trans;
struct call_leg *cl;
struct rtp_stream *rtps;
- struct osmo_sockaddr_str rtp_addr;
/* FIXME: in *rtp we should get the codec information of the remote
* leg. We will have to populate trans->conn->rtp.codec_cn with a
@@ -1735,7 +1992,7 @@ static int tch_rtp_connect(struct gsm_network *net, const struct gsm_mncc_rtp *r
return -EIO;
}
- LOG_TRANS_CAT(trans, DMNCC, LOGL_DEBUG, "rx %s\n", get_mncc_name(MNCC_RTP_CONNECT));
+ LOG_TRANS_CAT(trans, DMNCC, LOGL_DEBUG, "rx %s\n", get_mncc_name(rtp->msg_type));
cl = trans->msc_a->cc.call_leg;
rtps = cl ? cl->rtp[RTP_TO_CN] : NULL;
@@ -1746,8 +2003,21 @@ static int tch_rtp_connect(struct gsm_network *net, const struct gsm_mncc_rtp *r
return -EINVAL;
}
- osmo_sockaddr_str_from_32n(&rtp_addr, rtp->ip, rtp->port);
- rtp_stream_set_remote_addr(rtps, &rtp_addr);
+ if (rtp->sdp[0]) {
+ sdp_msg_from_str(&trans->cc.sdp.remote, rtp->sdp);
+ LOG_TRANS(trans, LOGL_DEBUG, "%s contained SDP %s\n",
+ get_mncc_name(rtp->msg_type),
+ sdp_msg_name(&trans->cc.sdp.remote));
+ }
+ rtp_stream_set_remote_addr_and_codecs(rtps, &trans->cc.sdp.remote);
+
+ if (!osmo_sockaddr_str_is_nonzero(&rtps->remote)) {
+ /* Didn't get an IP address from SDP. Try legacy MNCC IP address */
+ struct osmo_sockaddr_str rtp_addr;
+ osmo_sockaddr_str_from_32n(&rtp_addr, rtp->ip, rtp->port);
+ rtp_stream_set_remote_addr(rtps, &rtp_addr);
+ }
+
rtp_stream_commit(rtps);
return 0;
}
@@ -1929,6 +2199,19 @@ static int mncc_tx_to_gsm_cc(struct gsm_network *net, const union mncc_msg *msg)
return -ENOMEM;
}
+ /* Remember remote SDP, if any */
+ if (data->sdp[0]) {
+ if (sdp_msg_from_str(&trans->cc.sdp.remote, data->sdp)) {
+ LOG_TRANS(trans, LOGL_ERROR, "Failed to parse incoming SDP: %s\n",
+ osmo_quote_str(data->sdp, -1));
+ vlr_subscr_put(vsub, __func__);
+ mncc_release_ind(net, NULL, data->callref,
+ GSM48_CAUSE_LOC_PRN_S_LU,
+ GSM48_CC_CAUSE_NORMAL_UNSPEC);
+ return -EINVAL;
+ }
+ }
+
/* If subscriber has no conn */
if (!msc_a) {
/* This condition will return before the common logging of the received MNCC message below, so
@@ -1976,6 +2259,7 @@ static int mncc_tx_to_gsm_cc(struct gsm_network *net, const union mncc_msg *msg)
LOG_TRANS(trans, LOGL_DEBUG, "rx %s in paging state\n", get_mncc_name(msg->msg_type));
mncc_set_cause(&rel, GSM48_CAUSE_LOC_PRN_S_LU,
GSM48_CC_CAUSE_NORM_CALL_CLEAR);
+ trans->cc.mncc_release_sent = true;
if (msg->msg_type == MNCC_REL_REQ)
rc = mncc_recvmsg(net, trans, MNCC_REL_CNF, &rel);
else