/* GSM Mobile Radio Interface Layer 3 Call Control */ /* (C) 2008-2016 by Harald Welte * (C) 2008-2012 by Holger Hans Peter Freyther * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation; either version 3 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int gsm48_cc_tx_setup(struct gsm_trans *trans, void *arg); static int gsm48_cc_tx_release(struct gsm_trans *trans, void *arg); static int gsm48_cc_tx_disconnect(struct gsm_trans *trans, void *arg); static int trans_tx_gsm48(struct gsm_trans *trans, struct msgb *msg) { struct gsm48_hdr *gh = (struct gsm48_hdr *) msg->data; gh->proto_discr = GSM48_PDISC_CC | (trans->transaction_id << 4); OMSC_LINKID_CB(msg) = trans->dlci; return msc_a_tx_dtap_to_i(trans->msc_a, msg); } uint32_t msc_cc_next_outgoing_callref() { static uint32_t last_callref = 0x80000000; last_callref++; if (last_callref < 0x80000001) last_callref = 0x80000001; return last_callref; } static void gsm48_cc_guard_timeout(void *arg) { struct gsm_trans *trans = arg; LOG_TRANS(trans, LOGL_DEBUG, "guard timeout expired\n"); trans_free(trans); return; } static void gsm48_stop_guard_timer(struct gsm_trans *trans) { if (osmo_timer_pending(&trans->cc.timer_guard)) { LOG_TRANS(trans, LOGL_DEBUG, "stopping pending guard timer\n"); osmo_timer_del(&trans->cc.timer_guard); } } static void gsm48_start_guard_timer(struct gsm_trans *trans) { /* NOTE: The purpose of this timer is to prevent the cc state machine * from hanging in cases where mncc, gsm48 or both become unresponsive * for some reason. The timer is started initially with the setup from * the gsm48 side and then re-started with every incoming mncc message. * Once the mncc state reaches its active state the timer is stopped. * So if the cc state machine does not show any activity for an * extended amount of time during call setup or teardown the guard * timer will time out and hard-clear the connection. */ if (osmo_timer_pending(&trans->cc.timer_guard)) gsm48_stop_guard_timer(trans); LOG_TRANS(trans, LOGL_DEBUG, "starting guard timer with %d seconds\n", trans->net->mncc_guard_timeout); osmo_timer_setup(&trans->cc.timer_guard, gsm48_cc_guard_timeout, trans); osmo_timer_schedule(&trans->cc.timer_guard, trans->net->mncc_guard_timeout, 0); } /* Call Control */ void cc_tx_to_mncc(struct gsm_network *net, struct msgb *msg) { net->mncc_recv(net, msg); } int gsm48_cc_tx_notify_ss(struct gsm_trans *trans, const char *message) { struct gsm48_hdr *gh; struct msgb *ss_notify; ss_notify = gsm0480_create_notifySS(message); if (!ss_notify) return -1; gsm0480_wrap_invoke(ss_notify, GSM0480_OP_CODE_NOTIFY_SS, 0); uint8_t *data = msgb_push(ss_notify, 1); data[0] = ss_notify->len - 1; gh = (struct gsm48_hdr *) msgb_push(ss_notify, sizeof(*gh)); gh->msg_type = GSM48_MT_CC_FACILITY; return trans_tx_gsm48(trans, ss_notify); } /* FIXME: this count_statistics is a state machine behaviour. we should convert * the complete call control into a state machine. Afterwards we can move this * code into state transitions. */ static void count_statistics(struct gsm_trans *trans, int new_state) { int old_state = trans->cc.state; struct rate_ctr_group *msc = trans->net->msc_ctrs; if (old_state == new_state) return; /* state incoming */ switch (new_state) { case GSM_CSTATE_ACTIVE: osmo_stat_item_inc(trans->net->statg->items[MSC_STAT_ACTIVE_CALLS], 1); rate_ctr_inc(&msc->ctr[MSC_CTR_CALL_ACTIVE]); break; } /* state outgoing */ switch (old_state) { case GSM_CSTATE_ACTIVE: osmo_stat_item_dec(trans->net->statg->items[MSC_STAT_ACTIVE_CALLS], 1); if (new_state == GSM_CSTATE_DISCONNECT_REQ || new_state == GSM_CSTATE_DISCONNECT_IND) rate_ctr_inc(&msc->ctr[MSC_CTR_CALL_COMPLETE]); else rate_ctr_inc(&msc->ctr[MSC_CTR_CALL_INCOMPLETE]); break; } } static void new_cc_state(struct gsm_trans *trans, int state) { if (state > 31 || state < 0) return; LOG_TRANS(trans, LOGL_DEBUG, "new state %s -> %s\n", gsm48_cc_state_name(trans->cc.state), gsm48_cc_state_name(state)); count_statistics(trans, state); trans->cc.state = state; /* Stop the guard timer when a call reaches the active state */ if (state == GSM_CSTATE_ACTIVE) gsm48_stop_guard_timer(trans); } static int gsm48_cc_tx_status(struct gsm_trans *trans, void *arg) { struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC STATUS"); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); uint8_t *cause, *call_state; gh->msg_type = GSM48_MT_CC_STATUS; cause = msgb_put(msg, 3); cause[0] = 2; cause[1] = GSM48_CAUSE_CS_GSM | GSM48_CAUSE_LOC_USER; cause[2] = 0x80 | 30; /* response to status inquiry */ call_state = msgb_put(msg, 1); call_state[0] = 0xc0 | 0x00; return trans_tx_gsm48(trans, msg); } static void gsm48_stop_cc_timer(struct gsm_trans *trans) { if (osmo_timer_pending(&trans->cc.timer)) { LOG_TRANS(trans, LOGL_DEBUG, "stopping pending timer T%x\n", trans->cc.Tcurrent); osmo_timer_del(&trans->cc.timer); trans->cc.Tcurrent = 0; } } static int mncc_recvmsg(struct gsm_network *net, struct gsm_trans *trans, int msg_type, struct gsm_mncc *mncc) { struct msgb *msg; unsigned char *data; LOG_TRANS_CAT(trans, DMNCC, LOGL_DEBUG, "tx %s\n", get_mncc_name(msg_type)); mncc->msg_type = msg_type; msg = msgb_alloc(sizeof(struct gsm_mncc), "MNCC"); if (!msg) return -ENOMEM; data = msgb_put(msg, sizeof(struct gsm_mncc)); memcpy(data, mncc, sizeof(struct gsm_mncc)); cc_tx_to_mncc(net, msg); /* trans may be NULL when sending an MNCC error reply upon an invalid MNCC request */ if (trans) trans->cc.mncc_initiated = true; return 0; } 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); if (trans && trans->cc.state == GSM_CSTATE_RELEASE_REQ) return mncc_recvmsg(net, trans, MNCC_REL_CNF, &rel); return mncc_recvmsg(net, trans, MNCC_REL_IND, &rel); } /* Call Control Specific transaction release. * gets called by trans_free, DO NOT CALL YOURSELF! */ void _gsm48_cc_trans_free(struct gsm_trans *trans) { gsm48_stop_cc_timer(trans); /* send release to L4, if callref still exists */ if (trans->callref) { /* FIXME: currently, a CC trans that would not yet be in state GSM_CSTATE_RELEASE_REQ fails to send a * CC Release to the MS if it gets freed here. Hack it to do so. */ if (trans->cc.state != GSM_CSTATE_RELEASE_REQ) { struct gsm_mncc rel = {}; rel.callref = trans->callref; mncc_set_cause(&rel, GSM48_CAUSE_LOC_PRN_S_LU, GSM48_CC_CAUSE_RESOURCE_UNAVAIL); gsm48_cc_tx_release(trans, &rel); } /* Resource unavailable */ if (trans->cc.mncc_initiated) mncc_release_ind(trans->net, trans, trans->callref, GSM48_CAUSE_LOC_PRN_S_LU, GSM48_CC_CAUSE_RESOURCE_UNAVAIL); /* This is a final freeing of the transaction. The MNCC release may have triggered the * T308 release timer, but we don't have the luxury of graceful CC Release here. */ gsm48_stop_cc_timer(trans); } if (trans->cc.state != GSM_CSTATE_NULL) new_cc_state(trans, GSM_CSTATE_NULL); gsm48_stop_guard_timer(trans); if (trans->msc_a && trans->msc_a->cc.active_trans == trans) trans->msc_a->cc.active_trans = NULL; } /* call-back from paging the B-end of the connection */ static void cc_paging_cb(struct msc_a *msc_a, struct gsm_trans *trans) { if (trans->msc_a) { LOG_MSC_A_CAT(msc_a, DPAG, LOGL_ERROR, "Handle paging error: transaction already associated with subscriber," " apparently it was already handled. Skip.\n"); return; } if (msc_a) { LOG_TRANS(trans, LOGL_DEBUG, "Paging succeeded\n"); /* Assign conn */ msc_a_get(msc_a, MSC_A_USE_CC); trans->msc_a = msc_a; trans->paging_request = NULL; 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); } else { LOG_TRANS(trans, LOGL_DEBUG, "Paging expired\n"); /* Temporarily out of order */ mncc_release_ind(trans->net, trans, trans->callref, GSM48_CAUSE_LOC_PRN_S_LU, GSM48_CC_CAUSE_DEST_OOO); trans->callref = 0; trans->paging_request = NULL; trans_free(trans); } } /* bridge channels of two transactions */ static int tch_bridge(struct gsm_network *net, const struct gsm_mncc_bridge *bridge) { struct gsm_trans *trans1 = trans_find_by_callref(net, bridge->callref[0]); struct gsm_trans *trans2 = trans_find_by_callref(net, bridge->callref[1]); struct call_leg *cl1; struct call_leg *cl2; if (!trans1 || !trans2) { LOG_TRANS(trans1 ? : trans2, LOGL_ERROR, "Cannot MNCC_BRIDGE, one or both call legs are unset\n"); return -EIO; } if (!trans1->msc_a || !trans2->msc_a) { LOG_TRANS(trans1, LOGL_ERROR, "Cannot MNCC_BRIDGE, one or both call legs lack an active connection\n"); LOG_TRANS(trans2, LOGL_ERROR, "Cannot MNCC_BRIDGE, one or both call legs lack an active connection\n"); return -EIO; } LOG_TRANS(trans1, LOGL_DEBUG, "MNCC_BRIDGE: Local bridge to callref 0x%x\n", trans2->callref); LOG_TRANS(trans2, LOGL_DEBUG, "MNCC_BRIDGE: Local bridge to callref 0x%x\n", trans1->callref); /* This call bridging mechanism is only used with the internal MNCC (with external MNCC briding would be done by * the PBX). For inter-MSC Handover scenarios, an external MNCC is mandatory. The conclusion is that in this * code path, there is only one MSC, and the MSC-I role is local, and hence we can directly access the ran_conn. * If we can't, then we must give up. */ cl1 = trans1->msc_a->cc.call_leg; cl2 = trans2->msc_a->cc.call_leg; return call_leg_local_bridge(cl1, trans1->callref, trans1, cl2, trans2->callref, trans2); } static int gsm48_cc_rx_status_enq(struct gsm_trans *trans, struct msgb *msg) { LOG_TRANS(trans, LOGL_DEBUG, "-> STATUS ENQ\n"); return gsm48_cc_tx_status(trans, msg); } static void gsm48_cc_timeout(void *arg) { struct gsm_trans *trans = arg; int disconnect = 0, release = 0; int mo_cause = GSM48_CC_CAUSE_RECOVERY_TIMER; int mo_location = GSM48_CAUSE_LOC_USER; int l4_cause = GSM48_CC_CAUSE_NORMAL_UNSPEC; int l4_location = GSM48_CAUSE_LOC_PRN_S_LU; struct gsm_mncc mo_rel, l4_rel; memset(&mo_rel, 0, sizeof(struct gsm_mncc)); mo_rel.callref = trans->callref; memset(&l4_rel, 0, sizeof(struct gsm_mncc)); l4_rel.callref = trans->callref; switch(trans->cc.Tcurrent) { case 0x303: release = 1; l4_cause = GSM48_CC_CAUSE_USER_NOTRESPOND; break; case 0x310: disconnect = 1; l4_cause = GSM48_CC_CAUSE_USER_NOTRESPOND; break; case 0x313: disconnect = 1; /* unknown, did not find it in the specs */ break; case 0x301: disconnect = 1; l4_cause = GSM48_CC_CAUSE_USER_NOTRESPOND; break; case 0x308: if (!trans->cc.T308_second) { /* restart T308 a second time */ gsm48_cc_tx_release(trans, &trans->cc.msg); trans->cc.T308_second = 1; break; /* stay in release state */ } trans_free(trans); return; case 0x306: release = 1; mo_cause = trans->cc.msg.cause.value; mo_location = trans->cc.msg.cause.location; break; case 0x323: disconnect = 1; break; default: release = 1; } if (release && trans->callref) { /* process release towards layer 4 */ mncc_release_ind(trans->net, trans, trans->callref, l4_location, l4_cause); trans->callref = 0; } if (disconnect && trans->callref) { /* process disconnect towards layer 4 */ mncc_set_cause(&l4_rel, l4_location, l4_cause); mncc_recvmsg(trans->net, trans, MNCC_DISC_IND, &l4_rel); } /* process disconnect towards mobile station */ if (disconnect || release) { mncc_set_cause(&mo_rel, mo_location, mo_cause); mo_rel.cause.diag[0] = ((trans->cc.Tcurrent & 0xf00) >> 8) + '0'; mo_rel.cause.diag[1] = ((trans->cc.Tcurrent & 0x0f0) >> 4) + '0'; mo_rel.cause.diag[2] = (trans->cc.Tcurrent & 0x00f) + '0'; mo_rel.cause.diag_len = 3; if (disconnect) gsm48_cc_tx_disconnect(trans, &mo_rel); if (release) gsm48_cc_tx_release(trans, &mo_rel); } } /* disconnect both calls from the bridge */ static inline void disconnect_bridge(struct gsm_network *net, const struct gsm_mncc_bridge *bridge, int err) { struct gsm_trans *trans0 = trans_find_by_callref(net, bridge->callref[0]); struct gsm_trans *trans1 = trans_find_by_callref(net, bridge->callref[1]); struct gsm_mncc mx_rel; if (!trans0 || !trans1) return; LOG_TRANS(trans0, LOGL_ERROR, "Failed to bridge TCH for calls %x <-> %x :: %s \n", trans0->callref, trans1->callref, strerror(err)); LOG_TRANS(trans1, LOGL_ERROR, "Failed to bridge TCH for calls %x <-> %x :: %s \n", trans0->callref, trans1->callref, strerror(err)); memset(&mx_rel, 0, sizeof(struct gsm_mncc)); mncc_set_cause(&mx_rel, GSM48_CAUSE_LOC_INN_NET, GSM48_CC_CAUSE_CHAN_UNACCEPT); mx_rel.callref = trans0->callref; gsm48_cc_tx_disconnect(trans0, &mx_rel); mx_rel.callref = trans1->callref; gsm48_cc_tx_disconnect(trans1, &mx_rel); } static void gsm48_start_cc_timer(struct gsm_trans *trans, int current, int sec, int micro) { LOG_TRANS(trans, LOGL_DEBUG, "starting timer T%x with %d seconds\n", current, sec); osmo_timer_setup(&trans->cc.timer, gsm48_cc_timeout, trans); osmo_timer_schedule(&trans->cc.timer, sec, micro); trans->cc.Tcurrent = current; } static int gsm48_cc_rx_setup(struct gsm_trans *trans, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); uint8_t msg_type = gsm48_hdr_msg_type(gh); unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); struct tlv_parsed tp; struct gsm_mncc setup; gsm48_start_guard_timer(trans); 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) { setup.fields |= MNCC_F_EMERGENCY; setup.emergency = 1; /* use destination number as configured by user (if any) */ if (trans->net->emergency.route_to_msisdn) { setup.fields |= MNCC_F_CALLED; setup.called.type = 0; /* unknown */ setup.called.plan = 0; /* unknown */ OSMO_STRLCPY_ARRAY(setup.called.number, trans->net->emergency.route_to_msisdn); } } /* use subscriber as calling party number */ setup.fields |= MNCC_F_CALLING; OSMO_STRLCPY_ARRAY(setup.calling.number, trans->vsub->msisdn); OSMO_STRLCPY_ARRAY(setup.imsi, trans->vsub->imsi); /* bearer capability */ if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { setup.fields |= MNCC_F_BEARER_CAP; gsm48_decode_bearer_cap(&setup.bearer_cap, TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); /* 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)); } /* facility */ if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { setup.fields |= MNCC_F_FACILITY; gsm48_decode_facility(&setup.facility, TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); } /* called party bcd number */ if (TLVP_PRESENT(&tp, GSM48_IE_CALLED_BCD)) { setup.fields |= MNCC_F_CALLED; gsm48_decode_called(&setup.called, TLVP_VAL(&tp, GSM48_IE_CALLED_BCD)-1); } /* user-user */ if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { setup.fields |= MNCC_F_USERUSER; gsm48_decode_useruser(&setup.useruser, TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); } /* ss-version */ if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { setup.fields |= MNCC_F_SSVERSION; gsm48_decode_ssversion(&setup.ssversion, TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); } /* CLIR suppression */ if (TLVP_PRESENT(&tp, GSM48_IE_CLIR_SUPP)) setup.clir.sup = 1; /* CLIR invocation */ if (TLVP_PRESENT(&tp, GSM48_IE_CLIR_INVOC)) setup.clir.inv = 1; /* cc cap */ if (TLVP_PRESENT(&tp, GSM48_IE_CC_CAP)) { setup.fields |= MNCC_F_CCCAP; gsm48_decode_cccap(&setup.cccap, TLVP_VAL(&tp, GSM48_IE_CC_CAP)-1); } 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); } static int gsm48_cc_tx_setup(struct gsm_trans *trans, void *arg) { 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; gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); /* transaction id must not be assigned */ if (trans->transaction_id != TRANS_ID_UNASSIGNED) { LOG_TRANS(trans, LOGL_DEBUG, "TX Setup with assigned transaction. " "This is not allowed!\n"); /* Temporarily out of order */ rc = mncc_release_ind(trans->net, trans, trans->callref, GSM48_CAUSE_LOC_PRN_S_LU, GSM48_CC_CAUSE_RESOURCE_UNAVAIL); trans->callref = 0; trans_free(trans); msgb_free(msg); return rc; } /* Get free transaction_id */ trans_id = trans_assign_trans_id(trans->net, trans->vsub, TRANS_CC); if (trans_id < 0) { /* no free transaction ID */ rc = mncc_release_ind(trans->net, trans, trans->callref, GSM48_CAUSE_LOC_PRN_S_LU, GSM48_CC_CAUSE_RESOURCE_UNAVAIL); trans->callref = 0; trans_free(trans); msgb_free(msg); return rc; } trans->transaction_id = trans_id; gh->msg_type = GSM48_MT_CC_SETUP; /* 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; } /* 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); /* progress */ if (setup->fields & MNCC_F_PROGRESS) gsm48_encode_progress(msg, 0, &setup->progress); /* calling party BCD number */ if (setup->fields & MNCC_F_CALLING) gsm48_encode_calling(msg, &setup->calling); /* called party BCD number */ if (setup->fields & MNCC_F_CALLED) gsm48_encode_called(msg, &setup->called); /* user-user */ if (setup->fields & MNCC_F_USERUSER) gsm48_encode_useruser(msg, 0, &setup->useruser); /* redirecting party BCD number */ if (setup->fields & MNCC_F_REDIRECTING) gsm48_encode_redirecting(msg, &setup->redirecting); /* signal */ if (setup->fields & MNCC_F_SIGNAL) gsm48_encode_signal(msg, setup->signal); new_cc_state(trans, GSM_CSTATE_CALL_PRESENT); 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); } static int gsm48_cc_rx_call_conf(struct gsm_trans *trans, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); struct tlv_parsed tp; struct gsm_mncc call_conf; int rc; gsm48_stop_cc_timer(trans); gsm48_start_cc_timer(trans, 0x310, GSM48_T310); memset(&call_conf, 0, sizeof(struct gsm_mncc)); call_conf.callref = trans->callref; tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); #if 0 /* repeat */ if (TLVP_PRESENT(&tp, GSM48_IE_REPEAT_CIR)) call_conf.repeat = 1; if (TLVP_PRESENT(&tp, GSM48_IE_REPEAT_SEQ)) call_conf.repeat = 2; #endif /* bearer capability */ if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { call_conf.fields |= MNCC_F_BEARER_CAP; gsm48_decode_bearer_cap(&call_conf.bearer_cap, TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); /* 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, 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; gsm48_decode_cause(&call_conf.cause, TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); } /* cc cap */ if (TLVP_PRESENT(&tp, GSM48_IE_CC_CAP)) { call_conf.fields |= MNCC_F_CCCAP; gsm48_decode_cccap(&call_conf.cccap, TLVP_VAL(&tp, GSM48_IE_CC_CAP)-1); } /* IMSI of called subscriber */ OSMO_STRLCPY_ARRAY(call_conf.imsi, trans->vsub->imsi); /* Assign call (if not done yet) */ rc = msc_a_try_call_assignment(trans); /* don't continue, if there were problems with * the call assignment. */ if (rc) return rc; /* 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) { struct gsm_mncc *proceeding = arg; struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC PROC"); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); int rc; gh->msg_type = GSM48_MT_CC_CALL_PROC; new_cc_state(trans, GSM_CSTATE_MO_CALL_PROC); /* bearer capability */ if (proceeding->fields & MNCC_F_BEARER_CAP) { gsm48_encode_bearer_cap(msg, 0, &proceeding->bearer_cap); memcpy(&trans->bearer_cap, &proceeding->bearer_cap, sizeof(trans->bearer_cap)); } /* facility */ if (proceeding->fields & MNCC_F_FACILITY) gsm48_encode_facility(msg, 0, &proceeding->facility); /* progress */ if (proceeding->fields & MNCC_F_PROGRESS) gsm48_encode_progress(msg, 0, &proceeding->progress); rc = trans_tx_gsm48(trans, msg); if (rc) return rc; /* Assign call (if not done yet) */ return msc_a_try_call_assignment(trans); } static int gsm48_cc_rx_alerting(struct gsm_trans *trans, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); struct tlv_parsed tp; struct gsm_mncc alerting; gsm48_stop_cc_timer(trans); gsm48_start_cc_timer(trans, 0x301, GSM48_T301); memset(&alerting, 0, sizeof(struct gsm_mncc)); alerting.callref = trans->callref; tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); /* facility */ if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { alerting.fields |= MNCC_F_FACILITY; gsm48_decode_facility(&alerting.facility, TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); } /* progress */ if (TLVP_PRESENT(&tp, GSM48_IE_PROGR_IND)) { alerting.fields |= MNCC_F_PROGRESS; gsm48_decode_progress(&alerting.progress, TLVP_VAL(&tp, GSM48_IE_PROGR_IND)-1); } /* ss-version */ if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { alerting.fields |= MNCC_F_SSVERSION; gsm48_decode_ssversion(&alerting.ssversion, TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); } 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); } static int gsm48_cc_tx_alerting(struct gsm_trans *trans, void *arg) { struct gsm_mncc *alerting = arg; struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC ALERT"); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->msg_type = GSM48_MT_CC_ALERTING; /* facility */ if (alerting->fields & MNCC_F_FACILITY) gsm48_encode_facility(msg, 0, &alerting->facility); /* progress */ if (alerting->fields & MNCC_F_PROGRESS) gsm48_encode_progress(msg, 0, &alerting->progress); /* user-user */ if (alerting->fields & MNCC_F_USERUSER) gsm48_encode_useruser(msg, 0, &alerting->useruser); 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); } static int gsm48_cc_tx_progress(struct gsm_trans *trans, void *arg) { struct gsm_mncc *progress = arg; struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC PROGRESS"); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->msg_type = GSM48_MT_CC_PROGRESS; /* progress */ gsm48_encode_progress(msg, 1, &progress->progress); /* user-user */ if (progress->fields & MNCC_F_USERUSER) gsm48_encode_useruser(msg, 0, &progress->useruser); return trans_tx_gsm48(trans, msg); } static int gsm48_cc_tx_connect(struct gsm_trans *trans, void *arg) { struct gsm_mncc *connect = arg; struct msgb *msg = gsm48_msgb_alloc_name("GSN 04.08 CC CON"); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->msg_type = GSM48_MT_CC_CONNECT; gsm48_stop_cc_timer(trans); gsm48_start_cc_timer(trans, 0x313, GSM48_T313); /* facility */ if (connect->fields & MNCC_F_FACILITY) gsm48_encode_facility(msg, 0, &connect->facility); /* progress */ if (connect->fields & MNCC_F_PROGRESS) gsm48_encode_progress(msg, 0, &connect->progress); /* connected number */ if (connect->fields & MNCC_F_CONNECTED) gsm48_encode_connected(msg, &connect->connected); /* user-user */ if (connect->fields & MNCC_F_USERUSER) gsm48_encode_useruser(msg, 0, &connect->useruser); 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); } static int gsm48_cc_rx_connect(struct gsm_trans *trans, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); struct tlv_parsed tp; struct gsm_mncc connect; gsm48_stop_cc_timer(trans); memset(&connect, 0, sizeof(struct gsm_mncc)); connect.callref = trans->callref; tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); /* use subscriber as connected party number */ connect.fields |= MNCC_F_CONNECTED; OSMO_STRLCPY_ARRAY(connect.connected.number, trans->vsub->msisdn); OSMO_STRLCPY_ARRAY(connect.imsi, trans->vsub->imsi); /* facility */ if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { connect.fields |= MNCC_F_FACILITY; gsm48_decode_facility(&connect.facility, TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); } /* user-user */ if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { connect.fields |= MNCC_F_USERUSER; gsm48_decode_useruser(&connect.useruser, TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); } /* ss-version */ if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { connect.fields |= MNCC_F_SSVERSION; gsm48_decode_ssversion(&connect.ssversion, TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); } 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); } static int gsm48_cc_rx_connect_ack(struct gsm_trans *trans, struct msgb *msg) { struct gsm_mncc connect_ack; gsm48_stop_cc_timer(trans); new_cc_state(trans, GSM_CSTATE_ACTIVE); rate_ctr_inc(&trans->net->msc_ctrs->ctr[MSC_CTR_CALL_MO_CONNECT_ACK]); memset(&connect_ack, 0, sizeof(struct gsm_mncc)); connect_ack.callref = trans->callref; return mncc_recvmsg(trans->net, trans, MNCC_SETUP_COMPL_IND, &connect_ack); } static int gsm48_cc_tx_connect_ack(struct gsm_trans *trans, void *arg) { struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC CON ACK"); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->msg_type = GSM48_MT_CC_CONNECT_ACK; new_cc_state(trans, GSM_CSTATE_ACTIVE); return trans_tx_gsm48(trans, msg); } static int gsm48_cc_rx_disconnect(struct gsm_trans *trans, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); struct tlv_parsed tp; struct gsm_mncc disc; gsm48_stop_cc_timer(trans); new_cc_state(trans, GSM_CSTATE_DISCONNECT_REQ); memset(&disc, 0, sizeof(struct gsm_mncc)); disc.callref = trans->callref; tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_CAUSE, 0); /* cause */ if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { disc.fields |= MNCC_F_CAUSE; gsm48_decode_cause(&disc.cause, TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); } /* facility */ if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { disc.fields |= MNCC_F_FACILITY; gsm48_decode_facility(&disc.facility, TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); } /* user-user */ if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { disc.fields |= MNCC_F_USERUSER; gsm48_decode_useruser(&disc.useruser, TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); } /* ss-version */ if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { disc.fields |= MNCC_F_SSVERSION; gsm48_decode_ssversion(&disc.ssversion, TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); } return mncc_recvmsg(trans->net, trans, MNCC_DISC_IND, &disc); } static struct gsm_mncc_cause default_cause = { .location = GSM48_CAUSE_LOC_PRN_S_LU, .coding = 0, .rec = 0, .rec_val = 0, .value = GSM48_CC_CAUSE_NORMAL_UNSPEC, .diag_len = 0, .diag = { 0 }, }; static int gsm48_cc_tx_disconnect(struct gsm_trans *trans, void *arg) { struct gsm_mncc *disc = arg; struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC DISC"); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->msg_type = GSM48_MT_CC_DISCONNECT; gsm48_stop_cc_timer(trans); gsm48_start_cc_timer(trans, 0x306, GSM48_T306); /* cause */ if (disc->fields & MNCC_F_CAUSE) gsm48_encode_cause(msg, 1, &disc->cause); else gsm48_encode_cause(msg, 1, &default_cause); /* facility */ if (disc->fields & MNCC_F_FACILITY) gsm48_encode_facility(msg, 0, &disc->facility); /* progress */ if (disc->fields & MNCC_F_PROGRESS) gsm48_encode_progress(msg, 0, &disc->progress); /* user-user */ if (disc->fields & MNCC_F_USERUSER) gsm48_encode_useruser(msg, 0, &disc->useruser); /* store disconnect cause for T306 expiry */ memcpy(&trans->cc.msg, disc, sizeof(struct gsm_mncc)); new_cc_state(trans, GSM_CSTATE_DISCONNECT_IND); return trans_tx_gsm48(trans, msg); } static int gsm48_cc_rx_release(struct gsm_trans *trans, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); struct tlv_parsed tp; struct gsm_mncc rel; gsm48_stop_cc_timer(trans); memset(&rel, 0, sizeof(struct gsm_mncc)); rel.callref = trans->callref; tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); /* cause */ if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { rel.fields |= MNCC_F_CAUSE; gsm48_decode_cause(&rel.cause, TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); } /* facility */ if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { rel.fields |= MNCC_F_FACILITY; gsm48_decode_facility(&rel.facility, TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); } /* user-user */ if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { rel.fields |= MNCC_F_USERUSER; gsm48_decode_useruser(&rel.useruser, TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); } /* ss-version */ if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { rel.fields |= MNCC_F_SSVERSION; gsm48_decode_ssversion(&rel.ssversion, TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); } 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); trans->callref = 0; trans_free(trans); return 0; } static int gsm48_cc_tx_release(struct gsm_trans *trans, void *arg) { struct gsm_mncc *rel = arg; struct msgb *msg; struct gsm48_hdr *gh; if (!trans->msc_a) { LOG_TRANS(trans, LOGL_DEBUG, "Cannot send CC REL, there is no MSC-A connection\n"); return -EINVAL; } msg = gsm48_msgb_alloc_name("GSM 04.08 CC REL"); gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->msg_type = GSM48_MT_CC_RELEASE; gsm48_stop_cc_timer(trans); gsm48_start_cc_timer(trans, 0x308, GSM48_T308); /* cause */ if (rel->fields & MNCC_F_CAUSE) gsm48_encode_cause(msg, 0, &rel->cause); /* facility */ if (rel->fields & MNCC_F_FACILITY) gsm48_encode_facility(msg, 0, &rel->facility); /* user-user */ if (rel->fields & MNCC_F_USERUSER) gsm48_encode_useruser(msg, 0, &rel->useruser); trans->cc.T308_second = 0; memcpy(&trans->cc.msg, rel, sizeof(struct gsm_mncc)); if (trans->cc.state != GSM_CSTATE_RELEASE_REQ) new_cc_state(trans, GSM_CSTATE_RELEASE_REQ); return trans_tx_gsm48(trans, msg); } static int gsm48_cc_rx_release_compl(struct gsm_trans *trans, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); struct tlv_parsed tp; struct gsm_mncc rel; int rc = 0; gsm48_stop_cc_timer(trans); memset(&rel, 0, sizeof(struct gsm_mncc)); rel.callref = trans->callref; tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); /* cause */ if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { rel.fields |= MNCC_F_CAUSE; gsm48_decode_cause(&rel.cause, TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); } /* facility */ if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { rel.fields |= MNCC_F_FACILITY; gsm48_decode_facility(&rel.facility, TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); } /* user-user */ if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { rel.fields |= MNCC_F_USERUSER; gsm48_decode_useruser(&rel.useruser, TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); } /* ss-version */ if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { rel.fields |= MNCC_F_SSVERSION; gsm48_decode_ssversion(&rel.ssversion, TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); } 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); } } } trans->callref = 0; trans_free(trans); return rc; } static int gsm48_cc_tx_release_compl(struct gsm_trans *trans, void *arg) { struct gsm_mncc *rel = arg; struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC REL COMPL"); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); int ret; gh->msg_type = GSM48_MT_CC_RELEASE_COMPL; trans->callref = 0; gsm48_stop_cc_timer(trans); /* cause */ if (rel->fields & MNCC_F_CAUSE) gsm48_encode_cause(msg, 0, &rel->cause); /* facility */ if (rel->fields & MNCC_F_FACILITY) gsm48_encode_facility(msg, 0, &rel->facility); /* user-user */ if (rel->fields & MNCC_F_USERUSER) gsm48_encode_useruser(msg, 0, &rel->useruser); ret = trans_tx_gsm48(trans, msg); trans_free(trans); return ret; } static int gsm48_cc_rx_facility(struct gsm_trans *trans, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); struct tlv_parsed tp; struct gsm_mncc fac; memset(&fac, 0, sizeof(struct gsm_mncc)); fac.callref = trans->callref; tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_FACILITY, 0); /* facility */ if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { fac.fields |= MNCC_F_FACILITY; gsm48_decode_facility(&fac.facility, TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); } /* ss-version */ if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { fac.fields |= MNCC_F_SSVERSION; gsm48_decode_ssversion(&fac.ssversion, TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); } return mncc_recvmsg(trans->net, trans, MNCC_FACILITY_IND, &fac); } static int gsm48_cc_tx_facility(struct gsm_trans *trans, void *arg) { struct gsm_mncc *fac = arg; struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC FAC"); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->msg_type = GSM48_MT_CC_FACILITY; /* facility */ gsm48_encode_facility(msg, 1, &fac->facility); return trans_tx_gsm48(trans, msg); } static int gsm48_cc_rx_hold(struct gsm_trans *trans, struct msgb *msg) { struct gsm_mncc hold; memset(&hold, 0, sizeof(struct gsm_mncc)); hold.callref = trans->callref; return mncc_recvmsg(trans->net, trans, MNCC_HOLD_IND, &hold); } static int gsm48_cc_tx_hold_ack(struct gsm_trans *trans, void *arg) { struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC HLD ACK"); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->msg_type = GSM48_MT_CC_HOLD_ACK; return trans_tx_gsm48(trans, msg); } static int gsm48_cc_tx_hold_rej(struct gsm_trans *trans, void *arg) { struct gsm_mncc *hold_rej = arg; struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC HLD REJ"); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->msg_type = GSM48_MT_CC_HOLD_REJ; /* cause */ if (hold_rej->fields & MNCC_F_CAUSE) gsm48_encode_cause(msg, 1, &hold_rej->cause); else gsm48_encode_cause(msg, 1, &default_cause); return trans_tx_gsm48(trans, msg); } static int gsm48_cc_rx_retrieve(struct gsm_trans *trans, struct msgb *msg) { struct gsm_mncc retrieve; memset(&retrieve, 0, sizeof(struct gsm_mncc)); retrieve.callref = trans->callref; return mncc_recvmsg(trans->net, trans, MNCC_RETRIEVE_IND, &retrieve); } static int gsm48_cc_tx_retrieve_ack(struct gsm_trans *trans, void *arg) { struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC RETR ACK"); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->msg_type = GSM48_MT_CC_RETR_ACK; return trans_tx_gsm48(trans, msg); } static int gsm48_cc_tx_retrieve_rej(struct gsm_trans *trans, void *arg) { struct gsm_mncc *retrieve_rej = arg; struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC RETR REJ"); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->msg_type = GSM48_MT_CC_RETR_REJ; /* cause */ if (retrieve_rej->fields & MNCC_F_CAUSE) gsm48_encode_cause(msg, 1, &retrieve_rej->cause); else gsm48_encode_cause(msg, 1, &default_cause); return trans_tx_gsm48(trans, msg); } static int gsm48_cc_rx_start_dtmf(struct gsm_trans *trans, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); struct tlv_parsed tp; struct gsm_mncc dtmf; memset(&dtmf, 0, sizeof(struct gsm_mncc)); dtmf.callref = trans->callref; tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); /* keypad facility */ if (TLVP_PRESENT(&tp, GSM48_IE_KPD_FACILITY)) { dtmf.fields |= MNCC_F_KEYPAD; gsm48_decode_keypad(&dtmf.keypad, TLVP_VAL(&tp, GSM48_IE_KPD_FACILITY)-1); } return mncc_recvmsg(trans->net, trans, MNCC_START_DTMF_IND, &dtmf); } static int gsm48_cc_tx_start_dtmf_ack(struct gsm_trans *trans, void *arg) { struct gsm_mncc *dtmf = arg; struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 DTMF ACK"); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->msg_type = GSM48_MT_CC_START_DTMF_ACK; /* keypad */ if (dtmf->fields & MNCC_F_KEYPAD) gsm48_encode_keypad(msg, dtmf->keypad); return trans_tx_gsm48(trans, msg); } static int gsm48_cc_tx_start_dtmf_rej(struct gsm_trans *trans, void *arg) { struct gsm_mncc *dtmf = arg; struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 DTMF REJ"); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->msg_type = GSM48_MT_CC_START_DTMF_REJ; /* cause */ if (dtmf->fields & MNCC_F_CAUSE) gsm48_encode_cause(msg, 1, &dtmf->cause); else gsm48_encode_cause(msg, 1, &default_cause); return trans_tx_gsm48(trans, msg); } static int gsm48_cc_tx_stop_dtmf_ack(struct gsm_trans *trans, void *arg) { struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 DTMF STP ACK"); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->msg_type = GSM48_MT_CC_STOP_DTMF_ACK; return trans_tx_gsm48(trans, msg); } static int gsm48_cc_rx_stop_dtmf(struct gsm_trans *trans, struct msgb *msg) { struct gsm_mncc dtmf; memset(&dtmf, 0, sizeof(struct gsm_mncc)); dtmf.callref = trans->callref; return mncc_recvmsg(trans->net, trans, MNCC_STOP_DTMF_IND, &dtmf); } static int gsm48_cc_rx_modify(struct gsm_trans *trans, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); struct tlv_parsed tp; struct gsm_mncc modify; memset(&modify, 0, sizeof(struct gsm_mncc)); modify.callref = trans->callref; tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_BEARER_CAP, 0); /* bearer capability */ if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { modify.fields |= MNCC_F_BEARER_CAP; gsm48_decode_bearer_cap(&modify.bearer_cap, TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); /* Create a copy of the bearer capability * in the transaction struct, so we can use * this information later */ memcpy(&trans->bearer_cap,&modify.bearer_cap, sizeof(trans->bearer_cap)); } new_cc_state(trans, GSM_CSTATE_MO_ORIG_MODIFY); return mncc_recvmsg(trans->net, trans, MNCC_MODIFY_IND, &modify); } static int gsm48_cc_tx_modify(struct gsm_trans *trans, void *arg) { struct gsm_mncc *modify = arg; struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC MOD"); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->msg_type = GSM48_MT_CC_MODIFY; gsm48_start_cc_timer(trans, 0x323, GSM48_T323); /* bearer capability */ gsm48_encode_bearer_cap(msg, 1, &modify->bearer_cap); memcpy(&trans->bearer_cap, &modify->bearer_cap, sizeof(trans->bearer_cap)); new_cc_state(trans, GSM_CSTATE_MO_TERM_MODIFY); return trans_tx_gsm48(trans, msg); } static int gsm48_cc_rx_modify_complete(struct gsm_trans *trans, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); struct tlv_parsed tp; struct gsm_mncc modify; gsm48_stop_cc_timer(trans); memset(&modify, 0, sizeof(struct gsm_mncc)); modify.callref = trans->callref; tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_BEARER_CAP, 0); /* bearer capability */ if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { modify.fields |= MNCC_F_BEARER_CAP; gsm48_decode_bearer_cap(&modify.bearer_cap, TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); /* Create a copy of the bearer capability * in the transaction struct, so we can use * this information later */ memcpy(&trans->bearer_cap,&modify.bearer_cap, sizeof(trans->bearer_cap)); } new_cc_state(trans, GSM_CSTATE_ACTIVE); return mncc_recvmsg(trans->net, trans, MNCC_MODIFY_CNF, &modify); } static int gsm48_cc_tx_modify_complete(struct gsm_trans *trans, void *arg) { struct gsm_mncc *modify = arg; struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC MOD COMPL"); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->msg_type = GSM48_MT_CC_MODIFY_COMPL; /* bearer capability */ gsm48_encode_bearer_cap(msg, 1, &modify->bearer_cap); memcpy(&trans->bearer_cap, &modify->bearer_cap, sizeof(trans->bearer_cap)); new_cc_state(trans, GSM_CSTATE_ACTIVE); return trans_tx_gsm48(trans, msg); } static int gsm48_cc_rx_modify_reject(struct gsm_trans *trans, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); struct tlv_parsed tp; struct gsm_mncc modify; gsm48_stop_cc_timer(trans); memset(&modify, 0, sizeof(struct gsm_mncc)); modify.callref = trans->callref; tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_BEARER_CAP, GSM48_IE_CAUSE); /* bearer capability */ if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { modify.fields |= GSM48_IE_BEARER_CAP; gsm48_decode_bearer_cap(&modify.bearer_cap, TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); /* Create a copy of the bearer capability * in the transaction struct, so we can use * this information later */ memcpy(&trans->bearer_cap,&modify.bearer_cap, sizeof(trans->bearer_cap)); } /* cause */ if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { modify.fields |= MNCC_F_CAUSE; gsm48_decode_cause(&modify.cause, TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); } new_cc_state(trans, GSM_CSTATE_ACTIVE); return mncc_recvmsg(trans->net, trans, MNCC_MODIFY_REJ, &modify); } static int gsm48_cc_tx_modify_reject(struct gsm_trans *trans, void *arg) { struct gsm_mncc *modify = arg; struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC MOD REJ"); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->msg_type = GSM48_MT_CC_MODIFY_REJECT; /* bearer capability */ gsm48_encode_bearer_cap(msg, 1, &modify->bearer_cap); memcpy(&trans->bearer_cap, &modify->bearer_cap, sizeof(trans->bearer_cap)); /* cause */ gsm48_encode_cause(msg, 1, &modify->cause); new_cc_state(trans, GSM_CSTATE_ACTIVE); return trans_tx_gsm48(trans, msg); } static int gsm48_cc_tx_notify(struct gsm_trans *trans, void *arg) { struct gsm_mncc *notify = arg; struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC NOT"); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->msg_type = GSM48_MT_CC_NOTIFY; /* notify */ gsm48_encode_notify(msg, notify->notify); return trans_tx_gsm48(trans, msg); } static int gsm48_cc_rx_notify(struct gsm_trans *trans, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); // struct tlv_parsed tp; struct gsm_mncc notify; memset(¬ify, 0, sizeof(struct gsm_mncc)); notify.callref = trans->callref; // tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len); if (payload_len >= 1) gsm48_decode_notify(¬ify.notify, gh->data); return mncc_recvmsg(trans->net, trans, MNCC_NOTIFY_IND, ¬ify); } static int gsm48_cc_tx_userinfo(struct gsm_trans *trans, void *arg) { struct gsm_mncc *user = arg; struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 USR INFO"); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->msg_type = GSM48_MT_CC_USER_INFO; /* user-user */ if (user->fields & MNCC_F_USERUSER) gsm48_encode_useruser(msg, 1, &user->useruser); /* more data */ if (user->more) gsm48_encode_more(msg); return trans_tx_gsm48(trans, msg); } static int gsm48_cc_rx_userinfo(struct gsm_trans *trans, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); struct tlv_parsed tp; struct gsm_mncc user; memset(&user, 0, sizeof(struct gsm_mncc)); user.callref = trans->callref; tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_USER_USER, 0); /* user-user */ if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { user.fields |= MNCC_F_USERUSER; gsm48_decode_useruser(&user.useruser, TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); } /* more data */ if (TLVP_PRESENT(&tp, GSM48_IE_MORE_DATA)) user.more = 1; return mncc_recvmsg(trans->net, trans, MNCC_USERINFO_IND, &user); } 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) { uint8_t data[sizeof(struct gsm_mncc)]; struct gsm_mncc_rtp *rtp; memset(&data, 0, sizeof(data)); rtp = (struct gsm_mncc_rtp *) &data[0]; rtp->callref = callref; rtp->msg_type = cmd; if (rtp_addr) { rtp->ip = osmo_htonl(inet_addr(rtp_addr->ip)); rtp->port = rtp_addr->port; } 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, NULL); } static int tch_rtp_create(struct gsm_network *net, uint32_t callref) { struct gsm_trans *trans; /* Find callref */ trans = trans_find_by_callref(net, callref); if (!trans) { LOG_TRANS_CAT(trans, DMNCC, LOGL_ERROR, "RTP create for non-existing trans\n"); mncc_recv_rtp_err(net, trans, callref, MNCC_RTP_CREATE); return -EIO; } log_set_context(LOG_CTX_VLR_SUBSCR, trans->vsub); if (!trans->msc_a) { LOG_TRANS_CAT(trans, DMNCC, LOGL_NOTICE, "RTP create for trans without conn\n"); mncc_recv_rtp_err(net, trans, callref, MNCC_RTP_CREATE); return 0; } LOG_TRANS_CAT(trans, DMNCC, LOGL_DEBUG, "rx %s\n", get_mncc_name(MNCC_RTP_CREATE)); /* Assign call (if not done yet) */ 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) { /* This function is called as soon as the port, on which the * mgcp-gw expects the incoming RTP stream from the remote * end (e.g. Asterisk) is known. */ struct msc_a *msc_a = trans->msc_a; struct gsm_network *net = msc_a_net(msc_a); 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; 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; } 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, there is no codec available\n"); return -EINVAL; } /* 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); /* 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) { LOG_TRANS_CAT(trans, DMNCC, LOGL_ERROR, "Cannot RTP CREATE to MNCC, no local RTP IP:port set up\n"); return -EINVAL; } 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) { struct gsm_trans *trans; struct call_leg *cl; struct rtp_stream *rtps; /* 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 * meaningful value based on this information but unfortunately we * can't do that yet because the mncc API can not signal dynamic * payload types yet. This must be fixed first. Also there may be * additional members necessary in trans->conn->rtp because we * somehow need to deal with dynamic payload types that do not * comply to 3gpp's assumptions of payload type numbers on the A * interface. See also related tickets: OS#3399 and OS1683 */ /* Find callref */ trans = trans_find_by_callref(net, rtp->callref); if (!trans) { LOG_TRANS_CAT(trans, DMNCC, LOGL_ERROR, "RTP connect for non-existing trans\n"); mncc_recv_rtp_err(net, trans, rtp->callref, MNCC_RTP_CONNECT); return -EIO; } log_set_context(LOG_CTX_VLR_SUBSCR, trans->vsub); if (!trans->msc_a) { LOG_TRANS_CAT(trans, DMNCC, LOGL_ERROR, "RTP connect for trans without conn\n"); mncc_recv_rtp_err(net, trans, rtp->callref, MNCC_RTP_CONNECT); return -EIO; } 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; if (!rtps) { LOG_TRANS_CAT(trans, DMNCC, LOGL_ERROR, "RTP connect for trans without ongoing call\n"); mncc_recv_rtp_err(net, trans, rtp->callref, MNCC_RTP_CONNECT); return -EINVAL; } 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_32h(&rtp_addr, rtp->ip, rtp->port); rtp_stream_set_remote_addr(rtps, &rtp_addr); } rtp_stream_commit(rtps); return 0; } static struct downstate { uint32_t states; int type; int (*rout) (struct gsm_trans *trans, void *arg); } downstatelist[] = { /* mobile originating call establishment */ {SBIT(GSM_CSTATE_INITIATED), /* 5.2.1.2 */ MNCC_CALL_PROC_REQ, gsm48_cc_tx_call_proc_and_assign}, {SBIT(GSM_CSTATE_INITIATED) | SBIT(GSM_CSTATE_MO_CALL_PROC), /* 5.2.1.2 | 5.2.1.5 */ MNCC_ALERT_REQ, gsm48_cc_tx_alerting}, {SBIT(GSM_CSTATE_INITIATED) | SBIT(GSM_CSTATE_MO_CALL_PROC) | SBIT(GSM_CSTATE_CALL_DELIVERED), /* 5.2.1.2 | 5.2.1.6 | 5.2.1.6 */ MNCC_SETUP_RSP, gsm48_cc_tx_connect}, {SBIT(GSM_CSTATE_MO_CALL_PROC), /* 5.2.1.4.2 */ MNCC_PROGRESS_REQ, gsm48_cc_tx_progress}, /* mobile terminating call establishment */ {SBIT(GSM_CSTATE_NULL), /* 5.2.2.1 */ MNCC_SETUP_REQ, gsm48_cc_tx_setup}, {SBIT(GSM_CSTATE_CONNECT_REQUEST), MNCC_SETUP_COMPL_REQ, gsm48_cc_tx_connect_ack}, /* signalling during call */ {SBIT(GSM_CSTATE_ACTIVE), MNCC_NOTIFY_REQ, gsm48_cc_tx_notify}, {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_RELEASE_REQ), MNCC_FACILITY_REQ, gsm48_cc_tx_facility}, {ALL_STATES, MNCC_START_DTMF_RSP, gsm48_cc_tx_start_dtmf_ack}, {ALL_STATES, MNCC_START_DTMF_REJ, gsm48_cc_tx_start_dtmf_rej}, {ALL_STATES, MNCC_STOP_DTMF_RSP, gsm48_cc_tx_stop_dtmf_ack}, {SBIT(GSM_CSTATE_ACTIVE), MNCC_HOLD_CNF, gsm48_cc_tx_hold_ack}, {SBIT(GSM_CSTATE_ACTIVE), MNCC_HOLD_REJ, gsm48_cc_tx_hold_rej}, {SBIT(GSM_CSTATE_ACTIVE), MNCC_RETRIEVE_CNF, gsm48_cc_tx_retrieve_ack}, {SBIT(GSM_CSTATE_ACTIVE), MNCC_RETRIEVE_REJ, gsm48_cc_tx_retrieve_rej}, {SBIT(GSM_CSTATE_ACTIVE), MNCC_MODIFY_REQ, gsm48_cc_tx_modify}, {SBIT(GSM_CSTATE_MO_ORIG_MODIFY), MNCC_MODIFY_RSP, gsm48_cc_tx_modify_complete}, {SBIT(GSM_CSTATE_MO_ORIG_MODIFY), MNCC_MODIFY_REJ, gsm48_cc_tx_modify_reject}, {SBIT(GSM_CSTATE_ACTIVE), MNCC_USERINFO_REQ, gsm48_cc_tx_userinfo}, /* clearing */ {SBIT(GSM_CSTATE_INITIATED), MNCC_REJ_REQ, gsm48_cc_tx_release_compl}, {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_DISCONNECT_IND) - SBIT(GSM_CSTATE_RELEASE_REQ) - SBIT(GSM_CSTATE_DISCONNECT_REQ), /* 5.4.4 */ MNCC_DISC_REQ, gsm48_cc_tx_disconnect}, {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_RELEASE_REQ), /* 5.4.3.2 */ MNCC_REL_REQ, gsm48_cc_tx_release}, }; #define DOWNSLLEN \ (sizeof(downstatelist) / sizeof(struct downstate)) static int mncc_tx_to_gsm_cc(struct gsm_network *net, const union mncc_msg *msg) { int i, rc = 0; struct msc_a *msc_a = NULL; struct gsm_trans *trans = NULL; const struct gsm_mncc *data; /* handle special messages */ switch(msg->msg_type) { case MNCC_BRIDGE: rc = tch_bridge(net, &msg->bridge); if (rc < 0) disconnect_bridge(net, &msg->bridge, -rc); return rc; case MNCC_RTP_CREATE: return tch_rtp_create(net, msg->rtp.callref); case MNCC_RTP_CONNECT: return tch_rtp_connect(net, &msg->rtp); case MNCC_RTP_FREE: /* unused right now */ return -EIO; case MNCC_FRAME_DROP: case MNCC_FRAME_RECV: case GSM_TCHF_FRAME: case GSM_TCHF_FRAME_EFR: case GSM_TCHH_FRAME: case GSM_TCH_FRAME_AMR: LOG_TRANS_CAT(trans, DMNCC, LOGL_ERROR, "RTP streams must be handled externally; %s not supported.\n", get_mncc_name(msg->msg_type)); return -ENOTSUP; } data = &msg->signal; /* Find callref */ trans = trans_find_by_callref(net, data->callref); /* Callref unknown */ if (!trans) { struct vlr_subscr *vsub; if (msg->msg_type != MNCC_SETUP_REQ) { LOG_TRANS_CAT(trans, DCC, LOGL_ERROR, "Unknown call reference for %s\n", get_mncc_name(msg->msg_type)); /* Invalid call reference */ return mncc_release_ind(net, NULL, data->callref, GSM48_CAUSE_LOC_PRN_S_LU, GSM48_CC_CAUSE_INVAL_TRANS_ID); } if (!data->called.number[0] && !data->imsi[0]) { LOG_TRANS_CAT(trans, DCC, LOGL_ERROR, "Neither number nor IMSI in %s\n", get_mncc_name(msg->msg_type)); /* Invalid number */ return mncc_release_ind(net, NULL, data->callref, GSM48_CAUSE_LOC_PRN_S_LU, GSM48_CC_CAUSE_INV_NR_FORMAT); } /* New transaction due to setup, find subscriber */ if (data->called.number[0]) { vsub = vlr_subscr_find_by_msisdn(net->vlr, data->called.number, __func__); if (!vsub) LOG_TRANS_CAT(trans, DCC, LOGL_ERROR, "rx %s for unknown subscriber number '%s'\n", get_mncc_name(msg->msg_type), data->called.number); } else { vsub = vlr_subscr_find_by_imsi(net->vlr, data->imsi, __func__); if (!vsub) LOG_TRANS_CAT(trans, DCC, LOGL_ERROR, "rx %s for unknown subscriber IMSI '%s'\n", get_mncc_name(msg->msg_type), data->imsi); } if (!vsub) return mncc_release_ind(net, NULL, data->callref, GSM48_CAUSE_LOC_PRN_S_LU, GSM48_CC_CAUSE_USER_NOTRESPOND); /* update the subscriber we deal with */ log_set_context(LOG_CTX_VLR_SUBSCR, vsub); /* If subscriber is not "attached" */ if (!vsub->lu_complete) { LOG_TRANS_CAT(trans, DCC, LOGL_ERROR, "rx %s for subscriber that is not attached: %s\n", get_mncc_name(msg->msg_type), vlr_subscr_name(vsub)); vlr_subscr_put(vsub, __func__); /* Temporarily out of order */ return mncc_release_ind(net, NULL, data->callref, GSM48_CAUSE_LOC_PRN_S_LU, GSM48_CC_CAUSE_DEST_OOO); } /* Find valid conn */ msc_a = msc_a_for_vsub(vsub, true); /* If subscriber is BUSY and we do not DO call in call aka "call-waiting" */ if (!net->call_waiting && msc_a) { struct gsm_trans *existing_cc_trans = trans_find_by_type(msc_a, TRANS_CC); if (existing_cc_trans && existing_cc_trans->cc.state != GSM_CSTATE_NULL) { LOG_TRANS_CAT(existing_cc_trans, DCC, LOGL_NOTICE, "rx '%s' for subscriber %s with trans state (%s)" " rejecting with USER_BUSY\n", get_mncc_name(msg->msg_type), data->called.number, gsm48_cc_state_name(existing_cc_trans->cc.state)); return mncc_release_ind(net, NULL, data->callref, GSM48_CAUSE_LOC_PRN_S_LU, GSM48_CC_CAUSE_USER_BUSY); } } /* Create transaction */ trans = trans_alloc(net, vsub, TRANS_CC, TRANS_ID_UNASSIGNED, data->callref); if (!trans) { LOG_TRANS(trans, LOGL_ERROR, "No memory for trans.\n"); vlr_subscr_put(vsub, __func__); /* Resource unavailable */ mncc_release_ind(net, NULL, data->callref, GSM48_CAUSE_LOC_PRN_S_LU, GSM48_CC_CAUSE_RESOURCE_UNAVAIL); 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 * log it now. */ LOG_TRANS_CAT(trans, DMNCC, LOGL_DEBUG, "rx %s\n", get_mncc_name(msg->msg_type)); /* store setup information until paging succeeds */ memcpy(&trans->cc.msg, data, sizeof(struct gsm_mncc)); /* Request a channel. If Paging already started, paging_request_start() will append the new * trans to the already ongoing Paging. */ trans->paging_request = paging_request_start(vsub, PAGING_CAUSE_CALL_CONVERSATIONAL, cc_paging_cb, trans, "MNCC: establish call"); if (!trans->paging_request) { LOG_TRANS(trans, LOGL_ERROR, "Failed to allocate paging token.\n"); trans_free(trans); } vlr_subscr_put(vsub, __func__); return 0; } /* Assign conn */ trans->msc_a = msc_a; msc_a_get(msc_a, MSC_A_USE_CC); trans->dlci = 0x00; /* SAPI=0, not SACCH */ vlr_subscr_put(vsub, __func__); } else { /* update the subscriber we deal with */ log_set_context(LOG_CTX_VLR_SUBSCR, trans->vsub); } LOG_TRANS_CAT(trans, DMNCC, LOGL_DEBUG, "rx %s\n", get_mncc_name(msg->msg_type)); gsm48_start_guard_timer(trans); trans->cc.mncc_initiated = true; if (trans->msc_a) msc_a = trans->msc_a; /* if paging did not respond yet */ if (!msc_a) { struct gsm_mncc rel = { .callref = data->callref, }; 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 rc = mncc_recvmsg(net, trans, MNCC_REL_IND, &rel); trans->callref = 0; trans_free(trans); return rc; } else { LOG_TRANS(trans, LOGL_DEBUG, "rx %s in state %s\n", get_mncc_name(msg->msg_type), gsm48_cc_state_name(trans->cc.state)); } /* Find function for current state and message */ for (i = 0; i < DOWNSLLEN; i++) if ((msg->msg_type == downstatelist[i].type) && ((1 << trans->cc.state) & downstatelist[i].states)) break; if (i == DOWNSLLEN) { LOG_TRANS(trans, LOGL_DEBUG, "Message '%s' unhandled at state '%s'\n", get_mncc_name(msg->msg_type), gsm48_cc_state_name(trans->cc.state)); return 0; } rc = downstatelist[i].rout(trans, (void*)msg); return rc; } struct mncc_call *mncc_find_by_callref_from_msg(const union mncc_msg *msg) { uint32_t callref; switch (msg->msg_type) { case MNCC_BRIDGE: callref = msg->bridge.callref[0]; break; case MNCC_RTP_CREATE: case MNCC_RTP_CONNECT: callref = msg->rtp.callref; break; case MNCC_RTP_FREE: case MNCC_FRAME_DROP: case MNCC_FRAME_RECV: case GSM_TCHF_FRAME: case GSM_TCHF_FRAME_EFR: case GSM_TCHH_FRAME: case GSM_TCH_FRAME_AMR: return NULL; default: callref = msg->signal.callref; break; } return mncc_call_find_by_callref(callref); } /* Demux incoming genuine calls to GSM CC from MNCC forwarding for inter-MSC handover */ int mncc_tx_to_cc(struct gsm_network *net, void *arg) { const union mncc_msg *msg = arg; struct mncc_call *mncc_call = NULL; if (msg->msg_type == MNCC_SETUP_REQ) { /* Incoming call to forward for inter-MSC Handover? */ mncc_call = msc_t_check_call_to_handover_number(&msg->signal); if (mncc_call) LOG_MNCC_CALL(mncc_call, LOGL_DEBUG, "Incoming call matches pending inter-MSC Handover Number\n"); } if (!mncc_call) { /* Find already active MNCC FSM for this callref. * Currently only for inter-MSC call forwarding, but mncc_fsm could at some point also be used for direct * MNCC<->GSM-CC call handling. */ mncc_call = mncc_find_by_callref_from_msg(msg); } if (mncc_call) { mncc_call_rx(mncc_call, msg); return 0; } /* None of the above? Then it must be a normal GSM CC call related message. */ return mncc_tx_to_gsm_cc(net, msg); } static struct datastate { uint32_t states; int type; int (*rout) (struct gsm_trans *trans, struct msgb *msg); } datastatelist[] = { /* mobile originating call establishment */ {SBIT(GSM_CSTATE_NULL), /* 5.2.1.2 */ GSM48_MT_CC_SETUP, gsm48_cc_rx_setup}, {SBIT(GSM_CSTATE_NULL), /* 5.2.1.2 */ GSM48_MT_CC_EMERG_SETUP, gsm48_cc_rx_setup}, {SBIT(GSM_CSTATE_CONNECT_IND), /* 5.2.1.2 */ GSM48_MT_CC_CONNECT_ACK, gsm48_cc_rx_connect_ack}, /* mobile terminating call establishment */ {SBIT(GSM_CSTATE_CALL_PRESENT), /* 5.2.2.3.2 */ GSM48_MT_CC_CALL_CONF, gsm48_cc_rx_call_conf}, {SBIT(GSM_CSTATE_CALL_PRESENT) | SBIT(GSM_CSTATE_MO_TERM_CALL_CONF), /* ???? | 5.2.2.3.2 */ GSM48_MT_CC_ALERTING, gsm48_cc_rx_alerting}, {SBIT(GSM_CSTATE_CALL_PRESENT) | SBIT(GSM_CSTATE_MO_TERM_CALL_CONF) | SBIT(GSM_CSTATE_CALL_RECEIVED), /* (5.2.2.6) | 5.2.2.6 | 5.2.2.6 */ GSM48_MT_CC_CONNECT, gsm48_cc_rx_connect}, /* signalling during call */ {ALL_STATES - SBIT(GSM_CSTATE_NULL), GSM48_MT_CC_FACILITY, gsm48_cc_rx_facility}, {SBIT(GSM_CSTATE_ACTIVE), GSM48_MT_CC_NOTIFY, gsm48_cc_rx_notify}, {ALL_STATES, GSM48_MT_CC_START_DTMF, gsm48_cc_rx_start_dtmf}, {ALL_STATES, GSM48_MT_CC_STOP_DTMF, gsm48_cc_rx_stop_dtmf}, {ALL_STATES, GSM48_MT_CC_STATUS_ENQ, gsm48_cc_rx_status_enq}, {SBIT(GSM_CSTATE_ACTIVE), GSM48_MT_CC_HOLD, gsm48_cc_rx_hold}, {SBIT(GSM_CSTATE_ACTIVE), GSM48_MT_CC_RETR, gsm48_cc_rx_retrieve}, {SBIT(GSM_CSTATE_ACTIVE), GSM48_MT_CC_MODIFY, gsm48_cc_rx_modify}, {SBIT(GSM_CSTATE_MO_TERM_MODIFY), GSM48_MT_CC_MODIFY_COMPL, gsm48_cc_rx_modify_complete}, {SBIT(GSM_CSTATE_MO_TERM_MODIFY), GSM48_MT_CC_MODIFY_REJECT, gsm48_cc_rx_modify_reject}, {SBIT(GSM_CSTATE_ACTIVE), GSM48_MT_CC_USER_INFO, gsm48_cc_rx_userinfo}, /* clearing */ {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_RELEASE_REQ), /* 5.4.3.2 */ GSM48_MT_CC_DISCONNECT, gsm48_cc_rx_disconnect}, {ALL_STATES - SBIT(GSM_CSTATE_NULL), /* 5.4.4.1.2.2 */ GSM48_MT_CC_RELEASE, gsm48_cc_rx_release}, {ALL_STATES, /* 5.4.3.4 */ GSM48_MT_CC_RELEASE_COMPL, gsm48_cc_rx_release_compl}, }; #define DATASLLEN \ (sizeof(datastatelist) / sizeof(struct datastate)) int gsm0408_rcv_cc(struct msc_a *msc_a, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); uint8_t msg_type = gsm48_hdr_msg_type(gh); uint8_t transaction_id = gsm48_hdr_trans_id_flip_ti(gh); struct gsm_trans *trans = NULL; struct vlr_subscr *vsub = msc_a_vsub(msc_a); struct gsm_network *net = msc_a_net(msc_a); int i, rc = 0; if (msg_type & 0x80) { LOG_TRANS(trans, LOGL_DEBUG, "MSG 0x%2x not defined for PD error\n", msg_type); return -EINVAL; } if (!vsub) { LOG_TRANS(trans, LOGL_ERROR, "Invalid conn: no subscriber\n"); return -EINVAL; } /* Find transaction */ trans = trans_find_by_id(msc_a, TRANS_CC, transaction_id); /* Create transaction */ if (!trans) { /* Create transaction */ trans = trans_alloc(net, vsub, TRANS_CC, transaction_id, msc_cc_next_outgoing_callref()); if (!trans) { LOG_TRANS(trans, LOGL_ERROR, "No memory for trans.\n"); rc = gsm48_tx_simple(msc_a, GSM48_PDISC_CC | (transaction_id << 4), GSM48_MT_CC_RELEASE_COMPL); return -ENOMEM; } if (osmo_fsm_inst_dispatch(msc_a->c.fi, MSC_A_EV_TRANSACTION_ACCEPTED, trans)) { LOG_MSC_A(msc_a, LOGL_ERROR, "Not allowed to accept CC transaction\n"); trans_free(trans); return -EINVAL; } /* Assign transaction */ msc_a_get(msc_a, MSC_A_USE_CC); trans->msc_a = msc_a; trans->dlci = OMSC_LINKID_CB(msg); /* DLCI as received from BSC */ /* An earlier CM Service Request for this CC message now has concluded */ if (!osmo_use_count_by(&msc_a->use_count, MSC_A_USE_CM_SERVICE_CC)) LOG_MSC_A(msc_a, LOGL_ERROR, "Creating new CC transaction without prior CM Service Request\n"); else msc_a_put(msc_a, MSC_A_USE_CM_SERVICE_CC); } LOG_TRANS(trans, LOGL_DEBUG, "rx %s in state %s\n", gsm48_cc_msg_name(msg_type), gsm48_cc_state_name(trans->cc.state)); /* find function for current state and message */ for (i = 0; i < DATASLLEN; i++) if ((msg_type == datastatelist[i].type) && ((1 << trans->cc.state) & datastatelist[i].states)) break; if (i == DATASLLEN) { LOG_TRANS(trans, LOGL_ERROR, "Message unhandled at this state.\n"); /* If a transaction was just now created, it was a bogus transaction ID, and we need to clean up the * transaction right away. */ if (trans->cc.state == GSM_CSTATE_NULL) { LOG_TRANS(trans, LOGL_ERROR, "Unknown transaction ID for non-SETUP message is not allowed" " -- disarding new CC transaction right away\n"); trans_free(trans); } return 0; } assert(trans->vsub); rc = datastatelist[i].rout(trans, msg); return rc; }