2017-11-07 16:19:25 +00:00
|
|
|
/* (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
|
2018-02-08 13:05:43 +00:00
|
|
|
#define MGCP_RAN_TIMEOUT 120 /* in seconds */
|
2017-11-07 16:19:25 +00:00
|
|
|
#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,
|
2018-03-13 12:07:45 +00:00
|
|
|
MGCP_ERR_MGW_TIMEOUT,
|
2017-11-07 16:19:25 +00:00
|
|
|
MGCP_ERR_UNEXP_TEARDOWN,
|
|
|
|
MGCP_ERR_UNSUPP_ADDR_FMT,
|
|
|
|
MGCP_ERR_RAN_TIMEOUT,
|
|
|
|
MGCP_ERR_ASS_TIMEOUT,
|
2018-02-22 11:00:00 +00:00
|
|
|
MGCP_ERR_TOOLONG,
|
2017-11-07 16:19:25 +00:00
|
|
|
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"},
|
2018-03-13 12:07:45 +00:00
|
|
|
{MGCP_ERR_MGW_TIMEOUT, "request to MGW timed out"},
|
2017-11-07 16:19:25 +00:00
|
|
|
{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)"},
|
2018-02-22 11:00:00 +00:00
|
|
|
{MGCP_ERR_TOOLONG, "string value too long"},
|
2017-11-07 16:19:25 +00:00
|
|
|
{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,
|
|
|
|
};
|
|
|
|
|
2018-02-26 14:20:49 +00:00
|
|
|
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),
|
2018-12-18 21:03:46 +00:00
|
|
|
OSMO_VALUE_STRING(EV_MDCX_RAN_RESP),
|
|
|
|
OSMO_VALUE_STRING(EV_MDCX_CN_RESP),
|
2018-02-26 14:20:49 +00:00
|
|
|
OSMO_VALUE_STRING(EV_DLCX_ALL_RESP),
|
|
|
|
{0, NULL}
|
|
|
|
};
|
|
|
|
|
2017-11-07 16:19:25 +00:00
|
|
|
/* 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 */
|
2018-03-13 12:07:45 +00:00
|
|
|
#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)
|
2017-11-07 16:19:25 +00:00
|
|
|
{
|
2018-04-11 15:36:45 +00:00
|
|
|
bool dlcx_possible = true;
|
2017-11-07 16:19:25 +00:00
|
|
|
struct osmo_fsm_inst *fi;
|
2018-10-08 15:12:49 +00:00
|
|
|
struct gsm_mncc mncc;
|
2017-11-07 16:19:25 +00:00
|
|
|
|
|
|
|
OSMO_ASSERT(mgcp_ctx);
|
|
|
|
fi = mgcp_ctx->fsm;
|
|
|
|
OSMO_ASSERT(fi);
|
|
|
|
|
2018-04-11 15:36:45 +00:00
|
|
|
/* 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;
|
|
|
|
|
2017-11-07 16:19:25 +00:00
|
|
|
LOGPFSMLSRC(mgcp_ctx->fsm, LOGL_ERROR, file, line, "%s -- graceful shutdown...\n",
|
|
|
|
get_value_string(msc_mgcp_cause_codes_names, cause));
|
|
|
|
|
2018-09-28 12:26:35 +00:00
|
|
|
/* 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) {
|
2018-10-08 15:12:49 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-09-28 12:26:35 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2018-03-13 12:07:45 +00:00
|
|
|
/* 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 */
|
2018-04-11 15:36:45 +00:00
|
|
|
if (dlcx && dlcx_possible) {
|
2018-03-13 12:07:45 +00:00
|
|
|
/* 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);
|
|
|
|
}
|
2017-11-07 16:19:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
2018-03-13 12:07:45 +00:00
|
|
|
/* halt of the FSM */
|
|
|
|
handle_error(mgcp_ctx, MGCP_ERR_MGW_TIMEOUT, false);
|
2017-11-07 16:19:25 +00:00
|
|
|
} 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. */
|
2018-03-13 12:07:45 +00:00
|
|
|
handle_error(mgcp_ctx, MGCP_ERR_RAN_TIMEOUT, true);
|
2017-11-07 16:19:25 +00:00
|
|
|
} 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 */
|
2018-03-13 12:07:45 +00:00
|
|
|
handle_error(mgcp_ctx, MGCP_ERR_ASS_TIMEOUT, true);
|
2017-11-07 16:19:25 +00:00
|
|
|
} 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;
|
2018-05-29 10:02:38 +00:00
|
|
|
struct gsm_trans *trans;
|
rename gsm_subscriber_connection to ran_conn
In preparation for inter-BSC and inter-MSC handover, we need to separate the
subscriber management logic from the actual RAN connections. What better time
to finally rename gsm_subscriber_connection.
* Name choice:
In 2G, this is a connection to the BSS, but even though 3GPP TS commonly talk
of "BSS-A" and "BSS-B" when explaining handover, it's not good to call it
"bss_conn": in 3G a BSS is called RNS, IIUC.
The overall term for 2G (GERAN) and 3G (UTRAN) is RAN: Radio Access Network.
* Rationale:
A subscriber in the MSC so far has only one RAN connection, but e.g. for
inter-BSC handover, a second one needs to be created to handover to. Most of
the items in the former gsm_subscriber_connection are actually related to the
RAN, with only a few MM and RTP related items. So, as a first step, just rename
it to ran_conn, to cosmetically prepare for moving the not strictly RAN related
items away later.
Also:
- Rename some functions from msc_subscr_conn_* to ran_conn_*
- Rename "Subscr_Conn" FSM instance name to "RAN_conn"
- Rename SUBSCR_CONN_* to RAN_CONN_*
Change-Id: Ic595f7a558d3553c067f77dc67543ab59659707a
2018-11-29 21:37:51 +00:00
|
|
|
struct ran_conn *conn;
|
2018-05-29 10:02:38 +00:00
|
|
|
|
2017-11-07 16:19:25 +00:00
|
|
|
OSMO_ASSERT(mgcp_ctx);
|
|
|
|
mgcp = mgcp_ctx->mgcp;
|
|
|
|
OSMO_ASSERT(mgcp);
|
2018-05-29 10:02:38 +00:00
|
|
|
trans = mgcp_ctx->trans;
|
|
|
|
OSMO_ASSERT(trans);
|
|
|
|
conn = trans->conn;
|
|
|
|
OSMO_ASSERT(conn);
|
|
|
|
|
2018-04-11 15:36:45 +00:00
|
|
|
/* 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. */
|
|
|
|
|
2017-11-07 16:19:25 +00:00
|
|
|
LOGPFSML(fi, LOGL_DEBUG,
|
2018-04-11 13:23:30 +00:00
|
|
|
"CRCX/RAN: creating connection for the RAN side on MGW endpoint:%s...\n", mgcp_ctx->rtp_endpoint);
|
2017-11-07 16:19:25 +00:00
|
|
|
|
|
|
|
/* 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),
|
2018-02-08 13:15:59 +00:00
|
|
|
.call_id = mgcp_ctx->call_id,
|
2018-05-29 08:03:41 +00:00
|
|
|
.conn_mode = MGCP_CONN_RECV_ONLY
|
2017-11-07 16:19:25 +00:00
|
|
|
};
|
2018-12-19 00:01:46 +00:00
|
|
|
if (osmo_strlcpy(mgcp_msg.endpoint, mgcp_client_rtpbridge_wildcard(mgcp), sizeof(mgcp_msg.endpoint)) >=
|
2017-11-07 16:19:25 +00:00
|
|
|
MGCP_ENDPOINT_MAXLEN) {
|
2018-04-11 15:36:45 +00:00
|
|
|
handle_error(mgcp_ctx, MGCP_ERR_TOOLONG, false);
|
2017-11-07 16:19:25 +00:00
|
|
|
return;
|
|
|
|
}
|
2018-02-08 13:15:59 +00:00
|
|
|
|
2018-05-29 10:02:38 +00:00
|
|
|
/* HACK: We put the connection in loopback mode at the beginnig to
|
2018-07-26 15:44:13 +00:00
|
|
|
* 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. */
|
2018-05-29 10:02:38 +00:00
|
|
|
#ifdef BUILD_IU
|
2018-12-25 23:40:18 +00:00
|
|
|
if (conn->via_ran == OSMO_RAT_UTRAN_IU)
|
2018-05-29 10:02:38 +00:00
|
|
|
mgcp_msg.conn_mode = MGCP_CONN_LOOPBACK;
|
|
|
|
#endif
|
|
|
|
|
2017-11-07 16:19:25 +00:00
|
|
|
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) {
|
2018-04-11 15:36:45 +00:00
|
|
|
handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL, false);
|
2017-11-07 16:19:25 +00:00
|
|
|
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;
|
rename gsm_subscriber_connection to ran_conn
In preparation for inter-BSC and inter-MSC handover, we need to separate the
subscriber management logic from the actual RAN connections. What better time
to finally rename gsm_subscriber_connection.
* Name choice:
In 2G, this is a connection to the BSS, but even though 3GPP TS commonly talk
of "BSS-A" and "BSS-B" when explaining handover, it's not good to call it
"bss_conn": in 3G a BSS is called RNS, IIUC.
The overall term for 2G (GERAN) and 3G (UTRAN) is RAN: Radio Access Network.
* Rationale:
A subscriber in the MSC so far has only one RAN connection, but e.g. for
inter-BSC handover, a second one needs to be created to handover to. Most of
the items in the former gsm_subscriber_connection are actually related to the
RAN, with only a few MM and RTP related items. So, as a first step, just rename
it to ran_conn, to cosmetically prepare for moving the not strictly RAN related
items away later.
Also:
- Rename some functions from msc_subscr_conn_* to ran_conn_*
- Rename "Subscr_Conn" FSM instance name to "RAN_conn"
- Rename SUBSCR_CONN_* to RAN_CONN_*
Change-Id: Ic595f7a558d3553c067f77dc67543ab59659707a
2018-11-29 21:37:51 +00:00
|
|
|
struct ran_conn *conn;
|
2017-11-07 16:19:25 +00:00
|
|
|
|
2018-04-11 15:36:45 +00:00
|
|
|
/* 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. */
|
|
|
|
|
2017-11-07 16:19:25 +00:00
|
|
|
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);
|
2018-04-11 15:36:45 +00:00
|
|
|
handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL, false);
|
2017-11-07 16:19:25 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-02-08 13:15:59 +00:00
|
|
|
/* memorize connection identifier and specific endpoint id */
|
2017-11-07 16:19:25 +00:00
|
|
|
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);
|
2018-02-08 13:15:59 +00:00
|
|
|
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);
|
2017-11-07 16:19:25 +00:00
|
|
|
|
|
|
|
rc = mgcp_response_parse_params(r);
|
|
|
|
if (rc) {
|
|
|
|
LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR, "CRCX/RAN: Cannot parse response\n");
|
2018-04-11 15:36:45 +00:00
|
|
|
handle_error(mgcp_ctx, MGCP_ERR_MGW_INVAL_RESP, false);
|
2017-11-07 16:19:25 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-12-18 21:04:40 +00:00
|
|
|
LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/RAN: MGW responded with address %s:%u\n", r->audio_ip, r->audio_port);
|
2017-11-07 16:19:25 +00:00
|
|
|
|
|
|
|
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;
|
2018-05-29 10:02:38 +00:00
|
|
|
struct gsm_trans *trans;
|
rename gsm_subscriber_connection to ran_conn
In preparation for inter-BSC and inter-MSC handover, we need to separate the
subscriber management logic from the actual RAN connections. What better time
to finally rename gsm_subscriber_connection.
* Name choice:
In 2G, this is a connection to the BSS, but even though 3GPP TS commonly talk
of "BSS-A" and "BSS-B" when explaining handover, it's not good to call it
"bss_conn": in 3G a BSS is called RNS, IIUC.
The overall term for 2G (GERAN) and 3G (UTRAN) is RAN: Radio Access Network.
* Rationale:
A subscriber in the MSC so far has only one RAN connection, but e.g. for
inter-BSC handover, a second one needs to be created to handover to. Most of
the items in the former gsm_subscriber_connection are actually related to the
RAN, with only a few MM and RTP related items. So, as a first step, just rename
it to ran_conn, to cosmetically prepare for moving the not strictly RAN related
items away later.
Also:
- Rename some functions from msc_subscr_conn_* to ran_conn_*
- Rename "Subscr_Conn" FSM instance name to "RAN_conn"
- Rename SUBSCR_CONN_* to RAN_CONN_*
Change-Id: Ic595f7a558d3553c067f77dc67543ab59659707a
2018-11-29 21:37:51 +00:00
|
|
|
struct ran_conn *conn;
|
2018-05-29 10:02:38 +00:00
|
|
|
|
2017-11-07 16:19:25 +00:00
|
|
|
OSMO_ASSERT(mgcp_ctx);
|
|
|
|
mgcp = mgcp_ctx->mgcp;
|
|
|
|
OSMO_ASSERT(mgcp);
|
2018-05-29 10:02:38 +00:00
|
|
|
trans = mgcp_ctx->trans;
|
|
|
|
OSMO_ASSERT(trans);
|
|
|
|
conn = trans->conn;
|
|
|
|
OSMO_ASSERT(conn);
|
|
|
|
|
2017-11-07 16:19:25 +00:00
|
|
|
switch (event) {
|
|
|
|
case EV_CRCX_RAN_RESP:
|
|
|
|
break;
|
|
|
|
default:
|
2018-03-13 12:07:45 +00:00
|
|
|
handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true);
|
2017-11-07 16:19:25 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
LOGPFSML(fi, LOGL_DEBUG,
|
2018-04-11 13:23:30 +00:00
|
|
|
"CRCX/CN creating connection for the CN side on MGW endpoint:%s...\n", mgcp_ctx->rtp_endpoint);
|
2017-11-07 16:19:25 +00:00
|
|
|
|
|
|
|
/* 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),
|
2018-02-08 13:15:59 +00:00
|
|
|
.call_id = mgcp_ctx->call_id,
|
2018-05-29 08:03:41 +00:00
|
|
|
.conn_mode = MGCP_CONN_RECV_ONLY
|
2017-11-07 16:19:25 +00:00
|
|
|
};
|
2018-02-08 13:15:59 +00:00
|
|
|
if (osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->rtp_endpoint, sizeof(mgcp_msg.endpoint)) >=
|
2017-11-07 16:19:25 +00:00
|
|
|
MGCP_ENDPOINT_MAXLEN) {
|
2018-02-22 11:00:00 +00:00
|
|
|
handle_error(mgcp_ctx, MGCP_ERR_TOOLONG, true);
|
2017-11-07 16:19:25 +00:00
|
|
|
return;
|
|
|
|
}
|
2018-02-08 13:15:59 +00:00
|
|
|
|
2017-11-07 16:19:25 +00:00
|
|
|
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) {
|
2018-03-13 12:07:45 +00:00
|
|
|
handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL, true);
|
2017-11-07 16:19:25 +00:00
|
|
|
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;
|
rename gsm_subscriber_connection to ran_conn
In preparation for inter-BSC and inter-MSC handover, we need to separate the
subscriber management logic from the actual RAN connections. What better time
to finally rename gsm_subscriber_connection.
* Name choice:
In 2G, this is a connection to the BSS, but even though 3GPP TS commonly talk
of "BSS-A" and "BSS-B" when explaining handover, it's not good to call it
"bss_conn": in 3G a BSS is called RNS, IIUC.
The overall term for 2G (GERAN) and 3G (UTRAN) is RAN: Radio Access Network.
* Rationale:
A subscriber in the MSC so far has only one RAN connection, but e.g. for
inter-BSC handover, a second one needs to be created to handover to. Most of
the items in the former gsm_subscriber_connection are actually related to the
RAN, with only a few MM and RTP related items. So, as a first step, just rename
it to ran_conn, to cosmetically prepare for moving the not strictly RAN related
items away later.
Also:
- Rename some functions from msc_subscr_conn_* to ran_conn_*
- Rename "Subscr_Conn" FSM instance name to "RAN_conn"
- Rename SUBSCR_CONN_* to RAN_CONN_*
Change-Id: Ic595f7a558d3553c067f77dc67543ab59659707a
2018-11-29 21:37:51 +00:00
|
|
|
struct ran_conn *conn;
|
2017-11-07 16:19:25 +00:00
|
|
|
|
|
|
|
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);
|
2018-03-13 12:07:45 +00:00
|
|
|
handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL, true);
|
2017-11-07 16:19:25 +00:00
|
|
|
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");
|
2018-03-13 12:07:45 +00:00
|
|
|
handle_error(mgcp_ctx, MGCP_ERR_MGW_INVAL_RESP, true);
|
2017-11-07 16:19:25 +00:00
|
|
|
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;
|
rename gsm_subscriber_connection to ran_conn
In preparation for inter-BSC and inter-MSC handover, we need to separate the
subscriber management logic from the actual RAN connections. What better time
to finally rename gsm_subscriber_connection.
* Name choice:
In 2G, this is a connection to the BSS, but even though 3GPP TS commonly talk
of "BSS-A" and "BSS-B" when explaining handover, it's not good to call it
"bss_conn": in 3G a BSS is called RNS, IIUC.
The overall term for 2G (GERAN) and 3G (UTRAN) is RAN: Radio Access Network.
* Rationale:
A subscriber in the MSC so far has only one RAN connection, but e.g. for
inter-BSC handover, a second one needs to be created to handover to. Most of
the items in the former gsm_subscriber_connection are actually related to the
RAN, with only a few MM and RTP related items. So, as a first step, just rename
it to ran_conn, to cosmetically prepare for moving the not strictly RAN related
items away later.
Also:
- Rename some functions from msc_subscr_conn_* to ran_conn_*
- Rename "Subscr_Conn" FSM instance name to "RAN_conn"
- Rename SUBSCR_CONN_* to RAN_CONN_*
Change-Id: Ic595f7a558d3553c067f77dc67543ab59659707a
2018-11-29 21:37:51 +00:00
|
|
|
struct ran_conn *conn;
|
2017-11-07 16:19:25 +00:00
|
|
|
|
|
|
|
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:
|
2018-03-13 12:07:45 +00:00
|
|
|
handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true);
|
2017-11-07 16:19:25 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Forward assignment request to A/RANAP */
|
2018-12-25 23:40:18 +00:00
|
|
|
if (conn->via_ran == OSMO_RAT_UTRAN_IU) {
|
2017-11-07 16:19:25 +00:00
|
|
|
#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
|
2018-12-25 23:40:18 +00:00
|
|
|
} else if (conn->via_ran == OSMO_RAT_GERAN_A) {
|
2017-11-07 16:19:25 +00:00
|
|
|
/* 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
|
2018-02-08 13:05:43 +00:00
|
|
|
* and we are waiting now the other end to pick up. */
|
2017-11-07 16:19:25 +00:00
|
|
|
osmo_fsm_inst_state_chg(fi, ST_MDCX_CN, MGCP_RAN_TIMEOUT, MGCP_RAN_TIMEOUT_TIMER_NR);
|
|
|
|
return;
|
|
|
|
|
|
|
|
error:
|
2018-03-13 12:07:45 +00:00
|
|
|
handle_error(mgcp_ctx, MGCP_ERR_ASSGMNT_FAIL, true);
|
2017-11-07 16:19:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
rename gsm_subscriber_connection to ran_conn
In preparation for inter-BSC and inter-MSC handover, we need to separate the
subscriber management logic from the actual RAN connections. What better time
to finally rename gsm_subscriber_connection.
* Name choice:
In 2G, this is a connection to the BSS, but even though 3GPP TS commonly talk
of "BSS-A" and "BSS-B" when explaining handover, it's not good to call it
"bss_conn": in 3G a BSS is called RNS, IIUC.
The overall term for 2G (GERAN) and 3G (UTRAN) is RAN: Radio Access Network.
* Rationale:
A subscriber in the MSC so far has only one RAN connection, but e.g. for
inter-BSC handover, a second one needs to be created to handover to. Most of
the items in the former gsm_subscriber_connection are actually related to the
RAN, with only a few MM and RTP related items. So, as a first step, just rename
it to ran_conn, to cosmetically prepare for moving the not strictly RAN related
items away later.
Also:
- Rename some functions from msc_subscr_conn_* to ran_conn_*
- Rename "Subscr_Conn" FSM instance name to "RAN_conn"
- Rename SUBSCR_CONN_* to RAN_CONN_*
Change-Id: Ic595f7a558d3553c067f77dc67543ab59659707a
2018-11-29 21:37:51 +00:00
|
|
|
struct ran_conn *conn;
|
2017-11-07 16:19:25 +00:00
|
|
|
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:
|
2018-03-13 12:07:45 +00:00
|
|
|
handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true);
|
2017-11-07 16:19:25 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
LOGPFSML(fi, LOGL_DEBUG,
|
2018-03-17 00:54:32 +00:00
|
|
|
"MDCX/CN: completing connection for the CN side on MGW endpoint:%p, remote leg expects RTP input on address %s:%u\n",
|
2017-11-07 16:19:25 +00:00
|
|
|
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),
|
2018-02-08 13:15:59 +00:00
|
|
|
.call_id = mgcp_ctx->call_id,
|
2017-11-07 16:19:25 +00:00
|
|
|
.conn_id = mgcp_ctx->conn_id_cn,
|
|
|
|
.conn_mode = MGCP_CONN_RECV_SEND,
|
|
|
|
.audio_ip = conn->rtp.remote_addr_cn,
|
2018-08-07 11:00:14 +00:00
|
|
|
.audio_port = conn->rtp.remote_port_cn,
|
|
|
|
.codecs[0] = conn->rtp.codec_cn,
|
|
|
|
.codecs_len = 1
|
2017-11-07 16:19:25 +00:00
|
|
|
};
|
2018-02-08 13:15:59 +00:00
|
|
|
if (osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->rtp_endpoint, sizeof(mgcp_msg.endpoint)) >=
|
2017-11-07 16:19:25 +00:00
|
|
|
MGCP_ENDPOINT_MAXLEN) {
|
2018-02-22 11:00:00 +00:00
|
|
|
handle_error(mgcp_ctx, MGCP_ERR_TOOLONG, true);
|
2017-11-07 16:19:25 +00:00
|
|
|
return;
|
|
|
|
}
|
2018-02-08 13:15:59 +00:00
|
|
|
|
2017-11-07 16:19:25 +00:00
|
|
|
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) {
|
2018-03-13 12:07:45 +00:00
|
|
|
handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL, true);
|
2017-11-07 16:19:25 +00:00
|
|
|
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);
|
2018-03-13 12:07:45 +00:00
|
|
|
handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL, true);
|
2017-11-07 16:19:25 +00:00
|
|
|
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;
|
rename gsm_subscriber_connection to ran_conn
In preparation for inter-BSC and inter-MSC handover, we need to separate the
subscriber management logic from the actual RAN connections. What better time
to finally rename gsm_subscriber_connection.
* Name choice:
In 2G, this is a connection to the BSS, but even though 3GPP TS commonly talk
of "BSS-A" and "BSS-B" when explaining handover, it's not good to call it
"bss_conn": in 3G a BSS is called RNS, IIUC.
The overall term for 2G (GERAN) and 3G (UTRAN) is RAN: Radio Access Network.
* Rationale:
A subscriber in the MSC so far has only one RAN connection, but e.g. for
inter-BSC handover, a second one needs to be created to handover to. Most of
the items in the former gsm_subscriber_connection are actually related to the
RAN, with only a few MM and RTP related items. So, as a first step, just rename
it to ran_conn, to cosmetically prepare for moving the not strictly RAN related
items away later.
Also:
- Rename some functions from msc_subscr_conn_* to ran_conn_*
- Rename "Subscr_Conn" FSM instance name to "RAN_conn"
- Rename SUBSCR_CONN_* to RAN_CONN_*
Change-Id: Ic595f7a558d3553c067f77dc67543ab59659707a
2018-11-29 21:37:51 +00:00
|
|
|
struct ran_conn *conn;
|
2017-11-07 16:19:25 +00:00
|
|
|
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:
|
2018-03-13 12:07:45 +00:00
|
|
|
handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true);
|
2017-11-07 16:19:25 +00:00
|
|
|
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;
|
rename gsm_subscriber_connection to ran_conn
In preparation for inter-BSC and inter-MSC handover, we need to separate the
subscriber management logic from the actual RAN connections. What better time
to finally rename gsm_subscriber_connection.
* Name choice:
In 2G, this is a connection to the BSS, but even though 3GPP TS commonly talk
of "BSS-A" and "BSS-B" when explaining handover, it's not good to call it
"bss_conn": in 3G a BSS is called RNS, IIUC.
The overall term for 2G (GERAN) and 3G (UTRAN) is RAN: Radio Access Network.
* Rationale:
A subscriber in the MSC so far has only one RAN connection, but e.g. for
inter-BSC handover, a second one needs to be created to handover to. Most of
the items in the former gsm_subscriber_connection are actually related to the
RAN, with only a few MM and RTP related items. So, as a first step, just rename
it to ran_conn, to cosmetically prepare for moving the not strictly RAN related
items away later.
Also:
- Rename some functions from msc_subscr_conn_* to ran_conn_*
- Rename "Subscr_Conn" FSM instance name to "RAN_conn"
- Rename SUBSCR_CONN_* to RAN_CONN_*
Change-Id: Ic595f7a558d3553c067f77dc67543ab59659707a
2018-11-29 21:37:51 +00:00
|
|
|
struct ran_conn *conn;
|
2017-11-07 16:19:25 +00:00
|
|
|
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:
|
2018-03-13 12:07:45 +00:00
|
|
|
handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true);
|
2017-11-07 16:19:25 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
LOGPFSML(fi, LOGL_DEBUG,
|
2018-03-17 00:54:32 +00:00
|
|
|
"MDCX/RAN: completing connection for the CN side on MGW endpoint:%p, RAN expects RTP input on address %s:%u\n",
|
2017-11-07 16:19:25 +00:00
|
|
|
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),
|
2018-02-08 13:15:59 +00:00
|
|
|
.call_id = mgcp_ctx->call_id,
|
2017-11-07 16:19:25 +00:00
|
|
|
.conn_id = mgcp_ctx->conn_id_ran,
|
|
|
|
.conn_mode = MGCP_CONN_RECV_SEND,
|
|
|
|
.audio_ip = conn->rtp.remote_addr_ran,
|
2018-08-07 11:00:14 +00:00
|
|
|
.audio_port = conn->rtp.remote_port_ran,
|
|
|
|
.codecs[0] = conn->rtp.codec_ran,
|
|
|
|
.codecs_len = 1
|
2017-11-07 16:19:25 +00:00
|
|
|
};
|
2018-02-08 13:15:59 +00:00
|
|
|
if (osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->rtp_endpoint, sizeof(mgcp_msg.endpoint)) >=
|
2017-11-07 16:19:25 +00:00
|
|
|
MGCP_ENDPOINT_MAXLEN) {
|
2018-02-22 11:00:00 +00:00
|
|
|
handle_error(mgcp_ctx, MGCP_ERR_TOOLONG, true);
|
2017-11-07 16:19:25 +00:00
|
|
|
return;
|
|
|
|
}
|
2018-02-08 13:15:59 +00:00
|
|
|
|
2017-11-07 16:19:25 +00:00
|
|
|
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) {
|
2018-03-13 12:07:45 +00:00
|
|
|
handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL, true);
|
2017-11-07 16:19:25 +00:00
|
|
|
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);
|
2018-03-13 12:07:45 +00:00
|
|
|
handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL, true);
|
2017-11-07 16:19:25 +00:00
|
|
|
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:
|
2018-03-13 12:07:45 +00:00
|
|
|
handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true);
|
2017-11-07 16:19:25 +00:00
|
|
|
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,
|
2018-04-11 13:23:30 +00:00
|
|
|
"DLCX: removing connection for the RAN and CN side on MGW endpoint:%s...\n", mgcp_ctx->rtp_endpoint);
|
2017-11-07 16:19:25 +00:00
|
|
|
|
|
|
|
/* Generate MGCP message string */
|
|
|
|
mgcp_msg = (struct mgcp_msg) {
|
|
|
|
.verb = MGCP_VERB_DLCX,
|
|
|
|
.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID),
|
2018-02-08 13:15:59 +00:00
|
|
|
.call_id = mgcp_ctx->call_id
|
2017-11-07 16:19:25 +00:00
|
|
|
};
|
2018-02-08 13:15:59 +00:00
|
|
|
if (osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->rtp_endpoint, sizeof(mgcp_msg.endpoint)) >=
|
2017-11-07 16:19:25 +00:00
|
|
|
MGCP_ENDPOINT_MAXLEN) {
|
2018-02-22 11:00:00 +00:00
|
|
|
handle_error(mgcp_ctx, MGCP_ERR_TOOLONG, true);
|
2017-11-07 16:19:25 +00:00
|
|
|
return;
|
|
|
|
}
|
2018-02-08 13:15:59 +00:00
|
|
|
|
2017-11-07 16:19:25 +00:00
|
|
|
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) {
|
2018-03-13 12:07:45 +00:00
|
|
|
handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL, true);
|
2017-11-07 16:19:25 +00:00
|
|
|
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);
|
|
|
|
|
2018-02-10 09:43:38 +00:00
|
|
|
/* DLCX is the only command where 250 is permitted as positive result */
|
|
|
|
if (r->head.response_code != 200 && r->head.response_code != 250) {
|
2017-11-07 16:19:25 +00:00
|
|
|
LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
|
|
|
|
"DLCX: response yields error: %d %s\n", r->head.response_code, r->head.comment);
|
2018-03-13 12:07:45 +00:00
|
|
|
handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL, true);
|
2017-11-07 16:19:25 +00:00
|
|
|
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;
|
2018-03-14 12:37:44 +00:00
|
|
|
struct mgcp_client *mgcp;
|
2017-11-07 16:19:25 +00:00
|
|
|
|
|
|
|
OSMO_ASSERT(mgcp_ctx);
|
2018-03-14 12:37:44 +00:00
|
|
|
mgcp = mgcp_ctx->mgcp;
|
|
|
|
OSMO_ASSERT(mgcp);
|
2017-11-07 16:19:25 +00:00
|
|
|
|
|
|
|
/* 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) {
|
2018-03-14 12:37:44 +00:00
|
|
|
/* Be sure that there is no pending MGW transaction */
|
|
|
|
mgcp_client_cancel(mgcp, mgcp_ctx->mgw_pending_trans);
|
|
|
|
|
|
|
|
/* Free FSM and its context information */
|
2017-11-07 16:19:25 +00:00
|
|
|
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 = {
|
2018-12-18 21:05:24 +00:00
|
|
|
.name = "msc-mgcp",
|
2017-11-07 16:19:25 +00:00
|
|
|
.states = fsm_msc_mgcp_states,
|
|
|
|
.num_states = ARRAY_SIZE(fsm_msc_mgcp_states),
|
|
|
|
.log_subsys = DMGCP,
|
|
|
|
.timer_cb = fsm_timeout_cb,
|
2018-02-26 14:20:49 +00:00
|
|
|
.event_names = msc_mgcp_fsm_evt_names,
|
2017-11-07 16:19:25 +00:00
|
|
|
};
|
|
|
|
|
2018-12-20 02:03:44 +00:00
|
|
|
/* Try to invoke call assignment and set trans->cc.assignment_started flag if invoked.
|
2018-12-20 01:57:56 +00:00
|
|
|
* 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;
|
2018-12-20 02:03:44 +00:00
|
|
|
if (trans->cc.assignment_started)
|
2018-12-20 01:57:56 +00:00
|
|
|
return 0;
|
fix: incoming call during ongoing call
If a call is already busy and another call is coming in, do not try to
immediately assign an lchan (before this patch, it fails because there already
is an mgcp_ctx for the conn). Leave the second CC transaction waiting.
When a call is hung up, as soon as the old mgcp_ctx is discarded, look for
other CC transactions that are waiting. If there is one, trigger assignment, so
a new mgcp_ctx is set up for the new call.
This fixes the following scenario:
- from A, call B.
- from C, call B; B rings during ongoing call.
- in B, pick up the call, choose to drop the old call.
After this patch, and with osmo-bsc patch with change-id
I0c00ec2c120e5008281755adcd4944a3ce4d8355
we are now able to talk to the new caller.
I currently haven't tested yet what happens if there is *three* peers trying to
talk to the same number, running out of lab phones (not really, just not
bothering now). Possibly we should be taking over the particular call indicated
by the CC TI; instead, the current patch version takes on whichever waiting
call it finds first. This is fine if *one* additional call comes in on an
ongoing call, and this is already a huge improvement to what we had before.
Related: OS#3735
Change-Id: I0ba216b737909e92080a722db26e3577726c63cb
2018-12-20 01:59:32 +00:00
|
|
|
if (conn->rtp.mgcp_ctx) {
|
|
|
|
LOGPFSMSL(conn->fi, DMGCP, LOGL_INFO, "Another call is already ongoing, not assigning yet\n");
|
|
|
|
return 0;
|
|
|
|
}
|
2018-12-20 01:57:56 +00:00
|
|
|
LOGPFSMSL(conn->fi, DMGCP, LOGL_INFO, "Starting call assignment\n");
|
2018-12-20 02:03:44 +00:00
|
|
|
trans->cc.assignment_started = true;
|
2018-12-20 01:57:56 +00:00
|
|
|
return msc_mgcp_call_assignment(trans);
|
|
|
|
}
|
|
|
|
|
2017-11-07 16:19:25 +00:00
|
|
|
/* 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;
|
rename gsm_subscriber_connection to ran_conn
In preparation for inter-BSC and inter-MSC handover, we need to separate the
subscriber management logic from the actual RAN connections. What better time
to finally rename gsm_subscriber_connection.
* Name choice:
In 2G, this is a connection to the BSS, but even though 3GPP TS commonly talk
of "BSS-A" and "BSS-B" when explaining handover, it's not good to call it
"bss_conn": in 3G a BSS is called RNS, IIUC.
The overall term for 2G (GERAN) and 3G (UTRAN) is RAN: Radio Access Network.
* Rationale:
A subscriber in the MSC so far has only one RAN connection, but e.g. for
inter-BSC handover, a second one needs to be created to handover to. Most of
the items in the former gsm_subscriber_connection are actually related to the
RAN, with only a few MM and RTP related items. So, as a first step, just rename
it to ran_conn, to cosmetically prepare for moving the not strictly RAN related
items away later.
Also:
- Rename some functions from msc_subscr_conn_* to ran_conn_*
- Rename "Subscr_Conn" FSM instance name to "RAN_conn"
- Rename SUBSCR_CONN_* to RAN_CONN_*
Change-Id: Ic595f7a558d3553c067f77dc67543ab59659707a
2018-11-29 21:37:51 +00:00
|
|
|
struct ran_conn *conn;
|
2017-11-07 16:19:25 +00:00
|
|
|
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;
|
2018-12-25 23:40:18 +00:00
|
|
|
if (conn->via_ran == OSMO_RAT_UTRAN_IU)
|
2017-11-07 16:19:25 +00:00
|
|
|
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);
|
2018-12-19 00:01:46 +00:00
|
|
|
|
|
|
|
if (osmo_strlcpy(mgcp_ctx->rtp_endpoint, mgcp_client_rtpbridge_wildcard(mgcp), sizeof(mgcp_ctx->rtp_endpoint))
|
|
|
|
>= sizeof(mgcp_ctx->rtp_endpoint)) {
|
2018-02-08 13:15:59 +00:00
|
|
|
talloc_free(mgcp_ctx);
|
2018-12-19 00:01:46 +00:00
|
|
|
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));
|
2018-02-08 13:15:59 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
2018-12-18 21:05:24 +00:00
|
|
|
mgcp_ctx->fsm = osmo_fsm_inst_alloc(&fsm_msc_mgcp, NULL, NULL, LOGL_DEBUG, NULL);
|
2017-11-07 16:19:25 +00:00
|
|
|
OSMO_ASSERT(mgcp_ctx->fsm);
|
2018-12-18 21:05:24 +00:00
|
|
|
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);
|
2017-11-07 16:19:25 +00:00
|
|
|
mgcp_ctx->fsm->priv = mgcp_ctx;
|
|
|
|
mgcp_ctx->mgcp = mgcp;
|
|
|
|
mgcp_ctx->trans = trans;
|
2018-02-08 13:15:59 +00:00
|
|
|
mgcp_ctx->call_id = trans->callref;
|
2017-11-07 16:19:25 +00:00
|
|
|
|
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2018-12-11 17:59:27 +00:00
|
|
|
/* 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)
|
2017-11-07 16:19:25 +00:00
|
|
|
{
|
2018-12-11 17:59:27 +00:00
|
|
|
struct mgcp_ctx *mgcp_ctx;
|
2017-11-07 16:19:25 +00:00
|
|
|
|
2018-12-11 17:59:27 +00:00
|
|
|
OSMO_ASSERT(conn);
|
2017-11-07 16:19:25 +00:00
|
|
|
|
2018-12-11 17:59:27 +00:00
|
|
|
if (port == 0) {
|
|
|
|
LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid remote call leg port, assignment completion failed\n",
|
|
|
|
vlr_subscr_name(conn->vsub));
|
2017-11-07 16:19:25 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
2018-12-11 17:59:27 +00:00
|
|
|
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));
|
2017-11-07 16:19:25 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2018-12-11 17:59:27 +00:00
|
|
|
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));
|
2017-11-07 16:19:25 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2018-12-11 17:59:27 +00:00
|
|
|
/* 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. */
|
2018-12-07 13:47:34 +00:00
|
|
|
conn->rtp.remote_port_ran = port;
|
2018-12-11 17:59:27 +00:00
|
|
|
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);
|
2017-11-07 16:19:25 +00:00
|
|
|
|
2018-12-11 17:59:27 +00:00
|
|
|
/* 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);
|
2017-11-07 16:19:25 +00:00
|
|
|
|
2018-12-11 17:59:27 +00:00
|
|
|
return 0;
|
2017-11-07 16:19:25 +00:00
|
|
|
}
|
|
|
|
|
2018-12-19 16:05:13 +00:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2017-11-07 16:19:25 +00:00
|
|
|
/* 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;
|
rename gsm_subscriber_connection to ran_conn
In preparation for inter-BSC and inter-MSC handover, we need to separate the
subscriber management logic from the actual RAN connections. What better time
to finally rename gsm_subscriber_connection.
* Name choice:
In 2G, this is a connection to the BSS, but even though 3GPP TS commonly talk
of "BSS-A" and "BSS-B" when explaining handover, it's not good to call it
"bss_conn": in 3G a BSS is called RNS, IIUC.
The overall term for 2G (GERAN) and 3G (UTRAN) is RAN: Radio Access Network.
* Rationale:
A subscriber in the MSC so far has only one RAN connection, but e.g. for
inter-BSC handover, a second one needs to be created to handover to. Most of
the items in the former gsm_subscriber_connection are actually related to the
RAN, with only a few MM and RTP related items. So, as a first step, just rename
it to ran_conn, to cosmetically prepare for moving the not strictly RAN related
items away later.
Also:
- Rename some functions from msc_subscr_conn_* to ran_conn_*
- Rename "Subscr_Conn" FSM instance name to "RAN_conn"
- Rename SUBSCR_CONN_* to RAN_CONN_*
Change-Id: Ic595f7a558d3553c067f77dc67543ab59659707a
2018-11-29 21:37:51 +00:00
|
|
|
struct ran_conn *conn;
|
2017-11-07 16:19:25 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
fix: incoming call during ongoing call
If a call is already busy and another call is coming in, do not try to
immediately assign an lchan (before this patch, it fails because there already
is an mgcp_ctx for the conn). Leave the second CC transaction waiting.
When a call is hung up, as soon as the old mgcp_ctx is discarded, look for
other CC transactions that are waiting. If there is one, trigger assignment, so
a new mgcp_ctx is set up for the new call.
This fixes the following scenario:
- from A, call B.
- from C, call B; B rings during ongoing call.
- in B, pick up the call, choose to drop the old call.
After this patch, and with osmo-bsc patch with change-id
I0c00ec2c120e5008281755adcd4944a3ce4d8355
we are now able to talk to the new caller.
I currently haven't tested yet what happens if there is *three* peers trying to
talk to the same number, running out of lab phones (not really, just not
bothering now). Possibly we should be taking over the particular call indicated
by the CC TI; instead, the current patch version takes on whichever waiting
call it finds first. This is fine if *one* additional call comes in on an
ongoing call, and this is already a huge improvement to what we had before.
Related: OS#3735
Change-Id: I0ba216b737909e92080a722db26e3577726c63cb
2018-12-20 01:59:32 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2017-11-07 16:19:25 +00:00
|
|
|
/* 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;
|
2018-12-21 00:34:48 +00:00
|
|
|
struct ran_conn *conn = trans->conn;
|
fix: incoming call during ongoing call
If a call is already busy and another call is coming in, do not try to
immediately assign an lchan (before this patch, it fails because there already
is an mgcp_ctx for the conn). Leave the second CC transaction waiting.
When a call is hung up, as soon as the old mgcp_ctx is discarded, look for
other CC transactions that are waiting. If there is one, trigger assignment, so
a new mgcp_ctx is set up for the new call.
This fixes the following scenario:
- from A, call B.
- from C, call B; B rings during ongoing call.
- in B, pick up the call, choose to drop the old call.
After this patch, and with osmo-bsc patch with change-id
I0c00ec2c120e5008281755adcd4944a3ce4d8355
we are now able to talk to the new caller.
I currently haven't tested yet what happens if there is *three* peers trying to
talk to the same number, running out of lab phones (not really, just not
bothering now). Possibly we should be taking over the particular call indicated
by the CC TI; instead, the current patch version takes on whichever waiting
call it finds first. This is fine if *one* additional call comes in on an
ongoing call, and this is already a huge improvement to what we had before.
Related: OS#3735
Change-Id: I0ba216b737909e92080a722db26e3577726c63cb
2018-12-20 01:59:32 +00:00
|
|
|
struct gsm_trans *waiting_trans;
|
2017-11-07 16:19:25 +00:00
|
|
|
|
|
|
|
OSMO_ASSERT(trans);
|
|
|
|
|
2018-12-21 00:34:48 +00:00
|
|
|
if (!conn) {
|
2017-11-07 16:19:25 +00:00
|
|
|
LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid conn, call release failed\n",
|
|
|
|
vlr_subscr_name(trans->vsub));
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2018-12-21 00:34:48 +00:00
|
|
|
mgcp_ctx = conn->rtp.mgcp_ctx;
|
|
|
|
if (!mgcp_ctx) {
|
2017-11-07 16:19:25 +00:00
|
|
|
LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid mgcp context, call release failed.\n",
|
|
|
|
vlr_subscr_name(trans->vsub));
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2018-12-21 00:34:48 +00:00
|
|
|
if (!mgcp_ctx->fsm) {
|
2017-11-07 16:19:25 +00:00
|
|
|
LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) no FSM, call release failed\n",
|
|
|
|
vlr_subscr_name(trans->vsub));
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2018-12-21 00:35:21 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-12-21 00:34:48 +00:00
|
|
|
LOGP(DMGCP, LOGL_DEBUG, "(ti %02x %s) Call release: tearing down MGW endpoint\n",
|
|
|
|
trans->transaction_id, vlr_subscr_name(trans->vsub));
|
2017-11-07 16:19:25 +00:00
|
|
|
|
|
|
|
/* 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 */
|
2018-12-21 00:34:48 +00:00
|
|
|
conn->rtp.mgcp_ctx = NULL;
|
2017-11-07 16:19:25 +00:00
|
|
|
|
fix: incoming call during ongoing call
If a call is already busy and another call is coming in, do not try to
immediately assign an lchan (before this patch, it fails because there already
is an mgcp_ctx for the conn). Leave the second CC transaction waiting.
When a call is hung up, as soon as the old mgcp_ctx is discarded, look for
other CC transactions that are waiting. If there is one, trigger assignment, so
a new mgcp_ctx is set up for the new call.
This fixes the following scenario:
- from A, call B.
- from C, call B; B rings during ongoing call.
- in B, pick up the call, choose to drop the old call.
After this patch, and with osmo-bsc patch with change-id
I0c00ec2c120e5008281755adcd4944a3ce4d8355
we are now able to talk to the new caller.
I currently haven't tested yet what happens if there is *three* peers trying to
talk to the same number, running out of lab phones (not really, just not
bothering now). Possibly we should be taking over the particular call indicated
by the CC TI; instead, the current patch version takes on whichever waiting
call it finds first. This is fine if *one* additional call comes in on an
ongoing call, and this is already a huge improvement to what we had before.
Related: OS#3735
Change-Id: I0ba216b737909e92080a722db26e3577726c63cb
2018-12-20 01:59:32 +00:00
|
|
|
/* 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);
|
|
|
|
}
|
|
|
|
|
2017-11-07 16:19:25 +00:00
|
|
|
return 0;
|
|
|
|
}
|