diff options
Diffstat (limited to 'src/libmsc/msc_mgcp.c')
-rw-r--r-- | src/libmsc/msc_mgcp.c | 1254 |
1 files changed, 0 insertions, 1254 deletions
diff --git a/src/libmsc/msc_mgcp.c b/src/libmsc/msc_mgcp.c deleted file mode 100644 index 5c8888031..000000000 --- a/src/libmsc/msc_mgcp.c +++ /dev/null @@ -1,1254 +0,0 @@ -/* (C) 2017 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> - * All Rights Reserved - * - * Author: Philipp Maier - * - * 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 <http://www.gnu.org/licenses/>. - * - */ - -#include <arpa/inet.h> - -#include <osmocom/mgcp_client/mgcp_client.h> -#include <osmocom/core/logging.h> -#include <osmocom/core/utils.h> -#include <osmocom/core/timer.h> -#include <osmocom/core/fsm.h> -#include <osmocom/core/byteswap.h> -#include <osmocom/msc/msc_mgcp.h> -#include <osmocom/msc/debug.h> -#include <osmocom/msc/transaction.h> -#include <osmocom/msc/a_iface.h> -#include <osmocom/msc/msc_ifaces.h> -#include <osmocom/msc/gsm_04_08.h> -#include <osmocom/msc/iucs.h> -#include <osmocom/msc/vlr.h> - -#include "../../bscconfig.h" - -#define S(x) (1 << (x)) - -#define CONN_ID_RAN 1 -#define CONN_ID_CN 2 - -#define MGCP_MGW_TIMEOUT 4 /* in seconds */ -#define MGCP_MGW_TIMEOUT_TIMER_NR 1 -#define MGCP_RAN_TIMEOUT 120 /* in seconds */ -#define MGCP_RAN_TIMEOUT_TIMER_NR 2 -#define MGCP_REL_TIMEOUT 60 /* in seconds */ -#define MGCP_REL_TIMEOUT_TIMER_NR 3 -#define MGCP_ASS_TIMEOUT 10 /* in seconds */ -#define MGCP_ASS_TIMEOUT_TIMER_NR 4 - -/* Some internal cause codes to indicate fault condition inside the FSM */ -enum msc_mgcp_cause_code { - MGCP_ERR_MGW_FAIL, - MGCP_ERR_MGW_INVAL_RESP, - MGCP_ERR_MGW_TX_FAIL, - MGCP_ERR_MGW_TIMEOUT, - MGCP_ERR_UNEXP_TEARDOWN, - MGCP_ERR_UNSUPP_ADDR_FMT, - MGCP_ERR_RAN_TIMEOUT, - MGCP_ERR_ASS_TIMEOUT, - MGCP_ERR_TOOLONG, - MGCP_ERR_ASSGMNT_FAIL -}; - -/* Human readable respresentation of the faul codes, will be displayed by - * handle_error() */ -static const struct value_string msc_mgcp_cause_codes_names[] = { - {MGCP_ERR_MGW_FAIL, "operation failed on MGW"}, - {MGCP_ERR_MGW_INVAL_RESP, "invalid / unparseable response from MGW"}, - {MGCP_ERR_MGW_TX_FAIL, "failed to transmit MGCP message to MGW"}, - {MGCP_ERR_MGW_TIMEOUT, "request to MGW timed out"}, - {MGCP_ERR_UNEXP_TEARDOWN, "unexpected connection teardown"}, - {MGCP_ERR_UNSUPP_ADDR_FMT, "unsupported network address format used (RAN)"}, - {MGCP_ERR_RAN_TIMEOUT, "call could not be completed in time (RAN)"}, - {MGCP_ERR_ASS_TIMEOUT, "assignment could not be completed in time (RAN)"}, - {MGCP_ERR_TOOLONG, "string value too long"}, - {MGCP_ERR_ASSGMNT_FAIL, "assignment failure (RAN)"}, - {0, NULL} -}; - -enum fsm_msc_mgcp_states { - ST_CRCX_RAN, - ST_CRCX_CN, - ST_CRCX_COMPL, - ST_MDCX_CN, - ST_MDCX_CN_COMPL, - ST_MDCX_RAN, - ST_MDCX_RAN_COMPL, - ST_CALL, - ST_HALT, -}; - -enum msc_mgcp_fsm_evt { - /* Initial event: start off the state machine */ - EV_INIT, - - /* External event: Notify that the Assignment is complete and we - * may now forward IP/Port of the remote call leg to the MGW */ - EV_ASSIGN, - - /* External event: Notify that the Call is complete and that the - * two half open connections on the MGW should now be connected */ - EV_CONNECT, - - /* External event: Notify that the call is over and the connections - * on the mgw shall be removed */ - EV_TEARDOWN, - - /* Internal event: An error occurred that requires a controlled - * teardown of the RTP connections */ - EV_TEARDOWN_ERROR, - - /* Internal event: The mgcp_gw has sent its CRCX response for - * the RAN side */ - EV_CRCX_RAN_RESP, - - /* Internal event: The mgcp_gw has sent its CRCX response for - * the CN side */ - EV_CRCX_CN_RESP, - - /* Internal event: The mgcp_gw has sent its MDCX response for - * the RAN side */ - EV_MDCX_RAN_RESP, - - /* Internal event: The mgcp_gw has sent its MDCX response for - * the CN side */ - EV_MDCX_CN_RESP, - - /* Internal event: The mgcp_gw has sent its DLCX response for - * the RAN and CN side */ - EV_DLCX_ALL_RESP, -}; - -static const struct value_string msc_mgcp_fsm_evt_names[] = { - OSMO_VALUE_STRING(EV_INIT), - OSMO_VALUE_STRING(EV_ASSIGN), - OSMO_VALUE_STRING(EV_CONNECT), - OSMO_VALUE_STRING(EV_TEARDOWN), - OSMO_VALUE_STRING(EV_TEARDOWN_ERROR), - OSMO_VALUE_STRING(EV_CRCX_RAN_RESP), - OSMO_VALUE_STRING(EV_CRCX_CN_RESP), - OSMO_VALUE_STRING(EV_MDCX_RAN_RESP), - OSMO_VALUE_STRING(EV_MDCX_CN_RESP), - OSMO_VALUE_STRING(EV_DLCX_ALL_RESP), - {0, NULL} -}; - -/* A general error handler function. On error we still have an interest to - * remove a half open connection (if possible). This function will execute - * a controlled jump to the DLCX phase. From there, the FSM will then just - * continue like the call were ended normally */ -#define handle_error(mgcp_ctx, cause, dlcx) _handle_error(mgcp_ctx, cause, dlcx, __FILE__, __LINE__) -static void _handle_error(struct mgcp_ctx *mgcp_ctx, enum msc_mgcp_cause_code cause, bool dlcx, const char *file, - int line) -{ - bool dlcx_possible = true; - struct osmo_fsm_inst *fi; - struct gsm_mncc mncc; - - OSMO_ASSERT(mgcp_ctx); - fi = mgcp_ctx->fsm; - OSMO_ASSERT(fi); - - /* Check if the endpoint identifier is a specific endpoint identifier, - * since in order to perform a DLCX we must know the specific - * identifier of the endpoint we want to release. If we do not have - * this information because of errornous communication we can not - * perform a DLCX. */ - if (strstr(mgcp_ctx->rtp_endpoint, "*")) - dlcx_possible = false; - - LOGPFSMLSRC(mgcp_ctx->fsm, LOGL_ERROR, file, line, "%s -- graceful shutdown...\n", - get_value_string(msc_mgcp_cause_codes_names, cause)); - - /* Request the higher layers (gsm_04_08.c) to release the call. If the - * problem occured after msc_mgcp_call_release() was calls, remain - * silent because we already got informed and the higher layers might - * already freed their context information (trans). */ - if (!mgcp_ctx->free_ctx) { - mncc = (struct gsm_mncc) { - .msg_type = MNCC_REL_REQ, - .callref = mgcp_ctx->trans->callref, - .cause = { - .location = GSM48_CAUSE_LOC_PRN_S_LU, - .coding = 0, /* FIXME */ - .value = GSM48_CC_CAUSE_RESOURCE_UNAVAIL - } - }; - - mncc_set_cause(&mncc, GSM48_CAUSE_LOC_TRANS_NET, - GSM48_CC_CAUSE_RESOURCE_UNAVAIL); - mncc_tx_to_cc(mgcp_ctx->trans->net, MNCC_REL_REQ, &mncc); - } - - /* For the shutdown we have two options. Whenever it makes sense to - * send a DLCX to the MGW in order to be sure that the connection is - * properly cleaned up, the dlcx flag should be set. In other cases - * where a DLCX does not make sense (e.g. the MGW times out), halting - * directly is the better options. In those cases, the dlcx flag - * should not be set */ - if (dlcx && dlcx_possible) { - /* Fast-forward the FSM into call state. In this state the FSM - * expects either an EV_TEARDOWN or an EV_TEARDOWN_ERROR. When - * one of the two events is received a DLCX will be send to - * the MGW. After that. The FSM automatically halts but will - * still expect a call msc_mgcp_call_release() to be freed - * completely */ - osmo_fsm_inst_state_chg(fi, ST_CALL, 0, 0); - osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_TEARDOWN_ERROR, mgcp_ctx); - } else { - /* Halt the state machine immediately. The FSM will not be - * freed yet, we stil require the higher layers to call - * msc_mgcp_call_release() */ - osmo_fsm_inst_state_chg(fi, ST_HALT, 0, 0); - osmo_fsm_inst_dispatch(fi, EV_TEARDOWN_ERROR, mgcp_ctx); - } -} - -/* Timer callback to shut down in case of connectivity problems */ -static int fsm_timeout_cb(struct osmo_fsm_inst *fi) -{ - struct mgcp_ctx *mgcp_ctx = fi->priv; - struct mgcp_client *mgcp; - - OSMO_ASSERT(mgcp_ctx); - mgcp = mgcp_ctx->mgcp; - OSMO_ASSERT(mgcp); - - if (fi->T == MGCP_MGW_TIMEOUT_TIMER_NR) { - /* We were unable to communicate with the MGW, unfortunately - * there is no meaningful action we can take now other than - * giving up. */ - - /* Cancel the transaction that timed out */ - mgcp_client_cancel(mgcp, mgcp_ctx->mgw_pending_trans); - - /* halt of the FSM */ - handle_error(mgcp_ctx, MGCP_ERR_MGW_TIMEOUT, false); - } else if (fi->T == MGCP_RAN_TIMEOUT_TIMER_NR) { - /* If the logic that controls the RAN is unable to negotiate a - * connection, we presumably still have a working connection to - * the MGW, we will try to shut down gracefully. */ - handle_error(mgcp_ctx, MGCP_ERR_RAN_TIMEOUT, true); - } else if (fi->T == MGCP_REL_TIMEOUT_TIMER_NR) { - /* Under normal conditions, the MSC logic should always command - * to release the call at some point. However, the release may - * be missing due to errors in the MSC logic and we may have - * reached ST_HALT because of cascading errors and timeouts. In - * this and only in this case we will allow ST_HALT to free all - * context information on its own authority. */ - mgcp_ctx->free_ctx = true; - - /* Initiate self destruction of the FSM */ - osmo_fsm_inst_state_chg(fi, ST_HALT, 0, 0); - osmo_fsm_inst_dispatch(fi, EV_TEARDOWN, mgcp_ctx); - } else if (fi->T == MGCP_ASS_TIMEOUT_TIMER_NR) { - /* There may be rare cases in which the MSC is unable to - * complete the call assignment */ - handle_error(mgcp_ctx, MGCP_ERR_ASS_TIMEOUT, true); - } else { - /* Ther must not be any unsolicited timers in this FSM. If so, - * we have serious problem. */ - OSMO_ASSERT(false); - } - - return 0; -} - -static void mgw_crcx_ran_resp_cb(struct mgcp_response *r, void *priv); - -/* Callback for ST_CRCX_RAN: Send CRCX for RAN side to MGW */ -static void fsm_crcx_ran_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct mgcp_ctx *mgcp_ctx = data; - struct mgcp_client *mgcp; - struct mgcp_msg mgcp_msg; - struct msgb *msg; - int rc; - struct gsm_trans *trans; - struct ran_conn *conn; - - OSMO_ASSERT(mgcp_ctx); - mgcp = mgcp_ctx->mgcp; - OSMO_ASSERT(mgcp); - trans = mgcp_ctx->trans; - OSMO_ASSERT(trans); - conn = trans->conn; - OSMO_ASSERT(conn); - - /* NOTE: In case of error, we will not be able to perform any DLCX - * operation because until this point we do not have requested any - * endpoint yet. */ - - LOGPFSML(fi, LOGL_DEBUG, - "CRCX/RAN: creating connection for the RAN side on MGW endpoint:%s...\n", mgcp_ctx->rtp_endpoint); - - /* Generate MGCP message string */ - mgcp_msg = (struct mgcp_msg) { - .verb = MGCP_VERB_CRCX, - .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_MODE), - .call_id = mgcp_ctx->call_id, - .conn_mode = MGCP_CONN_RECV_ONLY - }; - if (osmo_strlcpy(mgcp_msg.endpoint, mgcp_client_rtpbridge_wildcard(mgcp), sizeof(mgcp_msg.endpoint)) >= - MGCP_ENDPOINT_MAXLEN) { - handle_error(mgcp_ctx, MGCP_ERR_TOOLONG, false); - return; - } - - /* HACK: We put the connection in loopback mode at the beginnig to - * trick the hNodeB into doing the IuUP negotiation with itself. - * This is a hack we need because osmo-mgw does not support IuUP yet, see OS#2459. */ -#ifdef BUILD_IU - if (conn->via_ran == OSMO_RAT_UTRAN_IU) - mgcp_msg.conn_mode = MGCP_CONN_LOOPBACK; -#endif - - msg = mgcp_msg_gen(mgcp, &mgcp_msg); - OSMO_ASSERT(msg); - - /* Transmit MGCP message to MGW */ - mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg); - rc = mgcp_client_tx(mgcp, msg, mgw_crcx_ran_resp_cb, mgcp_ctx); - if (rc < 0) { - handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL, false); - return; - } - - osmo_fsm_inst_state_chg(fi, ST_CRCX_CN, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR); -} - -/* Callback for MGCP-Client: handle response for RAN associated CRCX */ -static void mgw_crcx_ran_resp_cb(struct mgcp_response *r, void *priv) -{ - struct mgcp_ctx *mgcp_ctx = priv; - int rc; - struct gsm_trans *trans; - struct ran_conn *conn; - - /* NOTE: In case of error, we will not be able to perform any DLCX - * operation because until we either get a parseable message that - * contains an error code (no endpoint is seized in those cases) - * or we get an unparseable message. In this case we can not be - * sure, but we also can not draw any assumptions from unparseable - * messages. */ - - OSMO_ASSERT(mgcp_ctx); - trans = mgcp_ctx->trans; - OSMO_ASSERT(trans); - conn = trans->conn; - OSMO_ASSERT(conn); - - if (r->head.response_code != 200) { - LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR, - "CRCX/RAN: response yields error: %d %s\n", r->head.response_code, r->head.comment); - handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL, false); - return; - } - - /* memorize connection identifier and specific endpoint id */ - osmo_strlcpy(mgcp_ctx->conn_id_ran, r->head.conn_id, sizeof(mgcp_ctx->conn_id_ran)); - LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/RAN: MGW responded with CI: %s\n", mgcp_ctx->conn_id_ran); - osmo_strlcpy(mgcp_ctx->rtp_endpoint, r->head.endpoint, sizeof(mgcp_ctx->rtp_endpoint)); - LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/RAN: MGW assigned endpoint: %s\n", mgcp_ctx->rtp_endpoint); - - rc = mgcp_response_parse_params(r); - if (rc) { - LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR, "CRCX/RAN: Cannot parse response\n"); - handle_error(mgcp_ctx, MGCP_ERR_MGW_INVAL_RESP, false); - return; - } - - LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/RAN: MGW responded with address %s:%u\n", r->audio_ip, r->audio_port); - - conn->rtp.local_port_ran = r->audio_port; - osmo_strlcpy(conn->rtp.local_addr_ran, r->audio_ip, sizeof(conn->rtp.local_addr_ran)); - - /* Notify the FSM that we got the response. */ - osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_CRCX_RAN_RESP, mgcp_ctx); -} - -static void mgw_crcx_cn_resp_cb(struct mgcp_response *r, void *priv); - -/* Callback for ST_CRCX_CN: check MGW response and send CRCX for CN side to MGW */ -static void fsm_crcx_cn_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct mgcp_ctx *mgcp_ctx = data; - struct mgcp_client *mgcp; - struct mgcp_msg mgcp_msg; - struct msgb *msg; - int rc; - struct gsm_trans *trans; - struct ran_conn *conn; - - OSMO_ASSERT(mgcp_ctx); - mgcp = mgcp_ctx->mgcp; - OSMO_ASSERT(mgcp); - trans = mgcp_ctx->trans; - OSMO_ASSERT(trans); - conn = trans->conn; - OSMO_ASSERT(conn); - - switch (event) { - case EV_CRCX_RAN_RESP: - break; - default: - handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true); - return; - } - - LOGPFSML(fi, LOGL_DEBUG, - "CRCX/CN creating connection for the CN side on MGW endpoint:%s...\n", mgcp_ctx->rtp_endpoint); - - /* Generate MGCP message string */ - mgcp_msg = (struct mgcp_msg) { - .verb = MGCP_VERB_CRCX, - .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_MODE), - .call_id = mgcp_ctx->call_id, - .conn_mode = MGCP_CONN_RECV_ONLY - }; - if (osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->rtp_endpoint, sizeof(mgcp_msg.endpoint)) >= - MGCP_ENDPOINT_MAXLEN) { - handle_error(mgcp_ctx, MGCP_ERR_TOOLONG, true); - return; - } - - msg = mgcp_msg_gen(mgcp, &mgcp_msg); - OSMO_ASSERT(msg); - - /* Transmit MGCP message to MGW */ - mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg); - rc = mgcp_client_tx(mgcp, msg, mgw_crcx_cn_resp_cb, mgcp_ctx); - if (rc < 0) { - handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL, true); - return; - } - - osmo_fsm_inst_state_chg(fi, ST_CRCX_COMPL, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR); -} - -/* Callback for MGCP-Client: handle response for CN associated CRCX */ -static void mgw_crcx_cn_resp_cb(struct mgcp_response *r, void *priv) -{ - struct mgcp_ctx *mgcp_ctx = priv; - int rc; - struct gsm_trans *trans; - struct ran_conn *conn; - - OSMO_ASSERT(mgcp_ctx); - trans = mgcp_ctx->trans; - OSMO_ASSERT(trans); - conn = trans->conn; - OSMO_ASSERT(conn); - - if (r->head.response_code != 200) { - LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR, - "CRCX/CN: response yields error: %d %s\n", r->head.response_code, r->head.comment); - handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL, true); - return; - } - - /* memorize connection identifier */ - osmo_strlcpy(mgcp_ctx->conn_id_cn, r->head.conn_id, sizeof(mgcp_ctx->conn_id_cn)); - LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/CN: MGW responded with CI: %s\n", mgcp_ctx->conn_id_cn); - - rc = mgcp_response_parse_params(r); - if (rc) { - LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR, "CRCX/CN: Cannot parse response\n"); - handle_error(mgcp_ctx, MGCP_ERR_MGW_INVAL_RESP, true); - return; - } - - LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/CN: MGW responded with address %s:%u\n", r->audio_ip, r->audio_port); - - conn->rtp.local_port_cn = r->audio_port; - osmo_strlcpy(conn->rtp.local_addr_cn, r->audio_ip, sizeof(conn->rtp.local_addr_cn)); - - /* Notify the FSM that we got the response. */ - osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_CRCX_CN_RESP, mgcp_ctx); -} - -/* Callback for ST_CRCX_COMPL: check MGW response, start assignment */ -static void fsm_crcx_compl(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct mgcp_ctx *mgcp_ctx = data; - struct gsm_trans *trans; - struct ran_conn *conn; - - OSMO_ASSERT(mgcp_ctx); - trans = mgcp_ctx->trans; - OSMO_ASSERT(trans); - conn = trans->conn; - OSMO_ASSERT(conn); - - switch (event) { - case EV_CRCX_CN_RESP: - break; - default: - handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true); - return; - } - - /* Forward assignment request to A/RANAP */ - if (conn->via_ran == OSMO_RAT_UTRAN_IU) { -#ifdef BUILD_IU - /* Assign a voice channel via RANAP on 3G */ - if (iu_rab_act_cs(trans)) - goto error; -#else - LOGPFSML(fi, LOGL_ERROR, "Cannot send Iu RAB Assignment: built without Iu support\n"); - goto error; -#endif - } else if (conn->via_ran == OSMO_RAT_GERAN_A) { - /* Assign a voice channel via A on 2G */ - if (a_iface_tx_assignment(trans)) - goto error; - } else { - /* Unset or unimplemented new RAN type */ - LOGPFSML(fi, LOGL_ERROR, "Unknown RAN type: %d\n", conn->via_ran); - return; - } - - /* Respond back to MNCC (if requested) */ - if (trans->tch_rtp_create) { - if (gsm48_tch_rtp_create(trans)) - goto error; - } - - /* Note: When we reach this point then the situation is basically that - * we have two sides connected, both are in loopback. The local ports - * of the side pointing towards the BSS should be already communicated - * and we are waiting now the other end to pick up. */ - osmo_fsm_inst_state_chg(fi, ST_MDCX_CN, MGCP_RAN_TIMEOUT, MGCP_RAN_TIMEOUT_TIMER_NR); - return; - -error: - handle_error(mgcp_ctx, MGCP_ERR_ASSGMNT_FAIL, true); -} - -static void mgw_mdcx_cn_resp_cb(struct mgcp_response *r, void *priv); - -/* Callback for ST_MDCX_CN: send MDCX for RAN side to MGW */ -static void fsm_mdcx_cn_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct mgcp_ctx *mgcp_ctx = data; - struct mgcp_client *mgcp; - struct gsm_trans *trans; - struct ran_conn *conn; - struct mgcp_msg mgcp_msg; - struct msgb *msg; - int rc; - - OSMO_ASSERT(mgcp_ctx); - mgcp = mgcp_ctx->mgcp; - OSMO_ASSERT(mgcp); - trans = mgcp_ctx->trans; - OSMO_ASSERT(trans); - conn = trans->conn; - OSMO_ASSERT(conn); - - switch (event) { - case EV_CONNECT: - break; - default: - handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true); - return; - } - - LOGPFSML(fi, LOGL_DEBUG, - "MDCX/CN: completing connection for the CN side on MGW endpoint:%p, remote leg expects RTP input on address %s:%u\n", - mgcp_ctx->rtp_endpoint, conn->rtp.remote_addr_cn, conn->rtp.remote_port_cn); - - /* Generate MGCP message string */ - mgcp_msg = (struct mgcp_msg) { - .verb = MGCP_VERB_MDCX, - .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_ID | - MGCP_MSG_PRESENCE_CONN_MODE | MGCP_MSG_PRESENCE_AUDIO_IP | - MGCP_MSG_PRESENCE_AUDIO_PORT), - .call_id = mgcp_ctx->call_id, - .conn_id = mgcp_ctx->conn_id_cn, - .conn_mode = MGCP_CONN_RECV_SEND, - .audio_ip = conn->rtp.remote_addr_cn, - .audio_port = conn->rtp.remote_port_cn, - .codecs[0] = conn->rtp.codec_cn, - .codecs_len = 1 - }; - if (osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->rtp_endpoint, sizeof(mgcp_msg.endpoint)) >= - MGCP_ENDPOINT_MAXLEN) { - handle_error(mgcp_ctx, MGCP_ERR_TOOLONG, true); - return; - } - - msg = mgcp_msg_gen(mgcp, &mgcp_msg); - OSMO_ASSERT(msg); - - /* Transmit MGCP message to MGW */ - mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg); - rc = mgcp_client_tx(mgcp, msg, mgw_mdcx_cn_resp_cb, mgcp_ctx); - if (rc < 0) { - handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL, true); - return; - } - - osmo_fsm_inst_state_chg(fi, ST_MDCX_CN_COMPL, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR); -} - -/* Callback for MGCP-Client: handle response for CN associated CRCX */ -static void mgw_mdcx_cn_resp_cb(struct mgcp_response *r, void *priv) -{ - struct mgcp_ctx *mgcp_ctx = priv; - - OSMO_ASSERT(mgcp_ctx); - - if (r->head.response_code != 200) { - LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR, - "MDCX/CN: response yields error: %d %s\n", r->head.response_code, r->head.comment); - handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL, true); - return; - } - - /* Notify the FSM that we got the response. */ - osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_MDCX_CN_RESP, mgcp_ctx); -} - -/* Callback for ST_MDCX_CN_COMPL: wait for mgw response, move on with the MDCX - * for the RAN side if we already have valid IP/Port data for the RAN sided - * RTP stream. */ -static void fsm_mdcx_cn_compl_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct mgcp_ctx *mgcp_ctx = data; - struct ran_conn *conn; - struct gsm_trans *trans; - - OSMO_ASSERT(mgcp_ctx); - trans = mgcp_ctx->trans; - OSMO_ASSERT(trans); - conn = trans->conn; - OSMO_ASSERT(conn); - - switch (event) { - case EV_MDCX_CN_RESP: - break; - default: - handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true); - return; - } - - /* Enter MDCX phase, but we must be sure that the Assigmnet on the A or - * IuCS interface is complete (IP-Address and Port are valid) */ - osmo_fsm_inst_state_chg(fi, ST_MDCX_RAN, MGCP_ASS_TIMEOUT, MGCP_ASS_TIMEOUT_TIMER_NR); - - /* If we already have a valid remote port and IP-Address from the RAN side - * call leg, the assignment has been completed before we got here, so we - * may move on immediately */ - if (conn->rtp.remote_port_ran != 0 || strlen(conn->rtp.remote_addr_ran) > 0) - osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_ASSIGN, mgcp_ctx); -} - -static void mgw_mdcx_ran_resp_cb(struct mgcp_response *r, void *priv); - -/* Callback for ST_MDCX_RAN: wait for assignment completion, send MDCX for CN side to MGW */ -static void fsm_mdcx_ran_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct mgcp_ctx *mgcp_ctx = data; - struct mgcp_client *mgcp; - struct gsm_trans *trans; - struct ran_conn *conn; - struct mgcp_msg mgcp_msg; - struct msgb *msg; - int rc; - - OSMO_ASSERT(mgcp_ctx); - mgcp = mgcp_ctx->mgcp; - OSMO_ASSERT(mgcp); - trans = mgcp_ctx->trans; - OSMO_ASSERT(trans); - conn = trans->conn; - OSMO_ASSERT(conn); - - switch (event) { - case EV_ASSIGN: - break; - default: - handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true); - return; - } - - LOGPFSML(fi, LOGL_DEBUG, - "MDCX/RAN: completing connection for the CN side on MGW endpoint:%p, RAN expects RTP input on address %s:%u\n", - mgcp_ctx->rtp_endpoint, conn->rtp.remote_addr_ran, conn->rtp.remote_port_ran); - - /* Generate MGCP message string */ - mgcp_msg = (struct mgcp_msg) { - .verb = MGCP_VERB_MDCX, - .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_ID | - MGCP_MSG_PRESENCE_CONN_MODE | MGCP_MSG_PRESENCE_AUDIO_IP | - MGCP_MSG_PRESENCE_AUDIO_PORT), - .call_id = mgcp_ctx->call_id, - .conn_id = mgcp_ctx->conn_id_ran, - .conn_mode = MGCP_CONN_RECV_SEND, - .audio_ip = conn->rtp.remote_addr_ran, - .audio_port = conn->rtp.remote_port_ran, - .codecs[0] = conn->rtp.codec_ran, - .codecs_len = 1 - }; - if (osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->rtp_endpoint, sizeof(mgcp_msg.endpoint)) >= - MGCP_ENDPOINT_MAXLEN) { - handle_error(mgcp_ctx, MGCP_ERR_TOOLONG, true); - return; - } - - msg = mgcp_msg_gen(mgcp, &mgcp_msg); - OSMO_ASSERT(msg); - - /* Transmit MGCP message to MGW */ - mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg); - rc = mgcp_client_tx(mgcp, msg, mgw_mdcx_ran_resp_cb, mgcp_ctx); - if (rc < 0) { - handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL, true); - return; - } - - osmo_fsm_inst_state_chg(fi, ST_MDCX_RAN_COMPL, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR); -} - -/* Callback for MGCP-Client: handle response for CN associated CRCX */ -static void mgw_mdcx_ran_resp_cb(struct mgcp_response *r, void *priv) -{ - struct mgcp_ctx *mgcp_ctx = priv; - - OSMO_ASSERT(mgcp_ctx); - - if (r->head.response_code != 200) { - LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR, - "MDCX/RAN: response yields error: %d %s\n", r->head.response_code, r->head.comment); - handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL, true); - return; - } - - /* Notify the FSM that we got the response. */ - osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_MDCX_RAN_RESP, mgcp_ctx); -} - -/* Callback for ST_MDCX_RAN_COMPL: check MGW response */ -static void fsm_mdcx_ran_compl_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct mgcp_ctx *mgcp_ctx = data; - OSMO_ASSERT(mgcp_ctx); - - switch (event) { - case EV_MDCX_RAN_RESP: - break; - default: - handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true); - return; - } - - LOGPFSML(fi, LOGL_DEBUG, "call active, waiting for teardown...\n"); - osmo_fsm_inst_state_chg(fi, ST_CALL, 0, 0); -} - -static void mgw_dlcx_all_resp_cb(struct mgcp_response *r, void *priv); - -/* Callback for ST_CALL: call is active, send DLCX for both sides on teardown */ -static void fsm_call_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - - struct mgcp_ctx *mgcp_ctx = (struct mgcp_ctx *)data; - struct mgcp_client *mgcp; - struct mgcp_msg mgcp_msg; - struct msgb *msg; - int rc; - - OSMO_ASSERT(mgcp_ctx); - mgcp = mgcp_ctx->mgcp; - OSMO_ASSERT(mgcp); - - LOGPFSML(fi, LOGL_DEBUG, - "DLCX: removing connection for the RAN and CN side on MGW endpoint:%s...\n", mgcp_ctx->rtp_endpoint); - - /* Generate MGCP message string */ - mgcp_msg = (struct mgcp_msg) { - .verb = MGCP_VERB_DLCX, - .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID), - .call_id = mgcp_ctx->call_id - }; - if (osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->rtp_endpoint, sizeof(mgcp_msg.endpoint)) >= - MGCP_ENDPOINT_MAXLEN) { - handle_error(mgcp_ctx, MGCP_ERR_TOOLONG, true); - return; - } - - msg = mgcp_msg_gen(mgcp, &mgcp_msg); - OSMO_ASSERT(msg); - - /* Transmit MGCP message to MGW */ - mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg); - rc = mgcp_client_tx(mgcp, msg, mgw_dlcx_all_resp_cb, mgcp_ctx); - if (rc < 0) { - handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL, true); - return; - } - - osmo_fsm_inst_state_chg(fi, ST_HALT, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR); -} - -/* Callback for MGCP-Client: handle response for CN associated CRCX */ -static void mgw_dlcx_all_resp_cb(struct mgcp_response *r, void *priv) -{ - struct mgcp_ctx *mgcp_ctx = priv; - - OSMO_ASSERT(mgcp_ctx); - - /* DLCX is the only command where 250 is permitted as positive result */ - if (r->head.response_code != 200 && r->head.response_code != 250) { - LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR, - "DLCX: response yields error: %d %s\n", r->head.response_code, r->head.comment); - handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL, true); - return; - } - - /* Notify the FSM that we got the response. */ - osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_DLCX_ALL_RESP, mgcp_ctx); -} - -/* Callback for ST_HALT: Terminate the state machine */ -static void fsm_halt_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct mgcp_ctx *mgcp_ctx = data; - struct mgcp_client *mgcp; - - OSMO_ASSERT(mgcp_ctx); - mgcp = mgcp_ctx->mgcp; - OSMO_ASSERT(mgcp); - - /* NOTE: We must not free the context information now, we have to - * wait until msc_mgcp_call_release() is called. Then we are sure - * that the logic controlling us is fully aware that the context - * information is freed. If we would free early now the controlling - * logic might mistakenly think that the context info is still alive, - * so lets keep the context info until we are explicitly asked for - * throwing it away. */ - if (mgcp_ctx->free_ctx) { - /* Be sure that there is no pending MGW transaction */ - mgcp_client_cancel(mgcp, mgcp_ctx->mgw_pending_trans); - - /* Free FSM and its context information */ - osmo_fsm_inst_free(mgcp_ctx->fsm); - talloc_free(mgcp_ctx); - return; - } - - osmo_fsm_inst_state_chg(fi, ST_HALT, MGCP_REL_TIMEOUT, MGCP_REL_TIMEOUT_TIMER_NR); -} - -static struct osmo_fsm_state fsm_msc_mgcp_states[] = { - - /* Startup state machine, send CRCX for RAN side. */ - [ST_CRCX_RAN] = { - .in_event_mask = S(EV_INIT), - .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_CRCX_CN), - .name = OSMO_STRINGIFY(ST_CRCX_RAN), - .action = fsm_crcx_ran_cb, - }, - /* When the response to the RAN CRCX is received, then proceed with - sending the CRCX for CN side */ - [ST_CRCX_CN] = { - .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_CRCX_RAN_RESP), - .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_CRCX_COMPL), - .name = OSMO_STRINGIFY(ST_CRCX_CN), - .action = fsm_crcx_cn_cb, - }, - /* Complete the CRCX phase by starting the assignment. Depending on the - * RAT (Radio Access Technology), this will either trigger an - * Assignment Request on the A-Interface or an RAB-Assignment on the - * IU-interface */ - [ST_CRCX_COMPL] = { - .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_CRCX_CN_RESP), - .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_MDCX_CN), - .name = OSMO_STRINGIFY(ST_CRCX_COMPL), - .action = fsm_crcx_compl, - }, - /* Wait for MSC to complete the assignment request, when complete, we - * will enter the MDCX phase by sending an MDCX for the CN side to the - * MGW */ - [ST_MDCX_CN] = { - .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_CONNECT), - .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_MDCX_CN_COMPL), - .name = OSMO_STRINGIFY(ST_MDCX_CN), - .action = fsm_mdcx_cn_cb, - }, - /* We arrive in this state when the MDCX phase for the CN side has - * completed we will check the IP/Port of the RAN connection. If this - * data is valid we may continue with the MDCX phase for the RAN side. - * If not we wait until the assinment completes on the A or on the IuCS - * interface. The completion of the assignment will fill in the port and - * IP-Address of the RAN side and way may continue then. */ - [ST_MDCX_CN_COMPL] = { - .in_event_mask = S(EV_TEARDOWN) | S(EV_MDCX_CN_RESP), - .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_MDCX_RAN), - .name = OSMO_STRINGIFY(ST_MDCX_CN_COMPL), - .action = fsm_mdcx_cn_compl_cb, - }, - /* When the response for the CN MDCX is received, send the MDCX for the - * RAN side to the MGW */ - [ST_MDCX_RAN] = { - .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_ASSIGN), - .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_MDCX_RAN_COMPL), - .name = OSMO_STRINGIFY(ST_MDCX_RAN), - .action = fsm_mdcx_ran_cb, - }, - /* The RAN side MDCX phase is complete when the response is received - * from the MGW. The call is then active, we change to ST_CALL and wait - * there until the call ends. */ - [ST_MDCX_RAN_COMPL] = { - .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_MDCX_RAN_RESP), - .out_state_mask = S(ST_HALT) | S(ST_CALL), - .name = OSMO_STRINGIFY(ST_MDCX_RAN_COMPL), - .action = fsm_mdcx_ran_compl_cb, - }, - /* We are now in the active call phase, wait until the call is done - * and send a DLCX then to remove all connections from the MGW */ - [ST_CALL] = { - .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR), - .out_state_mask = S(ST_HALT), - .name = OSMO_STRINGIFY(ST_CALL), - .action = fsm_call_cb, - }, - /* When the MGW confirms that the connections are terminated, then halt - * the state machine. */ - [ST_HALT] = { - .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_DLCX_ALL_RESP), - .out_state_mask = S(ST_HALT), - .name = OSMO_STRINGIFY(ST_HALT), - .action = fsm_halt_cb, - }, -}; - -/* State machine definition */ -static struct osmo_fsm fsm_msc_mgcp = { - .name = "msc-mgcp", - .states = fsm_msc_mgcp_states, - .num_states = ARRAY_SIZE(fsm_msc_mgcp_states), - .log_subsys = DMGCP, - .timer_cb = fsm_timeout_cb, - .event_names = msc_mgcp_fsm_evt_names, -}; - -/* Try to invoke call assignment and set trans->cc.assignment_started flag if invoked. - * This is relevant for already ongoing calls -- scenario: - * - subscriber is in an active voice call, - * - another call is coming in. - * For the second call coming in, we must wait to establish RTP and assignment until the first call is CC-Disconnected. - */ -int msc_mgcp_try_call_assignment(struct gsm_trans *trans) -{ - struct ran_conn *conn = trans->conn; - if (trans->cc.assignment_started) - return 0; - if (conn->rtp.mgcp_ctx) { - LOGPFSMSL(conn->fi, DMGCP, LOGL_INFO, "Another call is already ongoing, not assigning yet\n"); - return 0; - } - LOGPFSMSL(conn->fi, DMGCP, LOGL_INFO, "Starting call assignment\n"); - trans->cc.assignment_started = true; - return msc_mgcp_call_assignment(trans); -} - -/* Notify that a new call begins. This will create a connection for the - * RAN and the CN on the MGW. - * Parameter: - * trans: transaction context. - * Returns -EINVAL on error, 0 on success. */ -int msc_mgcp_call_assignment(struct gsm_trans *trans) -{ - struct mgcp_ctx *mgcp_ctx; - static bool fsm_registered = false; - struct ran_conn *conn; - struct mgcp_client *mgcp; - - OSMO_ASSERT(trans); - - if (!trans->conn) { - LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid conn, call assignment failed\n", - vlr_subscr_name(trans->vsub)); - return -EINVAL; - } - - conn = trans->conn; - mgcp = conn->network->mgw.client; - OSMO_ASSERT(mgcp); - - if (conn->rtp.mgcp_ctx) { - LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) double assignment detected, dropping...\n", - vlr_subscr_name(trans->vsub)); - return -EINVAL; - } - -#ifdef BUILD_IU - /* FIXME: HACK. where to scope the RAB Id? At the conn / subscriber / ranap_ue_conn_ctx? */ - static uint8_t next_iu_rab_id = 1; - if (conn->via_ran == OSMO_RAT_UTRAN_IU) - conn->iu.rab_id = next_iu_rab_id++; -#endif - - /* Register the fsm description (if not already done) */ - if (fsm_registered == false) { - osmo_fsm_register(&fsm_msc_mgcp); - fsm_registered = true; - } - - /* Allocate and configure a new fsm instance */ - mgcp_ctx = talloc_zero(NULL, struct mgcp_ctx); - OSMO_ASSERT(mgcp_ctx); - - if (osmo_strlcpy(mgcp_ctx->rtp_endpoint, mgcp_client_rtpbridge_wildcard(mgcp), sizeof(mgcp_ctx->rtp_endpoint)) - >= sizeof(mgcp_ctx->rtp_endpoint)) { - talloc_free(mgcp_ctx); - LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) endpoint identifier exceeds maximum length: %s\n", - vlr_subscr_name(trans->vsub), osmo_quote_str(mgcp_client_rtpbridge_wildcard(mgcp), -1)); - return -EINVAL; - } - mgcp_ctx->fsm = osmo_fsm_inst_alloc(&fsm_msc_mgcp, NULL, NULL, LOGL_DEBUG, NULL); - OSMO_ASSERT(mgcp_ctx->fsm); - osmo_fsm_inst_update_id_f(mgcp_ctx->fsm, "%s_%s_trans%d", - vlr_subscr_name(trans->vsub), ran_conn_get_conn_id(conn), trans->transaction_id); - mgcp_ctx->fsm->priv = mgcp_ctx; - mgcp_ctx->mgcp = mgcp; - mgcp_ctx->trans = trans; - mgcp_ctx->call_id = trans->callref; - - /* start state machine */ - OSMO_ASSERT(mgcp_ctx->fsm->state == ST_CRCX_RAN); - osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_INIT, mgcp_ctx); - - conn->rtp.mgcp_ctx = mgcp_ctx; - - LOGP(DMGCP, LOGL_DEBUG, "(subscriber:%s) call assignment initiated\n", - vlr_subscr_name(conn->vsub)); - - return 0; -} - -/* Inform the FSM that the assignment (RAN connection) is now complete. - * Parameter: - * conn: RAN connection context. - * port: port number of the remote leg. - * addr: IP-address of the remote leg. - * Returns -EINVAL on error, 0 on success. */ -int msc_mgcp_ass_complete(struct ran_conn *conn, uint16_t port, char *addr) -{ - struct mgcp_ctx *mgcp_ctx; - - OSMO_ASSERT(conn); - - if (port == 0) { - LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid remote call leg port, assignment completion failed\n", - vlr_subscr_name(conn->vsub)); - return -EINVAL; - } - if (!addr || strlen(addr) <= 0) { - LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) missing remote call leg address, assignment completion failed\n", - vlr_subscr_name(conn->vsub)); - return -EINVAL; - } - - mgcp_ctx = conn->rtp.mgcp_ctx; - if (!mgcp_ctx) { - LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid mgcp context, assignment completion failed.\n", - vlr_subscr_name(conn->vsub)); - return -EINVAL; - } - - /* Memorize port and IP-Address of the remote RAN call leg. We need this - * information at latest when we enter the MDCX phase for the RAN side. */ - conn->rtp.remote_port_ran = port; - osmo_strlcpy(conn->rtp.remote_addr_ran, addr, sizeof(conn->rtp.remote_addr_ran)); - - LOGP(DMGCP, LOGL_DEBUG, "(subscriber:%s) assignment completed, rtp %s:%d.\n", - vlr_subscr_name(conn->vsub), conn->rtp.remote_addr_ran, port); - - /* Note: We only dispatch the event if we are really waiting for the - * assignment, if we are not yet waiting, there is no need to loudly - * broadcast an event that the all other states do not understand anyway */ - if (mgcp_ctx->fsm->state == ST_MDCX_RAN) - osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_ASSIGN, mgcp_ctx); - - return 0; -} - -/* Notify the MGCP context that Assignment failed. - * This will end the "ringing" on the other call leg, and will usually result in L3 and conn release (i.e. when no other - * transactions are still pending, which is usually the case). */ -int msc_mgcp_ass_fail(struct ran_conn *conn) -{ - struct mgcp_ctx *mgcp_ctx; - - OSMO_ASSERT(conn); - - mgcp_ctx = conn->rtp.mgcp_ctx; - if (!mgcp_ctx) - return -EINVAL; - - LOGPFSMSL(conn->fi, DMGCP, LOGL_ERROR, "Assignment failed\n"); - - osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_TEARDOWN_ERROR, mgcp_ctx); - return 0; -} - -/* Make the connection of a previously assigned call complete - * Parameter: - * trans: transaction context. - * port: port number of the remote leg. - * addr: IP-address of the remote leg. - * Returns -EINVAL on error, 0 on success. */ -int msc_mgcp_call_complete(struct gsm_trans *trans, uint16_t port, char *addr) -{ - struct mgcp_ctx *mgcp_ctx; - struct ran_conn *conn; - - OSMO_ASSERT(trans); - OSMO_ASSERT(addr); - - if (port == 0) { - LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid remote call leg port, call completion failed\n", - vlr_subscr_name(trans->vsub)); - return -EINVAL; - } - if (!addr || strlen(addr) <= 0) { - LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) missing remote call leg address, call completion failed\n", - vlr_subscr_name(trans->vsub)); - return -EINVAL; - } - if (!trans->conn) { - LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid conn, call completion failed\n", - vlr_subscr_name(trans->vsub)); - return -EINVAL; - } - if (!trans->conn->rtp.mgcp_ctx) { - LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid mgcp context, call completion failed.\n", - vlr_subscr_name(trans->vsub)); - return -EINVAL; - } - if (!trans->conn->rtp.mgcp_ctx->fsm) { - LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) no FSM, call completion failed\n", - vlr_subscr_name(trans->vsub)); - return -EINVAL; - } - - mgcp_ctx = trans->conn->rtp.mgcp_ctx; - - /* The FSM should already have passed all CRCX phases and be ready to move - * on with the MDCX phases. */ - if (mgcp_ctx->fsm->state != ST_MDCX_CN) { - LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid call state, call completion failed\n", - vlr_subscr_name(trans->vsub)); - return -EINVAL; - } - - conn = trans->conn; - osmo_strlcpy(conn->rtp.remote_addr_cn, addr, sizeof(conn->rtp.remote_addr_cn)); - conn->rtp.remote_port_cn = port; - - osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_CONNECT, mgcp_ctx); - - LOGP(DMGCP, LOGL_DEBUG, "(subscriber:%s) call completion initiated\n", - vlr_subscr_name(conn->vsub)); - - return 0; -} - -static struct gsm_trans *find_waiting_call(struct ran_conn *conn) -{ - struct gsm_trans *trans; - struct gsm_network *net = conn->network; - - llist_for_each_entry(trans, &net->trans_list, entry) { - if (trans->conn != conn) - continue; - if (trans->protocol != GSM48_PDISC_CC) - continue; - if (trans->cc.assignment_started) - continue; - return trans; - } - return NULL; -} - -/* Release ongoing call. - * Parameter: - * trans: connection context. - * Returns -EINVAL on error, 0 on success. */ -int msc_mgcp_call_release(struct gsm_trans *trans) -{ - struct mgcp_ctx *mgcp_ctx; - struct ran_conn *conn = trans->conn; - struct gsm_trans *waiting_trans; - - OSMO_ASSERT(trans); - - if (!conn) { - LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid conn, call release failed\n", - vlr_subscr_name(trans->vsub)); - return -EINVAL; - } - mgcp_ctx = conn->rtp.mgcp_ctx; - if (!mgcp_ctx) { - LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid mgcp context, call release failed.\n", - vlr_subscr_name(trans->vsub)); - return -EINVAL; - } - if (!mgcp_ctx->fsm) { - LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) no FSM, call release failed\n", - vlr_subscr_name(trans->vsub)); - return -EINVAL; - } - - if (mgcp_ctx->trans != trans) { - LOGP(DMGCP, LOGL_DEBUG, "(ti %02x %s) call release for background CC transaction\n", - trans->transaction_id, vlr_subscr_name(trans->vsub)); - return 0; - } - - LOGP(DMGCP, LOGL_DEBUG, "(ti %02x %s) Call release: tearing down MGW endpoint\n", - trans->transaction_id, vlr_subscr_name(trans->vsub)); - - /* Inform the FSM that as soon as it reaches ST_HALT it may free - * all context information immediately */ - mgcp_ctx->free_ctx = true; - - /* Initaite teardown, regardless of which state we are currently - * in */ - osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_TEARDOWN, mgcp_ctx); - - /* Prevent any further operation that is triggered from outside by - * overwriting the context pointer with NULL. The FSM will now - * take care for a graceful shutdown and when done it will free - * all related context information */ - conn->rtp.mgcp_ctx = NULL; - - /* If there is another call still waiting to be activated, this is the time when the mgcp_ctx is available again - * and the other call can start assigning. */ - waiting_trans = find_waiting_call(conn); - if (waiting_trans) { - LOGP(DMGCP, LOGL_DEBUG, "(ti %02x %s) Call waiting: starting Assignment\n", - waiting_trans->transaction_id, vlr_subscr_name(trans->vsub)); - msc_mgcp_try_call_assignment(waiting_trans); - } - - return 0; -} |