diff options
Diffstat (limited to 'src/libmsc/gsm_04_08_cc.c')
-rw-r--r-- | src/libmsc/gsm_04_08_cc.c | 508 |
1 files changed, 285 insertions, 223 deletions
diff --git a/src/libmsc/gsm_04_08_cc.c b/src/libmsc/gsm_04_08_cc.c index 62b5d12eb..aa9764968 100644 --- a/src/libmsc/gsm_04_08_cc.c +++ b/src/libmsc/gsm_04_08_cc.c @@ -48,7 +48,13 @@ #include <osmocom/abis/e1_input.h> #include <osmocom/core/bitvec.h> #include <osmocom/msc/vlr.h> -#include <osmocom/msc/msc_ifaces.h> +#include <osmocom/msc/msub.h> +#include <osmocom/msc/msc_a.h> +#include <osmocom/msc/paging.h> +#include <osmocom/msc/call_leg.h> +#include <osmocom/msc/rtp_stream.h> +#include <osmocom/msc/mncc_call.h> +#include <osmocom/msc/msc_t.h> #include <osmocom/gsm/gsm48.h> #include <osmocom/gsm/gsm0480.h> @@ -60,17 +66,29 @@ #include <osmocom/core/byteswap.h> #include <osmocom/gsm/tlv.h> #include <osmocom/crypt/auth.h> -#ifdef BUILD_IU -#include <osmocom/ranap/iu_client.h> -#endif - -#include <osmocom/msc/msc_ifaces.h> -#include <osmocom/msc/a_iface.h> -#include <osmocom/msc/msc_mgcp.h> #include <assert.h> -static uint32_t new_callref = 0x80000001; +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) { @@ -127,7 +145,7 @@ int gsm48_cc_tx_notify_ss(struct gsm_trans *trans, const char *message) data[0] = ss_notify->len - 1; gh = (struct gsm48_hdr *) msgb_push(ss_notify, sizeof(*gh)); gh->msg_type = GSM48_MT_CC_FACILITY; - return gsm48_conn_sendmsg(ss_notify, trans->conn, trans); + return trans_tx_gsm48(trans, ss_notify); } /* FIXME: this count_statistics is a state machine behaviour. we should convert @@ -163,11 +181,6 @@ static void count_statistics(struct gsm_trans *trans, int new_state) } } -/* The entire call control code is written in accordance with Figure 7.10c - * for 'very early assignment', i.e. we allocate a TCH/F during IMMEDIATE - * ASSIGN, then first use that TCH/F for signalling and later MODE MODIFY - * it for voice */ - static void new_cc_state(struct gsm_trans *trans, int state) { if (state > 31 || state < 0) @@ -201,7 +214,7 @@ static int gsm48_cc_tx_status(struct gsm_trans *trans, void *arg) call_state = msgb_put(msg, 1); call_state[0] = 0xc0 | 0x00; - return gsm48_conn_sendmsg(msg, trans->conn, trans); + return trans_tx_gsm48(trans, msg); } static void gsm48_stop_cc_timer(struct gsm_trans *trans) @@ -254,11 +267,16 @@ void _gsm48_cc_trans_free(struct gsm_trans *trans) { gsm48_stop_cc_timer(trans); - /* Initiate the teardown of the related connections on the MGW */ - msc_mgcp_call_release(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); + } /* Ressource unavailable */ mncc_release_ind(trans->net, trans, trans->callref, GSM48_CAUSE_LOC_PRN_S_LU, @@ -271,60 +289,57 @@ void _gsm48_cc_trans_free(struct gsm_trans *trans) new_cc_state(trans, GSM_CSTATE_NULL); gsm48_stop_guard_timer(trans); -} -static int gsm48_cc_tx_setup(struct gsm_trans *trans, void *arg); + 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 int setup_trig_pag_evt(unsigned int hooknum, unsigned int event, - struct msgb *msg, void *_conn, void *_transt) +static void cc_paging_cb(struct msc_a *msc_a, struct gsm_trans *trans) { - struct ran_conn *conn = _conn; - struct gsm_trans *transt = _transt; - enum gsm_paging_event paging_event = event; - - OSMO_ASSERT(!transt->conn); + 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; + } - switch (paging_event) { - case GSM_PAGING_SUCCEEDED: - LOG_TRANS(transt, LOGL_DEBUG, "Paging succeeded\n"); - OSMO_ASSERT(conn); + if (msc_a) { + LOG_TRANS(trans, LOGL_DEBUG, "Paging succeeded\n"); /* Assign conn */ - transt->conn = ran_conn_get(conn, RAN_CONN_USE_TRANS_CC); - transt->paging_request = NULL; + 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(transt, &transt->cc.msg); - break; - case GSM_PAGING_EXPIRED: - case GSM_PAGING_BUSY: - LOG_TRANS(transt, LOGL_DEBUG, "Paging expired\n"); + gsm48_cc_tx_setup(trans, &trans->cc.msg); + } else { + LOG_TRANS(trans, LOGL_DEBUG, "Paging expired\n"); /* Temporarily out of order */ - mncc_release_ind(transt->net, transt, - transt->callref, + mncc_release_ind(trans->net, trans, + trans->callref, GSM48_CAUSE_LOC_PRN_S_LU, GSM48_CC_CAUSE_DEST_OOO); - transt->callref = 0; - transt->paging_request = NULL; - trans_free(transt); - break; + trans->callref = 0; + trans->paging_request = NULL; + trans_free(trans); } - - return 0; } /* bridge channels of two transactions */ -static int tch_bridge(struct gsm_network *net, struct gsm_mncc_bridge *bridge) +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]); - int rc; + 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->conn || !trans2->conn) { + 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; @@ -333,30 +348,14 @@ static int tch_bridge(struct gsm_network *net, struct gsm_mncc_bridge *bridge) 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); - /* Which subscriber do we want to track trans1 or trans2? */ - log_set_context(LOG_CTX_VLR_SUBSCR, trans1->vsub); - - /* This call briding mechanism is only used with the internal MNCC. - * functionality (with external MNCC briding would be done by the PBX) - * This means we may just copy the codec info we have for the RAN side - * of the first leg to the CN side of both legs. This also means that - * if both legs use different codecs the MGW must perform transcoding - * on the second leg. */ - trans1->conn->rtp.codec_cn = trans1->conn->rtp.codec_ran; - trans2->conn->rtp.codec_cn = trans1->conn->rtp.codec_ran; - - /* Bridge RTP streams */ - rc = msc_mgcp_call_complete(trans1, trans2->conn->rtp.local_port_cn, - trans2->conn->rtp.local_addr_cn); - if (rc) - return -EINVAL; - - rc = msc_mgcp_call_complete(trans2, trans1->conn->rtp.local_port_cn, - trans1->conn->rtp.local_addr_cn); - if (rc) - return -EINVAL; + /* 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 0; + 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) @@ -365,9 +364,6 @@ static int gsm48_cc_rx_status_enq(struct gsm_trans *trans, struct msgb *msg) return gsm48_cc_tx_status(trans, msg); } -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 void gsm48_cc_timeout(void *arg) { struct gsm_trans *trans = arg; @@ -452,7 +448,7 @@ static void gsm48_cc_timeout(void *arg) /* disconnect both calls from the bridge */ static inline void disconnect_bridge(struct gsm_network *net, - struct gsm_mncc_bridge *bridge, int err) + 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]); @@ -527,7 +523,7 @@ static int gsm48_cc_rx_setup(struct gsm_trans *trans, struct msgb *msg) /* Create a copy of the bearer capability * in the transaction struct, so we can use * this information later */ - memcpy(&trans->bearer_cap,&setup.bearer_cap, + memcpy(&trans->bearer_cap, &setup.bearer_cap, sizeof(trans->bearer_cap)); } /* facility */ @@ -606,7 +602,7 @@ static int gsm48_cc_tx_setup(struct gsm_trans *trans, void *arg) } /* Get free transaction_id */ - trans_id = trans_assign_trans_id(trans->net, trans->vsub, GSM48_PDISC_CC); + 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, @@ -655,7 +651,7 @@ static int gsm48_cc_tx_setup(struct gsm_trans *trans, void *arg) rate_ctr_inc(&trans->net->msc_ctrs->ctr[MSC_CTR_CALL_MT_SETUP]); - return gsm48_conn_sendmsg(msg, trans->conn, trans); + return trans_tx_gsm48(trans, msg); } static int gsm48_cc_rx_call_conf(struct gsm_trans *trans, struct msgb *msg) @@ -711,7 +707,7 @@ static int gsm48_cc_rx_call_conf(struct gsm_trans *trans, struct msgb *msg) new_cc_state(trans, GSM_CSTATE_MO_TERM_CALL_CONF); /* Assign call (if not done yet) */ - rc = msc_mgcp_try_call_assignment(trans); + rc = msc_a_try_call_assignment(trans); /* don't continue, if there were problems with * the call assignment. */ @@ -745,12 +741,12 @@ static int gsm48_cc_tx_call_proc_and_assign(struct gsm_trans *trans, void *arg) if (proceeding->fields & MNCC_F_PROGRESS) gsm48_encode_progress(msg, 0, &proceeding->progress); - rc = gsm48_conn_sendmsg(msg, trans->conn, trans); + rc = trans_tx_gsm48(trans, msg); if (rc) return rc; /* Assign call (if not done yet) */ - return msc_mgcp_try_call_assignment(trans); + return msc_a_try_call_assignment(trans); } static int gsm48_cc_rx_alerting(struct gsm_trans *trans, struct msgb *msg) @@ -812,7 +808,7 @@ static int gsm48_cc_tx_alerting(struct gsm_trans *trans, void *arg) new_cc_state(trans, GSM_CSTATE_CALL_DELIVERED); - return gsm48_conn_sendmsg(msg, trans->conn, trans); + return trans_tx_gsm48(trans, msg); } static int gsm48_cc_tx_progress(struct gsm_trans *trans, void *arg) @@ -829,7 +825,7 @@ static int gsm48_cc_tx_progress(struct gsm_trans *trans, void *arg) if (progress->fields & MNCC_F_USERUSER) gsm48_encode_useruser(msg, 0, &progress->useruser); - return gsm48_conn_sendmsg(msg, trans->conn, trans); + return trans_tx_gsm48(trans, msg); } static int gsm48_cc_tx_connect(struct gsm_trans *trans, void *arg) @@ -858,7 +854,7 @@ static int gsm48_cc_tx_connect(struct gsm_trans *trans, void *arg) new_cc_state(trans, GSM_CSTATE_CONNECT_IND); - return gsm48_conn_sendmsg(msg, trans->conn, trans); + return trans_tx_gsm48(trans, msg); } static int gsm48_cc_rx_connect(struct gsm_trans *trans, struct msgb *msg) @@ -929,7 +925,7 @@ static int gsm48_cc_tx_connect_ack(struct gsm_trans *trans, void *arg) new_cc_state(trans, GSM_CSTATE_ACTIVE); - return gsm48_conn_sendmsg(msg, trans->conn, trans); + return trans_tx_gsm48(trans, msg); } static int gsm48_cc_rx_disconnect(struct gsm_trans *trans, struct msgb *msg) @@ -972,7 +968,6 @@ static int gsm48_cc_rx_disconnect(struct gsm_trans *trans, struct msgb *msg) } return mncc_recvmsg(trans->net, trans, MNCC_DISC_IND, &disc); - } static struct gsm_mncc_cause default_cause = { @@ -1017,7 +1012,7 @@ static int gsm48_cc_tx_disconnect(struct gsm_trans *trans, void *arg) new_cc_state(trans, GSM_CSTATE_DISCONNECT_IND); - return gsm48_conn_sendmsg(msg, trans->conn, trans); + return trans_tx_gsm48(trans, msg); } static int gsm48_cc_rx_release(struct gsm_trans *trans, struct msgb *msg) @@ -1062,7 +1057,7 @@ static int gsm48_cc_rx_release(struct gsm_trans *trans, struct msgb *msg) /* release collision 5.4.5 */ rc = mncc_recvmsg(trans->net, trans, MNCC_REL_CNF, &rel); } else { - rc = gsm48_tx_simple(trans->conn, + rc = gsm48_tx_simple(trans->msc_a, GSM48_PDISC_CC | (trans->transaction_id << 4), GSM48_MT_CC_RELEASE_COMPL); rc = mncc_recvmsg(trans->net, trans, MNCC_REL_IND, &rel); @@ -1103,7 +1098,7 @@ static int gsm48_cc_tx_release(struct gsm_trans *trans, void *arg) if (trans->cc.state != GSM_CSTATE_RELEASE_REQ) new_cc_state(trans, GSM_CSTATE_RELEASE_REQ); - return gsm48_conn_sendmsg(msg, trans->conn, trans); + return trans_tx_gsm48(trans, msg); } static int gsm48_cc_rx_release_compl(struct gsm_trans *trans, struct msgb *msg) @@ -1189,7 +1184,7 @@ static int gsm48_cc_tx_release_compl(struct gsm_trans *trans, void *arg) if (rel->fields & MNCC_F_USERUSER) gsm48_encode_useruser(msg, 0, &rel->useruser); - ret = gsm48_conn_sendmsg(msg, trans->conn, trans); + ret = trans_tx_gsm48(trans, msg); trans_free(trans); @@ -1233,7 +1228,7 @@ static int gsm48_cc_tx_facility(struct gsm_trans *trans, void *arg) /* facility */ gsm48_encode_facility(msg, 1, &fac->facility); - return gsm48_conn_sendmsg(msg, trans->conn, trans); + return trans_tx_gsm48(trans, msg); } static int gsm48_cc_rx_hold(struct gsm_trans *trans, struct msgb *msg) @@ -1252,7 +1247,7 @@ static int gsm48_cc_tx_hold_ack(struct gsm_trans *trans, void *arg) gh->msg_type = GSM48_MT_CC_HOLD_ACK; - return gsm48_conn_sendmsg(msg, trans->conn, trans); + return trans_tx_gsm48(trans, msg); } static int gsm48_cc_tx_hold_rej(struct gsm_trans *trans, void *arg) @@ -1269,7 +1264,7 @@ static int gsm48_cc_tx_hold_rej(struct gsm_trans *trans, void *arg) else gsm48_encode_cause(msg, 1, &default_cause); - return gsm48_conn_sendmsg(msg, trans->conn, trans); + return trans_tx_gsm48(trans, msg); } static int gsm48_cc_rx_retrieve(struct gsm_trans *trans, struct msgb *msg) @@ -1289,7 +1284,7 @@ static int gsm48_cc_tx_retrieve_ack(struct gsm_trans *trans, void *arg) gh->msg_type = GSM48_MT_CC_RETR_ACK; - return gsm48_conn_sendmsg(msg, trans->conn, trans); + return trans_tx_gsm48(trans, msg); } static int gsm48_cc_tx_retrieve_rej(struct gsm_trans *trans, void *arg) @@ -1306,7 +1301,7 @@ static int gsm48_cc_tx_retrieve_rej(struct gsm_trans *trans, void *arg) else gsm48_encode_cause(msg, 1, &default_cause); - return gsm48_conn_sendmsg(msg, trans->conn, trans); + return trans_tx_gsm48(trans, msg); } static int gsm48_cc_rx_start_dtmf(struct gsm_trans *trans, struct msgb *msg) @@ -1341,7 +1336,7 @@ static int gsm48_cc_tx_start_dtmf_ack(struct gsm_trans *trans, void *arg) if (dtmf->fields & MNCC_F_KEYPAD) gsm48_encode_keypad(msg, dtmf->keypad); - return gsm48_conn_sendmsg(msg, trans->conn, trans); + return trans_tx_gsm48(trans, msg); } static int gsm48_cc_tx_start_dtmf_rej(struct gsm_trans *trans, void *arg) @@ -1358,7 +1353,7 @@ static int gsm48_cc_tx_start_dtmf_rej(struct gsm_trans *trans, void *arg) else gsm48_encode_cause(msg, 1, &default_cause); - return gsm48_conn_sendmsg(msg, trans->conn, trans); + return trans_tx_gsm48(trans, msg); } static int gsm48_cc_tx_stop_dtmf_ack(struct gsm_trans *trans, void *arg) @@ -1368,7 +1363,7 @@ static int gsm48_cc_tx_stop_dtmf_ack(struct gsm_trans *trans, void *arg) gh->msg_type = GSM48_MT_CC_STOP_DTMF_ACK; - return gsm48_conn_sendmsg(msg, trans->conn, trans); + return trans_tx_gsm48(trans, msg); } static int gsm48_cc_rx_stop_dtmf(struct gsm_trans *trans, struct msgb *msg) @@ -1425,7 +1420,7 @@ static int gsm48_cc_tx_modify(struct gsm_trans *trans, void *arg) new_cc_state(trans, GSM_CSTATE_MO_TERM_MODIFY); - return gsm48_conn_sendmsg(msg, trans->conn, trans); + return trans_tx_gsm48(trans, msg); } static int gsm48_cc_rx_modify_complete(struct gsm_trans *trans, struct msgb *msg) @@ -1472,7 +1467,7 @@ static int gsm48_cc_tx_modify_complete(struct gsm_trans *trans, void *arg) new_cc_state(trans, GSM_CSTATE_ACTIVE); - return gsm48_conn_sendmsg(msg, trans->conn, trans); + return trans_tx_gsm48(trans, msg); } static int gsm48_cc_rx_modify_reject(struct gsm_trans *trans, struct msgb *msg) @@ -1527,7 +1522,7 @@ static int gsm48_cc_tx_modify_reject(struct gsm_trans *trans, void *arg) new_cc_state(trans, GSM_CSTATE_ACTIVE); - return gsm48_conn_sendmsg(msg, trans->conn, trans); + return trans_tx_gsm48(trans, msg); } static int gsm48_cc_tx_notify(struct gsm_trans *trans, void *arg) @@ -1541,7 +1536,7 @@ static int gsm48_cc_tx_notify(struct gsm_trans *trans, void *arg) /* notify */ gsm48_encode_notify(msg, notify->notify); - return gsm48_conn_sendmsg(msg, trans->conn, trans); + return trans_tx_gsm48(trans, msg); } static int gsm48_cc_rx_notify(struct gsm_trans *trans, struct msgb *msg) @@ -1575,7 +1570,7 @@ static int gsm48_cc_tx_userinfo(struct gsm_trans *trans, void *arg) if (user->more) gsm48_encode_more(msg); - return gsm48_conn_sendmsg(msg, trans->conn, trans); + return trans_tx_gsm48(trans, msg); } static int gsm48_cc_rx_userinfo(struct gsm_trans *trans, struct msgb *msg) @@ -1601,9 +1596,9 @@ static int gsm48_cc_rx_userinfo(struct gsm_trans *trans, struct msgb *msg) return mncc_recvmsg(trans->net, trans, MNCC_USERINFO_IND, &user); } -static void mncc_recv_rtp(struct gsm_network *net, struct gsm_trans *trans, uint32_t callref, - int cmd, uint32_t addr, uint16_t port, uint32_t payload_type, - uint32_t payload_msg_type) +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) { uint8_t data[sizeof(struct gsm_mncc)]; struct gsm_mncc_rtp *rtp; @@ -1613,55 +1608,18 @@ static void mncc_recv_rtp(struct gsm_network *net, struct gsm_trans *trans, uint rtp->callref = callref; rtp->msg_type = cmd; - rtp->ip = osmo_htonl(addr); - rtp->port = port; + 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; - mncc_recvmsg(net, trans, cmd, (struct gsm_mncc *)data); -} - -static void mncc_recv_rtp_sock(struct gsm_network *net, struct gsm_trans *trans, int cmd) -{ - int msg_type; - - /* FIXME This has to be set to some meaningful value. - * Possible options are: - * GSM_TCHF_FRAME, GSM_TCHF_FRAME_EFR, - * GSM_TCHH_FRAME, GSM_TCH_FRAME_AMR - * (0 if unknown) */ - msg_type = GSM_TCHF_FRAME; - - uint32_t addr = inet_addr(trans->conn->rtp.local_addr_cn); - uint16_t port = trans->conn->rtp.local_port_cn; - - if (addr == INADDR_NONE) { - LOGP(DMNCC, LOGL_ERROR, - "(subscriber:%s) external MNCC is signalling invalid IP-Address\n", - vlr_subscr_name(trans->vsub)); - return; - } - if (port == 0) { - LOGP(DMNCC, LOGL_ERROR, - "(subscriber:%s) external MNCC is signalling invalid Port\n", - vlr_subscr_name(trans->vsub)); - return; - } - - /* FIXME: This has to be set to some meaningful value, - * before the MSC-Split, this value was pulled from - * lchan->abis_ip.rtp_payload */ - uint32_t payload_type = 0; - - return mncc_recv_rtp(net, trans, trans->callref, cmd, - addr, - port, - payload_type, - msg_type); + 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) { - return mncc_recv_rtp(net, trans, callref, cmd, 0, 0, 0, 0); + mncc_recv_rtp(net, trans, callref, cmd, NULL, 0, 0); } static int tch_rtp_create(struct gsm_network *net, uint32_t callref) @@ -1676,7 +1634,7 @@ static int tch_rtp_create(struct gsm_network *net, uint32_t callref) return -EIO; } log_set_context(LOG_CTX_VLR_SUBSCR, trans->vsub); - if (!trans->conn) { + 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; @@ -1698,7 +1656,7 @@ static int tch_rtp_create(struct gsm_network *net, uint32_t callref) trans->tch_rtp_create = true; /* Assign call (if not done yet) */ - return msc_mgcp_try_call_assignment(trans); + return msc_a_try_call_assignment(trans); } /* Trigger TCH_RTP_CREATE acknowledgement */ @@ -1707,18 +1665,38 @@ 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; + /* FIXME: This has to be set to some meaningful value, + * before the MSC-Split, this value was pulled from + * lchan->abis_ip.rtp_payload */ + uint32_t payload_type = 0; + int msg_type; + + /* FIXME This has to be set to some meaningful value. + * Possible options are: + * GSM_TCHF_FRAME, GSM_TCHF_FRAME_EFR, + * GSM_TCHH_FRAME, GSM_TCH_FRAME_AMR + * (0 if unknown) */ + msg_type = GSM_TCHF_FRAME; - struct ran_conn *conn = trans->conn; - struct gsm_network *network = conn->network; + 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; + } - mncc_recv_rtp_sock(network, trans, MNCC_RTP_CREATE); - return 0; + return mncc_recv_rtp(net, trans, trans->callref, MNCC_RTP_CREATE, rtp_cn_local, payload_type, msg_type); } -static int tch_rtp_connect(struct gsm_network *net, struct gsm_mncc_rtp *rtp) +static int tch_rtp_connect(struct gsm_network *net, const struct gsm_mncc_rtp *rtp) { struct gsm_trans *trans; - struct in_addr addr; + struct call_leg *cl; + struct rtp_stream *rtps; + struct osmo_sockaddr_str rtp_addr; /* FIXME: in *rtp we should get the codec information of the remote * leg. We will have to populate trans->conn->rtp.codec_cn with a @@ -1738,16 +1716,29 @@ static int tch_rtp_connect(struct gsm_network *net, struct gsm_mncc_rtp *rtp) return -EIO; } log_set_context(LOG_CTX_VLR_SUBSCR, trans->vsub); - if (!trans->conn) { + 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 0; + return -EIO; + } + + LOG_TRANS_CAT(trans, DMNCC, LOGL_DEBUG, "rx %s\n", get_mncc_name(MNCC_RTP_CONNECT)); + + 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; } LOG_TRANS_CAT(trans, DMNCC, LOGL_DEBUG, "rx %s\n", get_mncc_name(MNCC_RTP_CONNECT)); - addr.s_addr = osmo_htonl(rtp->ip); - return msc_mgcp_call_complete(trans, rtp->port, inet_ntoa(addr)); + osmo_sockaddr_str_from_32n(&rtp_addr, rtp->ip, rtp->port); + rtp_stream_set_remote_addr(rtps, &rtp_addr); + rtp_stream_commit(rtps); + return 0; } static struct downstate { @@ -1809,24 +1800,24 @@ static struct downstate { (sizeof(downstatelist) / sizeof(struct downstate)) -int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg) +int mncc_tx_to_gsm_cc(struct gsm_network *net, const union mncc_msg *msg) { int i, rc = 0; - struct gsm_trans *trans = NULL, *transt; - struct ran_conn *conn = NULL; - struct gsm_mncc *data = arg, rel; + struct msc_a *msc_a = NULL; + struct gsm_trans *trans = NULL; + const struct gsm_mncc *data; /* handle special messages */ - switch(msg_type) { + switch(msg->msg_type) { case MNCC_BRIDGE: - rc = tch_bridge(net, arg); + rc = tch_bridge(net, &msg->bridge); if (rc < 0) - disconnect_bridge(net, arg, -rc); + disconnect_bridge(net, &msg->bridge, -rc); return rc; case MNCC_RTP_CREATE: - return tch_rtp_create(net, data->callref); + return tch_rtp_create(net, msg->rtp.callref); case MNCC_RTP_CONNECT: - return tch_rtp_connect(net, arg); + return tch_rtp_connect(net, &msg->rtp); case MNCC_RTP_FREE: /* unused right now */ return -EIO; @@ -1838,12 +1829,11 @@ int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg) 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_type)); + get_mncc_name(msg->msg_type)); return -ENOTSUP; } - memset(&rel, 0, sizeof(struct gsm_mncc)); - rel.callref = data->callref; + data = &msg->signal; /* Find callref */ trans = trans_find_by_callref(net, data->callref); @@ -1852,9 +1842,9 @@ int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg) if (!trans) { struct vlr_subscr *vsub; - if (msg_type != MNCC_SETUP_REQ) { + if (msg->msg_type != MNCC_SETUP_REQ) { LOG_TRANS_CAT(trans, DCC, LOGL_ERROR, "Unknown call reference for %s\n", - get_mncc_name(msg_type)); + get_mncc_name(msg->msg_type)); /* Invalid call reference */ return mncc_release_ind(net, NULL, data->callref, GSM48_CAUSE_LOC_PRN_S_LU, @@ -1862,7 +1852,7 @@ int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg) } 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_type)); + get_mncc_name(msg->msg_type)); /* Invalid number */ return mncc_release_ind(net, NULL, data->callref, GSM48_CAUSE_LOC_PRN_S_LU, @@ -1873,12 +1863,12 @@ int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg) 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_type), data->called.number); + 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_type), data->imsi); + get_mncc_name(msg->msg_type), data->imsi); } if (!vsub) return mncc_release_ind(net, NULL, data->callref, GSM48_CAUSE_LOC_PRN_S_LU, @@ -1889,7 +1879,7 @@ int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg) /* 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_type), vlr_subscr_name(vsub)); + 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, @@ -1897,7 +1887,7 @@ int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg) GSM48_CC_CAUSE_DEST_OOO); } /* Create transaction */ - trans = trans_alloc(net, vsub, GSM48_PDISC_CC, + trans = trans_alloc(net, vsub, TRANS_CC, TRANS_ID_UNASSIGNED, data->callref); if (!trans) { LOG_TRANS(trans, LOGL_ERROR, "No memory for trans.\n"); @@ -1909,20 +1899,16 @@ int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg) return -ENOMEM; } - /* Find conn */ - conn = connection_for_subscr(vsub); + /* Find valid conn */ + msc_a = msc_a_for_vsub(vsub, true); /* If subscriber has no conn */ - if (!conn) { - /* find transaction with this subscriber already paging */ - llist_for_each_entry(transt, &net->trans_list, entry) { - /* Transaction of our conn? */ - if (transt == trans || - transt->vsub != vsub) - continue; + if (!msc_a) { + + if (vsub->cs.is_paging) { LOG_TRANS(trans, LOGL_DEBUG, "rx %s, subscriber not yet connected, paging already started\n", - get_mncc_name(msg_type)); + get_mncc_name(msg->msg_type)); vlr_subscr_put(vsub, __func__); trans_free(trans); return 0; @@ -1932,24 +1918,19 @@ int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg) memcpy(&trans->cc.msg, data, sizeof(struct gsm_mncc)); /* Request a channel */ - trans->paging_request = subscr_request_conn( - vsub, - setup_trig_pag_evt, - trans, - "MNCC: establish call", - SGSAP_SERV_IND_CS_CALL); + 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"); - vlr_subscr_put(vsub, __func__); trans_free(trans); - return 0; } vlr_subscr_put(vsub, __func__); return 0; } /* Assign conn */ - trans->conn = ran_conn_get(conn, RAN_CONN_USE_TRANS_CC); + 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 { @@ -1957,19 +1938,22 @@ int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg) log_set_context(LOG_CTX_VLR_SUBSCR, trans->vsub); } - LOG_TRANS_CAT(trans, DMNCC, LOGL_DEBUG, "rx %s\n", get_mncc_name(msg_type)); + LOG_TRANS_CAT(trans, DMNCC, LOGL_DEBUG, "rx %s\n", get_mncc_name(msg->msg_type)); gsm48_start_guard_timer(trans); - if (trans->conn) - conn = trans->conn; + if (trans->msc_a) + msc_a = trans->msc_a; /* if paging did not respond yet */ - if (!conn) { - LOG_TRANS(trans, LOGL_DEBUG, "rx %s in paging state\n", get_mncc_name(msg_type)); + 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); - if (msg_type == MNCC_REL_REQ) + 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); @@ -1978,25 +1962,83 @@ int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg) return rc; } else { LOG_TRANS(trans, LOGL_DEBUG, "rx %s in state %s\n", - get_mncc_name(msg_type), gsm48_cc_state_name(trans->cc.state)); + 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_type == downstatelist[i].type) + 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_type), gsm48_cc_state_name(trans->cc.state)); + get_mncc_name(msg->msg_type), gsm48_cc_state_name(trans->cc.state)); return 0; } - rc = downstatelist[i].rout(trans, arg); + 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, int msg_type, void *arg) +{ + const union mncc_msg *msg = arg; + struct mncc_call *mncc_call = NULL; + OSMO_ASSERT(msg_type == msg->msg_type); + + 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; @@ -2052,12 +2094,14 @@ static struct datastate { #define DATASLLEN \ (sizeof(datastatelist) / sizeof(struct datastate)) -int gsm0408_rcv_cc(struct ran_conn *conn, struct msgb *msg) +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) { @@ -2065,33 +2109,44 @@ int gsm0408_rcv_cc(struct ran_conn *conn, struct msgb *msg) return -EINVAL; } - if (!conn->vsub) { + if (!vsub) { LOG_TRANS(trans, LOGL_ERROR, "Invalid conn: no subscriber\n"); return -EINVAL; } /* Find transaction */ - trans = trans_find_by_id(conn, GSM48_PDISC_CC, transaction_id); + trans = trans_find_by_id(msc_a, TRANS_CC, transaction_id); /* Create transaction */ if (!trans) { - DEBUGP(DCC, "Unknown transaction ID %x, " - "creating new trans.\n", transaction_id); /* Create transaction */ - trans = trans_alloc(conn->network, conn->vsub, - GSM48_PDISC_CC, - transaction_id, new_callref++); + 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(conn, + 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 */ - trans->conn = ran_conn_get(conn, RAN_CONN_USE_TRANS_CC); + 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 */ - cm_service_request_concludes(conn, msg); + + /* 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), @@ -2104,6 +2159,14 @@ int gsm0408_rcv_cc(struct ran_conn *conn, struct msgb *msg) 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; } @@ -2111,6 +2174,5 @@ int gsm0408_rcv_cc(struct ran_conn *conn, struct msgb *msg) rc = datastatelist[i].rout(trans, msg); - ran_conn_communicating(conn); return rc; } |