blob: 7c49c6ebce1405a506be9c19fa8561c2d411509d [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{
Philipp Maier79beccd2018-04-11 17:36:45 +0200159 bool dlcx_possible = true;
Philipp Maier621ba032017-11-07 17:19:25 +0100160 struct osmo_fsm_inst *fi;
Daniel Willmann58d9dd82018-02-15 17:49:22 +0100161 struct gsm_mncc mncc = {
162 .msg_type = MNCC_REL_REQ,
163 .callref = mgcp_ctx->trans->callref,
164 .cause = {
165 .location = GSM48_CAUSE_LOC_PRN_S_LU,
166 .coding = 0, /* FIXME */
167 .value = GSM48_CC_CAUSE_RESOURCE_UNAVAIL
168 }
169 };
Philipp Maier621ba032017-11-07 17:19:25 +0100170
171 OSMO_ASSERT(mgcp_ctx);
172 fi = mgcp_ctx->fsm;
173 OSMO_ASSERT(fi);
174
Philipp Maier79beccd2018-04-11 17:36:45 +0200175 /* Check if the endpoint identifier is a specific endpoint identifier,
176 * since in order to perform a DLCX we must know the specific
177 * identifier of the endpoint we want to release. If we do not have
178 * this information because of errornous communication we can not
179 * perform a DLCX. */
180 if (strstr(mgcp_ctx->rtp_endpoint, "*"))
181 dlcx_possible = false;
182
Philipp Maier621ba032017-11-07 17:19:25 +0100183 LOGPFSMLSRC(mgcp_ctx->fsm, LOGL_ERROR, file, line, "%s -- graceful shutdown...\n",
184 get_value_string(msc_mgcp_cause_codes_names, cause));
185
Philipp Maier4eef20b2018-03-13 13:07:45 +0100186 /* For the shutdown we have two options. Whenever it makes sense to
187 * send a DLCX to the MGW in order to be sure that the connection is
188 * properly cleaned up, the dlcx flag should be set. In other cases
189 * where a DLCX does not make sense (e.g. the MGW times out), halting
190 * directly is the better options. In those cases, the dlcx flag
191 * should not be set */
Philipp Maier79beccd2018-04-11 17:36:45 +0200192 if (dlcx && dlcx_possible) {
Philipp Maier4eef20b2018-03-13 13:07:45 +0100193 /* Fast-forward the FSM into call state. In this state the FSM
194 * expects either an EV_TEARDOWN or an EV_TEARDOWN_ERROR. When
195 * one of the two events is received a DLCX will be send to
196 * the MGW. After that. The FSM automatically halts but will
197 * still expect a call msc_mgcp_call_release() to be freed
198 * completely */
199 osmo_fsm_inst_state_chg(fi, ST_CALL, 0, 0);
200 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_TEARDOWN_ERROR, mgcp_ctx);
201 } else {
202 /* Halt the state machine immediately. The FSM will not be
203 * freed yet, we stil require the higher layers to call
204 * msc_mgcp_call_release() */
205 osmo_fsm_inst_state_chg(fi, ST_HALT, 0, 0);
206 osmo_fsm_inst_dispatch(fi, EV_TEARDOWN_ERROR, mgcp_ctx);
207 }
Philipp Maier621ba032017-11-07 17:19:25 +0100208
Philipp Maier04d6ddb22018-03-16 18:01:21 +0100209 /* Request the higher layers (gsm_04_08.c) to release the call. If the
210 * problem occured after msc_mgcp_call_release() was calls, remain
211 * silent because we already got informed and the higher layers might
212 * already freed their context information (trans). */
213 if (!mgcp_ctx->free_ctx) {
214 mncc_set_cause(&mncc, GSM48_CAUSE_LOC_TRANS_NET,
215 GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
216 mncc_tx_to_cc(mgcp_ctx->trans->net, MNCC_REL_REQ, &mncc);
217 }
Philipp Maier621ba032017-11-07 17:19:25 +0100218}
219
220/* Timer callback to shut down in case of connectivity problems */
221static int fsm_timeout_cb(struct osmo_fsm_inst *fi)
222{
223 struct mgcp_ctx *mgcp_ctx = fi->priv;
224 struct mgcp_client *mgcp;
225
226 OSMO_ASSERT(mgcp_ctx);
227 mgcp = mgcp_ctx->mgcp;
228 OSMO_ASSERT(mgcp);
229
230 if (fi->T == MGCP_MGW_TIMEOUT_TIMER_NR) {
231 /* We were unable to communicate with the MGW, unfortunately
232 * there is no meaningful action we can take now other than
233 * giving up. */
234
Philipp Maier621ba032017-11-07 17:19:25 +0100235 /* Cancel the transaction that timed out */
236 mgcp_client_cancel(mgcp, mgcp_ctx->mgw_pending_trans);
237
Philipp Maier4eef20b2018-03-13 13:07:45 +0100238 /* halt of the FSM */
239 handle_error(mgcp_ctx, MGCP_ERR_MGW_TIMEOUT, false);
Philipp Maier621ba032017-11-07 17:19:25 +0100240 } else if (fi->T == MGCP_RAN_TIMEOUT_TIMER_NR) {
241 /* If the logic that controls the RAN is unable to negotiate a
242 * connection, we presumably still have a working connection to
243 * the MGW, we will try to shut down gracefully. */
Philipp Maier4eef20b2018-03-13 13:07:45 +0100244 handle_error(mgcp_ctx, MGCP_ERR_RAN_TIMEOUT, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100245 } else if (fi->T == MGCP_REL_TIMEOUT_TIMER_NR) {
246 /* Under normal conditions, the MSC logic should always command
247 * to release the call at some point. However, the release may
248 * be missing due to errors in the MSC logic and we may have
249 * reached ST_HALT because of cascading errors and timeouts. In
250 * this and only in this case we will allow ST_HALT to free all
251 * context information on its own authority. */
252 mgcp_ctx->free_ctx = true;
253
254 /* Initiate self destruction of the FSM */
255 osmo_fsm_inst_state_chg(fi, ST_HALT, 0, 0);
256 osmo_fsm_inst_dispatch(fi, EV_TEARDOWN, mgcp_ctx);
257 } else if (fi->T == MGCP_ASS_TIMEOUT_TIMER_NR) {
258 /* There may be rare cases in which the MSC is unable to
259 * complete the call assignment */
Philipp Maier4eef20b2018-03-13 13:07:45 +0100260 handle_error(mgcp_ctx, MGCP_ERR_ASS_TIMEOUT, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100261 } else {
262 /* Ther must not be any unsolicited timers in this FSM. If so,
263 * we have serious problem. */
264 OSMO_ASSERT(false);
265 }
266
267 return 0;
268}
269
270static void mgw_crcx_ran_resp_cb(struct mgcp_response *r, void *priv);
271
272/* Callback for ST_CRCX_RAN: Send CRCX for RAN side to MGW */
273static void fsm_crcx_ran_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
274{
275 struct mgcp_ctx *mgcp_ctx = data;
276 struct mgcp_client *mgcp;
277 struct mgcp_msg mgcp_msg;
278 struct msgb *msg;
279 int rc;
280
281 OSMO_ASSERT(mgcp_ctx);
282 mgcp = mgcp_ctx->mgcp;
283 OSMO_ASSERT(mgcp);
284
Philipp Maier79beccd2018-04-11 17:36:45 +0200285 /* NOTE: In case of error, we will not be able to perform any DLCX
286 * operation because until this point we do not have requested any
287 * endpoint yet. */
288
Philipp Maier621ba032017-11-07 17:19:25 +0100289 LOGPFSML(fi, LOGL_DEBUG,
Philipp Maierd997fa12018-04-11 15:23:30 +0200290 "CRCX/RAN: creating connection for the RAN side on MGW endpoint:%s...\n", mgcp_ctx->rtp_endpoint);
Philipp Maier621ba032017-11-07 17:19:25 +0100291
292 /* Generate MGCP message string */
293 mgcp_msg = (struct mgcp_msg) {
294 .verb = MGCP_VERB_CRCX,
295 .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_MODE),
Philipp Maiera2353c62018-02-08 14:15:59 +0100296 .call_id = mgcp_ctx->call_id,
Philipp Maier621ba032017-11-07 17:19:25 +0100297 .conn_mode = MGCP_CONN_LOOPBACK
298 };
Philipp Maiera2353c62018-02-08 14:15:59 +0100299 if (osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->rtp_endpoint, sizeof(mgcp_msg.endpoint)) >=
Philipp Maier621ba032017-11-07 17:19:25 +0100300 MGCP_ENDPOINT_MAXLEN) {
Philipp Maier79beccd2018-04-11 17:36:45 +0200301 handle_error(mgcp_ctx, MGCP_ERR_TOOLONG, false);
Philipp Maier621ba032017-11-07 17:19:25 +0100302 return;
303 }
Philipp Maiera2353c62018-02-08 14:15:59 +0100304
Philipp Maier621ba032017-11-07 17:19:25 +0100305 msg = mgcp_msg_gen(mgcp, &mgcp_msg);
306 OSMO_ASSERT(msg);
307
308 /* Transmit MGCP message to MGW */
309 mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
310 rc = mgcp_client_tx(mgcp, msg, mgw_crcx_ran_resp_cb, mgcp_ctx);
311 if (rc < 0) {
Philipp Maier79beccd2018-04-11 17:36:45 +0200312 handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL, false);
Philipp Maier621ba032017-11-07 17:19:25 +0100313 return;
314 }
315
316 osmo_fsm_inst_state_chg(fi, ST_CRCX_CN, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
317}
318
319/* Callback for MGCP-Client: handle response for RAN associated CRCX */
320static void mgw_crcx_ran_resp_cb(struct mgcp_response *r, void *priv)
321{
322 struct mgcp_ctx *mgcp_ctx = priv;
323 int rc;
324 struct gsm_trans *trans;
325 struct gsm_subscriber_connection *conn;
326
Philipp Maier79beccd2018-04-11 17:36:45 +0200327 /* NOTE: In case of error, we will not be able to perform any DLCX
328 * operation because until we either get a parseable message that
329 * contains an error code (no endpoint is seized in those cases)
330 * or we get an unparseable message. In this case we can not be
331 * sure, but we also can not draw any assumptions from unparseable
332 * messages. */
333
Philipp Maier621ba032017-11-07 17:19:25 +0100334 OSMO_ASSERT(mgcp_ctx);
335 trans = mgcp_ctx->trans;
336 OSMO_ASSERT(trans);
337 conn = trans->conn;
338 OSMO_ASSERT(conn);
339
340 if (r->head.response_code != 200) {
341 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
342 "CRCX/RAN: response yields error: %d %s\n", r->head.response_code, r->head.comment);
Philipp Maier79beccd2018-04-11 17:36:45 +0200343 handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL, false);
Philipp Maier621ba032017-11-07 17:19:25 +0100344 return;
345 }
346
Philipp Maiera2353c62018-02-08 14:15:59 +0100347 /* memorize connection identifier and specific endpoint id */
Philipp Maier621ba032017-11-07 17:19:25 +0100348 osmo_strlcpy(mgcp_ctx->conn_id_ran, r->head.conn_id, sizeof(mgcp_ctx->conn_id_ran));
349 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 +0100350 osmo_strlcpy(mgcp_ctx->rtp_endpoint, r->head.endpoint, sizeof(mgcp_ctx->rtp_endpoint));
351 LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/RAN: MGW assigned endpoint: %s\n", mgcp_ctx->rtp_endpoint);
Philipp Maier621ba032017-11-07 17:19:25 +0100352
353 rc = mgcp_response_parse_params(r);
354 if (rc) {
355 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR, "CRCX/RAN: Cannot parse response\n");
Philipp Maier79beccd2018-04-11 17:36:45 +0200356 handle_error(mgcp_ctx, MGCP_ERR_MGW_INVAL_RESP, false);
Philipp Maier621ba032017-11-07 17:19:25 +0100357 return;
358 }
359
360 LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/BTS: MGW responded with address %s:%u\n", r->audio_ip, r->audio_port);
361
362 conn->rtp.local_port_ran = r->audio_port;
363 osmo_strlcpy(conn->rtp.local_addr_ran, r->audio_ip, sizeof(conn->rtp.local_addr_ran));
364
365 /* Notify the FSM that we got the response. */
366 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_CRCX_RAN_RESP, mgcp_ctx);
367}
368
369static void mgw_crcx_cn_resp_cb(struct mgcp_response *r, void *priv);
370
371/* Callback for ST_CRCX_CN: check MGW response and send CRCX for CN side to MGW */
372static void fsm_crcx_cn_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
373{
374 struct mgcp_ctx *mgcp_ctx = data;
375 struct mgcp_client *mgcp;
376 struct mgcp_msg mgcp_msg;
377 struct msgb *msg;
378 int rc;
379
380 OSMO_ASSERT(mgcp_ctx);
381 mgcp = mgcp_ctx->mgcp;
382 OSMO_ASSERT(mgcp);
383
384 switch (event) {
385 case EV_CRCX_RAN_RESP:
386 break;
387 default:
Philipp Maier4eef20b2018-03-13 13:07:45 +0100388 handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100389 return;
390 }
391
392 LOGPFSML(fi, LOGL_DEBUG,
Philipp Maierd997fa12018-04-11 15:23:30 +0200393 "CRCX/CN creating connection for the CN side on MGW endpoint:%s...\n", mgcp_ctx->rtp_endpoint);
Philipp Maier621ba032017-11-07 17:19:25 +0100394
395 /* Generate MGCP message string */
396 mgcp_msg = (struct mgcp_msg) {
397 .verb = MGCP_VERB_CRCX,
398 .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_MODE),
Philipp Maiera2353c62018-02-08 14:15:59 +0100399 .call_id = mgcp_ctx->call_id,
Philipp Maier621ba032017-11-07 17:19:25 +0100400 .conn_mode = MGCP_CONN_LOOPBACK
401 };
Philipp Maiera2353c62018-02-08 14:15:59 +0100402 if (osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->rtp_endpoint, sizeof(mgcp_msg.endpoint)) >=
Philipp Maier621ba032017-11-07 17:19:25 +0100403 MGCP_ENDPOINT_MAXLEN) {
Philipp Maier3a776522018-02-22 12:00:00 +0100404 handle_error(mgcp_ctx, MGCP_ERR_TOOLONG, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100405 return;
406 }
Philipp Maiera2353c62018-02-08 14:15:59 +0100407
Philipp Maier621ba032017-11-07 17:19:25 +0100408 msg = mgcp_msg_gen(mgcp, &mgcp_msg);
409 OSMO_ASSERT(msg);
410
411 /* Transmit MGCP message to MGW */
412 mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
413 rc = mgcp_client_tx(mgcp, msg, mgw_crcx_cn_resp_cb, mgcp_ctx);
414 if (rc < 0) {
Philipp Maier4eef20b2018-03-13 13:07:45 +0100415 handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100416 return;
417 }
418
419 osmo_fsm_inst_state_chg(fi, ST_CRCX_COMPL, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
420}
421
422/* Callback for MGCP-Client: handle response for CN associated CRCX */
423static void mgw_crcx_cn_resp_cb(struct mgcp_response *r, void *priv)
424{
425 struct mgcp_ctx *mgcp_ctx = priv;
426 int rc;
427 struct gsm_trans *trans;
428 struct gsm_subscriber_connection *conn;
429
430 OSMO_ASSERT(mgcp_ctx);
431 trans = mgcp_ctx->trans;
432 OSMO_ASSERT(trans);
433 conn = trans->conn;
434 OSMO_ASSERT(conn);
435
436 if (r->head.response_code != 200) {
437 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
438 "CRCX/CN: response yields error: %d %s\n", r->head.response_code, r->head.comment);
Philipp Maier4eef20b2018-03-13 13:07:45 +0100439 handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100440 return;
441 }
442
443 /* memorize connection identifier */
444 osmo_strlcpy(mgcp_ctx->conn_id_cn, r->head.conn_id, sizeof(mgcp_ctx->conn_id_cn));
445 LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/CN: MGW responded with CI: %s\n", mgcp_ctx->conn_id_cn);
446
447 rc = mgcp_response_parse_params(r);
448 if (rc) {
449 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR, "CRCX/CN: Cannot parse response\n");
Philipp Maier4eef20b2018-03-13 13:07:45 +0100450 handle_error(mgcp_ctx, MGCP_ERR_MGW_INVAL_RESP, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100451 return;
452 }
453
454 LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/CN: MGW responded with address %s:%u\n", r->audio_ip, r->audio_port);
455
456 conn->rtp.local_port_cn = r->audio_port;
457 osmo_strlcpy(conn->rtp.local_addr_cn, r->audio_ip, sizeof(conn->rtp.local_addr_cn));
458
459 /* Notify the FSM that we got the response. */
460 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_CRCX_CN_RESP, mgcp_ctx);
461}
462
463/* Callback for ST_CRCX_COMPL: check MGW response, start assignment */
464static void fsm_crcx_compl(struct osmo_fsm_inst *fi, uint32_t event, void *data)
465{
466 struct mgcp_ctx *mgcp_ctx = data;
467 struct gsm_trans *trans;
468 struct gsm_subscriber_connection *conn;
469
470 OSMO_ASSERT(mgcp_ctx);
471 trans = mgcp_ctx->trans;
472 OSMO_ASSERT(trans);
473 conn = trans->conn;
474 OSMO_ASSERT(conn);
475
476 switch (event) {
477 case EV_CRCX_CN_RESP:
478 break;
479 default:
Philipp Maier4eef20b2018-03-13 13:07:45 +0100480 handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100481 return;
482 }
483
484 /* Forward assignment request to A/RANAP */
485 if (conn->via_ran == RAN_UTRAN_IU) {
486#ifdef BUILD_IU
487 /* Assign a voice channel via RANAP on 3G */
488 if (iu_rab_act_cs(trans))
489 goto error;
490#else
491 LOGPFSML(fi, LOGL_ERROR, "Cannot send Iu RAB Assignment: built without Iu support\n");
492 goto error;
493#endif
494 } else if (conn->via_ran == RAN_GERAN_A) {
495 /* Assign a voice channel via A on 2G */
496 if (a_iface_tx_assignment(trans))
497 goto error;
498 } else {
499 /* Unset or unimplemented new RAN type */
500 LOGPFSML(fi, LOGL_ERROR, "Unknown RAN type: %d\n", conn->via_ran);
501 return;
502 }
503
504 /* Respond back to MNCC (if requested) */
505 if (trans->tch_rtp_create) {
506 if (gsm48_tch_rtp_create(trans))
507 goto error;
508 }
509
510 /* Note: When we reach this point then the situation is basically that
511 * we have two sides connected, both are in loopback. The local ports
512 * of the side pointing towards the BSS should be already communicated
Philipp Maier4c573772018-02-08 14:05:43 +0100513 * and we are waiting now the other end to pick up. */
Philipp Maier621ba032017-11-07 17:19:25 +0100514 osmo_fsm_inst_state_chg(fi, ST_MDCX_CN, MGCP_RAN_TIMEOUT, MGCP_RAN_TIMEOUT_TIMER_NR);
515 return;
516
517error:
Philipp Maier4eef20b2018-03-13 13:07:45 +0100518 handle_error(mgcp_ctx, MGCP_ERR_ASSGMNT_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100519}
520
521static void mgw_mdcx_cn_resp_cb(struct mgcp_response *r, void *priv);
522
523/* Callback for ST_MDCX_CN: send MDCX for RAN side to MGW */
524static void fsm_mdcx_cn_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
525{
526 struct mgcp_ctx *mgcp_ctx = data;
527 struct mgcp_client *mgcp;
528 struct gsm_trans *trans;
529 struct gsm_subscriber_connection *conn;
530 struct mgcp_msg mgcp_msg;
531 struct msgb *msg;
532 int rc;
533
534 OSMO_ASSERT(mgcp_ctx);
535 mgcp = mgcp_ctx->mgcp;
536 OSMO_ASSERT(mgcp);
537 trans = mgcp_ctx->trans;
538 OSMO_ASSERT(trans);
539 conn = trans->conn;
540 OSMO_ASSERT(conn);
541
542 switch (event) {
543 case EV_CONNECT:
544 break;
545 default:
Philipp Maier4eef20b2018-03-13 13:07:45 +0100546 handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100547 return;
548 }
549
550 LOGPFSML(fi, LOGL_DEBUG,
Pau Espin Pedrol9fac9852018-03-17 01:54:32 +0100551 "MDCX/CN: completing connection for the CN side on MGW endpoint:%p, remote leg expects RTP input on address %s:%u\n",
Philipp Maier621ba032017-11-07 17:19:25 +0100552 mgcp_ctx->rtp_endpoint, conn->rtp.remote_addr_cn, conn->rtp.remote_port_cn);
553
554 /* Generate MGCP message string */
555 mgcp_msg = (struct mgcp_msg) {
556 .verb = MGCP_VERB_MDCX,
557 .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_ID |
558 MGCP_MSG_PRESENCE_CONN_MODE | MGCP_MSG_PRESENCE_AUDIO_IP |
559 MGCP_MSG_PRESENCE_AUDIO_PORT),
Philipp Maiera2353c62018-02-08 14:15:59 +0100560 .call_id = mgcp_ctx->call_id,
Philipp Maier621ba032017-11-07 17:19:25 +0100561 .conn_id = mgcp_ctx->conn_id_cn,
562 .conn_mode = MGCP_CONN_RECV_SEND,
563 .audio_ip = conn->rtp.remote_addr_cn,
564 .audio_port = conn->rtp.remote_port_cn
565 };
Philipp Maiera2353c62018-02-08 14:15:59 +0100566 if (osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->rtp_endpoint, sizeof(mgcp_msg.endpoint)) >=
Philipp Maier621ba032017-11-07 17:19:25 +0100567 MGCP_ENDPOINT_MAXLEN) {
Philipp Maier3a776522018-02-22 12:00:00 +0100568 handle_error(mgcp_ctx, MGCP_ERR_TOOLONG, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100569 return;
570 }
Philipp Maiera2353c62018-02-08 14:15:59 +0100571
Philipp Maier621ba032017-11-07 17:19:25 +0100572 msg = mgcp_msg_gen(mgcp, &mgcp_msg);
573 OSMO_ASSERT(msg);
574
575 /* Transmit MGCP message to MGW */
576 mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
577 rc = mgcp_client_tx(mgcp, msg, mgw_mdcx_cn_resp_cb, mgcp_ctx);
578 if (rc < 0) {
Philipp Maier4eef20b2018-03-13 13:07:45 +0100579 handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100580 return;
581 }
582
583 osmo_fsm_inst_state_chg(fi, ST_MDCX_CN_COMPL, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
584}
585
586/* Callback for MGCP-Client: handle response for CN associated CRCX */
587static void mgw_mdcx_cn_resp_cb(struct mgcp_response *r, void *priv)
588{
589 struct mgcp_ctx *mgcp_ctx = priv;
590
591 OSMO_ASSERT(mgcp_ctx);
592
593 if (r->head.response_code != 200) {
594 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
595 "MDCX/CN: response yields error: %d %s\n", r->head.response_code, r->head.comment);
Philipp Maier4eef20b2018-03-13 13:07:45 +0100596 handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100597 return;
598 }
599
600 /* Notify the FSM that we got the response. */
601 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_MDCX_CN_RESP, mgcp_ctx);
602}
603
604/* Callback for ST_MDCX_CN_COMPL: wait for mgw response, move on with the MDCX
605 * for the RAN side if we already have valid IP/Port data for the RAN sided
606 * RTP stream. */
607static void fsm_mdcx_cn_compl_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
608{
609 struct mgcp_ctx *mgcp_ctx = data;
610 struct gsm_subscriber_connection *conn;
611 struct gsm_trans *trans;
612
613 OSMO_ASSERT(mgcp_ctx);
614 trans = mgcp_ctx->trans;
615 OSMO_ASSERT(trans);
616 conn = trans->conn;
617 OSMO_ASSERT(conn);
618
619 switch (event) {
620 case EV_MDCX_CN_RESP:
621 break;
622 default:
Philipp Maier4eef20b2018-03-13 13:07:45 +0100623 handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100624 return;
625 }
626
627 /* Enter MDCX phase, but we must be sure that the Assigmnet on the A or
628 * IuCS interface is complete (IP-Address and Port are valid) */
629 osmo_fsm_inst_state_chg(fi, ST_MDCX_RAN, MGCP_ASS_TIMEOUT, MGCP_ASS_TIMEOUT_TIMER_NR);
630
631 /* If we already have a valid remote port and IP-Address from the RAN side
632 * call leg, the assignment has been completed before we got here, so we
633 * may move on immediately */
634 if (conn->rtp.remote_port_ran != 0 || strlen(conn->rtp.remote_addr_ran) > 0)
635 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_ASSIGN, mgcp_ctx);
636}
637
638static void mgw_mdcx_ran_resp_cb(struct mgcp_response *r, void *priv);
639
640/* Callback for ST_MDCX_RAN: wait for assignment completion, send MDCX for CN side to MGW */
641static void fsm_mdcx_ran_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
642{
643 struct mgcp_ctx *mgcp_ctx = data;
644 struct mgcp_client *mgcp;
645 struct gsm_trans *trans;
646 struct gsm_subscriber_connection *conn;
647 struct mgcp_msg mgcp_msg;
648 struct msgb *msg;
649 int rc;
650
651 OSMO_ASSERT(mgcp_ctx);
652 mgcp = mgcp_ctx->mgcp;
653 OSMO_ASSERT(mgcp);
654 trans = mgcp_ctx->trans;
655 OSMO_ASSERT(trans);
656 conn = trans->conn;
657 OSMO_ASSERT(conn);
658
659 switch (event) {
660 case EV_ASSIGN:
661 break;
662 default:
Philipp Maier4eef20b2018-03-13 13:07:45 +0100663 handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100664 return;
665 }
666
667 LOGPFSML(fi, LOGL_DEBUG,
Pau Espin Pedrol9fac9852018-03-17 01:54:32 +0100668 "MDCX/RAN: completing connection for the CN side on MGW endpoint:%p, RAN expects RTP input on address %s:%u\n",
Philipp Maier621ba032017-11-07 17:19:25 +0100669 mgcp_ctx->rtp_endpoint, conn->rtp.remote_addr_ran, conn->rtp.remote_port_ran);
670
671 /* Generate MGCP message string */
672 mgcp_msg = (struct mgcp_msg) {
673 .verb = MGCP_VERB_MDCX,
674 .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_ID |
675 MGCP_MSG_PRESENCE_CONN_MODE | MGCP_MSG_PRESENCE_AUDIO_IP |
676 MGCP_MSG_PRESENCE_AUDIO_PORT),
Philipp Maiera2353c62018-02-08 14:15:59 +0100677 .call_id = mgcp_ctx->call_id,
Philipp Maier621ba032017-11-07 17:19:25 +0100678 .conn_id = mgcp_ctx->conn_id_ran,
679 .conn_mode = MGCP_CONN_RECV_SEND,
680 .audio_ip = conn->rtp.remote_addr_ran,
681 .audio_port = conn->rtp.remote_port_ran
682 };
Philipp Maiera2353c62018-02-08 14:15:59 +0100683 if (osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->rtp_endpoint, sizeof(mgcp_msg.endpoint)) >=
Philipp Maier621ba032017-11-07 17:19:25 +0100684 MGCP_ENDPOINT_MAXLEN) {
Philipp Maier3a776522018-02-22 12:00:00 +0100685 handle_error(mgcp_ctx, MGCP_ERR_TOOLONG, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100686 return;
687 }
Philipp Maiera2353c62018-02-08 14:15:59 +0100688
Philipp Maier621ba032017-11-07 17:19:25 +0100689 msg = mgcp_msg_gen(mgcp, &mgcp_msg);
690 OSMO_ASSERT(msg);
691
692 /* Transmit MGCP message to MGW */
693 mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
694 rc = mgcp_client_tx(mgcp, msg, mgw_mdcx_ran_resp_cb, mgcp_ctx);
695 if (rc < 0) {
Philipp Maier4eef20b2018-03-13 13:07:45 +0100696 handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100697 return;
698 }
699
700 osmo_fsm_inst_state_chg(fi, ST_MDCX_RAN_COMPL, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
701}
702
703/* Callback for MGCP-Client: handle response for CN associated CRCX */
704static void mgw_mdcx_ran_resp_cb(struct mgcp_response *r, void *priv)
705{
706 struct mgcp_ctx *mgcp_ctx = priv;
707
708 OSMO_ASSERT(mgcp_ctx);
709
710 if (r->head.response_code != 200) {
711 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
712 "MDCX/RAN: response yields error: %d %s\n", r->head.response_code, r->head.comment);
Philipp Maier4eef20b2018-03-13 13:07:45 +0100713 handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100714 return;
715 }
716
717 /* Notify the FSM that we got the response. */
718 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_MDCX_RAN_RESP, mgcp_ctx);
719}
720
721/* Callback for ST_MDCX_RAN_COMPL: check MGW response */
722static void fsm_mdcx_ran_compl_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
723{
724 struct mgcp_ctx *mgcp_ctx = data;
725 OSMO_ASSERT(mgcp_ctx);
726
727 switch (event) {
728 case EV_MDCX_RAN_RESP:
729 break;
730 default:
Philipp Maier4eef20b2018-03-13 13:07:45 +0100731 handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100732 return;
733 }
734
735 LOGPFSML(fi, LOGL_DEBUG, "call active, waiting for teardown...\n");
736 osmo_fsm_inst_state_chg(fi, ST_CALL, 0, 0);
737}
738
739static void mgw_dlcx_all_resp_cb(struct mgcp_response *r, void *priv);
740
741/* Callback for ST_CALL: call is active, send DLCX for both sides on teardown */
742static void fsm_call_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
743{
744
745 struct mgcp_ctx *mgcp_ctx = (struct mgcp_ctx *)data;
746 struct mgcp_client *mgcp;
747 struct mgcp_msg mgcp_msg;
748 struct msgb *msg;
749 int rc;
750
751 OSMO_ASSERT(mgcp_ctx);
752 mgcp = mgcp_ctx->mgcp;
753 OSMO_ASSERT(mgcp);
754
755 LOGPFSML(fi, LOGL_DEBUG,
Philipp Maierd997fa12018-04-11 15:23:30 +0200756 "DLCX: removing connection for the RAN and CN side on MGW endpoint:%s...\n", mgcp_ctx->rtp_endpoint);
Philipp Maier621ba032017-11-07 17:19:25 +0100757
Philipp Maier621ba032017-11-07 17:19:25 +0100758 /* Generate MGCP message string */
759 mgcp_msg = (struct mgcp_msg) {
760 .verb = MGCP_VERB_DLCX,
761 .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID),
Philipp Maiera2353c62018-02-08 14:15:59 +0100762 .call_id = mgcp_ctx->call_id
Philipp Maier621ba032017-11-07 17:19:25 +0100763 };
Philipp Maiera2353c62018-02-08 14:15:59 +0100764 if (osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->rtp_endpoint, sizeof(mgcp_msg.endpoint)) >=
Philipp Maier621ba032017-11-07 17:19:25 +0100765 MGCP_ENDPOINT_MAXLEN) {
Philipp Maier3a776522018-02-22 12:00:00 +0100766 handle_error(mgcp_ctx, MGCP_ERR_TOOLONG, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100767 return;
768 }
Philipp Maiera2353c62018-02-08 14:15:59 +0100769
Philipp Maier621ba032017-11-07 17:19:25 +0100770 msg = mgcp_msg_gen(mgcp, &mgcp_msg);
771 OSMO_ASSERT(msg);
772
773 /* Transmit MGCP message to MGW */
774 mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
775 rc = mgcp_client_tx(mgcp, msg, mgw_dlcx_all_resp_cb, mgcp_ctx);
776 if (rc < 0) {
Philipp Maier4eef20b2018-03-13 13:07:45 +0100777 handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100778 return;
779 }
780
781 osmo_fsm_inst_state_chg(fi, ST_HALT, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
782}
783
784/* Callback for MGCP-Client: handle response for CN associated CRCX */
785static void mgw_dlcx_all_resp_cb(struct mgcp_response *r, void *priv)
786{
787 struct mgcp_ctx *mgcp_ctx = priv;
788
789 OSMO_ASSERT(mgcp_ctx);
790
Harald Welte33d61e72018-02-10 10:43:38 +0100791 /* DLCX is the only command where 250 is permitted as positive result */
792 if (r->head.response_code != 200 && r->head.response_code != 250) {
Philipp Maier621ba032017-11-07 17:19:25 +0100793 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
794 "DLCX: response yields error: %d %s\n", r->head.response_code, r->head.comment);
Philipp Maier4eef20b2018-03-13 13:07:45 +0100795 handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100796 return;
797 }
798
799 /* Notify the FSM that we got the response. */
800 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_DLCX_ALL_RESP, mgcp_ctx);
801}
802
803/* Callback for ST_HALT: Terminate the state machine */
804static void fsm_halt_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
805{
806 struct mgcp_ctx *mgcp_ctx = data;
Philipp Maieraddf63b2018-03-14 13:37:44 +0100807 struct mgcp_client *mgcp;
Philipp Maier621ba032017-11-07 17:19:25 +0100808
809 OSMO_ASSERT(mgcp_ctx);
Philipp Maieraddf63b2018-03-14 13:37:44 +0100810 mgcp = mgcp_ctx->mgcp;
811 OSMO_ASSERT(mgcp);
Philipp Maier621ba032017-11-07 17:19:25 +0100812
813 /* NOTE: We must not free the context information now, we have to
814 * wait until msc_mgcp_call_release() is called. Then we are sure
815 * that the logic controlling us is fully aware that the context
816 * information is freed. If we would free early now the controlling
817 * logic might mistakenly think that the context info is still alive,
818 * so lets keep the context info until we are explicitly asked for
819 * throwing it away. */
820 if (mgcp_ctx->free_ctx) {
Philipp Maieraddf63b2018-03-14 13:37:44 +0100821 /* Be sure that there is no pending MGW transaction */
822 mgcp_client_cancel(mgcp, mgcp_ctx->mgw_pending_trans);
823
824 /* Free FSM and its context information */
Philipp Maier621ba032017-11-07 17:19:25 +0100825 osmo_fsm_inst_free(mgcp_ctx->fsm);
826 talloc_free(mgcp_ctx);
827 return;
828 }
829
830 osmo_fsm_inst_state_chg(fi, ST_HALT, MGCP_REL_TIMEOUT, MGCP_REL_TIMEOUT_TIMER_NR);
831}
832
833static struct osmo_fsm_state fsm_msc_mgcp_states[] = {
834
835 /* Startup state machine, send CRCX for RAN side. */
836 [ST_CRCX_RAN] = {
837 .in_event_mask = S(EV_INIT),
838 .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_CRCX_CN),
839 .name = OSMO_STRINGIFY(ST_CRCX_RAN),
840 .action = fsm_crcx_ran_cb,
841 },
842 /* When the response to the RAN CRCX is received, then proceed with
843 sending the CRCX for CN side */
844 [ST_CRCX_CN] = {
845 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_CRCX_RAN_RESP),
846 .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_CRCX_COMPL),
847 .name = OSMO_STRINGIFY(ST_CRCX_CN),
848 .action = fsm_crcx_cn_cb,
849 },
850 /* Complete the CRCX phase by starting the assignment. Depending on the
851 * RAT (Radio Access Technology), this will either trigger an
852 * Assignment Request on the A-Interface or an RAB-Assignment on the
853 * IU-interface */
854 [ST_CRCX_COMPL] = {
855 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_CRCX_CN_RESP),
856 .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_MDCX_CN),
857 .name = OSMO_STRINGIFY(ST_CRCX_COMPL),
858 .action = fsm_crcx_compl,
859 },
860 /* Wait for MSC to complete the assignment request, when complete, we
861 * will enter the MDCX phase by sending an MDCX for the CN side to the
862 * MGW */
863 [ST_MDCX_CN] = {
864 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_CONNECT),
865 .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_MDCX_CN_COMPL),
866 .name = OSMO_STRINGIFY(ST_MDCX_CN),
867 .action = fsm_mdcx_cn_cb,
868 },
869 /* We arrive in this state when the MDCX phase for the CN side has
870 * completed we will check the IP/Port of the RAN connection. If this
871 * data is valid we may continue with the MDCX phase for the RAN side.
872 * If not we wait until the assinment completes on the A or on the IuCS
873 * interface. The completion of the assignment will fill in the port and
874 * IP-Address of the RAN side and way may continue then. */
875 [ST_MDCX_CN_COMPL] = {
876 .in_event_mask = S(EV_TEARDOWN) | S(EV_MDCX_CN_RESP),
877 .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_MDCX_RAN),
878 .name = OSMO_STRINGIFY(ST_MDCX_CN_COMPL),
879 .action = fsm_mdcx_cn_compl_cb,
880 },
881 /* When the response for the CN MDCX is received, send the MDCX for the
882 * RAN side to the MGW */
883 [ST_MDCX_RAN] = {
884 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_ASSIGN),
885 .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_MDCX_RAN_COMPL),
886 .name = OSMO_STRINGIFY(ST_MDCX_RAN),
887 .action = fsm_mdcx_ran_cb,
888 },
889 /* The RAN side MDCX phase is complete when the response is received
890 * from the MGW. The call is then active, we change to ST_CALL and wait
891 * there until the call ends. */
892 [ST_MDCX_RAN_COMPL] = {
893 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_MDCX_RAN_RESP),
894 .out_state_mask = S(ST_HALT) | S(ST_CALL),
895 .name = OSMO_STRINGIFY(ST_MDCX_RAN_COMPL),
896 .action = fsm_mdcx_ran_compl_cb,
897 },
898 /* We are now in the active call phase, wait until the call is done
899 * and send a DLCX then to remove all connections from the MGW */
900 [ST_CALL] = {
901 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR),
902 .out_state_mask = S(ST_HALT),
903 .name = OSMO_STRINGIFY(ST_CALL),
904 .action = fsm_call_cb,
905 },
906 /* When the MGW confirms that the connections are terminated, then halt
907 * the state machine. */
908 [ST_HALT] = {
909 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_DLCX_ALL_RESP),
910 .out_state_mask = S(ST_HALT),
911 .name = OSMO_STRINGIFY(ST_HALT),
912 .action = fsm_halt_cb,
913 },
914};
915
916/* State machine definition */
917static struct osmo_fsm fsm_msc_mgcp = {
918 .name = "MGW",
919 .states = fsm_msc_mgcp_states,
920 .num_states = ARRAY_SIZE(fsm_msc_mgcp_states),
921 .log_subsys = DMGCP,
922 .timer_cb = fsm_timeout_cb,
Philipp Maiere4f91722018-02-26 15:20:49 +0100923 .event_names = msc_mgcp_fsm_evt_names,
Philipp Maier621ba032017-11-07 17:19:25 +0100924};
925
926/* Notify that a new call begins. This will create a connection for the
927 * RAN and the CN on the MGW.
928 * Parameter:
929 * trans: transaction context.
930 * Returns -EINVAL on error, 0 on success. */
931int msc_mgcp_call_assignment(struct gsm_trans *trans)
932{
933 struct mgcp_ctx *mgcp_ctx;
934 char name[32];
935 static bool fsm_registered = false;
936 struct gsm_subscriber_connection *conn;
937 struct mgcp_client *mgcp;
938
939 OSMO_ASSERT(trans);
940
941 if (!trans->conn) {
942 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid conn, call assignment failed\n",
943 vlr_subscr_name(trans->vsub));
944 return -EINVAL;
945 }
946
947 conn = trans->conn;
948 mgcp = conn->network->mgw.client;
949 OSMO_ASSERT(mgcp);
950
951 if (conn->rtp.mgcp_ctx) {
952 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) double assignment detected, dropping...\n",
953 vlr_subscr_name(trans->vsub));
954 return -EINVAL;
955 }
956
957#ifdef BUILD_IU
958 /* FIXME: HACK. where to scope the RAB Id? At the conn / subscriber / ranap_ue_conn_ctx? */
959 static uint8_t next_iu_rab_id = 1;
960 if (conn->via_ran == RAN_UTRAN_IU)
961 conn->iu.rab_id = next_iu_rab_id++;
962#endif
963
964 if (snprintf(name, sizeof(name), "MGW_%i", trans->transaction_id) >= sizeof(name))
965 return -EINVAL;
966
967 /* Register the fsm description (if not already done) */
968 if (fsm_registered == false) {
969 osmo_fsm_register(&fsm_msc_mgcp);
970 fsm_registered = true;
971 }
972
973 /* Allocate and configure a new fsm instance */
974 mgcp_ctx = talloc_zero(NULL, struct mgcp_ctx);
975 OSMO_ASSERT(mgcp_ctx);
Philipp Maiera2353c62018-02-08 14:15:59 +0100976 if (osmo_strlcpy(mgcp_ctx->rtp_endpoint, ENDPOINT_ID, sizeof(mgcp_ctx->rtp_endpoint)) >=
977 MGCP_ENDPOINT_MAXLEN) {
978 talloc_free(mgcp_ctx);
979 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) endpoint identifier (%s) exceeds maximum length...\n",
980 vlr_subscr_name(trans->vsub), ENDPOINT_ID);
981 return -EINVAL;
982 }
Philipp Maier621ba032017-11-07 17:19:25 +0100983 mgcp_ctx->fsm = osmo_fsm_inst_alloc(&fsm_msc_mgcp, NULL, NULL, LOGL_DEBUG, name);
984 OSMO_ASSERT(mgcp_ctx->fsm);
985 mgcp_ctx->fsm->priv = mgcp_ctx;
986 mgcp_ctx->mgcp = mgcp;
987 mgcp_ctx->trans = trans;
Philipp Maiera2353c62018-02-08 14:15:59 +0100988 mgcp_ctx->call_id = trans->callref;
Philipp Maier621ba032017-11-07 17:19:25 +0100989
990 /* start state machine */
991 OSMO_ASSERT(mgcp_ctx->fsm->state == ST_CRCX_RAN);
992 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_INIT, mgcp_ctx);
993
994 conn->rtp.mgcp_ctx = mgcp_ctx;
995
996 LOGP(DMGCP, LOGL_DEBUG, "(subscriber:%s) call assignment initiated\n",
997 vlr_subscr_name(conn->vsub));
998
999 return 0;
1000}
1001
1002/* Inform the FSM that the assignment (RAN connection) is now complete.
1003 * Parameter:
1004 * conn: subscriber connection context.
1005 * port: port number of the remote leg.
1006 * addr: IP-address of the remote leg.
1007 * Returns -EINVAL on error, 0 on success. */
1008int msc_mgcp_ass_complete(struct gsm_subscriber_connection *conn, uint16_t port, char *addr)
1009{
1010 struct mgcp_ctx *mgcp_ctx;
1011
1012 OSMO_ASSERT(conn);
1013
1014 if (port == 0) {
1015 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid remote call leg port, assignmnet completion failed\n",
1016 vlr_subscr_name(conn->vsub));
1017 return -EINVAL;
1018 }
1019 if (!addr || strlen(addr) <= 0) {
1020 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) missing remote call leg address, assignmnet completion failed\n",
1021 vlr_subscr_name(conn->vsub));
1022 return -EINVAL;
1023 }
1024
1025 mgcp_ctx = conn->rtp.mgcp_ctx;
1026 if (!mgcp_ctx) {
1027 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid mgcp context, assignmnet completion failed.\n",
1028 vlr_subscr_name(conn->vsub));
1029 return -EINVAL;
1030 }
1031
1032 /* Memorize port and IP-Address of the remote RAN call leg. We need this
1033 * information at latest when we enter the MDCX phase for the RAN side. */
1034 conn->rtp.remote_port_ran = port;
1035 osmo_strlcpy(conn->rtp.remote_addr_ran, addr, sizeof(conn->rtp.remote_addr_ran));
1036
1037 LOGP(DMGCP, LOGL_DEBUG, "(subscriber:%s) assignmnet completed, rtp %s:%d.\n",
1038 vlr_subscr_name(conn->vsub), conn->rtp.remote_addr_ran, port);
1039
1040 /* Note: We only dispatch the event if we are really waiting for the
1041 * assignment, if we are not yet waiting, there is no need to loudly
1042 * broadcast an event that the all other states do not understand anyway */
1043 if (mgcp_ctx->fsm->state == ST_MDCX_RAN)
1044 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_ASSIGN, mgcp_ctx);
1045
1046 return 0;
1047}
1048
1049/* Make the connection of a previously assigned call complete
1050 * Parameter:
1051 * trans: transaction context.
1052 * port: port number of the remote leg.
1053 * addr: IP-address of the remote leg.
1054 * Returns -EINVAL on error, 0 on success. */
1055int msc_mgcp_call_complete(struct gsm_trans *trans, uint16_t port, char *addr)
1056{
1057 struct mgcp_ctx *mgcp_ctx;
1058 struct gsm_subscriber_connection *conn;
1059
1060 OSMO_ASSERT(trans);
1061 OSMO_ASSERT(addr);
1062
1063 if (port == 0) {
1064 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid remote call leg port, call completion failed\n",
1065 vlr_subscr_name(trans->vsub));
1066 return -EINVAL;
1067 }
1068 if (!addr || strlen(addr) <= 0) {
1069 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) missing remote call leg address, call completion failed\n",
1070 vlr_subscr_name(trans->vsub));
1071 return -EINVAL;
1072 }
1073 if (!trans->conn) {
1074 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid conn, call completion failed\n",
1075 vlr_subscr_name(trans->vsub));
1076 return -EINVAL;
1077 }
1078 if (!trans->conn->rtp.mgcp_ctx) {
1079 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid mgcp context, call completion failed.\n",
1080 vlr_subscr_name(trans->vsub));
1081 return -EINVAL;
1082 }
1083 if (!trans->conn->rtp.mgcp_ctx->fsm) {
1084 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) no FSM, call completion failed\n",
1085 vlr_subscr_name(trans->vsub));
1086 return -EINVAL;
1087 }
1088
1089 mgcp_ctx = trans->conn->rtp.mgcp_ctx;
1090
1091 /* The FSM should already have passed all CRCX phases and be ready to move
1092 * on with the MDCX phases. */
1093 if (mgcp_ctx->fsm->state != ST_MDCX_CN) {
1094 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid call state, call completion failed\n",
1095 vlr_subscr_name(trans->vsub));
1096 return -EINVAL;
1097 }
1098
1099 conn = trans->conn;
1100 osmo_strlcpy(conn->rtp.remote_addr_cn, addr, sizeof(conn->rtp.remote_addr_cn));
1101 conn->rtp.remote_port_cn = port;
1102
1103 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_CONNECT, mgcp_ctx);
1104
1105 LOGP(DMGCP, LOGL_DEBUG, "(subscriber:%s) call completion initiated\n",
1106 vlr_subscr_name(conn->vsub));
1107
1108 return 0;
1109}
1110
1111/* Release ongoing call.
1112 * Parameter:
1113 * trans: connection context.
1114 * Returns -EINVAL on error, 0 on success. */
1115int msc_mgcp_call_release(struct gsm_trans *trans)
1116{
1117 struct mgcp_ctx *mgcp_ctx;
1118
1119 OSMO_ASSERT(trans);
1120
1121 if (!trans->conn) {
1122 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid conn, call release failed\n",
1123 vlr_subscr_name(trans->vsub));
1124 return -EINVAL;
1125 }
1126 if (!trans->conn->rtp.mgcp_ctx) {
1127 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid mgcp context, call release failed.\n",
1128 vlr_subscr_name(trans->vsub));
1129 return -EINVAL;
1130 }
1131 if (!trans->conn->rtp.mgcp_ctx->fsm) {
1132 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) no FSM, call release failed\n",
1133 vlr_subscr_name(trans->vsub));
1134 return -EINVAL;
1135 }
1136
1137 mgcp_ctx = trans->conn->rtp.mgcp_ctx;
1138
1139 /* Inform the FSM that as soon as it reaches ST_HALT it may free
1140 * all context information immediately */
1141 mgcp_ctx->free_ctx = true;
1142
1143 /* Initaite teardown, regardless of which state we are currently
1144 * in */
1145 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_TEARDOWN, mgcp_ctx);
1146
1147 /* Prevent any further operation that is triggered from outside by
1148 * overwriting the context pointer with NULL. The FSM will now
1149 * take care for a graceful shutdown and when done it will free
1150 * all related context information */
1151 trans->conn->rtp.mgcp_ctx = NULL;
1152
1153 LOGP(DMGCP, LOGL_DEBUG, "(subscriber:%s) call release initiated\n",
1154 vlr_subscr_name(trans->vsub));
1155
1156 return 0;
1157}