blob: bd60c132809337db5ffcc4904decc2e87e963d0c [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
47#define MGCP_RAN_TIMEOUT 10 /* in seconds */
48#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
54#define MGCP_ENDPOINT_FORMAT "%x@mgw"
55
56/* Some internal cause codes to indicate fault condition inside the FSM */
57enum msc_mgcp_cause_code {
58 MGCP_ERR_MGW_FAIL,
59 MGCP_ERR_MGW_INVAL_RESP,
60 MGCP_ERR_MGW_TX_FAIL,
61 MGCP_ERR_UNEXP_TEARDOWN,
62 MGCP_ERR_UNSUPP_ADDR_FMT,
63 MGCP_ERR_RAN_TIMEOUT,
64 MGCP_ERR_ASS_TIMEOUT,
65 MGCP_ERR_NOMEM,
66 MGCP_ERR_ASSGMNT_FAIL
67};
68
69/* Human readable respresentation of the faul codes, will be displayed by
70 * handle_error() */
71static const struct value_string msc_mgcp_cause_codes_names[] = {
72 {MGCP_ERR_MGW_FAIL, "operation failed on MGW"},
73 {MGCP_ERR_MGW_INVAL_RESP, "invalid / unparseable response from MGW"},
74 {MGCP_ERR_MGW_TX_FAIL, "failed to transmit MGCP message to MGW"},
75 {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)"},
79 {MGCP_ERR_NOMEM, "out of memory"},
80 {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
137/* A general error handler function. On error we still have an interest to
138 * remove a half open connection (if possible). This function will execute
139 * a controlled jump to the DLCX phase. From there, the FSM will then just
140 * continue like the call were ended normally */
141
142#define handle_error(mgcp_ctx, cause) \
143 _handle_error(mgcp_ctx, cause, __FILE__, __LINE__)
144static void _handle_error(struct mgcp_ctx *mgcp_ctx, enum msc_mgcp_cause_code cause,
145 const char *file, int line)
146{
147 struct osmo_fsm_inst *fi;
148
149 OSMO_ASSERT(mgcp_ctx);
150 fi = mgcp_ctx->fsm;
151 OSMO_ASSERT(fi);
152
153 LOGPFSMLSRC(mgcp_ctx->fsm, LOGL_ERROR, file, line, "%s -- graceful shutdown...\n",
154 get_value_string(msc_mgcp_cause_codes_names, cause));
155
156 /* Set the VM into the state where it waits for the call end */
157 osmo_fsm_inst_state_chg(fi, ST_CALL, 0, 0);
158
159 /* Simulate the call end by sending a teardown event, so that
160 * the FSM proceeds directly with the DLCX */
161 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_TEARDOWN_ERROR, mgcp_ctx);
162}
163
164/* Timer callback to shut down in case of connectivity problems */
165static int fsm_timeout_cb(struct osmo_fsm_inst *fi)
166{
167 struct mgcp_ctx *mgcp_ctx = fi->priv;
168 struct mgcp_client *mgcp;
169
170 OSMO_ASSERT(mgcp_ctx);
171 mgcp = mgcp_ctx->mgcp;
172 OSMO_ASSERT(mgcp);
173
174 if (fi->T == MGCP_MGW_TIMEOUT_TIMER_NR) {
175 /* We were unable to communicate with the MGW, unfortunately
176 * there is no meaningful action we can take now other than
177 * giving up. */
178
179 /* At least release the occupied endpoint ID */
180 mgcp_client_release_endpoint(mgcp_ctx->rtp_endpoint, mgcp);
181
182 /* Cancel the transaction that timed out */
183 mgcp_client_cancel(mgcp, mgcp_ctx->mgw_pending_trans);
184
185 /* Initiate self destruction of the FSM */
186 osmo_fsm_inst_state_chg(fi, ST_HALT, 0, 0);
187 osmo_fsm_inst_dispatch(fi, EV_TEARDOWN_ERROR, mgcp_ctx);
188 } else if (fi->T == MGCP_RAN_TIMEOUT_TIMER_NR) {
189 /* If the logic that controls the RAN is unable to negotiate a
190 * connection, we presumably still have a working connection to
191 * the MGW, we will try to shut down gracefully. */
192 handle_error(mgcp_ctx, MGCP_ERR_RAN_TIMEOUT);
193 } else if (fi->T == MGCP_REL_TIMEOUT_TIMER_NR) {
194 /* Under normal conditions, the MSC logic should always command
195 * to release the call at some point. However, the release may
196 * be missing due to errors in the MSC logic and we may have
197 * reached ST_HALT because of cascading errors and timeouts. In
198 * this and only in this case we will allow ST_HALT to free all
199 * context information on its own authority. */
200 mgcp_ctx->free_ctx = true;
201
202 /* Initiate self destruction of the FSM */
203 osmo_fsm_inst_state_chg(fi, ST_HALT, 0, 0);
204 osmo_fsm_inst_dispatch(fi, EV_TEARDOWN, mgcp_ctx);
205 } else if (fi->T == MGCP_ASS_TIMEOUT_TIMER_NR) {
206 /* There may be rare cases in which the MSC is unable to
207 * complete the call assignment */
208 handle_error(mgcp_ctx, MGCP_ERR_ASS_TIMEOUT);
209 } else {
210 /* Ther must not be any unsolicited timers in this FSM. If so,
211 * we have serious problem. */
212 OSMO_ASSERT(false);
213 }
214
215 return 0;
216}
217
218static void mgw_crcx_ran_resp_cb(struct mgcp_response *r, void *priv);
219
220/* Callback for ST_CRCX_RAN: Send CRCX for RAN side to MGW */
221static void fsm_crcx_ran_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
222{
223 struct mgcp_ctx *mgcp_ctx = data;
224 struct mgcp_client *mgcp;
225 struct mgcp_msg mgcp_msg;
226 struct msgb *msg;
227 int rc;
228
229 OSMO_ASSERT(mgcp_ctx);
230 mgcp = mgcp_ctx->mgcp;
231 OSMO_ASSERT(mgcp);
232
233 mgcp_ctx->rtp_endpoint = mgcp_client_next_endpoint(mgcp);
234
235 LOGPFSML(fi, LOGL_DEBUG,
236 "CRCX/RAN: creating connection for the RAN side on MGW endpoint:0x%x...\n", mgcp_ctx->rtp_endpoint);
237
238 /* Generate MGCP message string */
239 mgcp_msg = (struct mgcp_msg) {
240 .verb = MGCP_VERB_CRCX,
241 .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_MODE),
242 .call_id = mgcp_ctx->rtp_endpoint,
243 .conn_mode = MGCP_CONN_LOOPBACK
244 };
245 if (snprintf(mgcp_msg.endpoint, MGCP_ENDPOINT_MAXLEN, MGCP_ENDPOINT_FORMAT, mgcp_ctx->rtp_endpoint) >=
246 MGCP_ENDPOINT_MAXLEN) {
247 handle_error(mgcp_ctx, MGCP_ERR_NOMEM);
248 return;
249 }
250 msg = mgcp_msg_gen(mgcp, &mgcp_msg);
251 OSMO_ASSERT(msg);
252
253 /* Transmit MGCP message to MGW */
254 mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
255 rc = mgcp_client_tx(mgcp, msg, mgw_crcx_ran_resp_cb, mgcp_ctx);
256 if (rc < 0) {
257 handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL);
258 return;
259 }
260
261 osmo_fsm_inst_state_chg(fi, ST_CRCX_CN, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
262}
263
264/* Callback for MGCP-Client: handle response for RAN associated CRCX */
265static void mgw_crcx_ran_resp_cb(struct mgcp_response *r, void *priv)
266{
267 struct mgcp_ctx *mgcp_ctx = priv;
268 int rc;
269 struct gsm_trans *trans;
270 struct gsm_subscriber_connection *conn;
271
272 OSMO_ASSERT(mgcp_ctx);
273 trans = mgcp_ctx->trans;
274 OSMO_ASSERT(trans);
275 conn = trans->conn;
276 OSMO_ASSERT(conn);
277
278 if (r->head.response_code != 200) {
279 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
280 "CRCX/RAN: response yields error: %d %s\n", r->head.response_code, r->head.comment);
281 handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL);
282 return;
283 }
284
285 /* memorize connection identifier */
286 osmo_strlcpy(mgcp_ctx->conn_id_ran, r->head.conn_id, sizeof(mgcp_ctx->conn_id_ran));
287 LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/RAN: MGW responded with CI: %s\n", mgcp_ctx->conn_id_ran);
288
289 rc = mgcp_response_parse_params(r);
290 if (rc) {
291 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR, "CRCX/RAN: Cannot parse response\n");
292 handle_error(mgcp_ctx, MGCP_ERR_MGW_INVAL_RESP);
293 return;
294 }
295
296 LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/BTS: MGW responded with address %s:%u\n", r->audio_ip, r->audio_port);
297
298 conn->rtp.local_port_ran = r->audio_port;
299 osmo_strlcpy(conn->rtp.local_addr_ran, r->audio_ip, sizeof(conn->rtp.local_addr_ran));
300
301 /* Notify the FSM that we got the response. */
302 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_CRCX_RAN_RESP, mgcp_ctx);
303}
304
305static void mgw_crcx_cn_resp_cb(struct mgcp_response *r, void *priv);
306
307/* Callback for ST_CRCX_CN: check MGW response and send CRCX for CN side to MGW */
308static void fsm_crcx_cn_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
309{
310 struct mgcp_ctx *mgcp_ctx = data;
311 struct mgcp_client *mgcp;
312 struct mgcp_msg mgcp_msg;
313 struct msgb *msg;
314 int rc;
315
316 OSMO_ASSERT(mgcp_ctx);
317 mgcp = mgcp_ctx->mgcp;
318 OSMO_ASSERT(mgcp);
319
320 switch (event) {
321 case EV_CRCX_RAN_RESP:
322 break;
323 default:
324 handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN);
325 return;
326 }
327
328 LOGPFSML(fi, LOGL_DEBUG,
329 "CRCX/CN creating connection for the CN side on MGW endpoint:0x%x...\n", mgcp_ctx->rtp_endpoint);
330
331 /* Generate MGCP message string */
332 mgcp_msg = (struct mgcp_msg) {
333 .verb = MGCP_VERB_CRCX,
334 .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_MODE),
335 .call_id = mgcp_ctx->rtp_endpoint,
336 .conn_mode = MGCP_CONN_LOOPBACK
337 };
338 if (snprintf(mgcp_msg.endpoint, MGCP_ENDPOINT_MAXLEN, MGCP_ENDPOINT_FORMAT, mgcp_ctx->rtp_endpoint) >=
339 MGCP_ENDPOINT_MAXLEN) {
340 handle_error(mgcp_ctx, MGCP_ERR_NOMEM);
341 return;
342 }
343 msg = mgcp_msg_gen(mgcp, &mgcp_msg);
344 OSMO_ASSERT(msg);
345
346 /* Transmit MGCP message to MGW */
347 mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
348 rc = mgcp_client_tx(mgcp, msg, mgw_crcx_cn_resp_cb, mgcp_ctx);
349 if (rc < 0) {
350 handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL);
351 return;
352 }
353
354 osmo_fsm_inst_state_chg(fi, ST_CRCX_COMPL, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
355}
356
357/* Callback for MGCP-Client: handle response for CN associated CRCX */
358static void mgw_crcx_cn_resp_cb(struct mgcp_response *r, void *priv)
359{
360 struct mgcp_ctx *mgcp_ctx = priv;
361 int rc;
362 struct gsm_trans *trans;
363 struct gsm_subscriber_connection *conn;
364
365 OSMO_ASSERT(mgcp_ctx);
366 trans = mgcp_ctx->trans;
367 OSMO_ASSERT(trans);
368 conn = trans->conn;
369 OSMO_ASSERT(conn);
370
371 if (r->head.response_code != 200) {
372 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
373 "CRCX/CN: response yields error: %d %s\n", r->head.response_code, r->head.comment);
374 handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL);
375 return;
376 }
377
378 /* memorize connection identifier */
379 osmo_strlcpy(mgcp_ctx->conn_id_cn, r->head.conn_id, sizeof(mgcp_ctx->conn_id_cn));
380 LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/CN: MGW responded with CI: %s\n", mgcp_ctx->conn_id_cn);
381
382 rc = mgcp_response_parse_params(r);
383 if (rc) {
384 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR, "CRCX/CN: Cannot parse response\n");
385 handle_error(mgcp_ctx, MGCP_ERR_MGW_INVAL_RESP);
386 return;
387 }
388
389 LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/CN: MGW responded with address %s:%u\n", r->audio_ip, r->audio_port);
390
391 conn->rtp.local_port_cn = r->audio_port;
392 osmo_strlcpy(conn->rtp.local_addr_cn, r->audio_ip, sizeof(conn->rtp.local_addr_cn));
393
394 /* Notify the FSM that we got the response. */
395 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_CRCX_CN_RESP, mgcp_ctx);
396}
397
398/* Callback for ST_CRCX_COMPL: check MGW response, start assignment */
399static void fsm_crcx_compl(struct osmo_fsm_inst *fi, uint32_t event, void *data)
400{
401 struct mgcp_ctx *mgcp_ctx = data;
402 struct gsm_trans *trans;
403 struct gsm_subscriber_connection *conn;
404
405 OSMO_ASSERT(mgcp_ctx);
406 trans = mgcp_ctx->trans;
407 OSMO_ASSERT(trans);
408 conn = trans->conn;
409 OSMO_ASSERT(conn);
410
411 switch (event) {
412 case EV_CRCX_CN_RESP:
413 break;
414 default:
415 handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN);
416 return;
417 }
418
419 /* Forward assignment request to A/RANAP */
420 if (conn->via_ran == RAN_UTRAN_IU) {
421#ifdef BUILD_IU
422 /* Assign a voice channel via RANAP on 3G */
423 if (iu_rab_act_cs(trans))
424 goto error;
425#else
426 LOGPFSML(fi, LOGL_ERROR, "Cannot send Iu RAB Assignment: built without Iu support\n");
427 goto error;
428#endif
429 } else if (conn->via_ran == RAN_GERAN_A) {
430 /* Assign a voice channel via A on 2G */
431 if (a_iface_tx_assignment(trans))
432 goto error;
433 } else {
434 /* Unset or unimplemented new RAN type */
435 LOGPFSML(fi, LOGL_ERROR, "Unknown RAN type: %d\n", conn->via_ran);
436 return;
437 }
438
439 /* Respond back to MNCC (if requested) */
440 if (trans->tch_rtp_create) {
441 if (gsm48_tch_rtp_create(trans))
442 goto error;
443 }
444
445 /* Note: When we reach this point then the situation is basically that
446 * we have two sides connected, both are in loopback. The local ports
447 * of the side pointing towards the BSS should be already communicated
448 * and we are waiting now for the BSS to return with the assignment
449 * complete command. */
450 osmo_fsm_inst_state_chg(fi, ST_MDCX_CN, MGCP_RAN_TIMEOUT, MGCP_RAN_TIMEOUT_TIMER_NR);
451 return;
452
453error:
454 handle_error(mgcp_ctx, MGCP_ERR_ASSGMNT_FAIL);
455}
456
457static void mgw_mdcx_cn_resp_cb(struct mgcp_response *r, void *priv);
458
459/* Callback for ST_MDCX_CN: send MDCX for RAN side to MGW */
460static void fsm_mdcx_cn_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
461{
462 struct mgcp_ctx *mgcp_ctx = data;
463 struct mgcp_client *mgcp;
464 struct gsm_trans *trans;
465 struct gsm_subscriber_connection *conn;
466 struct mgcp_msg mgcp_msg;
467 struct msgb *msg;
468 int rc;
469
470 OSMO_ASSERT(mgcp_ctx);
471 mgcp = mgcp_ctx->mgcp;
472 OSMO_ASSERT(mgcp);
473 trans = mgcp_ctx->trans;
474 OSMO_ASSERT(trans);
475 conn = trans->conn;
476 OSMO_ASSERT(conn);
477
478 switch (event) {
479 case EV_CONNECT:
480 break;
481 default:
482 handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN);
483 return;
484 }
485
486 LOGPFSML(fi, LOGL_DEBUG,
487 "MDCX/CN: completing connection for the CN side on MGW endpoint:0x%x, remote leg expects RTP input on address %s:%u\n",
488 mgcp_ctx->rtp_endpoint, conn->rtp.remote_addr_cn, conn->rtp.remote_port_cn);
489
490 /* Generate MGCP message string */
491 mgcp_msg = (struct mgcp_msg) {
492 .verb = MGCP_VERB_MDCX,
493 .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_ID |
494 MGCP_MSG_PRESENCE_CONN_MODE | MGCP_MSG_PRESENCE_AUDIO_IP |
495 MGCP_MSG_PRESENCE_AUDIO_PORT),
496 .call_id = mgcp_ctx->rtp_endpoint,
497 .conn_id = mgcp_ctx->conn_id_cn,
498 .conn_mode = MGCP_CONN_RECV_SEND,
499 .audio_ip = conn->rtp.remote_addr_cn,
500 .audio_port = conn->rtp.remote_port_cn
501 };
502 if (snprintf(mgcp_msg.endpoint, MGCP_ENDPOINT_MAXLEN, MGCP_ENDPOINT_FORMAT, mgcp_ctx->rtp_endpoint) >=
503 MGCP_ENDPOINT_MAXLEN) {
504 handle_error(mgcp_ctx, MGCP_ERR_NOMEM);
505 return;
506 }
507 msg = mgcp_msg_gen(mgcp, &mgcp_msg);
508 OSMO_ASSERT(msg);
509
510 /* Transmit MGCP message to MGW */
511 mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
512 rc = mgcp_client_tx(mgcp, msg, mgw_mdcx_cn_resp_cb, mgcp_ctx);
513 if (rc < 0) {
514 handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL);
515 return;
516 }
517
518 osmo_fsm_inst_state_chg(fi, ST_MDCX_CN_COMPL, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
519}
520
521/* Callback for MGCP-Client: handle response for CN associated CRCX */
522static void mgw_mdcx_cn_resp_cb(struct mgcp_response *r, void *priv)
523{
524 struct mgcp_ctx *mgcp_ctx = priv;
525
526 OSMO_ASSERT(mgcp_ctx);
527
528 if (r->head.response_code != 200) {
529 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
530 "MDCX/CN: response yields error: %d %s\n", r->head.response_code, r->head.comment);
531 handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL);
532 return;
533 }
534
535 /* Notify the FSM that we got the response. */
536 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_MDCX_CN_RESP, mgcp_ctx);
537}
538
539/* Callback for ST_MDCX_CN_COMPL: wait for mgw response, move on with the MDCX
540 * for the RAN side if we already have valid IP/Port data for the RAN sided
541 * RTP stream. */
542static void fsm_mdcx_cn_compl_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
543{
544 struct mgcp_ctx *mgcp_ctx = data;
545 struct gsm_subscriber_connection *conn;
546 struct gsm_trans *trans;
547
548 OSMO_ASSERT(mgcp_ctx);
549 trans = mgcp_ctx->trans;
550 OSMO_ASSERT(trans);
551 conn = trans->conn;
552 OSMO_ASSERT(conn);
553
554 switch (event) {
555 case EV_MDCX_CN_RESP:
556 break;
557 default:
558 handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN);
559 return;
560 }
561
562 /* Enter MDCX phase, but we must be sure that the Assigmnet on the A or
563 * IuCS interface is complete (IP-Address and Port are valid) */
564 osmo_fsm_inst_state_chg(fi, ST_MDCX_RAN, MGCP_ASS_TIMEOUT, MGCP_ASS_TIMEOUT_TIMER_NR);
565
566 /* If we already have a valid remote port and IP-Address from the RAN side
567 * call leg, the assignment has been completed before we got here, so we
568 * may move on immediately */
569 if (conn->rtp.remote_port_ran != 0 || strlen(conn->rtp.remote_addr_ran) > 0)
570 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_ASSIGN, mgcp_ctx);
571}
572
573static void mgw_mdcx_ran_resp_cb(struct mgcp_response *r, void *priv);
574
575/* Callback for ST_MDCX_RAN: wait for assignment completion, send MDCX for CN side to MGW */
576static void fsm_mdcx_ran_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
577{
578 struct mgcp_ctx *mgcp_ctx = data;
579 struct mgcp_client *mgcp;
580 struct gsm_trans *trans;
581 struct gsm_subscriber_connection *conn;
582 struct mgcp_msg mgcp_msg;
583 struct msgb *msg;
584 int rc;
585
586 OSMO_ASSERT(mgcp_ctx);
587 mgcp = mgcp_ctx->mgcp;
588 OSMO_ASSERT(mgcp);
589 trans = mgcp_ctx->trans;
590 OSMO_ASSERT(trans);
591 conn = trans->conn;
592 OSMO_ASSERT(conn);
593
594 switch (event) {
595 case EV_ASSIGN:
596 break;
597 default:
598 handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN);
599 return;
600 }
601
602 LOGPFSML(fi, LOGL_DEBUG,
603 "MDCX/RAN: completing connection for the CN side on MGW endpoint:0x%x, RAN expects RTP input on address %s:%u\n",
604 mgcp_ctx->rtp_endpoint, conn->rtp.remote_addr_ran, conn->rtp.remote_port_ran);
605
606 /* Generate MGCP message string */
607 mgcp_msg = (struct mgcp_msg) {
608 .verb = MGCP_VERB_MDCX,
609 .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_ID |
610 MGCP_MSG_PRESENCE_CONN_MODE | MGCP_MSG_PRESENCE_AUDIO_IP |
611 MGCP_MSG_PRESENCE_AUDIO_PORT),
612 .call_id = mgcp_ctx->rtp_endpoint,
613 .conn_id = mgcp_ctx->conn_id_ran,
614 .conn_mode = MGCP_CONN_RECV_SEND,
615 .audio_ip = conn->rtp.remote_addr_ran,
616 .audio_port = conn->rtp.remote_port_ran
617 };
618 if (snprintf(mgcp_msg.endpoint, MGCP_ENDPOINT_MAXLEN, MGCP_ENDPOINT_FORMAT, mgcp_ctx->rtp_endpoint) >=
619 MGCP_ENDPOINT_MAXLEN) {
620 handle_error(mgcp_ctx, MGCP_ERR_NOMEM);
621 return;
622 }
623 msg = mgcp_msg_gen(mgcp, &mgcp_msg);
624 OSMO_ASSERT(msg);
625
626 /* Transmit MGCP message to MGW */
627 mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
628 rc = mgcp_client_tx(mgcp, msg, mgw_mdcx_ran_resp_cb, mgcp_ctx);
629 if (rc < 0) {
630 handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL);
631 return;
632 }
633
634 osmo_fsm_inst_state_chg(fi, ST_MDCX_RAN_COMPL, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
635}
636
637/* Callback for MGCP-Client: handle response for CN associated CRCX */
638static void mgw_mdcx_ran_resp_cb(struct mgcp_response *r, void *priv)
639{
640 struct mgcp_ctx *mgcp_ctx = priv;
641
642 OSMO_ASSERT(mgcp_ctx);
643
644 if (r->head.response_code != 200) {
645 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
646 "MDCX/RAN: response yields error: %d %s\n", r->head.response_code, r->head.comment);
647 handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL);
648 return;
649 }
650
651 /* Notify the FSM that we got the response. */
652 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_MDCX_RAN_RESP, mgcp_ctx);
653}
654
655/* Callback for ST_MDCX_RAN_COMPL: check MGW response */
656static void fsm_mdcx_ran_compl_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
657{
658 struct mgcp_ctx *mgcp_ctx = data;
659 OSMO_ASSERT(mgcp_ctx);
660
661 switch (event) {
662 case EV_MDCX_RAN_RESP:
663 break;
664 default:
665 handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN);
666 return;
667 }
668
669 LOGPFSML(fi, LOGL_DEBUG, "call active, waiting for teardown...\n");
670 osmo_fsm_inst_state_chg(fi, ST_CALL, 0, 0);
671}
672
673static void mgw_dlcx_all_resp_cb(struct mgcp_response *r, void *priv);
674
675/* Callback for ST_CALL: call is active, send DLCX for both sides on teardown */
676static void fsm_call_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
677{
678
679 struct mgcp_ctx *mgcp_ctx = (struct mgcp_ctx *)data;
680 struct mgcp_client *mgcp;
681 struct mgcp_msg mgcp_msg;
682 struct msgb *msg;
683 int rc;
684
685 OSMO_ASSERT(mgcp_ctx);
686 mgcp = mgcp_ctx->mgcp;
687 OSMO_ASSERT(mgcp);
688
689 LOGPFSML(fi, LOGL_DEBUG,
690 "DLCX: removing connection for the RAN and CN side on MGW endpoint:0x%x...\n", mgcp_ctx->rtp_endpoint);
691
692 /* We now relase the endpoint back to the pool in order to allow
693 * other connections to use this endpoint */
694 mgcp_client_release_endpoint(mgcp_ctx->rtp_endpoint, mgcp);
695
696 /* Generate MGCP message string */
697 mgcp_msg = (struct mgcp_msg) {
698 .verb = MGCP_VERB_DLCX,
699 .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID),
700 .call_id = mgcp_ctx->rtp_endpoint
701 };
702 if (snprintf(mgcp_msg.endpoint, MGCP_ENDPOINT_MAXLEN, MGCP_ENDPOINT_FORMAT, mgcp_ctx->rtp_endpoint) >=
703 MGCP_ENDPOINT_MAXLEN) {
704 handle_error(mgcp_ctx, MGCP_ERR_NOMEM);
705 return;
706 }
707 msg = mgcp_msg_gen(mgcp, &mgcp_msg);
708 OSMO_ASSERT(msg);
709
710 /* Transmit MGCP message to MGW */
711 mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
712 rc = mgcp_client_tx(mgcp, msg, mgw_dlcx_all_resp_cb, mgcp_ctx);
713 if (rc < 0) {
714 handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL);
715 return;
716 }
717
718 osmo_fsm_inst_state_chg(fi, ST_HALT, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
719}
720
721/* Callback for MGCP-Client: handle response for CN associated CRCX */
722static void mgw_dlcx_all_resp_cb(struct mgcp_response *r, void *priv)
723{
724 struct mgcp_ctx *mgcp_ctx = priv;
725
726 OSMO_ASSERT(mgcp_ctx);
727
728 if (r->head.response_code != 200) {
729 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
730 "DLCX: response yields error: %d %s\n", r->head.response_code, r->head.comment);
731 handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL);
732 return;
733 }
734
735 /* Notify the FSM that we got the response. */
736 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_DLCX_ALL_RESP, mgcp_ctx);
737}
738
739/* Callback for ST_HALT: Terminate the state machine */
740static void fsm_halt_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
741{
742 struct mgcp_ctx *mgcp_ctx = data;
743
744 OSMO_ASSERT(mgcp_ctx);
745
746 /* NOTE: We must not free the context information now, we have to
747 * wait until msc_mgcp_call_release() is called. Then we are sure
748 * that the logic controlling us is fully aware that the context
749 * information is freed. If we would free early now the controlling
750 * logic might mistakenly think that the context info is still alive,
751 * so lets keep the context info until we are explicitly asked for
752 * throwing it away. */
753 if (mgcp_ctx->free_ctx) {
754 osmo_fsm_inst_free(mgcp_ctx->fsm);
755 talloc_free(mgcp_ctx);
756 return;
757 }
758
759 osmo_fsm_inst_state_chg(fi, ST_HALT, MGCP_REL_TIMEOUT, MGCP_REL_TIMEOUT_TIMER_NR);
760}
761
762static struct osmo_fsm_state fsm_msc_mgcp_states[] = {
763
764 /* Startup state machine, send CRCX for RAN side. */
765 [ST_CRCX_RAN] = {
766 .in_event_mask = S(EV_INIT),
767 .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_CRCX_CN),
768 .name = OSMO_STRINGIFY(ST_CRCX_RAN),
769 .action = fsm_crcx_ran_cb,
770 },
771 /* When the response to the RAN CRCX is received, then proceed with
772 sending the CRCX for CN side */
773 [ST_CRCX_CN] = {
774 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_CRCX_RAN_RESP),
775 .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_CRCX_COMPL),
776 .name = OSMO_STRINGIFY(ST_CRCX_CN),
777 .action = fsm_crcx_cn_cb,
778 },
779 /* Complete the CRCX phase by starting the assignment. Depending on the
780 * RAT (Radio Access Technology), this will either trigger an
781 * Assignment Request on the A-Interface or an RAB-Assignment on the
782 * IU-interface */
783 [ST_CRCX_COMPL] = {
784 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_CRCX_CN_RESP),
785 .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_MDCX_CN),
786 .name = OSMO_STRINGIFY(ST_CRCX_COMPL),
787 .action = fsm_crcx_compl,
788 },
789 /* Wait for MSC to complete the assignment request, when complete, we
790 * will enter the MDCX phase by sending an MDCX for the CN side to the
791 * MGW */
792 [ST_MDCX_CN] = {
793 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_CONNECT),
794 .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_MDCX_CN_COMPL),
795 .name = OSMO_STRINGIFY(ST_MDCX_CN),
796 .action = fsm_mdcx_cn_cb,
797 },
798 /* We arrive in this state when the MDCX phase for the CN side has
799 * completed we will check the IP/Port of the RAN connection. If this
800 * data is valid we may continue with the MDCX phase for the RAN side.
801 * If not we wait until the assinment completes on the A or on the IuCS
802 * interface. The completion of the assignment will fill in the port and
803 * IP-Address of the RAN side and way may continue then. */
804 [ST_MDCX_CN_COMPL] = {
805 .in_event_mask = S(EV_TEARDOWN) | S(EV_MDCX_CN_RESP),
806 .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_MDCX_RAN),
807 .name = OSMO_STRINGIFY(ST_MDCX_CN_COMPL),
808 .action = fsm_mdcx_cn_compl_cb,
809 },
810 /* When the response for the CN MDCX is received, send the MDCX for the
811 * RAN side to the MGW */
812 [ST_MDCX_RAN] = {
813 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_ASSIGN),
814 .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_MDCX_RAN_COMPL),
815 .name = OSMO_STRINGIFY(ST_MDCX_RAN),
816 .action = fsm_mdcx_ran_cb,
817 },
818 /* The RAN side MDCX phase is complete when the response is received
819 * from the MGW. The call is then active, we change to ST_CALL and wait
820 * there until the call ends. */
821 [ST_MDCX_RAN_COMPL] = {
822 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_MDCX_RAN_RESP),
823 .out_state_mask = S(ST_HALT) | S(ST_CALL),
824 .name = OSMO_STRINGIFY(ST_MDCX_RAN_COMPL),
825 .action = fsm_mdcx_ran_compl_cb,
826 },
827 /* We are now in the active call phase, wait until the call is done
828 * and send a DLCX then to remove all connections from the MGW */
829 [ST_CALL] = {
830 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR),
831 .out_state_mask = S(ST_HALT),
832 .name = OSMO_STRINGIFY(ST_CALL),
833 .action = fsm_call_cb,
834 },
835 /* When the MGW confirms that the connections are terminated, then halt
836 * the state machine. */
837 [ST_HALT] = {
838 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_DLCX_ALL_RESP),
839 .out_state_mask = S(ST_HALT),
840 .name = OSMO_STRINGIFY(ST_HALT),
841 .action = fsm_halt_cb,
842 },
843};
844
845/* State machine definition */
846static struct osmo_fsm fsm_msc_mgcp = {
847 .name = "MGW",
848 .states = fsm_msc_mgcp_states,
849 .num_states = ARRAY_SIZE(fsm_msc_mgcp_states),
850 .log_subsys = DMGCP,
851 .timer_cb = fsm_timeout_cb,
852};
853
854/* Notify that a new call begins. This will create a connection for the
855 * RAN and the CN on the MGW.
856 * Parameter:
857 * trans: transaction context.
858 * Returns -EINVAL on error, 0 on success. */
859int msc_mgcp_call_assignment(struct gsm_trans *trans)
860{
861 struct mgcp_ctx *mgcp_ctx;
862 char name[32];
863 static bool fsm_registered = false;
864 struct gsm_subscriber_connection *conn;
865 struct mgcp_client *mgcp;
866
867 OSMO_ASSERT(trans);
868
869 if (!trans->conn) {
870 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid conn, call assignment failed\n",
871 vlr_subscr_name(trans->vsub));
872 return -EINVAL;
873 }
874
875 conn = trans->conn;
876 mgcp = conn->network->mgw.client;
877 OSMO_ASSERT(mgcp);
878
879 if (conn->rtp.mgcp_ctx) {
880 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) double assignment detected, dropping...\n",
881 vlr_subscr_name(trans->vsub));
882 return -EINVAL;
883 }
884
885#ifdef BUILD_IU
886 /* FIXME: HACK. where to scope the RAB Id? At the conn / subscriber / ranap_ue_conn_ctx? */
887 static uint8_t next_iu_rab_id = 1;
888 if (conn->via_ran == RAN_UTRAN_IU)
889 conn->iu.rab_id = next_iu_rab_id++;
890#endif
891
892 if (snprintf(name, sizeof(name), "MGW_%i", trans->transaction_id) >= sizeof(name))
893 return -EINVAL;
894
895 /* Register the fsm description (if not already done) */
896 if (fsm_registered == false) {
897 osmo_fsm_register(&fsm_msc_mgcp);
898 fsm_registered = true;
899 }
900
901 /* Allocate and configure a new fsm instance */
902 mgcp_ctx = talloc_zero(NULL, struct mgcp_ctx);
903 OSMO_ASSERT(mgcp_ctx);
904
905 mgcp_ctx->fsm = osmo_fsm_inst_alloc(&fsm_msc_mgcp, NULL, NULL, LOGL_DEBUG, name);
906 OSMO_ASSERT(mgcp_ctx->fsm);
907 mgcp_ctx->fsm->priv = mgcp_ctx;
908 mgcp_ctx->mgcp = mgcp;
909 mgcp_ctx->trans = trans;
910
911 /* start state machine */
912 OSMO_ASSERT(mgcp_ctx->fsm->state == ST_CRCX_RAN);
913 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_INIT, mgcp_ctx);
914
915 conn->rtp.mgcp_ctx = mgcp_ctx;
916
917 LOGP(DMGCP, LOGL_DEBUG, "(subscriber:%s) call assignment initiated\n",
918 vlr_subscr_name(conn->vsub));
919
920 return 0;
921}
922
923/* Inform the FSM that the assignment (RAN connection) is now complete.
924 * Parameter:
925 * conn: subscriber connection context.
926 * port: port number of the remote leg.
927 * addr: IP-address of the remote leg.
928 * Returns -EINVAL on error, 0 on success. */
929int msc_mgcp_ass_complete(struct gsm_subscriber_connection *conn, uint16_t port, char *addr)
930{
931 struct mgcp_ctx *mgcp_ctx;
932
933 OSMO_ASSERT(conn);
934
935 if (port == 0) {
936 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid remote call leg port, assignmnet completion failed\n",
937 vlr_subscr_name(conn->vsub));
938 return -EINVAL;
939 }
940 if (!addr || strlen(addr) <= 0) {
941 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) missing remote call leg address, assignmnet completion failed\n",
942 vlr_subscr_name(conn->vsub));
943 return -EINVAL;
944 }
945
946 mgcp_ctx = conn->rtp.mgcp_ctx;
947 if (!mgcp_ctx) {
948 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid mgcp context, assignmnet completion failed.\n",
949 vlr_subscr_name(conn->vsub));
950 return -EINVAL;
951 }
952
953 /* Memorize port and IP-Address of the remote RAN call leg. We need this
954 * information at latest when we enter the MDCX phase for the RAN side. */
955 conn->rtp.remote_port_ran = port;
956 osmo_strlcpy(conn->rtp.remote_addr_ran, addr, sizeof(conn->rtp.remote_addr_ran));
957
958 LOGP(DMGCP, LOGL_DEBUG, "(subscriber:%s) assignmnet completed, rtp %s:%d.\n",
959 vlr_subscr_name(conn->vsub), conn->rtp.remote_addr_ran, port);
960
961 /* Note: We only dispatch the event if we are really waiting for the
962 * assignment, if we are not yet waiting, there is no need to loudly
963 * broadcast an event that the all other states do not understand anyway */
964 if (mgcp_ctx->fsm->state == ST_MDCX_RAN)
965 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_ASSIGN, mgcp_ctx);
966
967 return 0;
968}
969
970/* Make the connection of a previously assigned call complete
971 * Parameter:
972 * trans: transaction context.
973 * port: port number of the remote leg.
974 * addr: IP-address of the remote leg.
975 * Returns -EINVAL on error, 0 on success. */
976int msc_mgcp_call_complete(struct gsm_trans *trans, uint16_t port, char *addr)
977{
978 struct mgcp_ctx *mgcp_ctx;
979 struct gsm_subscriber_connection *conn;
980
981 OSMO_ASSERT(trans);
982 OSMO_ASSERT(addr);
983
984 if (port == 0) {
985 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid remote call leg port, call completion failed\n",
986 vlr_subscr_name(trans->vsub));
987 return -EINVAL;
988 }
989 if (!addr || strlen(addr) <= 0) {
990 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) missing remote call leg address, call completion failed\n",
991 vlr_subscr_name(trans->vsub));
992 return -EINVAL;
993 }
994 if (!trans->conn) {
995 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid conn, call completion failed\n",
996 vlr_subscr_name(trans->vsub));
997 return -EINVAL;
998 }
999 if (!trans->conn->rtp.mgcp_ctx) {
1000 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid mgcp context, call completion failed.\n",
1001 vlr_subscr_name(trans->vsub));
1002 return -EINVAL;
1003 }
1004 if (!trans->conn->rtp.mgcp_ctx->fsm) {
1005 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) no FSM, call completion failed\n",
1006 vlr_subscr_name(trans->vsub));
1007 return -EINVAL;
1008 }
1009
1010 mgcp_ctx = trans->conn->rtp.mgcp_ctx;
1011
1012 /* The FSM should already have passed all CRCX phases and be ready to move
1013 * on with the MDCX phases. */
1014 if (mgcp_ctx->fsm->state != ST_MDCX_CN) {
1015 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid call state, call completion failed\n",
1016 vlr_subscr_name(trans->vsub));
1017 return -EINVAL;
1018 }
1019
1020 conn = trans->conn;
1021 osmo_strlcpy(conn->rtp.remote_addr_cn, addr, sizeof(conn->rtp.remote_addr_cn));
1022 conn->rtp.remote_port_cn = port;
1023
1024 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_CONNECT, mgcp_ctx);
1025
1026 LOGP(DMGCP, LOGL_DEBUG, "(subscriber:%s) call completion initiated\n",
1027 vlr_subscr_name(conn->vsub));
1028
1029 return 0;
1030}
1031
1032/* Release ongoing call.
1033 * Parameter:
1034 * trans: connection context.
1035 * Returns -EINVAL on error, 0 on success. */
1036int msc_mgcp_call_release(struct gsm_trans *trans)
1037{
1038 struct mgcp_ctx *mgcp_ctx;
1039
1040 OSMO_ASSERT(trans);
1041
1042 if (!trans->conn) {
1043 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid conn, call release failed\n",
1044 vlr_subscr_name(trans->vsub));
1045 return -EINVAL;
1046 }
1047 if (!trans->conn->rtp.mgcp_ctx) {
1048 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid mgcp context, call release failed.\n",
1049 vlr_subscr_name(trans->vsub));
1050 return -EINVAL;
1051 }
1052 if (!trans->conn->rtp.mgcp_ctx->fsm) {
1053 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) no FSM, call release failed\n",
1054 vlr_subscr_name(trans->vsub));
1055 return -EINVAL;
1056 }
1057
1058 mgcp_ctx = trans->conn->rtp.mgcp_ctx;
1059
1060 /* Inform the FSM that as soon as it reaches ST_HALT it may free
1061 * all context information immediately */
1062 mgcp_ctx->free_ctx = true;
1063
1064 /* Initaite teardown, regardless of which state we are currently
1065 * in */
1066 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_TEARDOWN, mgcp_ctx);
1067
1068 /* Prevent any further operation that is triggered from outside by
1069 * overwriting the context pointer with NULL. The FSM will now
1070 * take care for a graceful shutdown and when done it will free
1071 * all related context information */
1072 trans->conn->rtp.mgcp_ctx = NULL;
1073
1074 LOGP(DMGCP, LOGL_DEBUG, "(subscriber:%s) call release initiated\n",
1075 vlr_subscr_name(trans->vsub));
1076
1077 return 0;
1078}