blob: 5c615a952113813082fbc59108444374a3045a60 [file] [log] [blame]
Philipp Maier621ba032017-11-07 17:19:25 +01001/* (C) 2017 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
2 * All Rights Reserved
3 *
4 * Author: Philipp Maier
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Affero General Public License for more details.
15 *
16 * You should have received a copy of the GNU Affero General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 */
20
21#include <arpa/inet.h>
22
23#include <osmocom/mgcp_client/mgcp_client.h>
24#include <osmocom/core/logging.h>
25#include <osmocom/core/utils.h>
26#include <osmocom/core/timer.h>
27#include <osmocom/core/fsm.h>
28#include <osmocom/core/byteswap.h>
29#include <osmocom/msc/msc_mgcp.h>
30#include <osmocom/msc/debug.h>
31#include <osmocom/msc/transaction.h>
32#include <osmocom/msc/a_iface.h>
33#include <osmocom/msc/msc_ifaces.h>
34#include <osmocom/msc/gsm_04_08.h>
35#include <osmocom/msc/iucs.h>
36#include <osmocom/msc/vlr.h>
37
38#include "../../bscconfig.h"
39
40#define S(x) (1 << (x))
41
42#define CONN_ID_RAN 1
43#define CONN_ID_CN 2
44
45#define MGCP_MGW_TIMEOUT 4 /* in seconds */
46#define MGCP_MGW_TIMEOUT_TIMER_NR 1
Philipp Maier4c573772018-02-08 14:05:43 +010047#define MGCP_RAN_TIMEOUT 120 /* in seconds */
Philipp Maier621ba032017-11-07 17:19:25 +010048#define MGCP_RAN_TIMEOUT_TIMER_NR 2
49#define MGCP_REL_TIMEOUT 60 /* in seconds */
50#define MGCP_REL_TIMEOUT_TIMER_NR 3
51#define MGCP_ASS_TIMEOUT 10 /* in seconds */
52#define MGCP_ASS_TIMEOUT_TIMER_NR 4
53
Philipp Maiera2353c62018-02-08 14:15:59 +010054#define ENDPOINT_ID "rtpbridge/*@mgw"
Philipp Maier621ba032017-11-07 17:19:25 +010055
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,
Philipp Maier3a776522018-02-22 12:00:00 +010066 MGCP_ERR_TOOLONG,
Philipp Maier621ba032017-11-07 17:19:25 +010067 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)"},
Philipp Maier3a776522018-02-22 12:00:00 +010081 {MGCP_ERR_TOOLONG, "string value too long"},
Philipp Maier621ba032017-11-07 17:19:25 +010082 {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 Maier04d6ddb22018-03-16 18:01:21 +0100200 /* Request the higher layers (gsm_04_08.c) to release the call. If the
201 * problem occured after msc_mgcp_call_release() was calls, remain
202 * silent because we already got informed and the higher layers might
203 * already freed their context information (trans). */
204 if (!mgcp_ctx->free_ctx) {
205 mncc_set_cause(&mncc, GSM48_CAUSE_LOC_TRANS_NET,
206 GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
207 mncc_tx_to_cc(mgcp_ctx->trans->net, MNCC_REL_REQ, &mncc);
208 }
Philipp Maier621ba032017-11-07 17:19:25 +0100209}
210
211/* Timer callback to shut down in case of connectivity problems */
212static int fsm_timeout_cb(struct osmo_fsm_inst *fi)
213{
214 struct mgcp_ctx *mgcp_ctx = fi->priv;
215 struct mgcp_client *mgcp;
216
217 OSMO_ASSERT(mgcp_ctx);
218 mgcp = mgcp_ctx->mgcp;
219 OSMO_ASSERT(mgcp);
220
221 if (fi->T == MGCP_MGW_TIMEOUT_TIMER_NR) {
222 /* We were unable to communicate with the MGW, unfortunately
223 * there is no meaningful action we can take now other than
224 * giving up. */
225
Philipp Maier621ba032017-11-07 17:19:25 +0100226 /* Cancel the transaction that timed out */
227 mgcp_client_cancel(mgcp, mgcp_ctx->mgw_pending_trans);
228
Philipp Maier4eef20b2018-03-13 13:07:45 +0100229 /* halt of the FSM */
230 handle_error(mgcp_ctx, MGCP_ERR_MGW_TIMEOUT, false);
Philipp Maier621ba032017-11-07 17:19:25 +0100231 } else if (fi->T == MGCP_RAN_TIMEOUT_TIMER_NR) {
232 /* If the logic that controls the RAN is unable to negotiate a
233 * connection, we presumably still have a working connection to
234 * the MGW, we will try to shut down gracefully. */
Philipp Maier4eef20b2018-03-13 13:07:45 +0100235 handle_error(mgcp_ctx, MGCP_ERR_RAN_TIMEOUT, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100236 } else if (fi->T == MGCP_REL_TIMEOUT_TIMER_NR) {
237 /* Under normal conditions, the MSC logic should always command
238 * to release the call at some point. However, the release may
239 * be missing due to errors in the MSC logic and we may have
240 * reached ST_HALT because of cascading errors and timeouts. In
241 * this and only in this case we will allow ST_HALT to free all
242 * context information on its own authority. */
243 mgcp_ctx->free_ctx = true;
244
245 /* Initiate self destruction of the FSM */
246 osmo_fsm_inst_state_chg(fi, ST_HALT, 0, 0);
247 osmo_fsm_inst_dispatch(fi, EV_TEARDOWN, mgcp_ctx);
248 } else if (fi->T == MGCP_ASS_TIMEOUT_TIMER_NR) {
249 /* There may be rare cases in which the MSC is unable to
250 * complete the call assignment */
Philipp Maier4eef20b2018-03-13 13:07:45 +0100251 handle_error(mgcp_ctx, MGCP_ERR_ASS_TIMEOUT, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100252 } else {
253 /* Ther must not be any unsolicited timers in this FSM. If so,
254 * we have serious problem. */
255 OSMO_ASSERT(false);
256 }
257
258 return 0;
259}
260
261static void mgw_crcx_ran_resp_cb(struct mgcp_response *r, void *priv);
262
263/* Callback for ST_CRCX_RAN: Send CRCX for RAN side to MGW */
264static void fsm_crcx_ran_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
265{
266 struct mgcp_ctx *mgcp_ctx = data;
267 struct mgcp_client *mgcp;
268 struct mgcp_msg mgcp_msg;
269 struct msgb *msg;
270 int rc;
271
272 OSMO_ASSERT(mgcp_ctx);
273 mgcp = mgcp_ctx->mgcp;
274 OSMO_ASSERT(mgcp);
275
Philipp Maier621ba032017-11-07 17:19:25 +0100276 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),
Philipp Maiera2353c62018-02-08 14:15:59 +0100283 .call_id = mgcp_ctx->call_id,
Philipp Maier621ba032017-11-07 17:19:25 +0100284 .conn_mode = MGCP_CONN_LOOPBACK
285 };
Philipp Maiera2353c62018-02-08 14:15:59 +0100286 if (osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->rtp_endpoint, sizeof(mgcp_msg.endpoint)) >=
Philipp Maier621ba032017-11-07 17:19:25 +0100287 MGCP_ENDPOINT_MAXLEN) {
Philipp Maier3a776522018-02-22 12:00:00 +0100288 handle_error(mgcp_ctx, MGCP_ERR_TOOLONG, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100289 return;
290 }
Philipp Maiera2353c62018-02-08 14:15:59 +0100291
Philipp Maier621ba032017-11-07 17:19:25 +0100292 msg = mgcp_msg_gen(mgcp, &mgcp_msg);
293 OSMO_ASSERT(msg);
294
295 /* Transmit MGCP message to MGW */
296 mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
297 rc = mgcp_client_tx(mgcp, msg, mgw_crcx_ran_resp_cb, mgcp_ctx);
298 if (rc < 0) {
Philipp Maier4eef20b2018-03-13 13:07:45 +0100299 handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100300 return;
301 }
302
303 osmo_fsm_inst_state_chg(fi, ST_CRCX_CN, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
304}
305
306/* Callback for MGCP-Client: handle response for RAN associated CRCX */
307static void mgw_crcx_ran_resp_cb(struct mgcp_response *r, void *priv)
308{
309 struct mgcp_ctx *mgcp_ctx = priv;
310 int rc;
311 struct gsm_trans *trans;
312 struct gsm_subscriber_connection *conn;
313
314 OSMO_ASSERT(mgcp_ctx);
315 trans = mgcp_ctx->trans;
316 OSMO_ASSERT(trans);
317 conn = trans->conn;
318 OSMO_ASSERT(conn);
319
320 if (r->head.response_code != 200) {
321 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
322 "CRCX/RAN: response yields error: %d %s\n", r->head.response_code, r->head.comment);
Philipp Maier4eef20b2018-03-13 13:07:45 +0100323 handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100324 return;
325 }
326
Philipp Maiera2353c62018-02-08 14:15:59 +0100327 /* memorize connection identifier and specific endpoint id */
Philipp Maier621ba032017-11-07 17:19:25 +0100328 osmo_strlcpy(mgcp_ctx->conn_id_ran, r->head.conn_id, sizeof(mgcp_ctx->conn_id_ran));
329 LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/RAN: MGW responded with CI: %s\n", mgcp_ctx->conn_id_ran);
Philipp Maiera2353c62018-02-08 14:15:59 +0100330 osmo_strlcpy(mgcp_ctx->rtp_endpoint, r->head.endpoint, sizeof(mgcp_ctx->rtp_endpoint));
331 LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/RAN: MGW assigned endpoint: %s\n", mgcp_ctx->rtp_endpoint);
Philipp Maier621ba032017-11-07 17:19:25 +0100332
333 rc = mgcp_response_parse_params(r);
334 if (rc) {
335 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR, "CRCX/RAN: Cannot parse response\n");
Philipp Maier4eef20b2018-03-13 13:07:45 +0100336 handle_error(mgcp_ctx, MGCP_ERR_MGW_INVAL_RESP, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100337 return;
338 }
339
340 LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/BTS: MGW responded with address %s:%u\n", r->audio_ip, r->audio_port);
341
342 conn->rtp.local_port_ran = r->audio_port;
343 osmo_strlcpy(conn->rtp.local_addr_ran, r->audio_ip, sizeof(conn->rtp.local_addr_ran));
344
345 /* Notify the FSM that we got the response. */
346 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_CRCX_RAN_RESP, mgcp_ctx);
347}
348
349static void mgw_crcx_cn_resp_cb(struct mgcp_response *r, void *priv);
350
351/* Callback for ST_CRCX_CN: check MGW response and send CRCX for CN side to MGW */
352static void fsm_crcx_cn_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
353{
354 struct mgcp_ctx *mgcp_ctx = data;
355 struct mgcp_client *mgcp;
356 struct mgcp_msg mgcp_msg;
357 struct msgb *msg;
358 int rc;
359
360 OSMO_ASSERT(mgcp_ctx);
361 mgcp = mgcp_ctx->mgcp;
362 OSMO_ASSERT(mgcp);
363
364 switch (event) {
365 case EV_CRCX_RAN_RESP:
366 break;
367 default:
Philipp Maier4eef20b2018-03-13 13:07:45 +0100368 handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100369 return;
370 }
371
372 LOGPFSML(fi, LOGL_DEBUG,
373 "CRCX/CN creating connection for the CN side on MGW endpoint:0x%x...\n", mgcp_ctx->rtp_endpoint);
374
375 /* Generate MGCP message string */
376 mgcp_msg = (struct mgcp_msg) {
377 .verb = MGCP_VERB_CRCX,
378 .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_MODE),
Philipp Maiera2353c62018-02-08 14:15:59 +0100379 .call_id = mgcp_ctx->call_id,
Philipp Maier621ba032017-11-07 17:19:25 +0100380 .conn_mode = MGCP_CONN_LOOPBACK
381 };
Philipp Maiera2353c62018-02-08 14:15:59 +0100382 if (osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->rtp_endpoint, sizeof(mgcp_msg.endpoint)) >=
Philipp Maier621ba032017-11-07 17:19:25 +0100383 MGCP_ENDPOINT_MAXLEN) {
Philipp Maier3a776522018-02-22 12:00:00 +0100384 handle_error(mgcp_ctx, MGCP_ERR_TOOLONG, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100385 return;
386 }
Philipp Maiera2353c62018-02-08 14:15:59 +0100387
Philipp Maier621ba032017-11-07 17:19:25 +0100388 msg = mgcp_msg_gen(mgcp, &mgcp_msg);
389 OSMO_ASSERT(msg);
390
391 /* Transmit MGCP message to MGW */
392 mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
393 rc = mgcp_client_tx(mgcp, msg, mgw_crcx_cn_resp_cb, mgcp_ctx);
394 if (rc < 0) {
Philipp Maier4eef20b2018-03-13 13:07:45 +0100395 handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100396 return;
397 }
398
399 osmo_fsm_inst_state_chg(fi, ST_CRCX_COMPL, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
400}
401
402/* Callback for MGCP-Client: handle response for CN associated CRCX */
403static void mgw_crcx_cn_resp_cb(struct mgcp_response *r, void *priv)
404{
405 struct mgcp_ctx *mgcp_ctx = priv;
406 int rc;
407 struct gsm_trans *trans;
408 struct gsm_subscriber_connection *conn;
409
410 OSMO_ASSERT(mgcp_ctx);
411 trans = mgcp_ctx->trans;
412 OSMO_ASSERT(trans);
413 conn = trans->conn;
414 OSMO_ASSERT(conn);
415
416 if (r->head.response_code != 200) {
417 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
418 "CRCX/CN: response yields error: %d %s\n", r->head.response_code, r->head.comment);
Philipp Maier4eef20b2018-03-13 13:07:45 +0100419 handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100420 return;
421 }
422
423 /* memorize connection identifier */
424 osmo_strlcpy(mgcp_ctx->conn_id_cn, r->head.conn_id, sizeof(mgcp_ctx->conn_id_cn));
425 LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/CN: MGW responded with CI: %s\n", mgcp_ctx->conn_id_cn);
426
427 rc = mgcp_response_parse_params(r);
428 if (rc) {
429 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR, "CRCX/CN: Cannot parse response\n");
Philipp Maier4eef20b2018-03-13 13:07:45 +0100430 handle_error(mgcp_ctx, MGCP_ERR_MGW_INVAL_RESP, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100431 return;
432 }
433
434 LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/CN: MGW responded with address %s:%u\n", r->audio_ip, r->audio_port);
435
436 conn->rtp.local_port_cn = r->audio_port;
437 osmo_strlcpy(conn->rtp.local_addr_cn, r->audio_ip, sizeof(conn->rtp.local_addr_cn));
438
439 /* Notify the FSM that we got the response. */
440 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_CRCX_CN_RESP, mgcp_ctx);
441}
442
443/* Callback for ST_CRCX_COMPL: check MGW response, start assignment */
444static void fsm_crcx_compl(struct osmo_fsm_inst *fi, uint32_t event, void *data)
445{
446 struct mgcp_ctx *mgcp_ctx = data;
447 struct gsm_trans *trans;
448 struct gsm_subscriber_connection *conn;
449
450 OSMO_ASSERT(mgcp_ctx);
451 trans = mgcp_ctx->trans;
452 OSMO_ASSERT(trans);
453 conn = trans->conn;
454 OSMO_ASSERT(conn);
455
456 switch (event) {
457 case EV_CRCX_CN_RESP:
458 break;
459 default:
Philipp Maier4eef20b2018-03-13 13:07:45 +0100460 handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100461 return;
462 }
463
464 /* Forward assignment request to A/RANAP */
465 if (conn->via_ran == RAN_UTRAN_IU) {
466#ifdef BUILD_IU
467 /* Assign a voice channel via RANAP on 3G */
468 if (iu_rab_act_cs(trans))
469 goto error;
470#else
471 LOGPFSML(fi, LOGL_ERROR, "Cannot send Iu RAB Assignment: built without Iu support\n");
472 goto error;
473#endif
474 } else if (conn->via_ran == RAN_GERAN_A) {
475 /* Assign a voice channel via A on 2G */
476 if (a_iface_tx_assignment(trans))
477 goto error;
478 } else {
479 /* Unset or unimplemented new RAN type */
480 LOGPFSML(fi, LOGL_ERROR, "Unknown RAN type: %d\n", conn->via_ran);
481 return;
482 }
483
484 /* Respond back to MNCC (if requested) */
485 if (trans->tch_rtp_create) {
486 if (gsm48_tch_rtp_create(trans))
487 goto error;
488 }
489
490 /* Note: When we reach this point then the situation is basically that
491 * we have two sides connected, both are in loopback. The local ports
492 * of the side pointing towards the BSS should be already communicated
Philipp Maier4c573772018-02-08 14:05:43 +0100493 * and we are waiting now the other end to pick up. */
Philipp Maier621ba032017-11-07 17:19:25 +0100494 osmo_fsm_inst_state_chg(fi, ST_MDCX_CN, MGCP_RAN_TIMEOUT, MGCP_RAN_TIMEOUT_TIMER_NR);
495 return;
496
497error:
Philipp Maier4eef20b2018-03-13 13:07:45 +0100498 handle_error(mgcp_ctx, MGCP_ERR_ASSGMNT_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100499}
500
501static void mgw_mdcx_cn_resp_cb(struct mgcp_response *r, void *priv);
502
503/* Callback for ST_MDCX_CN: send MDCX for RAN side to MGW */
504static void fsm_mdcx_cn_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
505{
506 struct mgcp_ctx *mgcp_ctx = data;
507 struct mgcp_client *mgcp;
508 struct gsm_trans *trans;
509 struct gsm_subscriber_connection *conn;
510 struct mgcp_msg mgcp_msg;
511 struct msgb *msg;
512 int rc;
513
514 OSMO_ASSERT(mgcp_ctx);
515 mgcp = mgcp_ctx->mgcp;
516 OSMO_ASSERT(mgcp);
517 trans = mgcp_ctx->trans;
518 OSMO_ASSERT(trans);
519 conn = trans->conn;
520 OSMO_ASSERT(conn);
521
522 switch (event) {
523 case EV_CONNECT:
524 break;
525 default:
Philipp Maier4eef20b2018-03-13 13:07:45 +0100526 handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100527 return;
528 }
529
530 LOGPFSML(fi, LOGL_DEBUG,
531 "MDCX/CN: completing connection for the CN side on MGW endpoint:0x%x, remote leg expects RTP input on address %s:%u\n",
532 mgcp_ctx->rtp_endpoint, conn->rtp.remote_addr_cn, conn->rtp.remote_port_cn);
533
534 /* Generate MGCP message string */
535 mgcp_msg = (struct mgcp_msg) {
536 .verb = MGCP_VERB_MDCX,
537 .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_ID |
538 MGCP_MSG_PRESENCE_CONN_MODE | MGCP_MSG_PRESENCE_AUDIO_IP |
539 MGCP_MSG_PRESENCE_AUDIO_PORT),
Philipp Maiera2353c62018-02-08 14:15:59 +0100540 .call_id = mgcp_ctx->call_id,
Philipp Maier621ba032017-11-07 17:19:25 +0100541 .conn_id = mgcp_ctx->conn_id_cn,
542 .conn_mode = MGCP_CONN_RECV_SEND,
543 .audio_ip = conn->rtp.remote_addr_cn,
544 .audio_port = conn->rtp.remote_port_cn
545 };
Philipp Maiera2353c62018-02-08 14:15:59 +0100546 if (osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->rtp_endpoint, sizeof(mgcp_msg.endpoint)) >=
Philipp Maier621ba032017-11-07 17:19:25 +0100547 MGCP_ENDPOINT_MAXLEN) {
Philipp Maier3a776522018-02-22 12:00:00 +0100548 handle_error(mgcp_ctx, MGCP_ERR_TOOLONG, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100549 return;
550 }
Philipp Maiera2353c62018-02-08 14:15:59 +0100551
Philipp Maier621ba032017-11-07 17:19:25 +0100552 msg = mgcp_msg_gen(mgcp, &mgcp_msg);
553 OSMO_ASSERT(msg);
554
555 /* Transmit MGCP message to MGW */
556 mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
557 rc = mgcp_client_tx(mgcp, msg, mgw_mdcx_cn_resp_cb, mgcp_ctx);
558 if (rc < 0) {
Philipp Maier4eef20b2018-03-13 13:07:45 +0100559 handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100560 return;
561 }
562
563 osmo_fsm_inst_state_chg(fi, ST_MDCX_CN_COMPL, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
564}
565
566/* Callback for MGCP-Client: handle response for CN associated CRCX */
567static void mgw_mdcx_cn_resp_cb(struct mgcp_response *r, void *priv)
568{
569 struct mgcp_ctx *mgcp_ctx = priv;
570
571 OSMO_ASSERT(mgcp_ctx);
572
573 if (r->head.response_code != 200) {
574 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
575 "MDCX/CN: response yields error: %d %s\n", r->head.response_code, r->head.comment);
Philipp Maier4eef20b2018-03-13 13:07:45 +0100576 handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100577 return;
578 }
579
580 /* Notify the FSM that we got the response. */
581 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_MDCX_CN_RESP, mgcp_ctx);
582}
583
584/* Callback for ST_MDCX_CN_COMPL: wait for mgw response, move on with the MDCX
585 * for the RAN side if we already have valid IP/Port data for the RAN sided
586 * RTP stream. */
587static void fsm_mdcx_cn_compl_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
588{
589 struct mgcp_ctx *mgcp_ctx = data;
590 struct gsm_subscriber_connection *conn;
591 struct gsm_trans *trans;
592
593 OSMO_ASSERT(mgcp_ctx);
594 trans = mgcp_ctx->trans;
595 OSMO_ASSERT(trans);
596 conn = trans->conn;
597 OSMO_ASSERT(conn);
598
599 switch (event) {
600 case EV_MDCX_CN_RESP:
601 break;
602 default:
Philipp Maier4eef20b2018-03-13 13:07:45 +0100603 handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100604 return;
605 }
606
607 /* Enter MDCX phase, but we must be sure that the Assigmnet on the A or
608 * IuCS interface is complete (IP-Address and Port are valid) */
609 osmo_fsm_inst_state_chg(fi, ST_MDCX_RAN, MGCP_ASS_TIMEOUT, MGCP_ASS_TIMEOUT_TIMER_NR);
610
611 /* If we already have a valid remote port and IP-Address from the RAN side
612 * call leg, the assignment has been completed before we got here, so we
613 * may move on immediately */
614 if (conn->rtp.remote_port_ran != 0 || strlen(conn->rtp.remote_addr_ran) > 0)
615 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_ASSIGN, mgcp_ctx);
616}
617
618static void mgw_mdcx_ran_resp_cb(struct mgcp_response *r, void *priv);
619
620/* Callback for ST_MDCX_RAN: wait for assignment completion, send MDCX for CN side to MGW */
621static void fsm_mdcx_ran_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
622{
623 struct mgcp_ctx *mgcp_ctx = data;
624 struct mgcp_client *mgcp;
625 struct gsm_trans *trans;
626 struct gsm_subscriber_connection *conn;
627 struct mgcp_msg mgcp_msg;
628 struct msgb *msg;
629 int rc;
630
631 OSMO_ASSERT(mgcp_ctx);
632 mgcp = mgcp_ctx->mgcp;
633 OSMO_ASSERT(mgcp);
634 trans = mgcp_ctx->trans;
635 OSMO_ASSERT(trans);
636 conn = trans->conn;
637 OSMO_ASSERT(conn);
638
639 switch (event) {
640 case EV_ASSIGN:
641 break;
642 default:
Philipp Maier4eef20b2018-03-13 13:07:45 +0100643 handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100644 return;
645 }
646
647 LOGPFSML(fi, LOGL_DEBUG,
648 "MDCX/RAN: completing connection for the CN side on MGW endpoint:0x%x, RAN expects RTP input on address %s:%u\n",
649 mgcp_ctx->rtp_endpoint, conn->rtp.remote_addr_ran, conn->rtp.remote_port_ran);
650
651 /* Generate MGCP message string */
652 mgcp_msg = (struct mgcp_msg) {
653 .verb = MGCP_VERB_MDCX,
654 .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_ID |
655 MGCP_MSG_PRESENCE_CONN_MODE | MGCP_MSG_PRESENCE_AUDIO_IP |
656 MGCP_MSG_PRESENCE_AUDIO_PORT),
Philipp Maiera2353c62018-02-08 14:15:59 +0100657 .call_id = mgcp_ctx->call_id,
Philipp Maier621ba032017-11-07 17:19:25 +0100658 .conn_id = mgcp_ctx->conn_id_ran,
659 .conn_mode = MGCP_CONN_RECV_SEND,
660 .audio_ip = conn->rtp.remote_addr_ran,
661 .audio_port = conn->rtp.remote_port_ran
662 };
Philipp Maiera2353c62018-02-08 14:15:59 +0100663 if (osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->rtp_endpoint, sizeof(mgcp_msg.endpoint)) >=
Philipp Maier621ba032017-11-07 17:19:25 +0100664 MGCP_ENDPOINT_MAXLEN) {
Philipp Maier3a776522018-02-22 12:00:00 +0100665 handle_error(mgcp_ctx, MGCP_ERR_TOOLONG, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100666 return;
667 }
Philipp Maiera2353c62018-02-08 14:15:59 +0100668
Philipp Maier621ba032017-11-07 17:19:25 +0100669 msg = mgcp_msg_gen(mgcp, &mgcp_msg);
670 OSMO_ASSERT(msg);
671
672 /* Transmit MGCP message to MGW */
673 mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
674 rc = mgcp_client_tx(mgcp, msg, mgw_mdcx_ran_resp_cb, mgcp_ctx);
675 if (rc < 0) {
Philipp Maier4eef20b2018-03-13 13:07:45 +0100676 handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100677 return;
678 }
679
680 osmo_fsm_inst_state_chg(fi, ST_MDCX_RAN_COMPL, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
681}
682
683/* Callback for MGCP-Client: handle response for CN associated CRCX */
684static void mgw_mdcx_ran_resp_cb(struct mgcp_response *r, void *priv)
685{
686 struct mgcp_ctx *mgcp_ctx = priv;
687
688 OSMO_ASSERT(mgcp_ctx);
689
690 if (r->head.response_code != 200) {
691 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
692 "MDCX/RAN: response yields error: %d %s\n", r->head.response_code, r->head.comment);
Philipp Maier4eef20b2018-03-13 13:07:45 +0100693 handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100694 return;
695 }
696
697 /* Notify the FSM that we got the response. */
698 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_MDCX_RAN_RESP, mgcp_ctx);
699}
700
701/* Callback for ST_MDCX_RAN_COMPL: check MGW response */
702static void fsm_mdcx_ran_compl_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
703{
704 struct mgcp_ctx *mgcp_ctx = data;
705 OSMO_ASSERT(mgcp_ctx);
706
707 switch (event) {
708 case EV_MDCX_RAN_RESP:
709 break;
710 default:
Philipp Maier4eef20b2018-03-13 13:07:45 +0100711 handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100712 return;
713 }
714
715 LOGPFSML(fi, LOGL_DEBUG, "call active, waiting for teardown...\n");
716 osmo_fsm_inst_state_chg(fi, ST_CALL, 0, 0);
717}
718
719static void mgw_dlcx_all_resp_cb(struct mgcp_response *r, void *priv);
720
721/* Callback for ST_CALL: call is active, send DLCX for both sides on teardown */
722static void fsm_call_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
723{
724
725 struct mgcp_ctx *mgcp_ctx = (struct mgcp_ctx *)data;
726 struct mgcp_client *mgcp;
727 struct mgcp_msg mgcp_msg;
728 struct msgb *msg;
729 int rc;
730
731 OSMO_ASSERT(mgcp_ctx);
732 mgcp = mgcp_ctx->mgcp;
733 OSMO_ASSERT(mgcp);
734
735 LOGPFSML(fi, LOGL_DEBUG,
736 "DLCX: removing connection for the RAN and CN side on MGW endpoint:0x%x...\n", mgcp_ctx->rtp_endpoint);
737
Philipp Maier621ba032017-11-07 17:19:25 +0100738 /* Generate MGCP message string */
739 mgcp_msg = (struct mgcp_msg) {
740 .verb = MGCP_VERB_DLCX,
741 .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID),
Philipp Maiera2353c62018-02-08 14:15:59 +0100742 .call_id = mgcp_ctx->call_id
Philipp Maier621ba032017-11-07 17:19:25 +0100743 };
Philipp Maiera2353c62018-02-08 14:15:59 +0100744 if (osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->rtp_endpoint, sizeof(mgcp_msg.endpoint)) >=
Philipp Maier621ba032017-11-07 17:19:25 +0100745 MGCP_ENDPOINT_MAXLEN) {
Philipp Maier3a776522018-02-22 12:00:00 +0100746 handle_error(mgcp_ctx, MGCP_ERR_TOOLONG, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100747 return;
748 }
Philipp Maiera2353c62018-02-08 14:15:59 +0100749
Philipp Maier621ba032017-11-07 17:19:25 +0100750 msg = mgcp_msg_gen(mgcp, &mgcp_msg);
751 OSMO_ASSERT(msg);
752
753 /* Transmit MGCP message to MGW */
754 mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
755 rc = mgcp_client_tx(mgcp, msg, mgw_dlcx_all_resp_cb, mgcp_ctx);
756 if (rc < 0) {
Philipp Maier4eef20b2018-03-13 13:07:45 +0100757 handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100758 return;
759 }
760
761 osmo_fsm_inst_state_chg(fi, ST_HALT, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
762}
763
764/* Callback for MGCP-Client: handle response for CN associated CRCX */
765static void mgw_dlcx_all_resp_cb(struct mgcp_response *r, void *priv)
766{
767 struct mgcp_ctx *mgcp_ctx = priv;
768
769 OSMO_ASSERT(mgcp_ctx);
770
Harald Welte33d61e72018-02-10 10:43:38 +0100771 /* DLCX is the only command where 250 is permitted as positive result */
772 if (r->head.response_code != 200 && r->head.response_code != 250) {
Philipp Maier621ba032017-11-07 17:19:25 +0100773 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
774 "DLCX: response yields error: %d %s\n", r->head.response_code, r->head.comment);
Philipp Maier4eef20b2018-03-13 13:07:45 +0100775 handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100776 return;
777 }
778
779 /* Notify the FSM that we got the response. */
780 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_DLCX_ALL_RESP, mgcp_ctx);
781}
782
783/* Callback for ST_HALT: Terminate the state machine */
784static void fsm_halt_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
785{
786 struct mgcp_ctx *mgcp_ctx = data;
Philipp Maieraddf63b2018-03-14 13:37:44 +0100787 struct mgcp_client *mgcp;
Philipp Maier621ba032017-11-07 17:19:25 +0100788
789 OSMO_ASSERT(mgcp_ctx);
Philipp Maieraddf63b2018-03-14 13:37:44 +0100790 mgcp = mgcp_ctx->mgcp;
791 OSMO_ASSERT(mgcp);
Philipp Maier621ba032017-11-07 17:19:25 +0100792
793 /* NOTE: We must not free the context information now, we have to
794 * wait until msc_mgcp_call_release() is called. Then we are sure
795 * that the logic controlling us is fully aware that the context
796 * information is freed. If we would free early now the controlling
797 * logic might mistakenly think that the context info is still alive,
798 * so lets keep the context info until we are explicitly asked for
799 * throwing it away. */
800 if (mgcp_ctx->free_ctx) {
Philipp Maieraddf63b2018-03-14 13:37:44 +0100801 /* Be sure that there is no pending MGW transaction */
802 mgcp_client_cancel(mgcp, mgcp_ctx->mgw_pending_trans);
803
804 /* Free FSM and its context information */
Philipp Maier621ba032017-11-07 17:19:25 +0100805 osmo_fsm_inst_free(mgcp_ctx->fsm);
806 talloc_free(mgcp_ctx);
807 return;
808 }
809
810 osmo_fsm_inst_state_chg(fi, ST_HALT, MGCP_REL_TIMEOUT, MGCP_REL_TIMEOUT_TIMER_NR);
811}
812
813static struct osmo_fsm_state fsm_msc_mgcp_states[] = {
814
815 /* Startup state machine, send CRCX for RAN side. */
816 [ST_CRCX_RAN] = {
817 .in_event_mask = S(EV_INIT),
818 .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_CRCX_CN),
819 .name = OSMO_STRINGIFY(ST_CRCX_RAN),
820 .action = fsm_crcx_ran_cb,
821 },
822 /* When the response to the RAN CRCX is received, then proceed with
823 sending the CRCX for CN side */
824 [ST_CRCX_CN] = {
825 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_CRCX_RAN_RESP),
826 .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_CRCX_COMPL),
827 .name = OSMO_STRINGIFY(ST_CRCX_CN),
828 .action = fsm_crcx_cn_cb,
829 },
830 /* Complete the CRCX phase by starting the assignment. Depending on the
831 * RAT (Radio Access Technology), this will either trigger an
832 * Assignment Request on the A-Interface or an RAB-Assignment on the
833 * IU-interface */
834 [ST_CRCX_COMPL] = {
835 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_CRCX_CN_RESP),
836 .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_MDCX_CN),
837 .name = OSMO_STRINGIFY(ST_CRCX_COMPL),
838 .action = fsm_crcx_compl,
839 },
840 /* Wait for MSC to complete the assignment request, when complete, we
841 * will enter the MDCX phase by sending an MDCX for the CN side to the
842 * MGW */
843 [ST_MDCX_CN] = {
844 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_CONNECT),
845 .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_MDCX_CN_COMPL),
846 .name = OSMO_STRINGIFY(ST_MDCX_CN),
847 .action = fsm_mdcx_cn_cb,
848 },
849 /* We arrive in this state when the MDCX phase for the CN side has
850 * completed we will check the IP/Port of the RAN connection. If this
851 * data is valid we may continue with the MDCX phase for the RAN side.
852 * If not we wait until the assinment completes on the A or on the IuCS
853 * interface. The completion of the assignment will fill in the port and
854 * IP-Address of the RAN side and way may continue then. */
855 [ST_MDCX_CN_COMPL] = {
856 .in_event_mask = S(EV_TEARDOWN) | S(EV_MDCX_CN_RESP),
857 .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_MDCX_RAN),
858 .name = OSMO_STRINGIFY(ST_MDCX_CN_COMPL),
859 .action = fsm_mdcx_cn_compl_cb,
860 },
861 /* When the response for the CN MDCX is received, send the MDCX for the
862 * RAN side to the MGW */
863 [ST_MDCX_RAN] = {
864 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_ASSIGN),
865 .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_MDCX_RAN_COMPL),
866 .name = OSMO_STRINGIFY(ST_MDCX_RAN),
867 .action = fsm_mdcx_ran_cb,
868 },
869 /* The RAN side MDCX phase is complete when the response is received
870 * from the MGW. The call is then active, we change to ST_CALL and wait
871 * there until the call ends. */
872 [ST_MDCX_RAN_COMPL] = {
873 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_MDCX_RAN_RESP),
874 .out_state_mask = S(ST_HALT) | S(ST_CALL),
875 .name = OSMO_STRINGIFY(ST_MDCX_RAN_COMPL),
876 .action = fsm_mdcx_ran_compl_cb,
877 },
878 /* We are now in the active call phase, wait until the call is done
879 * and send a DLCX then to remove all connections from the MGW */
880 [ST_CALL] = {
881 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR),
882 .out_state_mask = S(ST_HALT),
883 .name = OSMO_STRINGIFY(ST_CALL),
884 .action = fsm_call_cb,
885 },
886 /* When the MGW confirms that the connections are terminated, then halt
887 * the state machine. */
888 [ST_HALT] = {
889 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_DLCX_ALL_RESP),
890 .out_state_mask = S(ST_HALT),
891 .name = OSMO_STRINGIFY(ST_HALT),
892 .action = fsm_halt_cb,
893 },
894};
895
896/* State machine definition */
897static struct osmo_fsm fsm_msc_mgcp = {
898 .name = "MGW",
899 .states = fsm_msc_mgcp_states,
900 .num_states = ARRAY_SIZE(fsm_msc_mgcp_states),
901 .log_subsys = DMGCP,
902 .timer_cb = fsm_timeout_cb,
Philipp Maiere4f91722018-02-26 15:20:49 +0100903 .event_names = msc_mgcp_fsm_evt_names,
Philipp Maier621ba032017-11-07 17:19:25 +0100904};
905
906/* Notify that a new call begins. This will create a connection for the
907 * RAN and the CN on the MGW.
908 * Parameter:
909 * trans: transaction context.
910 * Returns -EINVAL on error, 0 on success. */
911int msc_mgcp_call_assignment(struct gsm_trans *trans)
912{
913 struct mgcp_ctx *mgcp_ctx;
914 char name[32];
915 static bool fsm_registered = false;
916 struct gsm_subscriber_connection *conn;
917 struct mgcp_client *mgcp;
918
919 OSMO_ASSERT(trans);
920
921 if (!trans->conn) {
922 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid conn, call assignment failed\n",
923 vlr_subscr_name(trans->vsub));
924 return -EINVAL;
925 }
926
927 conn = trans->conn;
928 mgcp = conn->network->mgw.client;
929 OSMO_ASSERT(mgcp);
930
931 if (conn->rtp.mgcp_ctx) {
932 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) double assignment detected, dropping...\n",
933 vlr_subscr_name(trans->vsub));
934 return -EINVAL;
935 }
936
937#ifdef BUILD_IU
938 /* FIXME: HACK. where to scope the RAB Id? At the conn / subscriber / ranap_ue_conn_ctx? */
939 static uint8_t next_iu_rab_id = 1;
940 if (conn->via_ran == RAN_UTRAN_IU)
941 conn->iu.rab_id = next_iu_rab_id++;
942#endif
943
944 if (snprintf(name, sizeof(name), "MGW_%i", trans->transaction_id) >= sizeof(name))
945 return -EINVAL;
946
947 /* Register the fsm description (if not already done) */
948 if (fsm_registered == false) {
949 osmo_fsm_register(&fsm_msc_mgcp);
950 fsm_registered = true;
951 }
952
953 /* Allocate and configure a new fsm instance */
954 mgcp_ctx = talloc_zero(NULL, struct mgcp_ctx);
955 OSMO_ASSERT(mgcp_ctx);
Philipp Maiera2353c62018-02-08 14:15:59 +0100956 if (osmo_strlcpy(mgcp_ctx->rtp_endpoint, ENDPOINT_ID, sizeof(mgcp_ctx->rtp_endpoint)) >=
957 MGCP_ENDPOINT_MAXLEN) {
958 talloc_free(mgcp_ctx);
959 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) endpoint identifier (%s) exceeds maximum length...\n",
960 vlr_subscr_name(trans->vsub), ENDPOINT_ID);
961 return -EINVAL;
962 }
Philipp Maier621ba032017-11-07 17:19:25 +0100963 mgcp_ctx->fsm = osmo_fsm_inst_alloc(&fsm_msc_mgcp, NULL, NULL, LOGL_DEBUG, name);
964 OSMO_ASSERT(mgcp_ctx->fsm);
965 mgcp_ctx->fsm->priv = mgcp_ctx;
966 mgcp_ctx->mgcp = mgcp;
967 mgcp_ctx->trans = trans;
Philipp Maiera2353c62018-02-08 14:15:59 +0100968 mgcp_ctx->call_id = trans->callref;
Philipp Maier621ba032017-11-07 17:19:25 +0100969
970 /* start state machine */
971 OSMO_ASSERT(mgcp_ctx->fsm->state == ST_CRCX_RAN);
972 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_INIT, mgcp_ctx);
973
974 conn->rtp.mgcp_ctx = mgcp_ctx;
975
976 LOGP(DMGCP, LOGL_DEBUG, "(subscriber:%s) call assignment initiated\n",
977 vlr_subscr_name(conn->vsub));
978
979 return 0;
980}
981
982/* Inform the FSM that the assignment (RAN connection) is now complete.
983 * Parameter:
984 * conn: subscriber connection context.
985 * port: port number of the remote leg.
986 * addr: IP-address of the remote leg.
987 * Returns -EINVAL on error, 0 on success. */
988int msc_mgcp_ass_complete(struct gsm_subscriber_connection *conn, uint16_t port, char *addr)
989{
990 struct mgcp_ctx *mgcp_ctx;
991
992 OSMO_ASSERT(conn);
993
994 if (port == 0) {
995 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid remote call leg port, assignmnet completion failed\n",
996 vlr_subscr_name(conn->vsub));
997 return -EINVAL;
998 }
999 if (!addr || strlen(addr) <= 0) {
1000 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) missing remote call leg address, assignmnet completion failed\n",
1001 vlr_subscr_name(conn->vsub));
1002 return -EINVAL;
1003 }
1004
1005 mgcp_ctx = conn->rtp.mgcp_ctx;
1006 if (!mgcp_ctx) {
1007 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid mgcp context, assignmnet completion failed.\n",
1008 vlr_subscr_name(conn->vsub));
1009 return -EINVAL;
1010 }
1011
1012 /* Memorize port and IP-Address of the remote RAN call leg. We need this
1013 * information at latest when we enter the MDCX phase for the RAN side. */
1014 conn->rtp.remote_port_ran = port;
1015 osmo_strlcpy(conn->rtp.remote_addr_ran, addr, sizeof(conn->rtp.remote_addr_ran));
1016
1017 LOGP(DMGCP, LOGL_DEBUG, "(subscriber:%s) assignmnet completed, rtp %s:%d.\n",
1018 vlr_subscr_name(conn->vsub), conn->rtp.remote_addr_ran, port);
1019
1020 /* Note: We only dispatch the event if we are really waiting for the
1021 * assignment, if we are not yet waiting, there is no need to loudly
1022 * broadcast an event that the all other states do not understand anyway */
1023 if (mgcp_ctx->fsm->state == ST_MDCX_RAN)
1024 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_ASSIGN, mgcp_ctx);
1025
1026 return 0;
1027}
1028
1029/* Make the connection of a previously assigned call complete
1030 * Parameter:
1031 * trans: transaction context.
1032 * port: port number of the remote leg.
1033 * addr: IP-address of the remote leg.
1034 * Returns -EINVAL on error, 0 on success. */
1035int msc_mgcp_call_complete(struct gsm_trans *trans, uint16_t port, char *addr)
1036{
1037 struct mgcp_ctx *mgcp_ctx;
1038 struct gsm_subscriber_connection *conn;
1039
1040 OSMO_ASSERT(trans);
1041 OSMO_ASSERT(addr);
1042
1043 if (port == 0) {
1044 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid remote call leg port, call completion failed\n",
1045 vlr_subscr_name(trans->vsub));
1046 return -EINVAL;
1047 }
1048 if (!addr || strlen(addr) <= 0) {
1049 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) missing remote call leg address, call completion failed\n",
1050 vlr_subscr_name(trans->vsub));
1051 return -EINVAL;
1052 }
1053 if (!trans->conn) {
1054 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid conn, call completion failed\n",
1055 vlr_subscr_name(trans->vsub));
1056 return -EINVAL;
1057 }
1058 if (!trans->conn->rtp.mgcp_ctx) {
1059 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid mgcp context, call completion failed.\n",
1060 vlr_subscr_name(trans->vsub));
1061 return -EINVAL;
1062 }
1063 if (!trans->conn->rtp.mgcp_ctx->fsm) {
1064 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) no FSM, call completion failed\n",
1065 vlr_subscr_name(trans->vsub));
1066 return -EINVAL;
1067 }
1068
1069 mgcp_ctx = trans->conn->rtp.mgcp_ctx;
1070
1071 /* The FSM should already have passed all CRCX phases and be ready to move
1072 * on with the MDCX phases. */
1073 if (mgcp_ctx->fsm->state != ST_MDCX_CN) {
1074 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid call state, call completion failed\n",
1075 vlr_subscr_name(trans->vsub));
1076 return -EINVAL;
1077 }
1078
1079 conn = trans->conn;
1080 osmo_strlcpy(conn->rtp.remote_addr_cn, addr, sizeof(conn->rtp.remote_addr_cn));
1081 conn->rtp.remote_port_cn = port;
1082
1083 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_CONNECT, mgcp_ctx);
1084
1085 LOGP(DMGCP, LOGL_DEBUG, "(subscriber:%s) call completion initiated\n",
1086 vlr_subscr_name(conn->vsub));
1087
1088 return 0;
1089}
1090
1091/* Release ongoing call.
1092 * Parameter:
1093 * trans: connection context.
1094 * Returns -EINVAL on error, 0 on success. */
1095int msc_mgcp_call_release(struct gsm_trans *trans)
1096{
1097 struct mgcp_ctx *mgcp_ctx;
1098
1099 OSMO_ASSERT(trans);
1100
1101 if (!trans->conn) {
1102 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid conn, call release failed\n",
1103 vlr_subscr_name(trans->vsub));
1104 return -EINVAL;
1105 }
1106 if (!trans->conn->rtp.mgcp_ctx) {
1107 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid mgcp context, call release failed.\n",
1108 vlr_subscr_name(trans->vsub));
1109 return -EINVAL;
1110 }
1111 if (!trans->conn->rtp.mgcp_ctx->fsm) {
1112 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) no FSM, call release failed\n",
1113 vlr_subscr_name(trans->vsub));
1114 return -EINVAL;
1115 }
1116
1117 mgcp_ctx = trans->conn->rtp.mgcp_ctx;
1118
1119 /* Inform the FSM that as soon as it reaches ST_HALT it may free
1120 * all context information immediately */
1121 mgcp_ctx->free_ctx = true;
1122
1123 /* Initaite teardown, regardless of which state we are currently
1124 * in */
1125 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_TEARDOWN, mgcp_ctx);
1126
1127 /* Prevent any further operation that is triggered from outside by
1128 * overwriting the context pointer with NULL. The FSM will now
1129 * take care for a graceful shutdown and when done it will free
1130 * all related context information */
1131 trans->conn->rtp.mgcp_ctx = NULL;
1132
1133 LOGP(DMGCP, LOGL_DEBUG, "(subscriber:%s) call release initiated\n",
1134 vlr_subscr_name(trans->vsub));
1135
1136 return 0;
1137}