aboutsummaryrefslogtreecommitdiffstats
path: root/src/libmsc/msc_mgcp.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libmsc/msc_mgcp.c')
-rw-r--r--src/libmsc/msc_mgcp.c1254
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;
-}