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