diff options
author | Neels Hofmeyr <nhofmeyr@sysmocom.de> | 2022-01-13 23:18:02 +0100 |
---|---|---|
committer | Neels Hofmeyr <nhofmeyr@sysmocom.de> | 2023-03-18 03:05:34 +0100 |
commit | bd5f8e900b030f6857ae6e7e3c4421dcd2d2be9d (patch) | |
tree | 5fb38addd1f28fdb1a7464660d773b63e04b0003 /src | |
parent | 11a746acd3de12e8b067283f977c6f7eaeb3ada1 (diff) |
do CN CRCX first
In order to send the MSC's RTP endpoint IP address+port in the initial
SDP, move the MGCP CRCX up to an earlier point in the sequence of
establishing a voice call.
Update the voice call sequence chart to show the effects.
Though the semantic change is rather simple, the patch is rather huge --
things have to happen in a different order, and async waits have to
happen at different times.
The new codec filter helps to carry codec resolution information across
the newly arranged code paths.
Related: SYS#5066
Change-Id: Ie433db1ba0c46d4b97538a969233c155cefac21c
Diffstat (limited to 'src')
-rw-r--r-- | src/libmsc/gsm_04_08_cc.c | 167 | ||||
-rw-r--r-- | src/libmsc/msc_a.c | 152 |
2 files changed, 249 insertions, 70 deletions
diff --git a/src/libmsc/gsm_04_08_cc.c b/src/libmsc/gsm_04_08_cc.c index dd2c77654..3154c2ab8 100644 --- a/src/libmsc/gsm_04_08_cc.c +++ b/src/libmsc/gsm_04_08_cc.c @@ -571,8 +571,6 @@ static int gsm48_cc_rx_setup(struct gsm_trans *trans, struct msgb *msg) unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); struct tlv_parsed tp; struct gsm_mncc setup; - struct sdp_msg *sdp; - int rc; gsm48_start_guard_timer(trans); @@ -597,6 +595,8 @@ static int gsm48_cc_rx_setup(struct gsm_trans *trans, struct msgb *msg) msgb_free(gcr_msg); } + 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) { @@ -666,8 +666,6 @@ 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); - /* MO call leg starting, gather all codec information so far known: */ codec_filter_init(&trans->cc.codecs); codec_filter_set_ran(&trans->cc.codecs, trans->msc_a->c.ran->type); @@ -682,25 +680,66 @@ static int gsm48_cc_rx_setup(struct gsm_trans *trans, struct msgb *msg) rate_ctr_inc(rate_ctr_group_get_ctr(trans->net->msc_ctrs, MSC_CTR_CALL_MO_SETUP)); - /* FUTURE: so far this is composing SDP although the RTP address is not established yet (sending 0.0.0.0:0). - * Subsequent patch 'do CN CRCX first' (Ie433db1ba0c46d4b97538a969233c155cefac21c) changes the ordering so that - * the CN CRCX is completed before dispatching CC SETUP to remote, so that a valid RTP address is set. */ + 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; + int rc; + + if (trans->cc.state != GSM_CSTATE_INITIATED + || setup.msg_type != MNCC_SETUP_IND) { + LOG_TRANS(trans, LOGL_ERROR, + "Unexpected CC state. Expected GSM_CSTATE_INITIATED 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; + } + codec_filter_set_local_rtp(&trans->cc.codecs, rtp_cn_local); sdp = trans->cc.codecs.result.audio_codecs.count ? &trans->cc.codecs.result : NULL; rc = sdp_msg_to_sdp_str_buf(setup.sdp, sizeof(setup.sdp), sdp); if (rc >= sizeof(setup.sdp)) { LOG_TRANS(trans, LOGL_ERROR, "MNCC_SETUP_IND: SDP too long (%d > %zu bytes)\n", rc, sizeof(setup.sdp)); trans_free(trans); - return -EINVAL; + return; } /* 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 void rx_mncc_sdp(struct gsm_trans *trans, uint32_t mncc_msg_type, const char *sdp) @@ -903,8 +942,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); @@ -913,8 +950,48 @@ 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); + +static 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 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; + } + + /* 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; + } + codec_filter_set_local_rtp(&trans->cc.codecs, rtp_cn_local); + + codec_filter_run(&trans->cc.codecs); + LOG_TRANS(trans, LOGL_DEBUG, "codecs: %s\n", codec_filter_to_str(&trans->cc.codecs)); + + /* If we haven't completed Assignment yet, don't sent MNCC_RTP_CREATE */ + if (!sdp_audio_codec_is_set(&trans->cc.codecs.assignment)) { + LOG_TRANS(trans, LOGL_DEBUG, "no codec confirmed by Assignment yet\n"); + return 0; + } + + return mncc_recv_rtp(msc_a_net(msc_a), trans, trans->callref, MNCC_RTP_CREATE, rtp_cn_local, 0, 0, + &trans->cc.codecs.result); } static int gsm48_cc_tx_call_proc_and_assign(struct gsm_trans *trans, void *arg) @@ -1895,6 +1972,62 @@ static int tch_rtp_create(struct gsm_network *net, const struct gsm_mncc_rtp *rt return msc_a_try_call_assignment(trans); } +int cc_on_cn_local_rtp_port_known(struct gsm_trans *cc_trans) +{ + /* Depending on MO or MT call, dispatch the event differently */ + 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_on_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; + + case GSM_CSTATE_ACTIVE: + /* already active. MNCC finished before Abis completed the Assignment. */ + 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) { diff --git a/src/libmsc/msc_a.c b/src/libmsc/msc_a.c index a7424e03c..c022ee2d4 100644 --- a/src/libmsc/msc_a.c +++ b/src/libmsc/msc_a.c @@ -564,6 +564,66 @@ static void msc_a_fsm_authenticated(struct osmo_fsm_inst *fi, uint32_t event, vo } } +static struct call_leg *msc_a_ensure_call_leg(struct msc_a *msc_a, struct gsm_trans *for_cc_trans) +{ + struct call_leg *cl = msc_a->cc.call_leg; + struct gsm_network *net = msc_a_net(msc_a); + + /* Ensure that events about RTP endpoints coming from the msc_a->cc.call_leg know which gsm_trans to abort on + * error */ + if (!msc_a->cc.active_trans) + msc_a->cc.active_trans = for_cc_trans; + if (msc_a->cc.active_trans != for_cc_trans) { + LOG_TRANS(for_cc_trans, LOGL_ERROR, + "Cannot create call leg, another trans is already active for this conn\n"); + return NULL; + } + + if (!cl) { + cl = msc_a->cc.call_leg = call_leg_alloc(msc_a->c.fi, + MSC_EV_CALL_LEG_TERM, + MSC_EV_CALL_LEG_RTP_LOCAL_ADDR_AVAILABLE, + MSC_EV_CALL_LEG_RTP_COMPLETE); + OSMO_ASSERT(cl); + + if (net->use_osmux != OSMUX_USAGE_OFF) { + struct msc_i *msc_i = msc_a_msc_i(msc_a); + if (msc_i->c.remote_to) { + /* TODO: investigate what to do in this case */ + LOG_MSC_A(msc_a, LOGL_ERROR, "Osmux not yet supported for inter-MSC"); + } else { + cl->ran_peer_supports_osmux = msc_i->ran_conn->ran_peer->remote_supports_osmux; + } + } + + } + return cl; +} + +int msc_a_ensure_cn_local_rtp(struct msc_a *msc_a, struct gsm_trans *cc_trans) +{ + struct call_leg *cl; + struct rtp_stream *rtp_to_ran; + + cl = msc_a_ensure_call_leg(msc_a, cc_trans); + if (!cl) + return -EINVAL; + rtp_to_ran = cl->rtp[RTP_TO_RAN]; + + if (call_leg_local_ip(cl, RTP_TO_CN)) { + /* Already has an RTP address and port towards the CN, continue right away. */ + return osmo_fsm_inst_dispatch(msc_a->c.fi, MSC_EV_CALL_LEG_RTP_LOCAL_ADDR_AVAILABLE, cl->rtp[RTP_TO_CN]); + } + + /* No CN RTP address available yet, ask the MGW to create one. + * Set a codec to be used: if Assignment on the RAN side is already done, take the same codec as the RTP_TO_RAN. + * If no RAN side RTP is established, try to guess a preliminary codec from SDP -- before Assignment, picking a + * codec from the SDP is more politeness/avoiding confusion than necessity. The actual codec to be used would be + * determined later. If no codec could be determined, pass none for the time being. */ + return call_leg_ensure_ci(cl, RTP_TO_CN, cc_trans->callref, cc_trans, + rtp_to_ran->codecs_known ? &rtp_to_ran->codecs : NULL, NULL); +} + /* The MGW has given us a local IP address for the RAN side. Ready to start the Assignment of a voice channel. */ static void msc_a_call_leg_ran_local_addr_available(struct msc_a *msc_a) { @@ -617,15 +677,6 @@ static void msc_a_call_leg_ran_local_addr_available(struct msc_a *msc_a) } } -static void msc_a_call_leg_cn_local_addr_available(struct msc_a *msc_a, struct gsm_trans *cc_trans) -{ - if (gsm48_tch_rtp_create(cc_trans)) { - LOG_MSC_A(msc_a, LOGL_ERROR, "Cannot inform MNCC of RTP address\n"); - trans_free(cc_trans); - return; - } -} - static struct gsm_trans *find_waiting_call(struct msc_a *msc_a) { struct gsm_trans *trans; @@ -719,10 +770,7 @@ static void msc_a_fsm_communicating(struct osmo_fsm_inst *fi, uint32_t event, vo msc_a_call_leg_ran_local_addr_available(msc_a); return; case RTP_TO_CN: - /* The rtp_stream has gotten the new RTP address and port from the MGW. Also update the codecs - * filter result to convey this RTP address and port towards the remote call leg. */ - codec_filter_set_local_rtp(&msc_a->cc.active_trans->cc.codecs, &rtps->local); - msc_a_call_leg_cn_local_addr_available(msc_a, rtps->for_trans); + cc_on_cn_local_rtp_port_known(rtps->for_trans); return; default: LOG_MSC_A(msc_a, LOGL_ERROR, "Invalid data for %s\n", osmo_fsm_event_name(fi->fsm, event)); @@ -1341,7 +1389,7 @@ static void msc_a_up_call_assignment_complete(struct msc_a *msc_a, const struct struct rtp_stream *rtps_to_ran = msc_a->cc.call_leg ? msc_a->cc.call_leg->rtp[RTP_TO_RAN] : NULL; const struct gsm0808_speech_codec *codec_if_known = ac->assignment_complete.codec_present ? &ac->assignment_complete.codec : NULL; - const struct codec_mapping *codec_cn = NULL; + const struct codec_mapping *codec_cn; if (!rtps_to_ran) { LOG_MSC_A(msc_a, LOGL_ERROR, "Rx Assignment Complete, but no RTP stream is set up\n"); @@ -1400,23 +1448,19 @@ static void msc_a_up_call_assignment_complete(struct msc_a *msc_a, const struct if (rtps_to_ran->use_osmux) rtp_stream_set_remote_osmux_cid(rtps_to_ran, ac->assignment_complete.osmux_cid); - rtp_stream_commit(rtps_to_ran); /* Remember the Codec List (BSS Supported) */ if (ac->assignment_complete.codec_list_bss_supported) codec_filter_set_bss(&cc_trans->cc.codecs, ac->assignment_complete.codec_list_bss_supported); - /* Setup CN side endpoint CI: - * Now that - * - the first CI has been created and a definitive endpoint name is assigned to the call_leg's MGW - * endpoint, - * - the Assignment has chosen a speech codec - * go on to create the CN side RTP stream's CI. */ codec_filter_run(&cc_trans->cc.codecs); - if (call_leg_ensure_ci(msc_a->cc.call_leg, RTP_TO_CN, cc_trans->callref, cc_trans, - &cc_trans->cc.codecs.result.audio_codecs, NULL)) { - LOG_MSC_A_CAT(msc_a, DCC, LOGL_ERROR, "Error creating MGW CI towards CN\n"); + LOG_TRANS(cc_trans, LOGL_INFO, "Assignment Complete: RAN: %s, CN: %s\n", + sdp_audio_codecs_to_str(&rtps_to_ran->codecs), + sdp_audio_codecs_to_str(&cc_trans->cc.codecs.result.audio_codecs)); + + if (cc_on_assignment_done(cc_trans)) { + /* If an error occurred, it was logged in cc_assignment_done() */ call_leg_release(msc_a->cc.call_leg); return; } @@ -1807,10 +1851,9 @@ int msc_tx_common_id(struct msc_a *msc_a, enum msc_role to_role) static int msc_a_start_assignment(struct msc_a *msc_a, struct gsm_trans *cc_trans) { - struct call_leg *cl = msc_a->cc.call_leg; - struct msc_i *msc_i = msc_a_msc_i(msc_a); - struct gsm_network *net = msc_a_net(msc_a); - struct sdp_audio_codecs *codecs; + struct call_leg *cl; + bool cn_rtp_available; + bool ran_rtp_available; OSMO_ASSERT(!msc_a->cc.active_trans); msc_a->cc.active_trans = cc_trans; @@ -1818,36 +1861,39 @@ static int msc_a_start_assignment(struct msc_a *msc_a, struct gsm_trans *cc_tran cc_trans->cc.codecs.assignment = (struct sdp_audio_codec){}; OSMO_ASSERT(cc_trans && cc_trans->type == TRANS_CC); + cl = msc_a_ensure_call_leg(msc_a, cc_trans); + if (!cl) + return -EINVAL; - if (!cl) { - cl = msc_a->cc.call_leg = call_leg_alloc(msc_a->c.fi, - MSC_EV_CALL_LEG_TERM, - MSC_EV_CALL_LEG_RTP_LOCAL_ADDR_AVAILABLE, - MSC_EV_CALL_LEG_RTP_COMPLETE); - OSMO_ASSERT(cl); - } + /* See if we can set a preliminary codec. If not, pass none for the time being. */ + codec_filter_run(&cc_trans->cc.codecs); - if (net->use_osmux != OSMUX_USAGE_OFF) { - msc_i = msc_a_msc_i(msc_a); - if (msc_i->c.remote_to) { - /* TODO: investigate what to do in this case */ - LOG_MSC_A(msc_a, LOGL_ERROR, "Osmux not yet supported for inter-MSC"); - } else { - cl->ran_peer_supports_osmux = msc_i->ran_conn->ran_peer->remote_supports_osmux; - } + cn_rtp_available = call_leg_local_ip(cl, RTP_TO_CN); + ran_rtp_available = call_leg_local_ip(cl, RTP_TO_RAN); + + /* Set up RTP ports for both RAN and CN side. Even though we ask for both at the same time, the + * osmo_mgcpc_ep_fsm automagically waits for the first CRCX to complete before firing the second CRCX. The one + * issued first here will also be the first CRCX sent to the MGW. Usually both still need to be set up. */ + if (!cn_rtp_available) + call_leg_ensure_ci(cl, RTP_TO_CN, cc_trans->callref, cc_trans, + &cc_trans->cc.codecs.result.audio_codecs, NULL); + if (!ran_rtp_available) { + struct sdp_audio_codecs *codecs; + if (msc_a->c.ran->force_mgw_codecs_to_ran.count) + codecs = &msc_a->c.ran->force_mgw_codecs_to_ran; + else + codecs = &cc_trans->cc.codecs.result.audio_codecs; + return call_leg_ensure_ci(cl, RTP_TO_RAN, cc_trans->callref, cc_trans, codecs, NULL); } - /* Make sure an MGW endpoint towards RAN is present. If it is already set up, "skip" to - * MSC_EV_CALL_LEG_LOCAL_ADDR_AVAILABLE immediately. If not, set it up. */ - if (call_leg_local_ip(cl, RTP_TO_RAN)) + /* Should these already be set up, immediately continue by retriggering the events signalling that the RTP + * ports are available. The ordering is: first CN, then RAN. */ + if (cn_rtp_available && ran_rtp_available) return osmo_fsm_inst_dispatch(msc_a->c.fi, MSC_EV_CALL_LEG_RTP_LOCAL_ADDR_AVAILABLE, cl->rtp[RTP_TO_RAN]); - - codec_filter_run(&cc_trans->cc.codecs); - if (msc_a->c.ran->force_mgw_codecs_to_ran.count) - codecs = &msc_a->c.ran->force_mgw_codecs_to_ran; - else - codecs = &cc_trans->cc.codecs.result.audio_codecs; - return call_leg_ensure_ci(msc_a->cc.call_leg, RTP_TO_RAN, cc_trans->callref, cc_trans, codecs, NULL); + else if (cn_rtp_available) + return osmo_fsm_inst_dispatch(msc_a->c.fi, MSC_EV_CALL_LEG_RTP_LOCAL_ADDR_AVAILABLE, cl->rtp[RTP_TO_CN]); + /* Otherwise wait for MGCP response and continue from there. */ + return 0; } int msc_a_try_call_assignment(struct gsm_trans *cc_trans) |