blob: 5c8888031a24e2fab65fcba60645eb06b37c5bc0 [file] [log] [blame]
Philipp Maier621ba032017-11-07 17:19:25 +01001/* (C) 2017 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
2 * All Rights Reserved
3 *
4 * Author: Philipp Maier
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Affero General Public License for more details.
15 *
16 * You should have received a copy of the GNU Affero General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 */
20
21#include <arpa/inet.h>
22
23#include <osmocom/mgcp_client/mgcp_client.h>
24#include <osmocom/core/logging.h>
25#include <osmocom/core/utils.h>
26#include <osmocom/core/timer.h>
27#include <osmocom/core/fsm.h>
28#include <osmocom/core/byteswap.h>
29#include <osmocom/msc/msc_mgcp.h>
30#include <osmocom/msc/debug.h>
31#include <osmocom/msc/transaction.h>
32#include <osmocom/msc/a_iface.h>
33#include <osmocom/msc/msc_ifaces.h>
34#include <osmocom/msc/gsm_04_08.h>
35#include <osmocom/msc/iucs.h>
36#include <osmocom/msc/vlr.h>
37
38#include "../../bscconfig.h"
39
40#define S(x) (1 << (x))
41
42#define CONN_ID_RAN 1
43#define CONN_ID_CN 2
44
45#define MGCP_MGW_TIMEOUT 4 /* in seconds */
46#define MGCP_MGW_TIMEOUT_TIMER_NR 1
Philipp Maier4c573772018-02-08 14:05:43 +010047#define MGCP_RAN_TIMEOUT 120 /* in seconds */
Philipp Maier621ba032017-11-07 17:19:25 +010048#define MGCP_RAN_TIMEOUT_TIMER_NR 2
49#define MGCP_REL_TIMEOUT 60 /* in seconds */
50#define MGCP_REL_TIMEOUT_TIMER_NR 3
51#define MGCP_ASS_TIMEOUT 10 /* in seconds */
52#define MGCP_ASS_TIMEOUT_TIMER_NR 4
53
Philipp Maier621ba032017-11-07 17:19:25 +010054/* Some internal cause codes to indicate fault condition inside the FSM */
55enum msc_mgcp_cause_code {
56 MGCP_ERR_MGW_FAIL,
57 MGCP_ERR_MGW_INVAL_RESP,
58 MGCP_ERR_MGW_TX_FAIL,
Philipp Maier4eef20b2018-03-13 13:07:45 +010059 MGCP_ERR_MGW_TIMEOUT,
Philipp Maier621ba032017-11-07 17:19:25 +010060 MGCP_ERR_UNEXP_TEARDOWN,
61 MGCP_ERR_UNSUPP_ADDR_FMT,
62 MGCP_ERR_RAN_TIMEOUT,
63 MGCP_ERR_ASS_TIMEOUT,
Philipp Maier3a776522018-02-22 12:00:00 +010064 MGCP_ERR_TOOLONG,
Philipp Maier621ba032017-11-07 17:19:25 +010065 MGCP_ERR_ASSGMNT_FAIL
66};
67
68/* Human readable respresentation of the faul codes, will be displayed by
69 * handle_error() */
70static const struct value_string msc_mgcp_cause_codes_names[] = {
71 {MGCP_ERR_MGW_FAIL, "operation failed on MGW"},
72 {MGCP_ERR_MGW_INVAL_RESP, "invalid / unparseable response from MGW"},
73 {MGCP_ERR_MGW_TX_FAIL, "failed to transmit MGCP message to MGW"},
Philipp Maier4eef20b2018-03-13 13:07:45 +010074 {MGCP_ERR_MGW_TIMEOUT, "request to MGW timed out"},
Philipp Maier621ba032017-11-07 17:19:25 +010075 {MGCP_ERR_UNEXP_TEARDOWN, "unexpected connection teardown"},
76 {MGCP_ERR_UNSUPP_ADDR_FMT, "unsupported network address format used (RAN)"},
77 {MGCP_ERR_RAN_TIMEOUT, "call could not be completed in time (RAN)"},
78 {MGCP_ERR_ASS_TIMEOUT, "assignment could not be completed in time (RAN)"},
Philipp Maier3a776522018-02-22 12:00:00 +010079 {MGCP_ERR_TOOLONG, "string value too long"},
Philipp Maier621ba032017-11-07 17:19:25 +010080 {MGCP_ERR_ASSGMNT_FAIL, "assignment failure (RAN)"},
81 {0, NULL}
82};
83
84enum fsm_msc_mgcp_states {
85 ST_CRCX_RAN,
86 ST_CRCX_CN,
87 ST_CRCX_COMPL,
88 ST_MDCX_CN,
89 ST_MDCX_CN_COMPL,
90 ST_MDCX_RAN,
91 ST_MDCX_RAN_COMPL,
92 ST_CALL,
93 ST_HALT,
94};
95
96enum msc_mgcp_fsm_evt {
97 /* Initial event: start off the state machine */
98 EV_INIT,
99
100 /* External event: Notify that the Assignment is complete and we
101 * may now forward IP/Port of the remote call leg to the MGW */
102 EV_ASSIGN,
103
104 /* External event: Notify that the Call is complete and that the
105 * two half open connections on the MGW should now be connected */
106 EV_CONNECT,
107
108 /* External event: Notify that the call is over and the connections
109 * on the mgw shall be removed */
110 EV_TEARDOWN,
111
112 /* Internal event: An error occurred that requires a controlled
113 * teardown of the RTP connections */
114 EV_TEARDOWN_ERROR,
115
116 /* Internal event: The mgcp_gw has sent its CRCX response for
117 * the RAN side */
118 EV_CRCX_RAN_RESP,
119
120 /* Internal event: The mgcp_gw has sent its CRCX response for
121 * the CN side */
122 EV_CRCX_CN_RESP,
123
124 /* Internal event: The mgcp_gw has sent its MDCX response for
125 * the RAN side */
126 EV_MDCX_RAN_RESP,
127
128 /* Internal event: The mgcp_gw has sent its MDCX response for
129 * the CN side */
130 EV_MDCX_CN_RESP,
131
132 /* Internal event: The mgcp_gw has sent its DLCX response for
133 * the RAN and CN side */
134 EV_DLCX_ALL_RESP,
135};
136
Philipp Maiere4f91722018-02-26 15:20:49 +0100137static const struct value_string msc_mgcp_fsm_evt_names[] = {
138 OSMO_VALUE_STRING(EV_INIT),
139 OSMO_VALUE_STRING(EV_ASSIGN),
140 OSMO_VALUE_STRING(EV_CONNECT),
141 OSMO_VALUE_STRING(EV_TEARDOWN),
142 OSMO_VALUE_STRING(EV_TEARDOWN_ERROR),
143 OSMO_VALUE_STRING(EV_CRCX_RAN_RESP),
144 OSMO_VALUE_STRING(EV_CRCX_CN_RESP),
Neels Hofmeyr5d669702018-12-18 22:03:46 +0100145 OSMO_VALUE_STRING(EV_MDCX_RAN_RESP),
146 OSMO_VALUE_STRING(EV_MDCX_CN_RESP),
Philipp Maiere4f91722018-02-26 15:20:49 +0100147 OSMO_VALUE_STRING(EV_DLCX_ALL_RESP),
148 {0, NULL}
149};
150
Philipp Maier621ba032017-11-07 17:19:25 +0100151/* A general error handler function. On error we still have an interest to
152 * remove a half open connection (if possible). This function will execute
153 * a controlled jump to the DLCX phase. From there, the FSM will then just
154 * continue like the call were ended normally */
Philipp Maier4eef20b2018-03-13 13:07:45 +0100155#define handle_error(mgcp_ctx, cause, dlcx) _handle_error(mgcp_ctx, cause, dlcx, __FILE__, __LINE__)
156static void _handle_error(struct mgcp_ctx *mgcp_ctx, enum msc_mgcp_cause_code cause, bool dlcx, const char *file,
157 int line)
Philipp Maier621ba032017-11-07 17:19:25 +0100158{
Philipp Maier79beccd2018-04-11 17:36:45 +0200159 bool dlcx_possible = true;
Philipp Maier621ba032017-11-07 17:19:25 +0100160 struct osmo_fsm_inst *fi;
Philipp Maier782ccec2018-10-08 17:12:49 +0200161 struct gsm_mncc mncc;
Philipp Maier621ba032017-11-07 17:19:25 +0100162
163 OSMO_ASSERT(mgcp_ctx);
164 fi = mgcp_ctx->fsm;
165 OSMO_ASSERT(fi);
166
Philipp Maier79beccd2018-04-11 17:36:45 +0200167 /* Check if the endpoint identifier is a specific endpoint identifier,
168 * since in order to perform a DLCX we must know the specific
169 * identifier of the endpoint we want to release. If we do not have
170 * this information because of errornous communication we can not
171 * perform a DLCX. */
172 if (strstr(mgcp_ctx->rtp_endpoint, "*"))
173 dlcx_possible = false;
174
Philipp Maier621ba032017-11-07 17:19:25 +0100175 LOGPFSMLSRC(mgcp_ctx->fsm, LOGL_ERROR, file, line, "%s -- graceful shutdown...\n",
176 get_value_string(msc_mgcp_cause_codes_names, cause));
177
Stefan Sperling722f2b42018-09-28 14:26:35 +0200178 /* Request the higher layers (gsm_04_08.c) to release the call. If the
179 * problem occured after msc_mgcp_call_release() was calls, remain
180 * silent because we already got informed and the higher layers might
181 * already freed their context information (trans). */
182 if (!mgcp_ctx->free_ctx) {
Philipp Maier782ccec2018-10-08 17:12:49 +0200183 mncc = (struct gsm_mncc) {
184 .msg_type = MNCC_REL_REQ,
185 .callref = mgcp_ctx->trans->callref,
186 .cause = {
187 .location = GSM48_CAUSE_LOC_PRN_S_LU,
188 .coding = 0, /* FIXME */
189 .value = GSM48_CC_CAUSE_RESOURCE_UNAVAIL
190 }
191 };
192
Stefan Sperling722f2b42018-09-28 14:26:35 +0200193 mncc_set_cause(&mncc, GSM48_CAUSE_LOC_TRANS_NET,
194 GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
195 mncc_tx_to_cc(mgcp_ctx->trans->net, MNCC_REL_REQ, &mncc);
196 }
197
Philipp Maier4eef20b2018-03-13 13:07:45 +0100198 /* For the shutdown we have two options. Whenever it makes sense to
199 * send a DLCX to the MGW in order to be sure that the connection is
200 * properly cleaned up, the dlcx flag should be set. In other cases
201 * where a DLCX does not make sense (e.g. the MGW times out), halting
202 * directly is the better options. In those cases, the dlcx flag
203 * should not be set */
Philipp Maier79beccd2018-04-11 17:36:45 +0200204 if (dlcx && dlcx_possible) {
Philipp Maier4eef20b2018-03-13 13:07:45 +0100205 /* Fast-forward the FSM into call state. In this state the FSM
206 * expects either an EV_TEARDOWN or an EV_TEARDOWN_ERROR. When
207 * one of the two events is received a DLCX will be send to
208 * the MGW. After that. The FSM automatically halts but will
209 * still expect a call msc_mgcp_call_release() to be freed
210 * completely */
211 osmo_fsm_inst_state_chg(fi, ST_CALL, 0, 0);
212 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_TEARDOWN_ERROR, mgcp_ctx);
213 } else {
214 /* Halt the state machine immediately. The FSM will not be
215 * freed yet, we stil require the higher layers to call
216 * msc_mgcp_call_release() */
217 osmo_fsm_inst_state_chg(fi, ST_HALT, 0, 0);
218 osmo_fsm_inst_dispatch(fi, EV_TEARDOWN_ERROR, mgcp_ctx);
219 }
Philipp Maier621ba032017-11-07 17:19:25 +0100220}
221
222/* Timer callback to shut down in case of connectivity problems */
223static int fsm_timeout_cb(struct osmo_fsm_inst *fi)
224{
225 struct mgcp_ctx *mgcp_ctx = fi->priv;
226 struct mgcp_client *mgcp;
227
228 OSMO_ASSERT(mgcp_ctx);
229 mgcp = mgcp_ctx->mgcp;
230 OSMO_ASSERT(mgcp);
231
232 if (fi->T == MGCP_MGW_TIMEOUT_TIMER_NR) {
233 /* We were unable to communicate with the MGW, unfortunately
234 * there is no meaningful action we can take now other than
235 * giving up. */
236
Philipp Maier621ba032017-11-07 17:19:25 +0100237 /* Cancel the transaction that timed out */
238 mgcp_client_cancel(mgcp, mgcp_ctx->mgw_pending_trans);
239
Philipp Maier4eef20b2018-03-13 13:07:45 +0100240 /* halt of the FSM */
241 handle_error(mgcp_ctx, MGCP_ERR_MGW_TIMEOUT, false);
Philipp Maier621ba032017-11-07 17:19:25 +0100242 } else if (fi->T == MGCP_RAN_TIMEOUT_TIMER_NR) {
243 /* If the logic that controls the RAN is unable to negotiate a
244 * connection, we presumably still have a working connection to
245 * the MGW, we will try to shut down gracefully. */
Philipp Maier4eef20b2018-03-13 13:07:45 +0100246 handle_error(mgcp_ctx, MGCP_ERR_RAN_TIMEOUT, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100247 } else if (fi->T == MGCP_REL_TIMEOUT_TIMER_NR) {
248 /* Under normal conditions, the MSC logic should always command
249 * to release the call at some point. However, the release may
250 * be missing due to errors in the MSC logic and we may have
251 * reached ST_HALT because of cascading errors and timeouts. In
252 * this and only in this case we will allow ST_HALT to free all
253 * context information on its own authority. */
254 mgcp_ctx->free_ctx = true;
255
256 /* Initiate self destruction of the FSM */
257 osmo_fsm_inst_state_chg(fi, ST_HALT, 0, 0);
258 osmo_fsm_inst_dispatch(fi, EV_TEARDOWN, mgcp_ctx);
259 } else if (fi->T == MGCP_ASS_TIMEOUT_TIMER_NR) {
260 /* There may be rare cases in which the MSC is unable to
261 * complete the call assignment */
Philipp Maier4eef20b2018-03-13 13:07:45 +0100262 handle_error(mgcp_ctx, MGCP_ERR_ASS_TIMEOUT, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100263 } else {
264 /* Ther must not be any unsolicited timers in this FSM. If so,
265 * we have serious problem. */
266 OSMO_ASSERT(false);
267 }
268
269 return 0;
270}
271
272static void mgw_crcx_ran_resp_cb(struct mgcp_response *r, void *priv);
273
274/* Callback for ST_CRCX_RAN: Send CRCX for RAN side to MGW */
275static void fsm_crcx_ran_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
276{
277 struct mgcp_ctx *mgcp_ctx = data;
278 struct mgcp_client *mgcp;
279 struct mgcp_msg mgcp_msg;
280 struct msgb *msg;
281 int rc;
Philipp Maier5046db92018-05-29 12:02:38 +0200282 struct gsm_trans *trans;
Neels Hofmeyrc036b792018-11-29 22:37:51 +0100283 struct ran_conn *conn;
Philipp Maier5046db92018-05-29 12:02:38 +0200284
Philipp Maier621ba032017-11-07 17:19:25 +0100285 OSMO_ASSERT(mgcp_ctx);
286 mgcp = mgcp_ctx->mgcp;
287 OSMO_ASSERT(mgcp);
Philipp Maier5046db92018-05-29 12:02:38 +0200288 trans = mgcp_ctx->trans;
289 OSMO_ASSERT(trans);
290 conn = trans->conn;
291 OSMO_ASSERT(conn);
Philipp Maier5046db92018-05-29 12:02:38 +0200292
Philipp Maier79beccd2018-04-11 17:36:45 +0200293 /* NOTE: In case of error, we will not be able to perform any DLCX
294 * operation because until this point we do not have requested any
295 * endpoint yet. */
296
Philipp Maier621ba032017-11-07 17:19:25 +0100297 LOGPFSML(fi, LOGL_DEBUG,
Philipp Maierd997fa12018-04-11 15:23:30 +0200298 "CRCX/RAN: creating connection for the RAN side on MGW endpoint:%s...\n", mgcp_ctx->rtp_endpoint);
Philipp Maier621ba032017-11-07 17:19:25 +0100299
300 /* Generate MGCP message string */
301 mgcp_msg = (struct mgcp_msg) {
302 .verb = MGCP_VERB_CRCX,
303 .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_MODE),
Philipp Maiera2353c62018-02-08 14:15:59 +0100304 .call_id = mgcp_ctx->call_id,
Philipp Maier65d8d0d2018-05-29 10:03:41 +0200305 .conn_mode = MGCP_CONN_RECV_ONLY
Philipp Maier621ba032017-11-07 17:19:25 +0100306 };
Neels Hofmeyr2c268692018-12-19 01:01:46 +0100307 if (osmo_strlcpy(mgcp_msg.endpoint, mgcp_client_rtpbridge_wildcard(mgcp), sizeof(mgcp_msg.endpoint)) >=
Philipp Maier621ba032017-11-07 17:19:25 +0100308 MGCP_ENDPOINT_MAXLEN) {
Philipp Maier79beccd2018-04-11 17:36:45 +0200309 handle_error(mgcp_ctx, MGCP_ERR_TOOLONG, false);
Philipp Maier621ba032017-11-07 17:19:25 +0100310 return;
311 }
Philipp Maiera2353c62018-02-08 14:15:59 +0100312
Philipp Maier5046db92018-05-29 12:02:38 +0200313 /* HACK: We put the connection in loopback mode at the beginnig to
Neels Hofmeyra4efe3f2018-07-26 17:44:13 +0200314 * trick the hNodeB into doing the IuUP negotiation with itself.
315 * This is a hack we need because osmo-mgw does not support IuUP yet, see OS#2459. */
Philipp Maier5046db92018-05-29 12:02:38 +0200316#ifdef BUILD_IU
Neels Hofmeyr7814a832018-12-26 00:40:18 +0100317 if (conn->via_ran == OSMO_RAT_UTRAN_IU)
Philipp Maier5046db92018-05-29 12:02:38 +0200318 mgcp_msg.conn_mode = MGCP_CONN_LOOPBACK;
319#endif
320
Philipp Maier621ba032017-11-07 17:19:25 +0100321 msg = mgcp_msg_gen(mgcp, &mgcp_msg);
322 OSMO_ASSERT(msg);
323
324 /* Transmit MGCP message to MGW */
325 mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
326 rc = mgcp_client_tx(mgcp, msg, mgw_crcx_ran_resp_cb, mgcp_ctx);
327 if (rc < 0) {
Philipp Maier79beccd2018-04-11 17:36:45 +0200328 handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL, false);
Philipp Maier621ba032017-11-07 17:19:25 +0100329 return;
330 }
331
332 osmo_fsm_inst_state_chg(fi, ST_CRCX_CN, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
333}
334
335/* Callback for MGCP-Client: handle response for RAN associated CRCX */
336static void mgw_crcx_ran_resp_cb(struct mgcp_response *r, void *priv)
337{
338 struct mgcp_ctx *mgcp_ctx = priv;
339 int rc;
340 struct gsm_trans *trans;
Neels Hofmeyrc036b792018-11-29 22:37:51 +0100341 struct ran_conn *conn;
Philipp Maier621ba032017-11-07 17:19:25 +0100342
Philipp Maier79beccd2018-04-11 17:36:45 +0200343 /* NOTE: In case of error, we will not be able to perform any DLCX
344 * operation because until we either get a parseable message that
345 * contains an error code (no endpoint is seized in those cases)
346 * or we get an unparseable message. In this case we can not be
347 * sure, but we also can not draw any assumptions from unparseable
348 * messages. */
349
Philipp Maier621ba032017-11-07 17:19:25 +0100350 OSMO_ASSERT(mgcp_ctx);
351 trans = mgcp_ctx->trans;
352 OSMO_ASSERT(trans);
353 conn = trans->conn;
354 OSMO_ASSERT(conn);
355
356 if (r->head.response_code != 200) {
357 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
358 "CRCX/RAN: response yields error: %d %s\n", r->head.response_code, r->head.comment);
Philipp Maier79beccd2018-04-11 17:36:45 +0200359 handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL, false);
Philipp Maier621ba032017-11-07 17:19:25 +0100360 return;
361 }
362
Philipp Maiera2353c62018-02-08 14:15:59 +0100363 /* memorize connection identifier and specific endpoint id */
Philipp Maier621ba032017-11-07 17:19:25 +0100364 osmo_strlcpy(mgcp_ctx->conn_id_ran, r->head.conn_id, sizeof(mgcp_ctx->conn_id_ran));
365 LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/RAN: MGW responded with CI: %s\n", mgcp_ctx->conn_id_ran);
Philipp Maiera2353c62018-02-08 14:15:59 +0100366 osmo_strlcpy(mgcp_ctx->rtp_endpoint, r->head.endpoint, sizeof(mgcp_ctx->rtp_endpoint));
367 LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/RAN: MGW assigned endpoint: %s\n", mgcp_ctx->rtp_endpoint);
Philipp Maier621ba032017-11-07 17:19:25 +0100368
369 rc = mgcp_response_parse_params(r);
370 if (rc) {
371 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR, "CRCX/RAN: Cannot parse response\n");
Philipp Maier79beccd2018-04-11 17:36:45 +0200372 handle_error(mgcp_ctx, MGCP_ERR_MGW_INVAL_RESP, false);
Philipp Maier621ba032017-11-07 17:19:25 +0100373 return;
374 }
375
Neels Hofmeyr4e0bd4b2018-12-18 22:04:40 +0100376 LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/RAN: MGW responded with address %s:%u\n", r->audio_ip, r->audio_port);
Philipp Maier621ba032017-11-07 17:19:25 +0100377
378 conn->rtp.local_port_ran = r->audio_port;
379 osmo_strlcpy(conn->rtp.local_addr_ran, r->audio_ip, sizeof(conn->rtp.local_addr_ran));
380
381 /* Notify the FSM that we got the response. */
382 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_CRCX_RAN_RESP, mgcp_ctx);
383}
384
385static void mgw_crcx_cn_resp_cb(struct mgcp_response *r, void *priv);
386
387/* Callback for ST_CRCX_CN: check MGW response and send CRCX for CN side to MGW */
388static void fsm_crcx_cn_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
389{
390 struct mgcp_ctx *mgcp_ctx = data;
391 struct mgcp_client *mgcp;
392 struct mgcp_msg mgcp_msg;
393 struct msgb *msg;
394 int rc;
Philipp Maier5046db92018-05-29 12:02:38 +0200395 struct gsm_trans *trans;
Neels Hofmeyrc036b792018-11-29 22:37:51 +0100396 struct ran_conn *conn;
Philipp Maier5046db92018-05-29 12:02:38 +0200397
Philipp Maier621ba032017-11-07 17:19:25 +0100398 OSMO_ASSERT(mgcp_ctx);
399 mgcp = mgcp_ctx->mgcp;
400 OSMO_ASSERT(mgcp);
Philipp Maier5046db92018-05-29 12:02:38 +0200401 trans = mgcp_ctx->trans;
402 OSMO_ASSERT(trans);
403 conn = trans->conn;
404 OSMO_ASSERT(conn);
Philipp Maier5046db92018-05-29 12:02:38 +0200405
Philipp Maier621ba032017-11-07 17:19:25 +0100406 switch (event) {
407 case EV_CRCX_RAN_RESP:
408 break;
409 default:
Philipp Maier4eef20b2018-03-13 13:07:45 +0100410 handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100411 return;
412 }
413
414 LOGPFSML(fi, LOGL_DEBUG,
Philipp Maierd997fa12018-04-11 15:23:30 +0200415 "CRCX/CN creating connection for the CN side on MGW endpoint:%s...\n", mgcp_ctx->rtp_endpoint);
Philipp Maier621ba032017-11-07 17:19:25 +0100416
417 /* Generate MGCP message string */
418 mgcp_msg = (struct mgcp_msg) {
419 .verb = MGCP_VERB_CRCX,
420 .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_MODE),
Philipp Maiera2353c62018-02-08 14:15:59 +0100421 .call_id = mgcp_ctx->call_id,
Philipp Maier65d8d0d2018-05-29 10:03:41 +0200422 .conn_mode = MGCP_CONN_RECV_ONLY
Philipp Maier621ba032017-11-07 17:19:25 +0100423 };
Philipp Maiera2353c62018-02-08 14:15:59 +0100424 if (osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->rtp_endpoint, sizeof(mgcp_msg.endpoint)) >=
Philipp Maier621ba032017-11-07 17:19:25 +0100425 MGCP_ENDPOINT_MAXLEN) {
Philipp Maier3a776522018-02-22 12:00:00 +0100426 handle_error(mgcp_ctx, MGCP_ERR_TOOLONG, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100427 return;
428 }
Philipp Maiera2353c62018-02-08 14:15:59 +0100429
Philipp Maier621ba032017-11-07 17:19:25 +0100430 msg = mgcp_msg_gen(mgcp, &mgcp_msg);
431 OSMO_ASSERT(msg);
432
433 /* Transmit MGCP message to MGW */
434 mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
435 rc = mgcp_client_tx(mgcp, msg, mgw_crcx_cn_resp_cb, mgcp_ctx);
436 if (rc < 0) {
Philipp Maier4eef20b2018-03-13 13:07:45 +0100437 handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100438 return;
439 }
440
441 osmo_fsm_inst_state_chg(fi, ST_CRCX_COMPL, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
442}
443
444/* Callback for MGCP-Client: handle response for CN associated CRCX */
445static void mgw_crcx_cn_resp_cb(struct mgcp_response *r, void *priv)
446{
447 struct mgcp_ctx *mgcp_ctx = priv;
448 int rc;
449 struct gsm_trans *trans;
Neels Hofmeyrc036b792018-11-29 22:37:51 +0100450 struct ran_conn *conn;
Philipp Maier621ba032017-11-07 17:19:25 +0100451
452 OSMO_ASSERT(mgcp_ctx);
453 trans = mgcp_ctx->trans;
454 OSMO_ASSERT(trans);
455 conn = trans->conn;
456 OSMO_ASSERT(conn);
457
458 if (r->head.response_code != 200) {
459 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
460 "CRCX/CN: response yields error: %d %s\n", r->head.response_code, r->head.comment);
Philipp Maier4eef20b2018-03-13 13:07:45 +0100461 handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100462 return;
463 }
464
465 /* memorize connection identifier */
466 osmo_strlcpy(mgcp_ctx->conn_id_cn, r->head.conn_id, sizeof(mgcp_ctx->conn_id_cn));
467 LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/CN: MGW responded with CI: %s\n", mgcp_ctx->conn_id_cn);
468
469 rc = mgcp_response_parse_params(r);
470 if (rc) {
471 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR, "CRCX/CN: Cannot parse response\n");
Philipp Maier4eef20b2018-03-13 13:07:45 +0100472 handle_error(mgcp_ctx, MGCP_ERR_MGW_INVAL_RESP, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100473 return;
474 }
475
476 LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/CN: MGW responded with address %s:%u\n", r->audio_ip, r->audio_port);
477
478 conn->rtp.local_port_cn = r->audio_port;
479 osmo_strlcpy(conn->rtp.local_addr_cn, r->audio_ip, sizeof(conn->rtp.local_addr_cn));
480
481 /* Notify the FSM that we got the response. */
482 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_CRCX_CN_RESP, mgcp_ctx);
483}
484
485/* Callback for ST_CRCX_COMPL: check MGW response, start assignment */
486static void fsm_crcx_compl(struct osmo_fsm_inst *fi, uint32_t event, void *data)
487{
488 struct mgcp_ctx *mgcp_ctx = data;
489 struct gsm_trans *trans;
Neels Hofmeyrc036b792018-11-29 22:37:51 +0100490 struct ran_conn *conn;
Philipp Maier621ba032017-11-07 17:19:25 +0100491
492 OSMO_ASSERT(mgcp_ctx);
493 trans = mgcp_ctx->trans;
494 OSMO_ASSERT(trans);
495 conn = trans->conn;
496 OSMO_ASSERT(conn);
497
498 switch (event) {
499 case EV_CRCX_CN_RESP:
500 break;
501 default:
Philipp Maier4eef20b2018-03-13 13:07:45 +0100502 handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100503 return;
504 }
505
506 /* Forward assignment request to A/RANAP */
Neels Hofmeyr7814a832018-12-26 00:40:18 +0100507 if (conn->via_ran == OSMO_RAT_UTRAN_IU) {
Philipp Maier621ba032017-11-07 17:19:25 +0100508#ifdef BUILD_IU
509 /* Assign a voice channel via RANAP on 3G */
510 if (iu_rab_act_cs(trans))
511 goto error;
512#else
513 LOGPFSML(fi, LOGL_ERROR, "Cannot send Iu RAB Assignment: built without Iu support\n");
514 goto error;
515#endif
Neels Hofmeyr7814a832018-12-26 00:40:18 +0100516 } else if (conn->via_ran == OSMO_RAT_GERAN_A) {
Philipp Maier621ba032017-11-07 17:19:25 +0100517 /* Assign a voice channel via A on 2G */
518 if (a_iface_tx_assignment(trans))
519 goto error;
520 } else {
521 /* Unset or unimplemented new RAN type */
522 LOGPFSML(fi, LOGL_ERROR, "Unknown RAN type: %d\n", conn->via_ran);
523 return;
524 }
525
526 /* Respond back to MNCC (if requested) */
527 if (trans->tch_rtp_create) {
528 if (gsm48_tch_rtp_create(trans))
529 goto error;
530 }
531
532 /* Note: When we reach this point then the situation is basically that
533 * we have two sides connected, both are in loopback. The local ports
534 * of the side pointing towards the BSS should be already communicated
Philipp Maier4c573772018-02-08 14:05:43 +0100535 * and we are waiting now the other end to pick up. */
Philipp Maier621ba032017-11-07 17:19:25 +0100536 osmo_fsm_inst_state_chg(fi, ST_MDCX_CN, MGCP_RAN_TIMEOUT, MGCP_RAN_TIMEOUT_TIMER_NR);
537 return;
538
539error:
Philipp Maier4eef20b2018-03-13 13:07:45 +0100540 handle_error(mgcp_ctx, MGCP_ERR_ASSGMNT_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100541}
542
543static void mgw_mdcx_cn_resp_cb(struct mgcp_response *r, void *priv);
544
545/* Callback for ST_MDCX_CN: send MDCX for RAN side to MGW */
546static void fsm_mdcx_cn_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
547{
548 struct mgcp_ctx *mgcp_ctx = data;
549 struct mgcp_client *mgcp;
550 struct gsm_trans *trans;
Neels Hofmeyrc036b792018-11-29 22:37:51 +0100551 struct ran_conn *conn;
Philipp Maier621ba032017-11-07 17:19:25 +0100552 struct mgcp_msg mgcp_msg;
553 struct msgb *msg;
554 int rc;
555
556 OSMO_ASSERT(mgcp_ctx);
557 mgcp = mgcp_ctx->mgcp;
558 OSMO_ASSERT(mgcp);
559 trans = mgcp_ctx->trans;
560 OSMO_ASSERT(trans);
561 conn = trans->conn;
562 OSMO_ASSERT(conn);
563
564 switch (event) {
565 case EV_CONNECT:
566 break;
567 default:
Philipp Maier4eef20b2018-03-13 13:07:45 +0100568 handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100569 return;
570 }
571
572 LOGPFSML(fi, LOGL_DEBUG,
Pau Espin Pedrol9fac9852018-03-17 01:54:32 +0100573 "MDCX/CN: completing connection for the CN side on MGW endpoint:%p, remote leg expects RTP input on address %s:%u\n",
Philipp Maier621ba032017-11-07 17:19:25 +0100574 mgcp_ctx->rtp_endpoint, conn->rtp.remote_addr_cn, conn->rtp.remote_port_cn);
575
576 /* Generate MGCP message string */
577 mgcp_msg = (struct mgcp_msg) {
578 .verb = MGCP_VERB_MDCX,
579 .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_ID |
580 MGCP_MSG_PRESENCE_CONN_MODE | MGCP_MSG_PRESENCE_AUDIO_IP |
581 MGCP_MSG_PRESENCE_AUDIO_PORT),
Philipp Maiera2353c62018-02-08 14:15:59 +0100582 .call_id = mgcp_ctx->call_id,
Philipp Maier621ba032017-11-07 17:19:25 +0100583 .conn_id = mgcp_ctx->conn_id_cn,
584 .conn_mode = MGCP_CONN_RECV_SEND,
585 .audio_ip = conn->rtp.remote_addr_cn,
Philipp Maier8ad3dac2018-08-07 13:00:14 +0200586 .audio_port = conn->rtp.remote_port_cn,
587 .codecs[0] = conn->rtp.codec_cn,
588 .codecs_len = 1
Philipp Maier621ba032017-11-07 17:19:25 +0100589 };
Philipp Maiera2353c62018-02-08 14:15:59 +0100590 if (osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->rtp_endpoint, sizeof(mgcp_msg.endpoint)) >=
Philipp Maier621ba032017-11-07 17:19:25 +0100591 MGCP_ENDPOINT_MAXLEN) {
Philipp Maier3a776522018-02-22 12:00:00 +0100592 handle_error(mgcp_ctx, MGCP_ERR_TOOLONG, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100593 return;
594 }
Philipp Maiera2353c62018-02-08 14:15:59 +0100595
Philipp Maier621ba032017-11-07 17:19:25 +0100596 msg = mgcp_msg_gen(mgcp, &mgcp_msg);
597 OSMO_ASSERT(msg);
598
599 /* Transmit MGCP message to MGW */
600 mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
601 rc = mgcp_client_tx(mgcp, msg, mgw_mdcx_cn_resp_cb, mgcp_ctx);
602 if (rc < 0) {
Philipp Maier4eef20b2018-03-13 13:07:45 +0100603 handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100604 return;
605 }
606
607 osmo_fsm_inst_state_chg(fi, ST_MDCX_CN_COMPL, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
608}
609
610/* Callback for MGCP-Client: handle response for CN associated CRCX */
611static void mgw_mdcx_cn_resp_cb(struct mgcp_response *r, void *priv)
612{
613 struct mgcp_ctx *mgcp_ctx = priv;
614
615 OSMO_ASSERT(mgcp_ctx);
616
617 if (r->head.response_code != 200) {
618 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
619 "MDCX/CN: response yields error: %d %s\n", r->head.response_code, r->head.comment);
Philipp Maier4eef20b2018-03-13 13:07:45 +0100620 handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100621 return;
622 }
623
624 /* Notify the FSM that we got the response. */
625 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_MDCX_CN_RESP, mgcp_ctx);
626}
627
628/* Callback for ST_MDCX_CN_COMPL: wait for mgw response, move on with the MDCX
629 * for the RAN side if we already have valid IP/Port data for the RAN sided
630 * RTP stream. */
631static void fsm_mdcx_cn_compl_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
632{
633 struct mgcp_ctx *mgcp_ctx = data;
Neels Hofmeyrc036b792018-11-29 22:37:51 +0100634 struct ran_conn *conn;
Philipp Maier621ba032017-11-07 17:19:25 +0100635 struct gsm_trans *trans;
636
637 OSMO_ASSERT(mgcp_ctx);
638 trans = mgcp_ctx->trans;
639 OSMO_ASSERT(trans);
640 conn = trans->conn;
641 OSMO_ASSERT(conn);
642
643 switch (event) {
644 case EV_MDCX_CN_RESP:
645 break;
646 default:
Philipp Maier4eef20b2018-03-13 13:07:45 +0100647 handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100648 return;
649 }
650
651 /* Enter MDCX phase, but we must be sure that the Assigmnet on the A or
652 * IuCS interface is complete (IP-Address and Port are valid) */
653 osmo_fsm_inst_state_chg(fi, ST_MDCX_RAN, MGCP_ASS_TIMEOUT, MGCP_ASS_TIMEOUT_TIMER_NR);
654
655 /* If we already have a valid remote port and IP-Address from the RAN side
656 * call leg, the assignment has been completed before we got here, so we
657 * may move on immediately */
658 if (conn->rtp.remote_port_ran != 0 || strlen(conn->rtp.remote_addr_ran) > 0)
659 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_ASSIGN, mgcp_ctx);
660}
661
662static void mgw_mdcx_ran_resp_cb(struct mgcp_response *r, void *priv);
663
664/* Callback for ST_MDCX_RAN: wait for assignment completion, send MDCX for CN side to MGW */
665static void fsm_mdcx_ran_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
666{
667 struct mgcp_ctx *mgcp_ctx = data;
668 struct mgcp_client *mgcp;
669 struct gsm_trans *trans;
Neels Hofmeyrc036b792018-11-29 22:37:51 +0100670 struct ran_conn *conn;
Philipp Maier621ba032017-11-07 17:19:25 +0100671 struct mgcp_msg mgcp_msg;
672 struct msgb *msg;
673 int rc;
674
675 OSMO_ASSERT(mgcp_ctx);
676 mgcp = mgcp_ctx->mgcp;
677 OSMO_ASSERT(mgcp);
678 trans = mgcp_ctx->trans;
679 OSMO_ASSERT(trans);
680 conn = trans->conn;
681 OSMO_ASSERT(conn);
682
683 switch (event) {
684 case EV_ASSIGN:
685 break;
686 default:
Philipp Maier4eef20b2018-03-13 13:07:45 +0100687 handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100688 return;
689 }
690
691 LOGPFSML(fi, LOGL_DEBUG,
Pau Espin Pedrol9fac9852018-03-17 01:54:32 +0100692 "MDCX/RAN: completing connection for the CN side on MGW endpoint:%p, RAN expects RTP input on address %s:%u\n",
Philipp Maier621ba032017-11-07 17:19:25 +0100693 mgcp_ctx->rtp_endpoint, conn->rtp.remote_addr_ran, conn->rtp.remote_port_ran);
694
695 /* Generate MGCP message string */
696 mgcp_msg = (struct mgcp_msg) {
697 .verb = MGCP_VERB_MDCX,
698 .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_ID |
699 MGCP_MSG_PRESENCE_CONN_MODE | MGCP_MSG_PRESENCE_AUDIO_IP |
700 MGCP_MSG_PRESENCE_AUDIO_PORT),
Philipp Maiera2353c62018-02-08 14:15:59 +0100701 .call_id = mgcp_ctx->call_id,
Philipp Maier621ba032017-11-07 17:19:25 +0100702 .conn_id = mgcp_ctx->conn_id_ran,
703 .conn_mode = MGCP_CONN_RECV_SEND,
704 .audio_ip = conn->rtp.remote_addr_ran,
Philipp Maier8ad3dac2018-08-07 13:00:14 +0200705 .audio_port = conn->rtp.remote_port_ran,
706 .codecs[0] = conn->rtp.codec_ran,
707 .codecs_len = 1
Philipp Maier621ba032017-11-07 17:19:25 +0100708 };
Philipp Maiera2353c62018-02-08 14:15:59 +0100709 if (osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->rtp_endpoint, sizeof(mgcp_msg.endpoint)) >=
Philipp Maier621ba032017-11-07 17:19:25 +0100710 MGCP_ENDPOINT_MAXLEN) {
Philipp Maier3a776522018-02-22 12:00:00 +0100711 handle_error(mgcp_ctx, MGCP_ERR_TOOLONG, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100712 return;
713 }
Philipp Maiera2353c62018-02-08 14:15:59 +0100714
Philipp Maier621ba032017-11-07 17:19:25 +0100715 msg = mgcp_msg_gen(mgcp, &mgcp_msg);
716 OSMO_ASSERT(msg);
717
718 /* Transmit MGCP message to MGW */
719 mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
720 rc = mgcp_client_tx(mgcp, msg, mgw_mdcx_ran_resp_cb, mgcp_ctx);
721 if (rc < 0) {
Philipp Maier4eef20b2018-03-13 13:07:45 +0100722 handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100723 return;
724 }
725
726 osmo_fsm_inst_state_chg(fi, ST_MDCX_RAN_COMPL, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
727}
728
729/* Callback for MGCP-Client: handle response for CN associated CRCX */
730static void mgw_mdcx_ran_resp_cb(struct mgcp_response *r, void *priv)
731{
732 struct mgcp_ctx *mgcp_ctx = priv;
733
734 OSMO_ASSERT(mgcp_ctx);
735
736 if (r->head.response_code != 200) {
737 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
738 "MDCX/RAN: response yields error: %d %s\n", r->head.response_code, r->head.comment);
Philipp Maier4eef20b2018-03-13 13:07:45 +0100739 handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100740 return;
741 }
742
743 /* Notify the FSM that we got the response. */
744 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_MDCX_RAN_RESP, mgcp_ctx);
745}
746
747/* Callback for ST_MDCX_RAN_COMPL: check MGW response */
748static void fsm_mdcx_ran_compl_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
749{
750 struct mgcp_ctx *mgcp_ctx = data;
751 OSMO_ASSERT(mgcp_ctx);
752
753 switch (event) {
754 case EV_MDCX_RAN_RESP:
755 break;
756 default:
Philipp Maier4eef20b2018-03-13 13:07:45 +0100757 handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100758 return;
759 }
760
761 LOGPFSML(fi, LOGL_DEBUG, "call active, waiting for teardown...\n");
762 osmo_fsm_inst_state_chg(fi, ST_CALL, 0, 0);
763}
764
765static void mgw_dlcx_all_resp_cb(struct mgcp_response *r, void *priv);
766
767/* Callback for ST_CALL: call is active, send DLCX for both sides on teardown */
768static void fsm_call_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
769{
770
771 struct mgcp_ctx *mgcp_ctx = (struct mgcp_ctx *)data;
772 struct mgcp_client *mgcp;
773 struct mgcp_msg mgcp_msg;
774 struct msgb *msg;
775 int rc;
776
777 OSMO_ASSERT(mgcp_ctx);
778 mgcp = mgcp_ctx->mgcp;
779 OSMO_ASSERT(mgcp);
780
781 LOGPFSML(fi, LOGL_DEBUG,
Philipp Maierd997fa12018-04-11 15:23:30 +0200782 "DLCX: removing connection for the RAN and CN side on MGW endpoint:%s...\n", mgcp_ctx->rtp_endpoint);
Philipp Maier621ba032017-11-07 17:19:25 +0100783
Philipp Maier621ba032017-11-07 17:19:25 +0100784 /* Generate MGCP message string */
785 mgcp_msg = (struct mgcp_msg) {
786 .verb = MGCP_VERB_DLCX,
787 .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID),
Philipp Maiera2353c62018-02-08 14:15:59 +0100788 .call_id = mgcp_ctx->call_id
Philipp Maier621ba032017-11-07 17:19:25 +0100789 };
Philipp Maiera2353c62018-02-08 14:15:59 +0100790 if (osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->rtp_endpoint, sizeof(mgcp_msg.endpoint)) >=
Philipp Maier621ba032017-11-07 17:19:25 +0100791 MGCP_ENDPOINT_MAXLEN) {
Philipp Maier3a776522018-02-22 12:00:00 +0100792 handle_error(mgcp_ctx, MGCP_ERR_TOOLONG, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100793 return;
794 }
Philipp Maiera2353c62018-02-08 14:15:59 +0100795
Philipp Maier621ba032017-11-07 17:19:25 +0100796 msg = mgcp_msg_gen(mgcp, &mgcp_msg);
797 OSMO_ASSERT(msg);
798
799 /* Transmit MGCP message to MGW */
800 mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
801 rc = mgcp_client_tx(mgcp, msg, mgw_dlcx_all_resp_cb, mgcp_ctx);
802 if (rc < 0) {
Philipp Maier4eef20b2018-03-13 13:07:45 +0100803 handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100804 return;
805 }
806
807 osmo_fsm_inst_state_chg(fi, ST_HALT, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
808}
809
810/* Callback for MGCP-Client: handle response for CN associated CRCX */
811static void mgw_dlcx_all_resp_cb(struct mgcp_response *r, void *priv)
812{
813 struct mgcp_ctx *mgcp_ctx = priv;
814
815 OSMO_ASSERT(mgcp_ctx);
816
Harald Welte33d61e72018-02-10 10:43:38 +0100817 /* DLCX is the only command where 250 is permitted as positive result */
818 if (r->head.response_code != 200 && r->head.response_code != 250) {
Philipp Maier621ba032017-11-07 17:19:25 +0100819 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
820 "DLCX: response yields error: %d %s\n", r->head.response_code, r->head.comment);
Philipp Maier4eef20b2018-03-13 13:07:45 +0100821 handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100822 return;
823 }
824
825 /* Notify the FSM that we got the response. */
826 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_DLCX_ALL_RESP, mgcp_ctx);
827}
828
829/* Callback for ST_HALT: Terminate the state machine */
830static void fsm_halt_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
831{
832 struct mgcp_ctx *mgcp_ctx = data;
Philipp Maieraddf63b2018-03-14 13:37:44 +0100833 struct mgcp_client *mgcp;
Philipp Maier621ba032017-11-07 17:19:25 +0100834
835 OSMO_ASSERT(mgcp_ctx);
Philipp Maieraddf63b2018-03-14 13:37:44 +0100836 mgcp = mgcp_ctx->mgcp;
837 OSMO_ASSERT(mgcp);
Philipp Maier621ba032017-11-07 17:19:25 +0100838
839 /* NOTE: We must not free the context information now, we have to
840 * wait until msc_mgcp_call_release() is called. Then we are sure
841 * that the logic controlling us is fully aware that the context
842 * information is freed. If we would free early now the controlling
843 * logic might mistakenly think that the context info is still alive,
844 * so lets keep the context info until we are explicitly asked for
845 * throwing it away. */
846 if (mgcp_ctx->free_ctx) {
Philipp Maieraddf63b2018-03-14 13:37:44 +0100847 /* Be sure that there is no pending MGW transaction */
848 mgcp_client_cancel(mgcp, mgcp_ctx->mgw_pending_trans);
849
850 /* Free FSM and its context information */
Philipp Maier621ba032017-11-07 17:19:25 +0100851 osmo_fsm_inst_free(mgcp_ctx->fsm);
852 talloc_free(mgcp_ctx);
853 return;
854 }
855
856 osmo_fsm_inst_state_chg(fi, ST_HALT, MGCP_REL_TIMEOUT, MGCP_REL_TIMEOUT_TIMER_NR);
857}
858
859static struct osmo_fsm_state fsm_msc_mgcp_states[] = {
860
861 /* Startup state machine, send CRCX for RAN side. */
862 [ST_CRCX_RAN] = {
863 .in_event_mask = S(EV_INIT),
864 .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_CRCX_CN),
865 .name = OSMO_STRINGIFY(ST_CRCX_RAN),
866 .action = fsm_crcx_ran_cb,
867 },
868 /* When the response to the RAN CRCX is received, then proceed with
869 sending the CRCX for CN side */
870 [ST_CRCX_CN] = {
871 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_CRCX_RAN_RESP),
872 .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_CRCX_COMPL),
873 .name = OSMO_STRINGIFY(ST_CRCX_CN),
874 .action = fsm_crcx_cn_cb,
875 },
876 /* Complete the CRCX phase by starting the assignment. Depending on the
877 * RAT (Radio Access Technology), this will either trigger an
878 * Assignment Request on the A-Interface or an RAB-Assignment on the
879 * IU-interface */
880 [ST_CRCX_COMPL] = {
881 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_CRCX_CN_RESP),
882 .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_MDCX_CN),
883 .name = OSMO_STRINGIFY(ST_CRCX_COMPL),
884 .action = fsm_crcx_compl,
885 },
886 /* Wait for MSC to complete the assignment request, when complete, we
887 * will enter the MDCX phase by sending an MDCX for the CN side to the
888 * MGW */
889 [ST_MDCX_CN] = {
890 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_CONNECT),
891 .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_MDCX_CN_COMPL),
892 .name = OSMO_STRINGIFY(ST_MDCX_CN),
893 .action = fsm_mdcx_cn_cb,
894 },
895 /* We arrive in this state when the MDCX phase for the CN side has
896 * completed we will check the IP/Port of the RAN connection. If this
897 * data is valid we may continue with the MDCX phase for the RAN side.
898 * If not we wait until the assinment completes on the A or on the IuCS
899 * interface. The completion of the assignment will fill in the port and
900 * IP-Address of the RAN side and way may continue then. */
901 [ST_MDCX_CN_COMPL] = {
902 .in_event_mask = S(EV_TEARDOWN) | S(EV_MDCX_CN_RESP),
903 .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_MDCX_RAN),
904 .name = OSMO_STRINGIFY(ST_MDCX_CN_COMPL),
905 .action = fsm_mdcx_cn_compl_cb,
906 },
907 /* When the response for the CN MDCX is received, send the MDCX for the
908 * RAN side to the MGW */
909 [ST_MDCX_RAN] = {
910 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_ASSIGN),
911 .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_MDCX_RAN_COMPL),
912 .name = OSMO_STRINGIFY(ST_MDCX_RAN),
913 .action = fsm_mdcx_ran_cb,
914 },
915 /* The RAN side MDCX phase is complete when the response is received
916 * from the MGW. The call is then active, we change to ST_CALL and wait
917 * there until the call ends. */
918 [ST_MDCX_RAN_COMPL] = {
919 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_MDCX_RAN_RESP),
920 .out_state_mask = S(ST_HALT) | S(ST_CALL),
921 .name = OSMO_STRINGIFY(ST_MDCX_RAN_COMPL),
922 .action = fsm_mdcx_ran_compl_cb,
923 },
924 /* We are now in the active call phase, wait until the call is done
925 * and send a DLCX then to remove all connections from the MGW */
926 [ST_CALL] = {
927 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR),
928 .out_state_mask = S(ST_HALT),
929 .name = OSMO_STRINGIFY(ST_CALL),
930 .action = fsm_call_cb,
931 },
932 /* When the MGW confirms that the connections are terminated, then halt
933 * the state machine. */
934 [ST_HALT] = {
935 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_DLCX_ALL_RESP),
936 .out_state_mask = S(ST_HALT),
937 .name = OSMO_STRINGIFY(ST_HALT),
938 .action = fsm_halt_cb,
939 },
940};
941
942/* State machine definition */
943static struct osmo_fsm fsm_msc_mgcp = {
Neels Hofmeyra7259702018-12-18 22:05:24 +0100944 .name = "msc-mgcp",
Philipp Maier621ba032017-11-07 17:19:25 +0100945 .states = fsm_msc_mgcp_states,
946 .num_states = ARRAY_SIZE(fsm_msc_mgcp_states),
947 .log_subsys = DMGCP,
948 .timer_cb = fsm_timeout_cb,
Philipp Maiere4f91722018-02-26 15:20:49 +0100949 .event_names = msc_mgcp_fsm_evt_names,
Philipp Maier621ba032017-11-07 17:19:25 +0100950};
951
Neels Hofmeyra35712d2018-12-20 03:03:44 +0100952/* Try to invoke call assignment and set trans->cc.assignment_started flag if invoked.
Neels Hofmeyrb16259f2018-12-20 02:57:56 +0100953 * This is relevant for already ongoing calls -- scenario:
954 * - subscriber is in an active voice call,
955 * - another call is coming in.
956 * For the second call coming in, we must wait to establish RTP and assignment until the first call is CC-Disconnected.
957 */
958int msc_mgcp_try_call_assignment(struct gsm_trans *trans)
959{
960 struct ran_conn *conn = trans->conn;
Neels Hofmeyra35712d2018-12-20 03:03:44 +0100961 if (trans->cc.assignment_started)
Neels Hofmeyrb16259f2018-12-20 02:57:56 +0100962 return 0;
Neels Hofmeyrfcf49d32018-12-20 02:59:32 +0100963 if (conn->rtp.mgcp_ctx) {
964 LOGPFSMSL(conn->fi, DMGCP, LOGL_INFO, "Another call is already ongoing, not assigning yet\n");
965 return 0;
966 }
Neels Hofmeyrb16259f2018-12-20 02:57:56 +0100967 LOGPFSMSL(conn->fi, DMGCP, LOGL_INFO, "Starting call assignment\n");
Neels Hofmeyra35712d2018-12-20 03:03:44 +0100968 trans->cc.assignment_started = true;
Neels Hofmeyrb16259f2018-12-20 02:57:56 +0100969 return msc_mgcp_call_assignment(trans);
970}
971
Philipp Maier621ba032017-11-07 17:19:25 +0100972/* Notify that a new call begins. This will create a connection for the
973 * RAN and the CN on the MGW.
974 * Parameter:
975 * trans: transaction context.
976 * Returns -EINVAL on error, 0 on success. */
977int msc_mgcp_call_assignment(struct gsm_trans *trans)
978{
979 struct mgcp_ctx *mgcp_ctx;
Philipp Maier621ba032017-11-07 17:19:25 +0100980 static bool fsm_registered = false;
Neels Hofmeyrc036b792018-11-29 22:37:51 +0100981 struct ran_conn *conn;
Philipp Maier621ba032017-11-07 17:19:25 +0100982 struct mgcp_client *mgcp;
983
984 OSMO_ASSERT(trans);
985
986 if (!trans->conn) {
987 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid conn, call assignment failed\n",
988 vlr_subscr_name(trans->vsub));
989 return -EINVAL;
990 }
991
992 conn = trans->conn;
993 mgcp = conn->network->mgw.client;
994 OSMO_ASSERT(mgcp);
995
996 if (conn->rtp.mgcp_ctx) {
997 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) double assignment detected, dropping...\n",
998 vlr_subscr_name(trans->vsub));
999 return -EINVAL;
1000 }
1001
1002#ifdef BUILD_IU
1003 /* FIXME: HACK. where to scope the RAB Id? At the conn / subscriber / ranap_ue_conn_ctx? */
1004 static uint8_t next_iu_rab_id = 1;
Neels Hofmeyr7814a832018-12-26 00:40:18 +01001005 if (conn->via_ran == OSMO_RAT_UTRAN_IU)
Philipp Maier621ba032017-11-07 17:19:25 +01001006 conn->iu.rab_id = next_iu_rab_id++;
1007#endif
1008
Philipp Maier621ba032017-11-07 17:19:25 +01001009 /* Register the fsm description (if not already done) */
1010 if (fsm_registered == false) {
1011 osmo_fsm_register(&fsm_msc_mgcp);
1012 fsm_registered = true;
1013 }
1014
1015 /* Allocate and configure a new fsm instance */
1016 mgcp_ctx = talloc_zero(NULL, struct mgcp_ctx);
1017 OSMO_ASSERT(mgcp_ctx);
Neels Hofmeyr2c268692018-12-19 01:01:46 +01001018
1019 if (osmo_strlcpy(mgcp_ctx->rtp_endpoint, mgcp_client_rtpbridge_wildcard(mgcp), sizeof(mgcp_ctx->rtp_endpoint))
1020 >= sizeof(mgcp_ctx->rtp_endpoint)) {
Philipp Maiera2353c62018-02-08 14:15:59 +01001021 talloc_free(mgcp_ctx);
Neels Hofmeyr2c268692018-12-19 01:01:46 +01001022 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) endpoint identifier exceeds maximum length: %s\n",
1023 vlr_subscr_name(trans->vsub), osmo_quote_str(mgcp_client_rtpbridge_wildcard(mgcp), -1));
Philipp Maiera2353c62018-02-08 14:15:59 +01001024 return -EINVAL;
1025 }
Neels Hofmeyra7259702018-12-18 22:05:24 +01001026 mgcp_ctx->fsm = osmo_fsm_inst_alloc(&fsm_msc_mgcp, NULL, NULL, LOGL_DEBUG, NULL);
Philipp Maier621ba032017-11-07 17:19:25 +01001027 OSMO_ASSERT(mgcp_ctx->fsm);
Neels Hofmeyra7259702018-12-18 22:05:24 +01001028 osmo_fsm_inst_update_id_f(mgcp_ctx->fsm, "%s_%s_trans%d",
1029 vlr_subscr_name(trans->vsub), ran_conn_get_conn_id(conn), trans->transaction_id);
Philipp Maier621ba032017-11-07 17:19:25 +01001030 mgcp_ctx->fsm->priv = mgcp_ctx;
1031 mgcp_ctx->mgcp = mgcp;
1032 mgcp_ctx->trans = trans;
Philipp Maiera2353c62018-02-08 14:15:59 +01001033 mgcp_ctx->call_id = trans->callref;
Philipp Maier621ba032017-11-07 17:19:25 +01001034
1035 /* start state machine */
1036 OSMO_ASSERT(mgcp_ctx->fsm->state == ST_CRCX_RAN);
1037 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_INIT, mgcp_ctx);
1038
1039 conn->rtp.mgcp_ctx = mgcp_ctx;
1040
1041 LOGP(DMGCP, LOGL_DEBUG, "(subscriber:%s) call assignment initiated\n",
1042 vlr_subscr_name(conn->vsub));
1043
1044 return 0;
1045}
1046
Neels Hofmeyr85cb2532018-12-11 18:59:27 +01001047/* Inform the FSM that the assignment (RAN connection) is now complete.
1048 * Parameter:
1049 * conn: RAN connection context.
1050 * port: port number of the remote leg.
1051 * addr: IP-address of the remote leg.
1052 * Returns -EINVAL on error, 0 on success. */
1053int msc_mgcp_ass_complete(struct ran_conn *conn, uint16_t port, char *addr)
Philipp Maier621ba032017-11-07 17:19:25 +01001054{
Neels Hofmeyr85cb2532018-12-11 18:59:27 +01001055 struct mgcp_ctx *mgcp_ctx;
Philipp Maier621ba032017-11-07 17:19:25 +01001056
Neels Hofmeyr85cb2532018-12-11 18:59:27 +01001057 OSMO_ASSERT(conn);
Philipp Maier621ba032017-11-07 17:19:25 +01001058
1059 if (port == 0) {
Neels Hofmeyr85cb2532018-12-11 18:59:27 +01001060 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid remote call leg port, assignment completion failed\n",
1061 vlr_subscr_name(conn->vsub));
Philipp Maier621ba032017-11-07 17:19:25 +01001062 return -EINVAL;
1063 }
1064 if (!addr || strlen(addr) <= 0) {
Neels Hofmeyr85cb2532018-12-11 18:59:27 +01001065 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) missing remote call leg address, assignment completion failed\n",
1066 vlr_subscr_name(conn->vsub));
Philipp Maier621ba032017-11-07 17:19:25 +01001067 return -EINVAL;
1068 }
1069
Neels Hofmeyr85cb2532018-12-11 18:59:27 +01001070 mgcp_ctx = conn->rtp.mgcp_ctx;
1071 if (!mgcp_ctx) {
1072 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid mgcp context, assignment completion failed.\n",
1073 vlr_subscr_name(conn->vsub));
1074 return -EINVAL;
1075 }
1076
1077 /* Memorize port and IP-Address of the remote RAN call leg. We need this
1078 * information at latest when we enter the MDCX phase for the RAN side. */
Neels Hofmeyr212c0c92018-12-07 14:47:34 +01001079 conn->rtp.remote_port_ran = port;
Neels Hofmeyr85cb2532018-12-11 18:59:27 +01001080 osmo_strlcpy(conn->rtp.remote_addr_ran, addr, sizeof(conn->rtp.remote_addr_ran));
Philipp Maier621ba032017-11-07 17:19:25 +01001081
Neels Hofmeyr85cb2532018-12-11 18:59:27 +01001082 LOGP(DMGCP, LOGL_DEBUG, "(subscriber:%s) assignment completed, rtp %s:%d.\n",
1083 vlr_subscr_name(conn->vsub), conn->rtp.remote_addr_ran, port);
Philipp Maier621ba032017-11-07 17:19:25 +01001084
Neels Hofmeyr85cb2532018-12-11 18:59:27 +01001085 /* Note: We only dispatch the event if we are really waiting for the
1086 * assignment, if we are not yet waiting, there is no need to loudly
1087 * broadcast an event that the all other states do not understand anyway */
1088 if (mgcp_ctx->fsm->state == ST_MDCX_RAN)
1089 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_ASSIGN, mgcp_ctx);
1090
1091 return 0;
Philipp Maier621ba032017-11-07 17:19:25 +01001092}
1093
Neels Hofmeyrf383d412018-12-19 17:05:13 +01001094/* Notify the MGCP context that Assignment failed.
1095 * This will end the "ringing" on the other call leg, and will usually result in L3 and conn release (i.e. when no other
1096 * transactions are still pending, which is usually the case). */
1097int msc_mgcp_ass_fail(struct ran_conn *conn)
1098{
1099 struct mgcp_ctx *mgcp_ctx;
1100
1101 OSMO_ASSERT(conn);
1102
1103 mgcp_ctx = conn->rtp.mgcp_ctx;
1104 if (!mgcp_ctx)
1105 return -EINVAL;
1106
1107 LOGPFSMSL(conn->fi, DMGCP, LOGL_ERROR, "Assignment failed\n");
1108
1109 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_TEARDOWN_ERROR, mgcp_ctx);
1110 return 0;
1111}
1112
Philipp Maier621ba032017-11-07 17:19:25 +01001113/* Make the connection of a previously assigned call complete
1114 * Parameter:
1115 * trans: transaction context.
1116 * port: port number of the remote leg.
1117 * addr: IP-address of the remote leg.
1118 * Returns -EINVAL on error, 0 on success. */
1119int msc_mgcp_call_complete(struct gsm_trans *trans, uint16_t port, char *addr)
1120{
1121 struct mgcp_ctx *mgcp_ctx;
Neels Hofmeyrc036b792018-11-29 22:37:51 +01001122 struct ran_conn *conn;
Philipp Maier621ba032017-11-07 17:19:25 +01001123
1124 OSMO_ASSERT(trans);
1125 OSMO_ASSERT(addr);
1126
1127 if (port == 0) {
1128 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid remote call leg port, call completion failed\n",
1129 vlr_subscr_name(trans->vsub));
1130 return -EINVAL;
1131 }
1132 if (!addr || strlen(addr) <= 0) {
1133 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) missing remote call leg address, call completion failed\n",
1134 vlr_subscr_name(trans->vsub));
1135 return -EINVAL;
1136 }
1137 if (!trans->conn) {
1138 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid conn, call completion failed\n",
1139 vlr_subscr_name(trans->vsub));
1140 return -EINVAL;
1141 }
1142 if (!trans->conn->rtp.mgcp_ctx) {
1143 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid mgcp context, call completion failed.\n",
1144 vlr_subscr_name(trans->vsub));
1145 return -EINVAL;
1146 }
1147 if (!trans->conn->rtp.mgcp_ctx->fsm) {
1148 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) no FSM, call completion failed\n",
1149 vlr_subscr_name(trans->vsub));
1150 return -EINVAL;
1151 }
1152
1153 mgcp_ctx = trans->conn->rtp.mgcp_ctx;
1154
1155 /* The FSM should already have passed all CRCX phases and be ready to move
1156 * on with the MDCX phases. */
1157 if (mgcp_ctx->fsm->state != ST_MDCX_CN) {
1158 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid call state, call completion failed\n",
1159 vlr_subscr_name(trans->vsub));
1160 return -EINVAL;
1161 }
1162
1163 conn = trans->conn;
1164 osmo_strlcpy(conn->rtp.remote_addr_cn, addr, sizeof(conn->rtp.remote_addr_cn));
1165 conn->rtp.remote_port_cn = port;
1166
1167 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_CONNECT, mgcp_ctx);
1168
1169 LOGP(DMGCP, LOGL_DEBUG, "(subscriber:%s) call completion initiated\n",
1170 vlr_subscr_name(conn->vsub));
1171
1172 return 0;
1173}
1174
Neels Hofmeyrfcf49d32018-12-20 02:59:32 +01001175static struct gsm_trans *find_waiting_call(struct ran_conn *conn)
1176{
1177 struct gsm_trans *trans;
1178 struct gsm_network *net = conn->network;
1179
1180 llist_for_each_entry(trans, &net->trans_list, entry) {
1181 if (trans->conn != conn)
1182 continue;
1183 if (trans->protocol != GSM48_PDISC_CC)
1184 continue;
1185 if (trans->cc.assignment_started)
1186 continue;
1187 return trans;
1188 }
1189 return NULL;
1190}
1191
Philipp Maier621ba032017-11-07 17:19:25 +01001192/* Release ongoing call.
1193 * Parameter:
1194 * trans: connection context.
1195 * Returns -EINVAL on error, 0 on success. */
1196int msc_mgcp_call_release(struct gsm_trans *trans)
1197{
1198 struct mgcp_ctx *mgcp_ctx;
Neels Hofmeyrc43b9662018-12-21 01:34:48 +01001199 struct ran_conn *conn = trans->conn;
Neels Hofmeyrfcf49d32018-12-20 02:59:32 +01001200 struct gsm_trans *waiting_trans;
Philipp Maier621ba032017-11-07 17:19:25 +01001201
1202 OSMO_ASSERT(trans);
1203
Neels Hofmeyrc43b9662018-12-21 01:34:48 +01001204 if (!conn) {
Philipp Maier621ba032017-11-07 17:19:25 +01001205 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid conn, call release failed\n",
1206 vlr_subscr_name(trans->vsub));
1207 return -EINVAL;
1208 }
Neels Hofmeyrc43b9662018-12-21 01:34:48 +01001209 mgcp_ctx = conn->rtp.mgcp_ctx;
1210 if (!mgcp_ctx) {
Philipp Maier621ba032017-11-07 17:19:25 +01001211 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid mgcp context, call release failed.\n",
1212 vlr_subscr_name(trans->vsub));
1213 return -EINVAL;
1214 }
Neels Hofmeyrc43b9662018-12-21 01:34:48 +01001215 if (!mgcp_ctx->fsm) {
Philipp Maier621ba032017-11-07 17:19:25 +01001216 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) no FSM, call release failed\n",
1217 vlr_subscr_name(trans->vsub));
1218 return -EINVAL;
1219 }
1220
Neels Hofmeyr3350bf92018-12-21 01:35:21 +01001221 if (mgcp_ctx->trans != trans) {
1222 LOGP(DMGCP, LOGL_DEBUG, "(ti %02x %s) call release for background CC transaction\n",
1223 trans->transaction_id, vlr_subscr_name(trans->vsub));
1224 return 0;
1225 }
1226
Neels Hofmeyrc43b9662018-12-21 01:34:48 +01001227 LOGP(DMGCP, LOGL_DEBUG, "(ti %02x %s) Call release: tearing down MGW endpoint\n",
1228 trans->transaction_id, vlr_subscr_name(trans->vsub));
Philipp Maier621ba032017-11-07 17:19:25 +01001229
1230 /* Inform the FSM that as soon as it reaches ST_HALT it may free
1231 * all context information immediately */
1232 mgcp_ctx->free_ctx = true;
1233
1234 /* Initaite teardown, regardless of which state we are currently
1235 * in */
1236 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_TEARDOWN, mgcp_ctx);
1237
1238 /* Prevent any further operation that is triggered from outside by
1239 * overwriting the context pointer with NULL. The FSM will now
1240 * take care for a graceful shutdown and when done it will free
1241 * all related context information */
Neels Hofmeyrc43b9662018-12-21 01:34:48 +01001242 conn->rtp.mgcp_ctx = NULL;
Philipp Maier621ba032017-11-07 17:19:25 +01001243
Neels Hofmeyrfcf49d32018-12-20 02:59:32 +01001244 /* If there is another call still waiting to be activated, this is the time when the mgcp_ctx is available again
1245 * and the other call can start assigning. */
1246 waiting_trans = find_waiting_call(conn);
1247 if (waiting_trans) {
1248 LOGP(DMGCP, LOGL_DEBUG, "(ti %02x %s) Call waiting: starting Assignment\n",
1249 waiting_trans->transaction_id, vlr_subscr_name(trans->vsub));
1250 msc_mgcp_try_call_assignment(waiting_trans);
1251 }
1252
Philipp Maier621ba032017-11-07 17:19:25 +01001253 return 0;
1254}