blob: 0790b2bc4a954e400e20698fa1c732a8725bfc2b [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,
Philipp Maier4eef20b2018-03-13 13:07:45 +010061 MGCP_ERR_MGW_TIMEOUT,
Philipp Maier621ba032017-11-07 17:19:25 +010062 MGCP_ERR_UNEXP_TEARDOWN,
63 MGCP_ERR_UNSUPP_ADDR_FMT,
64 MGCP_ERR_RAN_TIMEOUT,
65 MGCP_ERR_ASS_TIMEOUT,
66 MGCP_ERR_NOMEM,
67 MGCP_ERR_ASSGMNT_FAIL
68};
69
70/* Human readable respresentation of the faul codes, will be displayed by
71 * handle_error() */
72static const struct value_string msc_mgcp_cause_codes_names[] = {
73 {MGCP_ERR_MGW_FAIL, "operation failed on MGW"},
74 {MGCP_ERR_MGW_INVAL_RESP, "invalid / unparseable response from MGW"},
75 {MGCP_ERR_MGW_TX_FAIL, "failed to transmit MGCP message to MGW"},
Philipp Maier4eef20b2018-03-13 13:07:45 +010076 {MGCP_ERR_MGW_TIMEOUT, "request to MGW timed out"},
Philipp Maier621ba032017-11-07 17:19:25 +010077 {MGCP_ERR_UNEXP_TEARDOWN, "unexpected connection teardown"},
78 {MGCP_ERR_UNSUPP_ADDR_FMT, "unsupported network address format used (RAN)"},
79 {MGCP_ERR_RAN_TIMEOUT, "call could not be completed in time (RAN)"},
80 {MGCP_ERR_ASS_TIMEOUT, "assignment could not be completed in time (RAN)"},
81 {MGCP_ERR_NOMEM, "out of memory"},
82 {MGCP_ERR_ASSGMNT_FAIL, "assignment failure (RAN)"},
83 {0, NULL}
84};
85
86enum fsm_msc_mgcp_states {
87 ST_CRCX_RAN,
88 ST_CRCX_CN,
89 ST_CRCX_COMPL,
90 ST_MDCX_CN,
91 ST_MDCX_CN_COMPL,
92 ST_MDCX_RAN,
93 ST_MDCX_RAN_COMPL,
94 ST_CALL,
95 ST_HALT,
96};
97
98enum msc_mgcp_fsm_evt {
99 /* Initial event: start off the state machine */
100 EV_INIT,
101
102 /* External event: Notify that the Assignment is complete and we
103 * may now forward IP/Port of the remote call leg to the MGW */
104 EV_ASSIGN,
105
106 /* External event: Notify that the Call is complete and that the
107 * two half open connections on the MGW should now be connected */
108 EV_CONNECT,
109
110 /* External event: Notify that the call is over and the connections
111 * on the mgw shall be removed */
112 EV_TEARDOWN,
113
114 /* Internal event: An error occurred that requires a controlled
115 * teardown of the RTP connections */
116 EV_TEARDOWN_ERROR,
117
118 /* Internal event: The mgcp_gw has sent its CRCX response for
119 * the RAN side */
120 EV_CRCX_RAN_RESP,
121
122 /* Internal event: The mgcp_gw has sent its CRCX response for
123 * the CN side */
124 EV_CRCX_CN_RESP,
125
126 /* Internal event: The mgcp_gw has sent its MDCX response for
127 * the RAN side */
128 EV_MDCX_RAN_RESP,
129
130 /* Internal event: The mgcp_gw has sent its MDCX response for
131 * the CN side */
132 EV_MDCX_CN_RESP,
133
134 /* Internal event: The mgcp_gw has sent its DLCX response for
135 * the RAN and CN side */
136 EV_DLCX_ALL_RESP,
137};
138
Philipp Maiere4f91722018-02-26 15:20:49 +0100139static const struct value_string msc_mgcp_fsm_evt_names[] = {
140 OSMO_VALUE_STRING(EV_INIT),
141 OSMO_VALUE_STRING(EV_ASSIGN),
142 OSMO_VALUE_STRING(EV_CONNECT),
143 OSMO_VALUE_STRING(EV_TEARDOWN),
144 OSMO_VALUE_STRING(EV_TEARDOWN_ERROR),
145 OSMO_VALUE_STRING(EV_CRCX_RAN_RESP),
146 OSMO_VALUE_STRING(EV_CRCX_CN_RESP),
147 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{
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
Philipp Maier4eef20b2018-03-13 13:07:45 +0100177 /* For the shutdown we have two options. Whenever it makes sense to
178 * send a DLCX to the MGW in order to be sure that the connection is
179 * properly cleaned up, the dlcx flag should be set. In other cases
180 * where a DLCX does not make sense (e.g. the MGW times out), halting
181 * directly is the better options. In those cases, the dlcx flag
182 * should not be set */
183 if (dlcx) {
184 /* Fast-forward the FSM into call state. In this state the FSM
185 * expects either an EV_TEARDOWN or an EV_TEARDOWN_ERROR. When
186 * one of the two events is received a DLCX will be send to
187 * the MGW. After that. The FSM automatically halts but will
188 * still expect a call msc_mgcp_call_release() to be freed
189 * completely */
190 osmo_fsm_inst_state_chg(fi, ST_CALL, 0, 0);
191 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_TEARDOWN_ERROR, mgcp_ctx);
192 } else {
193 /* Halt the state machine immediately. The FSM will not be
194 * freed yet, we stil require the higher layers to call
195 * msc_mgcp_call_release() */
196 osmo_fsm_inst_state_chg(fi, ST_HALT, 0, 0);
197 osmo_fsm_inst_dispatch(fi, EV_TEARDOWN_ERROR, mgcp_ctx);
198 }
Philipp Maier621ba032017-11-07 17:19:25 +0100199
Philipp Maier4eef20b2018-03-13 13:07:45 +0100200 /* Request the higher layers to release the call */
201 mncc_set_cause(&mncc, GSM48_CAUSE_LOC_TRANS_NET,
202 GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
Daniel Willmann58d9dd82018-02-15 17:49:22 +0100203 mncc_tx_to_cc(mgcp_ctx->trans->net, MNCC_REL_REQ, &mncc);
Philipp Maier621ba032017-11-07 17:19:25 +0100204}
205
206/* Timer callback to shut down in case of connectivity problems */
207static int fsm_timeout_cb(struct osmo_fsm_inst *fi)
208{
209 struct mgcp_ctx *mgcp_ctx = fi->priv;
210 struct mgcp_client *mgcp;
211
212 OSMO_ASSERT(mgcp_ctx);
213 mgcp = mgcp_ctx->mgcp;
214 OSMO_ASSERT(mgcp);
215
216 if (fi->T == MGCP_MGW_TIMEOUT_TIMER_NR) {
217 /* We were unable to communicate with the MGW, unfortunately
218 * there is no meaningful action we can take now other than
219 * giving up. */
220
221 /* At least release the occupied endpoint ID */
222 mgcp_client_release_endpoint(mgcp_ctx->rtp_endpoint, mgcp);
223
224 /* Cancel the transaction that timed out */
225 mgcp_client_cancel(mgcp, mgcp_ctx->mgw_pending_trans);
226
Philipp Maier4eef20b2018-03-13 13:07:45 +0100227 /* halt of the FSM */
228 handle_error(mgcp_ctx, MGCP_ERR_MGW_TIMEOUT, false);
Philipp Maier621ba032017-11-07 17:19:25 +0100229 } else if (fi->T == MGCP_RAN_TIMEOUT_TIMER_NR) {
230 /* If the logic that controls the RAN is unable to negotiate a
231 * connection, we presumably still have a working connection to
232 * the MGW, we will try to shut down gracefully. */
Philipp Maier4eef20b2018-03-13 13:07:45 +0100233 handle_error(mgcp_ctx, MGCP_ERR_RAN_TIMEOUT, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100234 } else if (fi->T == MGCP_REL_TIMEOUT_TIMER_NR) {
235 /* Under normal conditions, the MSC logic should always command
236 * to release the call at some point. However, the release may
237 * be missing due to errors in the MSC logic and we may have
238 * reached ST_HALT because of cascading errors and timeouts. In
239 * this and only in this case we will allow ST_HALT to free all
240 * context information on its own authority. */
241 mgcp_ctx->free_ctx = true;
242
243 /* Initiate self destruction of the FSM */
244 osmo_fsm_inst_state_chg(fi, ST_HALT, 0, 0);
245 osmo_fsm_inst_dispatch(fi, EV_TEARDOWN, mgcp_ctx);
246 } else if (fi->T == MGCP_ASS_TIMEOUT_TIMER_NR) {
247 /* There may be rare cases in which the MSC is unable to
248 * complete the call assignment */
Philipp Maier4eef20b2018-03-13 13:07:45 +0100249 handle_error(mgcp_ctx, MGCP_ERR_ASS_TIMEOUT, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100250 } else {
251 /* Ther must not be any unsolicited timers in this FSM. If so,
252 * we have serious problem. */
253 OSMO_ASSERT(false);
254 }
255
256 return 0;
257}
258
259static void mgw_crcx_ran_resp_cb(struct mgcp_response *r, void *priv);
260
261/* Callback for ST_CRCX_RAN: Send CRCX for RAN side to MGW */
262static void fsm_crcx_ran_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
263{
264 struct mgcp_ctx *mgcp_ctx = data;
265 struct mgcp_client *mgcp;
266 struct mgcp_msg mgcp_msg;
267 struct msgb *msg;
268 int rc;
269
270 OSMO_ASSERT(mgcp_ctx);
271 mgcp = mgcp_ctx->mgcp;
272 OSMO_ASSERT(mgcp);
273
274 mgcp_ctx->rtp_endpoint = mgcp_client_next_endpoint(mgcp);
275
276 LOGPFSML(fi, LOGL_DEBUG,
277 "CRCX/RAN: creating connection for the RAN side on MGW endpoint:0x%x...\n", mgcp_ctx->rtp_endpoint);
278
279 /* Generate MGCP message string */
280 mgcp_msg = (struct mgcp_msg) {
281 .verb = MGCP_VERB_CRCX,
282 .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_MODE),
283 .call_id = mgcp_ctx->rtp_endpoint,
284 .conn_mode = MGCP_CONN_LOOPBACK
285 };
286 if (snprintf(mgcp_msg.endpoint, MGCP_ENDPOINT_MAXLEN, MGCP_ENDPOINT_FORMAT, mgcp_ctx->rtp_endpoint) >=
287 MGCP_ENDPOINT_MAXLEN) {
Philipp Maier4eef20b2018-03-13 13:07:45 +0100288 handle_error(mgcp_ctx, MGCP_ERR_NOMEM, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100289 return;
290 }
291 msg = mgcp_msg_gen(mgcp, &mgcp_msg);
292 OSMO_ASSERT(msg);
293
294 /* Transmit MGCP message to MGW */
295 mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
296 rc = mgcp_client_tx(mgcp, msg, mgw_crcx_ran_resp_cb, mgcp_ctx);
297 if (rc < 0) {
Philipp Maier4eef20b2018-03-13 13:07:45 +0100298 handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100299 return;
300 }
301
302 osmo_fsm_inst_state_chg(fi, ST_CRCX_CN, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
303}
304
305/* Callback for MGCP-Client: handle response for RAN associated CRCX */
306static void mgw_crcx_ran_resp_cb(struct mgcp_response *r, void *priv)
307{
308 struct mgcp_ctx *mgcp_ctx = priv;
309 int rc;
310 struct gsm_trans *trans;
311 struct gsm_subscriber_connection *conn;
312
313 OSMO_ASSERT(mgcp_ctx);
314 trans = mgcp_ctx->trans;
315 OSMO_ASSERT(trans);
316 conn = trans->conn;
317 OSMO_ASSERT(conn);
318
319 if (r->head.response_code != 200) {
320 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
321 "CRCX/RAN: response yields error: %d %s\n", r->head.response_code, r->head.comment);
Philipp Maier4eef20b2018-03-13 13:07:45 +0100322 handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100323 return;
324 }
325
326 /* memorize connection identifier */
327 osmo_strlcpy(mgcp_ctx->conn_id_ran, r->head.conn_id, sizeof(mgcp_ctx->conn_id_ran));
328 LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/RAN: MGW responded with CI: %s\n", mgcp_ctx->conn_id_ran);
329
330 rc = mgcp_response_parse_params(r);
331 if (rc) {
332 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR, "CRCX/RAN: Cannot parse response\n");
Philipp Maier4eef20b2018-03-13 13:07:45 +0100333 handle_error(mgcp_ctx, MGCP_ERR_MGW_INVAL_RESP, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100334 return;
335 }
336
337 LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/BTS: MGW responded with address %s:%u\n", r->audio_ip, r->audio_port);
338
339 conn->rtp.local_port_ran = r->audio_port;
340 osmo_strlcpy(conn->rtp.local_addr_ran, r->audio_ip, sizeof(conn->rtp.local_addr_ran));
341
342 /* Notify the FSM that we got the response. */
343 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_CRCX_RAN_RESP, mgcp_ctx);
344}
345
346static void mgw_crcx_cn_resp_cb(struct mgcp_response *r, void *priv);
347
348/* Callback for ST_CRCX_CN: check MGW response and send CRCX for CN side to MGW */
349static void fsm_crcx_cn_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
350{
351 struct mgcp_ctx *mgcp_ctx = data;
352 struct mgcp_client *mgcp;
353 struct mgcp_msg mgcp_msg;
354 struct msgb *msg;
355 int rc;
356
357 OSMO_ASSERT(mgcp_ctx);
358 mgcp = mgcp_ctx->mgcp;
359 OSMO_ASSERT(mgcp);
360
361 switch (event) {
362 case EV_CRCX_RAN_RESP:
363 break;
364 default:
Philipp Maier4eef20b2018-03-13 13:07:45 +0100365 handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100366 return;
367 }
368
369 LOGPFSML(fi, LOGL_DEBUG,
370 "CRCX/CN creating connection for the CN side on MGW endpoint:0x%x...\n", mgcp_ctx->rtp_endpoint);
371
372 /* Generate MGCP message string */
373 mgcp_msg = (struct mgcp_msg) {
374 .verb = MGCP_VERB_CRCX,
375 .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_MODE),
376 .call_id = mgcp_ctx->rtp_endpoint,
377 .conn_mode = MGCP_CONN_LOOPBACK
378 };
379 if (snprintf(mgcp_msg.endpoint, MGCP_ENDPOINT_MAXLEN, MGCP_ENDPOINT_FORMAT, mgcp_ctx->rtp_endpoint) >=
380 MGCP_ENDPOINT_MAXLEN) {
Philipp Maier4eef20b2018-03-13 13:07:45 +0100381 handle_error(mgcp_ctx, MGCP_ERR_NOMEM, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100382 return;
383 }
384 msg = mgcp_msg_gen(mgcp, &mgcp_msg);
385 OSMO_ASSERT(msg);
386
387 /* Transmit MGCP message to MGW */
388 mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
389 rc = mgcp_client_tx(mgcp, msg, mgw_crcx_cn_resp_cb, mgcp_ctx);
390 if (rc < 0) {
Philipp Maier4eef20b2018-03-13 13:07:45 +0100391 handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100392 return;
393 }
394
395 osmo_fsm_inst_state_chg(fi, ST_CRCX_COMPL, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
396}
397
398/* Callback for MGCP-Client: handle response for CN associated CRCX */
399static void mgw_crcx_cn_resp_cb(struct mgcp_response *r, void *priv)
400{
401 struct mgcp_ctx *mgcp_ctx = priv;
402 int rc;
403 struct gsm_trans *trans;
404 struct gsm_subscriber_connection *conn;
405
406 OSMO_ASSERT(mgcp_ctx);
407 trans = mgcp_ctx->trans;
408 OSMO_ASSERT(trans);
409 conn = trans->conn;
410 OSMO_ASSERT(conn);
411
412 if (r->head.response_code != 200) {
413 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
414 "CRCX/CN: response yields error: %d %s\n", r->head.response_code, r->head.comment);
Philipp Maier4eef20b2018-03-13 13:07:45 +0100415 handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100416 return;
417 }
418
419 /* memorize connection identifier */
420 osmo_strlcpy(mgcp_ctx->conn_id_cn, r->head.conn_id, sizeof(mgcp_ctx->conn_id_cn));
421 LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/CN: MGW responded with CI: %s\n", mgcp_ctx->conn_id_cn);
422
423 rc = mgcp_response_parse_params(r);
424 if (rc) {
425 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR, "CRCX/CN: Cannot parse response\n");
Philipp Maier4eef20b2018-03-13 13:07:45 +0100426 handle_error(mgcp_ctx, MGCP_ERR_MGW_INVAL_RESP, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100427 return;
428 }
429
430 LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/CN: MGW responded with address %s:%u\n", r->audio_ip, r->audio_port);
431
432 conn->rtp.local_port_cn = r->audio_port;
433 osmo_strlcpy(conn->rtp.local_addr_cn, r->audio_ip, sizeof(conn->rtp.local_addr_cn));
434
435 /* Notify the FSM that we got the response. */
436 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_CRCX_CN_RESP, mgcp_ctx);
437}
438
439/* Callback for ST_CRCX_COMPL: check MGW response, start assignment */
440static void fsm_crcx_compl(struct osmo_fsm_inst *fi, uint32_t event, void *data)
441{
442 struct mgcp_ctx *mgcp_ctx = data;
443 struct gsm_trans *trans;
444 struct gsm_subscriber_connection *conn;
445
446 OSMO_ASSERT(mgcp_ctx);
447 trans = mgcp_ctx->trans;
448 OSMO_ASSERT(trans);
449 conn = trans->conn;
450 OSMO_ASSERT(conn);
451
452 switch (event) {
453 case EV_CRCX_CN_RESP:
454 break;
455 default:
Philipp Maier4eef20b2018-03-13 13:07:45 +0100456 handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100457 return;
458 }
459
460 /* Forward assignment request to A/RANAP */
461 if (conn->via_ran == RAN_UTRAN_IU) {
462#ifdef BUILD_IU
463 /* Assign a voice channel via RANAP on 3G */
464 if (iu_rab_act_cs(trans))
465 goto error;
466#else
467 LOGPFSML(fi, LOGL_ERROR, "Cannot send Iu RAB Assignment: built without Iu support\n");
468 goto error;
469#endif
470 } else if (conn->via_ran == RAN_GERAN_A) {
471 /* Assign a voice channel via A on 2G */
472 if (a_iface_tx_assignment(trans))
473 goto error;
474 } else {
475 /* Unset or unimplemented new RAN type */
476 LOGPFSML(fi, LOGL_ERROR, "Unknown RAN type: %d\n", conn->via_ran);
477 return;
478 }
479
480 /* Respond back to MNCC (if requested) */
481 if (trans->tch_rtp_create) {
482 if (gsm48_tch_rtp_create(trans))
483 goto error;
484 }
485
486 /* Note: When we reach this point then the situation is basically that
487 * we have two sides connected, both are in loopback. The local ports
488 * of the side pointing towards the BSS should be already communicated
Philipp Maier4c573772018-02-08 14:05:43 +0100489 * and we are waiting now the other end to pick up. */
Philipp Maier621ba032017-11-07 17:19:25 +0100490 osmo_fsm_inst_state_chg(fi, ST_MDCX_CN, MGCP_RAN_TIMEOUT, MGCP_RAN_TIMEOUT_TIMER_NR);
491 return;
492
493error:
Philipp Maier4eef20b2018-03-13 13:07:45 +0100494 handle_error(mgcp_ctx, MGCP_ERR_ASSGMNT_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100495}
496
497static void mgw_mdcx_cn_resp_cb(struct mgcp_response *r, void *priv);
498
499/* Callback for ST_MDCX_CN: send MDCX for RAN side to MGW */
500static void fsm_mdcx_cn_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
501{
502 struct mgcp_ctx *mgcp_ctx = data;
503 struct mgcp_client *mgcp;
504 struct gsm_trans *trans;
505 struct gsm_subscriber_connection *conn;
506 struct mgcp_msg mgcp_msg;
507 struct msgb *msg;
508 int rc;
509
510 OSMO_ASSERT(mgcp_ctx);
511 mgcp = mgcp_ctx->mgcp;
512 OSMO_ASSERT(mgcp);
513 trans = mgcp_ctx->trans;
514 OSMO_ASSERT(trans);
515 conn = trans->conn;
516 OSMO_ASSERT(conn);
517
518 switch (event) {
519 case EV_CONNECT:
520 break;
521 default:
Philipp Maier4eef20b2018-03-13 13:07:45 +0100522 handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100523 return;
524 }
525
526 LOGPFSML(fi, LOGL_DEBUG,
527 "MDCX/CN: completing connection for the CN side on MGW endpoint:0x%x, remote leg expects RTP input on address %s:%u\n",
528 mgcp_ctx->rtp_endpoint, conn->rtp.remote_addr_cn, conn->rtp.remote_port_cn);
529
530 /* Generate MGCP message string */
531 mgcp_msg = (struct mgcp_msg) {
532 .verb = MGCP_VERB_MDCX,
533 .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_ID |
534 MGCP_MSG_PRESENCE_CONN_MODE | MGCP_MSG_PRESENCE_AUDIO_IP |
535 MGCP_MSG_PRESENCE_AUDIO_PORT),
536 .call_id = mgcp_ctx->rtp_endpoint,
537 .conn_id = mgcp_ctx->conn_id_cn,
538 .conn_mode = MGCP_CONN_RECV_SEND,
539 .audio_ip = conn->rtp.remote_addr_cn,
540 .audio_port = conn->rtp.remote_port_cn
541 };
542 if (snprintf(mgcp_msg.endpoint, MGCP_ENDPOINT_MAXLEN, MGCP_ENDPOINT_FORMAT, mgcp_ctx->rtp_endpoint) >=
543 MGCP_ENDPOINT_MAXLEN) {
Philipp Maier4eef20b2018-03-13 13:07:45 +0100544 handle_error(mgcp_ctx, MGCP_ERR_NOMEM, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100545 return;
546 }
547 msg = mgcp_msg_gen(mgcp, &mgcp_msg);
548 OSMO_ASSERT(msg);
549
550 /* Transmit MGCP message to MGW */
551 mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
552 rc = mgcp_client_tx(mgcp, msg, mgw_mdcx_cn_resp_cb, mgcp_ctx);
553 if (rc < 0) {
Philipp Maier4eef20b2018-03-13 13:07:45 +0100554 handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100555 return;
556 }
557
558 osmo_fsm_inst_state_chg(fi, ST_MDCX_CN_COMPL, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
559}
560
561/* Callback for MGCP-Client: handle response for CN associated CRCX */
562static void mgw_mdcx_cn_resp_cb(struct mgcp_response *r, void *priv)
563{
564 struct mgcp_ctx *mgcp_ctx = priv;
565
566 OSMO_ASSERT(mgcp_ctx);
567
568 if (r->head.response_code != 200) {
569 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
570 "MDCX/CN: response yields error: %d %s\n", r->head.response_code, r->head.comment);
Philipp Maier4eef20b2018-03-13 13:07:45 +0100571 handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100572 return;
573 }
574
575 /* Notify the FSM that we got the response. */
576 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_MDCX_CN_RESP, mgcp_ctx);
577}
578
579/* Callback for ST_MDCX_CN_COMPL: wait for mgw response, move on with the MDCX
580 * for the RAN side if we already have valid IP/Port data for the RAN sided
581 * RTP stream. */
582static void fsm_mdcx_cn_compl_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
583{
584 struct mgcp_ctx *mgcp_ctx = data;
585 struct gsm_subscriber_connection *conn;
586 struct gsm_trans *trans;
587
588 OSMO_ASSERT(mgcp_ctx);
589 trans = mgcp_ctx->trans;
590 OSMO_ASSERT(trans);
591 conn = trans->conn;
592 OSMO_ASSERT(conn);
593
594 switch (event) {
595 case EV_MDCX_CN_RESP:
596 break;
597 default:
Philipp Maier4eef20b2018-03-13 13:07:45 +0100598 handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100599 return;
600 }
601
602 /* Enter MDCX phase, but we must be sure that the Assigmnet on the A or
603 * IuCS interface is complete (IP-Address and Port are valid) */
604 osmo_fsm_inst_state_chg(fi, ST_MDCX_RAN, MGCP_ASS_TIMEOUT, MGCP_ASS_TIMEOUT_TIMER_NR);
605
606 /* If we already have a valid remote port and IP-Address from the RAN side
607 * call leg, the assignment has been completed before we got here, so we
608 * may move on immediately */
609 if (conn->rtp.remote_port_ran != 0 || strlen(conn->rtp.remote_addr_ran) > 0)
610 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_ASSIGN, mgcp_ctx);
611}
612
613static void mgw_mdcx_ran_resp_cb(struct mgcp_response *r, void *priv);
614
615/* Callback for ST_MDCX_RAN: wait for assignment completion, send MDCX for CN side to MGW */
616static void fsm_mdcx_ran_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
617{
618 struct mgcp_ctx *mgcp_ctx = data;
619 struct mgcp_client *mgcp;
620 struct gsm_trans *trans;
621 struct gsm_subscriber_connection *conn;
622 struct mgcp_msg mgcp_msg;
623 struct msgb *msg;
624 int rc;
625
626 OSMO_ASSERT(mgcp_ctx);
627 mgcp = mgcp_ctx->mgcp;
628 OSMO_ASSERT(mgcp);
629 trans = mgcp_ctx->trans;
630 OSMO_ASSERT(trans);
631 conn = trans->conn;
632 OSMO_ASSERT(conn);
633
634 switch (event) {
635 case EV_ASSIGN:
636 break;
637 default:
Philipp Maier4eef20b2018-03-13 13:07:45 +0100638 handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100639 return;
640 }
641
642 LOGPFSML(fi, LOGL_DEBUG,
643 "MDCX/RAN: completing connection for the CN side on MGW endpoint:0x%x, RAN expects RTP input on address %s:%u\n",
644 mgcp_ctx->rtp_endpoint, conn->rtp.remote_addr_ran, conn->rtp.remote_port_ran);
645
646 /* Generate MGCP message string */
647 mgcp_msg = (struct mgcp_msg) {
648 .verb = MGCP_VERB_MDCX,
649 .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_ID |
650 MGCP_MSG_PRESENCE_CONN_MODE | MGCP_MSG_PRESENCE_AUDIO_IP |
651 MGCP_MSG_PRESENCE_AUDIO_PORT),
652 .call_id = mgcp_ctx->rtp_endpoint,
653 .conn_id = mgcp_ctx->conn_id_ran,
654 .conn_mode = MGCP_CONN_RECV_SEND,
655 .audio_ip = conn->rtp.remote_addr_ran,
656 .audio_port = conn->rtp.remote_port_ran
657 };
658 if (snprintf(mgcp_msg.endpoint, MGCP_ENDPOINT_MAXLEN, MGCP_ENDPOINT_FORMAT, mgcp_ctx->rtp_endpoint) >=
659 MGCP_ENDPOINT_MAXLEN) {
Philipp Maier4eef20b2018-03-13 13:07:45 +0100660 handle_error(mgcp_ctx, MGCP_ERR_NOMEM, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100661 return;
662 }
663 msg = mgcp_msg_gen(mgcp, &mgcp_msg);
664 OSMO_ASSERT(msg);
665
666 /* Transmit MGCP message to MGW */
667 mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
668 rc = mgcp_client_tx(mgcp, msg, mgw_mdcx_ran_resp_cb, mgcp_ctx);
669 if (rc < 0) {
Philipp Maier4eef20b2018-03-13 13:07:45 +0100670 handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100671 return;
672 }
673
674 osmo_fsm_inst_state_chg(fi, ST_MDCX_RAN_COMPL, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
675}
676
677/* Callback for MGCP-Client: handle response for CN associated CRCX */
678static void mgw_mdcx_ran_resp_cb(struct mgcp_response *r, void *priv)
679{
680 struct mgcp_ctx *mgcp_ctx = priv;
681
682 OSMO_ASSERT(mgcp_ctx);
683
684 if (r->head.response_code != 200) {
685 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
686 "MDCX/RAN: response yields error: %d %s\n", r->head.response_code, r->head.comment);
Philipp Maier4eef20b2018-03-13 13:07:45 +0100687 handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100688 return;
689 }
690
691 /* Notify the FSM that we got the response. */
692 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_MDCX_RAN_RESP, mgcp_ctx);
693}
694
695/* Callback for ST_MDCX_RAN_COMPL: check MGW response */
696static void fsm_mdcx_ran_compl_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
697{
698 struct mgcp_ctx *mgcp_ctx = data;
699 OSMO_ASSERT(mgcp_ctx);
700
701 switch (event) {
702 case EV_MDCX_RAN_RESP:
703 break;
704 default:
Philipp Maier4eef20b2018-03-13 13:07:45 +0100705 handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100706 return;
707 }
708
709 LOGPFSML(fi, LOGL_DEBUG, "call active, waiting for teardown...\n");
710 osmo_fsm_inst_state_chg(fi, ST_CALL, 0, 0);
711}
712
713static void mgw_dlcx_all_resp_cb(struct mgcp_response *r, void *priv);
714
715/* Callback for ST_CALL: call is active, send DLCX for both sides on teardown */
716static void fsm_call_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
717{
718
719 struct mgcp_ctx *mgcp_ctx = (struct mgcp_ctx *)data;
720 struct mgcp_client *mgcp;
721 struct mgcp_msg mgcp_msg;
722 struct msgb *msg;
723 int rc;
724
725 OSMO_ASSERT(mgcp_ctx);
726 mgcp = mgcp_ctx->mgcp;
727 OSMO_ASSERT(mgcp);
728
729 LOGPFSML(fi, LOGL_DEBUG,
730 "DLCX: removing connection for the RAN and CN side on MGW endpoint:0x%x...\n", mgcp_ctx->rtp_endpoint);
731
732 /* We now relase the endpoint back to the pool in order to allow
733 * other connections to use this endpoint */
734 mgcp_client_release_endpoint(mgcp_ctx->rtp_endpoint, mgcp);
735
736 /* Generate MGCP message string */
737 mgcp_msg = (struct mgcp_msg) {
738 .verb = MGCP_VERB_DLCX,
739 .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID),
740 .call_id = mgcp_ctx->rtp_endpoint
741 };
742 if (snprintf(mgcp_msg.endpoint, MGCP_ENDPOINT_MAXLEN, MGCP_ENDPOINT_FORMAT, mgcp_ctx->rtp_endpoint) >=
743 MGCP_ENDPOINT_MAXLEN) {
Philipp Maier4eef20b2018-03-13 13:07:45 +0100744 handle_error(mgcp_ctx, MGCP_ERR_NOMEM, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100745 return;
746 }
747 msg = mgcp_msg_gen(mgcp, &mgcp_msg);
748 OSMO_ASSERT(msg);
749
750 /* Transmit MGCP message to MGW */
751 mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
752 rc = mgcp_client_tx(mgcp, msg, mgw_dlcx_all_resp_cb, mgcp_ctx);
753 if (rc < 0) {
Philipp Maier4eef20b2018-03-13 13:07:45 +0100754 handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100755 return;
756 }
757
758 osmo_fsm_inst_state_chg(fi, ST_HALT, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
759}
760
761/* Callback for MGCP-Client: handle response for CN associated CRCX */
762static void mgw_dlcx_all_resp_cb(struct mgcp_response *r, void *priv)
763{
764 struct mgcp_ctx *mgcp_ctx = priv;
765
766 OSMO_ASSERT(mgcp_ctx);
767
Harald Welte33d61e72018-02-10 10:43:38 +0100768 /* DLCX is the only command where 250 is permitted as positive result */
769 if (r->head.response_code != 200 && r->head.response_code != 250) {
Philipp Maier621ba032017-11-07 17:19:25 +0100770 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
771 "DLCX: response yields error: %d %s\n", r->head.response_code, r->head.comment);
Philipp Maier4eef20b2018-03-13 13:07:45 +0100772 handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100773 return;
774 }
775
776 /* Notify the FSM that we got the response. */
777 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_DLCX_ALL_RESP, mgcp_ctx);
778}
779
780/* Callback for ST_HALT: Terminate the state machine */
781static void fsm_halt_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
782{
783 struct mgcp_ctx *mgcp_ctx = data;
Philipp Maieraddf63b2018-03-14 13:37:44 +0100784 struct mgcp_client *mgcp;
Philipp Maier621ba032017-11-07 17:19:25 +0100785
786 OSMO_ASSERT(mgcp_ctx);
Philipp Maieraddf63b2018-03-14 13:37:44 +0100787 mgcp = mgcp_ctx->mgcp;
788 OSMO_ASSERT(mgcp);
Philipp Maier621ba032017-11-07 17:19:25 +0100789
790 /* NOTE: We must not free the context information now, we have to
791 * wait until msc_mgcp_call_release() is called. Then we are sure
792 * that the logic controlling us is fully aware that the context
793 * information is freed. If we would free early now the controlling
794 * logic might mistakenly think that the context info is still alive,
795 * so lets keep the context info until we are explicitly asked for
796 * throwing it away. */
797 if (mgcp_ctx->free_ctx) {
Philipp Maieraddf63b2018-03-14 13:37:44 +0100798 /* Be sure that there is no pending MGW transaction */
799 mgcp_client_cancel(mgcp, mgcp_ctx->mgw_pending_trans);
800
801 /* Free FSM and its context information */
Philipp Maier621ba032017-11-07 17:19:25 +0100802 osmo_fsm_inst_free(mgcp_ctx->fsm);
803 talloc_free(mgcp_ctx);
804 return;
805 }
806
807 osmo_fsm_inst_state_chg(fi, ST_HALT, MGCP_REL_TIMEOUT, MGCP_REL_TIMEOUT_TIMER_NR);
808}
809
810static struct osmo_fsm_state fsm_msc_mgcp_states[] = {
811
812 /* Startup state machine, send CRCX for RAN side. */
813 [ST_CRCX_RAN] = {
814 .in_event_mask = S(EV_INIT),
815 .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_CRCX_CN),
816 .name = OSMO_STRINGIFY(ST_CRCX_RAN),
817 .action = fsm_crcx_ran_cb,
818 },
819 /* When the response to the RAN CRCX is received, then proceed with
820 sending the CRCX for CN side */
821 [ST_CRCX_CN] = {
822 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_CRCX_RAN_RESP),
823 .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_CRCX_COMPL),
824 .name = OSMO_STRINGIFY(ST_CRCX_CN),
825 .action = fsm_crcx_cn_cb,
826 },
827 /* Complete the CRCX phase by starting the assignment. Depending on the
828 * RAT (Radio Access Technology), this will either trigger an
829 * Assignment Request on the A-Interface or an RAB-Assignment on the
830 * IU-interface */
831 [ST_CRCX_COMPL] = {
832 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_CRCX_CN_RESP),
833 .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_MDCX_CN),
834 .name = OSMO_STRINGIFY(ST_CRCX_COMPL),
835 .action = fsm_crcx_compl,
836 },
837 /* Wait for MSC to complete the assignment request, when complete, we
838 * will enter the MDCX phase by sending an MDCX for the CN side to the
839 * MGW */
840 [ST_MDCX_CN] = {
841 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_CONNECT),
842 .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_MDCX_CN_COMPL),
843 .name = OSMO_STRINGIFY(ST_MDCX_CN),
844 .action = fsm_mdcx_cn_cb,
845 },
846 /* We arrive in this state when the MDCX phase for the CN side has
847 * completed we will check the IP/Port of the RAN connection. If this
848 * data is valid we may continue with the MDCX phase for the RAN side.
849 * If not we wait until the assinment completes on the A or on the IuCS
850 * interface. The completion of the assignment will fill in the port and
851 * IP-Address of the RAN side and way may continue then. */
852 [ST_MDCX_CN_COMPL] = {
853 .in_event_mask = S(EV_TEARDOWN) | S(EV_MDCX_CN_RESP),
854 .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_MDCX_RAN),
855 .name = OSMO_STRINGIFY(ST_MDCX_CN_COMPL),
856 .action = fsm_mdcx_cn_compl_cb,
857 },
858 /* When the response for the CN MDCX is received, send the MDCX for the
859 * RAN side to the MGW */
860 [ST_MDCX_RAN] = {
861 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_ASSIGN),
862 .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_MDCX_RAN_COMPL),
863 .name = OSMO_STRINGIFY(ST_MDCX_RAN),
864 .action = fsm_mdcx_ran_cb,
865 },
866 /* The RAN side MDCX phase is complete when the response is received
867 * from the MGW. The call is then active, we change to ST_CALL and wait
868 * there until the call ends. */
869 [ST_MDCX_RAN_COMPL] = {
870 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_MDCX_RAN_RESP),
871 .out_state_mask = S(ST_HALT) | S(ST_CALL),
872 .name = OSMO_STRINGIFY(ST_MDCX_RAN_COMPL),
873 .action = fsm_mdcx_ran_compl_cb,
874 },
875 /* We are now in the active call phase, wait until the call is done
876 * and send a DLCX then to remove all connections from the MGW */
877 [ST_CALL] = {
878 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR),
879 .out_state_mask = S(ST_HALT),
880 .name = OSMO_STRINGIFY(ST_CALL),
881 .action = fsm_call_cb,
882 },
883 /* When the MGW confirms that the connections are terminated, then halt
884 * the state machine. */
885 [ST_HALT] = {
886 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_DLCX_ALL_RESP),
887 .out_state_mask = S(ST_HALT),
888 .name = OSMO_STRINGIFY(ST_HALT),
889 .action = fsm_halt_cb,
890 },
891};
892
893/* State machine definition */
894static struct osmo_fsm fsm_msc_mgcp = {
895 .name = "MGW",
896 .states = fsm_msc_mgcp_states,
897 .num_states = ARRAY_SIZE(fsm_msc_mgcp_states),
898 .log_subsys = DMGCP,
899 .timer_cb = fsm_timeout_cb,
Philipp Maiere4f91722018-02-26 15:20:49 +0100900 .event_names = msc_mgcp_fsm_evt_names,
Philipp Maier621ba032017-11-07 17:19:25 +0100901};
902
903/* Notify that a new call begins. This will create a connection for the
904 * RAN and the CN on the MGW.
905 * Parameter:
906 * trans: transaction context.
907 * Returns -EINVAL on error, 0 on success. */
908int msc_mgcp_call_assignment(struct gsm_trans *trans)
909{
910 struct mgcp_ctx *mgcp_ctx;
911 char name[32];
912 static bool fsm_registered = false;
913 struct gsm_subscriber_connection *conn;
914 struct mgcp_client *mgcp;
915
916 OSMO_ASSERT(trans);
917
918 if (!trans->conn) {
919 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid conn, call assignment failed\n",
920 vlr_subscr_name(trans->vsub));
921 return -EINVAL;
922 }
923
924 conn = trans->conn;
925 mgcp = conn->network->mgw.client;
926 OSMO_ASSERT(mgcp);
927
928 if (conn->rtp.mgcp_ctx) {
929 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) double assignment detected, dropping...\n",
930 vlr_subscr_name(trans->vsub));
931 return -EINVAL;
932 }
933
934#ifdef BUILD_IU
935 /* FIXME: HACK. where to scope the RAB Id? At the conn / subscriber / ranap_ue_conn_ctx? */
936 static uint8_t next_iu_rab_id = 1;
937 if (conn->via_ran == RAN_UTRAN_IU)
938 conn->iu.rab_id = next_iu_rab_id++;
939#endif
940
941 if (snprintf(name, sizeof(name), "MGW_%i", trans->transaction_id) >= sizeof(name))
942 return -EINVAL;
943
944 /* Register the fsm description (if not already done) */
945 if (fsm_registered == false) {
946 osmo_fsm_register(&fsm_msc_mgcp);
947 fsm_registered = true;
948 }
949
950 /* Allocate and configure a new fsm instance */
951 mgcp_ctx = talloc_zero(NULL, struct mgcp_ctx);
952 OSMO_ASSERT(mgcp_ctx);
953
954 mgcp_ctx->fsm = osmo_fsm_inst_alloc(&fsm_msc_mgcp, NULL, NULL, LOGL_DEBUG, name);
955 OSMO_ASSERT(mgcp_ctx->fsm);
956 mgcp_ctx->fsm->priv = mgcp_ctx;
957 mgcp_ctx->mgcp = mgcp;
958 mgcp_ctx->trans = trans;
959
960 /* start state machine */
961 OSMO_ASSERT(mgcp_ctx->fsm->state == ST_CRCX_RAN);
962 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_INIT, mgcp_ctx);
963
964 conn->rtp.mgcp_ctx = mgcp_ctx;
965
966 LOGP(DMGCP, LOGL_DEBUG, "(subscriber:%s) call assignment initiated\n",
967 vlr_subscr_name(conn->vsub));
968
969 return 0;
970}
971
972/* Inform the FSM that the assignment (RAN connection) is now complete.
973 * Parameter:
974 * conn: subscriber connection context.
975 * port: port number of the remote leg.
976 * addr: IP-address of the remote leg.
977 * Returns -EINVAL on error, 0 on success. */
978int msc_mgcp_ass_complete(struct gsm_subscriber_connection *conn, uint16_t port, char *addr)
979{
980 struct mgcp_ctx *mgcp_ctx;
981
982 OSMO_ASSERT(conn);
983
984 if (port == 0) {
985 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid remote call leg port, assignmnet completion failed\n",
986 vlr_subscr_name(conn->vsub));
987 return -EINVAL;
988 }
989 if (!addr || strlen(addr) <= 0) {
990 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) missing remote call leg address, assignmnet completion failed\n",
991 vlr_subscr_name(conn->vsub));
992 return -EINVAL;
993 }
994
995 mgcp_ctx = conn->rtp.mgcp_ctx;
996 if (!mgcp_ctx) {
997 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid mgcp context, assignmnet completion failed.\n",
998 vlr_subscr_name(conn->vsub));
999 return -EINVAL;
1000 }
1001
1002 /* Memorize port and IP-Address of the remote RAN call leg. We need this
1003 * information at latest when we enter the MDCX phase for the RAN side. */
1004 conn->rtp.remote_port_ran = port;
1005 osmo_strlcpy(conn->rtp.remote_addr_ran, addr, sizeof(conn->rtp.remote_addr_ran));
1006
1007 LOGP(DMGCP, LOGL_DEBUG, "(subscriber:%s) assignmnet completed, rtp %s:%d.\n",
1008 vlr_subscr_name(conn->vsub), conn->rtp.remote_addr_ran, port);
1009
1010 /* Note: We only dispatch the event if we are really waiting for the
1011 * assignment, if we are not yet waiting, there is no need to loudly
1012 * broadcast an event that the all other states do not understand anyway */
1013 if (mgcp_ctx->fsm->state == ST_MDCX_RAN)
1014 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_ASSIGN, mgcp_ctx);
1015
1016 return 0;
1017}
1018
1019/* Make the connection of a previously assigned call complete
1020 * Parameter:
1021 * trans: transaction context.
1022 * port: port number of the remote leg.
1023 * addr: IP-address of the remote leg.
1024 * Returns -EINVAL on error, 0 on success. */
1025int msc_mgcp_call_complete(struct gsm_trans *trans, uint16_t port, char *addr)
1026{
1027 struct mgcp_ctx *mgcp_ctx;
1028 struct gsm_subscriber_connection *conn;
1029
1030 OSMO_ASSERT(trans);
1031 OSMO_ASSERT(addr);
1032
1033 if (port == 0) {
1034 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid remote call leg port, call completion failed\n",
1035 vlr_subscr_name(trans->vsub));
1036 return -EINVAL;
1037 }
1038 if (!addr || strlen(addr) <= 0) {
1039 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) missing remote call leg address, call completion failed\n",
1040 vlr_subscr_name(trans->vsub));
1041 return -EINVAL;
1042 }
1043 if (!trans->conn) {
1044 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid conn, call completion failed\n",
1045 vlr_subscr_name(trans->vsub));
1046 return -EINVAL;
1047 }
1048 if (!trans->conn->rtp.mgcp_ctx) {
1049 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid mgcp context, call completion failed.\n",
1050 vlr_subscr_name(trans->vsub));
1051 return -EINVAL;
1052 }
1053 if (!trans->conn->rtp.mgcp_ctx->fsm) {
1054 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) no FSM, call completion failed\n",
1055 vlr_subscr_name(trans->vsub));
1056 return -EINVAL;
1057 }
1058
1059 mgcp_ctx = trans->conn->rtp.mgcp_ctx;
1060
1061 /* The FSM should already have passed all CRCX phases and be ready to move
1062 * on with the MDCX phases. */
1063 if (mgcp_ctx->fsm->state != ST_MDCX_CN) {
1064 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid call state, call completion failed\n",
1065 vlr_subscr_name(trans->vsub));
1066 return -EINVAL;
1067 }
1068
1069 conn = trans->conn;
1070 osmo_strlcpy(conn->rtp.remote_addr_cn, addr, sizeof(conn->rtp.remote_addr_cn));
1071 conn->rtp.remote_port_cn = port;
1072
1073 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_CONNECT, mgcp_ctx);
1074
1075 LOGP(DMGCP, LOGL_DEBUG, "(subscriber:%s) call completion initiated\n",
1076 vlr_subscr_name(conn->vsub));
1077
1078 return 0;
1079}
1080
1081/* Release ongoing call.
1082 * Parameter:
1083 * trans: connection context.
1084 * Returns -EINVAL on error, 0 on success. */
1085int msc_mgcp_call_release(struct gsm_trans *trans)
1086{
1087 struct mgcp_ctx *mgcp_ctx;
1088
1089 OSMO_ASSERT(trans);
1090
1091 if (!trans->conn) {
1092 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid conn, call release failed\n",
1093 vlr_subscr_name(trans->vsub));
1094 return -EINVAL;
1095 }
1096 if (!trans->conn->rtp.mgcp_ctx) {
1097 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid mgcp context, call release failed.\n",
1098 vlr_subscr_name(trans->vsub));
1099 return -EINVAL;
1100 }
1101 if (!trans->conn->rtp.mgcp_ctx->fsm) {
1102 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) no FSM, call release failed\n",
1103 vlr_subscr_name(trans->vsub));
1104 return -EINVAL;
1105 }
1106
1107 mgcp_ctx = trans->conn->rtp.mgcp_ctx;
1108
1109 /* Inform the FSM that as soon as it reaches ST_HALT it may free
1110 * all context information immediately */
1111 mgcp_ctx->free_ctx = true;
1112
1113 /* Initaite teardown, regardless of which state we are currently
1114 * in */
1115 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_TEARDOWN, mgcp_ctx);
1116
1117 /* Prevent any further operation that is triggered from outside by
1118 * overwriting the context pointer with NULL. The FSM will now
1119 * take care for a graceful shutdown and when done it will free
1120 * all related context information */
1121 trans->conn->rtp.mgcp_ctx = NULL;
1122
1123 LOGP(DMGCP, LOGL_DEBUG, "(subscriber:%s) call release initiated\n",
1124 vlr_subscr_name(trans->vsub));
1125
1126 return 0;
1127}